refactor: migrate from SQLite to PostgreSQL for session management and data storage

- Updated package dependencies to replace SQLite with PostgreSQL.
- Modified privacy policy to reflect the change in database technology.
- Adjusted session management in server.js to use connect-pg-simple.
- Enhanced docker-compose.yml to include PostgreSQL service with health checks.
- Added Dockerfile for multi-stage builds for development and production environments.
- Introduced .dockerignore to exclude unnecessary files from the Docker context.
This commit is contained in:
Puechberty Arthur
2026-04-01 14:49:44 +02:00
parent 53f07a1d85
commit 1fcb1c2b88
12 changed files with 501 additions and 747 deletions
+6
View File
@@ -0,0 +1,6 @@
.git
.vscode
node_modules
app/node_modules
.env
.env.*
+8 -1
View File
@@ -1,9 +1,16 @@
CLIENT_ID=VOTRE_BOT_CLIENT_ID CLIENT_ID=VOTRE_BOT_CLIENT_ID
CLIENT_SECRET=VOTRE_BOT_CLIENT_SECRET CLIENT_SECRET=VOTRE_BOT_CLIENT_SECRET
REDIRECT_URI=https://lazybot.arthurp.fr/auth/discord/callback REDIRECT_URI=https://lazybot.arthurp.fr/auth/discord/callback
NODE_ENV=development
PORT=3000 PORT=3000
BOT_TOKEN=VOTRE_BOT_TOKEN BOT_TOKEN=VOTRE_BOT_TOKEN
SESSION_SECRET=UN_SECRET_LONG_ET_UNIQUE SESSION_SECRET=UN_SECRET_LONG_ET_UNIQUE
DB_PATH=database.sqlite POSTGRES_HOST=db
POSTGRES_PORT=5432
POSTGRES_DB=lazybot
POSTGRES_USER=lazybot
POSTGRES_PASSWORD=change_me
POSTGRES_DATA_PATH=./data/postgres
DATABASE_URL=postgresql://lazybot:change_me@db:5432/lazybot
OWNER=VOTRE_ID_UTILISATEUR OWNER=VOTRE_ID_UTILISATEUR
URL=https://lazybot.arthurp.fr URL=https://lazybot.arthurp.fr
+3
View File
@@ -74,6 +74,9 @@ web_modules/
.env.* .env.*
!.env.example !.env.example
# Docker volumes
data/postgres/
# parcel-bundler cache (https://parceljs.org/) # parcel-bundler cache (https://parceljs.org/)
.cache .cache
.parcel-cache .parcel-cache
+14
View File
@@ -0,0 +1,14 @@
FROM node:20-bookworm-slim AS development
WORKDIR /app
COPY app/package*.json ./
RUN npm install
COPY app/ ./
CMD ["node", "server.js"]
FROM node:20-bookworm-slim AS production
ENV NODE_ENV=production
WORKDIR /app
COPY app/package*.json ./
RUN npm install --omit=dev && npm cache clean --force
COPY app/ ./
CMD ["node", "server.js"]
+49 -3
View File
@@ -16,7 +16,7 @@ Site officiel: https://lazybot.arthurp.fr
- Node.js - Node.js
- Discord.js - Discord.js
- SQLite - PostgreSQL
- Docker et Docker Compose - Docker et Docker Compose
## Installation ## Installation
@@ -40,15 +40,41 @@ cp .env.example .env
CLIENT_ID=VOTRE_BOT_CLIENT_ID CLIENT_ID=VOTRE_BOT_CLIENT_ID
CLIENT_SECRET=VOTRE_BOT_CLIENT_SECRET CLIENT_SECRET=VOTRE_BOT_CLIENT_SECRET
REDIRECT_URI=https://lazybot.arthurp.fr/auth/discord/callback REDIRECT_URI=https://lazybot.arthurp.fr/auth/discord/callback
NODE_ENV=production
PORT=3000 PORT=3000
BOT_TOKEN=VOTRE_TOKEN_BOT BOT_TOKEN=VOTRE_TOKEN_BOT
SESSION_SECRET=UN_SECRET_LONG_ET_UNIQUE SESSION_SECRET=UN_SECRET_LONG_ET_UNIQUE
DB_PATH=database.sqlite POSTGRES_HOST=db
POSTGRES_PORT=5432
POSTGRES_DB=lazybot
POSTGRES_USER=lazybot
POSTGRES_PASSWORD=change_me
POSTGRES_DATA_PATH=./data/postgres
DATABASE_URL=postgresql://lazybot:change_me@db:5432/lazybot
OWNER=VOTRE_ID_UTILISATEUR OWNER=VOTRE_ID_UTILISATEUR
URL=https://lazybot.arthurp.fr URL=https://lazybot.arthurp.fr
``` ```
4. Lancer avec Docker Variables d'environnement prises en charge:
- CLIENT_ID
- CLIENT_SECRET
- REDIRECT_URI
- NODE_ENV
- PORT
- BOT_TOKEN
- SESSION_SECRET
- POSTGRES_HOST
- POSTGRES_PORT
- POSTGRES_DB
- POSTGRES_USER
- POSTGRES_PASSWORD
- POSTGRES_DATA_PATH
- DATABASE_URL
- OWNER
- URL
4. Lancer
```bash ```bash
docker compose up -d --build docker compose up -d --build
@@ -56,6 +82,26 @@ docker compose up -d --build
Le dashboard est ensuite disponible sur `http://localhost:3000`. Le dashboard est ensuite disponible sur `http://localhost:3000`.
## Exemples de profils
Exemple developpement (.env):
```env
NODE_ENV=development
PORT=3002
POSTGRES_DATA_PATH=./data/postgres/dev
URL=http://localhost:3002
```
Exemple production (.env):
```env
NODE_ENV=production
PORT=3000
POSTGRES_DATA_PATH=./data/postgres
URL=https://lazybot.arthurp.fr
```
## Securite avant publication ## Securite avant publication
- Ne jamais versionner `.env` - Ne jamais versionner `.env`
+234 -132
View File
@@ -1,34 +1,29 @@
const sqlite3 = require("sqlite3").verbose(); const { Pool } = require("pg");
const path = require("path");
const db = new sqlite3.Database( const pool = new Pool({
path.join(__dirname, process.env.DB_PATH || "database.sqlite"), connectionString: process.env.DATABASE_URL,
err => { host: process.env.POSTGRES_HOST,
if (err) console.error("Erreur DB:", err); port: process.env.POSTGRES_PORT ? Number(process.env.POSTGRES_PORT) : undefined,
else console.log("DB SQLite connectée"); user: process.env.POSTGRES_USER,
} password: process.env.POSTGRES_PASSWORD,
); database: process.env.POSTGRES_DB,
});
db.getAsync = (sql, params = []) => { const TABLES_WITH_ID = new Set([
return new Promise((resolve, reject) => { "shop_items",
db.get(sql, params, (err, row) => { "user_inventory",
if (err) reject(err); "stats_channels",
else resolve(row); "user_activity_stats",
}); "username_history",
}); "nickname_history",
}; "antiraid_warnings",
"warnings",
"scheduled_messages",
"role_panels",
"role_panel_buttons",
]);
db.allAsync = (sql, params = []) => { const schemaSql = `
return new Promise((resolve, reject) => {
db.all(sql, params, (err, rows) => {
if (err) reject(err);
else resolve(rows);
});
});
};
// Création de la table si elle n'existe pas
db.exec(`
CREATE TABLE IF NOT EXISTS welcome_config ( CREATE TABLE IF NOT EXISTS welcome_config (
guild_id TEXT PRIMARY KEY, guild_id TEXT PRIMARY KEY,
channel_id TEXT, channel_id TEXT,
@@ -106,7 +101,7 @@ db.exec(`
user_id TEXT NOT NULL, user_id TEXT NOT NULL,
xp INTEGER NOT NULL, xp INTEGER NOT NULL,
level INTEGER NOT NULL, level INTEGER NOT NULL,
last_xp_message_timestamp INTEGER, last_xp_message_timestamp BIGINT,
PRIMARY KEY (guild_id, user_id) PRIMARY KEY (guild_id, user_id)
); );
@@ -155,32 +150,32 @@ db.exec(`
user_id TEXT NOT NULL, user_id TEXT NOT NULL,
balance INTEGER NOT NULL DEFAULT 0, balance INTEGER NOT NULL DEFAULT 0,
bank INTEGER NOT NULL DEFAULT 0, bank INTEGER NOT NULL DEFAULT 0,
last_daily_timestamp INTEGER, last_daily_timestamp BIGINT,
last_work_timestamp INTEGER, last_work_timestamp BIGINT,
last_crime_timestamp INTEGER, last_crime_timestamp BIGINT,
last_steal_timestamp INTEGER, last_steal_timestamp BIGINT,
last_message_money_timestamp INTEGER, last_message_money_timestamp BIGINT,
PRIMARY KEY (guild_id, user_id) PRIMARY KEY (guild_id, user_id)
); );
CREATE TABLE IF NOT EXISTS shop_items ( CREATE TABLE IF NOT EXISTS shop_items (
id INTEGER PRIMARY KEY AUTOINCREMENT, id BIGSERIAL PRIMARY KEY,
guild_id TEXT NOT NULL, guild_id TEXT NOT NULL,
name TEXT NOT NULL, name TEXT NOT NULL,
description TEXT, description TEXT,
price INTEGER NOT NULL, price INTEGER NOT NULL,
role_id TEXT, role_id TEXT,
stock INTEGER DEFAULT -1, stock INTEGER DEFAULT -1,
created_at INTEGER NOT NULL created_at BIGINT NOT NULL
); );
CREATE TABLE IF NOT EXISTS user_inventory ( CREATE TABLE IF NOT EXISTS user_inventory (
id INTEGER PRIMARY KEY AUTOINCREMENT, id BIGSERIAL PRIMARY KEY,
guild_id TEXT NOT NULL, guild_id TEXT NOT NULL,
user_id TEXT NOT NULL, user_id TEXT NOT NULL,
item_id INTEGER NOT NULL, item_id BIGINT NOT NULL,
quantity INTEGER NOT NULL DEFAULT 1, quantity INTEGER NOT NULL DEFAULT 1,
purchased_at INTEGER NOT NULL, purchased_at BIGINT NOT NULL,
FOREIGN KEY (item_id) REFERENCES shop_items(id) FOREIGN KEY (item_id) REFERENCES shop_items(id)
); );
@@ -196,7 +191,7 @@ db.exec(`
channel_id TEXT PRIMARY KEY, channel_id TEXT PRIMARY KEY,
guild_id TEXT NOT NULL, guild_id TEXT NOT NULL,
owner_id TEXT NOT NULL, owner_id TEXT NOT NULL,
created_at INTEGER NOT NULL created_at BIGINT NOT NULL
); );
CREATE TABLE IF NOT EXISTS counting_config ( CREATE TABLE IF NOT EXISTS counting_config (
@@ -208,7 +203,7 @@ db.exec(`
); );
CREATE TABLE IF NOT EXISTS stats_channels ( CREATE TABLE IF NOT EXISTS stats_channels (
id INTEGER PRIMARY KEY AUTOINCREMENT, id BIGSERIAL PRIMARY KEY,
guild_id TEXT NOT NULL, guild_id TEXT NOT NULL,
channel_id TEXT NOT NULL, channel_id TEXT NOT NULL,
stat_type TEXT NOT NULL, stat_type TEXT NOT NULL,
@@ -218,7 +213,7 @@ db.exec(`
); );
CREATE TABLE IF NOT EXISTS user_activity_stats ( CREATE TABLE IF NOT EXISTS user_activity_stats (
id INTEGER PRIMARY KEY AUTOINCREMENT, id BIGSERIAL PRIMARY KEY,
guild_id TEXT NOT NULL, guild_id TEXT NOT NULL,
user_id TEXT NOT NULL, user_id TEXT NOT NULL,
stat_type TEXT NOT NULL, stat_type TEXT NOT NULL,
@@ -230,7 +225,7 @@ db.exec(`
CREATE TABLE IF NOT EXISTS voice_sessions ( CREATE TABLE IF NOT EXISTS voice_sessions (
guild_id TEXT NOT NULL, guild_id TEXT NOT NULL,
user_id TEXT NOT NULL, user_id TEXT NOT NULL,
join_timestamp INTEGER NOT NULL, join_timestamp BIGINT NOT NULL,
PRIMARY KEY(guild_id, user_id) PRIMARY KEY(guild_id, user_id)
); );
@@ -259,21 +254,21 @@ db.exec(`
); );
CREATE TABLE IF NOT EXISTS username_history ( CREATE TABLE IF NOT EXISTS username_history (
id INTEGER PRIMARY KEY AUTOINCREMENT, id BIGSERIAL PRIMARY KEY,
user_id TEXT NOT NULL, user_id TEXT NOT NULL,
username TEXT NOT NULL, username TEXT NOT NULL,
display_name TEXT, display_name TEXT,
changed_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) changed_at BIGINT NOT NULL DEFAULT (EXTRACT(EPOCH FROM NOW())::BIGINT)
); );
CREATE INDEX IF NOT EXISTS idx_username_history_user ON username_history(user_id, changed_at DESC); CREATE INDEX IF NOT EXISTS idx_username_history_user ON username_history(user_id, changed_at DESC);
CREATE TABLE IF NOT EXISTS nickname_history ( CREATE TABLE IF NOT EXISTS nickname_history (
id INTEGER PRIMARY KEY AUTOINCREMENT, id BIGSERIAL PRIMARY KEY,
guild_id TEXT NOT NULL, guild_id TEXT NOT NULL,
user_id TEXT NOT NULL, user_id TEXT NOT NULL,
nickname TEXT, nickname TEXT,
changed_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) changed_at BIGINT NOT NULL DEFAULT (EXTRACT(EPOCH FROM NOW())::BIGINT)
); );
CREATE INDEX IF NOT EXISTS idx_nickname_history_user ON nickname_history(guild_id, user_id, changed_at DESC); CREATE INDEX IF NOT EXISTS idx_nickname_history_user ON nickname_history(guild_id, user_id, changed_at DESC);
@@ -282,23 +277,20 @@ db.exec(`
guild_id TEXT PRIMARY KEY, guild_id TEXT PRIMARY KEY,
enabled INTEGER NOT NULL DEFAULT 0, enabled INTEGER NOT NULL DEFAULT 0,
log_channel_id TEXT, log_channel_id TEXT,
-- Anti-link
antilink_enabled INTEGER NOT NULL DEFAULT 0, antilink_enabled INTEGER NOT NULL DEFAULT 0,
antilink_action TEXT NOT NULL DEFAULT 'delete', antilink_action TEXT NOT NULL DEFAULT 'delete',
antilink_whitelist_domains TEXT DEFAULT '[]', antilink_whitelist_domains TEXT DEFAULT '[]',
antilink_exclude_channels TEXT DEFAULT '[]', antilink_exclude_channels TEXT DEFAULT '[]',
antilink_exclude_roles TEXT DEFAULT '[]', antilink_exclude_roles TEXT DEFAULT '[]',
antilink_warn_message TEXT DEFAULT '⚠️ Les liens ne sont pas autorisés ici.', antilink_warn_message TEXT DEFAULT '⚠️ Les liens ne sont pas autorisés ici.',
-- Anti-invite (liens Discord)
antiinvite_enabled INTEGER NOT NULL DEFAULT 0, antiinvite_enabled INTEGER NOT NULL DEFAULT 0,
antiinvite_action TEXT NOT NULL DEFAULT 'delete', antiinvite_action TEXT NOT NULL DEFAULT 'delete',
antiinvite_allow_own_server INTEGER NOT NULL DEFAULT 1, antiinvite_allow_own_server INTEGER NOT NULL DEFAULT 1,
antiinvite_exclude_channels TEXT DEFAULT '[]', antiinvite_exclude_channels TEXT DEFAULT '[]',
antiinvite_exclude_roles TEXT DEFAULT '[]', antiinvite_exclude_roles TEXT DEFAULT '[]',
-- Anti-spam
antispam_enabled INTEGER NOT NULL DEFAULT 0, antispam_enabled INTEGER NOT NULL DEFAULT 0,
antispam_action TEXT NOT NULL DEFAULT 'mute', antispam_action TEXT NOT NULL DEFAULT 'mute',
antispam_max_messages INTEGER NOT NULL DEFAULT 5, antispam_max_messages INTEGER NOT NULL DEFAULT 5,
@@ -306,59 +298,51 @@ db.exec(`
antispam_mute_duration_minutes INTEGER NOT NULL DEFAULT 10, antispam_mute_duration_minutes INTEGER NOT NULL DEFAULT 10,
antispam_exclude_channels TEXT DEFAULT '[]', antispam_exclude_channels TEXT DEFAULT '[]',
antispam_exclude_roles TEXT DEFAULT '[]', antispam_exclude_roles TEXT DEFAULT '[]',
-- Anti-duplicate (messages identiques)
antidupe_enabled INTEGER NOT NULL DEFAULT 0, antidupe_enabled INTEGER NOT NULL DEFAULT 0,
antidupe_action TEXT NOT NULL DEFAULT 'delete', antidupe_action TEXT NOT NULL DEFAULT 'delete',
antidupe_max_duplicates INTEGER NOT NULL DEFAULT 3, antidupe_max_duplicates INTEGER NOT NULL DEFAULT 3,
antidupe_interval_seconds INTEGER NOT NULL DEFAULT 60, antidupe_interval_seconds INTEGER NOT NULL DEFAULT 60,
antidupe_exclude_channels TEXT DEFAULT '[]', antidupe_exclude_channels TEXT DEFAULT '[]',
antidupe_exclude_roles TEXT DEFAULT '[]', antidupe_exclude_roles TEXT DEFAULT '[]',
-- Anti-mass mention
antimention_enabled INTEGER NOT NULL DEFAULT 0, antimention_enabled INTEGER NOT NULL DEFAULT 0,
antimention_action TEXT NOT NULL DEFAULT 'delete', antimention_action TEXT NOT NULL DEFAULT 'delete',
antimention_max_mentions INTEGER NOT NULL DEFAULT 5, antimention_max_mentions INTEGER NOT NULL DEFAULT 5,
antimention_exclude_channels TEXT DEFAULT '[]', antimention_exclude_channels TEXT DEFAULT '[]',
antimention_exclude_roles TEXT DEFAULT '[]', antimention_exclude_roles TEXT DEFAULT '[]',
-- Anti-mass emoji
antiemoji_enabled INTEGER NOT NULL DEFAULT 0, antiemoji_enabled INTEGER NOT NULL DEFAULT 0,
antiemoji_action TEXT NOT NULL DEFAULT 'delete', antiemoji_action TEXT NOT NULL DEFAULT 'delete',
antiemoji_max_emojis INTEGER NOT NULL DEFAULT 10, antiemoji_max_emojis INTEGER NOT NULL DEFAULT 10,
antiemoji_exclude_channels TEXT DEFAULT '[]', antiemoji_exclude_channels TEXT DEFAULT '[]',
antiemoji_exclude_roles TEXT DEFAULT '[]', antiemoji_exclude_roles TEXT DEFAULT '[]',
-- Anti-caps (majuscules)
anticaps_enabled INTEGER NOT NULL DEFAULT 0, anticaps_enabled INTEGER NOT NULL DEFAULT 0,
anticaps_action TEXT NOT NULL DEFAULT 'delete', anticaps_action TEXT NOT NULL DEFAULT 'delete',
anticaps_max_percent INTEGER NOT NULL DEFAULT 70, anticaps_max_percent INTEGER NOT NULL DEFAULT 70,
anticaps_min_length INTEGER NOT NULL DEFAULT 10, anticaps_min_length INTEGER NOT NULL DEFAULT 10,
anticaps_exclude_channels TEXT DEFAULT '[]', anticaps_exclude_channels TEXT DEFAULT '[]',
anticaps_exclude_roles TEXT DEFAULT '[]', anticaps_exclude_roles TEXT DEFAULT '[]',
-- Anti-bot join
antibot_enabled INTEGER NOT NULL DEFAULT 0, antibot_enabled INTEGER NOT NULL DEFAULT 0,
antibot_action TEXT NOT NULL DEFAULT 'kick', antibot_action TEXT NOT NULL DEFAULT 'kick',
antibot_min_account_age_days INTEGER NOT NULL DEFAULT 7, antibot_min_account_age_days INTEGER NOT NULL DEFAULT 7,
antibot_no_avatar_action INTEGER NOT NULL DEFAULT 0, antibot_no_avatar_action INTEGER NOT NULL DEFAULT 0,
antibot_suspicious_name_action INTEGER NOT NULL DEFAULT 0, antibot_suspicious_name_action INTEGER NOT NULL DEFAULT 0,
-- Anti-mass join (raid de comptes)
antimassj_enabled INTEGER NOT NULL DEFAULT 0, antimassj_enabled INTEGER NOT NULL DEFAULT 0,
antimassj_action TEXT NOT NULL DEFAULT 'kick', antimassj_action TEXT NOT NULL DEFAULT 'kick',
antimassj_max_joins INTEGER NOT NULL DEFAULT 10, antimassj_max_joins INTEGER NOT NULL DEFAULT 10,
antimassj_interval_seconds INTEGER NOT NULL DEFAULT 10, antimassj_interval_seconds INTEGER NOT NULL DEFAULT 10,
antimassj_lockdown_duration_minutes INTEGER NOT NULL DEFAULT 5, antimassj_lockdown_duration_minutes INTEGER NOT NULL DEFAULT 5,
-- Anti-newline spam
antinewline_enabled INTEGER NOT NULL DEFAULT 0, antinewline_enabled INTEGER NOT NULL DEFAULT 0,
antinewline_action TEXT NOT NULL DEFAULT 'delete', antinewline_action TEXT NOT NULL DEFAULT 'delete',
antinewline_max_lines INTEGER NOT NULL DEFAULT 15, antinewline_max_lines INTEGER NOT NULL DEFAULT 15,
antinewline_exclude_channels TEXT DEFAULT '[]', antinewline_exclude_channels TEXT DEFAULT '[]',
antinewline_exclude_roles TEXT DEFAULT '[]', antinewline_exclude_roles TEXT DEFAULT '[]',
-- Anti-badwords (gros mots)
antibadwords_enabled INTEGER NOT NULL DEFAULT 0, antibadwords_enabled INTEGER NOT NULL DEFAULT 0,
antibadwords_action TEXT NOT NULL DEFAULT 'delete', antibadwords_action TEXT NOT NULL DEFAULT 'delete',
antibadwords_words TEXT DEFAULT '[]', antibadwords_words TEXT DEFAULT '[]',
@@ -368,29 +352,27 @@ db.exec(`
); );
CREATE TABLE IF NOT EXISTS antiraid_warnings ( CREATE TABLE IF NOT EXISTS antiraid_warnings (
id INTEGER PRIMARY KEY AUTOINCREMENT, id BIGSERIAL PRIMARY KEY,
guild_id TEXT NOT NULL, guild_id TEXT NOT NULL,
user_id TEXT NOT NULL, user_id TEXT NOT NULL,
warning_type TEXT NOT NULL, warning_type TEXT NOT NULL,
warned_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) warned_at BIGINT NOT NULL DEFAULT (EXTRACT(EPOCH FROM NOW())::BIGINT)
); );
CREATE INDEX IF NOT EXISTS idx_antiraid_warnings ON antiraid_warnings(guild_id, user_id, warning_type, warned_at); CREATE INDEX IF NOT EXISTS idx_antiraid_warnings ON antiraid_warnings(guild_id, user_id, warning_type, warned_at);
-- Table des warns utilisateurs
CREATE TABLE IF NOT EXISTS warnings ( CREATE TABLE IF NOT EXISTS warnings (
id INTEGER PRIMARY KEY AUTOINCREMENT, id BIGSERIAL PRIMARY KEY,
guild_id TEXT NOT NULL, guild_id TEXT NOT NULL,
user_id TEXT NOT NULL, user_id TEXT NOT NULL,
moderator_id TEXT NOT NULL, moderator_id TEXT NOT NULL,
reason TEXT NOT NULL, reason TEXT NOT NULL,
source TEXT DEFAULT 'manual', source TEXT DEFAULT 'manual',
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) created_at BIGINT NOT NULL DEFAULT (EXTRACT(EPOCH FROM NOW())::BIGINT)
); );
CREATE INDEX IF NOT EXISTS idx_warnings ON warnings(guild_id, user_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_warnings ON warnings(guild_id, user_id, created_at DESC);
-- Configuration des sanctions automatiques par warns
CREATE TABLE IF NOT EXISTS warnings_config ( CREATE TABLE IF NOT EXISTS warnings_config (
guild_id TEXT PRIMARY KEY, guild_id TEXT PRIMARY KEY,
enabled INTEGER NOT NULL DEFAULT 0, enabled INTEGER NOT NULL DEFAULT 0,
@@ -411,7 +393,7 @@ db.exec(`
); );
CREATE TABLE IF NOT EXISTS scheduled_messages ( CREATE TABLE IF NOT EXISTS scheduled_messages (
id INTEGER PRIMARY KEY AUTOINCREMENT, id BIGSERIAL PRIMARY KEY,
guild_id TEXT NOT NULL, guild_id TEXT NOT NULL,
channel_id TEXT NOT NULL, channel_id TEXT NOT NULL,
message_content TEXT NOT NULL, message_content TEXT NOT NULL,
@@ -427,15 +409,14 @@ db.exec(`
force_send INTEGER NOT NULL DEFAULT 1, force_send INTEGER NOT NULL DEFAULT 1,
delete_previous INTEGER NOT NULL DEFAULT 0, delete_previous INTEGER NOT NULL DEFAULT 0,
enabled INTEGER NOT NULL DEFAULT 1, enabled INTEGER NOT NULL DEFAULT 1,
last_sent_at INTEGER, last_sent_at BIGINT,
last_message_id TEXT, last_message_id TEXT,
last_channel_activity INTEGER, last_channel_activity BIGINT,
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) created_at BIGINT NOT NULL DEFAULT (EXTRACT(EPOCH FROM NOW())::BIGINT)
); );
-- Système de rôles par boutons
CREATE TABLE IF NOT EXISTS role_panels ( CREATE TABLE IF NOT EXISTS role_panels (
id INTEGER PRIMARY KEY AUTOINCREMENT, id BIGSERIAL PRIMARY KEY,
guild_id TEXT NOT NULL, guild_id TEXT NOT NULL,
channel_id TEXT NOT NULL, channel_id TEXT NOT NULL,
message_id TEXT, message_id TEXT,
@@ -449,12 +430,12 @@ db.exec(`
exclusive INTEGER NOT NULL DEFAULT 0, exclusive INTEGER NOT NULL DEFAULT 0,
required_role_id TEXT, required_role_id TEXT,
enabled INTEGER NOT NULL DEFAULT 1, enabled INTEGER NOT NULL DEFAULT 1,
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) created_at BIGINT NOT NULL DEFAULT (EXTRACT(EPOCH FROM NOW())::BIGINT)
); );
CREATE TABLE IF NOT EXISTS role_panel_buttons ( CREATE TABLE IF NOT EXISTS role_panel_buttons (
id INTEGER PRIMARY KEY AUTOINCREMENT, id BIGSERIAL PRIMARY KEY,
panel_id INTEGER NOT NULL, panel_id BIGINT NOT NULL,
role_id TEXT NOT NULL, role_id TEXT NOT NULL,
label TEXT NOT NULL, label TEXT NOT NULL,
emoji TEXT, emoji TEXT,
@@ -463,60 +444,181 @@ db.exec(`
enabled INTEGER NOT NULL DEFAULT 1, enabled INTEGER NOT NULL DEFAULT 1,
FOREIGN KEY (panel_id) REFERENCES role_panels(id) ON DELETE CASCADE FOREIGN KEY (panel_id) REFERENCES role_panels(id) ON DELETE CASCADE
); );
`);
// Migration: Ajouter les nouvelles colonnes pour welcome/goodbye si elles n'existent pas ALTER TABLE IF EXISTS welcome_config ADD COLUMN IF NOT EXISTS message_type TEXT NOT NULL DEFAULT 'embed';
const migrateWelcomeGoodbye = () => { ALTER TABLE IF EXISTS welcome_config ADD COLUMN IF NOT EXISTS embed_title TEXT;
// Colonnes à ajouter pour welcome_config ALTER TABLE IF EXISTS welcome_config ADD COLUMN IF NOT EXISTS embed_description TEXT;
const welcomeColumns = [ ALTER TABLE IF EXISTS welcome_config ADD COLUMN IF NOT EXISTS embed_color TEXT DEFAULT '#57F287';
{ name: 'message_type', sql: "ALTER TABLE welcome_config ADD COLUMN message_type TEXT NOT NULL DEFAULT 'embed'" }, ALTER TABLE IF EXISTS welcome_config ADD COLUMN IF NOT EXISTS embed_thumbnail INTEGER NOT NULL DEFAULT 1;
{ name: 'embed_title', sql: "ALTER TABLE welcome_config ADD COLUMN embed_title TEXT" }, ALTER TABLE IF EXISTS welcome_config ADD COLUMN IF NOT EXISTS embed_footer TEXT;
{ name: 'embed_description', sql: "ALTER TABLE welcome_config ADD COLUMN embed_description TEXT" }, ALTER TABLE IF EXISTS welcome_config ADD COLUMN IF NOT EXISTS image_enabled INTEGER NOT NULL DEFAULT 0;
{ name: 'embed_color', sql: "ALTER TABLE welcome_config ADD COLUMN embed_color TEXT DEFAULT '#57F287'" }, ALTER TABLE IF EXISTS welcome_config ADD COLUMN IF NOT EXISTS image_gradient TEXT DEFAULT 'purple';
{ name: 'embed_thumbnail', sql: "ALTER TABLE welcome_config ADD COLUMN embed_thumbnail INTEGER NOT NULL DEFAULT 1" }, ALTER TABLE IF EXISTS welcome_config ADD COLUMN IF NOT EXISTS image_title TEXT;
{ name: 'embed_footer', sql: "ALTER TABLE welcome_config ADD COLUMN embed_footer TEXT" }, ALTER TABLE IF EXISTS welcome_config ADD COLUMN IF NOT EXISTS image_subtitle TEXT;
{ name: 'image_enabled', sql: "ALTER TABLE welcome_config ADD COLUMN image_enabled INTEGER NOT NULL DEFAULT 0" }, ALTER TABLE IF EXISTS welcome_config ADD COLUMN IF NOT EXISTS image_show_member_count INTEGER NOT NULL DEFAULT 1;
{ name: 'image_gradient', sql: "ALTER TABLE welcome_config ADD COLUMN image_gradient TEXT DEFAULT 'purple'" },
{ name: 'image_title', sql: "ALTER TABLE welcome_config ADD COLUMN image_title TEXT" },
{ name: 'image_subtitle', sql: "ALTER TABLE welcome_config ADD COLUMN image_subtitle TEXT" },
{ name: 'image_show_member_count', sql: "ALTER TABLE welcome_config ADD COLUMN image_show_member_count INTEGER NOT NULL DEFAULT 1" }
];
// Colonnes à ajouter pour goodbye_config ALTER TABLE IF EXISTS goodbye_config ADD COLUMN IF NOT EXISTS message_type TEXT NOT NULL DEFAULT 'embed';
const goodbyeColumns = [ ALTER TABLE IF EXISTS goodbye_config ADD COLUMN IF NOT EXISTS embed_title TEXT;
{ name: 'message_type', sql: "ALTER TABLE goodbye_config ADD COLUMN message_type TEXT NOT NULL DEFAULT 'embed'" }, ALTER TABLE IF EXISTS goodbye_config ADD COLUMN IF NOT EXISTS embed_description TEXT;
{ name: 'embed_title', sql: "ALTER TABLE goodbye_config ADD COLUMN embed_title TEXT" }, ALTER TABLE IF EXISTS goodbye_config ADD COLUMN IF NOT EXISTS embed_color TEXT DEFAULT '#ED4245';
{ name: 'embed_description', sql: "ALTER TABLE goodbye_config ADD COLUMN embed_description TEXT" }, ALTER TABLE IF EXISTS goodbye_config ADD COLUMN IF NOT EXISTS embed_thumbnail INTEGER NOT NULL DEFAULT 1;
{ name: 'embed_color', sql: "ALTER TABLE goodbye_config ADD COLUMN embed_color TEXT DEFAULT '#ED4245'" }, ALTER TABLE IF EXISTS goodbye_config ADD COLUMN IF NOT EXISTS embed_footer TEXT;
{ name: 'embed_thumbnail', sql: "ALTER TABLE goodbye_config ADD COLUMN embed_thumbnail INTEGER NOT NULL DEFAULT 1" }, ALTER TABLE IF EXISTS goodbye_config ADD COLUMN IF NOT EXISTS image_enabled INTEGER NOT NULL DEFAULT 0;
{ name: 'embed_footer', sql: "ALTER TABLE goodbye_config ADD COLUMN embed_footer TEXT" }, ALTER TABLE IF EXISTS goodbye_config ADD COLUMN IF NOT EXISTS image_gradient TEXT DEFAULT 'red';
{ name: 'image_enabled', sql: "ALTER TABLE goodbye_config ADD COLUMN image_enabled INTEGER NOT NULL DEFAULT 0" }, ALTER TABLE IF EXISTS goodbye_config ADD COLUMN IF NOT EXISTS image_title TEXT;
{ name: 'image_gradient', sql: "ALTER TABLE goodbye_config ADD COLUMN image_gradient TEXT DEFAULT 'red'" }, ALTER TABLE IF EXISTS goodbye_config ADD COLUMN IF NOT EXISTS image_subtitle TEXT;
{ name: 'image_title', sql: "ALTER TABLE goodbye_config ADD COLUMN image_title TEXT" }, ALTER TABLE IF EXISTS goodbye_config ADD COLUMN IF NOT EXISTS image_show_member_count INTEGER NOT NULL DEFAULT 1;
{ name: 'image_subtitle', sql: "ALTER TABLE goodbye_config ADD COLUMN image_subtitle TEXT" }, `;
{ name: 'image_show_member_count', sql: "ALTER TABLE goodbye_config ADD COLUMN image_show_member_count INTEGER NOT NULL DEFAULT 1" }
];
// Exécuter les migrations pour welcome_config function convertPlaceholders(sql) {
welcomeColumns.forEach(col => { let index = 1;
db.run(col.sql, (err) => { let out = "";
if (err && !err.message.includes('duplicate column')) { let inSingleQuote = false;
// Ignorer les erreurs de colonnes dupliquées
for (let i = 0; i < sql.length; i += 1) {
const char = sql[i];
if (char === "'") {
if (inSingleQuote && sql[i + 1] === "'") {
out += "''";
i += 1;
continue;
} }
}); inSingleQuote = !inSingleQuote;
}); out += char;
continue;
}
// Exécuter les migrations pour goodbye_config if (!inSingleQuote && char === "?") {
goodbyeColumns.forEach(col => { out += `$${index}`;
db.run(col.sql, (err) => { index += 1;
if (err && !err.message.includes('duplicate column')) { continue;
// Ignorer les erreurs de colonnes dupliquées }
}
}); out += char;
}); }
return out;
}
function normalizeArgs(params, callback) {
if (typeof params === "function") {
return { params: [], callback: params };
}
return { params: Array.isArray(params) ? params : [], callback };
}
function maybeAddReturningId(sql) {
if (/\breturning\b/i.test(sql)) {
return sql;
}
const match = sql.match(/^\s*INSERT\s+INTO\s+([A-Za-z_][A-Za-z0-9_]*)/i);
if (!match) {
return sql;
}
const tableName = match[1].toLowerCase();
if (!TABLES_WITH_ID.has(tableName)) {
return sql;
}
return `${sql.trim()} RETURNING id`;
}
const db = {
async exec(sql, callback) {
try {
await pool.query(sql);
if (callback) callback(null);
} catch (err) {
if (callback) callback(err);
else throw err;
}
},
run(sql, params, callback) {
const normalized = normalizeArgs(params, callback);
const sqlWithReturning = maybeAddReturningId(convertPlaceholders(sql));
pool
.query(sqlWithReturning, normalized.params)
.then((result) => {
const stmt = {
lastID: result.rows[0] ? result.rows[0].id : undefined,
changes: result.rowCount,
};
if (normalized.callback) {
normalized.callback.call(stmt, null);
}
})
.catch((err) => {
if (normalized.callback) {
normalized.callback.call({ lastID: undefined, changes: 0 }, err);
return;
}
console.error("Erreur SQL (run):", err);
});
},
get(sql, params, callback) {
const normalized = normalizeArgs(params, callback);
pool
.query(convertPlaceholders(sql), normalized.params)
.then((result) => {
if (normalized.callback) {
normalized.callback(null, result.rows[0]);
}
})
.catch((err) => {
if (normalized.callback) {
normalized.callback(err, undefined);
return;
}
console.error("Erreur SQL (get):", err);
});
},
all(sql, params, callback) {
const normalized = normalizeArgs(params, callback);
pool
.query(convertPlaceholders(sql), normalized.params)
.then((result) => {
if (normalized.callback) {
normalized.callback(null, result.rows);
}
})
.catch((err) => {
if (normalized.callback) {
normalized.callback(err, []);
return;
}
console.error("Erreur SQL (all):", err);
});
},
getAsync(sql, params = []) {
return pool.query(convertPlaceholders(sql), params).then((result) => result.rows[0]);
},
allAsync(sql, params = []) {
return pool.query(convertPlaceholders(sql), params).then((result) => result.rows);
},
pool,
}; };
// Exécuter la migration (async () => {
migrateWelcomeGoodbye(); try {
await pool.query("SELECT 1");
console.log("DB PostgreSQL connectee");
await pool.query(schemaSql);
} catch (err) {
console.error("Erreur DB:", err);
}
})();
module.exports = db; module.exports = db;
+3 -2
View File
@@ -238,8 +238,9 @@ async function trackVoiceTime(guildId, userId, oldState, newState) {
if (newState.channelId && !oldState.channelId) { if (newState.channelId && !oldState.channelId) {
// Save join timestamp // Save join timestamp
db.run( db.run(
`INSERT OR REPLACE INTO voice_sessions (guild_id, user_id, join_timestamp) `INSERT INTO voice_sessions (guild_id, user_id, join_timestamp)
VALUES (?, ?, ?)`, VALUES (?, ?, ?)
ON CONFLICT (guild_id, user_id) DO UPDATE SET join_timestamp = EXCLUDED.join_timestamp`,
[guildId, userId, Date.now()] [guildId, userId, Date.now()]
); );
} }
+152 -595
View File
@@ -11,13 +11,13 @@
"dependencies": { "dependencies": {
"canvas": "^3.2.1", "canvas": "^3.2.1",
"chartjs-node-canvas": "^4.1.6", "chartjs-node-canvas": "^4.1.6",
"connect-sqlite3": "^0.9.16", "connect-pg-simple": "^10.0.0",
"cross-fetch": "^4.1.0", "cross-fetch": "^4.1.0",
"discord.js": "^14.25.1", "discord.js": "^14.25.1",
"dotenv": "^17.2.3", "dotenv": "^17.2.3",
"express": "^5.2.1", "express": "^5.2.1",
"express-session": "^1.18.2", "express-session": "^1.18.2",
"sqlite3": "^5.1.7" "pg": "^8.16.3"
} }
}, },
"node_modules/@discordjs/builders": { "node_modules/@discordjs/builders": {
@@ -150,13 +150,6 @@
"url": "https://github.com/discordjs/discord.js?sponsor" "url": "https://github.com/discordjs/discord.js?sponsor"
} }
}, },
"node_modules/@gar/promisify": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz",
"integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==",
"license": "MIT",
"optional": true
},
"node_modules/@mapbox/node-pre-gyp": { "node_modules/@mapbox/node-pre-gyp": {
"version": "1.0.11", "version": "1.0.11",
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
@@ -225,32 +218,6 @@
"set-blocking": "^2.0.0" "set-blocking": "^2.0.0"
} }
}, },
"node_modules/@npmcli/fs": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz",
"integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==",
"license": "ISC",
"optional": true,
"dependencies": {
"@gar/promisify": "^1.0.1",
"semver": "^7.3.5"
}
},
"node_modules/@npmcli/move-file": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz",
"integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==",
"deprecated": "This functionality has been moved to @npmcli/fs",
"license": "MIT",
"optional": true,
"dependencies": {
"mkdirp": "^1.0.4",
"rimraf": "^3.0.2"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@sapphire/async-queue": { "node_modules/@sapphire/async-queue": {
"version": "1.5.5", "version": "1.5.5",
"resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.5.tgz", "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.5.tgz",
@@ -284,16 +251,6 @@
"npm": ">=7.0.0" "npm": ">=7.0.0"
} }
}, },
"node_modules/@tootallnate/once": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
"integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
"license": "MIT",
"optional": true,
"engines": {
"node": ">= 6"
}
},
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "25.0.8", "version": "25.0.8",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.8.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.8.tgz",
@@ -353,33 +310,6 @@
"node": ">= 6.0.0" "node": ">= 6.0.0"
} }
}, },
"node_modules/agentkeepalive": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz",
"integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==",
"license": "MIT",
"optional": true,
"dependencies": {
"humanize-ms": "^1.2.1"
},
"engines": {
"node": ">= 8.0.0"
}
},
"node_modules/aggregate-error": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
"integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
"license": "MIT",
"optional": true,
"dependencies": {
"clean-stack": "^2.0.0",
"indent-string": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/ansi-regex": { "node_modules/ansi-regex": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
@@ -395,21 +325,6 @@
"integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==", "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/are-we-there-yet": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz",
"integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==",
"deprecated": "This package is no longer supported.",
"license": "ISC",
"optional": true,
"dependencies": {
"delegates": "^1.0.0",
"readable-stream": "^3.6.0"
},
"engines": {
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
}
},
"node_modules/balanced-match": { "node_modules/balanced-match": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -436,15 +351,6 @@
], ],
"license": "MIT" "license": "MIT"
}, },
"node_modules/bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"license": "MIT",
"dependencies": {
"file-uri-to-path": "1.0.0"
}
},
"node_modules/bl": { "node_modules/bl": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
@@ -523,36 +429,6 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/cacache": {
"version": "15.3.0",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz",
"integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==",
"license": "ISC",
"optional": true,
"dependencies": {
"@npmcli/fs": "^1.0.0",
"@npmcli/move-file": "^1.0.1",
"chownr": "^2.0.0",
"fs-minipass": "^2.0.0",
"glob": "^7.1.4",
"infer-owner": "^1.0.4",
"lru-cache": "^6.0.0",
"minipass": "^3.1.1",
"minipass-collect": "^1.0.2",
"minipass-flush": "^1.0.5",
"minipass-pipeline": "^1.2.2",
"mkdirp": "^1.0.3",
"p-map": "^4.0.0",
"promise-inflight": "^1.0.1",
"rimraf": "^3.0.2",
"ssri": "^8.0.1",
"tar": "^6.0.2",
"unique-filename": "^1.1.1"
},
"engines": {
"node": ">= 10"
}
},
"node_modules/call-bind-apply-helpers": { "node_modules/call-bind-apply-helpers": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
@@ -675,16 +551,6 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/clean-stack": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
"integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
"license": "MIT",
"optional": true,
"engines": {
"node": ">=6"
}
},
"node_modules/color-support": { "node_modules/color-support": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
@@ -700,15 +566,16 @@
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/connect-sqlite3": { "node_modules/connect-pg-simple": {
"version": "0.9.16", "version": "10.0.0",
"resolved": "https://registry.npmjs.org/connect-sqlite3/-/connect-sqlite3-0.9.16.tgz", "resolved": "https://registry.npmjs.org/connect-pg-simple/-/connect-pg-simple-10.0.0.tgz",
"integrity": "sha512-2gqo0QmcBBL8p8+eqpBETn7RgM/PaoKvpQGl8PfjEgwlr0VuMYNMxRJRrRCo3KR3fxMYeSsCw2tGNG0JKN9Nvg==", "integrity": "sha512-pBGVazlqiMrackzCr0eKhn4LO5trJXsOX0nQoey9wCOayh80MYtThCbq8eoLsjpiWgiok/h+1/uti9/2/Una8A==",
"license": "MIT",
"dependencies": { "dependencies": {
"sqlite3": "^5.0.2" "pg": "^8.12.0"
}, },
"engines": { "engines": {
"node": ">=0.4.x" "node": "^18.18.0 || ^20.9.0 || >=22.0.0"
} }
}, },
"node_modules/console-control-strings": { "node_modules/console-control-strings": {
@@ -920,6 +787,7 @@
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"peer": true,
"dependencies": { "dependencies": {
"iconv-lite": "^0.6.2" "iconv-lite": "^0.6.2"
} }
@@ -930,6 +798,7 @@
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"peer": true,
"dependencies": { "dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0" "safer-buffer": ">= 2.1.2 < 3.0.0"
}, },
@@ -946,23 +815,6 @@
"once": "^1.4.0" "once": "^1.4.0"
} }
}, },
"node_modules/env-paths": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
"integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
"license": "MIT",
"optional": true,
"engines": {
"node": ">=6"
}
},
"node_modules/err-code": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
"integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
"license": "MIT",
"optional": true
},
"node_modules/es-define-property": { "node_modules/es-define-property": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
@@ -1106,12 +958,6 @@
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
"license": "MIT"
},
"node_modules/finalhandler": { "node_modules/finalhandler": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz",
@@ -1184,27 +1030,6 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/gauge": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz",
"integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==",
"deprecated": "This package is no longer supported.",
"license": "ISC",
"optional": true,
"dependencies": {
"aproba": "^1.0.3 || ^2.0.0",
"color-support": "^1.1.3",
"console-control-strings": "^1.1.0",
"has-unicode": "^2.0.1",
"signal-exit": "^3.0.7",
"string-width": "^4.2.3",
"strip-ansi": "^6.0.1",
"wide-align": "^1.1.5"
},
"engines": {
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
}
},
"node_modules/get-intrinsic": { "node_modules/get-intrinsic": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
@@ -1281,13 +1106,6 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"license": "ISC",
"optional": true
},
"node_modules/has-symbols": { "node_modules/has-symbols": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
@@ -1318,13 +1136,6 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/http-cache-semantics": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
"integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==",
"license": "BSD-2-Clause",
"optional": true
},
"node_modules/http-errors": { "node_modules/http-errors": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
@@ -1345,21 +1156,6 @@
"url": "https://opencollective.com/express" "url": "https://opencollective.com/express"
} }
}, },
"node_modules/http-proxy-agent": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
"integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
"license": "MIT",
"optional": true,
"dependencies": {
"@tootallnate/once": "1",
"agent-base": "6",
"debug": "4"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/https-proxy-agent": { "node_modules/https-proxy-agent": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
@@ -1373,16 +1169,6 @@
"node": ">= 6" "node": ">= 6"
} }
}, },
"node_modules/humanize-ms": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
"integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
"license": "MIT",
"optional": true,
"dependencies": {
"ms": "^2.0.0"
}
},
"node_modules/iconv-lite": { "node_modules/iconv-lite": {
"version": "0.7.2", "version": "0.7.2",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
@@ -1419,33 +1205,6 @@
], ],
"license": "BSD-3-Clause" "license": "BSD-3-Clause"
}, },
"node_modules/imurmurhash": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
"license": "MIT",
"optional": true,
"engines": {
"node": ">=0.8.19"
}
},
"node_modules/indent-string": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
"license": "MIT",
"optional": true,
"engines": {
"node": ">=8"
}
},
"node_modules/infer-owner": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
"integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==",
"license": "ISC",
"optional": true
},
"node_modules/inflight": { "node_modules/inflight": {
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -1469,16 +1228,6 @@
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/ip-address": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
"integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
"license": "MIT",
"optional": true,
"engines": {
"node": ">= 12"
}
},
"node_modules/ipaddr.js": { "node_modules/ipaddr.js": {
"version": "1.9.1", "version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
@@ -1497,26 +1246,12 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/is-lambda": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz",
"integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==",
"license": "MIT",
"optional": true
},
"node_modules/is-promise": { "node_modules/is-promise": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"license": "ISC",
"optional": true
},
"node_modules/lodash": { "node_modules/lodash": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
@@ -1529,19 +1264,6 @@
"integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"license": "ISC",
"optional": true,
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/magic-bytes.js": { "node_modules/magic-bytes.js": {
"version": "1.12.1", "version": "1.12.1",
"resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.12.1.tgz", "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.12.1.tgz",
@@ -1572,44 +1294,6 @@
"semver": "bin/semver.js" "semver": "bin/semver.js"
} }
}, },
"node_modules/make-fetch-happen": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz",
"integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==",
"license": "ISC",
"optional": true,
"dependencies": {
"agentkeepalive": "^4.1.3",
"cacache": "^15.2.0",
"http-cache-semantics": "^4.1.0",
"http-proxy-agent": "^4.0.1",
"https-proxy-agent": "^5.0.0",
"is-lambda": "^1.0.1",
"lru-cache": "^6.0.0",
"minipass": "^3.1.3",
"minipass-collect": "^1.0.2",
"minipass-fetch": "^1.3.2",
"minipass-flush": "^1.0.5",
"minipass-pipeline": "^1.2.4",
"negotiator": "^0.6.2",
"promise-retry": "^2.0.1",
"socks-proxy-agent": "^6.0.0",
"ssri": "^8.0.0"
},
"engines": {
"node": ">= 10"
}
},
"node_modules/make-fetch-happen/node_modules/negotiator": {
"version": "0.6.4",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz",
"integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==",
"license": "MIT",
"optional": true,
"engines": {
"node": ">= 0.6"
}
},
"node_modules/math-intrinsics": { "node_modules/math-intrinsics": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -1710,76 +1394,6 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/minipass-collect": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz",
"integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==",
"license": "ISC",
"optional": true,
"dependencies": {
"minipass": "^3.0.0"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/minipass-fetch": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz",
"integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==",
"license": "MIT",
"optional": true,
"dependencies": {
"minipass": "^3.1.0",
"minipass-sized": "^1.0.3",
"minizlib": "^2.0.0"
},
"engines": {
"node": ">=8"
},
"optionalDependencies": {
"encoding": "^0.1.12"
}
},
"node_modules/minipass-flush": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz",
"integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==",
"license": "ISC",
"optional": true,
"dependencies": {
"minipass": "^3.0.0"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/minipass-pipeline": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz",
"integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==",
"license": "ISC",
"optional": true,
"dependencies": {
"minipass": "^3.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/minipass-sized": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz",
"integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==",
"license": "ISC",
"optional": true,
"dependencies": {
"minipass": "^3.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/minizlib": { "node_modules/minizlib": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
@@ -1876,31 +1490,6 @@
} }
} }
}, },
"node_modules/node-gyp": {
"version": "8.4.1",
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz",
"integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==",
"license": "MIT",
"optional": true,
"dependencies": {
"env-paths": "^2.2.0",
"glob": "^7.1.4",
"graceful-fs": "^4.2.6",
"make-fetch-happen": "^9.1.0",
"nopt": "^5.0.0",
"npmlog": "^6.0.0",
"rimraf": "^3.0.2",
"semver": "^7.3.5",
"tar": "^6.1.2",
"which": "^2.0.2"
},
"bin": {
"node-gyp": "bin/node-gyp.js"
},
"engines": {
"node": ">= 10.12.0"
}
},
"node_modules/nopt": { "node_modules/nopt": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
@@ -1916,23 +1505,6 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/npmlog": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz",
"integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==",
"deprecated": "This package is no longer supported.",
"license": "ISC",
"optional": true,
"dependencies": {
"are-we-there-yet": "^3.0.0",
"console-control-strings": "^1.1.0",
"gauge": "^4.0.3",
"set-blocking": "^2.0.0"
},
"engines": {
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
}
},
"node_modules/object-assign": { "node_modules/object-assign": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -1984,22 +1556,6 @@
"wrappy": "1" "wrappy": "1"
} }
}, },
"node_modules/p-map": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
"integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
"license": "MIT",
"optional": true,
"dependencies": {
"aggregate-error": "^3.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/parseurl": { "node_modules/parseurl": {
"version": "1.3.3", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -2028,6 +1584,134 @@
"url": "https://opencollective.com/express" "url": "https://opencollective.com/express"
} }
}, },
"node_modules/pg": {
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.20.0.tgz",
"integrity": "sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==",
"license": "MIT",
"dependencies": {
"pg-connection-string": "^2.12.0",
"pg-pool": "^3.13.0",
"pg-protocol": "^1.13.0",
"pg-types": "2.2.0",
"pgpass": "1.0.5"
},
"engines": {
"node": ">= 16.0.0"
},
"optionalDependencies": {
"pg-cloudflare": "^1.3.0"
},
"peerDependencies": {
"pg-native": ">=3.0.1"
},
"peerDependenciesMeta": {
"pg-native": {
"optional": true
}
}
},
"node_modules/pg-cloudflare": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.3.0.tgz",
"integrity": "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==",
"license": "MIT",
"optional": true
},
"node_modules/pg-connection-string": {
"version": "2.12.0",
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.12.0.tgz",
"integrity": "sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==",
"license": "MIT"
},
"node_modules/pg-int8": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
"license": "ISC",
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/pg-pool": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.13.0.tgz",
"integrity": "sha512-gB+R+Xud1gLFuRD/QgOIgGOBE2KCQPaPwkzBBGC9oG69pHTkhQeIuejVIk3/cnDyX39av2AxomQiyPT13WKHQA==",
"license": "MIT",
"peerDependencies": {
"pg": ">=8.0"
}
},
"node_modules/pg-protocol": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.13.0.tgz",
"integrity": "sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==",
"license": "MIT"
},
"node_modules/pg-types": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
"license": "MIT",
"dependencies": {
"pg-int8": "1.0.1",
"postgres-array": "~2.0.0",
"postgres-bytea": "~1.0.0",
"postgres-date": "~1.0.4",
"postgres-interval": "^1.1.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/pgpass": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
"license": "MIT",
"dependencies": {
"split2": "^4.1.0"
}
},
"node_modules/postgres-array": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/postgres-bytea": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz",
"integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/postgres-date": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/postgres-interval": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
"license": "MIT",
"dependencies": {
"xtend": "^4.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/prebuild-install": { "node_modules/prebuild-install": {
"version": "7.1.3", "version": "7.1.3",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
@@ -2054,27 +1738,6 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/promise-inflight": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
"integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==",
"license": "ISC",
"optional": true
},
"node_modules/promise-retry": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz",
"integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==",
"license": "MIT",
"optional": true,
"dependencies": {
"err-code": "^2.0.2",
"retry": "^0.12.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/proxy-addr": { "node_modules/proxy-addr": {
"version": "2.0.7", "version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -2175,16 +1838,6 @@
"node": ">= 6" "node": ">= 6"
} }
}, },
"node_modules/retry": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
"integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
"license": "MIT",
"optional": true,
"engines": {
"node": ">= 4"
}
},
"node_modules/rimraf": { "node_modules/rimraf": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
@@ -2435,82 +2088,13 @@
"simple-concat": "^1.0.0" "simple-concat": "^1.0.0"
} }
}, },
"node_modules/smart-buffer": { "node_modules/split2": {
"version": "4.2.0", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
"integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
"license": "MIT",
"optional": true,
"engines": {
"node": ">= 6.0.0",
"npm": ">= 3.0.0"
}
},
"node_modules/socks": {
"version": "2.8.7",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz",
"integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==",
"license": "MIT",
"optional": true,
"dependencies": {
"ip-address": "^10.0.1",
"smart-buffer": "^4.2.0"
},
"engines": {
"node": ">= 10.0.0",
"npm": ">= 3.0.0"
}
},
"node_modules/socks-proxy-agent": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz",
"integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==",
"license": "MIT",
"optional": true,
"dependencies": {
"agent-base": "^6.0.2",
"debug": "^4.3.3",
"socks": "^2.6.2"
},
"engines": {
"node": ">= 10"
}
},
"node_modules/sqlite3": {
"version": "5.1.7",
"resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz",
"integrity": "sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==",
"hasInstallScript": true,
"license": "BSD-3-Clause",
"dependencies": {
"bindings": "^1.5.0",
"node-addon-api": "^7.0.0",
"prebuild-install": "^7.1.1",
"tar": "^6.1.11"
},
"optionalDependencies": {
"node-gyp": "8.x"
},
"peerDependencies": {
"node-gyp": "8.x"
},
"peerDependenciesMeta": {
"node-gyp": {
"optional": true
}
}
},
"node_modules/ssri": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz",
"integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==",
"license": "ISC", "license": "ISC",
"optional": true,
"dependencies": {
"minipass": "^3.1.1"
},
"engines": { "engines": {
"node": ">= 8" "node": ">= 10.x"
} }
}, },
"node_modules/statuses": { "node_modules/statuses": {
@@ -2706,26 +2290,6 @@
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/unique-filename": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
"integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==",
"license": "ISC",
"optional": true,
"dependencies": {
"unique-slug": "^2.0.0"
}
},
"node_modules/unique-slug": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz",
"integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==",
"license": "ISC",
"optional": true,
"dependencies": {
"imurmurhash": "^0.1.4"
}
},
"node_modules/unpipe": { "node_modules/unpipe": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
@@ -2766,22 +2330,6 @@
"webidl-conversions": "^3.0.0" "webidl-conversions": "^3.0.0"
} }
}, },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"license": "ISC",
"optional": true,
"dependencies": {
"isexe": "^2.0.0"
},
"bin": {
"node-which": "bin/node-which"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/wide-align": { "node_modules/wide-align": {
"version": "1.1.5", "version": "1.1.5",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
@@ -2818,6 +2366,15 @@
} }
} }
}, },
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"license": "MIT",
"engines": {
"node": ">=0.4"
}
},
"node_modules/yallist": { "node_modules/yallist": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+2 -2
View File
@@ -25,12 +25,12 @@
"dependencies": { "dependencies": {
"canvas": "^3.2.1", "canvas": "^3.2.1",
"chartjs-node-canvas": "^4.1.6", "chartjs-node-canvas": "^4.1.6",
"connect-sqlite3": "^0.9.16", "connect-pg-simple": "^10.0.0",
"cross-fetch": "^4.1.0", "cross-fetch": "^4.1.0",
"discord.js": "^14.25.1", "discord.js": "^14.25.1",
"dotenv": "^17.2.3", "dotenv": "^17.2.3",
"express": "^5.2.1", "express": "^5.2.1",
"express-session": "^1.18.2", "express-session": "^1.18.2",
"sqlite3": "^5.1.7" "pg": "^8.16.3"
} }
} }
+1 -1
View File
@@ -85,7 +85,7 @@
<section class="tos-section"> <section class="tos-section">
<h2>4. Stockage et sécurité</h2> <h2>4. Stockage et sécurité</h2>
<ul> <ul>
<li>Les données sont stockées dans une base de données SQLite sécurisée</li> <li>Les données sont stockées dans une base de données PostgreSQL sécurisée</li>
<li>L'accès aux données est limité et protégé</li> <li>L'accès aux données est limité et protégé</li>
<li>Les sessions du dashboard utilisent des cookies sécurisés</li> <li>Les sessions du dashboard utilisent des cookies sécurisés</li>
<li>L'authentification passe par OAuth2 de Discord (nous ne stockons pas votre mot de passe)</li> <li>L'authentification passe par OAuth2 de Discord (nous ne stockons pas votre mot de passe)</li>
+6 -2
View File
@@ -2,7 +2,7 @@ require("dotenv").config(); // charge les variables depuis .env
const express = require("express"); const express = require("express");
const session = require("express-session"); const session = require("express-session");
const SQLiteStore = require("connect-sqlite3")(session); const PgSession = require("connect-pg-simple")(session);
const fetch = require("cross-fetch"); // fetch compatible Node const fetch = require("cross-fetch"); // fetch compatible Node
const path = require("path"); const path = require("path");
@@ -21,7 +21,11 @@ const REDIRECT_URI = process.env.REDIRECT_URI;
// --- Session setup --- // --- Session setup ---
app.use(session({ app.use(session({
store: new SQLiteStore({ db: "sessions.sqlite", dir: "./" }), store: new PgSession({
pool: db.pool,
tableName: "user_sessions",
createTableIfMissing: true
}),
secret: process.env.SESSION_SECRET, secret: process.env.SESSION_SECRET,
resave: false, resave: false,
saveUninitialized: false, saveUninitialized: false,
+23 -9
View File
@@ -1,13 +1,27 @@
services: services:
web: web:
image: node:20 build: .
container_name: discord_bot_web image: lazybot:latest
working_dir: /app
volumes:
- ./app:/app
ports:
- "${PORT}:${PORT}"
env_file: env_file:
- ./.env - ./.env
command: sh -c "npm install && node server.js" ports:
restart: unless-stopped - "${PORT:-3000}:${PORT:-3000}"
depends_on:
db:
condition: service_healthy
restart: unless-stopped
db:
image: postgres:16-alpine
env_file:
- ./.env
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-lazybot} -d ${POSTGRES_DB:-lazybot}"]
interval: 10s
timeout: 5s
retries: 5
volumes:
- ${POSTGRES_DATA_PATH:-./data/postgres}:/var/lib/postgresql/data
restart: unless-stopped
volumes: {}