From 67fcc35877f4c51e1ad96651892c0578d4ae7d3b Mon Sep 17 00:00:00 2001 From: Sami Assiri Date: Sun, 12 Apr 2026 23:13:29 +0300 Subject: [PATCH] chore(dealix): launch checklist, E2E port helper, OpenAPI path scan - Document verify-launch, py/py-3, E2E, and port 3000 troubleshooting - Add scripts/kill-port-3000.ps1 for Playwright webServer conflicts - Increase Playwright webServer timeout to 180s - Extend verify_frontend_openapi_paths for template literals; empty allowlist - Commit Next routes.d.ts reference in next-env.d.ts after build Made-with: Cursor --- salesflow-saas/docs/LAUNCH_CHECKLIST.md | 17 ++++---- salesflow-saas/frontend/next-env.d.ts | 1 + salesflow-saas/frontend/playwright.config.ts | 2 +- salesflow-saas/scripts/kill-port-3000.ps1 | 7 ++++ .../scripts/verify_frontend_openapi_paths.py | 39 +++++++++++++------ 5 files changed, 46 insertions(+), 20 deletions(-) create mode 100644 salesflow-saas/scripts/kill-port-3000.ps1 diff --git a/salesflow-saas/docs/LAUNCH_CHECKLIST.md b/salesflow-saas/docs/LAUNCH_CHECKLIST.md index 7a1143f8..603cdfeb 100644 --- a/salesflow-saas/docs/LAUNCH_CHECKLIST.md +++ b/salesflow-saas/docs/LAUNCH_CHECKLIST.md @@ -2,19 +2,22 @@ ## 1. الكود والاختبارات -- [ ] `cd backend && py -m pytest tests -q` — يجب أن تمر كل الاختبارات. -- [ ] `cd frontend && npm run lint && npm run build`. +- [ ] **اختبارات الباكند:** من `backend/` شغّل `python -m pytest tests -q` (مثل CI على Linux) أو على ويندوز إذا الأمر `python` غير موجود: `py -3 -m pytest tests -q`. +- [ ] **بوابة موحّدة (موصى به):** من جذر `salesflow-saas`: `.\verify-launch.ps1` — يشغّل pytest + مزامنة التسويق + lint + build. +- [ ] `cd frontend && npm run lint && npm run build` (أو تُغطّى بواسطة `verify-launch.ps1`). - [ ] من جذر `salesflow-saas`: `node scripts/sync-marketing-to-public.cjs` (يُشغَّل أيضاً تلقائياً قبل `npm run build`). -- [ ] (اختياري) من جذر `salesflow-saas`: `py scripts/verify_frontend_openapi_paths.py` — يطابق مسارات `/api/v1` الظاهرة حرفيًا في الفرونت مع OpenAPI. +- [ ] **E2E (Playwright):** بعد `npm run build`، حرّر المنفذ **3000** ثم من `frontend/`: `CI=true npm run test:e2e`. إن ظهر «port already in use» أو timeout على `webServer`: من جذر `salesflow-saas` شغّل `.\scripts\kill-port-3000.ps1` ثم أعد المحاولة. +- [ ] (اختياري) من جذر `salesflow-saas`: `py -3 scripts/verify_frontend_openapi_paths.py` (أو `python3 scripts/...`) — يطابق مسارات `/api/v1` في الفرونت مع OpenAPI (حرفيًا وفي قوالب مثل `` `${base}/api/v1/...` ``). ## 2. الخادم (API) - [ ] تشغيل من **أحدث** كود في المستودع: - `cd backend && py -m uvicorn app.main:app --host 0.0.0.0 --port 8000` + `cd backend && python -m uvicorn app.main:app --host 0.0.0.0 --port 8000` + (ويندوز: `py -3 -m uvicorn app.main:app --host 0.0.0.0 --port 8000`) - [ ] إذا ظهر **404** على `/api/v1/marketing/hub` أو `/api/v1/strategy/summary` فالعملية غالباً **قديمة** — أعد تشغيل `uvicorn` بعد `git pull`. -- [ ] اختبار HTTP: - `py scripts/full_stack_launch_test.py --http-only --soft-ready` - أو: +- [ ] اختبار HTTP (من مجلد `backend/`): + `py -3 scripts/full_stack_launch_test.py --http-only --soft-ready` + أو من جذر `salesflow-saas`: `.\scripts\grand_launch_verify.ps1 -HttpCheck -SoftReady` مع `DEALIX_BASE_URL` إذا لم يكن الـ API على `http://127.0.0.1:8000`. diff --git a/salesflow-saas/frontend/next-env.d.ts b/salesflow-saas/frontend/next-env.d.ts index 1b3be084..830fb594 100644 --- a/salesflow-saas/frontend/next-env.d.ts +++ b/salesflow-saas/frontend/next-env.d.ts @@ -1,5 +1,6 @@ /// /// +/// // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/salesflow-saas/frontend/playwright.config.ts b/salesflow-saas/frontend/playwright.config.ts index 959a6744..43ea66e7 100644 --- a/salesflow-saas/frontend/playwright.config.ts +++ b/salesflow-saas/frontend/playwright.config.ts @@ -16,7 +16,7 @@ export default defineConfig({ webServer: { command: "node .next/standalone/server.js", url: "http://127.0.0.1:3000", - timeout: 120_000, + timeout: 180_000, reuseExistingServer: !process.env.CI, }, }); diff --git a/salesflow-saas/scripts/kill-port-3000.ps1 b/salesflow-saas/scripts/kill-port-3000.ps1 new file mode 100644 index 00000000..1f601cc1 --- /dev/null +++ b/salesflow-saas/scripts/kill-port-3000.ps1 @@ -0,0 +1,7 @@ +# Stops processes listening on TCP port 3000 (fixes Playwright webServer "port already in use"). +# Run from salesflow-saas: .\scripts\kill-port-3000.ps1 +$ErrorActionPreference = "SilentlyContinue" +Get-NetTCPConnection -LocalPort 3000 -ErrorAction SilentlyContinue | ForEach-Object { + Stop-Process -Id $_.OwningProcess -Force -ErrorAction SilentlyContinue +} +Write-Host "Port 3000 cleared (if anything was listening)." -ForegroundColor DarkGray diff --git a/salesflow-saas/scripts/verify_frontend_openapi_paths.py b/salesflow-saas/scripts/verify_frontend_openapi_paths.py index 759de530..0a1ab5b2 100644 --- a/salesflow-saas/scripts/verify_frontend_openapi_paths.py +++ b/salesflow-saas/scripts/verify_frontend_openapi_paths.py @@ -1,10 +1,14 @@ #!/usr/bin/env python3 """ -Scan frontend/src for literal /api/v1/... path strings and verify exact matches +Scan frontend/src for /api/v1/... path strings and verify exact matches against the FastAPI OpenAPI schema. +Detects: + - Quoted literals: '/api/v1/foo', "/api/v1/foo", `/api/v1/foo` + - Template tails after ${...}: `${base}/api/v1/foo` (query string stripped) + Run from anywhere: - py salesflow-saas/scripts/verify_frontend_openapi_paths.py + py -3 salesflow-saas/scripts/verify_frontend_openapi_paths.py Requires backend deps on PYTHONPATH (run after: cd salesflow-saas/backend && py -m pip install -r requirements.txt). """ @@ -15,6 +19,10 @@ import re import sys from pathlib import Path +# Paths that appear in the frontend but use OpenAPI path parameters ({id}, etc.) +# or are intentionally not registered as separate operations — extend only with a comment. +OPENAPI_PATH_ALLOWLIST: frozenset[str] = frozenset() + def main() -> int: saas = Path(__file__).resolve().parent.parent @@ -31,30 +39,37 @@ def main() -> int: schema = app.openapi() open_paths = {p.rstrip("/") or "/" for p in schema.get("paths", {}).keys()} - # Literal path segments in quotes or template strings (no ${...} inside path) - pat = re.compile(r"""['"`]((/api/v1/[a-zA-Z0-9_\-./]+))['"`]""") + quoted = re.compile(r"""['"`]((/api/v1/[a-zA-Z0-9_\-./]+))['"`]""") + after_subst = re.compile(r"\$\{[^}]+\}(/api/v1/[a-zA-Z0-9_\-./]+)") + found: set[str] = set() for p in fe_src.rglob("*"): if p.suffix not in (".ts", ".tsx"): continue text = p.read_text(encoding="utf-8", errors="ignore") - for m in pat.finditer(text): - raw = m.group(1).rstrip("/") - if "${" in raw or "{" in raw: - continue + for pat in (quoted,): + for m in pat.finditer(text): + raw = m.group(1).split("?")[0].rstrip("/") + if "${" in raw or "{" in raw: + continue + if raw.endswith("/api/v1"): + continue + found.add(raw) + for m in after_subst.finditer(text): + raw = m.group(1).split("?")[0].rstrip("/") if raw.endswith("/api/v1"): continue found.add(raw) - missing = sorted(p for p in found if p not in open_paths) + missing = sorted(p for p in found if p not in open_paths and p not in OPENAPI_PATH_ALLOWLIST) if missing: - print("Frontend literal paths not found as exact OpenAPI paths (may use path params or be dynamic):") + print("Frontend paths not found as exact OpenAPI paths (may use path params or be dynamic):") for m in missing: print(f" - {m}") - print("\nTip: paths with {{id}} in OpenAPI need manual review.") + print("\nTip: paths with {id} in OpenAPI need allowlisting or a manual mapping.") return 1 - print(f"OK: {len(found)} literal /api/v1 paths match OpenAPI.") + print(f"OK: {len(found)} /api/v1 paths in frontend match OpenAPI.") return 0