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
This commit is contained in:
Sami Assiri 2026-04-12 23:13:29 +03:00
parent fcdbc1f004
commit 67fcc35877
5 changed files with 46 additions and 20 deletions

View File

@ -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`.

View File

@ -1,5 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference path="./.next/types/routes.d.ts" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.

View File

@ -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,
},
});

View File

@ -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

View File

@ -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 pat in (quoted,):
for m in pat.finditer(text):
raw = m.group(1).rstrip("/")
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