Phase 1 Sprint B: Neue Tenant-Symbol APIs

This commit is contained in:
Pepe Ziberi
2026-05-20 21:19:17 +02:00
parent 3b57ca4594
commit ca26f1e733
5 changed files with 602 additions and 52 deletions

View File

@@ -9,10 +9,53 @@ export async function GET(
{ params }: { params: { id: string } } { params }: { params: { id: string } }
) { ) {
try { try {
const icon = await prisma.iconAsset.findUnique({ const id = params.id
where: { id: params.id },
// ─── 1. Try TenantSymbol first (Phase 1 architecture) ───
const tenantSymbol = await (prisma as any).tenantSymbol.findUnique({
where: { id },
select: { svgPath: true, iconId: true, name: true },
}) })
if (tenantSymbol?.svgPath) {
// Serve from public/signaturen/ or MinIO
const contentType = tenantSymbol.svgPath.endsWith('.svg') ? 'image/svg+xml' : 'image/png'
if (tenantSymbol.svgPath.startsWith('signaturen/')) {
try {
const filePath = join(process.cwd(), 'public', tenantSymbol.svgPath)
const buffer = await readFile(filePath)
return new NextResponse(buffer, {
headers: {
'Content-Type': contentType,
'Cache-Control': 'public, max-age=31536000',
},
})
} catch {
console.error('TenantSymbol file not found:', tenantSymbol.svgPath)
}
}
// If not in public/, try MinIO
try {
const { stream, contentType: ct } = await getFileStream(tenantSymbol.svgPath)
const chunks: Buffer[] = []
for await (const chunk of stream) {
chunks.push(Buffer.from(chunk))
}
const buffer = Buffer.concat(chunks)
return new NextResponse(buffer, {
headers: {
'Content-Type': ct || contentType,
'Cache-Control': 'public, max-age=86400',
},
})
} catch {
console.error('Error streaming TenantSymbol from MinIO:', tenantSymbol.svgPath)
}
}
// ─── 2. Fallback: IconAsset (legacy architecture) ───
const icon = await prisma.iconAsset.findUnique({ where: { id } })
if (!icon) { if (!icon) {
return NextResponse.json({ error: 'Icon nicht gefunden' }, { status: 404 }) return NextResponse.json({ error: 'Icon nicht gefunden' }, { status: 404 })
} }

View File

@@ -0,0 +1,116 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/db'
import { getSession } from '@/lib/auth'
async function getTenantId() {
const user = await getSession()
if (!user) return { error: 'Nicht autorisiert', status: 401 }
if (user.role !== 'TENANT_ADMIN' && user.role !== 'SERVER_ADMIN') {
return { error: 'Keine Berechtigung', status: 403 }
}
if (!user.tenantId) return { error: 'Kein Mandant zugeordnet', status: 400 }
return { tenantId: user.tenantId }
}
/**
* POST /api/templates/import
* Importiert Symbole aus einem Template-Paket als TenantSymbols.
*
* Body:
* {
* packageId: "feuerwehr-ch",
* symbolIds?: ["id1", "id2"], // optional: nur ausgewählte, sonst alle
* }
*
* Response:
* { imported: [{ tenantSymbolId, name, categoryId }] }
*/
export async function POST(req: NextRequest) {
try {
const auth = await getTenantId()
if ('error' in auth) return NextResponse.json({ error: auth.error }, { status: auth.status })
const { tenantId } = auth
const { packageId, symbolIds } = await req.json()
if (!packageId) return NextResponse.json({ error: 'packageId erforderlich' }, { status: 400 })
// 1. Fetch templates to import
const where: any = { packageId }
if (symbolIds && Array.isArray(symbolIds) && symbolIds.length > 0) {
where.id = { in: symbolIds }
}
const templates = await (prisma as any).symbolTemplate.findMany({ where })
if (templates.length === 0) {
return NextResponse.json({ error: 'Keine Symbole im Paket gefunden' }, { status: 404 })
}
// 2. Ensure default category exists for each template category
const categoryMap = new Map<string, string>() // categoryName -> TenantCategory.id
const uniqueCategoryNames: string[] = [...new Set<string>(templates.map((t: any) => t.categoryName as string))]
for (const catName of uniqueCategoryNames) {
let tenantCat = await (prisma as any).tenantCategory.findFirst({
where: { tenantId, name: catName },
})
if (!tenantCat) {
tenantCat = await (prisma as any).tenantCategory.create({
data: { tenantId, name: catName, sortOrder: 0 },
})
}
categoryMap.set(catName, tenantCat.id)
}
// 3. Get current max sortOrder for tenant
const maxSortAgg = await (prisma as any).tenantSymbol.aggregate({
where: { tenantId },
_max: { sortOrder: true },
})
let currentSort = (maxSortAgg._max.sortOrder ?? -1) + 1
// 4. Import each template as TenantSymbol
const imported: Array<{
tenantSymbolId: string
name: string
categoryId: string
svgPath: string
}> = []
for (const tpl of templates) {
const categoryId = categoryMap.get(tpl.categoryName)
if (!categoryId) continue
// Check if already exists (same name + svgPath in this tenant)
const existing = await (prisma as any).tenantSymbol.findFirst({
where: { tenantId, name: tpl.name, svgPath: tpl.svgPath },
})
if (existing) {
// Skip already imported symbols
continue
}
const tenantSymbol = await (prisma as any).tenantSymbol.create({
data: {
tenantId,
categoryId,
name: tpl.name,
svgPath: tpl.svgPath,
sortOrder: currentSort++,
isUploaded: false,
},
})
imported.push({
tenantSymbolId: tenantSymbol.id,
name: tenantSymbol.name,
categoryId: tenantSymbol.categoryId,
svgPath: tenantSymbol.svgPath,
})
}
return NextResponse.json({ imported, count: imported.length })
} catch (error) {
console.error('Error importing templates:', error)
return NextResponse.json({ error: 'Interner Fehler' }, { status: 500 })
}
}

View File

@@ -0,0 +1,50 @@
import { NextResponse } from 'next/server'
import { prisma } from '@/lib/db'
/**
* GET /api/templates
* Listet alle verfügbaren Vorlagen-Pakete mit Vorschau.
*/
export async function GET() {
try {
const packages = await (prisma as any).symbolTemplate.groupBy({
by: ['packageId', 'packageName'],
_count: { id: true },
})
const result = await Promise.all(
packages.map(async (pkg: any) => {
// Get category breakdown for this package
const categories = await (prisma as any).symbolTemplate.groupBy({
by: ['categoryName'],
where: { packageId: pkg.packageId },
_count: { id: true },
})
// Get a few preview symbols
const previews = await (prisma as any).symbolTemplate.findMany({
where: { packageId: pkg.packageId },
take: 5,
select: { id: true, name: true, svgPath: true, categoryName: true },
})
return {
packageId: pkg.packageId,
packageName: pkg.packageName,
symbolCount: pkg._count.id,
categoryCount: categories.length,
categories: categories.map((c: any) => ({
categoryName: c.categoryName,
symbolCount: c._count.id,
})),
previews,
}
})
)
return NextResponse.json({ packages: result })
} catch (error) {
console.error('Error fetching templates:', error)
return NextResponse.json({ error: 'Interner Fehler' }, { status: 500 })
}
}

View File

@@ -0,0 +1,185 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/db'
import { getSession } from '@/lib/auth'
async function getTenantId() {
const user = await getSession()
if (!user) return { error: 'Nicht autorisiert', status: 401 }
if (user.role !== 'TENANT_ADMIN' && user.role !== 'SERVER_ADMIN') {
return { error: 'Keine Berechtigung', status: 403 }
}
if (!user.tenantId) return { error: 'Kein Mandant zugeordnet', status: 400 }
return { tenantId: user.tenantId }
}
// ─── GET ────────────────────────────────────────────────────────────────────
/**
* GET /api/tenant/categories
* Liefert alle Tenant-Kategorien des aktuellen Mandanten mit Symbol-Zählung.
*/
export async function GET() {
try {
const auth = await getTenantId()
if ('error' in auth) return NextResponse.json({ error: auth.error }, { status: auth.status })
const { tenantId } = auth
const categories = await (prisma as any).tenantCategory.findMany({
where: { tenantId },
include: {
_count: {
select: { symbols: true },
},
},
orderBy: { sortOrder: 'asc' },
})
const result = categories.map((cat: any) => ({
id: cat.id,
name: cat.name,
sortOrder: cat.sortOrder,
icon: cat.icon,
symbolCount: cat._count.symbols,
createdAt: cat.createdAt,
}))
return NextResponse.json({ categories: result })
} catch (error) {
console.error('Error fetching tenant categories:', error)
return NextResponse.json({ error: 'Interner Fehler' }, { status: 500 })
}
}
// ─── POST ───────────────────────────────────────────────────────────────────
/**
* POST /api/tenant/categories
* Legt eine neue Kategorie an.
*
* Body: { name: string, sortOrder?: number, icon?: string }
*/
export async function POST(req: NextRequest) {
try {
const auth = await getTenantId()
if ('error' in auth) return NextResponse.json({ error: auth.error }, { status: auth.status })
const { tenantId } = auth
const { name, sortOrder, icon } = await req.json()
if (!name || typeof name !== 'string') {
return NextResponse.json({ error: 'Name erforderlich' }, { status: 400 })
}
// Check for duplicate name within tenant
const 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 })
}
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 })
} catch (error) {
console.error('Error creating tenant category:', error)
return NextResponse.json({ error: 'Interner Fehler' }, { status: 500 })
}
}
// ─── PATCH ──────────────────────────────────────────────────────────────────
/**
* PATCH /api/tenant/categories
* Aktualisiert eine Kategorie.
*
* Body: { id: string, name?: string, sortOrder?: number, icon?: string }
*/
export async function PATCH(req: NextRequest) {
try {
const auth = await getTenantId()
if ('error' in auth) return NextResponse.json({ error: auth.error }, { status: auth.status })
const { tenantId } = auth
const { id, name, sortOrder, icon } = await req.json()
if (!id) return NextResponse.json({ error: 'id erforderlich' }, { status: 400 })
const data: any = {}
if (name !== undefined) data.name = name.trim()
if (sortOrder !== undefined) data.sortOrder = sortOrder
if (icon !== undefined) data.icon = icon || null
// If renaming, check for duplicates
if (name) {
const 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 })
}
}
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 })
}
return NextResponse.json({ success: true })
} catch (error) {
console.error('Error updating tenant category:', error)
return NextResponse.json({ error: 'Interner Fehler' }, { status: 500 })
}
}
// ─── DELETE ─────────────────────────────────────────────────────────────────
/**
* DELETE /api/tenant/categories
* Löscht eine Kategorie (nur wenn leer).
*
* Body: { id: string }
*/
export async function DELETE(req: NextRequest) {
try {
const auth = await getTenantId()
if ('error' in auth) return NextResponse.json({ error: auth.error }, { status: auth.status })
const { tenantId } = auth
const { id } = await req.json()
if (!id) return NextResponse.json({ error: 'id erforderlich' }, { status: 400 })
// Check if category has symbols
const symbolCount = await (prisma as any).tenantSymbol.count({
where: { categoryId: id, tenantId },
})
if (symbolCount > 0) {
return NextResponse.json(
{ error: `Kategorie enthält ${symbolCount} Symbol(e) — bitte zuerst verschieben oder löschen` },
{ status: 409 }
)
}
await (prisma as any).tenantCategory.deleteMany({
where: { id, tenantId },
})
return NextResponse.json({ success: true })
} catch (error) {
console.error('Error deleting tenant category:', error)
return NextResponse.json({ error: 'Interner Fehler' }, { status: 500 })
}
}

