fix(admin): migration Subscription/BYOK et repli getUsers
Some checks failed
CI / Deploy production (on server) (push) Has been cancelled
CI / Lint, Test & Build (push) Has been cancelled

La table Subscription était dans le schéma sans migration SQL, ce qui
cassait le rendu Server Components de /admin. Migration idempotente +
fallback getUsers si la jointure échoue encore.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Antigravity
2026-05-17 13:56:02 +00:00
parent 759487cb36
commit 37a88d7e3d
2 changed files with 143 additions and 12 deletions

View File

@@ -23,9 +23,31 @@ async function checkAdmin() {
return session
}
const userListSelect = {
id: true,
name: true,
email: true,
role: true,
createdAt: true,
subscription: {
select: {
tier: true,
status: true,
currentPeriodEnd: true,
},
},
} as const
export async function getUsers() {
await checkAdmin()
try {
return await prisma.user.findMany({
orderBy: { createdAt: 'desc' },
select: userListSelect,
})
} catch (error) {
// Prod DB parfois en retard sur les migrations (ex. table Subscription absente).
console.error('getUsers with subscription failed, fallback without:', error)
const users = await prisma.user.findMany({
orderBy: { createdAt: 'desc' },
select: {
@@ -34,19 +56,9 @@ export async function getUsers() {
email: true,
role: true,
createdAt: true,
subscription: {
select: {
tier: true,
status: true,
currentPeriodEnd: true,
}
}
}
},
})
return users
} catch (error) {
console.error('Failed to fetch users:', error)
throw new Error('Failed to fetch users')
return users.map((u) => ({ ...u, subscription: null }))
}
}

View File

@@ -0,0 +1,119 @@
-- Tables présentes dans schema.prisma mais jamais migrées (admin / billing / BYOK).
DO $$ BEGIN
CREATE TYPE "SubscriptionTier" AS ENUM ('BASIC', 'PRO', 'BUSINESS', 'ENTERPRISE');
EXCEPTION
WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
CREATE TYPE "SubscriptionStatus" AS ENUM ('ACTIVE', 'PAST_DUE', 'CANCELED', 'TRIALING', 'INACTIVE');
EXCEPTION
WHEN duplicate_object THEN NULL;
END $$;
CREATE TABLE IF NOT EXISTS "UserAPIKey" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"provider" TEXT NOT NULL,
"alias" TEXT NOT NULL DEFAULT '',
"encryptedKey" TEXT NOT NULL,
"keyHash" TEXT NOT NULL,
"model" TEXT,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"lastUsedAt" TIMESTAMP(3),
"lastUsedFor" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "UserAPIKey_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX IF NOT EXISTS "UserAPIKey_userId_provider_key" ON "UserAPIKey"("userId", "provider");
CREATE INDEX IF NOT EXISTS "UserAPIKey_userId_idx" ON "UserAPIKey"("userId");
CREATE INDEX IF NOT EXISTS "UserAPIKey_keyHash_idx" ON "UserAPIKey"("keyHash");
DO $$ BEGIN
ALTER TABLE "UserAPIKey" ADD CONSTRAINT "UserAPIKey_userId_fkey"
FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION
WHEN duplicate_object THEN NULL;
END $$;
CREATE TABLE IF NOT EXISTS "Subscription" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"tier" "SubscriptionTier" NOT NULL DEFAULT 'BASIC',
"status" "SubscriptionStatus" NOT NULL DEFAULT 'ACTIVE',
"stripeCustomerId" TEXT,
"stripeSubscriptionId" TEXT,
"stripePriceId" TEXT,
"trialEndsAt" TIMESTAMP(3),
"currentPeriodStart" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"currentPeriodEnd" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"canceledAt" TIMESTAMP(3),
"cancelAtPeriodEnd" BOOLEAN NOT NULL DEFAULT false,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Subscription_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX IF NOT EXISTS "Subscription_userId_key" ON "Subscription"("userId");
CREATE UNIQUE INDEX IF NOT EXISTS "Subscription_stripeCustomerId_key" ON "Subscription"("stripeCustomerId");
CREATE UNIQUE INDEX IF NOT EXISTS "Subscription_stripeSubscriptionId_key" ON "Subscription"("stripeSubscriptionId");
DO $$ BEGIN
ALTER TABLE "Subscription" ADD CONSTRAINT "Subscription_userId_fkey"
FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION
WHEN duplicate_object THEN NULL;
END $$;
CREATE TABLE IF NOT EXISTS "UsageLog" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"feature" TEXT NOT NULL,
"periodStart" TIMESTAMP(3) NOT NULL,
"periodEnd" TIMESTAMP(3) NOT NULL,
"requestsCount" INTEGER NOT NULL DEFAULT 0,
"tokensUsed" INTEGER NOT NULL DEFAULT 0,
"syncedAt" TIMESTAMP(3),
"metadata" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "UsageLog_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX IF NOT EXISTS "UsageLog_userId_feature_periodStart_key" ON "UsageLog"("userId", "feature", "periodStart");
CREATE INDEX IF NOT EXISTS "UsageLog_userId_periodStart_idx" ON "UsageLog"("userId", "periodStart");
CREATE INDEX IF NOT EXISTS "UsageLog_periodStart_idx" ON "UsageLog"("periodStart");
DO $$ BEGIN
ALTER TABLE "UsageLog" ADD CONSTRAINT "UsageLog_userId_fkey"
FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION
WHEN duplicate_object THEN NULL;
END $$;
CREATE TABLE IF NOT EXISTS "FeatureFlag" (
"id" TEXT NOT NULL,
"key" TEXT NOT NULL,
"enabled" BOOLEAN NOT NULL DEFAULT false,
"tiers" TEXT[],
"metadata" TEXT,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "FeatureFlag_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX IF NOT EXISTS "FeatureFlag_key_key" ON "FeatureFlag"("key");
DO $$ BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'User' AND column_name = 'accentColor'
) THEN
ALTER TABLE "User" ADD COLUMN "accentColor" TEXT NOT NULL DEFAULT '#A47148';
END IF;
END $$;