-- Chattyness Admin Restore Props Function -- PostgreSQL 18 -- -- Admin function to restore missing essential props for users. -- Load via: psql -f schema/functions/003_admin_restore_props.sql \set ON_ERROR_STOP on BEGIN; -- ============================================================================= -- Restore Essential Props for a Single User -- ============================================================================= -- Restores missing face-tagged server props to a user's inventory and fixes -- any broken avatar slot references. -- -- Usage: SELECT * FROM auth.restore_essential_props('username'); -- ============================================================================= CREATE OR REPLACE FUNCTION auth.restore_essential_props(p_username TEXT) RETURNS TABLE( action TEXT, prop_slug TEXT, inventory_id UUID ) AS $$ DECLARE v_user_id UUID; v_prop RECORD; v_inventory_id UUID; v_avatar_id UUID; BEGIN -- Get user ID SELECT id INTO v_user_id FROM auth.users WHERE username = p_username; IF v_user_id IS NULL THEN RAISE EXCEPTION 'User not found: %', p_username; END IF; -- For each face-tagged server prop FOR v_prop IN SELECT id, name, asset_path, default_layer, default_emotion, default_position, slug, is_transferable, is_portable, is_droppable FROM server.props WHERE tags @> ARRAY['face'] AND is_active = true LOOP -- Check if user already has this prop SELECT inv.id INTO v_inventory_id FROM auth.inventory inv WHERE inv.user_id = v_user_id AND inv.server_prop_id = v_prop.id; IF v_inventory_id IS NULL THEN -- Insert missing prop INSERT INTO auth.inventory ( user_id, server_prop_id, prop_name, prop_asset_path, layer, position, origin, is_transferable, is_portable, is_droppable ) VALUES ( v_user_id, v_prop.id, v_prop.name, v_prop.asset_path, v_prop.default_layer, CASE WHEN v_prop.default_layer IS NOT NULL THEN v_prop.default_position ELSE NULL END, 'server_library', v_prop.is_transferable, v_prop.is_portable, v_prop.is_droppable ) RETURNING id INTO v_inventory_id; action := 'restored'; prop_slug := v_prop.slug; inventory_id := v_inventory_id; RETURN NEXT; ELSE action := 'exists'; prop_slug := v_prop.slug; inventory_id := v_inventory_id; RETURN NEXT; END IF; END LOOP; -- Get the user's default avatar (slot 0) SELECT id INTO v_avatar_id FROM auth.avatars WHERE user_id = v_user_id AND slot_number = 0; IF v_avatar_id IS NOT NULL THEN -- Update avatar slots with correct inventory references where NULL UPDATE auth.avatars a SET l_skin_4 = COALESCE(a.l_skin_4, ( SELECT inv.id FROM auth.inventory inv JOIN server.props sp ON inv.server_prop_id = sp.id WHERE inv.user_id = v_user_id AND sp.slug = 'face' )), e_neutral_4 = COALESCE(a.e_neutral_4, ( SELECT inv.id FROM auth.inventory inv JOIN server.props sp ON inv.server_prop_id = sp.id WHERE inv.user_id = v_user_id AND sp.slug = 'neutral' )), e_happy_4 = COALESCE(a.e_happy_4, ( SELECT inv.id FROM auth.inventory inv JOIN server.props sp ON inv.server_prop_id = sp.id WHERE inv.user_id = v_user_id AND sp.slug = 'smile' )), e_sad_4 = COALESCE(a.e_sad_4, ( SELECT inv.id FROM auth.inventory inv JOIN server.props sp ON inv.server_prop_id = sp.id WHERE inv.user_id = v_user_id AND sp.slug = 'sad' )), e_angry_4 = COALESCE(a.e_angry_4, ( SELECT inv.id FROM auth.inventory inv JOIN server.props sp ON inv.server_prop_id = sp.id WHERE inv.user_id = v_user_id AND sp.slug = 'angry' )), e_surprised_4 = COALESCE(a.e_surprised_4, ( SELECT inv.id FROM auth.inventory inv JOIN server.props sp ON inv.server_prop_id = sp.id WHERE inv.user_id = v_user_id AND sp.slug = 'surprised' )), e_thinking_4 = COALESCE(a.e_thinking_4, ( SELECT inv.id FROM auth.inventory inv JOIN server.props sp ON inv.server_prop_id = sp.id WHERE inv.user_id = v_user_id AND sp.slug = 'thinking' )), e_laughing_4 = COALESCE(a.e_laughing_4, ( SELECT inv.id FROM auth.inventory inv JOIN server.props sp ON inv.server_prop_id = sp.id WHERE inv.user_id = v_user_id AND sp.slug = 'laughing' )), e_crying_4 = COALESCE(a.e_crying_4, ( SELECT inv.id FROM auth.inventory inv JOIN server.props sp ON inv.server_prop_id = sp.id WHERE inv.user_id = v_user_id AND sp.slug = 'crying' )), e_love_4 = COALESCE(a.e_love_4, ( SELECT inv.id FROM auth.inventory inv JOIN server.props sp ON inv.server_prop_id = sp.id WHERE inv.user_id = v_user_id AND sp.slug = 'love' )), e_confused_4 = COALESCE(a.e_confused_4, ( SELECT inv.id FROM auth.inventory inv JOIN server.props sp ON inv.server_prop_id = sp.id WHERE inv.user_id = v_user_id AND sp.slug = 'confused' )), e_sleeping_4 = COALESCE(a.e_sleeping_4, ( SELECT inv.id FROM auth.inventory inv JOIN server.props sp ON inv.server_prop_id = sp.id WHERE inv.user_id = v_user_id AND sp.slug = 'sleeping' )), e_wink_4 = COALESCE(a.e_wink_4, ( SELECT inv.id FROM auth.inventory inv JOIN server.props sp ON inv.server_prop_id = sp.id WHERE inv.user_id = v_user_id AND sp.slug = 'wink' )) WHERE a.id = v_avatar_id; action := 'avatar_fixed'; prop_slug := NULL; inventory_id := v_avatar_id; RETURN NEXT; END IF; END; $$ LANGUAGE plpgsql; COMMENT ON FUNCTION auth.restore_essential_props(TEXT) IS 'Restore missing essential (face-tagged) props to a user''s inventory and fix avatar slots'; -- ============================================================================= -- Restore Essential Props for All Users -- ============================================================================= -- Batch operation to restore props for all users. -- -- Usage: SELECT * FROM auth.restore_essential_props_all_users(); -- ============================================================================= CREATE OR REPLACE FUNCTION auth.restore_essential_props_all_users() RETURNS TABLE( username TEXT, props_restored INTEGER ) AS $$ DECLARE v_user RECORD; v_count INTEGER; BEGIN FOR v_user IN SELECT id, u.username FROM auth.users u LOOP SELECT COUNT(*) INTO v_count FROM auth.restore_essential_props(v_user.username) WHERE action = 'restored'; IF v_count > 0 THEN username := v_user.username; props_restored := v_count; RETURN NEXT; END IF; END LOOP; END; $$ LANGUAGE plpgsql; COMMENT ON FUNCTION auth.restore_essential_props_all_users() IS 'Restore missing essential props for all users - returns users who had props restored'; COMMIT;