app/api/translate ruft DeepL für EN+PL auf und schreibt das Ergebnis in die jeweilige _en/_pl-Spalte (Staff/Admin-only, Service-Role-Key). Ohne DEEPL_API_KEY (neue Docker-Variable) antwortet die Route mit configured:false, das Frontend zeigt dann den Hinweis zur manuellen Eingabe statt eines Fehlers.
101 lines
3.4 KiB
TypeScript
101 lines
3.4 KiB
TypeScript
import { NextResponse } from "next/server";
|
||
import { createClient } from "@/lib/supabase/server";
|
||
import { createAdminClient } from "@/lib/supabase/admin";
|
||
import { isSameOrigin } from "@/lib/origin";
|
||
|
||
// Übersetzt einen deutschen Text nach Englisch/Polnisch via DeepL und
|
||
// schreibt das Ergebnis in die _en/_pl-Spalte der angegebenen Zeile.
|
||
// Ohne DEEPL_API_KEY läuft die Route kontrolliert ins Leere (configured:
|
||
// false), damit das UI die manuelle Eingabe anbieten kann.
|
||
|
||
const ALLOWED_TABLES: Record<string, { idColumn: string; textColumn: string }> = {
|
||
dishes: { idColumn: "id", textColumn: "name" },
|
||
sections: { idColumn: "key", textColumn: "label" },
|
||
allergens: { idColumn: "code", textColumn: "name" },
|
||
additives: { idColumn: "code", textColumn: "name" },
|
||
};
|
||
|
||
async function translateWithDeepL(text: string, targetLang: "EN" | "PL", apiKey: string) {
|
||
const host = apiKey.endsWith(":fx")
|
||
? "https://api-free.deepl.com"
|
||
: "https://api.deepl.com";
|
||
const res = await fetch(`${host}/v2/translate`, {
|
||
method: "POST",
|
||
headers: {
|
||
Authorization: `DeepL-Auth-Key ${apiKey}`,
|
||
"Content-Type": "application/json",
|
||
},
|
||
body: JSON.stringify({
|
||
text: [text],
|
||
target_lang: targetLang,
|
||
source_lang: "DE",
|
||
}),
|
||
});
|
||
if (!res.ok) {
|
||
throw new Error(`DeepL antwortete mit ${res.status}: ${await res.text()}`);
|
||
}
|
||
const data = await res.json();
|
||
return data.translations?.[0]?.text as string | undefined;
|
||
}
|
||
|
||
export async function POST(request: Request) {
|
||
if (!isSameOrigin(request)) {
|
||
return NextResponse.json({ error: "Nicht erlaubt." }, { status: 403 });
|
||
}
|
||
|
||
const supabase = await createClient();
|
||
const {
|
||
data: { user },
|
||
} = await supabase.auth.getUser();
|
||
if (!user) return NextResponse.json({ error: "Nicht angemeldet." }, { status: 401 });
|
||
|
||
const admin = createAdminClient();
|
||
const { data: profile } = await admin
|
||
.from("profiles")
|
||
.select("role")
|
||
.eq("id", user.id)
|
||
.single();
|
||
if (!profile || !["staff", "admin"].includes(profile.role)) {
|
||
return NextResponse.json({ error: "Nicht berechtigt." }, { status: 403 });
|
||
}
|
||
|
||
const body = await request.json().catch(() => null);
|
||
const { table, id, text } = body ?? {};
|
||
const config = ALLOWED_TABLES[table];
|
||
if (!config || typeof text !== "string" || !text.trim()) {
|
||
return NextResponse.json({ error: "Ungültige Eingaben." }, { status: 400 });
|
||
}
|
||
// Bei einem noch nicht gespeicherten (neuen) Eintrag gibt es keine
|
||
// Zeile zum Aktualisieren – nur übersetzen, das Speichern übernimmt
|
||
// anschließend das reguläre Formular.
|
||
const persist = typeof id === "string" && id.length > 0;
|
||
|
||
const apiKey = process.env.DEEPL_API_KEY;
|
||
if (!apiKey) {
|
||
return NextResponse.json({ configured: false });
|
||
}
|
||
|
||
try {
|
||
const [en, pl] = await Promise.all([
|
||
translateWithDeepL(text, "EN", apiKey),
|
||
translateWithDeepL(text, "PL", apiKey),
|
||
]);
|
||
if (!en || !pl) throw new Error("Keine Übersetzung erhalten.");
|
||
|
||
if (persist) {
|
||
const { error } = await admin
|
||
.from(table)
|
||
.update({ [`${config.textColumn}_en`]: en, [`${config.textColumn}_pl`]: pl })
|
||
.eq(config.idColumn, id);
|
||
if (error) throw new Error(error.message);
|
||
}
|
||
|
||
return NextResponse.json({ configured: true, en, pl });
|
||
} catch (err) {
|
||
return NextResponse.json(
|
||
{ error: err instanceof Error ? err.message : "Übersetzung fehlgeschlagen." },
|
||
{ status: 502 }
|
||
);
|
||
}
|
||
}
|