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
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 11m39s
This commit is contained in:
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user