import { pgTable, serial, text, timestamp, integer } from 'drizzle-orm/pg-core'; /** * The connection to the fofos server. One row, and the ONLY thing this plugin stores. * * ── Why there is nothing else here ── * * Every temptation to add a table to this file should be resisted, and it is worth writing down why * rather than relying on taste. fofos owns its own Postgres and its own media store; this plugin * reaches them over HTTP. If the client's collections and photographs lived here instead, they would * live under Officer's plugin installer — which drops a plugin's tables when the "delete its data" * checkbox is ticked. * * So the strongest thing that can be said about this plugin is a consequence of this file being one * table long: **no operation on it can destroy the client's work.** Deleting it removes a URL and a * token, and the site is untouched. * * A cache would be the thin end of it. If a panel ever needs one, it belongs in memory or in the * browser, not in a table that outlives the request. * * ── Why not core `service_connections` ── * * It fits the shape — `url`, an encrypted `secret` — but its `ServiceName` is a closed TypeScript * union, so using it means editing the platform to add a name, and a plugin should install into a * platform it did not have to change. The same reasoning `plugins/proxy-manager/db/schema.ts` gives, * and worth revisiting on the day that union opens. */ export const fofosAdminConnection = pgTable('fofos_admin_connection', { id: serial('id').primaryKey(), /** Where the fofos server is — the origin its API is served from. Normalised without a trailing slash. */ url: text('url').notNull(), /** * fofos's `FOFOS_API_TOKEN`, encrypted with `encryptSecret('fofos-admin', …)` — its own purpose * key, so a leak of that one opens this column and nothing else. * * **Never returned across the API boundary**, at any level, to anybody, including the owner. A * settings form asking "is one set" gets a boolean. * * There is exactly one of these rather than one per user, and that is not laziness: fofos has a * single token and no notion of accounts. WHO is editing is a question Officer answers with its own * accounts and permissions, in front of this. */ token: text('token').notNull(), /** From the last successful probe — which sections that server says it serves. Diagnostic only. */ sections: text('sections').array(), lastProbeAt: timestamp('last_probe_at', { withTimezone: true }), /** * `integer` rather than `boolean` for the same reason proxy-manager uses one: three states, not * two. Null means never probed, which reads differently from probed-and-failed. */ lastProbeOk: integer('last_probe_ok'), createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(), }); export type FofosAdminConnection = typeof fofosAdminConnection.$inferSelect;