-- Simple Migration - Only creates tables if they don't exist -- Run this in Supabase SQL Editor -- Core user tables CREATE TABLE IF NOT EXISTS user_profiles ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), username VARCHAR(255), xp INTEGER DEFAULT 0, prestige_level INTEGER DEFAULT 0, total_xp_earned INTEGER DEFAULT 0, daily_streak INTEGER DEFAULT 0, last_daily TIMESTAMP WITH TIME ZONE, title VARCHAR(255), bio TEXT, avatar_url VARCHAR(512), created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS discord_links ( id SERIAL PRIMARY KEY, discord_id VARCHAR(32) UNIQUE NOT NULL, user_id UUID, username VARCHAR(255), linked_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS discord_verifications ( id SERIAL PRIMARY KEY, discord_id VARCHAR(32) UNIQUE NOT NULL, verification_code VARCHAR(32) NOT NULL, expires_at TIMESTAMP WITH TIME ZONE NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS server_config ( id SERIAL PRIMARY KEY, guild_id VARCHAR(32) UNIQUE NOT NULL, prefix VARCHAR(10) DEFAULT '!', verified_role_id VARCHAR(32), welcome_channel_id VARCHAR(32), welcome_message TEXT, goodbye_channel_id VARCHAR(32), goodbye_message TEXT, log_channel_id VARCHAR(32), mod_log_channel_id VARCHAR(32), starboard_channel_id VARCHAR(32), starboard_threshold INTEGER DEFAULT 3, level_up_channel_id VARCHAR(32), level_up_message TEXT, xp_mode VARCHAR(32) DEFAULT 'standalone', created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS xp_config ( id SERIAL PRIMARY KEY, guild_id VARCHAR(32) UNIQUE NOT NULL, xp_per_message INTEGER DEFAULT 15, xp_cooldown INTEGER DEFAULT 60, voice_xp_per_minute INTEGER DEFAULT 5, reaction_xp INTEGER DEFAULT 5, difficulty VARCHAR(32) DEFAULT 'normal', bonus_channels JSONB DEFAULT '[]', role_multipliers JSONB DEFAULT '{}', created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS guild_user_xp ( id SERIAL PRIMARY KEY, guild_id VARCHAR(32) NOT NULL, user_id VARCHAR(32) NOT NULL, xp INTEGER DEFAULT 0, level INTEGER DEFAULT 0, prestige INTEGER DEFAULT 0, messages INTEGER DEFAULT 0, voice_minutes INTEGER DEFAULT 0, last_xp_at TIMESTAMP WITH TIME ZONE, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), UNIQUE(guild_id, user_id) ); CREATE TABLE IF NOT EXISTS user_stats ( id SERIAL PRIMARY KEY, user_id UUID, guild_id VARCHAR(32) NOT NULL, messages_sent INTEGER DEFAULT 0, reactions_given INTEGER DEFAULT 0, reactions_received INTEGER DEFAULT 0, voice_minutes INTEGER DEFAULT 0, commands_used INTEGER DEFAULT 0, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS level_roles ( id SERIAL PRIMARY KEY, guild_id VARCHAR(32) NOT NULL, level INTEGER NOT NULL, role_id VARCHAR(32) NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), UNIQUE(guild_id, level) ); CREATE TABLE IF NOT EXISTS achievements ( id SERIAL PRIMARY KEY, guild_id VARCHAR(32) NOT NULL, name VARCHAR(255) NOT NULL, description TEXT, icon VARCHAR(64), trigger_type VARCHAR(32) NOT NULL, trigger_value INTEGER NOT NULL, xp_reward INTEGER DEFAULT 0, role_reward_id VARCHAR(32), hidden BOOLEAN DEFAULT false, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS quests ( id SERIAL PRIMARY KEY, guild_id VARCHAR(32) NOT NULL, name VARCHAR(255) NOT NULL, description TEXT, quest_type VARCHAR(32) DEFAULT 'daily', trigger_type VARCHAR(32) NOT NULL, target_value INTEGER NOT NULL, xp_reward INTEGER DEFAULT 100, active BOOLEAN DEFAULT true, starts_at TIMESTAMP WITH TIME ZONE, expires_at TIMESTAMP WITH TIME ZONE, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS shop_items ( id SERIAL PRIMARY KEY, guild_id VARCHAR(32) NOT NULL, name VARCHAR(255) NOT NULL, description TEXT, price INTEGER NOT NULL, item_type VARCHAR(32) NOT NULL, item_data JSONB DEFAULT '{}', stock INTEGER, enabled BOOLEAN DEFAULT true, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS warnings ( id SERIAL PRIMARY KEY, guild_id VARCHAR(32) NOT NULL, user_id VARCHAR(32) NOT NULL, moderator_id VARCHAR(32) NOT NULL, reason TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS mod_actions ( id SERIAL PRIMARY KEY, guild_id VARCHAR(32) NOT NULL, user_id VARCHAR(32) NOT NULL, moderator_id VARCHAR(32) NOT NULL, action VARCHAR(32) NOT NULL, reason TEXT, duration INTEGER, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS command_cooldowns ( id SERIAL PRIMARY KEY, guild_id VARCHAR(32) NOT NULL, user_id VARCHAR(32) NOT NULL, command VARCHAR(64) NOT NULL, expires_at TIMESTAMP WITH TIME ZONE NOT NULL, UNIQUE(guild_id, user_id, command) ); CREATE TABLE IF NOT EXISTS command_logs ( id SERIAL PRIMARY KEY, guild_id VARCHAR(32) NOT NULL, user_id VARCHAR(32) NOT NULL, command VARCHAR(64) NOT NULL, options JSONB, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- Enable RLS with permissive policies DO $$ DECLARE t text; tables text[] := ARRAY[ 'user_profiles', 'discord_links', 'discord_verifications', 'server_config', 'xp_config', 'guild_user_xp', 'user_stats', 'level_roles', 'achievements', 'quests', 'shop_items', 'warnings', 'mod_actions', 'command_cooldowns', 'command_logs' ]; BEGIN FOREACH t IN ARRAY tables LOOP BEGIN EXECUTE format('ALTER TABLE %I ENABLE ROW LEVEL SECURITY', t); EXECUTE format('DROP POLICY IF EXISTS "Allow all" ON %I', t); EXECUTE format('CREATE POLICY "Allow all" ON %I FOR ALL USING (true) WITH CHECK (true)', t); EXCEPTION WHEN OTHERS THEN NULL; END; END LOOP; END $$; GRANT ALL ON ALL TABLES IN SCHEMA public TO service_role; GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO service_role;