the site sections open, because there is finally something behind them

`ready` was hardcoded false for as long as the sections proxied to an address
nobody could set — the connection form was removed and nothing replaced it, so
nine doors led to the same "not connected" message.

Both halves exist now and neither is typed by a person: the development server
runs on a port this plugin chose, holding a FOFOS_API_TOKEN this plugin
generated and handed it at start. So the credentials are DERIVED rather than
stored. Asking somebody to copy a value out of one part of this plugin into
another was never a setting, it was a chore.

Development and not production, and that is not a preference. Production is a
directory of files behind nginx — no API, no database, nothing listening — so
editing against it is impossible rather than merely wrong. Sections read and
write dev; Build is what publishes. An edit is visible immediately and public
only when somebody decides it should be.

The gate asks whether the server ANSWERS, not whether pm2 says online. `bun
--hot` survives a crash at startup: the process stays up, pm2 reports online,
and nothing is listening. Opening nine sections onto that would make one failed
boot look like nine broken screens.

And when /status itself cannot be reached, `configured` stays true while `ready`
goes false. Telling somebody they have set nothing up is alarming and wrong;
opening nine sections onto a server we could not confirm is answering is nine
screens that each fail separately. Cannot-tell keeps them shut and lets the next
poll open them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-29 20:00:18 +01:00
co-authored by Claude Opus 5
parent 2153d4875b
commit f0d29c8000
3 changed files with 73 additions and 18 deletions
+27 -4
View File
@@ -1,7 +1,7 @@
import { db } from 'officerdb/db';
import { encryptSecret, decryptSecret } from 'officerdb/crypto';
import { eq } from 'drizzle-orm';
import { fofosAdminConnection, type FofosAdminConnection } from '../db/schema';
import { fofosAdminConnection, fofosAdminSettings, type FofosAdminConnection } from '../db/schema';
import { normaliseUrl, type FofosCredentials } from './fofos-client';
/**
@@ -56,10 +56,33 @@ export async function readConnection(): Promise<ConnectionView> {
}
/** Decrypted, for the client only. Never serialise the result of this. */
/**
* How to reach the site's admin API — DERIVED now, not stored.
*
* ── Why there is nothing to configure ──
*
* This used to read a URL and a token somebody typed into a form. Both are now facts this plugin
* already holds: the development server runs on a port it chose, and `FOFOS_API_TOKEN` is a secret it
* generated and handed to that server at start. Asking a person to supply either would be asking them
* to copy a value out of one part of this plugin and into another.
*
* ── Why DEVELOPMENT and not production ──
*
* Production is a directory of files behind nginx. It has no API, no database connection and nothing
* listening — editing against it is not merely wrong, it is impossible. So the sections read and write
* the development server, and Build is what publishes. That is the shape the site's own build script
* already implies, and it means an edit is visible on dev immediately and public only when somebody
* decides it should be.
*
* Null when there is no port or no secret yet, which the panel reports as not being set up.
*/
export async function readCredentials(): Promise<FofosCredentials | null> {
const found = await row();
if (!found) return null;
return { url: found.url, token: decryptSecret(PURPOSE, found.token) };
const settings = (await db.select().from(fofosAdminSettings).limit(1))[0] ?? null;
if (!settings?.devPort || !settings.siteApiToken) return null;
return {
url: `http://127.0.0.1:${settings.devPort}`,
token: decryptSecret('fofos-admin', settings.siteApiToken),
};
}
export type SaveParams = { url: string; token?: string };