Files
membership-be/migrations/000_initial_schema.sql

694 lines
22 KiB
PL/PgSQL

-- ============================================================================
-- Migration 000: Initial Database Schema
-- ============================================================================
-- Description: Creates all base tables, enums, and indexes for the LOAF
-- membership platform. This migration should be run first on
-- a fresh database.
-- Date: 2024-12-18
-- Author: LOAF Development Team
-- ============================================================================
BEGIN;
-- ============================================================================
-- SECTION 1: Create ENUM Types
-- ============================================================================
-- User status enum
CREATE TYPE userstatus AS ENUM (
'pending_email',
'pending_validation',
'pre_validated',
'payment_pending',
'active',
'inactive',
'canceled',
'expired',
'abandoned',
'rejected'
);
-- User role enum
CREATE TYPE userrole AS ENUM (
'guest',
'member',
'admin',
'finance',
'superadmin'
);
-- RSVP status enum
CREATE TYPE rsvpstatus AS ENUM (
'yes',
'no',
'maybe'
);
-- Subscription status enum
CREATE TYPE subscriptionstatus AS ENUM (
'active',
'cancelled',
'expired'
);
-- Donation type enum
CREATE TYPE donationtype AS ENUM (
'member',
'public'
);
-- Donation status enum
CREATE TYPE donationstatus AS ENUM (
'pending',
'completed',
'failed'
);
-- Invitation status enum
CREATE TYPE invitationstatus AS ENUM (
'pending',
'accepted',
'expired',
'revoked'
);
-- Import job status enum
CREATE TYPE importjobstatus AS ENUM (
'processing',
'completed',
'failed',
'partial',
'validating',
'preview_ready',
'rolled_back'
);
COMMIT;
-- Display progress
SELECT 'Step 1/8 completed: ENUM types created' AS progress;
BEGIN;
-- ============================================================================
-- SECTION 2: Create Core Tables
-- ============================================================================
-- Import Jobs table (must be created before users due to FK reference)
CREATE TABLE IF NOT EXISTS import_jobs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
filename VARCHAR NOT NULL,
status importjobstatus NOT NULL DEFAULT 'processing',
total_rows INTEGER DEFAULT 0,
processed_rows INTEGER DEFAULT 0,
success_count INTEGER DEFAULT 0,
error_count INTEGER DEFAULT 0,
error_log JSONB DEFAULT '[]'::jsonb,
-- WordPress import enhancements
field_mapping JSONB DEFAULT '{}'::jsonb,
wordpress_metadata JSONB DEFAULT '{}'::jsonb,
imported_user_ids JSONB DEFAULT '[]'::jsonb,
rollback_at TIMESTAMP WITH TIME ZONE,
rollback_by UUID, -- Will be updated with FK after users table exists
started_by UUID, -- Will be updated with FK after users table exists
started_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
completed_at TIMESTAMP WITH TIME ZONE
);
-- Users table
CREATE TABLE IF NOT EXISTS users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Authentication
email VARCHAR NOT NULL UNIQUE,
password_hash VARCHAR NOT NULL,
email_verified BOOLEAN NOT NULL DEFAULT FALSE,
email_verification_token VARCHAR UNIQUE,
email_verification_expires TIMESTAMP WITH TIME ZONE,
-- Personal Information
first_name VARCHAR NOT NULL,
last_name VARCHAR NOT NULL,
phone VARCHAR,
address VARCHAR,
city VARCHAR,
state VARCHAR(2),
zipcode VARCHAR(10),
date_of_birth DATE,
-- Profile
profile_photo_url VARCHAR,
-- Social Media
social_media_facebook VARCHAR,
social_media_instagram VARCHAR,
social_media_twitter VARCHAR,
social_media_linkedin VARCHAR,
-- Partner Information
partner_first_name VARCHAR,
partner_last_name VARCHAR,
partner_is_member BOOLEAN DEFAULT FALSE,
partner_plan_to_become_member BOOLEAN DEFAULT FALSE,
-- Referral
referred_by_member_name VARCHAR,
lead_sources JSONB DEFAULT '[]'::jsonb,
-- Status & Role
status userstatus NOT NULL DEFAULT 'pending_email',
role userrole NOT NULL DEFAULT 'guest',
role_id UUID, -- For dynamic RBAC
-- Newsletter Preferences
newsletter_subscribed BOOLEAN DEFAULT TRUE,
newsletter_publish_name BOOLEAN DEFAULT FALSE NOT NULL,
newsletter_publish_photo BOOLEAN DEFAULT FALSE NOT NULL,
newsletter_publish_birthday BOOLEAN DEFAULT FALSE NOT NULL,
newsletter_publish_none BOOLEAN DEFAULT FALSE NOT NULL,
-- Volunteer Interests
volunteer_interests JSONB DEFAULT '[]'::jsonb,
-- Scholarship Request
scholarship_requested BOOLEAN DEFAULT FALSE NOT NULL,
scholarship_reason TEXT,
-- Directory Settings
show_in_directory BOOLEAN DEFAULT FALSE NOT NULL,
directory_email VARCHAR,
directory_bio TEXT,
directory_address VARCHAR,
directory_phone VARCHAR,
directory_dob DATE,
directory_partner_name VARCHAR,
-- Password Reset
password_reset_token VARCHAR,
password_reset_expires TIMESTAMP WITH TIME ZONE,
force_password_change BOOLEAN DEFAULT FALSE NOT NULL,
-- Terms of Service
accepts_tos BOOLEAN DEFAULT FALSE NOT NULL,
tos_accepted_at TIMESTAMP WITH TIME ZONE,
-- Membership
member_since DATE,
-- Reminder Tracking
email_verification_reminders_sent INTEGER DEFAULT 0 NOT NULL,
last_email_verification_reminder_at TIMESTAMP WITH TIME ZONE,
event_attendance_reminders_sent INTEGER DEFAULT 0 NOT NULL,
last_event_attendance_reminder_at TIMESTAMP WITH TIME ZONE,
payment_reminders_sent INTEGER DEFAULT 0 NOT NULL,
last_payment_reminder_at TIMESTAMP WITH TIME ZONE,
renewal_reminders_sent INTEGER DEFAULT 0 NOT NULL,
last_renewal_reminder_at TIMESTAMP WITH TIME ZONE,
-- Rejection Tracking
rejection_reason TEXT,
rejected_at TIMESTAMP WITH TIME ZONE,
rejected_by UUID REFERENCES users(id),
-- WordPress Import Tracking
import_source VARCHAR(50),
import_job_id UUID REFERENCES import_jobs(id),
wordpress_user_id BIGINT,
wordpress_registered_date TIMESTAMP WITH TIME ZONE,
-- Role Change Audit Trail
role_changed_at TIMESTAMP WITH TIME ZONE,
role_changed_by UUID REFERENCES users(id) ON DELETE SET NULL,
-- Timestamps
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- Events table
CREATE TABLE IF NOT EXISTS events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Event Details
title VARCHAR NOT NULL,
description TEXT,
location VARCHAR,
cover_image_url VARCHAR,
-- Schedule
start_at TIMESTAMP WITH TIME ZONE NOT NULL,
end_at TIMESTAMP WITH TIME ZONE,
-- Capacity
capacity INTEGER,
published BOOLEAN NOT NULL DEFAULT FALSE,
-- Calendar Integration
calendar_uid VARCHAR UNIQUE,
microsoft_calendar_id VARCHAR,
microsoft_calendar_sync_enabled BOOLEAN DEFAULT FALSE,
-- Metadata
created_by UUID REFERENCES users(id),
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- Event RSVPs table
CREATE TABLE IF NOT EXISTS event_rsvps (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
event_id UUID NOT NULL REFERENCES events(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
-- RSVP Details
rsvp_status rsvpstatus NOT NULL DEFAULT 'maybe',
attended BOOLEAN DEFAULT FALSE,
attended_at TIMESTAMP WITH TIME ZONE,
-- Timestamps
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
-- Unique constraint: one RSVP per user per event
UNIQUE(event_id, user_id)
);
-- Event Gallery table
CREATE TABLE IF NOT EXISTS event_galleries (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
event_id UUID NOT NULL REFERENCES events(id) ON DELETE CASCADE,
-- Image Details
image_url VARCHAR NOT NULL,
caption TEXT,
order_index INTEGER DEFAULT 0,
-- Metadata
uploaded_by UUID REFERENCES users(id),
uploaded_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
COMMIT;
-- Display progress
SELECT 'Step 2/8 completed: Core tables (users, events, rsvps, gallery) created' AS progress;
BEGIN;
-- ============================================================================
-- SECTION 3: Create Subscription & Payment Tables
-- ============================================================================
-- Subscription Plans table
CREATE TABLE IF NOT EXISTS subscription_plans (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Plan Details
name VARCHAR NOT NULL,
description TEXT,
price_cents INTEGER NOT NULL,
billing_cycle VARCHAR NOT NULL DEFAULT 'yearly',
stripe_price_id VARCHAR, -- Legacy, deprecated
-- Configuration
active BOOLEAN NOT NULL DEFAULT TRUE,
-- Custom billing cycle fields (for recurring date ranges like Jan 1 - Dec 31)
custom_cycle_enabled BOOLEAN DEFAULT FALSE NOT NULL,
custom_cycle_start_month INTEGER,
custom_cycle_start_day INTEGER,
custom_cycle_end_month INTEGER,
custom_cycle_end_day INTEGER,
-- Dynamic pricing fields
minimum_price_cents INTEGER DEFAULT 3000 NOT NULL,
suggested_price_cents INTEGER,
allow_donation BOOLEAN DEFAULT TRUE NOT NULL,
-- Timestamps
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- Subscriptions table
CREATE TABLE IF NOT EXISTS subscriptions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
plan_id UUID NOT NULL REFERENCES subscription_plans(id),
-- Stripe Integration
stripe_subscription_id VARCHAR,
stripe_customer_id VARCHAR,
-- Status & Dates
status subscriptionstatus DEFAULT 'active',
start_date TIMESTAMP WITH TIME ZONE NOT NULL,
end_date TIMESTAMP WITH TIME ZONE,
-- Payment Details
amount_paid_cents INTEGER,
base_subscription_cents INTEGER NOT NULL,
donation_cents INTEGER DEFAULT 0 NOT NULL,
-- Stripe transaction metadata (for validation and audit)
stripe_payment_intent_id VARCHAR,
stripe_charge_id VARCHAR,
stripe_invoice_id VARCHAR,
payment_completed_at TIMESTAMP WITH TIME ZONE,
card_last4 VARCHAR(4),
card_brand VARCHAR(20),
stripe_receipt_url VARCHAR,
-- Manual Payment Support
manual_payment BOOLEAN DEFAULT FALSE NOT NULL,
manual_payment_notes TEXT,
manual_payment_admin_id UUID REFERENCES users(id),
manual_payment_date TIMESTAMP WITH TIME ZONE,
payment_method VARCHAR,
-- Timestamps
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- Donations table
CREATE TABLE IF NOT EXISTS donations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Donation Details
amount_cents INTEGER NOT NULL,
donation_type donationtype NOT NULL DEFAULT 'public',
status donationstatus NOT NULL DEFAULT 'pending',
-- Donor Information
user_id UUID REFERENCES users(id), -- NULL for public donations
donor_email VARCHAR,
donor_name VARCHAR,
-- Payment Details
stripe_checkout_session_id VARCHAR,
stripe_payment_intent_id VARCHAR,
payment_method VARCHAR,
-- Stripe transaction metadata (for validation and audit)
stripe_charge_id VARCHAR,
stripe_customer_id VARCHAR,
payment_completed_at TIMESTAMP WITH TIME ZONE,
card_last4 VARCHAR(4),
card_brand VARCHAR(20),
stripe_receipt_url VARCHAR,
-- Metadata
notes TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE
);
COMMIT;
-- Display progress
SELECT 'Step 3/8 completed: Subscription and donation tables created' AS progress;
BEGIN;
-- ============================================================================
-- SECTION 4: Create RBAC Tables
-- ============================================================================
-- Permissions table
CREATE TABLE IF NOT EXISTS permissions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
code VARCHAR NOT NULL UNIQUE,
name VARCHAR NOT NULL,
description TEXT,
module VARCHAR NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- Roles table (for dynamic RBAC)
CREATE TABLE IF NOT EXISTS roles (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
code VARCHAR NOT NULL UNIQUE,
name VARCHAR NOT NULL,
description TEXT,
is_system_role BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
created_by UUID REFERENCES users(id) ON DELETE SET NULL
);
-- Role Permissions junction table
CREATE TABLE IF NOT EXISTS role_permissions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
role userrole, -- Legacy enum-based role (for backward compatibility)
role_id UUID REFERENCES roles(id) ON DELETE CASCADE, -- Dynamic role
permission_id UUID NOT NULL REFERENCES permissions(id) ON DELETE CASCADE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
created_by UUID REFERENCES users(id) ON DELETE SET NULL
);
COMMIT;
-- Display progress
SELECT 'Step 4/8 completed: RBAC tables created' AS progress;
BEGIN;
-- ============================================================================
-- SECTION 5: Create Document Management Tables
-- ============================================================================
-- Newsletter Archive table
CREATE TABLE IF NOT EXISTS newsletter_archives (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
title VARCHAR NOT NULL,
file_url VARCHAR NOT NULL,
file_size_bytes INTEGER,
issue_date DATE NOT NULL,
description TEXT,
uploaded_by UUID REFERENCES users(id),
uploaded_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- Financial Reports table
CREATE TABLE IF NOT EXISTS financial_reports (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
title VARCHAR NOT NULL,
file_url VARCHAR NOT NULL,
file_size_bytes INTEGER,
fiscal_period VARCHAR NOT NULL,
report_type VARCHAR,
uploaded_by UUID REFERENCES users(id),
uploaded_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- Bylaws Documents table
CREATE TABLE IF NOT EXISTS bylaws_documents (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
title VARCHAR NOT NULL,
file_url VARCHAR NOT NULL,
file_size_bytes INTEGER,
version VARCHAR NOT NULL,
effective_date DATE NOT NULL,
description TEXT,
is_current BOOLEAN DEFAULT TRUE,
uploaded_by UUID REFERENCES users(id),
uploaded_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
COMMIT;
-- Display progress
SELECT 'Step 5/8 completed: Document management tables created' AS progress;
BEGIN;
-- ============================================================================
-- SECTION 6: Create System Tables
-- ============================================================================
-- Storage Usage table
CREATE TABLE IF NOT EXISTS storage_usage (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
total_bytes_used BIGINT NOT NULL DEFAULT 0,
max_bytes_allowed BIGINT NOT NULL DEFAULT 1073741824, -- 1GB
last_updated TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- User Invitations table
CREATE TABLE IF NOT EXISTS user_invitations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR NOT NULL,
token VARCHAR NOT NULL UNIQUE,
role userrole NOT NULL,
status invitationstatus NOT NULL DEFAULT 'pending',
invited_by UUID REFERENCES users(id) ON DELETE SET NULL,
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
accepted_at TIMESTAMP WITH TIME ZONE,
revoked_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- Add FK constraints to import_jobs (now that users table exists)
ALTER TABLE import_jobs
ADD CONSTRAINT fk_import_jobs_rollback_by FOREIGN KEY (rollback_by) REFERENCES users(id),
ADD CONSTRAINT fk_import_jobs_started_by FOREIGN KEY (started_by) REFERENCES users(id);
-- Import Rollback Audit table (for tracking rollback operations)
CREATE TABLE IF NOT EXISTS import_rollback_audit (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
import_job_id UUID NOT NULL REFERENCES import_jobs(id),
rolled_back_by UUID NOT NULL REFERENCES users(id),
rolled_back_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
deleted_user_count INTEGER NOT NULL,
deleted_user_ids JSONB NOT NULL,
reason TEXT,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);
COMMIT;
-- Display progress
SELECT 'Step 6/8 completed: System tables created' AS progress;
BEGIN;
-- ============================================================================
-- SECTION 7: Create Indexes
-- ============================================================================
-- Users table indexes
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
CREATE INDEX IF NOT EXISTS idx_users_status ON users(status);
CREATE INDEX IF NOT EXISTS idx_users_role ON users(role);
CREATE INDEX IF NOT EXISTS idx_users_role_id ON users(role_id);
CREATE INDEX IF NOT EXISTS idx_users_email_verified ON users(email_verified);
CREATE INDEX IF NOT EXISTS idx_users_rejected_at ON users(rejected_at) WHERE rejected_at IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_users_created_at ON users(created_at);
CREATE INDEX IF NOT EXISTS idx_users_import_job ON users(import_job_id) WHERE import_job_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_users_import_source ON users(import_source) WHERE import_source IS NOT NULL;
-- Events table indexes
CREATE INDEX IF NOT EXISTS idx_events_created_by ON events(created_by);
CREATE INDEX IF NOT EXISTS idx_events_start_at ON events(start_at);
CREATE INDEX IF NOT EXISTS idx_events_published ON events(published);
-- Event RSVPs indexes
CREATE INDEX IF NOT EXISTS idx_event_rsvps_event_id ON event_rsvps(event_id);
CREATE INDEX IF NOT EXISTS idx_event_rsvps_user_id ON event_rsvps(user_id);
CREATE INDEX IF NOT EXISTS idx_event_rsvps_rsvp_status ON event_rsvps(rsvp_status);
-- Event Gallery indexes
CREATE INDEX IF NOT EXISTS idx_event_galleries_event_id ON event_galleries(event_id);
-- Subscriptions indexes
CREATE INDEX IF NOT EXISTS idx_subscriptions_user_id ON subscriptions(user_id);
CREATE INDEX IF NOT EXISTS idx_subscriptions_plan_id ON subscriptions(plan_id);
CREATE INDEX IF NOT EXISTS idx_subscriptions_status ON subscriptions(status);
CREATE INDEX IF NOT EXISTS idx_subscriptions_stripe_subscription_id ON subscriptions(stripe_subscription_id);
CREATE INDEX IF NOT EXISTS idx_subscriptions_payment_intent ON subscriptions(stripe_payment_intent_id);
CREATE INDEX IF NOT EXISTS idx_subscriptions_charge_id ON subscriptions(stripe_charge_id);
CREATE INDEX IF NOT EXISTS idx_subscriptions_invoice_id ON subscriptions(stripe_invoice_id);
-- Donations indexes
CREATE INDEX IF NOT EXISTS idx_donation_user ON donations(user_id);
CREATE INDEX IF NOT EXISTS idx_donation_type ON donations(donation_type);
CREATE INDEX IF NOT EXISTS idx_donation_status ON donations(status);
CREATE INDEX IF NOT EXISTS idx_donation_created ON donations(created_at);
CREATE INDEX IF NOT EXISTS idx_donation_payment_intent ON donations(stripe_payment_intent_id);
CREATE INDEX IF NOT EXISTS idx_donation_charge_id ON donations(stripe_charge_id);
CREATE INDEX IF NOT EXISTS idx_donation_customer_id ON donations(stripe_customer_id);
-- Import Jobs indexes
CREATE INDEX IF NOT EXISTS idx_import_jobs_status ON import_jobs(status);
CREATE INDEX IF NOT EXISTS idx_import_jobs_started_by ON import_jobs(started_by);
-- Import Rollback Audit indexes
CREATE INDEX IF NOT EXISTS idx_rollback_audit_import_job ON import_rollback_audit(import_job_id);
CREATE INDEX IF NOT EXISTS idx_rollback_audit_rolled_back_at ON import_rollback_audit(rolled_back_at DESC);
-- Permissions indexes
CREATE INDEX IF NOT EXISTS idx_permissions_code ON permissions(code);
CREATE INDEX IF NOT EXISTS idx_permissions_module ON permissions(module);
-- Roles indexes
CREATE INDEX IF NOT EXISTS idx_roles_code ON roles(code);
CREATE INDEX IF NOT EXISTS idx_roles_is_system_role ON roles(is_system_role);
-- Role Permissions indexes
CREATE INDEX IF NOT EXISTS idx_role_permissions_role ON role_permissions(role);
CREATE INDEX IF NOT EXISTS idx_role_permissions_role_id ON role_permissions(role_id);
CREATE UNIQUE INDEX IF NOT EXISTS idx_role_permission ON role_permissions(role, permission_id) WHERE role IS NOT NULL;
CREATE UNIQUE INDEX IF NOT EXISTS idx_dynamic_role_permission ON role_permissions(role_id, permission_id) WHERE role_id IS NOT NULL;
COMMIT;
-- Display progress
SELECT 'Step 7/8 completed: Indexes created' AS progress;
BEGIN;
-- ============================================================================
-- SECTION 8: Initialize Default Data
-- ============================================================================
-- Insert initial storage usage record
INSERT INTO storage_usage (id, total_bytes_used, max_bytes_allowed, last_updated)
SELECT
gen_random_uuid(),
0,
1073741824, -- 1GB
CURRENT_TIMESTAMP
WHERE NOT EXISTS (SELECT 1 FROM storage_usage);
COMMIT;
-- Display progress
SELECT 'Step 8/8 completed: Default data initialized' AS progress;
-- ============================================================================
-- Migration Complete
-- ============================================================================
SELECT '
================================================================================
✅ Migration 000 completed successfully!
================================================================================
Database schema initialized with:
- 10 ENUM types
- 17 tables (users, events, subscriptions, donations, RBAC, documents, system)
- 30+ indexes for performance
- 1 storage usage record
Next steps:
1. Run: python seed_permissions_rbac.py (to populate permissions and roles)
2. Run: python create_admin.py (to create superadmin user)
3. Run remaining migrations in sequence (001-010)
Database is ready for LOAF membership platform! 🎉
================================================================================
' AS migration_complete;