Files
atelier/sidecar/db/providers/queries.ts
T
pastilhas f23ec1283b manifest, thin proxy router, and the sidecar backend
ported from the standalone officer-atelier service: providers (adapter-shaped, openai the only
adapter today), the generation queue, collections, and the database layer — kept on its own
Postgres database rather than folded into officer_db, since both collections and provider_settings
had rows on the machine this was extracted from.

what changed rather than just moved:
- images are served from an authenticated GET /files/:filename instead of an unauthenticated
  PUBLIC_URL-prefixed static route
- the OpenAI-key bootstrap fallback reads the secret store (getSecret) before .env, per-adapter
- provider_settings (the one-row-per-adapter shape providers replaced) did not come along — it has
  no callers left anywhere in the server code, only a migration script that also did not move

ping/echo (smoke-test routes with no caller) did not move either; the sidecar's own /_health covers
liveness.

not yet wired into the platform: sidecar/index.ts exists but nothing mounts it, and web/ has not
been written, so this is not yet a discoverable plugin.
2026-08-20 03:53:53 +00:00

101 lines
3.4 KiB
TypeScript

import { and, asc, eq, ne } from 'drizzle-orm';
import { db } from '../db';
import { providers } from './schema';
export type Provider = typeof providers.$inferSelect;
export type NewProvider = typeof providers.$inferInsert;
export async function listProviders(): Promise<Provider[]> {
return db.select().from(providers).orderBy(asc(providers.name));
}
export async function getProvider(id: string): Promise<Provider | undefined> {
const [row] = await db.select().from(providers).where(eq(providers.id, id)).limit(1);
return row;
}
export async function getProviderByName(name: string): Promise<Provider | undefined> {
const [row] = await db.select().from(providers).where(eq(providers.name, name)).limit(1);
return row;
}
/**
* Which provider serves a request, in the order a caller would expect.
*
* 1. the model, if exactly one enabled provider offers it
* 2. the model, preferring the default when several do — two keys for the same model is an
* ordinary thing and the tie has to break somewhere stable
* 3. the default provider, for a model nothing is configured for
*
* Step 3 is deliberate rather than a 404. Vendors ship models faster than rows get added, and the
* request still names a model the adapter will happily pass along — refusing it would make Atelier
* the reason a brand-new model cannot be used.
*/
export async function resolveProviderForModel(model: string | undefined): Promise<Provider | undefined> {
if (model) {
const candidates = await db
.select()
.from(providers)
.where(and(eq(providers.model, model), eq(providers.enabled, true)))
.orderBy(asc(providers.createdAt));
const match = candidates.find((row) => row.isDefault) ?? candidates[0];
if (match) return match;
}
return defaultProvider();
}
export async function defaultProvider(): Promise<Provider | undefined> {
const [byFlag] = await db
.select()
.from(providers)
.where(and(eq(providers.isDefault, true), eq(providers.enabled, true)))
.limit(1);
if (byFlag) return byFlag;
// No flag set — the oldest enabled row, so a single-provider install needs no configuration at all.
const [oldest] = await db
.select()
.from(providers)
.where(eq(providers.enabled, true))
.orderBy(asc(providers.createdAt))
.limit(1);
return oldest;
}
export async function createProvider(values: NewProvider): Promise<Provider> {
const [row] = await db.insert(providers).values(values).returning();
if (row?.isDefault) await clearOtherDefaults(row.id);
return row!;
}
export async function updateProvider(id: string, values: Partial<NewProvider>): Promise<Provider | undefined> {
const [row] = await db
.update(providers)
.set({ ...values, updatedAt: new Date() })
.where(eq(providers.id, id))
.returning();
if (row?.isDefault) await clearOtherDefaults(id);
return row;
}
export async function deleteProvider(id: string): Promise<void> {
await db.delete(providers).where(eq(providers.id, id));
}
/**
* Exactly one default.
*
* Enforced by clearing the others on write rather than by a partial unique index, because the write
* that sets a new default would otherwise have to clear the old one first — two statements racing to
* leave zero defaults instead of two.
*/
async function clearOtherDefaults(keepId: string): Promise<void> {
await db
.update(providers)
.set({ isDefault: false })
.where(and(eq(providers.isDefault, true), ne(providers.id, keepId)));
}