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:
@@ -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.
|
||||
Reference in New Issue
Block a user