-- 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';