Some checks failed
Build and Push Docker Image / build-and-push (push) Has been cancelled
185 lines
6.1 KiB
TypeScript
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,
|
|
})
|
|
}
|