75 lines
2.4 KiB
Markdown
75 lines
2.4 KiB
Markdown
# 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).
|