Compare commits
3 commits
8ae3b403ab
...
071f759b8a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
071f759b8a | ||
|
|
0da63ae676 | ||
|
|
f1c75faba1 |
3 changed files with 57 additions and 43 deletions
55
webapp/src/routes/setup.js
Normal file
55
webapp/src/routes/setup.js
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const bcrypt = require('bcryptjs');
|
||||
const supabase = require('../services/supabase');
|
||||
const { asyncHandler } = require('../middleware/security');
|
||||
|
||||
// GET /setup — show wizard if no admin exists
|
||||
router.get('/', asyncHandler(async (req, res) => {
|
||||
const { count } = await supabase
|
||||
.from('portal_users')
|
||||
.select('*', { count: 'exact', head: true })
|
||||
.eq('role', 'admin');
|
||||
|
||||
if (count > 0) return res.redirect('/login');
|
||||
|
||||
res.render('pages/setup', { error: null });
|
||||
}));
|
||||
|
||||
// POST /setup — create first admin securely (race-condition safe)
|
||||
router.post('/', asyncHandler(async (req, res) => {
|
||||
const { username, password } = req.body;
|
||||
if (!username || !password) {
|
||||
return res.render('pages/setup', { error: 'Username and password are required' });
|
||||
}
|
||||
if (password.length < 6) {
|
||||
return res.render('pages/setup', { error: 'Password must be at least 6 characters' });
|
||||
}
|
||||
|
||||
// Race-condition safety: double-check no admin exists
|
||||
const { data: existing } = await supabase
|
||||
.from('portal_users')
|
||||
.select('id')
|
||||
.eq('role', 'admin')
|
||||
.single();
|
||||
|
||||
if (existing) {
|
||||
return res.render('pages/setup', { error: 'Admin already configured' });
|
||||
}
|
||||
|
||||
const hash = await bcrypt.hash(password, 12);
|
||||
const { error } = await supabase.from('portal_users').insert({
|
||||
username,
|
||||
password_hash: hash,
|
||||
role: 'admin',
|
||||
is_active: true,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
return res.render('pages/setup', { error: 'Failed to create admin: ' + error.message });
|
||||
}
|
||||
|
||||
res.redirect('/login');
|
||||
}));
|
||||
|
||||
module.exports = router;
|
||||
|
|
@ -154,48 +154,6 @@ app.get('/logout', (req, res) => {
|
|||
res.redirect('/login');
|
||||
});
|
||||
|
||||
app.get('/setup', asyncHandler(async (req, res) => {
|
||||
// Check if any user exists
|
||||
const { count } = await supabase
|
||||
.from('portal_users')
|
||||
.select('*', { count: 'exact', head: true });
|
||||
|
||||
if (count > 0) {
|
||||
return res.redirect('/login');
|
||||
}
|
||||
|
||||
res.render('pages/setup', { error: null });
|
||||
}));
|
||||
|
||||
app.post('/setup', asyncHandler(async (req, res) => {
|
||||
const { count } = await supabase
|
||||
.from('portal_users')
|
||||
.select('*', { count: 'exact', head: true });
|
||||
|
||||
if (count > 0) {
|
||||
return res.redirect('/login');
|
||||
}
|
||||
|
||||
const { username, password } = req.body;
|
||||
if (!username || !password || password.length < 6) {
|
||||
return res.render('pages/setup', { error: 'Username required and password must be at least 6 characters' });
|
||||
}
|
||||
|
||||
const hash = await bcrypt.hash(password, 10);
|
||||
const { error } = await supabase.from('portal_users').insert({
|
||||
username,
|
||||
password_hash: hash,
|
||||
role: 'admin',
|
||||
is_active: true,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
return res.render('pages/setup', { error: 'Failed to create admin. ' + error.message });
|
||||
}
|
||||
|
||||
res.redirect('/login');
|
||||
}));
|
||||
|
||||
// ============================================================
|
||||
// API ROUTES (for React dashboard + WhatsApp parser)
|
||||
// ============================================================
|
||||
|
|
@ -240,6 +198,7 @@ app.get('/api/stats', requireAuth, asyncHandler(async (req, res) => {
|
|||
// ============================================================
|
||||
|
||||
app.use('/', require('./routes/dashboard'));
|
||||
app.use('/setup', require('./routes/setup'));
|
||||
app.use('/loads', require('./routes/loads'));
|
||||
app.use('/shippers', require('./routes/shippers'));
|
||||
app.use('/vehicles', require('./routes/vehicles'));
|
||||
|
|
|
|||
|
|
@ -41,6 +41,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/app.js"></script>
|
||||
<script src="/js/app.js?v=<%= typeof assetVersion !== 'undefined' ? assetVersion : '1' %>"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Reference in a new issue