# sarlinkportal Monorepo for the SAR Link member portal. ``` backend/ Django 5.2 + DRF API (knox auth, postgres, procrastinate) frontend/ Vite + React + TypeScript SPA (served by nginx in production) .build/prod/ production images and compose ``` ## Quick start ```sh cp backend/.env.example backend/.env # add SMS_API_KEY for real texts docker compose up --build docker compose exec backend python manage.py createsuperuser # asks for a mobile number ``` Atolls and islands are seeded by migration. With `SMS_API_KEY` empty, OTP codes are printed to the backend log instead of being texted. | | | |---|---| | SPA | http://localhost:5173 | | API | http://localhost:8000/api/ | | API docs (DEBUG) | http://localhost:8000/api/docs/ | | Django admin | http://localhost:8000/admin/ | The root `compose.yml` just includes `backend/compose.yml` and `frontend/compose.yml`, so each side can also be brought up on its own. ## How the two talk The SPA only ever calls a relative `/api/...`: - **dev** — the vite dev server proxies `/api`, `/admin`, `/static`, `/media` to the backend container. - **prod** — one nginx serves the built SPA and proxies the same prefixes to gunicorn, so there's a single origin and no CORS. ## Authentication One entry point - the mobile number - and the API decides the second step: ``` POST /api/auth/start/ {mobile} -> next: "password" | "otp" password -> POST /api/auth/login/password/ {mobile, password} -> next: "dashboard" + knox token otp -> POST /api/auth/verify/ {mobile, code} -> next: "dashboard" + knox token -> next: "register" + ticket -> POST /api/auth/register/ {ticket, ...form} -> pending account ``` `start` gives nothing away: a number with no account gets the same "code sent" response as a member's. Only a confirmed code reveals which it was. New numbers then go form -> **pending**, and an admin approves the application in the Django admin before the account is usable. `backend/README.md` documents the responses, OTP/ticket policy, the SMS gateway and the approval actions; `frontend/README.md` documents the forms. ## Tests ```sh docker compose exec backend python manage.py test --settings=apibase.settings_test cd frontend && npm run build && npm run lint ``` ## Production See [`.build/prod/README.md`](.build/prod/README.md).