a005acb93d
CI / full-gate (push) Successful in 2m38s
README loses the competitor comparison, the personas and the repeated philosophy; the
five near-identical E2E command blocks become a table plus one command, and the file
map a clause per entry. AGENTS.md keeps every decision but drops the narrative around
them. todo.md's completed items collapse to their task line — git holds the rest.
Comments lose restatement, README duplication and history ("used to", "originally",
dated notes). AGENTS.md gains a Prose discipline section making this a standing pass on
every change rather than a one-off cleanup.
src/compose.test.ts now expects 6 documented E2E run commands, not 10, since the README
states the command once instead of per suite.
66 lines
4.1 KiB
Markdown
66 lines
4.1 KiB
Markdown
# Admin — the system-administration plugin
|
|
|
|
The Users / Groups / OAuth2-clients screens for running Plainpages itself, shipped as a **drop-in
|
|
example plugin** so a fresh clone has no admin GUI until you opt in. Copy this folder into `plugins/`
|
|
(it keeps the id and mount path `admin`, so the screens live at `/admin/*`) and restart:
|
|
|
|
```bash
|
|
cp -r examples/plugins/admin plugins/admin
|
|
docker compose up -d
|
|
```
|
|
|
|
The bootstrap grants the seeded `admin@plainpages.local` every permission this plugin declares, so
|
|
the section appears in the menu and the screens work immediately. An older copy already in
|
|
`plugins/` is yours — the host never updates it — so re-copy after a pull; a stale one stops the boot
|
|
with a message naming it ([README → Upgrading](../../../README.md#upgrading)).
|
|
|
|
Every string it renders comes from its own catalogs (`i18n/en-US.ts`, `i18n/sv-SE.ts`), the nav
|
|
labels included. Each pure view-model builder takes an optional `t` defaulting to the plugin's own
|
|
English, so a unit test reads in words rather than keys.
|
|
|
|
## What it demonstrates — a *system* plugin
|
|
|
|
Most plugins fetch their data from an upstream service of their own (see the [scheduling
|
|
reference](../scheduling/README.md)). The admin screens instead administer **Plainpages' own identity
|
|
stack**, so they use the privileged **`ctx.system`** surface the host exposes to a system plugin:
|
|
|
|
- **`ctx.system.kratosAdmin`** — create/edit/deactivate/delete Kratos identities (Users).
|
|
- **`ctx.system.keto`** — read/write the Keto relationship graph (group membership, permission grants).
|
|
- **`ctx.system.hydra`** — register/list/delete Ory Hydra OAuth2 clients.
|
|
- **`ctx.system.revoke(sub)`** — the optional instant-revoke hook: a deactivate/delete or a
|
|
user's permission change kills that subject's live tokens at once instead of waiting out the JWT TTL.
|
|
|
|
`ctx.system` is populated only when the host wired those services. Where a capability is absent the
|
|
screen degrades to a themed 503 rather than crashing. Everything else is an ordinary plugin:
|
|
folder-discovered, gated per route by its screen's `<resource>:<action>` permission, rendering the
|
|
core building blocks in `views/`.
|
|
|
|
Each screen is its own resource — `users`, `groups`, `oauth2-clients` — split into `:read` and
|
|
`:write`, so a helpdesk account can be given `users:read` alone. Holding none of the six hides the
|
|
Admin section entirely.
|
|
|
|
There is **no Permissions screen**. Permission names are declared in plugin code, not created in a
|
|
GUI, so the host's catalog (`ctx.declaredPermissions`) is the fixed list — and holding one is a
|
|
property of a user or a group, edited as a checkbox list on those two screens (`admin-grants.ts`).
|
|
|
|
## Layout
|
|
|
|
- `plugin.ts` — the manifest: the Admin nav fragment, the six permissions the plugin declares, and
|
|
the route table — one thin handler per method+path, gated via `permissionName(resource, actionForMethod(method))`
|
|
so a GET needs `:read` and a POST `:write`.
|
|
- `admin-grants.ts` — the permission picker and the grant diff, shared by the Users and Groups
|
|
screens: what a submitted checkbox set grants and revokes, against the host's declared catalog.
|
|
- `admin-users.ts` · `admin-groups.ts` · `admin-clients.ts` — each a set of pure
|
|
view-model builders (unit-tested in the matching `*.test.ts`) plus thin per-route handlers keyed on
|
|
`ctx.params` (the host extracts `:id`/`:name`), sharing a small `withX` wrapper that resolves the
|
|
screen's permission gate + the needed `ctx.system` clients once.
|
|
- `admin-shared.ts` — the permission naming (`permissionName` / `actionForMethod`), the shared gate
|
|
(`requirePermission`), CSRF form reader (`guardedForm`), confirm
|
|
model, nav fragment, and the not-found / unavailable helpers.
|
|
- `views/` — the screens' EJS, plus the admin-specific body partials under `views/partials/`. They
|
|
`include()` the core building-block partials (shell, data-table, filter-bar, field, …).
|
|
|
|
The three screens hold **no state** — everything lives in Ory. Handlers are thin, so their builders
|
|
unit-test as pure functions with no host; the HTTP routing/gate/CSRF is covered in
|
|
`src/http/app.test.ts` (which mounts this plugin) and end-to-end in `e2e-tests/full-flow.spec.ts`.
|