Comprehensive admin + user dashboards (production-ready)
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "content_flag" ADD COLUMN "severity" TEXT NOT NULL DEFAULT 'medium',
|
||||
ADD COLUMN "source" TEXT NOT NULL DEFAULT 'system';
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "worker_heartbeat" (
|
||||
"name" TEXT NOT NULL,
|
||||
"lastBeatAt" TIMESTAMP(3) NOT NULL,
|
||||
"queued" INTEGER,
|
||||
"running" INTEGER,
|
||||
"meta" JSONB,
|
||||
|
||||
CONSTRAINT "worker_heartbeat_pkey" PRIMARY KEY ("name")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "webhook_event" (
|
||||
"id" TEXT NOT NULL,
|
||||
"provider" TEXT NOT NULL,
|
||||
"eventId" TEXT NOT NULL,
|
||||
"type" TEXT NOT NULL,
|
||||
"status" TEXT NOT NULL DEFAULT 'processed',
|
||||
"error" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "webhook_event_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "webhook_event_eventId_key" ON "webhook_event"("eventId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "webhook_event_provider_createdAt_idx" ON "webhook_event"("provider", "createdAt");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "episode_createdAt_idx" ON "episode"("createdAt");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "subscription_status_idx" ON "subscription"("status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "subscription_createdAt_idx" ON "subscription"("createdAt");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "user_createdAt_idx" ON "user"("createdAt");
|
||||
@@ -0,0 +1,25 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "episode" ADD COLUMN "shareId" TEXT,
|
||||
ADD COLUMN "sharedAt" TIMESTAMP(3);
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "user" ADD COLUMN "onboardedAt" TIMESTAMP(3);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "user_preferences" (
|
||||
"userId" TEXT NOT NULL,
|
||||
"defaultVoiceId" TEXT,
|
||||
"defaultLanguage" TEXT NOT NULL DEFAULT 'en',
|
||||
"emailOnEpisodeReady" BOOLEAN NOT NULL DEFAULT true,
|
||||
"productEmails" BOOLEAN NOT NULL DEFAULT true,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "user_preferences_pkey" PRIMARY KEY ("userId")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "episode_shareId_key" ON "episode"("shareId");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "user_preferences" ADD CONSTRAINT "user_preferences_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
+53
-2
@@ -30,6 +30,7 @@ model User {
|
||||
// app fields
|
||||
stripeCustomerId String?
|
||||
paypalPayerId String?
|
||||
onboardedAt DateTime?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
@@ -37,13 +38,15 @@ model User {
|
||||
sessions Session[]
|
||||
accounts Account[]
|
||||
memberships Member[]
|
||||
sentInvitations Invitation[] @relation("SentInvitations")
|
||||
sentInvitations Invitation[] @relation("SentInvitations")
|
||||
episodes Episode[]
|
||||
series Series[]
|
||||
apiKeys ApiKey[]
|
||||
usageRecords UsageRecord[]
|
||||
auditLogs AuditLog[] @relation("ActorLogs")
|
||||
auditLogs AuditLog[] @relation("ActorLogs")
|
||||
preferences UserPreferences?
|
||||
|
||||
@@index([createdAt])
|
||||
@@map("user")
|
||||
}
|
||||
|
||||
@@ -210,6 +213,8 @@ model Subscription {
|
||||
@@index([referenceId])
|
||||
@@index([stripeSubscriptionId])
|
||||
@@index([paypalSubscriptionId])
|
||||
@@index([status])
|
||||
@@index([createdAt])
|
||||
@@map("subscription")
|
||||
}
|
||||
|
||||
@@ -290,6 +295,11 @@ model Episode {
|
||||
stage String? // human-readable current step
|
||||
errorMessage String?
|
||||
|
||||
// Public share: when shareId is set, the episode is reachable at /p/<shareId>
|
||||
// without auth. Clearing shareId disables the public page.
|
||||
shareId String? @unique
|
||||
sharedAt DateTime?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@ -305,6 +315,7 @@ model Episode {
|
||||
@@index([organizationId])
|
||||
@@index([seriesId])
|
||||
@@index([status])
|
||||
@@index([createdAt])
|
||||
@@map("episode")
|
||||
}
|
||||
|
||||
@@ -483,6 +494,8 @@ model ContentFlag {
|
||||
episodeId String
|
||||
episode Episode @relation(fields: [episodeId], references: [id], onDelete: Cascade)
|
||||
reason String
|
||||
source String @default("system") // "moderation" | "report" | "system"
|
||||
severity String @default("medium") // "low" | "medium" | "high"
|
||||
status String @default("open") // open | reviewed | removed
|
||||
reviewedBy String?
|
||||
createdAt DateTime @default(now())
|
||||
@@ -490,3 +503,41 @@ model ContentFlag {
|
||||
@@index([status])
|
||||
@@map("content_flag")
|
||||
}
|
||||
|
||||
/// Liveness of a long-running worker process (heartbeat). One row per worker name.
|
||||
model WorkerHeartbeat {
|
||||
name String @id
|
||||
lastBeatAt DateTime
|
||||
queued Int?
|
||||
running Int?
|
||||
meta Json?
|
||||
|
||||
@@map("worker_heartbeat")
|
||||
}
|
||||
|
||||
/// Per-user editor defaults and notification preferences (settings page).
|
||||
model UserPreferences {
|
||||
userId String @id
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
defaultVoiceId String?
|
||||
defaultLanguage String @default("en")
|
||||
emailOnEpisodeReady Boolean @default(true)
|
||||
productEmails Boolean @default(true)
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@map("user_preferences")
|
||||
}
|
||||
|
||||
/// Billing webhook delivery log — also dedups replayed events on `eventId`.
|
||||
model WebhookEvent {
|
||||
id String @id @default(cuid())
|
||||
provider String // "stripe" | "paypal"
|
||||
eventId String @unique
|
||||
type String
|
||||
status String @default("processed") // processed | failed | skipped
|
||||
error String?
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@index([provider, createdAt])
|
||||
@@map("webhook_event")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user