import { NextRequest, NextResponse } from 'next/server'; import prisma from '@/lib/prisma'; import { Prisma } from '@prisma/client'; import { cookies } from 'next/headers'; import jwt from 'jsonwebtoken'; const LIBRETRANSLATE_URL = process.env.LIBRETRANSLATE_URL || 'https://libretranslate.stackbyte.cloud'; const SUPPORTED_LOCALES = ['pt', 'en', 'es']; const TARGET_TRANSLATION_LOCALES: Array<'en' | 'es'> = ['en', 'es']; // Middleware de autenticação async function authenticate() { const cookieStore = await cookies(); const token = cookieStore.get('auth_token')?.value; if (!token) { return null; } try { const decoded = jwt.verify(token, process.env.JWT_SECRET!) as { userId: string }; const user = await prisma.user.findUnique({ where: { id: decoded.userId }, select: { id: true, email: true, name: true } }); return user; } catch (error) { return null; } } // Tradução com cache async function translateText(text: string, targetLang: string): Promise { if (!text || text.trim() === '' || targetLang === 'pt') return text; try { const cached = await prisma.translation.findUnique({ where: { sourceText_sourceLang_targetLang: { sourceText: text, sourceLang: 'pt', targetLang } } }); if (cached) { return cached.translatedText; } } catch (error) { console.error('[i18n] Erro ao buscar cache de tradução:', error); } try { const response = await fetch(`${LIBRETRANSLATE_URL}/translate`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ q: text, source: 'pt', target: targetLang, format: 'text' }), }); if (response.ok) { const data = await response.json(); const translatedText = data.translatedText || text; try { await prisma.translation.create({ data: { sourceText: text, sourceLang: 'pt', targetLang, translatedText } }); } catch (cacheError) { console.warn('[i18n] Falha ao salvar cache de tradução:', cacheError); } return translatedText; } } catch (error) { console.error(`[i18n] Erro ao traduzir texto para ${targetLang}:`, error); } return text; } async function translateContent(content: unknown, targetLang: string): Promise { if (targetLang === 'pt') return content; const skipKeys = ['icon', 'image', 'img', 'url', 'href', 'id', 'slug', 'src', 'email', 'phone', 'whatsapp', 'link', 'linkText']; if (typeof content === 'string') { return translateText(content, targetLang); } if (Array.isArray(content)) { const translated = [] as unknown[]; for (const item of content) { translated.push(await translateContent(item, targetLang)); } return translated; } if (content && typeof content === 'object') { const result: Record = {}; for (const [key, value] of Object.entries(content)) { if (skipKeys.includes(key)) { result[key] = value; } else { result[key] = await translateContent(value, targetLang); } } return result; } return content; } async function translateInBackground(slug: string, content: unknown) { console.log(`[i18n] Iniciando tradução de "${slug}" para EN/ES em background...`); for (const targetLocale of TARGET_TRANSLATION_LOCALES) { try { const translatedContent = await translateContent(content, targetLocale) as Prisma.InputJsonValue; await prisma.pageContent.upsert({ where: { slug_locale: { slug, locale: targetLocale } }, update: { content: translatedContent }, create: { slug, locale: targetLocale, content: translatedContent } }); console.log(`[i18n] ✓ "${slug}" traduzido para ${targetLocale.toUpperCase()}`); } catch (error) { console.error(`[i18n] ✗ Erro ao traduzir "${slug}" para ${targetLocale}:`, error); } } console.log(`[i18n] Traduções de "${slug}" finalizadas.`); } // GET /api/pages/[slug] - Buscar página específica (público) // Suporta ?locale=en para buscar versão traduzida export async function GET( request: NextRequest, { params }: { params: Promise<{ slug: string }> } ) { try { const { slug } = await params; const locale = request.nextUrl.searchParams.get('locale') || 'pt'; // Buscar a versão do idioma solicitado const page = await prisma.pageContent.findUnique({ where: { slug_locale: { slug, locale } } }); if (page) { return NextResponse.json(page); } // Se não existe a versão traduzida, buscar PT como fallback if (locale !== 'pt') { const ptPage = await prisma.pageContent.findUnique({ where: { slug_locale: { slug, locale: 'pt' } } }); if (ptPage) { // Retorna versão PT com flag indicando que não está traduzido return NextResponse.json({ ...ptPage, locale: 'pt', fallback: true }); } } return NextResponse.json({ error: 'Página não encontrada' }, { status: 404 }); } catch (error) { console.error('Erro ao buscar página:', error); return NextResponse.json({ error: 'Erro ao buscar página' }, { status: 500 }); } } // PUT /api/pages/[slug] - Atualizar página (admin apenas) // Quando salva em PT, automaticamente traduz e salva EN e ES export async function PUT( request: NextRequest, { params }: { params: Promise<{ slug: string }> } ) { try { const user = await authenticate(); if (!user) { return NextResponse.json({ error: 'Não autorizado' }, { status: 401 }); } const { slug } = await params; const body = await request.json(); const { content } = body; if (!content) { return NextResponse.json({ error: 'Conteúdo é obrigatório' }, { status: 400 }); } // 1. Salvar versão em português (principal) const ptPage = await prisma.pageContent.upsert({ where: { slug_locale: { slug, locale: 'pt' } }, update: { content }, create: { slug, locale: 'pt', content } }); // 2. Disparar traduções em background para EN/ES translateInBackground(slug, content).catch(error => { console.error(`[i18n] Erro fatal na tradução em background de "${slug}":`, error); }); return NextResponse.json({ success: true, page: ptPage, message: 'Conteúdo salvo com sucesso!' }); } catch (error) { console.error('Erro ao atualizar página:', error); return NextResponse.json({ error: 'Erro ao atualizar página' }, { status: 500 }); } } // DELETE /api/pages/[slug] - Deletar página (admin apenas) // Remove todas as versões (PT, EN, ES) export async function DELETE( request: NextRequest, { params }: { params: Promise<{ slug: string }> } ) { try { const user = await authenticate(); if (!user) { return NextResponse.json({ error: 'Não autorizado' }, { status: 401 }); } const { slug } = await params; // Deletar todas as versões de idioma await prisma.pageContent.deleteMany({ where: { slug } }); return NextResponse.json({ success: true, message: 'Página deletada com sucesso (todos os idiomas)' }); } catch (error) { console.error('Erro ao deletar página:', error); return NextResponse.json({ error: 'Erro ao deletar página' }, { status: 500 }); } }