Files
plainpages/todo.md
T
2026-08-02 17:25:07 +02:00

10 KiB

  • Organize the files in src in to folders so it is easier to understand the structure of the code.
  • Move docs/plugin-contract.md into README.md and remove the docs folder.
  • The plugins/scheduling is an example and shouldn't be committed to the plugins directory since that should be empty to be able to be mounted in via docker or other means for the users/develoeprs using this application/framework. Put it in the examples folder instead.
  • The config folder should be empty and the current settings in the menu.ts should be the fallback default. IF a menu.ts where to appear in that folder, it should override the default settings with whatever is in it. The idea is the folder should be empty by default and you mount it in your docker container with your config.
  • Make the internal admin pages for users groups etc into a plugin instead in the examples folder and remove them from the internal source. Add a part in the quick start about copying this plugin into the plugins folder to enable GUI user- and group admining.
  • CI/CD - Test on push to any branch except main. (.gitea/workflows/ci.yml runs bash ci.sh; the one-time act_runner setup it needs is documented in README → CI/CD.)
  • CI/CD - Require PR to main and don't allow merge if tests does not pass. Only allow linear history and history that leaves the last commit hash on main the exact same as on the branch we just merged in. (Gitea branch protection on main + fast-forward-only merge style, set via API; documented in README → CI/CD.)
  • CI/CD - Sync up to github after every successful merge to main, URL: git@github.com:larvit/plainpages.git - also note the true home top of the README. Force push to github, it should only ever be a mirror of the gitea.larvit.se repository. (.gitea/workflows/mirror.yml force-pushes main + tags over HTTPS with a dedicated account's PAT in the MIRROR_GITHUB_TOKEN secret; setup documented in README → CI/CD.)
  • CI/CD - Build docker images as part of the requirements to be able to merge to main. Push them with the git commit hash as docker tag. Push to container registry at Gitea. (ci.yml builds + pushes gitea.larvit.se/larvit/plainpages:<commit hash> after a green gate — with ff-only merges that is the main commit's image; auth via the DOCKER_REGISTRY_USER variable + DOCKER_REGISTRY_TOKEN secret, retention via an org cleanup rule; documented in README → CI/CD.)
  • CI/CD - Re-tag docker images from git hash to semver when a semver git tag is pushed. (release.yml on a vX.Y.Z tag pulls the commit-hash image and re-tags it X.Y.Z/X.Y/X/latest, failing loud if the gated image is missing; tag pushes also trigger the GitHub mirror; documented in README → CI/CD.)
  • CI/CD - Sync docker images to docker hub after each re-tag to git tags. (release.yml pushes the same X.Y.Z/X.Y/X/latest tags to docker.io/larvit/plainpages after the Gitea re-tag — releases only, no hash tags; auth via the DOCKERHUB_USER variable + DOCKERHUB_TOKEN secret; documented in README → CI/CD.)
  • Write a short text on how to use this docker image to publish on docker hub and save it to README-dockerhub.md (tagline, tags, clone-free quick start — the image ships the Ory config, extracted via docker run … tar + a self-contained compose.yml — env table, first plugin; pasted into the Docker Hub overview by hand — noted in README → CI/CD.)
  • CI/CD - Setup renovate bot. Check how other repos on this Gitea is setup you can get access to, there should be a number of renovate bot activated ones. (renovate.yml runs the self-hosted renovate/renovate image nightly against renovate.json — this repo only, via the shared renovate@larvit.se bot + RENOVATE_TOKEN secret, mirroring the pwrpln/core pattern; standard managers cover npm/Dockerfiles/compose/gitea-action pins, two custom regex managers cover the image tags embedded in workflow run: steps, the Ory + Playwright lockstep sets are grouped, every bump stays an exact pin, and each PR automerges once the gate is green; documented in README → CI/CD.)
  • CI/CD - Renovate: set a read-only GITHUB_COM_TOKEN env in renovate.yml so Renovate stops hitting github.com rate limits when resolving github-hosted deps (Playwright, lucide, actions/checkout) and can fetch changelogs. Non-blocking refinement; needs a read-only GitHub PAT stored as an Actions secret. (The renovate job forwards the RENOVATE_GITHUB_TOKEN secret — a scopeless read-only github.com PAT; Gitea rejects GITHUB_-prefixed secret names — into the container as GITHUB_COM_TOKEN; documented in README → CI/CD.)
  • CI/CD - When renovate updates a dependency - also release a new version of plainpages based on what got updated with Renovate. Major typescript? New apiVersion + new major. A tiny patch to ejs? Only patch release etc. Before implementing, explain in detail how you will solve this. (renovate.yml gains an auto-release job (needs: renovate) that cuts one vX.Y.Z tag per run for what Renovate merged; level = highest Release-Bump: trailer Renovate stamps via commitBody, any dep's major/minor/patch mapped straight through (default patch). Decoupled from apiVersion (tag-only, HOST_API_VERSION untouched — a "major" is just a bigger image tag, never a plugin break); pre-1.0 shifts down so nothing auto-crosses into 1.0.0. Pure auto-release/next-version.ts + unit tests; tag pushed with renovate-bot's PAT so release.yml fires; documented in README → CI/CD.)
  • Add an e2e test for the admin plugin's OAuth2-clients (Hydra) screen. The full-flow e2e suite runs without Hydra (compose.full.yml), so /admin/clients register/detail/delete is only unit-covered (src/http/app.test.ts); wire Hydra into an e2e stack and drive the screen in the browser. (compose.full.yml now includes Hydra (serve all --dev) and full-flow.spec.ts drives /admin/clients register → one-time secret → list → detail → delete in the browser; documented in README → Testing.)
  • Build and publish docker image as CI/CD. (Duplicate of the CI/CD items above: ci.yml builds and pushes gitea.larvit.se/larvit/plainpages:<commit hash> behind the green gate, release.yml re-tags it to semver and syncs those tags to Docker Hub.)
  • The human developer understands the security model in the auth in this project. (README → Auth → Security model: trust boundaries — browser untrusted, JWT untrusted until verified, the private network as the only guard on the unauthenticated Ory admin APIs, plugins trusted and unsandboxed, row rules upstream — plus a threat→defense table, the fail-closed rule, and pointers to the limits that are deliberately not guaranteed. Signed-not-encrypted is called out so nothing secret lands in a claim, and the JWT's ~10m TTL is separated from the 30-day Kratos session that re-mints it. Every row of the threat table is enforced by a test — the mandatory-exp guard was the one gap, now asserted in src/auth/jwt-middleware.test.ts; the trust-boundary bullets are not testable claims. Review also corrected the hardening checklist — REQUIRE_SECURE_SECRETS guards only CSRF_SECRET, so the committed Kratos/Hydra/Postgres dev secrets are now listed in "What you must supply".)
  • Add i18n support.

Architectural review findings (2026-07-02)

Prioritized. Overall verdict: architecture is sound (contract-first plugin API, functional core/imperative shell, strong test seams); these are refinements.

  • HIGH — Split handleRequest in src/http/app.ts (~380 lines). It mixes the request pipeline with inline implementations of ~10 built-in endpoints (Kratos flows, /oauth2/*, /auth/complete, /logout, /, /dashboard, 404/405). Extract each endpoint into a named handler (auth/OAuth2 group → src/auth/ route module) with the same (req, res, ctx) shape plugin routes use; reduce handleRequest to pipeline → internal route table → sendResult.
  • MEDIUM — Add complexity/method-size static analysis to the CI gate. Only tsc --strict today; a size/complexity rule would have caught the app.ts growth. Also when wiring CI/CD: keep the merge gate fast (typecheck + units + Ory-free visual suite; heavy e2e suites required-but-separate) and make the pipeline the only path to a published image (build once at tag, promote).
  • MEDIUM — De-duplicate examples/plugins/admin/admin-groups.ts and admin-roles.ts (~80% identical). Same "Keto membership object admin" concept twice; extract a parameterized helper keyed on { namespace, base, labels, columns }, leave roles' effective-access view as the only delta. Matters extra because this is the reference plugin people copy.
  • MEDIUM→LOW — Add a list-page view-model helper in src/ui/. Every list screen (users, groups, roles, shifts) hand-rewrites the same ~40 lines bridging parseListQuery/paginate to the EJS partials; at minimum a buildPaginationModel(page, hrefFor) block.
  • LOW→MEDIUM — Retire src/ui/shell-context.ts. ShellModel/buildShellContext has one consumer left (dashboard) and duplicates PageChrome on almost every field, incl. identical brand-assembly in chrome.ts and shell-context.ts. Fold the dashboard onto ctx.chrome + title/breadcrumbs; keep shellUser as the shared primitive.
  • LOW — Fix stale doc references to removed docs/plugin-contract.md in views/index.ejs (user-visible dashboard text; also links /scheduling as if pre-installed) and examples/plugins/scheduling/views/shifts.ejs.
  • LOW — Decide (once) on a ctx.system facade. #plugin-api exposes raw Ory client shapes, so an Ory client refactor is a major apiVersion bump. AGENTS.md accepts this; revisit only if external plugin authors appear. Record the decision.
  • LOW — README/AGENTS.md gaps: state the intended lifetime/horizon explicitly, add a short domain glossary (host, manifest, chrome, nav fragment, permission token, system plugin, denylist…), and note the expected plugin-author population (first-party vs external) to justify the versioning machinery.