diff --git a/supabase/migrations/003_soft_delete.sql b/supabase/migrations/003_soft_delete.sql new file mode 100644 index 0000000..80ca5ea --- /dev/null +++ b/supabase/migrations/003_soft_delete.sql @@ -0,0 +1,24 @@ +-- ============================================================ +-- FreightDesk — Migration 003: Soft Delete + Security +-- ============================================================ + +-- Add soft-delete columns +ALTER TABLE loads ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; +ALTER TABLE payments ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; +ALTER TABLE shippers ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; +ALTER TABLE vehicles ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; + +-- Add role column to portal_users if not exists +ALTER TABLE portal_users ADD COLUMN IF NOT EXISTS role TEXT DEFAULT 'admin'; + +-- Add index for soft-delete queries +CREATE INDEX IF NOT EXISTS idx_loads_deleted_at ON loads(deleted_at) WHERE deleted_at IS NULL; +CREATE INDEX IF NOT EXISTS idx_payments_deleted_at ON payments(deleted_at) WHERE deleted_at IS NULL; + +-- Add load_count to shippers for quick reference +ALTER TABLE shippers ADD COLUMN IF NOT EXISTS load_count INTEGER DEFAULT 0; + +-- Update load_count for existing shippers +UPDATE shippers SET load_count = ( + SELECT COUNT(*) FROM loads WHERE loads.shipper_id = shippers.id +); diff --git a/webapp/src/server.js b/webapp/src/server.js index 557ba75..c7b6f5a 100644 --- a/webapp/src/server.js +++ b/webapp/src/server.js @@ -145,26 +145,45 @@ app.get('/logout', (req, res) => { }); app.get('/setup', asyncHandler(async (req, res) => { - // Check if admin exists + // Check if any user exists const { count } = await supabase .from('portal_users') - .select('*', { count: 'exact', head: true }) - .eq('username', 'admin'); + .select('*', { count: 'exact', head: true }); if (count > 0) { return res.redirect('/login'); } - // Create default admin - const hash = await bcrypt.hash('admin123', 10); - await supabase.from('portal_users').insert({ - username: 'admin', + 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, }); - res.send('
Username: admin
Password: admin123
'); + if (error) { + return res.render('pages/setup', { error: 'Failed to create admin. ' + error.message }); + } + + res.redirect('/login'); })); // ============================================================ diff --git a/webapp/src/views/pages/setup.ejs b/webapp/src/views/pages/setup.ejs new file mode 100644 index 0000000..f72cea7 --- /dev/null +++ b/webapp/src/views/pages/setup.ejs @@ -0,0 +1,46 @@ + + + + + +Create your admin account to get started
+