fix: Auth URL race, Seeder Reset 500, and UI confirmation modal

- auth.ts: await initConfig() before refreshSession to fix localhost fallback
- server.js: optional chaining req.body?.password for reset endpoint
- seeder/page.tsx: replace confirm() with elegant AlertDialog for reset
This commit is contained in:
Yamamoto 2026-01-03 14:21:29 -03:00
parent 621e4594c6
commit 5155fa853d
7 changed files with 55 additions and 10 deletions

BIN
frontend-source.tar.gz Normal file

Binary file not shown.

View file

@ -27,6 +27,8 @@ export default function SeederPage() {
const [processStatus, setProcessStatus] = useState<'idle' | 'running' | 'success' | 'error'>('idle');
const scrollEndRef = useRef<HTMLDivElement>(null);
const [showConfirmDialog, setShowConfirmDialog] = useState(false);
// Auto-scroll to bottom of logs
useEffect(() => {
if (scrollEndRef.current) {
@ -54,7 +56,11 @@ export default function SeederPage() {
if (type === 'reset') {
// Legacy Reset (POST) - Mimic stream output
setLogs(['🚀 Starting flush/reset process...']);
const res = await fetch(`${getSeederApiUrl()}/reset`, { method: 'POST' });
const res = await fetch(`${getSeederApiUrl()}/reset`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' }, // Fix for Express parsing empty body if needed
body: JSON.stringify({}) // Explicit empty body
});
const data = await res.json();
if (res.ok) {
setLogs(prev => [...prev, '✅ Reset completed successfully!', JSON.stringify(data, null, 2)]);
@ -196,9 +202,7 @@ export default function SeederPage() {
</ul>
</div>
<Button
onClick={() => {
if (confirm("Tem certeza ABSOLUTA?")) handleStream('reset', 'reset');
}}
onClick={() => setShowConfirmDialog(true)}
disabled={isSeeding || isResetting}
className="w-full"
variant="destructive"
@ -277,6 +281,37 @@ export default function SeederPage() {
</DialogFooter>
</DialogContent>
</Dialog>
{/* Confirmation Dialog */}
<Dialog open={showConfirmDialog} onOpenChange={setShowConfirmDialog}>
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-2 text-destructive">
<AlertTriangle className="h-6 w-6" />
Atenção Absoluta!
</DialogTitle>
<DialogDescription className="text-base pt-2">
Você está prestes a <strong>APAGAR TODOS OS DADOS</strong> do banco de dados (exceto admin).
<br /><br />
Esta ação é irreversível. Todas as vagas, candidaturas e empresas serão perdidas.
</DialogDescription>
</DialogHeader>
<DialogFooter className="gap-2 sm:gap-0">
<Button variant="ghost" onClick={() => setShowConfirmDialog(false)}>
Cancelar, me tire daqui!
</Button>
<Button
variant="destructive"
onClick={() => {
setShowConfirmDialog(false);
handleStream('reset', 'reset');
}}
>
Sim, apagar tudo
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
);
}

View file

@ -8,6 +8,7 @@
"backoffice": "Backoffice",
"messages": "Messages",
"tickets": "Tickets",
"settings": "Settings",
"my_jobs": "My Jobs",
"applications": "Applications",
"my_applications": "My Applications",

View file

@ -8,6 +8,7 @@
"backoffice": "Backoffice",
"messages": "Mensagens",
"tickets": "Tickets",
"settings": "Configurações",
"my_jobs": "Minhas Vagas",
"applications": "Candidaturas",
"my_applications": "Minhas Candidaturas",

View file

@ -122,6 +122,11 @@ function mapRoleFromBackend(roles: string[]): "candidate" | "admin" | "company"
*/
export async function refreshSession(): Promise<User | null> {
try {
// Ensure runtime config is loaded before making the request
if (typeof window !== "undefined") {
await import("./config").then((m) => m.initConfig());
}
console.log("%c[AUTH] Attempting to refresh session...", "color: #3b82f6");
const res = await fetch(`${getApiV1Url()}/users/me`, {
method: "GET",

View file

@ -127,6 +127,7 @@ async function seedDatabase() {
// Lite version (skips cities for faster seeding)
async function seedDatabaseLite() {
console.log('🌱 Starting database seeding (LITE - no cities)...\n');
console.log('🔍 Debug: seedDatabaseLite started');
console.log('🌶️ PASSWORD_PEPPER:', process.env.PASSWORD_PEPPER ? `"${process.env.PASSWORD_PEPPER}"` : '(not set)');
try {
@ -138,9 +139,13 @@ async function seedDatabaseLite() {
console.log('');
// Seed in order (respecting foreign key dependencies)
console.log('🔍 Debug: seeding location data (lite)...');
await seedLocationDataLite(); // ⚡ Fast mode - no cities
console.log('🔍 Debug: seeding companies...');
await seedCompanies();
console.log('🔍 Debug: seeding users...');
await seedUsers();
console.log('🔍 Debug: seeding jobs...');
await seedJobs();
await seedAcmeCorp();
await seedWileECoyote();
@ -152,13 +157,13 @@ async function seedDatabaseLite() {
await seedTags();
console.log('\n✅ Database seeding (LITE) completed successfully!');
console.log('🔍 Debug: seedDatabaseLite finished normally');
console.log(' ⚡ Cities skipped for faster seeding');
} catch (error) {
console.error('\n❌ Seeding failed:', error.message);
console.error(error);
} finally {
await closePool();
throw error;
}
}
@ -194,8 +199,7 @@ async function seedDatabaseNoLocations() {
} catch (error) {
console.error('\n❌ Seeding failed:', error.message);
console.error(error);
} finally {
await closePool();
throw error;
}
}
@ -233,4 +237,3 @@ if (process.argv[1] === fileURLToPath(import.meta.url)) {
}
})();
}

View file

@ -149,7 +149,7 @@ app.post('/reset', async (req, res) => {
return res.status(409).json({ error: 'Seeding/Reset in progress' });
}
const password = req.body.password;
const password = req.body?.password;
if (process.env.SEED_PASSWORD && password !== process.env.SEED_PASSWORD) {
return res.status(401).json({ error: 'Unauthorized' });
}