Files
blackcanyontickets/supabase/migrations/004_add_admin_system.sql
2025-07-08 12:31:31 -06:00

199 lines
7.4 KiB
PL/PgSQL

-- Add admin system with role-based access control
-- Add user roles
ALTER TABLE users
ADD COLUMN role VARCHAR(20) DEFAULT 'organizer',
ADD COLUMN is_active BOOLEAN DEFAULT true,
ADD COLUMN last_login TIMESTAMP WITH TIME ZONE,
ADD COLUMN created_by UUID REFERENCES users(id); -- Track who created this user
-- Update existing users to have organizer role
UPDATE users SET role = 'organizer' WHERE role IS NULL;
-- Create admin_settings table for platform configuration
CREATE TABLE admin_settings (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
setting_key TEXT UNIQUE NOT NULL,
setting_value JSONB NOT NULL,
description TEXT,
updated_by UUID REFERENCES users(id),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Create audit_logs table for tracking admin actions
CREATE TABLE audit_logs (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID REFERENCES users(id),
action TEXT NOT NULL, -- 'create', 'update', 'delete', 'view'
resource_type TEXT NOT NULL, -- 'user', 'organization', 'event', 'ticket'
resource_id UUID,
old_values JSONB,
new_values JSONB,
ip_address INET,
user_agent TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Create platform_stats view for admin dashboard
CREATE VIEW platform_stats AS
SELECT
(SELECT COUNT(*) FROM users WHERE role = 'organizer' AND is_active = true) as active_organizers,
(SELECT COUNT(*) FROM users WHERE role = 'admin') as admin_users,
(SELECT COUNT(*) FROM organizations) as total_organizations,
(SELECT COUNT(*) FROM events) as total_events,
(SELECT COUNT(*) FROM events WHERE start_time >= NOW()) as upcoming_events,
(SELECT COUNT(*) FROM tickets) as total_tickets_sold,
(SELECT COALESCE(SUM(price), 0) FROM tickets) as total_revenue,
(SELECT COALESCE(SUM(platform_fee_charged), 0) FROM tickets) as total_platform_fees,
(SELECT COUNT(DISTINCT DATE(created_at)) FROM tickets WHERE created_at >= NOW() - INTERVAL '30 days') as active_days_last_30,
(SELECT COUNT(*) FROM users WHERE created_at >= NOW() - INTERVAL '7 days') as new_users_last_7_days;
-- Update RLS policies for admin access
-- Users table - admins can view all users
CREATE POLICY "Admins can view all users" ON users
FOR SELECT USING (
auth.uid() IN (SELECT id FROM users WHERE role = 'admin') OR
id = auth.uid()
);
CREATE POLICY "Admins can update any user" ON users
FOR UPDATE USING (
auth.uid() IN (SELECT id FROM users WHERE role = 'admin') OR
id = auth.uid()
);
CREATE POLICY "Admins can create users" ON users
FOR INSERT WITH CHECK (
auth.uid() IN (SELECT id FROM users WHERE role = 'admin')
);
-- Organizations table - admins can view all organizations
CREATE POLICY "Admins can view all organizations" ON organizations
FOR SELECT USING (
auth.uid() IN (SELECT id FROM users WHERE role = 'admin') OR
id IN (SELECT organization_id FROM users WHERE id = auth.uid())
);
CREATE POLICY "Admins can update any organization" ON organizations
FOR UPDATE USING (
auth.uid() IN (SELECT id FROM users WHERE role = 'admin') OR
id IN (SELECT organization_id FROM users WHERE id = auth.uid())
);
-- Events table - admins can view all events
CREATE POLICY "Admins can view all events" ON events
FOR SELECT USING (
auth.uid() IN (SELECT id FROM users WHERE role = 'admin') OR
organization_id IN (SELECT organization_id FROM users WHERE id = auth.uid())
);
-- Tickets table - admins can view all tickets
CREATE POLICY "Admins can view all tickets" ON tickets
FOR SELECT USING (
auth.uid() IN (SELECT id FROM users WHERE role = 'admin') OR
event_id IN (
SELECT id FROM events WHERE organization_id IN (
SELECT organization_id FROM users WHERE id = auth.uid()
)
)
);
-- Enable RLS on new tables
ALTER TABLE audit_logs ENABLE ROW LEVEL SECURITY;
ALTER TABLE admin_settings ENABLE ROW LEVEL SECURITY;
-- RLS Policies for audit_logs
CREATE POLICY "Admins can view all audit logs" ON audit_logs
FOR SELECT USING (auth.uid() IN (SELECT id FROM users WHERE role = 'admin'));
CREATE POLICY "System can create audit logs" ON audit_logs
FOR INSERT WITH CHECK (true);
-- RLS Policies for admin_settings
CREATE POLICY "Admins can manage settings" ON admin_settings
FOR ALL USING (auth.uid() IN (SELECT id FROM users WHERE role = 'admin'));
-- Insert default admin settings
INSERT INTO admin_settings (setting_key, setting_value, description) VALUES
('platform_name', '"Black Canyon Tickets"', 'Platform display name'),
('platform_email', '"support@blackcanyontickets.com"', 'Platform support email'),
('default_platform_fee_percentage', '0.03', 'Default platform fee percentage'),
('default_platform_fee_fixed', '30', 'Default platform fee fixed amount in cents'),
('max_events_per_organization', '100', 'Maximum events per organization'),
('email_notifications_enabled', 'true', 'Enable email notifications'),
('maintenance_mode', 'false', 'Platform maintenance mode');
-- Create function to log admin actions
CREATE OR REPLACE FUNCTION log_admin_action(
p_action TEXT,
p_resource_type TEXT,
p_resource_id UUID DEFAULT NULL,
p_old_values JSONB DEFAULT NULL,
p_new_values JSONB DEFAULT NULL
) RETURNS VOID AS $$
BEGIN
INSERT INTO audit_logs (user_id, action, resource_type, resource_id, old_values, new_values)
VALUES (auth.uid(), p_action, p_resource_type, p_resource_id, p_old_values, p_new_values);
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- Create function to check if user is admin
CREATE OR REPLACE FUNCTION is_admin(user_uuid UUID DEFAULT auth.uid())
RETURNS BOOLEAN AS $$
BEGIN
RETURN EXISTS (
SELECT 1 FROM users
WHERE id = user_uuid AND role = 'admin' AND is_active = true
);
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- Create indexes for performance
CREATE INDEX idx_users_role ON users(role);
CREATE INDEX idx_users_is_active ON users(is_active);
CREATE INDEX idx_users_last_login ON users(last_login);
CREATE INDEX idx_audit_logs_user_id ON audit_logs(user_id);
CREATE INDEX idx_audit_logs_resource_type ON audit_logs(resource_type);
CREATE INDEX idx_audit_logs_created_at ON audit_logs(created_at);
CREATE INDEX idx_admin_settings_key ON admin_settings(setting_key);
-- Update the auth signup function to handle admin creation
CREATE OR REPLACE FUNCTION handle_auth_signup()
RETURNS TRIGGER AS $$
DECLARE
user_role TEXT := 'organizer';
BEGIN
-- Check if this is the first user (make them admin)
IF NOT EXISTS (SELECT 1 FROM users LIMIT 1) THEN
user_role := 'admin';
END IF;
-- Create user record in users table with error handling
INSERT INTO users (id, email, name, role)
VALUES (
NEW.id,
NEW.email,
COALESCE(NEW.raw_user_meta_data->>'name', NEW.email),
user_role
)
ON CONFLICT (id) DO UPDATE SET
email = EXCLUDED.email,
name = COALESCE(EXCLUDED.name, users.name),
last_login = NOW();
RETURN NEW;
EXCEPTION
WHEN OTHERS THEN
-- Log the error but don't fail the auth process
RAISE WARNING 'Failed to create user record for %: %', NEW.email, SQLERRM;
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- Add comments for clarity
COMMENT ON COLUMN users.role IS 'User role: admin, organizer, staff';
COMMENT ON COLUMN users.is_active IS 'Whether the user account is active';
COMMENT ON COLUMN users.created_by IS 'Admin user who created this account';
COMMENT ON TABLE audit_logs IS 'Audit trail for admin actions';
COMMENT ON TABLE admin_settings IS 'Platform-wide configuration settings';
COMMENT ON VIEW platform_stats IS 'Aggregated platform statistics for admin dashboard';