No description
  • TypeScript 98%
  • PLpgSQL 0.9%
  • JavaScript 0.7%
  • Dockerfile 0.4%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-28 20:41:28 +01:00
.forgejo/workflows feat(openapi): commit openapi.json, generated by pnpm openapi:generate 2026-10-15 20:11:30 +02:00
drizzle fix(security): upgrade better-auth to 1.7.14 2026-10-28 20:31:45 +01:00
src fix(security): upgrade better-auth to 1.7.14 2026-10-28 20:31:45 +01:00
test Merge main into develop: sync support on the 1.0.0 refactors 2026-10-16 17:27:32 +02:00
.dockerignore ci: add nest build, changelog/front version check, secret scan and image build 2026-10-14 20:42:44 +02:00
.env.example feat: password reset, email change, per-owner names and full audit pass 2026-10-01 22:41:14 +02:00
.fallowrc.json fix(security): patch HIGH/CRITICAL image vulnerabilities 2026-10-26 21:33:30 +01:00
.gitignore chore: simplify codebase reuse, quality, and efficiency (#35) 2026-05-07 16:04:27 +02:00
.gitleaksignore ci: add nest build, changelog/front version check, secret scan and image build 2026-10-14 20:42:44 +02:00
.nvmrc feat: password reset, email change, per-owner names and full audit pass 2026-10-01 22:41:14 +02:00
.pnpmfile.cjs fix(security): patch HIGH/CRITICAL image vulnerabilities 2026-10-26 21:33:30 +01:00
.prettierrc Init repo 2026-03-18 19:59:00 +01:00
CLAUDE.md Merge main into develop: sync support on the 1.0.0 refactors 2026-10-16 17:27:32 +02:00
compose.yml chore(docker): harden the compose services and pin postgres 18.6 2026-10-14 20:42:44 +02:00
dev.compose.yml chore(docker): harden the compose services and pin postgres 18.6 2026-10-14 20:42:44 +02:00
Dockerfile fix(security): patch HIGH/CRITICAL image vulnerabilities 2026-10-26 21:33:30 +01:00
drizzle.config.ts Fixed schema and added migration 2026-03-20 14:13:28 +01:00
eslint.config.mjs build(ts): enable strict mode and lint explicit any 2026-10-15 19:54:23 +02:00
jest.int.config.cjs test: silence pg pool teardown noise so test:int exits 0 on green 2026-05-07 22:27:59 +02:00
jest.unit.config.cjs refactor(config): read a typed, frozen env instead of process.env 2026-10-15 18:40:24 +02:00
LICENSE Replace LICENSE with verbatim FSF AGPL-3.0 text 2026-05-30 20:49:54 +02:00
nest-cli.json feat(openapi): describe every response with the swagger plugin and DTOs 2026-10-15 20:11:24 +02:00
openapi.json chore(openapi): regenerate for 1.1.2 2026-10-24 21:15:46 +02:00
package.json fix(security): upgrade better-auth to 1.7.14 2026-10-28 20:31:45 +01:00
pnpm-lock.yaml fix(security): upgrade better-auth to 1.7.14 2026-10-28 20:31:45 +01:00
pnpm-workspace.yaml fix(security): patch HIGH/CRITICAL image vulnerabilities 2026-10-26 21:33:30 +01:00
README.md Merge origin/develop into fix/code-review 2026-10-15 20:41:22 +02:00
tsconfig.build.json Init repo 2026-03-18 19:59:00 +01:00
tsconfig.json build(ts): enable strict mode and lint explicit any 2026-10-15 19:54:23 +02:00

rollnote-api

NestJS REST API powering the rollnote film-photography journal: it manages users, film stocks, shoots, frames, lenses, ISOs, and weather. Data lives in PostgreSQL via Drizzle ORM. Auth is handled by Better-Auth.

Prerequisites

  • Node.js 24+ (.nvmrc; engines is enforced)
  • pnpm (version pinned by packageManager, use corepack)
  • Docker or Podman (for the dev Postgres stack and the integration tests)

Local dev

pnpm install
cp .env.example .env.local   # POSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DB for the dev DB
docker compose -f dev.compose.yml up -d   # Postgres on 127.0.0.1:5432 (reads .env.local)
cp .env.example .env         # app config, loaded by dotenv at boot
pnpm start:dev               # watch mode on :3000

Two env files, two consumers: dev.compose.yml reads .env.local for the Postgres container, and the app itself loads .env (via dotenv/config). When the app runs on the host, point DATABASE_URL in .env at localhost:5432. On boot the app applies pending migrations and runs the idempotent seed, so a fresh database initializes itself.

The Swagger UI is mounted at /api whenever NODE_ENV !== production.

OpenAPI document

openapi.json at the repo root describes every route except Better-Auth's /api/auth/*. Regenerate it after changing a route or a response shape and commit it:

pnpm openapi:generate   # nest build, then writes openapi.json; needs no database

CI runs the same command and fails if the committed file differs.

Environment

Required variables are listed in .env.example. The schema lives in src/config/env.schema.ts and is validated at boot - startup fails loudly if anything required is missing or malformed. ENCRYPTION_KEY is a base64-encoded 32-byte key (openssl rand -base64 32); SMTP_* and EMAIL_FROM are only required when NODE_ENV=production.

Database

Migrations are managed by drizzle-kit:

pnpm drizzle-kit generate   # diff schema -> new SQL file under drizzle/
pnpm drizzle-kit migrate    # apply pending migrations

The app also runs migrate(...) on boot, so a fresh container will catch itself up automatically.

Tests, lint, format

pnpm test          # unit tests (fast, no external deps)
pnpm test:int      # integration tests (boots throwaway Postgres + Mailpit)
pnpm test:all      # unit + integration
pnpm lint          # eslint, check only (CI gate)
pnpm lint:fix      # eslint --fix
pnpm format        # prettier --write
pnpm build         # nest build -> dist/

Integration tests need a Docker-compatible socket. On Podman (default on Arch + many Linux setups), enable the user socket once and the suite auto-detects it:

systemctl --user enable --now podman.socket

To override, set DOCKER_HOST before running, e.g. DOCKER_HOST=unix:///run/user/$UID/podman/podman.sock pnpm test:int.

Architecture

One NestJS module per domain (shoot/, frame/, ...), each with a controller, a service and its Drizzle schema in src/db/<domain>-schema.ts. Services join other domains' tables directly where a query needs them, always scoped to the caller. See CLAUDE.md for the conventions guide.

Deployment

compose.yml runs the production stack: rollnote-db (Postgres 18) and rollnote-api (built from the Dockerfile, Node 24 alpine, runs as node on port 3000, joined to the external websites network for the reverse proxy). Both containers are hardened (no-new-privileges, read-only root filesystem with tmpfs for the paths they write, capabilities dropped; the database keeps only what its entrypoint needs to drop to the postgres user) and have CPU, memory and pid limits plus log rotation.

docker compose up -d --build   # reads .env for every ${VAR} in compose.yml

CI (.forgejo/workflows/test.yml, triggered by the run-ci PR label) runs lint, typecheck, nest build, the openapi.json drift check, the changelog/front-version check, a gitleaks secret scan, fallow, unit and integration tests, and a build of the Docker image.