View File

@@ -1,6 +1,7 @@
import { NextRequest, NextResponse } from 'next/server' 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'
import { uploadFile } from '@/lib/minio'
async function getTenantId() { async function getTenantId() {
const user = await getSession() const user = await getSession()
@@ -12,117 +13,261 @@ async function getTenantId() {
return { tenantId: user.tenantId } return { tenantId: user.tenantId }
} }
// GET: Returns library (all system icons) + tenant's own symbol collection // ─── GET ────────────────────────────────────────────────────────────────────
export async function GET() {
/**
* GET /api/tenant/symbols
* Liefert die Symbole des Mandanten gruppiert nach TenantCategory.
* Optional: ?grouped=true (default) oder ?grouped=false (flache Liste)
*/
export async function GET(req: NextRequest) {
try { try {
const auth = await getTenantId() const auth = await getTenantId()
if ('error' in auth) return NextResponse.json({ error: auth.error }, { status: auth.status }) if ('error' in auth) return NextResponse.json({ error: auth.error }, { status: auth.status })
const { tenantId } = auth const { tenantId } = auth
// All system icons grouped by category (the library) const { searchParams } = new URL(req.url)
const icons = await (prisma as any).iconAsset.findMany({ const grouped = searchParams.get('grouped') !== 'false'
where: { isActive: true },
include: { category: { select: { id: true, name: true } } },
orderBy: [{ category: { sortOrder: 'asc' } }, { name: 'asc' }],
})
const library = icons.map((icon: any) => ({
id: icon.id,
name: icon.name,
mimeType: icon.mimeType,
iconType: icon.iconType,
categoryId: icon.categoryId,
categoryName: icon.category?.name || 'Ohne Kategorie',
}))
// Tenant's own symbol collection
const tenantSymbols = await (prisma as any).tenantSymbol.findMany({ const tenantSymbols = await (prisma as any).tenantSymbol.findMany({
where: { tenantId }, where: { tenantId },
include: { icon: { select: { id: true, name: true, mimeType: true, iconType: true, category: { select: { name: true } } } } }, include: {
category: { select: { id: true, name: true, sortOrder: true } },
},
orderBy: [{ category: { sortOrder: 'asc' } }, { sortOrder: 'asc' }, { name: 'asc' }],
})
const mapped = tenantSymbols.map((ts: any) => ({
id: ts.id,
name: ts.name || ts.customName || 'Unbenannt',
customName: ts.customName,
svgPath: ts.svgPath,
categoryId: ts.categoryId,
categoryName: ts.category?.name || 'Ohne Kategorie',
sortOrder: ts.sortOrder,
isUploaded: ts.isUploaded,
createdAt: ts.createdAt,
}))
if (!grouped) {
return NextResponse.json({ symbols: mapped })
}
// Group by category
const groupedResult: Record<string, any[]> = {}
for (const sym of mapped) {
const catName = sym.categoryName
if (!groupedResult[catName]) groupedResult[catName] = []
groupedResult[catName].push(sym)
}
// Sort categories by sortOrder from category
const categories = await (prisma as any).tenantCategory.findMany({
where: { tenantId },
orderBy: { sortOrder: 'asc' }, orderBy: { sortOrder: 'asc' },
}) })
const mySymbols = tenantSymbols.map((ts: any) => ({ const ordered: Array<{ categoryId: string | null; categoryName: string; symbols: any[] }> = []
id: ts.id, for (const cat of categories) {
iconId: ts.iconId, if (groupedResult[cat.name]) {
name: ts.customName || ts.icon.name, ordered.push({ categoryId: cat.id, categoryName: cat.name, symbols: groupedResult[cat.name] })
customName: ts.customName, delete groupedResult[cat.name]
baseName: ts.icon.name, }
mimeType: ts.icon.mimeType, }
iconType: ts.icon.iconType, // Append any remaining uncategorized
categoryName: ts.icon.category?.name || 'Ohne Kategorie', for (const [catName, symbols] of Object.entries(groupedResult)) {
sortOrder: ts.sortOrder, ordered.push({ categoryId: null, categoryName: catName, symbols })
})) }
return NextResponse.json({ library, mySymbols }) return NextResponse.json({ categories: ordered })
} catch (error) { } catch (error) {
console.error('Error fetching tenant symbols:', error) console.error('Error fetching tenant symbols:', error)
return NextResponse.json({ error: 'Interner Fehler' }, { status: 500 }) return NextResponse.json({ error: 'Interner Fehler' }, { status: 500 })
} }
} }
// POST: Add a symbol from the library to "my symbols" // ─── POST ───────────────────────────────────────────────────────────────────
/**
* POST /api/tenant/symbols
* Erstellt ein neues TenantSymbol (manuell oder aus Template/IconAsset).
*
* Body (manuell aus IconAsset):
* { iconId: string, customName?: string, categoryId?: string }
*
* Body (aus Template-Import):
* { templateId: string, categoryId?: string }
*
* Body (manueller Upload):
* FormData mit: file (SVG/PNG), name, categoryId
*/
export async function POST(req: NextRequest) { export async function POST(req: NextRequest) {
try { try {
const auth = await getTenantId() const auth = await getTenantId()
if ('error' in auth) return NextResponse.json({ error: auth.error }, { status: auth.status }) if ('error' in auth) return NextResponse.json({ error: auth.error }, { status: auth.status })
const { tenantId } = auth const { tenantId } = auth
const { iconId, customName } = await req.json() const contentType = req.headers.get('content-type') || ''
if (!iconId) return NextResponse.json({ error: 'iconId erforderlich' }, { status: 400 })
// Get max sortOrder for this tenant // ─── File Upload ─────────────────────────────────────────────────────────
const maxSort = await (prisma as any).tenantSymbol.aggregate({ if (contentType.includes('multipart/form-data')) {
const formData = await req.formData()
const file = formData.get('file') as File | null
const name = formData.get('name') as string | null
const categoryId = formData.get('categoryId') as string | null
if (!file || !name) {
return NextResponse.json({ error: 'Datei und Name erforderlich' }, { status: 400 })
}
const maxSortAgg = await (prisma as any).tenantSymbol.aggregate({
where: { tenantId },
_max: { sortOrder: true },
})
const bytes = await file.arrayBuffer()
const buffer = Buffer.from(bytes)
const ext = file.name.split('.').pop()?.toLowerCase() || 'svg'
const mimeType = ext === 'svg' ? 'image/svg+xml' : `image/${ext}`
const fileKey = `tenant-${tenantId}/symbols/${Date.now()}-${name.replace(/\s+/g, '_').toLowerCase()}.${ext}`
await uploadFile(fileKey, buffer, mimeType)
const symbol = await (prisma as any).tenantSymbol.create({
data: {
tenantId,
name: name.trim(),
svgPath: fileKey,
categoryId: categoryId || null,
sortOrder: (maxSortAgg._max.sortOrder ?? -1) + 1,
isUploaded: true,
},
})
return NextResponse.json({
id: symbol.id,
name: symbol.name,
svgPath: symbol.svgPath,
categoryId: symbol.categoryId,
sortOrder: symbol.sortOrder,
isUploaded: true,
}, { status: 201 })
}
// ─── JSON (from IconAsset or Template) ──────────────────────────────────
const body = await req.json()
const { iconId, customName, templateId, categoryId } = body
const maxSortAgg = await (prisma as any).tenantSymbol.aggregate({
where: { tenantId }, where: { tenantId },
_max: { sortOrder: true }, _max: { sortOrder: true },
}) })
const nextSort = (maxSortAgg._max.sortOrder ?? -1) + 1
// From SymbolTemplate
if (templateId) {
const tpl = await (prisma as any).symbolTemplate.findUnique({
where: { id: templateId },
})
if (!tpl) {
return NextResponse.json({ error: 'Template nicht gefunden' }, { status: 404 })
}
const symbol = await (prisma as any).tenantSymbol.create({
data: {
tenantId,
name: tpl.name,
svgPath: tpl.svgPath,
categoryId: categoryId || null,
sortOrder: nextSort,
isUploaded: false,
},
})
return NextResponse.json({
id: symbol.id,
name: symbol.name,
svgPath: symbol.svgPath,
categoryId: symbol.categoryId,
sortOrder: symbol.sortOrder,
isUploaded: false,
}, { status: 201 })
}
// From IconAsset (legacy path)
if (!iconId) {
return NextResponse.json({ error: 'iconId oder templateId erforderlich' }, { status: 400 })
}
const icon = await prisma.iconAsset.findUnique({
where: { id: iconId },
include: { category: true },
})
if (!icon) {
return NextResponse.json({ error: 'Icon nicht gefunden' }, { status: 404 })
}
const symbol = await (prisma as any).tenantSymbol.create({ const symbol = await (prisma as any).tenantSymbol.create({
data: { data: {
tenantId, tenantId,
iconId, iconId: icon.id,
customName: customName || null, customName: customName || null,
sortOrder: (maxSort._max.sortOrder ?? -1) + 1, name: customName || icon.name,
svgPath: icon.fileKey,
categoryId: categoryId || null,
sortOrder: nextSort,
isUploaded: false,
}, },
include: { icon: { select: { name: true, mimeType: true, iconType: true, category: { select: { name: true } } } } },
}) })
return NextResponse.json({ return NextResponse.json({
id: symbol.id, id: symbol.id,
iconId: symbol.iconId, iconId: symbol.iconId,
name: symbol.customName || symbol.icon.name, name: symbol.name,
customName: symbol.customName, customName: symbol.customName,
baseName: symbol.icon.name, svgPath: symbol.svgPath,
mimeType: symbol.icon.mimeType, categoryId: symbol.categoryId,
iconType: symbol.icon.iconType,
categoryName: symbol.icon.category?.name || 'Ohne Kategorie',
sortOrder: symbol.sortOrder, sortOrder: symbol.sortOrder,
}) isUploaded: false,
}, { status: 201 })
} catch (error) { } catch (error) {
console.error('Error adding tenant symbol:', error) console.error('Error adding tenant symbol:', error)
return NextResponse.json({ error: 'Interner Fehler' }, { status: 500 }) return NextResponse.json({ error: 'Interner Fehler' }, { status: 500 })
} }
} }
// PATCH: Rename a symbol or update sortOrder // ─── PATCH ──────────────────────────────────────────────────────────────────
/**
* PATCH /api/tenant/symbols
* Aktualisiert ein TenantSymbol.
*
* Body: { id: string, name?: string, customName?: string, categoryId?: string, sortOrder?: number }
*/
export async function PATCH(req: NextRequest) { export async function PATCH(req: NextRequest) {
try { try {
const auth = await getTenantId() const auth = await getTenantId()
if ('error' in auth) return NextResponse.json({ error: auth.error }, { status: auth.status }) if ('error' in auth) return NextResponse.json({ error: auth.error }, { status: auth.status })
const { tenantId } = auth const { tenantId } = auth
const { id, customName, sortOrder } = await req.json() const { id, name, customName, categoryId, sortOrder } = await req.json()
if (!id) return NextResponse.json({ error: 'id erforderlich' }, { status: 400 }) if (!id) return NextResponse.json({ error: 'id erforderlich' }, { status: 400 })
const data: any = {} const data: any = {}
if (name !== undefined) data.name = name || null
if (customName !== undefined) data.customName = customName || null if (customName !== undefined) data.customName = customName || null
if (categoryId !== undefined) data.categoryId = categoryId || null
if (sortOrder !== undefined) data.sortOrder = sortOrder if (sortOrder !== undefined) data.sortOrder = sortOrder
await (prisma as any).tenantSymbol.updateMany({ const result = await (prisma as any).tenantSymbol.updateMany({
where: { id, tenantId }, where: { id, tenantId },
data, data,
}) })
if (result.count === 0) {
return NextResponse.json({ error: 'Symbol nicht gefunden' }, { status: 404 })
}
return NextResponse.json({ success: true }) return NextResponse.json({ success: true })
} catch (error) { } catch (error) {
console.error('Error updating tenant symbol:', error) console.error('Error updating tenant symbol:', error)
@@ -130,7 +275,14 @@ export async function PATCH(req: NextRequest) {
} }
} }
// DELETE: Remove a symbol from "my symbols" // ─── DELETE ─────────────────────────────────────────────────────────────────
/**
* DELETE /api/tenant/symbols
* Löscht ein TenantSymbol.
*
* Body: { id: string }
*/
export async function DELETE(req: NextRequest) { export async function DELETE(req: NextRequest) {
try { try {
const auth = await getTenantId() const auth = await getTenantId()
@@ -140,10 +292,14 @@ export async function DELETE(req: NextRequest) {
const { id } = await req.json() const { id } = await req.json()
if (!id) return NextResponse.json({ error: 'id erforderlich' }, { status: 400 }) if (!id) return NextResponse.json({ error: 'id erforderlich' }, { status: 400 })
await (prisma as any).tenantSymbol.deleteMany({ const result = await (prisma as any).tenantSymbol.deleteMany({
where: { id, tenantId }, where: { id, tenantId },
}) })
if (result.count === 0) {
return NextResponse.json({ error: 'Symbol nicht gefunden' }, { status: 404 })
}
return NextResponse.json({ success: true }) return NextResponse.json({ success: true })
} catch (error) { } catch (error) {
console.error('Error deleting tenant symbol:', error) console.error('Error deleting tenant symbol:', error)