The name, and what it is for

An Officer plugin that puts the fofos admin inside Officer, as panels — so
editing the site uses Officer's accounts and permissions rather than a second
password on a second domain.

Nothing is built. The README says so, and says what the shape will be: nav plus
detail after plugins/proxy-manager, nine sections, messages first because it is
one read-only route and proves the whole path before anything harder.

The API it consumes already exists, at /api/admin in fofos/platform. It is there
rather than here because the query layer is the only thing the site and the admin
share, and a plugin carrying its own copy of that schema would be a second
definition of the same tables in another repository.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Pastilhas
2026-08-28 22:32:23 +01:00
co-authored by Claude Opus 5
commit 0a967bef98
2 changed files with 109 additions and 0 deletions
+106
View File
@@ -0,0 +1,106 @@
# Fofos Admin
An **Officer plugin**. It puts the fofos website's admin inside Officer, as panels.
**Status: nothing is built yet.** This repository is a name and a README. What it will be is below,
and the API it consumes already exists — see *The other half*.
---
## What this is
[fofos](https://gitea.officer.dev/fofos/platform) is a website with three surfaces in one process: a
public site generated to static files, a JSON API, and an admin that has until now been its own
server-rendered application on its own subdomain, behind its own password.
That third one is moving here. Editing the site becomes something you do **inside Officer**, with
Officer's accounts, Officer's permissions and Officer's workspace — rather than a second login on a
second domain that has to be told separately who is allowed in.
## Why a plugin, rather than leaving it where it was
**One place to sign in.** The admin had its own argon2 passwords, its own session cookie and its own
`/admin/setup` bootstrap. All of it duplicated something Officer already does properly, and none of
it could be shared: a person given access to the machine still had to be given access to the admin,
separately, by hand.
**Officer's permissions instead of a second answer.** One grantable permission, per role, at read or
write — the same page as every other plugin, with no special case to learn.
**The site keeps its own data.** This plugin stores nothing of the client's. Content, uploads and
every photograph stay in fofos's own database and media store; the only thing in Officer's database
is the connection to reach them. So deleting this plugin — with or without the "delete its data"
checkbox — cannot lose a single line of copy or a single image. That is a deliberate property, not a
happy accident.
## What it will look like
A nav down the left and a detail panel beside it, following `plugins/proxy-manager` rather than
`plugins/gitea` — a list of sections over a remote service whose credential the plugin holds, which
is this problem exactly.
Nine sections, in the order they will be built:
| | |
|---|---|
| **Messages** | contact form submissions. Read only, and there is no delete |
| **Site settings** | partners, social links, contact details |
| **About** | one page of copy and a header photograph |
| **Home** | the hero, the intro, and an ordered gallery |
| **Gallery** · **Drops** · **Collabs** | pieces, each with ordered media |
| **Collections** | the nested one — collections hold items, items hold media |
| **Services** | a fixed tree. Content only: no adding, no removing, no reordering |
Messages is first on purpose. It is one read-only route, so it proves the whole path — Officer panel,
through this plugin, to fofos and back — before anything harder is attempted.
## The other half
**The API this consumes is already built and running**, in the fofos repository at `/api/admin`. It
covers all nine sections, authenticates with a bearer token, and its full route reference is in
[`docs/SURFACES.md`](https://gitea.officer.dev/fofos/platform/src/branch/master/docs/SURFACES.md).
It lives there rather than here for one reason worth stating: the query layer is the only thing the
site and the admin share, and a plugin carrying its own copy of fofos's database schema would be a
second definition of the same tables in a different repository, drifting from the day it was written.
So this plugin holds a URL and a token and knows nothing about Postgres.
## Configuration
One connection: where fofos is, and a token to reach it. Stored in a plugin-owned table, encrypted
under this plugin's own purpose key, and **never returned across the API boundary** — to anybody,
including the owner. A settings form asking "is one set" gets a boolean.
`/_config` is `ownerOnly`. The token is write authority over the whole public site, so while reading
what is on the site is reasonable to delegate, handing over the credential is not.
## Not in the marketplace
This is a plugin for one website, and it is useless without that website. It installs from its git
URL and has no catalogue entry.
---
## Files
Nothing yet. What Officer requires, once there is something:
```
manifest.ts identity, the permission, ownerOnly on /_config
db/schema.ts ONE table: the fofos connection
api/router.ts mounted at /api/fofos-admin — forwards to fofos
web/panels.ts the panels. REQUIRED alongside web/
web/layout.ts how they are arranged. REQUIRED alongside web/
```
Presence is the declaration — Officer discovers a plugin by what is on disk, and `manifest.ts` holds
only what a directory listing cannot say. A `web/` missing either of its two files is refused at
discovery, by name.
## Conventions
**`master`, not `main`.**
**Every colour is a semantic token** — `bg-muted`, `text-muted-foreground`, `border-border` — and
never a Tailwind palette class. Palette classes do not follow the theme, so a plugin using them looks
correct under exactly one theme and wrong under the rest.