Initial import
This commit is contained in:
0
packages/db/prisma/.gitkeep
Normal file
0
packages/db/prisma/.gitkeep
Normal file
366
packages/db/prisma/migrations/20260309181500_init/migration.sql
Normal file
366
packages/db/prisma/migrations/20260309181500_init/migration.sql
Normal file
@@ -0,0 +1,366 @@
|
||||
-- CreateSchema
|
||||
CREATE SCHEMA IF NOT EXISTS "public";
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "SubscriptionStatus" AS ENUM ('pending_activation', 'active', 'past_due', 'canceled', 'expired');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "PaymentInvoiceStatus" AS ENUM ('pending', 'paid', 'expired', 'canceled');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "GenerationMode" AS ENUM ('text_to_image', 'image_to_image');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "GenerationRequestStatus" AS ENUM ('queued', 'running', 'succeeded', 'failed', 'canceled');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "GenerationAttemptStatus" AS ENUM ('started', 'succeeded', 'failed');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "ProviderFailureCategory" AS ENUM ('transport', 'timeout', 'provider_5xx', 'provider_4xx_user', 'insufficient_funds', 'unknown');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "ProviderKeyState" AS ENUM ('active', 'cooldown', 'out_of_funds', 'manual_review', 'disabled');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "UsageLedgerEntryType" AS ENUM ('cycle_reset', 'generation_success', 'manual_adjustment', 'refund');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "TelegramPairingStatus" AS ENUM ('pending', 'completed', 'expired', 'revoked');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "AdminActorType" AS ENUM ('system', 'web_admin', 'telegram_admin', 'cli_operator');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "User" (
|
||||
"id" TEXT NOT NULL,
|
||||
"email" TEXT NOT NULL,
|
||||
"passwordHash" TEXT NOT NULL,
|
||||
"passwordResetVersion" INTEGER NOT NULL DEFAULT 0,
|
||||
"isAdmin" BOOLEAN NOT NULL DEFAULT false,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "SubscriptionPlan" (
|
||||
"id" TEXT NOT NULL,
|
||||
"code" TEXT NOT NULL,
|
||||
"displayName" TEXT NOT NULL,
|
||||
"monthlyRequestLimit" INTEGER NOT NULL,
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "SubscriptionPlan_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Subscription" (
|
||||
"id" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"planId" TEXT NOT NULL,
|
||||
"status" "SubscriptionStatus" NOT NULL,
|
||||
"renewsManually" BOOLEAN NOT NULL DEFAULT true,
|
||||
"activatedAt" TIMESTAMP(3),
|
||||
"currentPeriodStart" TIMESTAMP(3),
|
||||
"currentPeriodEnd" TIMESTAMP(3),
|
||||
"canceledAt" TIMESTAMP(3),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Subscription_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "PaymentInvoice" (
|
||||
"id" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"subscriptionId" TEXT,
|
||||
"provider" TEXT NOT NULL,
|
||||
"providerInvoiceId" TEXT,
|
||||
"status" "PaymentInvoiceStatus" NOT NULL,
|
||||
"currency" TEXT NOT NULL,
|
||||
"amountCrypto" DECIMAL(20,8) NOT NULL,
|
||||
"amountUsd" DECIMAL(12,2),
|
||||
"paymentAddress" TEXT,
|
||||
"expiresAt" TIMESTAMP(3),
|
||||
"paidAt" TIMESTAMP(3),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "PaymentInvoice_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "GenerationRequest" (
|
||||
"id" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"mode" "GenerationMode" NOT NULL,
|
||||
"status" "GenerationRequestStatus" NOT NULL DEFAULT 'queued',
|
||||
"providerModel" TEXT NOT NULL,
|
||||
"prompt" TEXT NOT NULL,
|
||||
"sourceImageKey" TEXT,
|
||||
"resolutionPreset" TEXT NOT NULL,
|
||||
"batchSize" INTEGER NOT NULL,
|
||||
"imageStrength" DECIMAL(4,3),
|
||||
"idempotencyKey" TEXT,
|
||||
"terminalErrorCode" TEXT,
|
||||
"terminalErrorText" TEXT,
|
||||
"requestedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"startedAt" TIMESTAMP(3),
|
||||
"completedAt" TIMESTAMP(3),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "GenerationRequest_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "GenerationAttempt" (
|
||||
"id" TEXT NOT NULL,
|
||||
"generationRequestId" TEXT NOT NULL,
|
||||
"providerKeyId" TEXT NOT NULL,
|
||||
"attemptIndex" INTEGER NOT NULL,
|
||||
"status" "GenerationAttemptStatus" NOT NULL DEFAULT 'started',
|
||||
"usedProxy" BOOLEAN NOT NULL DEFAULT false,
|
||||
"directFallbackUsed" BOOLEAN NOT NULL DEFAULT false,
|
||||
"failureCategory" "ProviderFailureCategory",
|
||||
"providerHttpStatus" INTEGER,
|
||||
"providerErrorCode" TEXT,
|
||||
"providerErrorText" TEXT,
|
||||
"startedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"completedAt" TIMESTAMP(3),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "GenerationAttempt_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "GeneratedAsset" (
|
||||
"id" TEXT NOT NULL,
|
||||
"generationRequestId" TEXT NOT NULL,
|
||||
"objectKey" TEXT NOT NULL,
|
||||
"mimeType" TEXT NOT NULL,
|
||||
"width" INTEGER,
|
||||
"height" INTEGER,
|
||||
"bytes" INTEGER,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "GeneratedAsset_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "UsageLedgerEntry" (
|
||||
"id" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"generationRequestId" TEXT,
|
||||
"entryType" "UsageLedgerEntryType" NOT NULL,
|
||||
"deltaRequests" INTEGER NOT NULL,
|
||||
"cycleStartedAt" TIMESTAMP(3),
|
||||
"cycleEndsAt" TIMESTAMP(3),
|
||||
"note" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "UsageLedgerEntry_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "ProviderProxy" (
|
||||
"id" TEXT NOT NULL,
|
||||
"label" TEXT NOT NULL,
|
||||
"baseUrl" TEXT NOT NULL,
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "ProviderProxy_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "ProviderKey" (
|
||||
"id" TEXT NOT NULL,
|
||||
"providerCode" TEXT NOT NULL,
|
||||
"label" TEXT NOT NULL,
|
||||
"apiKeyCiphertext" TEXT NOT NULL,
|
||||
"apiKeyLastFour" TEXT NOT NULL,
|
||||
"state" "ProviderKeyState" NOT NULL DEFAULT 'active',
|
||||
"roundRobinOrder" INTEGER NOT NULL,
|
||||
"consecutiveRetryableFailures" INTEGER NOT NULL DEFAULT 0,
|
||||
"cooldownUntil" TIMESTAMP(3),
|
||||
"lastErrorCategory" "ProviderFailureCategory",
|
||||
"lastErrorCode" TEXT,
|
||||
"lastErrorAt" TIMESTAMP(3),
|
||||
"balanceMinorUnits" BIGINT,
|
||||
"balanceCurrency" TEXT,
|
||||
"balanceRefreshedAt" TIMESTAMP(3),
|
||||
"proxyId" TEXT,
|
||||
"disabledAt" TIMESTAMP(3),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "ProviderKey_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "ProviderKeyStatusEvent" (
|
||||
"id" TEXT NOT NULL,
|
||||
"providerKeyId" TEXT NOT NULL,
|
||||
"fromState" "ProviderKeyState",
|
||||
"toState" "ProviderKeyState" NOT NULL,
|
||||
"reason" TEXT NOT NULL,
|
||||
"errorCategory" "ProviderFailureCategory",
|
||||
"errorCode" TEXT,
|
||||
"actorType" "AdminActorType" NOT NULL,
|
||||
"actorRef" TEXT,
|
||||
"metadata" JSONB,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "ProviderKeyStatusEvent_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "TelegramPairing" (
|
||||
"id" TEXT NOT NULL,
|
||||
"telegramUserId" TEXT NOT NULL,
|
||||
"telegramUsername" TEXT,
|
||||
"displayNameSnapshot" TEXT NOT NULL,
|
||||
"codeHash" TEXT NOT NULL,
|
||||
"expiresAt" TIMESTAMP(3) NOT NULL,
|
||||
"status" "TelegramPairingStatus" NOT NULL DEFAULT 'pending',
|
||||
"completedAt" TIMESTAMP(3),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "TelegramPairing_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "TelegramAdminAllowlistEntry" (
|
||||
"telegramUserId" TEXT NOT NULL,
|
||||
"telegramUsername" TEXT,
|
||||
"displayNameSnapshot" TEXT NOT NULL,
|
||||
"pairedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"revokedAt" TIMESTAMP(3),
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "TelegramAdminAllowlistEntry_pkey" PRIMARY KEY ("telegramUserId")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "AdminAuditLog" (
|
||||
"id" TEXT NOT NULL,
|
||||
"actorType" "AdminActorType" NOT NULL,
|
||||
"actorRef" TEXT,
|
||||
"action" TEXT NOT NULL,
|
||||
"targetType" TEXT NOT NULL,
|
||||
"targetId" TEXT,
|
||||
"metadata" JSONB,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "AdminAuditLog_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "SubscriptionPlan_code_key" ON "SubscriptionPlan"("code");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Subscription_userId_status_idx" ON "Subscription"("userId", "status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "PaymentInvoice_providerInvoiceId_key" ON "PaymentInvoice"("providerInvoiceId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "PaymentInvoice_userId_status_idx" ON "PaymentInvoice"("userId", "status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "GenerationRequest_idempotencyKey_key" ON "GenerationRequest"("idempotencyKey");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "GenerationRequest_userId_status_requestedAt_idx" ON "GenerationRequest"("userId", "status", "requestedAt");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "GenerationAttempt_providerKeyId_startedAt_idx" ON "GenerationAttempt"("providerKeyId", "startedAt");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "GenerationAttempt_generationRequestId_attemptIndex_key" ON "GenerationAttempt"("generationRequestId", "attemptIndex");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "GeneratedAsset_objectKey_key" ON "GeneratedAsset"("objectKey");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "GeneratedAsset_generationRequestId_idx" ON "GeneratedAsset"("generationRequestId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "UsageLedgerEntry_generationRequestId_key" ON "UsageLedgerEntry"("generationRequestId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "UsageLedgerEntry_userId_createdAt_idx" ON "UsageLedgerEntry"("userId", "createdAt");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "ProviderProxy_label_key" ON "ProviderProxy"("label");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "ProviderKey_label_key" ON "ProviderKey"("label");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ProviderKey_providerCode_state_roundRobinOrder_idx" ON "ProviderKey"("providerCode", "state", "roundRobinOrder");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ProviderKeyStatusEvent_providerKeyId_createdAt_idx" ON "ProviderKeyStatusEvent"("providerKeyId", "createdAt");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "TelegramPairing_telegramUserId_status_idx" ON "TelegramPairing"("telegramUserId", "status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "TelegramPairing_expiresAt_status_idx" ON "TelegramPairing"("expiresAt", "status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "AdminAuditLog_targetType_targetId_createdAt_idx" ON "AdminAuditLog"("targetType", "targetId", "createdAt");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "AdminAuditLog_actorType_createdAt_idx" ON "AdminAuditLog"("actorType", "createdAt");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Subscription" ADD CONSTRAINT "Subscription_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Subscription" ADD CONSTRAINT "Subscription_planId_fkey" FOREIGN KEY ("planId") REFERENCES "SubscriptionPlan"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "PaymentInvoice" ADD CONSTRAINT "PaymentInvoice_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "PaymentInvoice" ADD CONSTRAINT "PaymentInvoice_subscriptionId_fkey" FOREIGN KEY ("subscriptionId") REFERENCES "Subscription"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "GenerationRequest" ADD CONSTRAINT "GenerationRequest_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "GenerationAttempt" ADD CONSTRAINT "GenerationAttempt_generationRequestId_fkey" FOREIGN KEY ("generationRequestId") REFERENCES "GenerationRequest"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "GenerationAttempt" ADD CONSTRAINT "GenerationAttempt_providerKeyId_fkey" FOREIGN KEY ("providerKeyId") REFERENCES "ProviderKey"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "GeneratedAsset" ADD CONSTRAINT "GeneratedAsset_generationRequestId_fkey" FOREIGN KEY ("generationRequestId") REFERENCES "GenerationRequest"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "UsageLedgerEntry" ADD CONSTRAINT "UsageLedgerEntry_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "UsageLedgerEntry" ADD CONSTRAINT "UsageLedgerEntry_generationRequestId_fkey" FOREIGN KEY ("generationRequestId") REFERENCES "GenerationRequest"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "ProviderKey" ADD CONSTRAINT "ProviderKey_proxyId_fkey" FOREIGN KEY ("proxyId") REFERENCES "ProviderProxy"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "ProviderKeyStatusEvent" ADD CONSTRAINT "ProviderKeyStatusEvent_providerKeyId_fkey" FOREIGN KEY ("providerKeyId") REFERENCES "ProviderKey"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
CREATE TABLE "UserSession" (
|
||||
"id" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"tokenHash" TEXT NOT NULL,
|
||||
"expiresAt" TIMESTAMP(3) NOT NULL,
|
||||
"revokedAt" TIMESTAMP(3),
|
||||
"lastSeenAt" TIMESTAMP(3),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "UserSession_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX "UserSession_tokenHash_key" ON "UserSession"("tokenHash");
|
||||
CREATE INDEX "UserSession_userId_createdAt_idx" ON "UserSession"("userId", "createdAt");
|
||||
CREATE INDEX "UserSession_expiresAt_revokedAt_idx" ON "UserSession"("expiresAt", "revokedAt");
|
||||
|
||||
ALTER TABLE "UserSession"
|
||||
ADD CONSTRAINT "UserSession_userId_fkey"
|
||||
FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,18 @@
|
||||
CREATE TABLE "PasswordResetToken" (
|
||||
"id" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"tokenHash" TEXT NOT NULL,
|
||||
"expiresAt" TIMESTAMP(3) NOT NULL,
|
||||
"consumedAt" TIMESTAMP(3),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "PasswordResetToken_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX "PasswordResetToken_tokenHash_key" ON "PasswordResetToken"("tokenHash");
|
||||
CREATE INDEX "PasswordResetToken_userId_createdAt_idx" ON "PasswordResetToken"("userId", "createdAt");
|
||||
CREATE INDEX "PasswordResetToken_expiresAt_consumedAt_idx" ON "PasswordResetToken"("expiresAt", "consumedAt");
|
||||
|
||||
ALTER TABLE "PasswordResetToken"
|
||||
ADD CONSTRAINT "PasswordResetToken_userId_fkey"
|
||||
FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE "SubscriptionPlan"
|
||||
ADD COLUMN "monthlyPriceUsd" DECIMAL(12,2) NOT NULL DEFAULT 9.99,
|
||||
ADD COLUMN "billingCurrency" TEXT NOT NULL DEFAULT 'USDT';
|
||||
2
packages/db/prisma/migrations/migration_lock.toml
Normal file
2
packages/db/prisma/migrations/migration_lock.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
# Do not edit by hand unless you are intentionally resetting migration history.
|
||||
provider = "postgresql"
|
||||
351
packages/db/prisma/schema.prisma
Normal file
351
packages/db/prisma/schema.prisma
Normal file
@@ -0,0 +1,351 @@
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
enum SubscriptionStatus {
|
||||
pending_activation
|
||||
active
|
||||
past_due
|
||||
canceled
|
||||
expired
|
||||
}
|
||||
|
||||
enum PaymentInvoiceStatus {
|
||||
pending
|
||||
paid
|
||||
expired
|
||||
canceled
|
||||
}
|
||||
|
||||
enum GenerationMode {
|
||||
text_to_image
|
||||
image_to_image
|
||||
}
|
||||
|
||||
enum GenerationRequestStatus {
|
||||
queued
|
||||
running
|
||||
succeeded
|
||||
failed
|
||||
canceled
|
||||
}
|
||||
|
||||
enum GenerationAttemptStatus {
|
||||
started
|
||||
succeeded
|
||||
failed
|
||||
}
|
||||
|
||||
enum ProviderFailureCategory {
|
||||
transport
|
||||
timeout
|
||||
provider_5xx
|
||||
provider_4xx_user
|
||||
insufficient_funds
|
||||
unknown
|
||||
}
|
||||
|
||||
enum ProviderKeyState {
|
||||
active
|
||||
cooldown
|
||||
out_of_funds
|
||||
manual_review
|
||||
disabled
|
||||
}
|
||||
|
||||
enum UsageLedgerEntryType {
|
||||
cycle_reset
|
||||
generation_success
|
||||
manual_adjustment
|
||||
refund
|
||||
}
|
||||
|
||||
enum TelegramPairingStatus {
|
||||
pending
|
||||
completed
|
||||
expired
|
||||
revoked
|
||||
}
|
||||
|
||||
enum AdminActorType {
|
||||
system
|
||||
web_admin
|
||||
telegram_admin
|
||||
cli_operator
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(cuid())
|
||||
email String @unique
|
||||
passwordHash String
|
||||
passwordResetVersion Int @default(0)
|
||||
isAdmin Boolean @default(false)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
subscriptions Subscription[]
|
||||
invoices PaymentInvoice[]
|
||||
generationRequests GenerationRequest[]
|
||||
usageLedgerEntries UsageLedgerEntry[]
|
||||
sessions UserSession[]
|
||||
passwordResetTokens PasswordResetToken[]
|
||||
}
|
||||
|
||||
model UserSession {
|
||||
id String @id @default(cuid())
|
||||
userId String
|
||||
tokenHash String @unique
|
||||
expiresAt DateTime
|
||||
revokedAt DateTime?
|
||||
lastSeenAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([userId, createdAt])
|
||||
@@index([expiresAt, revokedAt])
|
||||
}
|
||||
|
||||
model PasswordResetToken {
|
||||
id String @id @default(cuid())
|
||||
userId String
|
||||
tokenHash String @unique
|
||||
expiresAt DateTime
|
||||
consumedAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([userId, createdAt])
|
||||
@@index([expiresAt, consumedAt])
|
||||
}
|
||||
|
||||
model SubscriptionPlan {
|
||||
id String @id @default(cuid())
|
||||
code String @unique
|
||||
displayName String
|
||||
monthlyRequestLimit Int
|
||||
monthlyPriceUsd Decimal @db.Decimal(12, 2)
|
||||
billingCurrency String
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
subscriptions Subscription[]
|
||||
}
|
||||
|
||||
model Subscription {
|
||||
id String @id @default(cuid())
|
||||
userId String
|
||||
planId String
|
||||
status SubscriptionStatus
|
||||
renewsManually Boolean @default(true)
|
||||
activatedAt DateTime?
|
||||
currentPeriodStart DateTime?
|
||||
currentPeriodEnd DateTime?
|
||||
canceledAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
plan SubscriptionPlan @relation(fields: [planId], references: [id], onDelete: Restrict)
|
||||
invoices PaymentInvoice[]
|
||||
|
||||
@@index([userId, status])
|
||||
}
|
||||
|
||||
model PaymentInvoice {
|
||||
id String @id @default(cuid())
|
||||
userId String
|
||||
subscriptionId String?
|
||||
provider String
|
||||
providerInvoiceId String? @unique
|
||||
status PaymentInvoiceStatus
|
||||
currency String
|
||||
amountCrypto Decimal @db.Decimal(20, 8)
|
||||
amountUsd Decimal? @db.Decimal(12, 2)
|
||||
paymentAddress String?
|
||||
expiresAt DateTime?
|
||||
paidAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
subscription Subscription? @relation(fields: [subscriptionId], references: [id], onDelete: SetNull)
|
||||
|
||||
@@index([userId, status])
|
||||
}
|
||||
|
||||
model GenerationRequest {
|
||||
id String @id @default(cuid())
|
||||
userId String
|
||||
mode GenerationMode
|
||||
status GenerationRequestStatus @default(queued)
|
||||
providerModel String
|
||||
prompt String
|
||||
sourceImageKey String?
|
||||
resolutionPreset String
|
||||
batchSize Int
|
||||
imageStrength Decimal? @db.Decimal(4, 3)
|
||||
idempotencyKey String? @unique
|
||||
terminalErrorCode String?
|
||||
terminalErrorText String?
|
||||
requestedAt DateTime @default(now())
|
||||
startedAt DateTime?
|
||||
completedAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
attempts GenerationAttempt[]
|
||||
assets GeneratedAsset[]
|
||||
usageLedgerEntry UsageLedgerEntry?
|
||||
|
||||
@@index([userId, status, requestedAt])
|
||||
}
|
||||
|
||||
model GenerationAttempt {
|
||||
id String @id @default(cuid())
|
||||
generationRequestId String
|
||||
providerKeyId String
|
||||
attemptIndex Int
|
||||
status GenerationAttemptStatus @default(started)
|
||||
usedProxy Boolean @default(false)
|
||||
directFallbackUsed Boolean @default(false)
|
||||
failureCategory ProviderFailureCategory?
|
||||
providerHttpStatus Int?
|
||||
providerErrorCode String?
|
||||
providerErrorText String?
|
||||
startedAt DateTime @default(now())
|
||||
completedAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
generationRequest GenerationRequest @relation(fields: [generationRequestId], references: [id], onDelete: Cascade)
|
||||
providerKey ProviderKey @relation(fields: [providerKeyId], references: [id], onDelete: Restrict)
|
||||
|
||||
@@unique([generationRequestId, attemptIndex])
|
||||
@@index([providerKeyId, startedAt])
|
||||
}
|
||||
|
||||
model GeneratedAsset {
|
||||
id String @id @default(cuid())
|
||||
generationRequestId String
|
||||
objectKey String @unique
|
||||
mimeType String
|
||||
width Int?
|
||||
height Int?
|
||||
bytes Int?
|
||||
createdAt DateTime @default(now())
|
||||
generationRequest GenerationRequest @relation(fields: [generationRequestId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([generationRequestId])
|
||||
}
|
||||
|
||||
model UsageLedgerEntry {
|
||||
id String @id @default(cuid())
|
||||
userId String
|
||||
generationRequestId String? @unique
|
||||
entryType UsageLedgerEntryType
|
||||
deltaRequests Int
|
||||
cycleStartedAt DateTime?
|
||||
cycleEndsAt DateTime?
|
||||
note String?
|
||||
createdAt DateTime @default(now())
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
generationRequest GenerationRequest? @relation(fields: [generationRequestId], references: [id], onDelete: SetNull)
|
||||
|
||||
@@index([userId, createdAt])
|
||||
}
|
||||
|
||||
model ProviderProxy {
|
||||
id String @id @default(cuid())
|
||||
label String @unique
|
||||
baseUrl String
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
providerKeys ProviderKey[]
|
||||
}
|
||||
|
||||
model ProviderKey {
|
||||
id String @id @default(cuid())
|
||||
providerCode String
|
||||
label String @unique
|
||||
apiKeyCiphertext String
|
||||
apiKeyLastFour String
|
||||
state ProviderKeyState @default(active)
|
||||
roundRobinOrder Int
|
||||
consecutiveRetryableFailures Int @default(0)
|
||||
cooldownUntil DateTime?
|
||||
lastErrorCategory ProviderFailureCategory?
|
||||
lastErrorCode String?
|
||||
lastErrorAt DateTime?
|
||||
balanceMinorUnits BigInt?
|
||||
balanceCurrency String?
|
||||
balanceRefreshedAt DateTime?
|
||||
proxyId String?
|
||||
disabledAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
proxy ProviderProxy? @relation(fields: [proxyId], references: [id], onDelete: SetNull)
|
||||
attempts GenerationAttempt[]
|
||||
statusEvents ProviderKeyStatusEvent[]
|
||||
|
||||
@@index([providerCode, state, roundRobinOrder])
|
||||
}
|
||||
|
||||
model ProviderKeyStatusEvent {
|
||||
id String @id @default(cuid())
|
||||
providerKeyId String
|
||||
fromState ProviderKeyState?
|
||||
toState ProviderKeyState
|
||||
reason String
|
||||
errorCategory ProviderFailureCategory?
|
||||
errorCode String?
|
||||
actorType AdminActorType
|
||||
actorRef String?
|
||||
metadata Json?
|
||||
createdAt DateTime @default(now())
|
||||
providerKey ProviderKey @relation(fields: [providerKeyId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([providerKeyId, createdAt])
|
||||
}
|
||||
|
||||
model TelegramPairing {
|
||||
id String @id @default(cuid())
|
||||
telegramUserId String
|
||||
telegramUsername String?
|
||||
displayNameSnapshot String
|
||||
codeHash String
|
||||
expiresAt DateTime
|
||||
status TelegramPairingStatus @default(pending)
|
||||
completedAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([telegramUserId, status])
|
||||
@@index([expiresAt, status])
|
||||
}
|
||||
|
||||
model TelegramAdminAllowlistEntry {
|
||||
telegramUserId String @id
|
||||
telegramUsername String?
|
||||
displayNameSnapshot String
|
||||
pairedAt DateTime @default(now())
|
||||
revokedAt DateTime?
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model AdminAuditLog {
|
||||
id String @id @default(cuid())
|
||||
actorType AdminActorType
|
||||
actorRef String?
|
||||
action String
|
||||
targetType String
|
||||
targetId String?
|
||||
metadata Json?
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@index([targetType, targetId, createdAt])
|
||||
@@index([actorType, createdAt])
|
||||
}
|
||||
Reference in New Issue
Block a user