import { pgTable, text, timestamp, integer } from "drizzle-orm/pg-core"; import { nostrIdentities } from "./nostr-identities"; // ── Status + reviewer types ─────────────────────────────────────────────────── export const QUEUE_STATUSES = ["pending", "approved", "rejected", "auto_approved", "flagged"] as const; export type QueueStatus = (typeof QUEUE_STATUSES)[number]; export const QUEUE_REVIEWERS = ["timmy_ai", "admin"] as const; export type QueueReviewer = (typeof QUEUE_REVIEWERS)[number]; // ── relay_event_queue ───────────────────────────────────────────────────────── // Holds every event submitted by whitelisted (non-elite) accounts. // Events wait here as "pending" until Timmy AI or an admin approves/rejects. // On approval the API server injects the event into strfry via HTTP import. // Elite accounts bypass this table entirely. export const relayEventQueue = pgTable("relay_event_queue", { eventId: text("event_id").primaryKey(), pubkey: text("pubkey") .notNull() .references(() => nostrIdentities.pubkey, { onDelete: "cascade" }), kind: integer("kind").notNull(), // Full raw NIP-01 event JSON, stored as text so it can be forwarded to strfry rawEvent: text("raw_event").notNull(), status: text("status") .$type() .notNull() .default("pending"), // "timmy_ai" or "admin" — null until a decision is made reviewedBy: text("reviewed_by").$type(), reviewReason: text("review_reason"), createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(), decidedAt: timestamp("decided_at", { withTimezone: true }), }); export type RelayEventQueueRow = typeof relayEventQueue.$inferSelect;