mirror of
http://forgejo-oa09toasww4dgii9cj3gpzda.187.127.164.61.sslip.io/iamcoolvivek007/bharath.git
synced 2026-06-11 00:06:51 +00:00
Security: - Add CSRF protection on all forms - Fix session config (resave:false, saveUninitialized:false) - Secure cookie settings for production - Input sanitization middleware - Request logging middleware - Security headers via Helmet Code Quality: - Async error handling on ALL route handlers - Proper HTTP status codes (400, 401, 403, 404, 409, 500) - Input validation on all forms (server-side) - Username validation (3-30 chars, alphanumeric+underscore) - Password min length increased to 6 - Generic error messages (no info leakage) - Graceful shutdown on SIGTERM UI/UX: - Dark mode toggle with persistence - Toast notifications for success/error - Loading states on form submit - Improved CSS with CSS variables - Better desktop responsive design - New 403 Forbidden page - Pagination controls - Improved header with desktop nav Features: - Pagination on all list pages (loads, trips, users, messages, etc.) - Admin stats JSON endpoint - Admin user delete route - Load cancel route - Mark invoice as paid route - Search/filter preserved on loadboard Database: - Additional composite indexes for performance - Updated timestamps trigger on trips - Improved FULL migration script DevEx: - Development seed script (seed.js) - Improved Dockerfile (non-root, healthcheck) - Comprehensive .gitignore - Updated README v2.0
120 lines
5.5 KiB
PL/PgSQL
120 lines
5.5 KiB
PL/PgSQL
-- ============================================================
|
|
-- BharathTrucks — FULL DATABASE SETUP v2.0
|
|
-- Run this ONCE in Supabase SQL Editor
|
|
-- ============================================================
|
|
|
|
-- 1. USERS
|
|
CREATE TABLE IF NOT EXISTS app_users (
|
|
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
|
username TEXT UNIQUE NOT NULL,
|
|
name TEXT NOT NULL,
|
|
password_hash TEXT NOT NULL,
|
|
role TEXT NOT NULL CHECK (role IN ('driver', 'shipper', 'broker', 'admin')),
|
|
phone TEXT,
|
|
city TEXT,
|
|
state TEXT,
|
|
is_verified BOOLEAN DEFAULT FALSE,
|
|
is_premium BOOLEAN DEFAULT FALSE,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_app_users_role ON app_users(role);
|
|
CREATE INDEX IF NOT EXISTS idx_app_users_username ON app_users(username);
|
|
CREATE INDEX IF NOT EXISTS idx_app_users_active ON app_users(is_active);
|
|
CREATE INDEX IF NOT EXISTS idx_app_users_created ON app_users(created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_app_users_phone ON app_users(phone) WHERE phone IS NOT NULL;
|
|
|
|
-- 2. LOADS
|
|
CREATE TABLE IF NOT EXISTS loads (
|
|
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
|
posted_by UUID NOT NULL REFERENCES app_users(id) ON DELETE CASCADE,
|
|
origin_city TEXT NOT NULL,
|
|
destination_city TEXT NOT NULL,
|
|
weight_tons NUMERIC(5,1) NOT NULL,
|
|
truck_type TEXT NOT NULL,
|
|
material_type TEXT,
|
|
budget NUMERIC(10,2),
|
|
pickup_date DATE NOT NULL,
|
|
description TEXT,
|
|
is_urgent BOOLEAN DEFAULT FALSE,
|
|
status TEXT DEFAULT 'open' CHECK (status IN ('open', 'booked', 'in_transit', 'delivered', 'cancelled')),
|
|
bid_count INTEGER DEFAULT 0,
|
|
accepted_bid_id UUID,
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_loads_status ON loads(status);
|
|
CREATE INDEX IF NOT EXISTS idx_loads_origin ON loads(origin_city);
|
|
CREATE INDEX IF NOT EXISTS idx_loads_destination ON loads(destination_city);
|
|
CREATE INDEX IF NOT EXISTS idx_loads_posted_by ON loads(posted_by);
|
|
CREATE INDEX IF NOT EXISTS idx_loads_status_created ON loads(status, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_loads_truck_type ON loads(truck_type);
|
|
CREATE INDEX IF NOT EXISTS idx_loads_urgent ON loads(is_urgent) WHERE is_urgent = true;
|
|
|
|
-- 3. BIDS
|
|
CREATE INDEX IF NOT EXISTS idx_bids_load ON bids(load_id);
|
|
CREATE INDEX IF NOT EXISTS idx_bids_driver ON bids(driver_id);
|
|
CREATE INDEX IF NOT EXISTS idx_bids_status ON bids(status);
|
|
CREATE INDEX IF NOT EXISTS idx_bids_load_driver ON bids(load_id, driver_id);
|
|
|
|
-- 4. TRIPS
|
|
CREATE INDEX IF NOT EXISTS idx_trips_driver ON trips(driver_id);
|
|
CREATE INDEX IF NOT EXISTS idx_trips_shipper ON trips(shipper_id);
|
|
CREATE INDEX IF NOT EXISTS idx_trips_status ON trips(status);
|
|
CREATE INDEX IF NOT EXISTS idx_trips_driver_status ON trips(driver_id, status);
|
|
CREATE INDEX IF NOT EXISTS idx_trips_shipper_status ON trips(shipper_id, status);
|
|
CREATE INDEX IF NOT EXISTS idx_trips_created ON trips(created_at DESC);
|
|
|
|
-- 5. MESSAGES
|
|
CREATE INDEX IF NOT EXISTS idx_messages_receiver ON messages(receiver_id, is_read);
|
|
CREATE INDEX IF NOT EXISTS idx_messages_sender ON messages(sender_id);
|
|
CREATE INDEX IF NOT EXISTS idx_messages_conversation ON messages(sender_id, receiver_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_messages_load ON messages(load_id) WHERE load_id IS NOT NULL;
|
|
|
|
-- 6. TRIGGERS
|
|
CREATE OR REPLACE FUNCTION increment_bid_count()
|
|
RETURNS TRIGGER AS $$ BEGIN UPDATE loads SET bid_count = bid_count + 1 WHERE id = NEW.load_id; RETURN NEW; END; $$ LANGUAGE plpgsql;
|
|
DROP TRIGGER IF EXISTS bid_count_trigger ON bids;
|
|
CREATE TRIGGER bid_count_trigger AFTER INSERT ON bids FOR EACH ROW EXECUTE FUNCTION increment_bid_count();
|
|
|
|
CREATE OR REPLACE FUNCTION update_updated_at()
|
|
RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = NOW(); RETURN NEW; END; $$ LANGUAGE plpgsql;
|
|
DROP TRIGGER IF EXISTS loads_updated_at ON loads;
|
|
CREATE TRIGGER loads_updated_at BEFORE UPDATE ON loads FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
|
|
|
-- Add updated_at to trips if not exists
|
|
DO $$ BEGIN
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'trips' AND column_name = 'updated_at') THEN
|
|
ALTER TABLE trips ADD COLUMN updated_at TIMESTAMPTZ DEFAULT NOW();
|
|
CREATE TRIGGER trips_updated_at BEFORE UPDATE ON trips FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
|
END IF;
|
|
END $$;
|
|
|
|
-- 7. RLS (open for now — tighten later)
|
|
ALTER TABLE app_users ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE loads ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE bids ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE trips ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE messages ENABLE ROW LEVEL SECURITY;
|
|
|
|
DROP POLICY IF EXISTS "open" ON app_users;
|
|
DROP POLICY IF EXISTS "open" ON loads;
|
|
DROP POLICY IF EXISTS "open" ON bids;
|
|
DROP POLICY IF EXISTS "open" ON trips;
|
|
DROP POLICY IF EXISTS "open" ON messages;
|
|
|
|
CREATE POLICY "open" ON app_users FOR ALL USING (true) WITH CHECK (true);
|
|
CREATE POLICY "open" ON loads FOR ALL USING (true) WITH CHECK (true);
|
|
CREATE POLICY "open" ON bids FOR ALL USING (true) WITH CHECK (true);
|
|
CREATE POLICY "open" ON trips FOR ALL USING (true) WITH CHECK (true);
|
|
CREATE POLICY "open" ON messages FOR ALL USING (true) WITH CHECK (true);
|
|
|
|
-- 8. SEED ADMIN USER (password: admin123)
|
|
INSERT INTO app_users (username, name, password_hash, role) VALUES
|
|
('admin', 'Admin', '$2a$10$LAzb105XOhAqSXm1jMUQue0fp/lrUJBhsLgkobOQTeU.8Jh4rTaoq', 'admin')
|
|
ON CONFLICT (username) DO NOTHING;
|
|
|
|
-- ============================================================
|
|
-- DONE! Your database is ready.
|
|
-- Admin login: username=admin, password=admin123
|
|
-- ============================================================
|