149 lines
5.1 KiB
JavaScript
149 lines
5.1 KiB
JavaScript
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 '{}'
|
|
);
|
|
`);
|
|
|
|
await p.query(`
|
|
CREATE TABLE IF NOT EXISTS admin_logs (
|
|
id SERIAL PRIMARY KEY,
|
|
guild_id TEXT NOT NULL,
|
|
action TEXT NOT NULL, -- 'kick', 'ban', 'timeout'
|
|
target_user_id TEXT NOT NULL,
|
|
target_username TEXT NOT NULL,
|
|
moderator_user_id TEXT NOT NULL,
|
|
moderator_username TEXT NOT NULL,
|
|
reason TEXT NOT NULL,
|
|
duration TEXT, -- for timeout/ban (e.g., '1d', '30m', 'permanent')
|
|
end_date TIMESTAMP WITH TIME ZONE, -- calculated end date for timeout/ban
|
|
timestamp TIMESTAMP WITH TIME ZONE DEFAULT now()
|
|
);
|
|
`);
|
|
}
|
|
|
|
// 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]);
|
|
}
|
|
|
|
// Admin Logs
|
|
async function addAdminLog(logData) {
|
|
const p = initPool();
|
|
const q = `INSERT INTO admin_logs(guild_id, action, target_user_id, target_username, moderator_user_id, moderator_username, reason, duration, end_date)
|
|
VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9)`;
|
|
await p.query(q, [
|
|
logData.guildId,
|
|
logData.action,
|
|
logData.targetUserId,
|
|
logData.targetUsername,
|
|
logData.moderatorUserId,
|
|
logData.moderatorUsername,
|
|
logData.reason,
|
|
logData.duration || null,
|
|
logData.endDate || null
|
|
]);
|
|
}
|
|
|
|
async function getAdminLogs(guildId, limit = 50) {
|
|
const p = initPool();
|
|
const res = await p.query('SELECT * FROM admin_logs WHERE guild_id = $1 ORDER BY timestamp DESC LIMIT $2', [guildId, limit]);
|
|
return res.rows;
|
|
}
|
|
|
|
async function getAdminLogsByAction(guildId, action, limit = 50) {
|
|
const p = initPool();
|
|
const res = await p.query('SELECT * FROM admin_logs WHERE guild_id = $1 AND action = $2 ORDER BY timestamp DESC LIMIT $3', [guildId, action, limit]);
|
|
return res.rows;
|
|
}
|
|
|
|
async function deleteAdminLog(guildId, logId) {
|
|
const p = initPool();
|
|
await p.query('DELETE FROM admin_logs WHERE guild_id = $1 AND id = $2', [guildId, logId]);
|
|
}
|
|
|
|
async function deleteAllAdminLogs(guildId) {
|
|
const p = initPool();
|
|
await p.query('DELETE FROM admin_logs WHERE guild_id = $1', [guildId]);
|
|
}
|
|
|
|
// 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, addAdminLog, getAdminLogs, getAdminLogsByAction, deleteAdminLog, deleteAllAdminLogs };
|