Files
shihaam 2f5659f3ff
Build and Push Docker Images / Build and Push Docker Images (push) Failing after 8s
document the ui
2026-08-02 18:44:46 +05:00

5.7 KiB

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; 401clearAuth() + 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 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.