Aktueller Stand

This commit is contained in:
2026-01-15 16:24:09 +01:00
parent 5d2630a02f
commit 46eae2a2a9
70 changed files with 7866 additions and 447 deletions

View File

@@ -0,0 +1,76 @@
import { NextResponse } from "next/server";
import { prisma } from "../../../../lib/prisma";
import { isSuperAdminSession, requireSession } from "../../../../lib/auth-helpers";
export async function GET() {
const { session } = await requireSession();
if (!session) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const apiKeySetting = await prisma.setting.findUnique({
where: { key: "google_places_api_key" }
});
const providerSetting = await prisma.setting.findUnique({
where: { key: "geocoding_provider" }
});
const registrationSetting = await prisma.setting.findUnique({
where: { key: "registration_enabled" }
});
const apiKey = apiKeySetting?.value || "";
const provider =
providerSetting?.value || (apiKey ? "google" : "osm");
const registrationEnabled = registrationSetting?.value !== "false";
return NextResponse.json({ apiKey, provider, registrationEnabled });
}
export async function POST(request: Request) {
const { session } = await requireSession();
if (!session) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
if (!isSuperAdminSession(session)) {
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
}
const body = await request.json();
const { apiKey, provider, registrationEnabled } = body || {};
if (!provider || !["google", "osm"].includes(provider)) {
return NextResponse.json({ error: "Provider erforderlich." }, { status: 400 });
}
if (provider === "google" && !apiKey) {
return NextResponse.json({ error: "API-Key erforderlich." }, { status: 400 });
}
const apiKeyValue = provider === "google" ? apiKey : "";
const apiKeySetting = await prisma.setting.upsert({
where: { key: "google_places_api_key" },
update: { value: apiKeyValue },
create: { key: "google_places_api_key", value: apiKeyValue }
});
const providerSetting = await prisma.setting.upsert({
where: { key: "geocoding_provider" },
update: { value: provider },
create: { key: "geocoding_provider", value: provider }
});
const registrationValue = registrationEnabled === false ? "false" : "true";
await prisma.setting.upsert({
where: { key: "registration_enabled" },
update: { value: registrationValue },
create: { key: "registration_enabled", value: registrationValue }
});
return NextResponse.json({
apiKey: apiKeySetting.value,
provider: providerSetting.value,
registrationEnabled: registrationValue !== "false"
});
}

View File

@@ -0,0 +1,118 @@
import { NextResponse } from "next/server";
import { promises as fs } from "fs";
import path from "path";
import { prisma } from "../../../../lib/prisma";
import { isSuperAdminSession, requireSession } from "../../../../lib/auth-helpers";
export const dynamic = "force-dynamic";
export const revalidate = 0;
const DATA_DIR = path.join(process.cwd(), "prisma", "data");
const UPLOADS_DIR = path.join(DATA_DIR, "uploads");
const MIME_TO_EXT: Record<string, string> = {
"image/png": "png",
"image/jpeg": "jpg",
"image/webp": "webp",
"image/svg+xml": "svg"
};
const resolveLogoPath = (relativePath: string) => {
const absolutePath = path.join(DATA_DIR, relativePath);
if (!absolutePath.startsWith(DATA_DIR)) {
throw new Error("Ungültiger Pfad.");
}
return absolutePath;
};
const getLogoSetting = async () =>
prisma.setting.findUnique({ where: { key: "app_logo_path" } });
const getLogoTypeSetting = async () =>
prisma.setting.findUnique({ where: { key: "app_logo_type" } });
export async function POST(request: Request) {
const { session } = await requireSession();
if (!session) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
if (!isSuperAdminSession(session)) {
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
}
const formData = await request.formData();
const file = formData.get("file");
if (!file || !(file instanceof File)) {
return NextResponse.json({ error: "Datei fehlt." }, { status: 400 });
}
const extension = MIME_TO_EXT[file.type];
if (!extension) {
return NextResponse.json({ error: "Dateityp nicht unterstützt." }, { status: 400 });
}
await fs.mkdir(UPLOADS_DIR, { recursive: true });
const previousSetting = await getLogoSetting();
const previousTypeSetting = await getLogoTypeSetting();
const filename = `app-logo.${extension}`;
const relativePath = path.join("uploads", filename);
const absolutePath = resolveLogoPath(relativePath);
const buffer = Buffer.from(await file.arrayBuffer());
await fs.writeFile(absolutePath, buffer);
if (previousSetting?.value && previousSetting.value !== relativePath) {
try {
await fs.unlink(resolveLogoPath(previousSetting.value));
} catch {
// ignore missing old file
}
}
await prisma.setting.upsert({
where: { key: "app_logo_path" },
update: { value: relativePath },
create: { key: "app_logo_path", value: relativePath }
});
await prisma.setting.upsert({
where: { key: "app_logo_type" },
update: { value: file.type },
create: { key: "app_logo_type", value: file.type }
});
return NextResponse.json({ ok: true });
}
export async function DELETE() {
const { session } = await requireSession();
if (!session) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
if (!isSuperAdminSession(session)) {
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
}
const logoSetting = await getLogoSetting();
const typeSetting = await getLogoTypeSetting();
if (logoSetting?.value) {
try {
await fs.unlink(resolveLogoPath(logoSetting.value));
} catch {
// ignore missing file
}
}
if (logoSetting) {
await prisma.setting.delete({ where: { key: "app_logo_path" } });
}
if (typeSetting) {
await prisma.setting.delete({ where: { key: "app_logo_type" } });
}
return NextResponse.json({ ok: true });
}