Build and Push Docker Images / Build and Push Docker Images (push) Failing after 8s
87 lines
5.7 KiB
Markdown
87 lines
5.7 KiB
Markdown
# Architecture
|
|
|
|
## What the app is
|
|
|
|
SAR Link Portal — a customer + admin portal for an ISP. Users register (verified against the national identity registry), add network devices, and pay for device subscriptions either by bank transfer (MIB) or from a prepaid wallet they top up. Admins verify users, manage devices/payments/topups, and credit wallets.
|
|
|
|
## Stack (current)
|
|
|
|
- **Next.js 15** (App Router, React 19), TypeScript.
|
|
- **Tailwind CSS v4** (CSS-first, no config file) + **shadcn/ui** (`new-york`, neutral) + Radix + **lucide** icons.
|
|
- **Jotai** (global UI state) + **React Query** (mounted, not yet used).
|
|
- **react-hook-form** + **zod** (forms/validation), **nuqs** (URL query state), **sonner** (toasts), **next-themes**, **motion** (animation).
|
|
- **Auth:** mid-migration — old **next-auth** (JWT session) → new **localStorage token** (`lib/auth-store.ts`) + axios **api-client** (`lib/api-client.ts`).
|
|
|
|
## Folder map
|
|
|
|
```
|
|
app/
|
|
(auth)/auth/{signin,signup,verify-otp,verify-otp-registration}/ # public auth pages
|
|
(dashboard)/{devices,payments,top-ups,wallet,users,...}/ # authed app (user + admin)
|
|
api/{auth/[...nextauth],check-devices}/ # frontend API routes (being removed)
|
|
layout.tsx page.tsx globals.css auth.ts
|
|
actions/ # server actions: auth-actions, payment, user-actions, (omada, ninja = dead)
|
|
queries/ # server data fetchers: authentication, devices, islands, users, wallet
|
|
components/ # feature components + components/ui (shadcn primitives)
|
|
lib/ # auth-store, api-client, atoms, backend-types, schemas, person, utils
|
|
utils/ # tryCatch, axios-client (dead), axiosInstance (dead)
|
|
providers/ # QueryProvider, theme, (AuthProvider = next-auth, being removed)
|
|
hooks/ middleware.ts(removed)
|
|
docs/ # this documentation
|
|
deploy/ # (superseded — real prod is .build/prod/, see below)
|
|
```
|
|
|
|
## Current architecture (BFF — what's being replaced)
|
|
|
|
```
|
|
Browser ──(RSC + Server Actions)──▶ Next.js server ──(fetch + Token)──▶ Django API
|
|
```
|
|
|
|
The Next.js server sits between the browser and Django:
|
|
- Server components + server actions read the token via `getServerSession` and call Django server-side over `SARLINK_API_BASE_URL`.
|
|
- Public origin `/api/*` = Next.js's own routes (NextAuth); Django's `/api` is reached only internally.
|
|
- Deployed as a Node container behind nginx (`.build/prod/`).
|
|
|
|
## Target architecture (static + nginx, direct-to-API)
|
|
|
|
```
|
|
Browser ──(static HTML/JS from nginx)
|
|
├─ API_URL calls (/api, /callback, /auth/*) ─▶ nginx ─▶ Django
|
|
└─ everything else ─▶ nginx serves the static build
|
|
```
|
|
|
|
Goals set by the project owner:
|
|
- **No Node/Bun in production** — ship a static build served by nginx.
|
|
- **Configurable API base:** `API_URL=http://localhost:8000` in dev, `API_URL=https://portal.sarlink.net/api` (nginx → backend) in prod.
|
|
- Browser calls the Django API **directly** (same-origin in prod via nginx; cross-origin in dev with CORS).
|
|
|
|
Two ways to get there (under discussion):
|
|
1. **Next.js static export** (`output: "export"`) — keeps Next; fights the framework (no server actions, `searchParams` needs Suspense, dynamic routes need query-params, middleware gone). Foundation already built: `lib/auth-store.ts`, `lib/api-client.ts`, `components/auth/route-guard.tsx`, config flip, dev `rewrites()` proxy. See `../STATIC_MIGRATION_PLAN.md`.
|
|
2. **Vite + React SPA (recommended)** — natural fit for a static client app with a configurable `API_URL` (`import.meta.env.VITE_API_URL`). A **port, not a rewrite**: all components, shadcn, react-query, jotai, zod transfer 1:1; only the shell (routing, `next/link`, `next/font`, `next/image`, layouts) changes. TanStack Router or React Router for routing.
|
|
|
|
Either way the **auth/data plumbing already written is reusable**: token in localStorage, `Authorization: Token` interceptor, client route guard, React Query for reads/mutations.
|
|
|
|
## Auth model (target)
|
|
|
|
1. Login: `POST {API}/callback/auth/ { token: pin }` → `{ token, user, expiry }`.
|
|
2. Store token + user in localStorage (`setAuth`).
|
|
3. Attach `Authorization: Token <token>` on every request (axios interceptor).
|
|
4. `RouteGuard` gates authed pages; `401` → `clearAuth()` + redirect to signin.
|
|
5. Admin gating from `user.is_admin` / `user.user_permissions`.
|
|
|
|
## Production deployment (where nginx lives)
|
|
|
|
The real prod stack is **`.build/prod/`** (repo root), not `frontend/deploy/`:
|
|
- `compose.yml` — postgres + backend (Django/gunicorn) + frontend + **nginx** (single entrypoint, `:8080→80`).
|
|
- `nginx.conf` — reverse proxy. **Currently built for the BFF model** (`/` → Next Node server; `/api` NOT proxied to Django). For the static migration this must change to: serve the static build + proxy `/api`, `/callback`, `/auth/{login,logout,mobile}` to Django; the separate Node `frontend` service goes away.
|
|
- `frontend.Dockerfile` — currently `output:"standalone"` + `node server.js`; becomes a static build folded into the nginx image.
|
|
|
|
See [api-endpoints.md](./api-endpoints.md) for exact paths and the `/auth/*` frontend-vs-backend collision that the nginx config must handle.
|
|
|
|
## Known issues / cleanup
|
|
|
|
- **Mid-migration auth:** dashboard pages/actions/queries still use `getServerSession`; they must move to the client token store (or be removed in the SPA port).
|
|
- **Stale wallet balance:** header balance comes from the login `userAtom` snapshot; not refreshed after topups/payments until re-login.
|
|
- **Dead code:** Omada, Invoice Ninja, `/auth/login/`, `/islands/`, `/inventory/`, `backendMobileLogin`, `app/api/check-devices`, `utils/axios-client.ts`, `utils/axiosInstance.ts`.
|
|
- **person-verify** is called directly from the frontend; planned to route through the backend.
|