"""update profile settings schema in trigger Revision ID: 20260428_0002 Revises: 20260428_0001 Create Date: 2026-04-28 """ from alembic import op revision = "20260428_0002" down_revision = "20260428_0001" branch_labels = None depends_on = None def upgrade() -> None: op.execute( """ CREATE OR REPLACE FUNCTION public.initialize_profile_and_invite_code_on_signup() RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path = public AS $$ DECLARE v_username text; v_invite_code text; v_referrer_id uuid; v_attempts int := 0; invite_code_value text; BEGIN v_username := 'user_' || substring(md5(new.id::text || clock_timestamp()::text || random()::text) from 1 for 6); INSERT INTO public.profiles (id, username, avatar_url, bio, settings) VALUES ( new.id, v_username, null, null, jsonb_build_object( 'version', 1, 'preferences', jsonb_build_object( 'language', 'zh-CN', 'timezone', 'Asia/Shanghai', 'country', 'US' ), 'privacy', jsonb_build_object( 'can_sell', false, 'profile_visibility', 'public' ), 'notification', jsonb_build_object( 'allow_notifications', true, 'allow_vibration', true ), 'divination_tutorial', jsonb_build_object( 'divination_entry_shown', false, 'auto_divination_shown', false, 'manual_divination_shown', false ) ) ) ON CONFLICT (id) DO NOTHING; LOOP BEGIN v_invite_code := public.generate_invite_code(); INSERT INTO public.invite_codes (code, owner_id, status, used_count, max_uses, expires_at, reward_config) VALUES ( v_invite_code, new.id, 'active', 0, NULL, NULL, '{}'::jsonb ); EXIT; EXCEPTION WHEN unique_violation THEN v_attempts := v_attempts + 1; IF v_attempts >= 100 THEN RAISE EXCEPTION 'Failed to generate unique invite code after 100 attempts'; END IF; END; END LOOP; invite_code_value := new.raw_user_meta_data ->> 'invite_code'; IF invite_code_value IS NOT NULL AND length(invite_code_value) = 6 THEN invite_code_value := upper(invite_code_value); IF invite_code_value ~ '^[ABCDEFGHJKMNPQRSTUVWXYZ23456789]{6}$' THEN UPDATE public.invite_codes SET used_count = used_count + 1 WHERE code = invite_code_value AND status = 'active' AND (max_uses IS NULL OR used_count < max_uses) AND (expires_at IS NULL OR expires_at > NOW()) RETURNING owner_id INTO v_referrer_id; IF v_referrer_id IS NOT NULL THEN UPDATE public.profiles SET referred_by = v_referrer_id WHERE id = new.id; END IF; END IF; END IF; RETURN NEW; END; $$; """ ) def downgrade() -> None: op.execute( """ CREATE OR REPLACE FUNCTION public.initialize_profile_and_invite_code_on_signup() RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path = public AS $$ DECLARE v_username text; v_invite_code text; v_referrer_id uuid; v_attempts int := 0; invite_code_value text; BEGIN v_username := 'user_' || substring(md5(new.id::text || clock_timestamp()::text || random()::text) from 1 for 6); INSERT INTO public.profiles (id, username, avatar_url, bio, settings) VALUES ( new.id, v_username, null, null, jsonb_build_object( 'version', 1, 'preferences', jsonb_build_object( 'interface_language', 'zh-CN', 'ai_language', 'zh-CN', 'timezone', 'Asia/Shanghai', 'country', 'CN' ), 'privacy', jsonb_build_object('profile_visibility', 'public'), 'notification', jsonb_build_object( 'allow_notifications', true, 'allow_vibration', true ) ) ) ON CONFLICT (id) DO NOTHING; LOOP BEGIN v_invite_code := public.generate_invite_code(); INSERT INTO public.invite_codes (code, owner_id, status, used_count, max_uses, expires_at, reward_config) VALUES ( v_invite_code, new.id, 'active', 0, NULL, NULL, '{}'::jsonb ); EXIT; EXCEPTION WHEN unique_violation THEN v_attempts := v_attempts + 1; IF v_attempts >= 100 THEN RAISE EXCEPTION 'Failed to generate unique invite code after 100 attempts'; END IF; END; END LOOP; invite_code_value := new.raw_user_meta_data ->> 'invite_code'; IF invite_code_value IS NOT NULL AND length(invite_code_value) = 6 THEN invite_code_value := upper(invite_code_value); IF invite_code_value ~ '^[ABCDEFGHJKMNPQRSTUVWXYZ23456789]{6}$' THEN UPDATE public.invite_codes SET used_count = used_count + 1 WHERE code = invite_code_value AND status = 'active' AND (max_uses IS NULL OR used_count < max_uses) AND (expires_at IS NULL OR expires_at > NOW()) RETURNING owner_id INTO v_referrer_id; IF v_referrer_id IS NOT NULL THEN UPDATE public.profiles SET referred_by = v_referrer_id WHERE id = new.id; END IF; END IF; END IF; RETURN NEW; END; $$; """ )