officer_db with atelier_ prefixes, like every other plugin
the extraction kept atelier's own postgres database, because the brief said to when the tables already had rows. the owner reversed it, and the reason is not tidiness: a plugin that behaves differently from every other plugin is a plugin nobody can reason about. the separate database meant a second connection string to set before install, a second thing to back up, a second thing to get wrong on a new machine, and failure modes no other plugin shares — none of it bought anything except avoiding a migration. four tables now: atelier_collections, atelier_collection_items, atelier_providers, atelier_generations. index and check constraint names carry the prefix too — those are global in postgres and are the one thing that would otherwise collide with another plugin. db/schema.ts re-exports the definitions rather than duplicating them: they stay beside the code that queries them in sidecar/db/, and that file exists to be found by gen-plugin-schemas. ATELIER_POSTGRES_URL is gone. db.ts reads OFFICERDEV_POSTGRES_URL ?? POSTGRES_URL, which the platform already sets. the standalone service's rows are NOT here and nothing depends on them being here — the old service is still running against the old database, untouched, and migrating is a separate watched step after this is proven on alpha.
This commit is contained in:
@@ -8,7 +8,7 @@ import { generations } from '../generations/schema';
|
||||
* collection by name and creates it when it does not exist, so without the constraint a second
|
||||
* request with the same name would quietly start a second collection and split the batch in two.
|
||||
*/
|
||||
export const collections = pgTable('collections', {
|
||||
export const collections = pgTable('atelier_collections', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
name: text('name').notNull().unique(),
|
||||
description: text('description'),
|
||||
@@ -27,8 +27,7 @@ export const collections = pgTable('collections', {
|
||||
* is a way of looking at generations, not a container that owns them. Deleting a generation removes
|
||||
* its memberships, since a row pointing at nothing is only a way to crash a join later.
|
||||
*/
|
||||
export const collectionItems = pgTable(
|
||||
'collection_items',
|
||||
export const collectionItems = pgTable('atelier_collection_items',
|
||||
{
|
||||
collectionId: uuid('collection_id')
|
||||
.notNull()
|
||||
@@ -41,6 +40,6 @@ export const collectionItems = pgTable(
|
||||
(table) => [
|
||||
// The composite key is what makes adding an image twice a no-op instead of a duplicate.
|
||||
primaryKey({ columns: [table.collectionId, table.generationId] }),
|
||||
index('collection_items_generation_idx').on(table.generationId),
|
||||
index('atelier_collection_items_generation_idx').on(table.generationId),
|
||||
],
|
||||
);
|
||||
|
||||
+13
-13
@@ -2,22 +2,22 @@ import { drizzle } from 'drizzle-orm/postgres-js';
|
||||
import postgres from 'postgres';
|
||||
import * as schema from './schema';
|
||||
|
||||
// The `atelier` Postgres database — separate from officer_db, on purpose. See PLUGIN.md.
|
||||
// officer_db, like every other plugin.
|
||||
//
|
||||
// `ATELIER_POSTGRES_URL` rather than officer's own `OFFICERDEV_POSTGRES_URL`/`POSTGRES_URL`: this
|
||||
// plugin talks to a different database on (most likely) the same Postgres server, and the platform's
|
||||
// own connection string must never be reused here by accident — the standalone service's
|
||||
// `atelier_db/src/env.ts` had a whole enforcement mechanism for exactly this mistake ("a development
|
||||
// process cannot reach the production database"), which no longer applies once this runs as one
|
||||
// process under one PM2 entry, but the principle — don't let this plugin's queries land on the
|
||||
// platform's own tables — still does.
|
||||
const POSTGRES_URL = process.env.ATELIER_POSTGRES_URL;
|
||||
// This originally connected to a separate `atelier` database via `ATELIER_POSTGRES_URL`, carried over
|
||||
// from the standalone service. Reversed on 2026-08-20 by the owner: the tables are `atelier_`-prefixed
|
||||
// in officer_db, the plugin starts empty, and the standalone service's rows are migrated afterwards
|
||||
// with someone watching.
|
||||
//
|
||||
// The reason is not tidiness. A second database meant a second connection string to set, a second thing
|
||||
// to back up, and a plugin whose failure modes nobody else's plugin shares. `OFFICERDEV_POSTGRES_URL ??
|
||||
// POSTGRES_URL` is the house convention (src/servers/env-prefix.ts), and the standalone repo's whole
|
||||
// "a development process cannot reach production" enforcement stops applying once this is one process
|
||||
// under one PM2 entry.
|
||||
const POSTGRES_URL = process.env.OFFICERDEV_POSTGRES_URL ?? process.env.POSTGRES_URL;
|
||||
|
||||
if (!POSTGRES_URL) {
|
||||
throw new Error(
|
||||
'ATELIER_POSTGRES_URL environment variable is required — set it in the platform .env to the ' +
|
||||
'`atelier` Postgres database this plugin should use. See plugins/atelier/PLUGIN.md.',
|
||||
);
|
||||
throw new Error('OFFICERDEV_POSTGRES_URL (or POSTGRES_URL) is required — the platform sets it in .env.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,8 +32,7 @@ export type GenerationOperation = (typeof GENERATION_OPERATIONS)[number];
|
||||
* unauthenticated as ordinary images, so they need to be a file a static route can hand to
|
||||
* `new Response(Bun.file(...))` — not a column something has to decode first.
|
||||
*/
|
||||
export const generations = pgTable(
|
||||
'generations',
|
||||
export const generations = pgTable('atelier_generations',
|
||||
{
|
||||
/**
|
||||
* Also the filename on disk. One id means one image, so a row can be turned into a served URL
|
||||
@@ -123,11 +122,11 @@ export const generations = pgTable(
|
||||
finishedAt: timestamp('finished_at', { withTimezone: true }),
|
||||
},
|
||||
(table) => [
|
||||
check('generations_status_check', sql`${table.status} in ('queued', 'running', 'succeeded', 'failed')`),
|
||||
check('generations_operation_check', sql`${table.operation} in ('generate', 'edit')`),
|
||||
check('atelier_generations_status_check', sql`${table.status} in ('queued', 'running', 'succeeded', 'failed')`),
|
||||
check('atelier_generations_operation_check', sql`${table.operation} in ('generate', 'edit')`),
|
||||
// The queue reads "what is still to do, oldest first" on every tick and after every restart.
|
||||
index('generations_status_created_idx').on(table.status, table.createdAt),
|
||||
index('atelier_generations_status_created_idx').on(table.status, table.createdAt),
|
||||
// Every poll of a job is this lookup.
|
||||
index('generations_request_idx').on(table.requestId, table.index),
|
||||
index('atelier_generations_request_idx').on(table.requestId, table.index),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -18,8 +18,7 @@ import { pgTable, uuid, text, boolean, timestamp, index } from 'drizzle-orm/pg-c
|
||||
* repo is updated, and refusing a new one on release day is a worse failure than passing it through
|
||||
* and letting the vendor answer.
|
||||
*/
|
||||
export const providers = pgTable(
|
||||
'providers',
|
||||
export const providers = pgTable('atelier_providers',
|
||||
{
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
|
||||
@@ -50,6 +49,6 @@ export const providers = pgTable(
|
||||
},
|
||||
(table) => [
|
||||
// Resolving a request by model is the hot path: every generation does it once.
|
||||
index('providers_model_idx').on(table.model, table.enabled),
|
||||
index('atelier_providers_model_idx').on(table.model, table.enabled),
|
||||
],
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user