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
@@ -0,0 +1,15 @@
CREATE TABLE photo_exports (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
event_id uuid NOT NULL REFERENCES events(id) ON DELETE CASCADE,
requested_by text NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
filter text NOT NULL CHECK (filter IN ('approved', 'all')),
status text NOT NULL DEFAULT 'pending',
total integer NOT NULL DEFAULT 0,
processed integer NOT NULL DEFAULT 0,
expires_at timestamptz NOT NULL,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX photo_exports_queue_idx ON photo_exports(status, created_at);
CREATE UNIQUE INDEX photo_exports_active_idx ON photo_exports(event_id, requested_by)
WHERE status IN ('pending', 'processing');
@@ -64,6 +64,13 @@
"when": 1788906000000,
"tag": "0008_email_webhooks",
"breakpoints": true
},
{
"idx": 9,
"version": "7",
"when": 1788906100000,
"tag": "0009_photo_exports",
"breakpoints": true
}
]
}
+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" }),