write down how the two halves connect
هر بار کسی تازه به این پروژه میرسد، همان سه چیز را از نو کشف میکند: اینکه فرانت مسیرهای نسبی صدا میزند و nginx بهجای ۴۰۴ صفحهی اپ را برمیگرداند، اینکه ترتیبِ بالا آوردن مهم است، و اینکه کلاینت را نباید از روی حافظه نوشت. همه در CLAUDE.md نوشته شد، نه در فایلِ تازه — همانجایی که خوانده میشود. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
49
CLAUDE.md
49
CLAUDE.md
@@ -64,8 +64,57 @@ API docs: Swagger `/api/docs/`, Redoc `/api/redoc/`, admin `/admin/`.
|
||||
|
||||
**Payment (Zarinpal)**: `create_order`/`CreateOrderView` never call Zarinpal directly — `paymentUrl` points at our own `GET /api/payment/orders/{id}/pay` (`PaymentRedirectView`), which calls Zarinpal's PaymentRequest API on first visit (storing `Order.gateway_authority`) and redirects to StartPay; this keeps order creation from depending on Zarinpal's uptime. Zarinpal redirects the user back to `GET /api/payment/zarinpal/callback` (`ZarinpalCallbackView`), which always re-verifies server-to-server via `payment/zarinpal.py::verify_payment` before marking an order paid — a forged `Status=OK` in the query string alone does nothing. Both `payment/zarinpal.py` and `payment/bazaar.py` fail closed (503 / invalid) when their credentials aren't configured, never a fake success.
|
||||
|
||||
## Frontend ↔ backend (read before wiring the two together)
|
||||
|
||||
The app is two repos and two domains: the React frontend lives in
|
||||
`Tadrisino/MathApp_frontend` (deployed at `math.tadrisino2.ir`, owned by another
|
||||
developer) and this backend serves `api.math.tadrisino2.ir`. Our typed client for them
|
||||
is `src/api/` inside their repo, delivered by pull request — we have read-only Gitea
|
||||
access there, so changes go via a fork + PR, never a direct push.
|
||||
|
||||
**The one trap that keeps biting.** Their `App.tsx` calls relative paths
|
||||
(`fetch('/api/auth/login')`). Those resolve against the *frontend* origin, where nginx
|
||||
answers any unknown path with `index.html` — so the response is `200` with an HTML body
|
||||
and the code dies on `res.json()` with `Unexpected token '<'`. Verify with
|
||||
`curl https://math.tadrisino2.ir/api/grades`: HTTP 200, HTML. Two fixes exist, both
|
||||
shipped in PR #3: a `vite` dev proxy (dev works unchanged) and an exported `apiUrl()`
|
||||
for builds. **The better long-term fix is infrastructure, not code** — proxy `/api` on
|
||||
the frontend domain to this backend, which makes everything same-origin and removes CORS
|
||||
entirely. That needs the server admin, not us.
|
||||
|
||||
**Order matters when bringing the stack up:** backend first, then frontend. If the
|
||||
frontend is tested while `api.math.tadrisino2.ir` is down, every screen fails and it
|
||||
looks like the client is broken.
|
||||
|
||||
**Never hand-write the client from memory.** Three of fifteen calls were silently wrong
|
||||
the first time. Dump the real contract with
|
||||
`python manage.py spectacular --file schema.yaml --format openapi`, then drive the actual
|
||||
client module against a running server (`runserver` on a spare port + `npx tsx`) before
|
||||
sending anything. `content/tests_frontend_contract.py` is the backend-side guard.
|
||||
|
||||
**Content the frontend expects but nobody has filled in yet:** `Unit.blurb`,
|
||||
`Plan.features`/`badge`, `Lesson.example`/`steps`. All optional, all editable from the
|
||||
admin panel. `Node.icon`/`color` fall back to a deterministic palette so cards are never
|
||||
colourless.
|
||||
|
||||
## Operations
|
||||
|
||||
`./deploy.sh` is the whole release: pull, install, migrate, collectstatic, restart, then
|
||||
it polls `/api/health` and prints the likely cause if the service does not come back.
|
||||
Settings boot with working defaults (hosts, CSRF, CORS, secret key, cache), so a stale or
|
||||
partial `.env` on the server no longer breaks startup — env values are *merged with*
|
||||
those defaults, not replaced by them.
|
||||
|
||||
`GET /api/health` is the outside view: database, unapplied migrations, cache, content
|
||||
counts, security warnings, and recent traffic. It is how you check whether anyone has
|
||||
actually connected without asking a human.
|
||||
|
||||
## Known gaps (per README pre-deploy checklist)
|
||||
|
||||
- Still on SQLite, not Postgres.
|
||||
- Throttle rates need periodic review against real traffic.
|
||||
- `DEV_MASTER_OTP` (`0000`) must stay gated behind `DEBUG` — never enabled in prod.
|
||||
- The server runs with `DJANGO_DEBUG=1` on a public domain, so OTP `0000` currently logs
|
||||
anyone in as anyone. Harmless until real users exist; `/api/health` warns about it.
|
||||
- No video content exists yet, though `Lesson.kind`/`video_url` can express it.
|
||||
- Question generation and textbook ingest are blocked on AI API credit.
|
||||
|
||||
Reference in New Issue
Block a user