const { Pool } = require('pg'); const format = require('pg-format'); let pool; function initPool() { if (pool) return pool; const connectionString = process.env.DATABASE_URL; if (!connectionString) throw new Error('DATABASE_URL is not set'); pool = new Pool({ connectionString }); return pool; } async function ensureSchema() { const p = initPool(); // basic tables: servers (settings), invites await p.query(` CREATE TABLE IF NOT EXISTS servers ( guild_id TEXT PRIMARY KEY, settings JSONB DEFAULT '{}' ); `); await p.query(` CREATE TABLE IF NOT EXISTS invites ( code TEXT PRIMARY KEY, guild_id TEXT NOT NULL, url TEXT, channel_id TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT now(), max_uses INTEGER DEFAULT 0, max_age INTEGER DEFAULT 0, temporary BOOLEAN DEFAULT false ); `); await p.query(` CREATE TABLE IF NOT EXISTS users ( id SERIAL PRIMARY KEY, discord_id TEXT UNIQUE, data JSONB DEFAULT '{}' ); `); } // Servers async function getServerSettings(guildId) { const p = initPool(); const res = await p.query('SELECT settings FROM servers WHERE guild_id = $1', [guildId]); if (res.rowCount === 0) return null; return res.rows[0].settings || {}; } async function upsertServerSettings(guildId, settings) { const p = initPool(); await p.query(`INSERT INTO servers(guild_id, settings) VALUES($1, $2) ON CONFLICT (guild_id) DO UPDATE SET settings = $2`, [guildId, settings]); } // Invites async function listInvites(guildId) { const p = initPool(); const res = await p.query('SELECT code, url, channel_id, created_at, max_uses, max_age, temporary FROM invites WHERE guild_id = $1 ORDER BY created_at DESC', [guildId]); return res.rows; } async function addInvite(inv) { const p = initPool(); const q = `INSERT INTO invites(code, guild_id, url, channel_id, created_at, max_uses, max_age, temporary) VALUES($1,$2,$3,$4,$5,$6,$7,$8) ON CONFLICT (code) DO UPDATE SET url = EXCLUDED.url, channel_id = EXCLUDED.channel_id, max_uses = EXCLUDED.max_uses, max_age = EXCLUDED.max_age, temporary = EXCLUDED.temporary, created_at = EXCLUDED.created_at`; await p.query(q, [inv.code, inv.guildId, inv.url, inv.channelId, inv.createdAt ? new Date(inv.createdAt) : new Date(), inv.maxUses || 0, inv.maxAge || 0, inv.temporary || false]); } async function deleteInvite(guildId, code) { const p = initPool(); await p.query('DELETE FROM invites WHERE guild_id = $1 AND code = $2', [guildId, code]); } // Users async function getUserData(discordId) { const p = initPool(); const res = await p.query('SELECT data FROM users WHERE discord_id = $1', [discordId]); if (res.rowCount === 0) return null; return res.rows[0].data || {}; } async function upsertUserData(discordId, data) { const p = initPool(); await p.query(`INSERT INTO users(discord_id, data) VALUES($1, $2) ON CONFLICT (discord_id) DO UPDATE SET data = $2`, [discordId, data]); } module.exports = { initPool, ensureSchema, getServerSettings, upsertServerSettings, listInvites, addInvite, deleteInvite, getUserData, upsertUserData };