fix(icons): /api/icons auf Raw-SQL umgestellt – zeigt jetzt alle Tenant-Kategorien in der App
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 11m39s

This commit is contained in:
Pepe Ziberi
2026-05-21 15:00:47 +02:00
parent 93d5519e58
commit 9cba24aad8

View File

@@ -2,6 +2,38 @@ import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/db' import { prisma } from '@/lib/db'
import { getSession } from '@/lib/auth' import { getSession } from '@/lib/auth'
async function ensureTables() {
await prisma.$executeRawUnsafe(`
CREATE TABLE IF NOT EXISTS tenant_symbols (
id TEXT PRIMARY KEY DEFAULT gen_random_uuid(),
"isActive" BOOLEAN NOT NULL DEFAULT true,
"tenantId" TEXT NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
"iconId" TEXT REFERENCES icon_assets(id) ON DELETE SET NULL,
"name" TEXT,
"svgPath" TEXT,
"isUploaded" BOOLEAN NOT NULL DEFAULT false,
"categoryId" TEXT,
"migratedFromIconId" TEXT,
"customName" TEXT,
"sortOrder" INTEGER,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
)
`)
await prisma.$executeRawUnsafe(`
CREATE TABLE IF NOT EXISTS tenant_categories (
id TEXT PRIMARY KEY DEFAULT gen_random_uuid(),
"name" TEXT NOT NULL,
"description" TEXT,
"sortOrder" INTEGER NOT NULL DEFAULT 0,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"tenantId" TEXT NOT NULL REFERENCES tenants(id) ON DELETE CASCADE
)
`)
}
export async function GET() { export async function GET() {
/* ─── 1. Global library (legacy IconAsset) ─── */ /* ─── 1. Global library (legacy IconAsset) ─── */
let categoriesWithUrls: any[] = [] let categoriesWithUrls: any[] = []
@@ -9,41 +41,38 @@ export async function GET() {
const user = await getSession() const user = await getSession()
const tenantId = user?.tenantId const tenantId = user?.tenantId
const categoryWhere: any = tenantId const categories = await prisma.$queryRawUnsafe(
? { OR: [{ tenantId: null }, { tenantId }] } `SELECT * FROM icon_categories WHERE "tenantId" IS NULL OR "tenantId" = $1 ORDER BY "sortOrder" ASC`,
: {} tenantId || null
) as any[]
const categories = await (prisma as any).iconCategory.findMany({
where: categoryWhere,
orderBy: { sortOrder: 'asc' },
include: {
icons: {
where: tenantId
? { isActive: true, OR: [{ tenantId: null }, { tenantId }] }
: { isActive: true, tenantId: null },
orderBy: { name: 'asc' },
},
},
})
let hiddenIconIds: string[] = [] let hiddenIconIds: string[] = []
if (tenantId) { if (tenantId) {
const tenant = await (prisma as any).tenant.findUnique({ const tenant = await prisma.$queryRawUnsafe(
where: { id: tenantId }, `SELECT "hiddenIconIds" FROM tenants WHERE id = $1 LIMIT 1`,
select: { hiddenIconIds: true }, tenantId
}) ) as any[]
hiddenIconIds = tenant?.hiddenIconIds || [] hiddenIconIds = tenant[0]?.hiddenIconIds || []
} }
categoriesWithUrls = categories.map((cat: any) => ({ for (const cat of categories) {
...cat, const icons = await prisma.$queryRawUnsafe(
icons: cat.icons `SELECT * FROM icon_assets WHERE "categoryId" = $1 AND "isActive" = true AND ("tenantId" IS NULL OR "tenantId" = $2) ORDER BY name ASC`,
cat.id, tenantId || null
) as any[]
const filteredIcons = icons
.filter((icon: any) => !hiddenIconIds.includes(icon.id)) .filter((icon: any) => !hiddenIconIds.includes(icon.id))
.map((icon: any) => ({ .map((icon: any) => ({
...icon, ...icon,
url: `/api/icons/${icon.id}/image`, url: `/api/icons/${icon.id}/image`,
})), }))
})) if (filteredIcons.length > 0) {
categoriesWithUrls.push({
...cat,
icons: filteredIcons,
})
}
}
} catch (err) { } catch (err) {
console.error('Error fetching legacy icon categories:', err) console.error('Error fetching legacy icon categories:', err)
categoriesWithUrls = [] categoriesWithUrls = []
@@ -58,18 +87,23 @@ export async function GET() {
const tenantId = user?.tenantId const tenantId = user?.tenantId
if (tenantId) { if (tenantId) {
const tenantSymbols = await (prisma as any).tenantSymbol.findMany({ await ensureTables()
where: { tenantId },
include: { category: true }, const tenantSymbols = await prisma.$queryRawUnsafe(
orderBy: [{ category: { sortOrder: 'asc' } }, { sortOrder: 'asc' }], `SELECT ts.*, tc.id as "catId", tc.name as "catName"
}) FROM tenant_symbols ts
LEFT JOIN tenant_categories tc ON ts."categoryId" = tc.id
WHERE ts."tenantId" = $1 AND ts."isActive" = true
ORDER BY COALESCE(tc."sortOrder", 9999) ASC, COALESCE(ts."sortOrder", 9999) ASC`,
tenantId
) as any[]
flatTenantSymbols = tenantSymbols.map((ts: any) => ({ flatTenantSymbols = tenantSymbols.map((ts: any) => ({
id: ts.id, id: ts.id,
name: ts.customName || ts.name, name: ts.customName || ts.name,
customName: ts.customName, customName: ts.customName,
categoryId: ts.categoryId, categoryId: ts.categoryId,
categoryName: ts.category?.name || null, categoryName: ts.catName || null,
isUploaded: ts.isUploaded, isUploaded: ts.isUploaded,
migratedFromIconId: ts.migratedFromIconId, migratedFromIconId: ts.migratedFromIconId,
imageUrl: ts.isUploaded imageUrl: ts.isUploaded
@@ -87,12 +121,13 @@ export async function GET() {
} }
const catIds = Array.from(groups.keys()).filter(Boolean) as string[] const catIds = Array.from(groups.keys()).filter(Boolean) as string[]
const tenantCategories = catIds.length let tenantCategories: any[] = []
? await (prisma as any).tenantCategory.findMany({ if (catIds.length > 0) {
where: { id: { in: catIds }, tenantId }, tenantCategories = await prisma.$queryRawUnsafe(
orderBy: { sortOrder: 'asc' }, `SELECT * FROM tenant_categories WHERE id = ANY($1) AND "tenantId" = $2 ORDER BY "sortOrder" ASC`,
}) catIds, tenantId
: [] ) as any[]
}
const catMap = new Map(tenantCategories.map((c: any) => [c.id, c])) const catMap = new Map(tenantCategories.map((c: any) => [c.id, c]))
@@ -100,8 +135,8 @@ export async function GET() {
const cat = catId ? catMap.get(catId) : null const cat = catId ? catMap.get(catId) : null
return { return {
categoryId: catId, categoryId: catId,
categoryName: cat ? (cat as any).name || 'Kategorie' : 'Ohne Kategorie', categoryName: cat ? cat.name || 'Kategorie' : 'Ohne Kategorie',
sortOrder: cat ? (cat as any).sortOrder ?? 999 : 999, sortOrder: cat ? cat.sortOrder ?? 999 : 999,
symbols, symbols,
} }
}) })
@@ -120,19 +155,22 @@ export async function GET() {
const tenantId = user?.tenantId const tenantId = user?.tenantId
if (tenantId) { if (tenantId) {
const legacy = await (prisma as any).tenantSymbol.findMany({ const legacy = await prisma.$queryRawUnsafe(
where: { tenantId, iconId: { not: null } }, `SELECT ts.*, ia.id as "iconId", ia.name as "iconName", ia."mimeType", ia."iconType"
include: { icon: { select: { id: true, name: true, mimeType: true, iconType: true } } }, FROM tenant_symbols ts
orderBy: { sortOrder: 'asc' }, JOIN icon_assets ia ON ts."iconId" = ia.id
}) WHERE ts."tenantId" = $1 AND ts."iconId" IS NOT NULL
ORDER BY COALESCE(ts."sortOrder", 9999) ASC`,
tenantId
) as any[]
mySymbolsLegacy = legacy.map((ts: any) => ({ mySymbolsLegacy = legacy.map((ts: any) => ({
id: ts.icon.id, id: ts.iconId,
tenantSymbolId: ts.id, tenantSymbolId: ts.id,
name: ts.customName || ts.icon.name, name: ts.customName || ts.iconName,
customName: ts.customName, customName: ts.customName,
mimeType: ts.icon.mimeType, mimeType: ts.mimeType,
iconType: ts.icon.iconType, iconType: ts.iconType,
url: `/api/icons/${ts.icon.id}/image`, url: `/api/icons/${ts.iconId}/image`,
})) }))
} }
} catch (err) { } catch (err) {