diff --git a/frontend/src/app/admin/layout.tsx b/frontend/src/app/admin/layout.tsx
index 810d714..1588f44 100644
--- a/frontend/src/app/admin/layout.tsx
+++ b/frontend/src/app/admin/layout.tsx
@@ -87,8 +87,8 @@ export default function AdminLayout({
useEffect(() => {
setMounted(true);
const fetchUser = async () => {
- // Pular verificação para a rota de emergência (suporta com ou sem prefixo de idioma)
- if (pathname?.endsWith('/admin/backup/emergency')) {
+ // Pular verificação para rotas de emergência
+ if (pathname?.endsWith('/admin/backup/emergency') || pathname?.endsWith('/admin/rescue')) {
setIsLoading(false);
return;
}
@@ -177,8 +177,8 @@ export default function AdminLayout({
);
}
- // Se for a rota de emergência, renderiza apenas o conteúdo
- if (pathname?.endsWith('/admin/backup/emergency')) {
+ // Se for uma rota de emergência, renderiza apenas o conteúdo
+ if (pathname?.endsWith('/admin/backup/emergency') || pathname?.endsWith('/admin/rescue')) {
return (
{children}
diff --git a/frontend/src/app/admin/rescue/page.tsx b/frontend/src/app/admin/rescue/page.tsx
new file mode 100644
index 0000000..60fb500
--- /dev/null
+++ b/frontend/src/app/admin/rescue/page.tsx
@@ -0,0 +1,103 @@
+'use client';
+
+import { useState } from 'react';
+import { useRouter } from 'next/navigation';
+
+export default function RescuePage() {
+ const [email, setEmail] = useState('');
+ const [password, setPassword] = useState('');
+ const [name, setName] = useState('');
+ const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
+ const [message, setMessage] = useState('');
+ const router = useRouter();
+
+ const handleCreate = async (e: React.FormEvent) => {
+ e.preventDefault();
+ setStatus('loading');
+
+ try {
+ const res = await fetch('/api/admin/rescue', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ email, password, name }),
+ });
+
+ if (res.ok) {
+ setStatus('success');
+ setMessage('Usuário administrador criado com sucesso! Redirecionando...');
+ setTimeout(() => router.push('/acesso'), 2000);
+ } else {
+ const error = await res.json();
+ setStatus('error');
+ setMessage(error.error || 'Erro ao criar usuário.');
+ }
+ } catch (err) {
+ setStatus('error');
+ setMessage('Erro de conexão.');
+ }
+ };
+
+ return (
+
+
+
+
+
Resgate de Emergência
+
+
+
Use esta página apenas se você foi bloqueado do sistema. Ela criará um novo administrador.
+
+
+
+
+ );
+}
diff --git a/frontend/src/app/api/admin/backup/full/route.ts b/frontend/src/app/api/admin/backup/full/route.ts
index 7eaf787..9fedfac 100644
--- a/frontend/src/app/api/admin/backup/full/route.ts
+++ b/frontend/src/app/api/admin/backup/full/route.ts
@@ -146,15 +146,23 @@ export async function POST(req: NextRequest) {
// Importação atômica com timeout estendido para 120 segundos
console.log('🔄 Iniciando transação no banco de dados...');
await prisma.$transaction(async (tx) => {
- console.log('🧹 Limpando tabelas atuais...');
+ console.log('🧹 Limpando tabelas atuais (exceto usuários se o backup estiver incompleto)...');
await tx.message.deleteMany();
await tx.project.deleteMany();
await tx.service.deleteMany();
await tx.pageContent.deleteMany();
- await tx.user.deleteMany();
+
+ // Só apaga os usuários se viermos com novos usuários no ZIP
+ const hasUsersInBackup = data.users && data.users.length > 0;
+ if (hasUsersInBackup) {
+ console.log('👥 Backup contém usuários. Atualizando tabela de usuários...');
+ await tx.user.deleteMany();
+ await tx.user.createMany({ data: data.users });
+ } else {
+ console.warn('⚠️ Backup NÃO contém usuários. Preservando usuários atuais para evitar bloqueio.');
+ }
console.log('📝 Inserindo novos dados...');
- if (data.users?.length > 0) await tx.user.createMany({ data: data.users });
if (data.projects?.length > 0) await tx.project.createMany({ data: data.projects });
if (data.services?.length > 0) await tx.service.createMany({ data: data.services });
if (data.pageContents?.length > 0) await tx.pageContent.createMany({ data: data.pageContents });
diff --git a/frontend/src/app/api/admin/rescue/route.ts b/frontend/src/app/api/admin/rescue/route.ts
new file mode 100644
index 0000000..7d6017a
--- /dev/null
+++ b/frontend/src/app/api/admin/rescue/route.ts
@@ -0,0 +1,35 @@
+import { NextRequest, NextResponse } from 'next/server';
+import prisma from '@/lib/prisma';
+import bcrypt from 'bcryptjs';
+
+export async function POST(req: NextRequest) {
+ try {
+ const { email, password, name } = await req.json();
+
+ if (!email || !password) {
+ return NextResponse.json({ error: 'Email e senha são obrigatórios' }, { status: 400 });
+ }
+
+ // Criptografar senha
+ const hashedPassword = await bcrypt.hash(password, 10);
+
+ // Criar ou atualizar usuário
+ const user = await prisma.user.upsert({
+ where: { email },
+ update: {
+ password: hashedPassword,
+ name: name || undefined
+ },
+ create: {
+ email,
+ password: hashedPassword,
+ name: name || 'Administrador'
+ }
+ });
+
+ return NextResponse.json({ message: 'Usuário configurado com sucesso' });
+ } catch (error) {
+ console.error('Erro no resgate de usuário:', error);
+ return NextResponse.json({ error: 'Erro interno no servidor' }, { status: 500 });
+ }
+}