From c5ad57145cd90a22ab8a1860e083a692909f531f Mon Sep 17 00:00:00 2001 From: voidash Date: Sun, 27 Sep 2026 16:38:18 +0545 Subject: [PATCH 1/3] docs: correct setup and design documentation The setup script creates no seed data, but the README and docs/frontend.md described seeded issues and members and built the verification steps on them. Those steps now use real sign-ins and the repository's own issues. Also: - describe the actual styling (Tailwind tokens and shadcn/ui on Base UI) instead of the removed Primer-based stylesheets - state that Compose runs migrations before the app starts - drop notes on dark mode that no longer hold, and sections that repeated each other - remove the section describing one host's private setup Signed-off-by: voidash --- README.md | 56 +++++++++++------------------------ docs/deployment.md | 31 ++++--------------- docs/frontend.md | 74 ++++++++-------------------------------------- 3 files changed, 35 insertions(+), 126 deletions(-) diff --git a/README.md b/README.md index febba84..2a25e12 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ backend are separate code boundaries, not separate deployments. |---|---| | Runtime | Node 24 LTS (Bun for package management and scripts) | | Framework | Next.js 16 App Router — pages + route handlers in one app | -| UI | React 19, DevNepal design system (Primer CSS vendored + tokens), react-markdown | +| UI | React 19, Tailwind CSS 4, shadcn/ui on Base UI, SWR, react-markdown | | Database | PostgreSQL 17 | | ORM | Drizzle ORM + drizzle-kit (plain-SQL migrations) | | Auth | Auth.js v5 (`next-auth@5` beta) — GitHub OAuth, JWT sessions, no adapter | @@ -34,7 +34,7 @@ apps/api/ src/server/ services, repositories, authorization seam, storage, errors src/db/ Drizzle schema + client drizzle/ generated SQL migrations (committed) - src/scripts/ seed, GitHub sync, dev-session tools + src/scripts/ project init, GitHub sync, dev-session tools tests/ unit + integration tests, test DB bootstrap packages/api-contract/ canonical OpenAPI description packages/api-client/ generated API types + browser HTTP client @@ -118,8 +118,8 @@ bun run setup 5. verifies `SDOC-Team/devnepal` through the GitHub API, initializes the project row, and syncs its real issues -Nothing else is required — the design assets, translations, fonts, and vendored -styles all ship with the repository. +Nothing else is required — translations and design assets ship with the +repository. ### 2. Run @@ -134,9 +134,9 @@ bun run dev # UI + API on one port → http://localhost:3000/en | `curl localhost:3000/health` | `{"status":"ok"}` | | http://localhost:3000/en | home page with the state strip, hero, and project card | | http://localhost:3000/ne | the same page in Nepali | -| http://localhost:3000/en/issues | 8 seeded issues; label filter and search work | -| http://localhost:3000/en/members | 3 approved members; pending/rejected/hidden are absent | -| `curl 'localhost:3000/v1/project/issues?perPage=2'` | JSON with `"total": 8` | +| http://localhost:3000/en/issues | the repository's open issues; label filter and search work | +| http://localhost:3000/en/members | approved members only (empty until someone signs in and is approved) | +| `curl 'localhost:3000/v1/project/issues?perPage=2'` | JSON whose `total` matches the repository's open issues | | `bun run test` | the full Vitest suite passes against `refined_test` | ### 4. Optional: sign in with GitHub @@ -158,17 +158,18 @@ provider requests **`read:user` only** — email is never requested or stored. ### 5. Optional: test signed-in screens without GitHub -`bun run dev:session ` mints a real session cookie for a seeded -member so the profile editor and admin screens can be tested offline: +`bun run dev:session ` mints a real session cookie for an +existing member (sign in once with GitHub to create one), so the profile editor +and admin screens can be tested without repeating the OAuth flow: ```sh -bun run dev:session nisha-tamang +bun run dev:session ``` The command prints the `authjs.session-token` value and the member's GitHub ID. Add the cookie in DevTools → Application → Cookies → `http://localhost:3000`, and put the printed ID in `ADMIN_GITHUB_IDS` (then restart) for admin access. -See [`docs/frontend.md`](docs/frontend.md) for the full verification matrix. +See [`docs/frontend.md`](docs/frontend.md) for the verification matrix. ### 6. Refresh GitHub issues @@ -300,13 +301,9 @@ itself is reached from the home hero and the project card. When a member is signed in, the header shows **My profile**, and **Admin** appears for accounts listed in `ADMIN_GITHUB_IDS`. -The visual language is ported from the DevNepal frontend -([`voidash/DevNepal`, branch `demo/minimal-validated-flow`](https://github.com/voidash/DevNepal/tree/demo/minimal-validated-flow)): -the government state strip with the emblem, the black condensed headings, the -blue action ramp, and the component styles in -`apps/api/public/assets/devnepal/` (provenance and licences in that folder's -README). Primer CSS is vendored underneath as the base layer. Translations live -in `apps/api/src/lib/i18n.ts`. +Styling is Tailwind CSS with the design tokens defined in +`apps/api/src/app/tailwind.css`; UI primitives live in +`apps/api/src/components/ui`. Translations live in `apps/api/src/lib/i18n.ts`. ## Security invariants (tested) @@ -348,24 +345,5 @@ with mocked HTTP, and Auth.js CORS handling. Override the database with ## Deployment -See [`docs/deployment.md`](docs/deployment.md) for the full checklist, known -problems and fixes, k2 options, backups, and the cutover plan. - -```sh -docker build -f apps/api/Dockerfile -t gov-portal . -``` - -The image runs the Next.js standalone server as a non-root user (`node`), serves -the UI and the REST API on port 3000, and expects the environment variables -above. Avatars live in `/app/storage` — mount a persistent volume there. Run -migrations against the target database before starting the new image -(`bun run db:migrate` from a checkout with the production `DATABASE_URL`). - -## Notes - -- `/v1` is the canonical product API. The unversioned aliases are deprecated - migration aids and can be removed after all known consumers move to `/v1`. -- Live webhook delivery and contribution indexing are a later phase; issues - are reconciled on demand with `bun run sync:github`, and the signed webhook - endpoint is ready when delivery is wired. -- Dark mode: the design tokens ship with light mode only for now. +See [`docs/deployment.md`](docs/deployment.md) for the checklist, known +problems and their fixes, and backups. diff --git a/docs/deployment.md b/docs/deployment.md index 6b1ce75..72d24b1 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -16,14 +16,11 @@ read at runtime from the process environment. ## Before you deploy — checklist 1. **Postgres 17** reachable from the app. Set `DATABASE_URL`. -2. **Apply migrations before starting the new image**, against the target - database. Either from a checkout (`bun run db:migrate`) or with the shipped - migration runner: - ```sh - docker compose run --rm migrate - ``` - The app never migrates on boot by design; a crash-looping deploy must not - half-migrate a database. +2. **Migrations run before the app starts.** With Compose, the `api` service + waits for the one-shot `migrate` service to finish successfully. Outside + Compose, run `bun run db:migrate` against the target database first. The app + process itself never migrates, so a crash-looping app cannot half-migrate a + database. 3. **`AUTH_SECRET`** ≥ 32 characters, generated fresh per environment (`openssl rand -base64 48`). Changing it signs everyone out. 4. **GitHub OAuth**: add the production callback URL to the OAuth App / @@ -61,24 +58,6 @@ read at runtime from the process environment. | Rate limits reset on restart | In-memory counters, single instance | Accept for one replica; move to a shared store before scaling out | | Webhook deliveries fail silently | Ephemeral hostnames (quick tunnels) change | Use the stable production hostname; `sync:github` reconciles gaps | -## Deploy on k2 (current host) - -k2 is a macOS machine with Caddy, `cloudflared`, and launchd; the old Django -site currently owns `devnepal.zapper.cloud`. Two viable modes: - -- **Docker (recommended for parity)**: install colima or Docker Desktop on k2, - then `docker compose up -d db api` with a production `.env.local`. The compose - file already has restart policies; put the database port behind the host - firewall or remove its `ports:` mapping for production. -- **launchd (no Docker)**: run Postgres (brew) plus the standalone server via a - launchd unit that sources an env file and runs `node apps/api/server.js`. - This mirrors the old deployment but loses image parity. - -**Cutover without downtime**: deploy the new stack on a separate hostname -(e.g. `next.devnepal.zapper.cloud` → Caddy → `127.0.0.1:3000`), validate it, -then switch the `devnepal.zapper.cloud` tunnel ingress to the new upstream and -keep the old service running until the DNS/TLS is confirmed. - ## Backups - **Database**: `docker compose exec db pg_dump -U refined refined > backup.sql` diff --git a/docs/frontend.md b/docs/frontend.md index f67bac4..8800e5d 100644 --- a/docs/frontend.md +++ b/docs/frontend.md @@ -12,62 +12,16 @@ Auth.js internals, or environment configuration; the linter enforces this. ## Design -The visual language is the DevNepal design system, ported from -`voidash/DevNepal` (branch `demo/minimal-validated-flow`) and served from -`apps/api/public/assets/devnepal/` — tokens, base, components, devnepal and -public-discovery stylesheets, the vendored Primer base layer, Inter/Barlow -fonts, and the emblem assets. See the README in that folder for provenance and -licences. Use the existing `dn-*`, `btn`, `card`, `Label`, `tag` and -`field` classes rather than inventing new ones. - -## Environment - -Everything the UI needs is created by `bun run setup`. For reference: - -| Variable | Where | Purpose | -|---|---|---| -| `DATABASE_URL` | `apps/api/.env.local` | PostgreSQL connection | -| `AUTH_SECRET` | `apps/api/.env.local` | session encryption (generated) | -| `AUTH_GITHUB_ID` / `AUTH_GITHUB_SECRET` | `apps/api/.env.local` | GitHub sign-in (optional for public pages) | -| `ADMIN_GITHUB_IDS` | `apps/api/.env.local` | comma-separated GitHub numeric ids that may use `/en/admin` | -| `STORAGE_DIR` | `apps/api/.env.local` | where avatars are stored | -| `WEB_ORIGIN` | `apps/api/.env.local` | only relevant to external clients; UI and API are same-origin now | +Styling is Tailwind CSS with the design tokens in `apps/api/src/app/tailwind.css`. +UI primitives are in `apps/api/src/components/ui`; reuse them before adding new +ones. ## Run it -```sh -bun run setup # env files, database, migrations, seed data (safe to rerun) -bun run dev # UI + API at http://localhost:3000/en -``` - -Public pages work with the seed alone. Seeded members: - -| Username | Status | -|---|---| -| `aashish-khanal`, `priya-sharma`, `bikash-gurung` | approved | -| `nisha-tamang` | pending | -| `rejected-sample` | rejected | -| `hidden-sample` | hidden | - -## Sign in without GitHub - -`bun run dev:session ` mints a real session cookie for a seeded -member, so you can test the profile editor and admin screens without OAuth: - -```sh -bun run dev:session nisha-tamang -``` - -It prints `authjs.session-token=…`. Add it in DevTools → Application → Cookies → -`http://localhost:3000`, then reload. For admin screens, put the printed GitHub -id into `ADMIN_GITHUB_IDS` and restart the dev server: - -```sh -bun run dev:session voidash # prints the id to add -``` - -Real GitHub sign-in needs OAuth credentials in `apps/api/.env.local` (see the -README). +Setup, environment variables, GitHub sign-in and `bun run dev:session` are +described in the [README](../README.md). The setup creates no member accounts: +sign in with GitHub to create one, then use `bun run dev:session` to test the +signed-in screens. ## Routes @@ -93,13 +47,13 @@ handler, and request frontend and backend review. Runtime validation remains in | Behaviour | How to verify | |---|---| -| Directory lists approved members only | `/en/members` shows 3 seeded members; `nisha-tamang` absent | -| Non-public profile hidden | `/en/members/nisha-tamang` → "Profile not available"; with her session cookie → visible with a status banner | -| Username + GitHub id parity | `curl /v1/members/bikash-gurung` and `curl /v1/members/id/900103` return the same member | -| Issue filters | `/en/issues` → filter by `good first issue` (only matching rows), search `nepali` (matches title and body) | -| Issue detail | `/en/issues/101` renders labels, author, sanitized Markdown, GitHub link | +| Directory lists approved members only | a newly signed-in (pending) member is absent from `/en/members` | +| Non-public profile hidden | `/en/members/` → "Profile not available"; with that member's session cookie → visible with a status banner | +| Username + GitHub id parity | `curl /v1/members/` and `curl /v1/members/id/` return the same member | +| Issue filters | `/en/issues` → filter by a label (only matching rows), search a word from an issue title or body | +| Issue detail | `/en/issues/` renders labels, author, sanitized Markdown, GitHub link | | Profile validation | `/en/profile`: empty display name, 6 links, `http://` link, unknown skill → inline errors; save persists | -| Moderation | `/en/admin`: approve `nisha-tamang` → she appears in `/en/members`; reject → 404 publicly; priority reorders | +| Moderation | `/en/admin`: approve a pending member → they appear in `/en/members`; reject → 404 publicly; priority reorders | | Non-admin denial | sign in without the id in `ADMIN_GITHUB_IDS` → `/en/admin` shows "Not authorized" | | Bilingual | switch `EN | ने`; paths keep the locale and copy changes | | Sign in / out | header button completes the GitHub flow; sign-out returns to the page | @@ -109,5 +63,3 @@ handler, and request frontend and backend review. Runtime validation remains in - Live webhook delivery is deferred; issues come from `bun run sync:github` (a signed webhook endpoint exists and is tested, but nothing is wired to it). - Contribution indexing and recognition are a later phase. -- The seed's GitHub usernames are fictional; real avatars appear after real - sign-ins. From 6e25fe76a0f5c5359df163ad03696117b43eab4b Mon Sep 17 00:00:00 2001 From: voidash Date: Sun, 27 Sep 2026 18:02:13 +0545 Subject: [PATCH 2/3] docs: fix setup order and remaining inaccuracies The README called GitHub sign-in optional and ran the app before setting it up, but the app validates its configuration at startup and refuses to start without AUTH_GITHUB_ID and AUTH_GITHUB_SECRET. Creating the OAuth App is now step 2, before running the app, and both variables are marked required. Also correct the primary navigation, the Compose services, GITHUB_PROJECT_REPOSITORY (optional, used by db:init), the language switcher, and add the welcome page to the layout listing. Signed-off-by: voidash --- README.md | 61 ++++++++++++++++++++++++------------------------ docs/frontend.md | 2 +- 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 2a25e12..c399b00 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ backend are separate code boundaries, not separate deployments. ``` apps/api/ - src/app/(site)/[locale]/ UI pages (en/ne): home, project, issues, members, profile, admin, about + src/app/(site)/[locale]/ UI pages (en/ne): home, project, issues, members, profile, welcome, admin, about src/app/v1/ versioned REST route handlers src/app/ temporary compatibility aliases to `/v1` src/components/ site chrome and UI primitives @@ -41,7 +41,7 @@ packages/api-client/ generated API types + browser HTTP client packages/shared/src/ runtime Zod validation + internal service DTOs docs/frontend.md frontend onboarding and verification matrix scripts/setup.ts one-command local bootstrap -compose.yaml PostgreSQL for development (and a full api profile) +compose.yaml PostgreSQL for development, plus api and migrate services .github/workflows/ci.yml Lint, typecheck, tests, build on every PR ``` @@ -118,16 +118,32 @@ bun run setup 5. verifies `SDOC-Team/devnepal` through the GitHub API, initializes the project row, and syncs its real issues -Nothing else is required — translations and design assets ship with the -repository. +Translations and design assets ship with the repository. -### 2. Run +### 2. Create a GitHub OAuth App + +The app checks its configuration at startup and refuses to start without +GitHub OAuth credentials, so this step is required even for the public pages. + +1. Create an OAuth App at → **New OAuth App** + - Homepage URL: `http://localhost:3000` + - Authorization callback URL: `http://localhost:3000/api/auth/callback/github` +2. Put the credentials in `apps/api/.env.local`: + - `AUTH_GITHUB_ID` — the client ID + - `AUTH_GITHUB_SECRET` — generate a client secret and paste it +3. To get the admin queue, add your numeric GitHub ID to `ADMIN_GITHUB_IDS` + (`https://api.github.com/users/` → `id`); comma-separate several admins. + +Never commit these values; share team development credentials out-of-band. The +provider requests **`read:user` only** — email is never requested or stored. + +### 3. Run ```sh bun run dev # UI + API on one port → http://localhost:3000/en ``` -### 3. Verify the scaffold +### 4. Verify the scaffold | Check | Expected | |---|---| @@ -139,24 +155,7 @@ bun run dev # UI + API on one port → http://localhost:3000/en | `curl 'localhost:3000/v1/project/issues?perPage=2'` | JSON whose `total` matches the repository's open issues | | `bun run test` | the full Vitest suite passes against `refined_test` | -### 4. Optional: sign in with GitHub - -Sign-in is required only for the profile editor and admin screens. - -1. Create an OAuth App at → **New OAuth App** - - Homepage URL: `http://localhost:3000` - - Authorization callback URL: `http://localhost:3000/api/auth/callback/github` -2. Put the credentials in `apps/api/.env.local`: - - `AUTH_GITHUB_ID` — the client ID - - `AUTH_GITHUB_SECRET` — generate a client secret and paste it -3. To get the admin queue, add your numeric GitHub ID to `ADMIN_GITHUB_IDS` - (`https://api.github.com/users/` → `id`); comma-separate several admins. -4. Restart the dev server. - -Never commit these values; share team development credentials out-of-band. The -provider requests **`read:user` only** — email is never requested or stored. - -### 5. Optional: test signed-in screens without GitHub +### 5. Optional: test signed-in screens without repeating OAuth `bun run dev:session ` mints a real session cookie for an existing member (sign in once with GitHub to create one), so the profile editor @@ -211,10 +210,10 @@ until then. Contribution indexing is a later phase. |---|---|---| | `DATABASE_URL` | yes | PostgreSQL connection string | | `AUTH_SECRET` | yes | Auth.js session encryption, ≥ 32 chars | -| `AUTH_GITHUB_ID` | for sign-in | GitHub OAuth App client ID | -| `AUTH_GITHUB_SECRET` | for sign-in | GitHub OAuth App client secret | +| `AUTH_GITHUB_ID` | yes | GitHub OAuth App client ID; the app does not start without it | +| `AUTH_GITHUB_SECRET` | yes | GitHub OAuth App client secret; the app does not start without it | | `ADMIN_GITHUB_IDS` | for admin | Comma-separated admin GitHub numeric IDs (may be empty) | -| `GITHUB_PROJECT_REPOSITORY` | yes | Public GitHub repository to index; defaults to `SDOC-Team/devnepal` | +| `GITHUB_PROJECT_REPOSITORY` | optional | Public GitHub repository that `db:init` indexes; defaults to `SDOC-Team/devnepal` | | `STORAGE_DIR` | yes | Directory for stored avatars (persistent volume) | | `WEB_ORIGIN` | external clients | CORS allowlist for non-browser clients (mobile); the UI is same-origin | | `GITHUB_TOKEN` | optional | Raises the GitHub API rate limit for `sync:github` | @@ -296,10 +295,10 @@ Server-rendered pages under `/en` and `/ne` (English default; `/` redirects to sanitized Markdown, member directory, member profiles, own profile editor, admin moderation, and a how-to-contribute page. -Primary navigation is: Open issues · Members · How to contribute — the project -itself is reached from the home hero and the project card. When a member is -signed in, the header shows **My profile**, and **Admin** appears for accounts -listed in `ADMIN_GITHUB_IDS`. +Primary navigation is: How to contribute · Projects · Members · About. Open +issues are reached from the project page and the home page. When a member is +signed in, the header's account menu offers **My profile**, plus **Admin** for +accounts listed in `ADMIN_GITHUB_IDS`. Styling is Tailwind CSS with the design tokens defined in `apps/api/src/app/tailwind.css`; UI primitives live in diff --git a/docs/frontend.md b/docs/frontend.md index 8800e5d..4541bb7 100644 --- a/docs/frontend.md +++ b/docs/frontend.md @@ -55,7 +55,7 @@ handler, and request frontend and backend review. Runtime validation remains in | Profile validation | `/en/profile`: empty display name, 6 links, `http://` link, unknown skill → inline errors; save persists | | Moderation | `/en/admin`: approve a pending member → they appear in `/en/members`; reject → 404 publicly; priority reorders | | Non-admin denial | sign in without the id in `ADMIN_GITHUB_IDS` → `/en/admin` shows "Not authorized" | -| Bilingual | switch `EN | ने`; paths keep the locale and copy changes | +| Bilingual | switch between English and नेपाली in the language menu; paths keep the locale and copy changes | | Sign in / out | header button completes the GitHub flow; sign-out returns to the page | ## Known gaps (intentional) From fd1ae41cfddf09e330b9fc30dec637d5c95ac75b Mon Sep 17 00:00:00 2001 From: voidash Date: Sun, 27 Sep 2026 21:50:46 +0545 Subject: [PATCH 3/3] docs: say what dev:session does and does not replace A reviewer read the frontend guide as saying dev:session lets the app start without GitHub OAuth credentials. It does not: the app refuses to start without them, and dev:session only mints a cookie for a member who already signed in once. The guide now says both. Signed-off-by: voidash --- docs/frontend.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/frontend.md b/docs/frontend.md index 4541bb7..bb32b11 100644 --- a/docs/frontend.md +++ b/docs/frontend.md @@ -18,10 +18,15 @@ ones. ## Run it -Setup, environment variables, GitHub sign-in and `bun run dev:session` are -described in the [README](../README.md). The setup creates no member accounts: -sign in with GitHub to create one, then use `bun run dev:session` to test the -signed-in screens. +Setup, environment variables and GitHub sign-in are described in the +[README](../README.md). The GitHub OAuth App credentials are required: the app +does not start without them. + +The setup creates no member accounts, so sign in with GitHub once to create +yours. After that, `bun run dev:session ` prints a session +cookie for that member, so you can test the profile editor and admin screens +without going through GitHub sign-in each time. It does not replace the OAuth +credentials. ## Routes