Initial commit - Black Canyon Tickets whitelabel platform
🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
199
supabase/migrations/004_add_admin_system.sql
Normal file
199
supabase/migrations/004_add_admin_system.sql
Normal file
@@ -0,0 +1,199 @@
|
||||
-- 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';
|
||||
Reference in New Issue
Block a user