debug: add S3 network diagnostics to start.sh
This commit is contained in:
@@ -1,92 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { useRouter } from 'next/navigation';
|
|
||||||
|
|
||||||
export default function EmergencyBackupPage() {
|
|
||||||
const [file, setFile] = useState<File | null>(null);
|
|
||||||
const [status, setStatus] = useState<'idle' | 'uploading' | 'success' | 'error'>('idle');
|
|
||||||
const [message, setMessage] = useState('');
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const handleUpload = async () => {
|
|
||||||
if (!file) return;
|
|
||||||
setStatus('uploading');
|
|
||||||
setMessage('Restaurando sistema de emergência... Por favor, aguarde.');
|
|
||||||
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('file', file);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await fetch('/api/admin/backup/full', {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (res.ok) {
|
|
||||||
setStatus('success');
|
|
||||||
setMessage('Sistema restaurado com sucesso! Redirecionando para o login...');
|
|
||||||
setTimeout(() => router.push('/admin/login'), 3000);
|
|
||||||
} else {
|
|
||||||
const errorData = await res.json();
|
|
||||||
setStatus('error');
|
|
||||||
setMessage(errorData.error || 'Erro ao restaurar backup.');
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
setStatus('error');
|
|
||||||
setMessage('A conexão caiu, mas o servidor pode ainda estar processando. Aguarde 2 minutos e tente logar. Se não funcionar, verifique os logs do Dokploy.');
|
|
||||||
console.error('Erro de restauração:', err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="min-h-screen bg-slate-900 flex items-center justify-center p-4">
|
|
||||||
<div className="max-w-md w-full bg-slate-800 rounded-2xl shadow-2xl p-8 border border-white/10">
|
|
||||||
<h1 className="text-2xl font-bold text-white mb-2">🔑 Chave Mestra de Restauração</h1>
|
|
||||||
<p className="text-slate-400 mb-8">Use esta página para restaurar seu backup ZIP em um banco de dados novo.</p>
|
|
||||||
|
|
||||||
<div className="space-y-6">
|
|
||||||
<div className="border-2 border-dashed border-slate-600 rounded-xl p-8 text-center hover:border-blue-500 transition-colors cursor-pointer relative">
|
|
||||||
<input
|
|
||||||
type="file"
|
|
||||||
accept=".zip"
|
|
||||||
className="absolute inset-0 opacity-0 cursor-pointer"
|
|
||||||
onChange={(e) => setFile(e.target.files?.[0] || null)}
|
|
||||||
/>
|
|
||||||
<div className="text-slate-300">
|
|
||||||
{file ? (
|
|
||||||
<span className="text-blue-400 font-medium">{file.name}</span>
|
|
||||||
) : (
|
|
||||||
'Clique ou arraste o arquivo .zip de backup aqui'
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button
|
|
||||||
onClick={handleUpload}
|
|
||||||
disabled={!file || status === 'uploading'}
|
|
||||||
className="w-full bg-blue-600 hover:bg-blue-500 disabled:bg-slate-700 text-white font-bold py-4 rounded-xl transition-all shadow-lg flex items-center justify-center gap-3"
|
|
||||||
>
|
|
||||||
{status === 'uploading' ? (
|
|
||||||
<>
|
|
||||||
<div className="w-5 h-5 border-2 border-white/30 border-t-white rounded-full animate-spin"></div>
|
|
||||||
Restaurando...
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
'Restaurar Sistema Agora'
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{message && (
|
|
||||||
<div className={`p-4 rounded-lg text-center text-sm font-medium ${status === 'success' ? 'bg-emerald-500/20 text-emerald-400 border border-emerald-500/30' :
|
|
||||||
status === 'error' ? 'bg-rose-500/20 text-rose-400 border border-rose-500/30' :
|
|
||||||
'bg-blue-500/10 text-blue-400 border border-blue-500/20'
|
|
||||||
}`}>
|
|
||||||
{message}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -87,12 +87,6 @@ export default function AdminLayout({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setMounted(true);
|
setMounted(true);
|
||||||
const fetchUser = async () => {
|
const fetchUser = async () => {
|
||||||
// Pular verificação para rotas de emergência
|
|
||||||
if (pathname?.endsWith('/admin/backup/emergency') || pathname?.endsWith('/admin/rescue')) {
|
|
||||||
setIsLoading(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/auth/me');
|
const response = await fetch('/api/auth/me');
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
@@ -177,15 +171,6 @@ export default function AdminLayout({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Se for uma rota de emergência, renderiza apenas o conteúdo
|
|
||||||
if (pathname?.endsWith('/admin/backup/emergency') || pathname?.endsWith('/admin/rescue')) {
|
|
||||||
return (
|
|
||||||
<div className="min-h-screen bg-slate-900">
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Se não tem usuário após loading, não renderizar nada (está redirecionando)
|
// Se não tem usuário após loading, não renderizar nada (está redirecionando)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -1,103 +0,0 @@
|
|||||||
'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 (
|
|
||||||
<div className="min-h-screen bg-slate-900 flex items-center justify-center p-4">
|
|
||||||
<div className="max-w-md w-full bg-slate-800 rounded-2xl shadow-2xl p-8 border border-rose-500/30">
|
|
||||||
<div className="flex items-center gap-3 mb-6">
|
|
||||||
<i className="ri-alarm-warning-line text-3xl text-rose-500"></i>
|
|
||||||
<h1 className="text-2xl font-bold text-white">Resgate de Emergência</h1>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p className="text-slate-400 mb-8 text-sm">Use esta página apenas se você foi bloqueado do sistema. Ela criará um novo administrador.</p>
|
|
||||||
|
|
||||||
<form onSubmit={handleCreate} className="space-y-4">
|
|
||||||
<div>
|
|
||||||
<label className="block text-xs font-medium text-slate-400 uppercase mb-1">Nome Completo</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
required
|
|
||||||
className="w-full bg-slate-700 border border-slate-600 rounded-lg px-4 py-3 text-white focus:ring-2 focus:ring-rose-500 outline-none"
|
|
||||||
value={name}
|
|
||||||
onChange={(e) => setName(e.target.value)}
|
|
||||||
placeholder="Ex: Erik"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label className="block text-xs font-medium text-slate-400 uppercase mb-1">E-mail de Acesso</label>
|
|
||||||
<input
|
|
||||||
type="email"
|
|
||||||
required
|
|
||||||
className="w-full bg-slate-700 border border-slate-600 rounded-lg px-4 py-3 text-white focus:ring-2 focus:ring-rose-500 outline-none"
|
|
||||||
value={email}
|
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
|
||||||
placeholder="seu@email.com"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label className="block text-xs font-medium text-slate-400 uppercase mb-1">Nova Senha</label>
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
required
|
|
||||||
className="w-full bg-slate-700 border border-slate-600 rounded-lg px-4 py-3 text-white focus:ring-2 focus:ring-rose-500 outline-none"
|
|
||||||
value={password}
|
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
|
||||||
placeholder="••••••••"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
disabled={status === 'loading'}
|
|
||||||
className="w-full bg-rose-600 hover:bg-rose-500 text-white font-bold py-4 rounded-xl transition-all shadow-lg flex items-center justify-center gap-3 mt-4"
|
|
||||||
>
|
|
||||||
{status === 'loading' ? 'Criando...' : 'Criar Administrador Agora'}
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{message && (
|
|
||||||
<div className={`p-4 rounded-lg text-center text-sm font-medium mt-4 ${status === 'success' ? 'bg-emerald-500/20 text-emerald-400' : 'bg-rose-500/20 text-rose-400'
|
|
||||||
}`}>
|
|
||||||
{message}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
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 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -50,5 +50,30 @@ else
|
|||||||
echo "⚠️ DATABASE_URL não encontrada. O site pode falhar."
|
echo "⚠️ DATABASE_URL não encontrada. O site pode falhar."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# --- DIAGNÓSTICO DE REDE (S3) ---
|
||||||
|
if [ ! -z "$S3_ENDPOINT" ]; then
|
||||||
|
echo "📡 Testando conexão S3 em $S3_ENDPOINT na porta ${S3_PORT:-9000}..."
|
||||||
|
node -e "
|
||||||
|
const net = require('net');
|
||||||
|
const s3Host = '$S3_ENDPOINT';
|
||||||
|
const s3Port = parseInt('${S3_PORT:-9000}');
|
||||||
|
|
||||||
|
const client = net.createConnection({ host: s3Host, port: s3Port, timeout: 5000 }, () => {
|
||||||
|
console.log('✅ REDE S3 OK: A porta ' + s3Port + ' está aberta!');
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('error', (err) => {
|
||||||
|
console.log('❌ REDE S3 ERRO: ' + err.message);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('timeout', () => {
|
||||||
|
console.log('❌ REDE S3 TIMEOUT: O host ' + s3Host + ' não respondeu.');
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
" || echo "⚠️ Aviso: Não foi possível confirmar o S3, mas continuaremos..."
|
||||||
|
fi
|
||||||
|
|
||||||
echo "🚀 Iniciando Octto Engenharia..."
|
echo "🚀 Iniciando Octto Engenharia..."
|
||||||
exec node server.js
|
exec node server.js
|
||||||
|
|||||||
Reference in New Issue
Block a user