import postgres from 'postgres'; import { User } from '../types'; const client = postgres(process.env.DATABASE_URL); const getDefaultUser = (): User => ({ settings: { participating: true, }, statistics: { pets: 0, }, }); export async function init(): Promise { await client` CREATE TABLE IF NOT EXISTS users ( user_id TEXT PRIMARY KEY, data JSONB NOT NULL ) `; } export async function getUser(userId: string): Promise { let [user] = await client<{ user_id: string; data: User }[]>` SELECT user_id, data FROM users WHERE user_id = ${userId} `; if (user) return user.data; [user] = await client<{ user_id: string; data: User }[]>` INSERT INTO users (user_id, data) VALUES (${userId}, ${client.json(getDefaultUser())}) ON CONFLICT (user_id) DO NOTHING RETURNING user_id, data `; return user?.data ?? getDefaultUser(); } export async function setUser(userId: string, data: User): Promise { await client` INSERT INTO users (user_id, data) VALUES (${userId}, ${client.json(data)}) ON CONFLICT (user_id) DO UPDATE SET data = EXCLUDED.data `; } export async function deleteUser(userId: string): Promise { await client` DELETE FROM users WHERE user_id = ${userId} `; } export async function updateUserSettings(userId: string, settings: Partial): Promise { await client` UPDATE users SET data = jsonb_set( data, '{settings}', data->'settings' || ${client.json(settings)} ) WHERE user_id = ${userId} `; } export async function updateUserStatistics(userId: string, statistics: Partial): Promise { await client` UPDATE users SET data = jsonb_set( data, '{statistics}', data->'statistics' || ${client.json(statistics)} ) WHERE user_id = ${userId} `; } //#region Leaderboard export async function getHighestPets(limit = 10): Promise<{ userId: string; pets: number }[]> { const users = await client<{ user_id: string; pets: number }[]>` SELECT user_id, (data->'statistics'->>'pets')::integer AS pets FROM users WHERE COALESCE((data->'flags'->>'blacklisted')::boolean, false) = false AND COALESCE((data->'settings'->>'participating')::boolean, true) = true ORDER BY pets DESC, user_id ASC LIMIT ${limit} `; return users.map((user) => ({ userId: user.user_id, pets: user.pets, })); } export async function getPetPosition(userId: string): Promise { const result = await client<{ position: number }[]>` SELECT position FROM ( SELECT user_id, RANK() OVER (ORDER BY pets DESC, user_id ASC) AS position FROM ( SELECT user_id, (data->'statistics'->>'pets')::integer AS pets FROM users WHERE COALESCE((data->'flags'->>'blacklisted')::boolean, false) = false AND COALESCE((data->'settings'->>'participating')::boolean, true) = true ) ranked ) ranked_users WHERE user_id = ${userId} `; return result[0]?.position ?? null; } //#endregion