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:
parent
621e4594c6
commit
5155fa853d
7 changed files with 55 additions and 10 deletions
BIN
frontend-source.tar.gz
Normal file
BIN
frontend-source.tar.gz
Normal file
Binary file not shown.
|
|
@ -27,6 +27,8 @@ export default function SeederPage() {
|
||||||
const [processStatus, setProcessStatus] = useState<'idle' | 'running' | 'success' | 'error'>('idle');
|
const [processStatus, setProcessStatus] = useState<'idle' | 'running' | 'success' | 'error'>('idle');
|
||||||
const scrollEndRef = useRef<HTMLDivElement>(null);
|
const scrollEndRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const [showConfirmDialog, setShowConfirmDialog] = useState(false);
|
||||||
|
|
||||||
// Auto-scroll to bottom of logs
|
// Auto-scroll to bottom of logs
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (scrollEndRef.current) {
|
if (scrollEndRef.current) {
|
||||||
|
|
@ -54,7 +56,11 @@ export default function SeederPage() {
|
||||||
if (type === 'reset') {
|
if (type === 'reset') {
|
||||||
// Legacy Reset (POST) - Mimic stream output
|
// Legacy Reset (POST) - Mimic stream output
|
||||||
setLogs(['🚀 Starting flush/reset process...']);
|
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();
|
const data = await res.json();
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
setLogs(prev => [...prev, '✅ Reset completed successfully!', JSON.stringify(data, null, 2)]);
|
setLogs(prev => [...prev, '✅ Reset completed successfully!', JSON.stringify(data, null, 2)]);
|
||||||
|
|
@ -196,9 +202,7 @@ export default function SeederPage() {
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => setShowConfirmDialog(true)}
|
||||||
if (confirm("Tem certeza ABSOLUTA?")) handleStream('reset', 'reset');
|
|
||||||
}}
|
|
||||||
disabled={isSeeding || isResetting}
|
disabled={isSeeding || isResetting}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
|
|
@ -277,6 +281,37 @@ export default function SeederPage() {
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
"backoffice": "Backoffice",
|
"backoffice": "Backoffice",
|
||||||
"messages": "Messages",
|
"messages": "Messages",
|
||||||
"tickets": "Tickets",
|
"tickets": "Tickets",
|
||||||
|
"settings": "Settings",
|
||||||
"my_jobs": "My Jobs",
|
"my_jobs": "My Jobs",
|
||||||
"applications": "Applications",
|
"applications": "Applications",
|
||||||
"my_applications": "My Applications",
|
"my_applications": "My Applications",
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
"backoffice": "Backoffice",
|
"backoffice": "Backoffice",
|
||||||
"messages": "Mensagens",
|
"messages": "Mensagens",
|
||||||
"tickets": "Tickets",
|
"tickets": "Tickets",
|
||||||
|
"settings": "Configurações",
|
||||||
"my_jobs": "Minhas Vagas",
|
"my_jobs": "Minhas Vagas",
|
||||||
"applications": "Candidaturas",
|
"applications": "Candidaturas",
|
||||||
"my_applications": "Minhas Candidaturas",
|
"my_applications": "Minhas Candidaturas",
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,11 @@ function mapRoleFromBackend(roles: string[]): "candidate" | "admin" | "company"
|
||||||
*/
|
*/
|
||||||
export async function refreshSession(): Promise<User | null> {
|
export async function refreshSession(): Promise<User | null> {
|
||||||
try {
|
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");
|
console.log("%c[AUTH] Attempting to refresh session...", "color: #3b82f6");
|
||||||
const res = await fetch(`${getApiV1Url()}/users/me`, {
|
const res = await fetch(`${getApiV1Url()}/users/me`, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,7 @@ async function seedDatabase() {
|
||||||
// Lite version (skips cities for faster seeding)
|
// Lite version (skips cities for faster seeding)
|
||||||
async function seedDatabaseLite() {
|
async function seedDatabaseLite() {
|
||||||
console.log('🌱 Starting database seeding (LITE - no cities)...\n');
|
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)');
|
console.log('🌶️ PASSWORD_PEPPER:', process.env.PASSWORD_PEPPER ? `"${process.env.PASSWORD_PEPPER}"` : '(not set)');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -138,9 +139,13 @@ async function seedDatabaseLite() {
|
||||||
console.log('');
|
console.log('');
|
||||||
|
|
||||||
// Seed in order (respecting foreign key dependencies)
|
// Seed in order (respecting foreign key dependencies)
|
||||||
|
console.log('🔍 Debug: seeding location data (lite)...');
|
||||||
await seedLocationDataLite(); // ⚡ Fast mode - no cities
|
await seedLocationDataLite(); // ⚡ Fast mode - no cities
|
||||||
|
console.log('🔍 Debug: seeding companies...');
|
||||||
await seedCompanies();
|
await seedCompanies();
|
||||||
|
console.log('🔍 Debug: seeding users...');
|
||||||
await seedUsers();
|
await seedUsers();
|
||||||
|
console.log('🔍 Debug: seeding jobs...');
|
||||||
await seedJobs();
|
await seedJobs();
|
||||||
await seedAcmeCorp();
|
await seedAcmeCorp();
|
||||||
await seedWileECoyote();
|
await seedWileECoyote();
|
||||||
|
|
@ -152,13 +157,13 @@ async function seedDatabaseLite() {
|
||||||
await seedTags();
|
await seedTags();
|
||||||
|
|
||||||
console.log('\n✅ Database seeding (LITE) completed successfully!');
|
console.log('\n✅ Database seeding (LITE) completed successfully!');
|
||||||
|
console.log('🔍 Debug: seedDatabaseLite finished normally');
|
||||||
console.log(' ⚡ Cities skipped for faster seeding');
|
console.log(' ⚡ Cities skipped for faster seeding');
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('\n❌ Seeding failed:', error.message);
|
console.error('\n❌ Seeding failed:', error.message);
|
||||||
console.error(error);
|
console.error(error);
|
||||||
} finally {
|
throw error;
|
||||||
await closePool();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -194,8 +199,7 @@ async function seedDatabaseNoLocations() {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('\n❌ Seeding failed:', error.message);
|
console.error('\n❌ Seeding failed:', error.message);
|
||||||
console.error(error);
|
console.error(error);
|
||||||
} finally {
|
throw error;
|
||||||
await closePool();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -233,4 +237,3 @@ if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -149,7 +149,7 @@ app.post('/reset', async (req, res) => {
|
||||||
return res.status(409).json({ error: 'Seeding/Reset in progress' });
|
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) {
|
if (process.env.SEED_PASSWORD && password !== process.env.SEED_PASSWORD) {
|
||||||
return res.status(401).json({ error: 'Unauthorized' });
|
return res.status(401).json({ error: 'Unauthorized' });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue