Prepare production and Coolify deployment with original exports and datetime pickers

This commit is contained in:
2026-09-09 17:59:20 -04:00
parent 574f29a68e
commit 64d7acc9c0
38 changed files with 1296 additions and 8 deletions
+16
View File
@@ -0,0 +1,16 @@
import { eq, sql } from "drizzle-orm";
import { closeDb, getDb, platformAdministrators, user } from "@album/database";
// Promote an existing verified account, never create credentials or bypass email verification.
const userId = process.argv[2];
if (!userId) throw new Error("Usage: bun packages/database/src/bootstrap-admin.ts <verified-user-id>");
try {
await getDb().transaction(async tx => {
await tx.execute(sql`LOCK TABLE platform_administrators IN EXCLUSIVE MODE`);
if ((await tx.select({id:platformAdministrators.id}).from(platformAdministrators).limit(1)).length) throw new Error("An administrator already exists; use the admin UI instead");
const [existing] = await tx.select({id:user.id,verified:user.emailVerified}).from(user).where(eq(user.id,userId));
if (!existing?.verified) throw new Error("Account must exist and have verified email");
await tx.insert(platformAdministrators).values({userId,role:"super_admin"});
});
console.info("Initial administrator promoted");
} finally { await closeDb(); }
+12
View File
@@ -199,6 +199,18 @@ export const emailWebhookEvents = pgTable("email_webhook_events", {
receivedAt: timestamp("received_at", { withTimezone: true }).notNull().defaultNow(),
}, (table) => [index("email_webhook_events_provider_idx").on(table.providerId)]);
export const photoExports = pgTable("photo_exports", {
id: uuid("id").defaultRandom().primaryKey(),
eventId: uuid("event_id").notNull().references(() => events.id, { onDelete: "cascade" }),
requestedBy: text("requested_by").notNull().references(() => user.id, { onDelete: "cascade" }),
filter: text("filter").notNull(),
status: text("status").notNull().default("pending"),
total: integer("total").notNull().default(0),
processed: integer("processed").notNull().default(0),
expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
...timestamps,
}, (table) => [index("photo_exports_queue_idx").on(table.status, table.createdAt), uniqueIndex("photo_exports_active_idx").on(table.eventId,table.requestedBy).where(sql`${table.status} IN ('pending', 'processing')`), check("photo_exports_filter_check",sql`${table.filter} IN ('approved', 'all')`)]);
export const eventBanners = pgTable("event_banners", {
id: uuid("id").defaultRandom().primaryKey(),
eventId: uuid("event_id").notNull().references(() => events.id, { onDelete: "cascade" }),