# Database Schema Overview Complete database schema for MotoVaultPro 5-container architecture with 15 feature capsules. ## Migration System ### Migration Order (Dependencies) 1. **admin** - Admin users and audit logs, no dependencies 2. **user-profile** - User profiles, no dependencies 3. **user-preferences** - User preferences, depends on user-profile 4. **terms-agreement** - Terms acceptance audit, no dependencies 5. **platform** - Vehicle lookup data (engines, transmissions, vehicle_options) 6. **vehicles** - Primary vehicle entity, no dependencies 7. **fuel-logs** - Depends on vehicles (vehicle_id FK) 8. **maintenance** - Depends on vehicles (vehicle_id FK) 9. **stations** - Independent feature (includes community_stations) 10. **documents** - Depends on vehicles (vehicle_id FK) 11. **notifications** - Email templates and logs, depends on maintenance/documents 12. **backup** - Backup schedules, history, settings Features without migrations: auth, onboarding, user-export ### Migration Tracking - **Table**: `_migrations` - **Purpose**: Tracks executed migration files to prevent re-execution - **Behavior**: Migration system is **idempotent at the file level** - will skip already executed files - **Safety**: Safe to re-run the migration system; unsafe to manually re-run individual SQL files ## Admin Tables ### admin_users Admin role-based access control. ```sql admin_users ( auth0_sub VARCHAR(255) PRIMARY KEY, email VARCHAR(255) NOT NULL UNIQUE, role VARCHAR(50) NOT NULL DEFAULT 'admin', created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, created_by VARCHAR(255) NOT NULL, revoked_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ) ``` **Indexes**: email, created_at, revoked_at **Triggers**: auto-update updated_at ### admin_audit_logs Audit trail for admin actions. ```sql admin_audit_logs ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), actor_admin_id VARCHAR(255) NOT NULL, target_admin_id VARCHAR(255), action VARCHAR(100) NOT NULL, resource_type VARCHAR(100), resource_id VARCHAR(255), context JSONB, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ) ``` **Indexes**: actor_admin_id, target_admin_id, action, created_at ## User Tables ### user_profiles User profile information with Auth0 integration. ```sql user_profiles ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), auth0_sub VARCHAR(255) NOT NULL UNIQUE, email VARCHAR(255) NOT NULL, display_name VARCHAR(100), notification_email VARCHAR(255), email_verified BOOLEAN DEFAULT false, onboarding_completed_at TIMESTAMP WITH TIME ZONE, subscription_tier VARCHAR(50) DEFAULT 'free', deactivated_at TIMESTAMP WITH TIME ZONE, deletion_requested_at TIMESTAMP WITH TIME ZONE, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ) ``` **Indexes**: auth0_sub **Triggers**: auto-update updated_at ### user_preferences User preference settings. ```sql user_preferences ( id UUID PRIMARY KEY, user_id VARCHAR(255) NOT NULL UNIQUE, unit_system VARCHAR(20) DEFAULT 'imperial', currency_code VARCHAR(3) DEFAULT 'USD', time_zone VARCHAR(100) DEFAULT 'America/New_York', created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ) ``` **Indexes**: user_id ### terms_agreements Legal audit trail for T&C acceptance. ```sql terms_agreements ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id VARCHAR(255) NOT NULL, agreed_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, ip_address VARCHAR(45) NOT NULL, user_agent TEXT NOT NULL, terms_version VARCHAR(50) NOT NULL, terms_url VARCHAR(255) NOT NULL, terms_content_hash VARCHAR(64) NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ) ``` **Indexes**: user_id **Invariant**: One record per user, created at signup ## Vehicle Tables ### vehicles Primary entity for all vehicle data. ```sql vehicles ( id UUID PRIMARY KEY, user_id VARCHAR(255) NOT NULL, vin VARCHAR(17), make VARCHAR(100), model VARCHAR(100), year INTEGER, trim VARCHAR(100), engine VARCHAR(100), transmission VARCHAR(100), nickname VARCHAR(100), color VARCHAR(50), license_plate VARCHAR(20), odometer_reading INTEGER DEFAULT 0, image_bucket VARCHAR(128), image_key VARCHAR(512), is_active BOOLEAN DEFAULT true, deleted_at TIMESTAMP WITH TIME ZONE, created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE ) ``` **Indexes**: user_id, vin, is_active, created_at **Triggers**: auto-update updated_at **Constraints**: VIN nullable (license plate can be used instead) ### fuel_logs Tracks fuel purchases and efficiency metrics. ```sql fuel_logs ( id UUID PRIMARY KEY, user_id VARCHAR(255) NOT NULL, vehicle_id UUID NOT NULL REFERENCES vehicles(id) ON DELETE CASCADE, date_time TIMESTAMP WITH TIME ZONE NOT NULL, odometer NUMERIC, trip_distance NUMERIC, fuel_type VARCHAR(50), fuel_grade VARCHAR(50), fuel_units DECIMAL(10,3) NOT NULL, cost_per_unit DECIMAL(10,3) NOT NULL, total_cost DECIMAL(10,2), location_data VARCHAR(500), notes TEXT, created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE ) ``` **Foreign Keys**: vehicle_id -> vehicles.id (ON DELETE CASCADE) **Indexes**: user_id, vehicle_id, date_time, created_at ### maintenance_logs Completed maintenance records. ```sql maintenance_logs ( id UUID PRIMARY KEY, user_id VARCHAR(255) NOT NULL, vehicle_id UUID NOT NULL REFERENCES vehicles(id) ON DELETE CASCADE, category VARCHAR(50) NOT NULL, subtypes TEXT[], service_date DATE NOT NULL, odometer INTEGER, cost DECIMAL(10,2), shop_name VARCHAR(200), notes TEXT, created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE ) ``` **Foreign Keys**: vehicle_id -> vehicles.id (ON DELETE CASCADE) **Indexes**: user_id, vehicle_id, service_date ### maintenance_schedules Recurring maintenance schedules with notification settings. ```sql maintenance_schedules ( id UUID PRIMARY KEY, user_id VARCHAR(255) NOT NULL, vehicle_id UUID NOT NULL REFERENCES vehicles(id) ON DELETE CASCADE, category VARCHAR(50) NOT NULL, subtypes TEXT[], interval_months INTEGER, interval_miles INTEGER, last_service_date DATE, last_service_mileage INTEGER, next_due_date DATE, next_due_mileage INTEGER, email_notifications BOOLEAN DEFAULT false, notes TEXT, created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE ) ``` **Foreign Keys**: vehicle_id -> vehicles.id (ON DELETE CASCADE) **Indexes**: user_id, vehicle_id, next_due_date ### documents Vehicle document storage (insurance, registration, etc.). ```sql documents ( id UUID PRIMARY KEY, user_id VARCHAR(255) NOT NULL, vehicle_id UUID NOT NULL REFERENCES vehicles(id) ON DELETE CASCADE, document_type VARCHAR(32) NOT NULL CHECK (document_type IN ('insurance','registration','manual')), title VARCHAR(200) NOT NULL, notes TEXT, details JSONB, storage_bucket VARCHAR(128), storage_key VARCHAR(512), file_name VARCHAR(255), content_type VARCHAR(128), file_size BIGINT, file_hash VARCHAR(128), issued_date DATE, expiration_date DATE, email_notifications BOOLEAN DEFAULT false, created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW(), deleted_at TIMESTAMP WITHOUT TIME ZONE ) ``` **Foreign Keys**: vehicle_id -> vehicles.id (ON DELETE CASCADE) **Indexes**: user_id, vehicle_id, document_type, expiration_date ## Station Tables ### stations Gas station locations from Google Maps. ```sql stations ( id UUID PRIMARY KEY, google_place_id VARCHAR(255) UNIQUE, name VARCHAR(200) NOT NULL, address TEXT, latitude DECIMAL(10,8), longitude DECIMAL(11,8), phone VARCHAR(20), website VARCHAR(255), has_93_octane BOOLEAN, photo_reference VARCHAR(500), created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE ) ``` **External Source**: Google Maps Places API **Cache Strategy**: Postgres-based cache with TTL management ### community_stations User-reported station information. ```sql community_stations ( id UUID PRIMARY KEY, station_id UUID REFERENCES stations(id), user_id VARCHAR(255) NOT NULL, has_93_octane BOOLEAN, reported_at TIMESTAMP WITH TIME ZONE, removed_at TIMESTAMP WITH TIME ZONE, removal_reason VARCHAR(100) ) ``` **Foreign Keys**: station_id -> stations.id **Purpose**: Track user reports of 93 octane availability ## Platform Tables ### engines Engine specifications for vehicle catalog. ```sql engines ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, displacement VARCHAR(50), configuration VARCHAR(50), horsepower VARCHAR(100), torque VARCHAR(100), fuel_type VARCHAR(100), fuel_system VARCHAR(255), aspiration VARCHAR(100), specs_json JSONB, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ``` **Unique Index**: LOWER(name) - case-insensitive uniqueness **Indexes**: displacement, configuration ### transmissions Transmission specifications for vehicle catalog. ```sql transmissions ( id SERIAL PRIMARY KEY, type VARCHAR(100) NOT NULL, speeds VARCHAR(50), drive_type VARCHAR(100), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ``` **Unique Index**: LOWER(type) - case-insensitive uniqueness ### vehicle_options Denormalized vehicle lookup data for dropdown cascades. ```sql vehicle_options ( id SERIAL PRIMARY KEY, year INTEGER NOT NULL, make VARCHAR(100) NOT NULL, model VARCHAR(255) NOT NULL, trim VARCHAR(255) NOT NULL, engine_id INTEGER REFERENCES engines(id) ON DELETE SET NULL, transmission_id INTEGER REFERENCES transmissions(id) ON DELETE SET NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ``` **Foreign Keys**: engine_id -> engines.id, transmission_id -> transmissions.id **Unique Index**: (year, make, model, trim, engine_id, transmission_id) **Indexes**: Optimized for cascade queries (year, year+make, year+make+model, etc.) ## Notification Tables ### email_templates Admin-editable email templates. ```sql email_templates ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), template_key VARCHAR(50) NOT NULL UNIQUE CHECK (template_key IN ( 'maintenance_due_soon', 'maintenance_overdue', 'document_expiring', 'document_expired' )), name VARCHAR(100) NOT NULL, description TEXT, subject VARCHAR(255) NOT NULL, body TEXT NOT NULL, variables JSONB DEFAULT '[]'::jsonb, is_active BOOLEAN DEFAULT true, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ) ``` **Seeded Templates**: 4 predefined templates for maintenance and document notifications ### notification_logs Track sent email notifications. ```sql notification_logs ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id VARCHAR(255) NOT NULL, notification_type VARCHAR(20) NOT NULL CHECK (notification_type IN ('email', 'toast')), template_key VARCHAR(50) NOT NULL, recipient_email VARCHAR(255), subject VARCHAR(255), reference_type VARCHAR(50), reference_id UUID, status VARCHAR(20) DEFAULT 'sent' CHECK (status IN ('pending', 'sent', 'failed')), error_message TEXT, sent_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ) ``` **Indexes**: user_id, reference_type+reference_id, sent_at ## Backup Tables ### backup_schedules Scheduled backup configurations. ```sql backup_schedules ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name VARCHAR(100) NOT NULL, frequency VARCHAR(20) NOT NULL CHECK (frequency IN ('hourly', 'daily', 'weekly', 'monthly')), cron_expression VARCHAR(50) NOT NULL, retention_count INTEGER NOT NULL DEFAULT 7, is_enabled BOOLEAN DEFAULT true, last_run_at TIMESTAMP WITH TIME ZONE, next_run_at TIMESTAMP WITH TIME ZONE, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ) ``` **Indexes**: is_enabled, next_run_at ### backup_history Record of all backup operations. ```sql backup_history ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), schedule_id UUID REFERENCES backup_schedules(id) ON DELETE SET NULL, backup_type VARCHAR(20) NOT NULL CHECK (backup_type IN ('scheduled', 'manual')), filename VARCHAR(255) NOT NULL, file_path VARCHAR(500) NOT NULL, file_size_bytes BIGINT NOT NULL, database_tables_count INTEGER, documents_count INTEGER, status VARCHAR(20) NOT NULL CHECK (status IN ('in_progress', 'completed', 'failed')), error_message TEXT, started_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), completed_at TIMESTAMP WITH TIME ZONE, created_by VARCHAR(255), metadata JSONB DEFAULT '{}' ) ``` **Foreign Keys**: schedule_id -> backup_schedules.id (ON DELETE SET NULL) **Indexes**: status, started_at, schedule_id ### backup_settings Global backup configuration. ```sql backup_settings ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), setting_key VARCHAR(50) UNIQUE NOT NULL, setting_value TEXT NOT NULL, updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ) ``` **Default Settings**: email_on_success, email_on_failure, admin_email, max_backup_size_mb, compression_enabled ## Relationships ``` user_profiles (1) ---- (1) user_preferences | +---- (1) terms_agreements vehicles (1) ---- (many) fuel_logs | +---- (many) maintenance_logs | +---- (many) maintenance_schedules | +---- (many) documents stations (1) ---- (many) community_stations vehicle_options (many) ---- (1) engines | +---- (1) transmissions backup_schedules (1) ---- (many) backup_history ``` ## Data Constraints ### User Data Isolation - All user data tables include `user_id` column - Application enforces user-scoped queries - No cross-user data access possible ### Referential Integrity - fuel_logs, maintenance_logs, maintenance_schedules, documents -> vehicles.id (ON DELETE CASCADE) - community_stations -> stations.id - vehicle_options -> engines.id, transmissions.id (ON DELETE SET NULL) - backup_history -> backup_schedules.id (ON DELETE SET NULL) ### VIN Validation - 17 characters when provided (now optional) - Cannot contain letters I, O, or Q - Application-level checksum validation - Either VIN or license_plate required ## Caching Strategy ### Application-Level Caching (Redis) - **Platform dropdown data**: 6 hours (key: `dropdown:{dataType}:{params}`) - **VIN decodes**: 7 days (key: `vin:decode:{vin}`) - **User vehicle lists**: 5 minutes (key: `vehicles:user:{userId}`) - **Fuel logs per vehicle**: 5 minutes (key: `fuel-logs:vehicle:{vehicleId}:{unitSystem}`) - **Maintenance data**: Unit system-aware caching where applicable ## Migration Commands ### Run All Migrations ```bash # In container npm run migrate:all # Via Docker make migrate ``` ### Migration Files - **Location**: `backend/src/features/[feature]/migrations/` - **Format**: `001_descriptive_name.sql` - **Order**: Lexicographic sorting (001, 002, etc.) ## Database Connection ### Development (Docker) - **Host**: mvp-postgres (container name) - **Port**: 5432 - **Database**: motovaultpro - **User**: postgres - **Password**: Loaded from secrets file `/run/secrets/postgres-password` ### Connection Pool - **Implementation**: pg (node-postgres) - **Pool Size**: Default (10 connections) - **Idle Timeout**: 30 seconds - **Location**: `backend/src/core/config/database.ts`