diff --git a/README.md b/README.md index 0fcf963..e146b1a 100644 --- a/README.md +++ b/README.md @@ -1,350 +1,221 @@ -# devnepal +# devNepal -A public portal for one open-source project: its open GitHub issues, the approved -member directory, and the profile/moderation flows around it. +Office of the Prime Minister and Council of Ministers, Government of Nepal · + -One Next.js app serves both the server-rendered UI (`/en`, `/ne`) and the -versioned JSON REST API (`/v1/members`, `/v1/project`, …). The frontend and -backend are separate code boundaries, not separate deployments. +The Government of Nepal's public collaboration portal. It presents one +open-source project, the open issues from its GitHub repository, and a +directory of contributors whose profiles an administrator has approved. The +site has English (`/en`) and Nepali (`/ne`) versions. -## Stack +[नेपालीमा पढ्नुहोस् →](./README.ne.md) -| Concern | Choice | -|---|---| -| 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, 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 | -| API contract | OpenAPI 3.1 in `packages/api-contract`, generated TypeScript client | -| Runtime validation | Zod 4 in `packages/shared` | -| Tests | Vitest against a real PostgreSQL database | -| Lint/format | Biome | +> **Status:** in development, before the first release. Licensed under +> [Apache-2.0](./LICENSE). -## Layout +- [Run it with Docker](#run-it-with-docker) +- [Develop locally](#develop-locally) +- [Configuration](#configuration) +- [Commands](#commands) +- [How it is built](#how-it-is-built) +- [Contributing](#contributing) -``` -apps/api/ - 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 - src/lib/ i18n dictionaries, client auth helpers - src/server/ services, repositories, authorization seam, storage, errors - src/db/ Drizzle schema + client - drizzle/ generated SQL migrations (committed) - 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 -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, plus api and migrate services -.github/workflows/ci.yml Lint, typecheck, tests, build on every PR -``` -- **Project conventions.** Branch names, review policy, contribution guidance, - and documentation standards are in [docs/CONVENTIONS.md](./docs/CONVENTIONS.md). - -## Working together (frontend + backend) - -One application, one API contract, two code territories. - -- **OpenAPI is the HTTP contract.** `packages/api-contract/openapi.yaml` defines - the public `/v1` surface. `packages/api-client/src/schema.gen.ts` is generated; - hand-editing it is forbidden. CI lints the description, regenerates the file, - and rejects drift. -- **Ownership.** Frontend work lives in `apps/api/src/app/(site)/**`, - `apps/api/src/components/**`, and `apps/api/src/lib/**` (i18n, client helpers). - Backend work lives in `apps/api/src/app/v1/**`, `apps/api/src/server/**`, and - `apps/api/src/db/**`. API changes start in `packages/api-contract`. See - `.github/CODEOWNERS`. -- **Same origin.** UI and API run in one app on one port; no CORS juggling, and - the session cookie is first-party. `WEB_ORIGIN` remains only as the allowlist - for future external clients (e.g. mobile). -- **No loopback HTTP from the server.** Server Components and Server Actions - call `src/server` services in-process. Browser components call `/v1` through - `@gov-portal/api-client`. Both paths converge on the same service layer. -- **The UI cannot bypass the backend.** Biome rejects presentation imports of - `src/db`, repositories, authentication, and environment configuration. -- **Real project data.** `bun run setup` initializes `SDOC-Team/devnepal` from - GitHub and syncs its issues. Member profiles are created only through GitHub - sign-in; the setup path does not insert fabricated accounts or activity. -- **Contract discipline.** Change OpenAPI first, regenerate the client, then - implement and test the handler. Additive changes are the default. A breaking - change needs both teams, a migration note, and a new API version. -- **Review flow.** Short-lived branches, PR into `main`, CI green + 1 review - required. Backend changes come with tests in `apps/api/tests` that name the - invariant they protect. - -### Branch workflow - -`main` is protected: no direct pushes (the owner can bypass for emergencies -only), the `verify` CI check must pass, and one review is required to merge. - -```sh -git switch main && git pull --ff-only -git switch -c feat/ -# ... work, then run the local gates before pushing -bun run lint && bun run typecheck && bun run test && bun run build -git push -u origin HEAD -gh pr create --fill -``` - -Keep branches short-lived and rebase on `main` when it moves. A PR that touches -`packages/api-contract` needs a reviewer from each side. Branches are deleted -automatically after merge. - -## Scaffolding the project - -From an empty machine to a running portal. Prerequisites: -[Bun](https://bun.sh) ≥ 1.4, Docker (colima works), Git, and the disk space for -the Postgres image (~2 GB the first time). - -### 1. Clone and bootstrap - -```sh -git clone git@github.com:SDOC-Team/devnepal.git -cd devnepal -bun run setup -``` - -`bun run setup` is idempotent and does the whole bootstrap: - -1. writes `apps/api/.env.local` from `.env.example` with a freshly generated - `AUTH_SECRET` (never overwrites an existing file) -2. starts PostgreSQL with Docker Compose and waits until it is healthy -3. installs the workspace dependencies with Bun -4. applies the Drizzle migrations -5. verifies `SDOC-Team/devnepal` through the GitHub API, initializes the project - row, and syncs its real issues +## Run it with Docker -Translations and design assets ship with the repository. +Requires Docker with Compose v2.24 or later. Nothing else runs on the host. -### 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** +1. **Create a GitHub OAuth App.** The app checks its configuration at startup + and does not start without one. At + → **New OAuth App**, use - 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. +2. **Create the configuration file** and fill in `AUTH_SECRET` + (`openssl rand -base64 48`), `AUTH_GITHUB_ID` and `AUTH_GITHUB_SECRET`: -Never commit these values; share team development credentials out-of-band. The -provider requests **`read:user` only** — email is never requested or stored. + ```sh + cp apps/api/.env.example apps/api/.env.local + ``` -### 3. Run + Compose sets its own `DATABASE_URL`, so the one in this file is ignored. +3. **Start the stack.** Postgres starts, the migrations run, then the app: -```sh -bun run dev # UI + API on one port → http://localhost:3000/en -``` + ```sh + docker compose up -d --build + ``` -### 4. Verify the scaffold +4. **Load the project and its issues from GitHub:** -| Check | Expected | -|---|---| -| `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 | 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` | + ```sh + docker compose run --rm migrate node scripts/init-project.js + ``` -### 5. Optional: test signed-in screens without repeating OAuth +Open . If ports 3000 or 5432 are already taken, set +`API_PORT` and `DB_PORT`, for example `API_PORT=13000 DB_PORT=15432 docker +compose up -d`; with a different `API_PORT`, use that port in the OAuth App's +URLs too. To refresh issues later, run +`docker compose run --rm migrate node scripts/sync-github.js`. +`docker compose down -v` removes the stack and its data. -`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: +Compose also reads a `.env` file next to `docker-compose.yml`, which is how +hosting platforms provide settings. For production, use +`prod-docker-compose.yaml`; [docs/deployment.md](docs/deployment.md) covers +deploying it on Dokploy. -```sh -bun run dev:session -``` +## Develop locally -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 verification matrix. +Requires [Bun](https://bun.sh) 1.4 or later and Docker (for Postgres). -### 6. Refresh GitHub issues +1. **Bootstrap.** `bun run setup` writes `apps/api/.env.local` with a generated + `AUTH_SECRET` (it never overwrites an existing file), starts Postgres, + installs dependencies, applies the migrations, and loads the project and its + issues from GitHub. It is safe to rerun. -Issues are reconciled from the project's public repository over the REST API -(no webhook infrastructure needed yet): + ```sh + git clone git@github.com:SDOC-Team/devnepal.git + cd devnepal + bun run setup + ``` -```sh -bun run sync:github # reconciles issues for the configured active project -``` +2. **Add the GitHub OAuth App credentials** to `apps/api/.env.local` + (`AUTH_GITHUB_ID`, `AUTH_GITHUB_SECRET`; see step 1 of the Docker + instructions). For the admin screens, add your numeric GitHub ID + (`https://api.github.com/users/` → `id`) to `ADMIN_GITHUB_IDS`. +3. **Run it:** -Set `GITHUB_TOKEN` to lift the anonymous rate limit. GitHub remains the source -of truth; the portal does not insert placeholder issues when the repository has -none. + ```sh + bun run dev # http://localhost:3000/en + ``` -A signature-verified webhook endpoint (`POST /webhooks/github`) is implemented -and tested — deliveries are deduplicated in an event ledger and applied to the -stored issues. Wiring live delivery (repo webhook or `gh webhook forward`) is -optional and deferred; reconciliation with `sync:github` is the reliable path -until then. Contribution indexing is a later phase. +4. **Check it:** -### Ports + | Check | Expected | + |---|---| + | `curl localhost:3000/health` | `{"status":"ok"}` | + | , | the home page in each language | + | | the repository's open issues, with label filter and search | + | | approved members only (empty until someone signs in and is approved) | + | `bun run test` | the whole suite passes | -| Service | URL | -|---|---| -| UI + API | http://localhost:3000 (`/en`, `/ne`) | -| PostgreSQL | localhost:5432 — user `refined`, password `refined`, databases `refined` / `refined_test` | +The setup creates no member accounts. 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 signed-in screens without going through +GitHub each time. See [docs/frontend.md](docs/frontend.md). -### Troubleshooting +**Troubleshooting** -- `docker compose` fails → Docker isn't running: `colima start` (or start Docker Desktop). -- Port 3000 already in use → `lsof -ti :3000 | xargs kill` -- Port 5432 already in use → stop the other Postgres, or change the `db` port - mapping in `compose.yaml` and `DATABASE_URL` in `apps/api/.env.local` together. -- Reset all local data → `docker compose down -v && bun run setup` +- `docker compose` fails: Docker isn't running (`colima start`, or start Docker + Desktop). +- Port 5432 or 3000 is taken: set `DB_PORT` / `API_PORT`, and change + `DATABASE_URL` in `apps/api/.env.local` to the new database port. +- Start again from nothing: `docker compose down -v && bun run setup`. -### Environment variables +## Configuration + +All settings are environment variables, read from `apps/api/.env.local`. +[`apps/api/.env.example`](apps/api/.env.example) lists every one with a +comment; `.env*` files other than the example are never committed. | Variable | Required | Purpose | |---|---|---| | `DATABASE_URL` | yes | PostgreSQL connection string | -| `AUTH_SECRET` | yes | Auth.js session encryption, ≥ 32 chars | -| `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` | 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` | -| `GITHUB_WEBHOOK_SECRET` | optional | HMAC secret for `POST /webhooks/github` deliveries (endpoint is inert without it) | -| `TEST_DATABASE_URL` | tests | Separate database used by the test suite | - -`.env*` files are gitignored; `.env.example` is the only committed reference. +| `AUTH_SECRET` | yes | Session encryption, at least 32 characters | +| `AUTH_GITHUB_ID` | yes | GitHub OAuth App client ID | +| `AUTH_GITHUB_SECRET` | yes | GitHub OAuth App client secret | +| `ADMIN_GITHUB_IDS` | no | Comma-separated numeric GitHub IDs allowed to moderate members | +| `GITHUB_PROJECT_REPOSITORY` | no | Repository `db:init` loads; defaults to `SDOC-Team/devnepal` | +| `GITHUB_TOKEN` | no | Raises the GitHub API rate limit for `db:init` and `sync:github` | +| `STORAGE_DIR` | no | Where avatars are stored; defaults to `./storage` (a volume in Docker) | +| `WEB_ORIGIN` | no | Extra origin allowed to call the API with credentials; the UI itself is same-origin | +| `GITHUB_WEBHOOK_SECRET` | no | Enables `POST /webhooks/github`; the endpoint is inert without it | +| `TEST_DATABASE_URL` | no | Database for the test suite | ## Commands ```sh -bun run setup # one-command local bootstrap (safe to rerun) -bun run dev # UI + API at http://localhost:3000 +bun run setup # local bootstrap (safe to rerun) +bun run dev # development server at http://localhost:3000 bun run build # production build (standalone output) -bun run start # production server -bun run test # Vitest (+ creates and migrates the test database) -bun run typecheck # tsc --noEmit -bun run lint # Biome check -bun run format # Biome format --write -bun run api:lint # lint packages/api-contract/openapi.yaml -bun run api:generate # regenerate and format the typed client -bun run api:check # lint + generate + fail if generated types drift -bun run db:generate # generate a migration from the Drizzle schema +bun run start # serve the production build +bun run test # test suite (creates and migrates the test database) +bun run typecheck # TypeScript +bun run lint # Biome +bun run format # Biome formatter +bun run api:check # lint the OpenAPI contract and fail if the client is out of date +bun run db:generate # create a migration from the schema bun run db:migrate # apply migrations to DATABASE_URL -bun run db:init # initialize SDOC-Team/devnepal and sync its GitHub issues -bun run sync:github # reconcile issues from the project's GitHub repository -bun run dev:session # mint a dev session cookie for an existing member +bun run db:init # load the project and its issues from GitHub +bun run sync:github # refresh issues from GitHub +bun run dev:session # print a session cookie for an existing member ``` -## REST API - -Canonical paths are versioned and have **no trailing slash** (`/v1/members`, -not `/v1/members/`). The old unversioned product routes are compatibility -aliases during migration; new code must not use them. -Success bodies are `{ "member": ... }` / `{ "members": [...] }` / -`{ "project": ... }`; errors are `{ "error": { "code", "message", "details"? } }`. -UI pages live under `/en` and `/ne` and do not collide with these paths. - -| Method | Path | Access | Notes | -|---|---|---|---| -| `GET` | `/health` | public | 200 when the database answers | -| `GET` | `/v1/project` | public | The single project + open issue / member counts | -| `GET` | `/v1/project/issues?label=&q=&page=&perPage=` | public | Open issues, newest first | -| `GET` | `/v1/project/issues/labels` | public | Open-issue label counts | -| `GET` | `/v1/project/issues/{number}` | public | Single issue | -| `GET` | `/v1/members` | public | Approved members only, `priority DESC`, `approvedAt DESC` | -| `GET` | `/v1/members/{githubUsername}` | public / owner | Case-insensitive username lookup | -| `GET` | `/v1/members/id/{githubId}` | public / owner | Same member, numeric GitHub ID | -| `GET` | `/avatars/{key}` | public | Stored avatar bytes, immutable cache headers | -| `GET` | `/v1/profile` | authenticated | Own member incl. `id`, `status`, `approvedAt`, plus `isAdmin` | -| `PATCH` | `/v1/profile` | authenticated | Updates the session member only | -| `GET` | `/v1/admin/members?status=` | admin | Moderation queue, optional status filter | -| `PATCH` | `/v1/admin/members/{id}` | admin | `{ status?, priority? }` | -| `GET/POST` | `/api/auth/*` | public | Auth.js endpoints (sign-in, callback, session, sign-out) | - -### Visibility rules - -- `approved` → public on both member routes. -- `pending`, `rejected`, `hidden` → `404` to everyone except the member - themselves (who sees their own profile while signed in). -- Public payloads never include the internal UUID, moderation status, priority, - or approval fields. - -### Profile updates - -`PATCH /v1/profile` accepts only: `displayName`, `headline`, `affiliation`, -`location`, `bio`, `links`, `skills`. The payload is strict — any other key -(`id`, `githubId`, `githubUsername`, `status`, `priority`, `avatarPath`, …) is -rejected with `400`. The update target is always the authenticated member. - -Validation highlights: display name required ≤ 80 chars, no URLs, no control -characters; bio ≤ 400 chars; links ≤ 5, HTTPS only, unique; skills from the -fixed taxonomy in `packages/shared/src/skills.ts`. - -## UI - -Server-rendered pages under `/en` and `/ne` (English default; `/` redirects to -`/en`): home, project, issues list with label/search filters, issue detail with -sanitized Markdown, member directory, member profiles, own profile editor, -admin moderation, and a how-to-contribute page. - -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 -`apps/api/src/components/ui`. Translations live in `apps/api/src/lib/i18n.ts`. - -## Security invariants (tested) - -- **Self-write only** — profile writes resolve the target exclusively from the - session; there is no route or payload that can target another member. -- **Immutables** — UUID, GitHub ID, GitHub username, avatar path, moderation - fields, and priority are not writable through `/v1/profile`. -- **Admin is env-only** — `ADMIN_GITHUB_IDS`, re-checked per request and in the - server-rendered admin page. -- **No email** — not requested (OAuth scope `read:user`), no column, never returned. -- **Avatars** — downloaded to `STORAGE_DIR`, capped at 2 MB, magic-byte checked - (png/jpeg/webp only), stored under a generated UUID key; GitHub URLs are never - stored or served. If the download fails the member is still created and the - avatar self-heals on the next login. -- **Issue content** — bodies are stored as-is and rendered through - `react-markdown` + `rehype-sanitize`; raw HTML is never injected. -- **CSRF** — Auth.js protects its own routes; cookie-authenticated mutations - additionally reject any request whose `Origin` is neither the app's own origin - (same-origin UI, proxied or not) nor `WEB_ORIGIN`. -- **Rate limits** — auth routes, the webhook endpoint, and write endpoints are - limited per client IP (`429` with `Retry-After`). The counters are in-memory, - which is correct for the single-instance deployment; a shared store would be - needed before scaling out. -- **Secrets** — only placeholders are committed; `.env*` is gitignored. - -## Tests - -```sh -bun run test -``` +## How it is built -The suite creates `refined_test` if needed, applies migrations, and runs tests -covering: member write isolation and injection attempts, all visibility -states on both member routes, admin approve/reject/hide/priority and non-admin -denials, directory ordering, profile validation, avatar handling, the absence of -an email column, project/issue endpoints and filters, GitHub sync reconciliation -with mocked HTTP, and Auth.js CORS handling. Override the database with -`TEST_DATABASE_URL`. +One Next.js 16 application serves both the pages (`/en`, `/ne`) and a versioned +JSON API (`/v1/...`), backed by PostgreSQL 17 through Drizzle ORM. Sign-in is +GitHub OAuth through Auth.js. The UI uses React 19, Tailwind CSS 4 with +shadcn/ui components on Base UI, and SWR. Tests run on Vitest against a real +database; Biome lints and formats. -## Deployment +``` +apps/api/ + src/app/(site)/[locale]/ pages: home, project, issues, members, profile, welcome, admin, about + src/app/v1/ API route handlers + src/components/ UI components + src/lib/ translations (i18n.ts) and client helpers + src/server/ services, repositories, authorization, storage + src/db/ schema and database client + drizzle/ generated SQL migrations + src/scripts/ project init, GitHub sync, dev session + tests/ unit and integration tests +packages/api-contract/ OpenAPI description of the API +packages/api-client/ generated API types and browser client +packages/shared/ validation schemas and shared types +docs/ frontend guide and deployment notes +``` -See [`docs/deployment.md`](docs/deployment.md) for the checklist, known -problems and their fixes, and backups. +**The API contract** is +[`packages/api-contract/openapi.yaml`](packages/api-contract/openapi.yaml). +Change it first, regenerate the client with `bun run api:generate`, then +implement the handler; CI fails if the generated client is out of date. + +**Issues** come from the project's public GitHub repository. `db:init` and +`sync:github` reconcile them; GitHub stays the source of truth. + +**Member profiles** are created at first GitHub sign-in as `pending`. Only +`approved` profiles are public. For a pending, rejected or hidden profile the +API returns `404` to everyone except the member, and the page shows "Profile not +available". Public responses never include the internal id, moderation status, +priority or approval details. + +**Privacy and security**, each covered by tests: + +- Sign-in requests the `read:user` scope only. Email addresses are never + requested, stored, or kept in the session. +- A member can only change their own profile, and only its editable fields. + Administrators are the GitHub IDs in `ADMIN_GITHUB_IDS`, checked on every + request. +- Avatars are downloaded once, checked to be real PNG, JPEG or WebP images of + at most 2 MB, and served from the portal. Pages never load an avatar from + GitHub. +- Issue text is rendered as sanitised Markdown; raw HTML is never injected. +- Changes made with a session cookie are rejected from other origins, sign-in + and write routes are rate-limited per client, and every response carries + baseline security headers. + +## Contributing + +Start with [CONTRIBUTING.md](CONTRIBUTING.md): how to pick up an issue (only +those labelled `ready`), work on a fork, and sign off every commit with +`git commit -s`. Branch names, commit types, labels and what makes a pull +request done are in [docs/CONVENTIONS.md](docs/CONVENTIONS.md). + +A pull request needs passing CI (lint, typecheck, tests, build, API contract +check) and one approving review. Before pushing, run +`bun run lint && bun run typecheck && bun run test && bun run build`. + +- [Code of conduct](CODE_OF_CONDUCT.md) · [Governance](GOVERNANCE.md) · + [Maintainers](MAINTAINERS.md) +- Security problems: never in a public issue. See [SECURITY.md](SECURITY.md). +- Product requirements: [docs/PRD-v0.1.md](docs/PRD-v0.1.md) +- Privacy: [PRIVACY.md](PRIVACY.md) +- Deployment: [docs/deployment.md](docs/deployment.md) diff --git a/README.ne.md b/README.ne.md index 65924a5..599120a 100644 --- a/README.ne.md +++ b/README.ne.md @@ -1,98 +1,226 @@ # devNepal -**सरकारी प्रविधि — सार्वजनिक रूपमा, सार्वजनिक योगदानसहित निर्मित।** - -प्रधानमन्त्री तथा मन्त्रिपरिषद्को कार्यालय, नेपाल सरकार - · [Read in English →](./README.md) - -> **हालको चरण: दस्तावेजीकरण र योजना।** - ---- - -## यो के हो - -devNepal मा नेपाल सरकारले प्रविधिसम्बन्धी परियोजनाहरू प्रकाशित गर्छ, र जो कोहीले तिनमा योगदान गर्न सक्छन्। सबै काम सार्वजनिक रिपोजिटरीमा हुन्छ। यो रिपोजिटरी devNepal का लागि हो। - -**devNepal ले बनाउँदै गरेको पहिलो परियोजना devNepal आफैँ हो — पहिलो कमिटदेखि नै सार्वजनिक रूपमा।** - ---- - -## यहाँबाट सुरु गर्नुहोस् - -| | | -|---|---| -| **[खुला इस्युहरू](../../issues)** | `ready` र `good-first-issue` दुवै लेबल भएका कामबाट सुरु गर्नुहोस् | -| **[CONTRIBUTING.md](./CONTRIBUTING.md)** | हामी कसरी काम गर्छौं, र कति समयमा जवाफ दिन्छौं | -| **[docs/CONVENTIONS.md](./docs/CONVENTIONS.md)** | ब्रान्चको नाम, कमिट सन्देश, लेबल, र काम पूरा भएको मानक | -| **[CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md)** | आचरणसम्बन्धी अपेक्षा | -| **[SECURITY.md](./SECURITY.md)** | सुरक्षा समस्या निजी रूपमा जनाउनुहोस् — सार्वजनिक इस्युमा कहिल्यै नलेख्नुहोस् | -| **[GOVERNANCE.md](./GOVERNANCE.md)** | कसले के निर्णय गर्छ, र समीक्षा अधिकार कसले राख्छ | -| **[MAINTAINERS.md](./MAINTAINERS.md)** | कुन क्षेत्रमा कसलाई सोध्ने | -| **[PRIVACY.md — अङ्ग्रेजी मस्यौदा](./PRIVACY.md)** | सदस्य खाताका लागि गोपनीयता सूचनाको मस्यौदा | - -**योगदानका लागि devNepal खाता चाहिँदैन।** योगदान GitHub मा हुन्छ। devNepal प्रोफाइल ऐच्छिक हो। - ---- - -## योगदान कोडमा मात्र सीमित छैन - -डिजाइन, नेपाली अनुवाद, दस्तावेजीकरण, परीक्षण, पहुँचयोग्यता र सुरक्षासम्बन्धी कामको समान रूपमा समीक्षा गरिन्छ र श्रेय दिइन्छ। क्षेत्रगत सम्पर्कका लागि [MAINTAINERS.md](./MAINTAINERS.md) हेर्नुहोस्। यो सूचीमा नपरेको क्षेत्रमा योगदान गर्न चाहनुहुन्छ भने इस्यु खोलेर सोध्नुहोस्। - ---- - -## स्थानीय रूपमा काम गर्ने तरिका - -```bash -git clone https://github.com/SDOC-Team/devnepal.git -cd devnepal +प्रधानमन्त्री तथा मन्त्रिपरिषद्को कार्यालय, नेपाल सरकार · + + +नेपाल सरकारको सार्वजनिक सहकार्य पोर्टल। यसले एउटा खुला स्रोत (open-source) +परियोजना, त्यसको GitHub रिपोजिटरीका खुला इस्युहरू, र प्रशासकले स्वीकृत गरेका +योगदानकर्ताहरूको निर्देशिका देखाउँछ। साइटको अङ्ग्रेजी (`/en`) र नेपाली (`/ne`) +संस्करण छन्। + +[Read in English →](./README.md) + +> **अवस्था:** विकासको चरणमा, पहिलो रिलिजअघि। [Apache-2.0](./LICENSE) अन्तर्गत +> इजाजतपत्र दिइएको। + +- [Docker बाट चलाउनुहोस्](#docker-बाट-चलाउनुहोस्) +- [आफ्नै कम्प्युटरमा विकास](#आफ्नै-कम्प्युटरमा-विकास) +- [कन्फिगरेसन](#कन्फिगरेसन) +- [कमान्डहरू](#कमान्डहरू) +- [यो कसरी बनेको छ](#यो-कसरी-बनेको-छ) +- [योगदान](#योगदान) + +## Docker बाट चलाउनुहोस् + +Compose v2.24 वा त्योभन्दा नयाँ भएको Docker चाहिन्छ। होस्टमा अरू केही चलाउनु +पर्दैन। + +1. **GitHub OAuth App बनाउनुहोस्।** एप सुरु हुँदा आफ्नो कन्फिगरेसन जाँच्छ र + यसबिना सुरु हुँदैन। → **New OAuth + App** मा: + - Homepage URL: `http://localhost:3000` + - Authorization callback URL: `http://localhost:3000/api/auth/callback/github` +2. **कन्फिगरेसन फाइल बनाउनुहोस्** र `AUTH_SECRET` (`openssl rand -base64 48`), + `AUTH_GITHUB_ID` र `AUTH_GITHUB_SECRET` भर्नुहोस्: + + ```sh + cp apps/api/.env.example apps/api/.env.local + ``` + + Compose ले आफ्नै `DATABASE_URL` राख्छ, त्यसैले यो फाइलमा भएको मान प्रयोग + हुँदैन। +3. **स्ट्याक सुरु गर्नुहोस्।** पहिले Postgres सुरु हुन्छ, माइग्रेसन चल्छ, अनि + एप: + + ```sh + docker compose up -d --build + ``` + +4. **GitHub बाट परियोजना र त्यसका इस्युहरू ल्याउनुहोस्:** + + ```sh + docker compose run --rm migrate node scripts/init-project.js + ``` + + खोल्नुहोस्। पोर्ट 3000 वा 5432 पहिल्यै प्रयोगमा छ भने +`API_PORT` र `DB_PORT` राख्नुहोस्, जस्तै `API_PORT=13000 DB_PORT=15432 docker +compose up -d`; `API_PORT` फरक भए OAuth App का URL मा पनि त्यही पोर्ट राख्नुहोस्। +पछि इस्युहरू ताजा गर्न `docker compose run --rm migrate node scripts/sync-github.js` +चलाउनुहोस्। `docker compose down -v` ले स्ट्याक र त्यसको डाटा हटाउँछ। + +Compose ले `docker-compose.yml` सँगैको `.env` फाइल पनि पढ्छ; होस्टिङ प्लेटफर्महरूले +सेटिङ यसैगरी दिन्छन्। उत्पादन (production) का लागि `prod-docker-compose.yaml` +प्रयोग गर्नुहोस्; Dokploy मा डिप्लोय गर्ने तरिका +[docs/deployment.md](docs/deployment.md) मा छ। + +## आफ्नै कम्प्युटरमा विकास + +[Bun](https://bun.sh) 1.4 वा नयाँ र Docker (Postgres का लागि) चाहिन्छ। + +1. **सुरुआती सेटअप।** `bun run setup` ले नयाँ `AUTH_SECRET` सहित + `apps/api/.env.local` लेख्छ (भइरहेको फाइल कहिल्यै मेटाउँदैन), Postgres सुरु + गर्छ, निर्भरता (dependencies) इन्स्टल गर्छ, माइग्रेसन लागू गर्छ, र GitHub बाट + परियोजना र इस्युहरू ल्याउँछ। यसलाई फेरि चलाउँदा केही बिग्रँदैन। + + ```sh + git clone git@github.com:SDOC-Team/devnepal.git + cd devnepal + bun run setup + ``` + +2. **GitHub OAuth App का क्रेडेन्सियल** `apps/api/.env.local` मा थप्नुहोस् + (`AUTH_GITHUB_ID`, `AUTH_GITHUB_SECRET`; Docker निर्देशनको पहिलो चरण + हेर्नुहोस्)। प्रशासन पृष्ठका लागि आफ्नो GitHub को अङ्कमा भएको ID + (`https://api.github.com/users/` → `id`) `ADMIN_GITHUB_IDS` मा थप्नुहोस्। +3. **चलाउनुहोस्:** + + ```sh + bun run dev # http://localhost:3000/ne + ``` + +4. **जाँच्नुहोस्:** + + | जाँच | अपेक्षित नतिजा | + |---|---| + | `curl localhost:3000/health` | `{"status":"ok"}` | + | , | दुवै भाषामा गृहपृष्ठ | + | | रिपोजिटरीका खुला इस्युहरू, लेबल फिल्टर र खोजसहित | + | | स्वीकृत सदस्य मात्र (कसैले साइन इन गरेर स्वीकृत नभएसम्म खाली) | + | `bun run test` | सबै टेस्ट पास हुन्छन् | + +सेटअपले कुनै सदस्य खाता बनाउँदैन। आफ्नो खाता बनाउन एकपटक GitHub बाट साइन इन +गर्नुहोस्; त्यसपछि `bun run dev:session ` ले त्यो +सदस्यको सेसन कुकी देखाउँछ, जसले गर्दा हरेकपटक GitHub बाट साइन इन नगरी साइन इन +भएपछिका पृष्ठहरू जाँच्न सकिन्छ। [docs/frontend.md](docs/frontend.md) हेर्नुहोस्। + +**समस्या समाधान** + +- `docker compose` चल्दैन: Docker चलिरहेको छैन (`colima start`, वा Docker + Desktop सुरु गर्नुहोस्)। +- पोर्ट 5432 वा 3000 प्रयोगमा छ: `DB_PORT` / `API_PORT` राख्नुहोस्, र + `apps/api/.env.local` को `DATABASE_URL` मा नयाँ डाटाबेस पोर्ट राख्नुहोस्। +- सुरुदेखि फेरि गर्न: `docker compose down -v && bun run setup`। + +## कन्फिगरेसन + +सबै सेटिङ एनभाइरनमेन्ट भेरिएबल (environment variable) हुन्, जुन +`apps/api/.env.local` बाट पढिन्छन्। [`apps/api/.env.example`](apps/api/.env.example) +मा हरेकको टिप्पणीसहित सूची छ; उदाहरण फाइलबाहेक कुनै `.env*` फाइल कहिल्यै +कमिट गरिँदैन। + +| भेरिएबल | अनिवार्य | प्रयोजन | +|---|---|---| +| `DATABASE_URL` | हो | PostgreSQL जडान स्ट्रिङ | +| `AUTH_SECRET` | हो | सेसन इन्क्रिप्सन, कम्तीमा ३२ अक्षर | +| `AUTH_GITHUB_ID` | हो | GitHub OAuth App को client ID | +| `AUTH_GITHUB_SECRET` | हो | GitHub OAuth App को client secret | +| `ADMIN_GITHUB_IDS` | होइन | सदस्य मोडरेट गर्न पाउने GitHub ID हरू, अल्पविरामले छुट्याएर | +| `GITHUB_PROJECT_REPOSITORY` | होइन | `db:init` ले ल्याउने रिपोजिटरी; पूर्वनिर्धारित `SDOC-Team/devnepal` | +| `GITHUB_TOKEN` | होइन | `db:init` र `sync:github` का लागि GitHub API को सीमा बढाउँछ | +| `STORAGE_DIR` | होइन | अवतार राखिने ठाउँ; पूर्वनिर्धारित `./storage` (Docker मा भोल्युम) | +| `WEB_ORIGIN` | होइन | क्रेडेन्सियलसहित API बोलाउन पाउने थप origin; UI आफैँ उही origin मा छ | +| `GITHUB_WEBHOOK_SECRET` | होइन | `POST /webhooks/github` सक्रिय गर्छ; यसबिना उक्त endpoint निष्क्रिय रहन्छ | +| `TEST_DATABASE_URL` | होइन | टेस्टहरूका लागि डाटाबेस | + +## कमान्डहरू + +```sh +bun run setup # local bootstrap (safe to rerun) +bun run dev # development server at http://localhost:3000 +bun run build # production build (standalone output) +bun run start # serve the production build +bun run test # test suite (creates and migrates the test database) +bun run typecheck # TypeScript +bun run lint # Biome +bun run format # Biome formatter +bun run api:check # lint the OpenAPI contract and fail if the client is out of date +bun run db:generate # create a migration from the schema +bun run db:migrate # apply migrations to DATABASE_URL +bun run db:init # load the project and its issues from GitHub +bun run sync:github # refresh issues from GitHub +bun run dev:session # print a session cookie for an existing member ``` -पुल रिक्वेस्ट पठाउन [CONTRIBUTING.md](./CONTRIBUTING.md) हेर्नुहोस्। चलाउन मिल्ने एप्लिकेसन अझै छैन। +## यो कसरी बनेको छ ---- +एउटै Next.js 16 एप्लिकेसनले पृष्ठहरू (`/en`, `/ne`) र संस्करणसहितको JSON API +(`/v1/...`) दुवै दिन्छ, र Drizzle ORM मार्फत PostgreSQL 17 प्रयोग गर्छ। साइन इन +Auth.js मार्फत GitHub OAuth बाट हुन्छ। UI मा React 19, Base UI माथिका shadcn/ui +कम्पोनेन्टसहित Tailwind CSS 4, र SWR प्रयोग भएका छन्। टेस्टहरू Vitest मा +वास्तविक डाटाबेसविरुद्ध चल्छन्; Biome ले लिन्ट र फर्म्याट गर्छ। -## कहाँ के छ - -| पथ | हालको सामग्री | -|---|---| -| `docs/` | कार्यपरम्परा, उत्पादनका आवश्यकता र तयारीको प्रगति | -| `.github/` | इस्यु र पुल रिक्वेस्ट टेम्प्लेट | - -### योजनामा रहेको एप्लिकेसन संरचना - -| पथ | योजनामा रहेको सामग्री | -|---|---| -| `src/` | एप्लिकेसन | -| `ui/tokens/src/` | डिजाइन टोकन — रङ, स्पेसिङ, टाइप | -| `ui/css/` | स्टाइलसिट र साझा प्याटर्न | -| `locale/` | अङ्ग्रेजी र नेपालीका प्रयोगकर्ता-मुखी स्ट्रिङ | - -`ui/tokens/dist/` र `ui/css/dist/` स्रोतबाट पुनःनिर्माण गर्नुपर्छ र Git ले तिनलाई बेवास्ता गर्छ। - ---- - -## एप्लिकेसनका आवश्यकता - -- **अङ्ग्रेजी र नेपाली दुवैमा।** प्रकाशित प्रत्येक पृष्ठ दुवै भाषामा हुनुपर्छ। देवनागरीका लागि नेपालीको आफ्नै लाइन-हाइट चाहिन्छ -- **डिजाइन टोकन।** एप्लिकेसनको शैलीमा अर्थअनुसारका टोकन प्रयोग गर्नुपर्छ; रङ वा स्पेसिङका मान सिधै कोडमा नराख्नुहोस् -- **पहुँचयोग्यता।** किबोर्डबाट चल्ने, देखिने फोकस र नापिएको कन्ट्रास्ट v0.1 का आवश्यकता हुन्। पूर्ण पहुँचयोग्यता परीक्षण पछिको संस्करणका लागि स्थगित गरिएको छ -- **सबैका लागि उही जाँच।** समीक्षा आवश्यकता कोर टोली र बाह्य योगदानकर्ता दुवैलाई समान रूपमा लागू हुन्छ - ---- - -## के-के अहिले बनाइरहेका छैनौँ - -- सदस्य ब्लग र समुदाय-स्वामित्वका परियोजना सूची -- सार्वजनिक योगदान लिडरबोर्ड, क्रम वा अङ्क -- भत्ता वा बाउन्टी -- मन्त्रालयको आफ्नै प्रकाशन प्रणाली - -प्रत्येक कारणसहित स्थगित गरिएको हो, र आउँदा सूचनासहित आउनेछ। - ---- - -## अनुमतिपत्र र सञ्चालन - -[Apache License 2.0](./LICENSE)। जुनसुकै व्यक्ति वा कम्पनीले यो कोड प्रयोग, परिमार्जन र व्यावसायिक रूपमा उपयोग गर्न सक्छन्। **तपाईंको योगदानको प्रतिलिपि अधिकार तपाईंसँगै रहन्छ।** +``` +apps/api/ + src/app/(site)/[locale]/ pages: home, project, issues, members, profile, welcome, admin, about + src/app/v1/ API route handlers + src/components/ UI components + src/lib/ translations (i18n.ts) and client helpers + src/server/ services, repositories, authorization, storage + src/db/ schema and database client + drizzle/ generated SQL migrations + src/scripts/ project init, GitHub sync, dev session + tests/ unit and integration tests +packages/api-contract/ OpenAPI description of the API +packages/api-client/ generated API types and browser client +packages/shared/ validation schemas and shared types +docs/ frontend guide and deployment notes +``` -प्रधानमन्त्री तथा मन्त्रिपरिषद्को कार्यालयद्वारा सञ्चालित। मेन्टेनर सरकार बाहिरका पनि हुन सक्छन्। **मर्ज र डिप्लोयमेन्ट सरकारी टोलीले मात्र गर्छ।** +**API सम्झौता (contract)** +[`packages/api-contract/openapi.yaml`](packages/api-contract/openapi.yaml) हो। +पहिले यसलाई बदल्नुहोस्, `bun run api:generate` ले क्लाइन्ट फेरि बनाउनुहोस्, अनि +handler लेख्नुहोस्; बनाइएको क्लाइन्ट पुरानो भए CI असफल हुन्छ। + +**इस्युहरू** परियोजनाको सार्वजनिक GitHub रिपोजिटरीबाट आउँछन्। `db:init` र +`sync:github` ले तिनलाई मिलाउँछन्; सत्यको स्रोत GitHub नै रहन्छ। + +**सदस्य प्रोफाइल** पहिलोपटक GitHub बाट साइन इन गर्दा `pending` अवस्थामा बन्छ। +`approved` प्रोफाइल मात्र सार्वजनिक हुन्छन्। pending, rejected वा hidden +प्रोफाइलका लागि सदस्य आफूबाहेक सबैलाई API ले `404` फर्काउँछ, र पृष्ठमा "प्रोफाइल +उपलब्ध छैन" देखिन्छ। सार्वजनिक जवाफमा आन्तरिक id, मोडरेसन अवस्था, प्राथमिकता वा +स्वीकृतिको विवरण कहिल्यै हुँदैन। + +**गोपनीयता र सुरक्षा**, प्रत्येकको टेस्टसहित: + +- साइन इनले `read:user` स्कोप मात्र माग्छ। इमेल ठेगाना कहिल्यै मागिँदैन, राखिँदैन, + वा सेसनमा रहँदैन। +- सदस्यले आफ्नो प्रोफाइल मात्र, र त्यसका सम्पादन गर्न मिल्ने फिल्ड मात्र बदल्न + सक्छन्। प्रशासक `ADMIN_GITHUB_IDS` मा भएका GitHub ID हुन्, जुन हरेक अनुरोधमा + जाँचिन्छ। +- अवतार एकपटक डाउनलोड गरिन्छ, बढीमा २ MB का वास्तविक PNG, JPEG वा WebP तस्बिर + हुन् भनी जाँचिन्छ, र पोर्टलबाटै दिइन्छ। पृष्ठहरूले GitHub बाट अवतार कहिल्यै + लोड गर्दैनन्। +- इस्युको पाठ सुरक्षित (sanitised) Markdown का रूपमा देखाइन्छ; कच्चा HTML कहिल्यै + राखिँदैन। +- सेसन कुकीसहित गरिएका परिवर्तन अर्को origin बाट आए अस्वीकार हुन्छन्, साइन इन र + लेख्ने रुटहरूमा प्रति-क्लाइन्ट दर सीमा (rate limit) छ, र हरेक जवाफमा आधारभूत + सुरक्षा हेडरहरू हुन्छन्। + +## योगदान + +[CONTRIBUTING.md](CONTRIBUTING.md) बाट सुरु गर्नुहोस्: इस्यु कसरी लिने (`ready` +लेबल भएका मात्र), fork मा कसरी काम गर्ने, र हरेक कमिटमा `git commit -s` ले कसरी +sign-off गर्ने। ब्रान्चका नाम, कमिटका प्रकार, लेबल, र पुल रिक्वेस्ट कहिले पूरा +मानिन्छ भन्ने कुरा [docs/CONVENTIONS.md](docs/CONVENTIONS.md) मा छन्। + +पुल रिक्वेस्टका लागि CI (lint, typecheck, टेस्ट, build, API सम्झौता जाँच) पास +हुनुपर्छ र एउटा स्वीकृति (approving review) चाहिन्छ। push गर्नुअघि +`bun run lint && bun run typecheck && bun run test && bun run build` चलाउनुहोस्। + +- [आचारसंहिता](CODE_OF_CONDUCT.md) · [सुशासन](GOVERNANCE.md) · + [मेन्टेनरहरू](MAINTAINERS.md) +- सुरक्षा समस्या: सार्वजनिक इस्युमा कहिल्यै नराख्नुहोस्। [SECURITY.md](SECURITY.md) + हेर्नुहोस्। +- उत्पादनका आवश्यकता: [docs/PRD-v0.1.md](docs/PRD-v0.1.md) +- गोपनीयता: [PRIVACY.md](PRIVACY.md) +- डिप्लोयमेन्ट: [docs/deployment.md](docs/deployment.md)