fix(auto-migrate): tenant_categories on-the-fly erstellung + migrate.js fail-fast
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 10m41s

This commit is contained in:
Pepe Ziberi
2026-05-21 13:40:34 +02:00
parent 3722a04091
commit 3606c9a2a4
3 changed files with 202 additions and 35 deletions

View File

@@ -1,6 +1,11 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/db'
import { getSession } from '@/lib/auth'
import { ensureTenantCategoriesTable } from '@/lib/auto-migrate'
function isMissingTable(err: any) {
return err?.message?.includes('tenant_categories') || err?.code === 'P2021'
}
async function getTenantId() {
const user = await getSession()
@@ -37,10 +42,25 @@ export async function GET() {
})
} catch (dbErr: any) {
console.error('tenantCategory.findMany failed:', dbErr)
if (dbErr?.message?.includes('tenant_categories') || dbErr?.code === 'P2021') {
return NextResponse.json({ categories: [] })
if (isMissingTable(dbErr)) {
try {
await ensureTenantCategoriesTable()
categories = await (prisma as any).tenantCategory.findMany({
where: { tenantId },
include: {
_count: {
select: { symbols: true },
},
},
orderBy: { sortOrder: 'asc' },
})
} catch (retryErr) {
console.error('Retry after auto-migrate failed:', retryErr)
return NextResponse.json({ categories: [] })
}
} else {
throw dbErr
}
throw dbErr
}
const result = categories.map((cat: any) => ({
@@ -79,19 +99,23 @@ export async function POST(req: NextRequest) {
}
// Check for duplicate name within tenant
let existing: any = null
try {
const existing = await (prisma as any).tenantCategory.findFirst({
existing = await (prisma as any).tenantCategory.findFirst({
where: { tenantId, name: { equals: name, mode: 'insensitive' } },
})
if (existing) {
return NextResponse.json({ error: 'Kategorie existiert bereits' }, { status: 409 })
}
} catch (dbErr: any) {
console.error('tenantCategory.findFirst failed:', dbErr)
if (dbErr?.message?.includes('tenant_categories') || dbErr?.code === 'P2021') {
return NextResponse.json({ error: 'Datenbank nicht bereit Migration läuft möglicherweise noch' }, { status: 503 })
if (isMissingTable(dbErr)) {
await ensureTenantCategoriesTable()
existing = await (prisma as any).tenantCategory.findFirst({
where: { tenantId, name: { equals: name, mode: 'insensitive' } },
})
} else {
throw dbErr
}
throw dbErr
}
if (existing) {
return NextResponse.json({ error: 'Kategorie existiert bereits' }, { status: 409 })
}
try {
@@ -105,9 +129,17 @@ export async function POST(req: NextRequest) {
})
return NextResponse.json({ category }, { status: 201 })
} catch (dbErr: any) {
console.error('tenantCategory.create failed:', dbErr)
if (dbErr?.message?.includes('tenant_categories') || dbErr?.code === 'P2021') {
return NextResponse.json({ error: 'Datenbank nicht bereit Migration läuft möglicherweise noch' }, { status: 503 })
if (isMissingTable(dbErr)) {
await ensureTenantCategoriesTable()
const category = await (prisma as any).tenantCategory.create({
data: {
tenantId,
name: name.trim(),
sortOrder: sortOrder ?? 0,
icon: icon || null,
},
})
return NextResponse.json({ category }, { status: 201 })
}
throw dbErr
}
@@ -141,23 +173,31 @@ export async function PATCH(req: NextRequest) {
// If renaming, check for duplicates
if (name) {
let existing: any = null
try {
const existing = await (prisma as any).tenantCategory.findFirst({
existing = await (prisma as any).tenantCategory.findFirst({
where: {
tenantId,
name: { equals: name.trim(), mode: 'insensitive' },
id: { not: id },
},
})
if (existing) {
return NextResponse.json({ error: 'Kategorie existiert bereits' }, { status: 409 })
}
} catch (dbErr: any) {
console.error('tenantCategory.findFirst failed:', dbErr)
if (dbErr?.message?.includes('tenant_categories') || dbErr?.code === 'P2021') {
return NextResponse.json({ error: 'Datenbank nicht bereit Migration läuft möglicherweise noch' }, { status: 503 })
if (isMissingTable(dbErr)) {
await ensureTenantCategoriesTable()
existing = await (prisma as any).tenantCategory.findFirst({
where: {
tenantId,
name: { equals: name.trim(), mode: 'insensitive' },
id: { not: id },
},
})
} else {
throw dbErr
}
throw dbErr
}
if (existing) {
return NextResponse.json({ error: 'Kategorie existiert bereits' }, { status: 409 })
}
}
@@ -171,11 +211,18 @@ export async function PATCH(req: NextRequest) {
return NextResponse.json({ error: 'Kategorie nicht gefunden' }, { status: 404 })
}
} catch (dbErr: any) {
console.error('tenantCategory.updateMany failed:', dbErr)
if (dbErr?.message?.includes('tenant_categories') || dbErr?.code === 'P2021') {
return NextResponse.json({ error: 'Datenbank nicht bereit Migration läuft möglicherweise noch' }, { status: 503 })
if (isMissingTable(dbErr)) {
await ensureTenantCategoriesTable()
const category = await (prisma as any).tenantCategory.updateMany({
where: { id, tenantId },
data,
})
if (category.count === 0) {
return NextResponse.json({ error: 'Kategorie nicht gefunden' }, { status: 404 })
}
} else {
throw dbErr
}
throw dbErr
}
return NextResponse.json({ success: true })
@@ -214,7 +261,6 @@ export async function DELETE(req: NextRequest) {
)
}
} catch (dbErr: any) {
console.error('tenantSymbol.count failed:', dbErr)
if (dbErr?.message?.includes('tenant_symbols') || dbErr?.code === 'P2021') {
// Skip check if table doesn't exist
} else {
@@ -227,11 +273,14 @@ export async function DELETE(req: NextRequest) {
where: { id, tenantId },
})
} catch (dbErr: any) {
console.error('tenantCategory.deleteMany failed:', dbErr)
if (dbErr?.message?.includes('tenant_categories') || dbErr?.code === 'P2021') {
return NextResponse.json({ error: 'Datenbank nicht bereit Migration läuft möglicherweise noch' }, { status: 503 })
if (isMissingTable(dbErr)) {
await ensureTenantCategoriesTable()
await (prisma as any).tenantCategory.deleteMany({
where: { id, tenantId },
})
} else {
throw dbErr
}
throw dbErr
}
return NextResponse.json({ success: true })