Files
Lageplan/src/app/api/icons/route.ts
Pepe Ziberi 0cbea843ab
Some checks failed
Build and Push Docker Image / build-and-push (push) Has been cancelled
fix(symbol-groups): API + Frontend auf category-Objekt-Format umgestellt – zeigt jetzt alle Kategorien korrekt
2026-05-21 15:25:12 +02:00

185 lines
6.1 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/db'
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() {
/* ─── 1. Global library (legacy IconAsset) ─── */
let categoriesWithUrls: any[] = []
try {
const user = await getSession()
const tenantId = user?.tenantId
const categories = await prisma.$queryRawUnsafe(
`SELECT * FROM icon_categories WHERE "tenantId" IS NULL OR "tenantId" = $1 ORDER BY "sortOrder" ASC`,
tenantId || null
) as any[]
let hiddenIconIds: string[] = []
if (tenantId) {
const tenant = await prisma.$queryRawUnsafe(
`SELECT "hiddenIconIds" FROM tenants WHERE id = $1 LIMIT 1`,
tenantId
) as any[]
hiddenIconIds = tenant[0]?.hiddenIconIds || []
}
for (const cat of categories) {
const icons = await prisma.$queryRawUnsafe(
`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))
.map((icon: any) => ({
...icon,
url: `/api/icons/${icon.id}/image`,
}))
if (filteredIcons.length > 0) {
categoriesWithUrls.push({
...cat,
icons: filteredIcons,
})
}
}
} catch (err) {
console.error('Error fetching legacy icon categories:', err)
categoriesWithUrls = []
}
/* ─── 2. Tenant symbols (Phase 1 architecture) ─── */
let tenantSymbolGroups: any[] = []
let flatTenantSymbols: any[] = []
try {
const user = await getSession()
const tenantId = user?.tenantId
if (tenantId) {
await ensureTables()
const tenantSymbols = await prisma.$queryRawUnsafe(
`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) => ({
id: ts.id,
name: ts.customName || ts.name,
customName: ts.customName,
categoryId: ts.categoryId,
categoryName: ts.catName || null,
isUploaded: ts.isUploaded,
migratedFromIconId: ts.migratedFromIconId,
imageUrl: ts.isUploaded
? `/api/tenant/symbols/${ts.id}/image`
: ts.migratedFromIconId
? `/api/icons/${ts.migratedFromIconId}/image`
: `/api/icons/${ts.id}/image`,
}))
const groups = new Map<string | null, any[]>()
for (const sym of flatTenantSymbols) {
const key = sym.categoryId || '__none__'
if (!groups.has(key)) groups.set(key, [])
groups.get(key)!.push(sym)
}
const catIds = Array.from(groups.keys()).filter(k => k !== '__none__') as string[]
let tenantCategories: any[] = []
if (catIds.length > 0) {
tenantCategories = await prisma.$queryRawUnsafe(
`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]))
tenantSymbolGroups = Array.from(groups.entries()).map(([catId, symbols]) => {
const cat = catId !== '__none__' ? catMap.get(catId) : null
return {
category: cat ? { id: cat.id, name: cat.name } : null,
symbols,
}
})
}
} catch (err) {
console.error('Error fetching tenant symbols:', err)
tenantSymbolGroups = []
flatTenantSymbols = []
}
/* ─── 3. Legacy mySymbols (keep for old clients during transition) ─── */
let mySymbolsLegacy: any[] = []
try {
const user = await getSession()
const tenantId = user?.tenantId
if (tenantId) {
const legacy = await prisma.$queryRawUnsafe(
`SELECT ts.*, ia.id as "iconId", ia.name as "iconName", ia."mimeType", ia."iconType"
FROM tenant_symbols ts
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) => ({
id: ts.iconId,
tenantSymbolId: ts.id,
name: ts.customName || ts.iconName,
customName: ts.customName,
mimeType: ts.mimeType,
iconType: ts.iconType,
url: `/api/icons/${ts.iconId}/image`,
}))
}
} catch (err) {
console.error('Error fetching legacy mySymbols:', err)
mySymbolsLegacy = []
}
return NextResponse.json({
categories: categoriesWithUrls,
mySymbols: mySymbolsLegacy,
tenantSymbols: flatTenantSymbols,
tenantSymbolGroups,
})
}