Files
timmy-tower/lib/db/src/schema/relay-event-queue.ts

45 lines
1.9 KiB
TypeScript

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<QueueStatus>()
.notNull()
.default("pending"),
// "timmy_ai" or "admin" — null until a decision is made
reviewedBy: text("reviewed_by").$type<QueueReviewer>(),
reviewReason: text("review_reason"),
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
decidedAt: timestamp("decided_at", { withTimezone: true }),
});
export type RelayEventQueueRow = typeof relayEventQueue.$inferSelect;