- TypeScript 86.1%
- HTML 6.6%
- SCSS 5.8%
- Python 0.9%
- JavaScript 0.4%
- Other 0.2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| .forgejo/workflows | ||
| .vscode | ||
| docs | ||
| e2e | ||
| public | ||
| scripts | ||
| src | ||
| .dockerignore | ||
| .editorconfig | ||
| .fallowrc.jsonc | ||
| .gitignore | ||
| .gitleaksignore | ||
| .nvmrc | ||
| angular.json | ||
| CLAUDE.md | ||
| code-style.md | ||
| compose.yaml | ||
| Dockerfile | ||
| eslint.config.mjs | ||
| LICENSE | ||
| nginx-security-headers.conf | ||
| nginx.conf | ||
| ngsw-config.json | ||
| package.json | ||
| playwright.config.ts | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| tsconfig.app.json | ||
| tsconfig.json | ||
| tsconfig.spec.json | ||
rollnote-front
The rollnote web app: an Angular 21 SPA (standalone components, zoneless,
signals) with PrimeNG, a Better-Auth client and an Angular service worker.
It talks to rollnote-api
and is served as static files by nginx in production.
Prerequisites
- Node.js 24+ (
.nvmrc;enginesis enforced) - pnpm, the version pinned by
packageManager(corepack enable) - A running
rollnote-apionhttp://localhost:3000for local development
Setup
pnpm install
pnpm start # dev server on http://localhost:4200
There is no .env: the API URL is compiled in from
src/environments/environment.development.ts (ng serve, localhost:3000)
and src/environments/environment.ts (production builds,
https://api.roll-note.com).
Scripts
pnpm start # ng serve (development configuration)
pnpm build # production build -> dist/rollnote-front/browser
pnpm test # Vitest unit tests (jsdom)
pnpm e2e # Playwright e2e tests against a mocked API (starts ng serve)
pnpm lint # ESLint (src + e2e)
pnpm typecheck # tsc --noEmit
pnpm format # Prettier --write (src, e2e, scripts)
pnpm format:check # Prettier --check, run in CI
pnpm csp:hashes # print the CSP hashes of the built index.html's inline code
pnpm api:types # regenerate src/lib/api/schema.d.ts from ../rollnote-api/openapi.json
Code conventions live in code-style.md and CLAUDE.md.
Deployment
Dockerfile builds the app with Node 24 and serves it with
nginxinc/nginx-unprivileged (nginx runs as uid 101, never as root):
- The container listens on port 8080, not 80. Point the reverse proxy
upstream at
rollnote-front:8080on thewebsitesnetwork. GET /healthreturns 200 (used by the imageHEALTHCHECKand compose).- Security headers come from
nginx-security-headers.conf: a Content-Security-Policy, currently sent asContent-Security-Policy-Report-Only(inline-script hashes are computed from the build byscripts/csp-hashes.mjs; switch the header name to enforce it),Permissions-Policy(geolocation for the app itself, camera/microphone/payment off),X-Frame-Options,X-Content-Type-OptionsandReferrer-Policy. HSTS is left to the reverse proxy that terminates TLS. - Text assets are pre-compressed at build time and served with
gzip_static; hashed bundles are cached as immutable,index.htmland the service worker files areno-cache.
docker compose up -d --build
compose.yaml runs the container read-only (tmpfs /tmp), with
no-new-privileges, all capabilities dropped, CPU/memory/pid limits and log
rotation.
Self-hosting with another API origin
Set apiUrl in src/environments/environment.ts to your API, and pass the
same origin to the image build so the CSP connect-src allows it:
API_ORIGIN=https://api.example.org docker compose build
# or: docker build --build-arg API_ORIGIN=https://api.example.org .
CI
.forgejo/workflows/test.yml runs when the run-ci label is added to a PR:
lint, format check, typecheck, a drift check of the generated API types against
rollnote-api's openapi.json, a gitleaks secret scan, the fallow audit, unit
tests, the production build, a Docker image build (not pushed) and the
Playwright e2e suite.