Remove local database connections and use Supabase exclusively

Updates bot.js to remove PostgreSQL pool setup and pg dependency, switching all command logging and analytics to use Supabase.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: aed2e46d-25bb-4b73-81a1-bb9e8437c261
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: a88f9025-8fdd-4882-8793-e15b62c35cda
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3bdfff67-975a-46ad-9845-fbb6b4a4c4b5/aed2e46d-25bb-4b73-81a1-bb9e8437c261/7aeQEfK
Replit-Helium-Checkpoint-Created: true
This commit is contained in:
sirpiglr 2025-12-08 17:17:41 +00:00
parent 734e2f251f
commit cf175b87e9
2 changed files with 95 additions and 117 deletions

View file

@ -22,10 +22,6 @@ externalPort = 80
localPort = 8080 localPort = 8080
externalPort = 8080 externalPort = 8080
[[ports]]
localPort = 33401
externalPort = 3000
[workflows] [workflows]
runButton = "Project" runButton = "Project"

View file

@ -12,7 +12,6 @@ const { createClient } = require("@supabase/supabase-js");
const http = require("http"); const http = require("http");
const fs = require("fs"); const fs = require("fs");
const path = require("path"); const path = require("path");
const { Pool } = require("pg");
const WebSocket = require("ws"); const WebSocket = require("ws");
// Dashboard HTML path // Dashboard HTML path
@ -69,97 +68,80 @@ if (process.env.SUPABASE_URL && process.env.SUPABASE_SERVICE_ROLE) {
} }
// ============================================================================= // =============================================================================
// POSTGRESQL DATABASE SETUP (Local database for analytics & config) // COMMAND LOGGING SYSTEM (Supabase-based)
// =============================================================================
let pgPool = null;
if (process.env.DATABASE_URL) {
pgPool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 10,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
});
pgPool.on('error', (err) => {
console.error('PostgreSQL pool error:', err.message);
});
console.log("PostgreSQL connected");
} else {
console.log("PostgreSQL not configured - using in-memory storage");
}
// =============================================================================
// COMMAND LOGGING SYSTEM
// ============================================================================= // =============================================================================
async function logCommand(data) { async function logCommand(data) {
if (!pgPool) return; if (!supabase) return;
try { try {
await pgPool.query( await supabase.from('command_logs').insert({
`INSERT INTO command_logs (command_name, user_id, user_tag, guild_id, guild_name, channel_id, success, error_message, execution_time_ms) command_name: data.commandName,
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`, user_id: data.userId,
[ user_tag: data.userTag,
data.commandName, guild_id: data.guildId,
data.userId, guild_name: data.guildName,
data.userTag, channel_id: data.channelId,
data.guildId, success: data.success,
data.guildName, error_message: data.errorMessage || null,
data.channelId, execution_time_ms: data.executionTime || null
data.success, });
data.errorMessage || null,
data.executionTime || null
]
);
} catch (err) { } catch (err) {
console.error('Failed to log command:', err.message); console.error('Failed to log command:', err.message);
} }
} }
async function getCommandAnalytics(days = 7) { async function getCommandAnalytics(days = 7) {
if (!pgPool) return { commands: [], hourly: [], daily: [], topUsers: [] }; if (!supabase) return { commands: [], hourly: [], daily: [], topUsers: [] };
try { try {
const commandsResult = await pgPool.query( const cutoffDate = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
`SELECT command_name, COUNT(*) as count,
SUM(CASE WHEN success THEN 1 ELSE 0 END) as success_count,
AVG(execution_time_ms) as avg_time
FROM command_logs
WHERE created_at > NOW() - INTERVAL '${days} days'
GROUP BY command_name
ORDER BY count DESC
LIMIT 20`
);
const hourlyResult = await pgPool.query( const { data: logs } = await supabase
`SELECT EXTRACT(HOUR FROM created_at) as hour, COUNT(*) as count .from('command_logs')
FROM command_logs .select('*')
WHERE created_at > NOW() - INTERVAL '24 hours' .gte('created_at', cutoffDate);
GROUP BY hour
ORDER BY hour`
);
const dailyResult = await pgPool.query( if (!logs) return { commands: [], hourly: [], daily: [], topUsers: [] };
`SELECT DATE(created_at) as date, COUNT(*) as count
FROM command_logs
WHERE created_at > NOW() - INTERVAL '${days} days'
GROUP BY date
ORDER BY date`
);
const topUsersResult = await pgPool.query( // Calculate command usage
`SELECT user_id, user_tag, COUNT(*) as command_count const commandCounts = {};
FROM command_logs const userCounts = {};
WHERE created_at > NOW() - INTERVAL '${days} days' const hourlyActivity = Array(24).fill(0);
GROUP BY user_id, user_tag const dailyActivity = {};
ORDER BY command_count DESC
LIMIT 10`
);
return { for (const log of logs) {
commands: commandsResult.rows, // Command counts
hourly: hourlyResult.rows, commandCounts[log.command_name] = (commandCounts[log.command_name] || 0) + 1;
daily: dailyResult.rows,
topUsers: topUsersResult.rows // User counts
}; const userKey = `${log.user_id}|${log.user_tag}`;
userCounts[userKey] = (userCounts[userKey] || 0) + 1;
// Hourly
const hour = new Date(log.created_at).getHours();
hourlyActivity[hour]++;
// Daily
const dateKey = new Date(log.created_at).toISOString().split('T')[0];
dailyActivity[dateKey] = (dailyActivity[dateKey] || 0) + 1;
}
const commands = Object.entries(commandCounts)
.map(([name, count]) => ({ command_name: name, count }))
.sort((a, b) => b.count - a.count)
.slice(0, 20);
const topUsers = Object.entries(userCounts)
.map(([key, count]) => {
const [user_id, user_tag] = key.split('|');
return { user_id, user_tag, command_count: count };
})
.sort((a, b) => b.command_count - a.command_count)
.slice(0, 10);
const hourly = hourlyActivity.map((count, hour) => ({ hour, count }));
const daily = Object.entries(dailyActivity).map(([date, count]) => ({ date, count }));
return { commands, hourly, daily, topUsers };
} catch (err) { } catch (err) {
console.error('Failed to get command analytics:', err.message); console.error('Failed to get command analytics:', err.message);
return { commands: [], hourly: [], daily: [], topUsers: [] }; return { commands: [], hourly: [], daily: [], topUsers: [] };
@ -167,33 +149,29 @@ async function getCommandAnalytics(days = 7) {
} }
async function getTotalCommandCount() { async function getTotalCommandCount() {
if (!pgPool) return 0; if (!supabase) return 0;
try { try {
const result = await pgPool.query('SELECT COUNT(*) as count FROM command_logs'); const { count } = await supabase.from('command_logs').select('*', { count: 'exact', head: true });
return parseInt(result.rows[0].count) || 0; return count || 0;
} catch (err) { } catch (err) {
return 0; return 0;
} }
} }
// PostgreSQL-based server config functions // Supabase-based server config functions
async function saveServerConfigToDB(guildId, config) { async function saveServerConfigToDB(guildId, config) {
if (!pgPool) return false; if (!supabase) return false;
try { try {
await pgPool.query( await supabase.from('server_config').upsert({
`INSERT INTO server_config (guild_id, welcome_channel, goodbye_channel, modlog_channel, level_up_channel, auto_role, verified_role, updated_at) guild_id: guildId,
VALUES ($1, $2, $3, $4, $5, $6, $7, NOW()) welcome_channel: config.welcome_channel,
ON CONFLICT (guild_id) DO UPDATE SET goodbye_channel: config.goodbye_channel,
welcome_channel = EXCLUDED.welcome_channel, modlog_channel: config.modlog_channel,
goodbye_channel = EXCLUDED.goodbye_channel, level_up_channel: config.level_up_channel,
modlog_channel = EXCLUDED.modlog_channel, auto_role: config.auto_role,
level_up_channel = EXCLUDED.level_up_channel, verified_role: config.verified_role,
auto_role = EXCLUDED.auto_role, updated_at: new Date().toISOString()
verified_role = EXCLUDED.verified_role, });
updated_at = NOW()`,
[guildId, config.welcome_channel, config.goodbye_channel, config.modlog_channel,
config.level_up_channel, config.auto_role, config.verified_role]
);
return true; return true;
} catch (err) { } catch (err) {
console.error('Failed to save server config:', err.message); console.error('Failed to save server config:', err.message);
@ -202,29 +180,30 @@ async function saveServerConfigToDB(guildId, config) {
} }
async function getServerConfigFromDB(guildId) { async function getServerConfigFromDB(guildId) {
if (!pgPool) return null; if (!supabase) return null;
try { try {
const result = await pgPool.query( const { data } = await supabase
'SELECT * FROM server_config WHERE guild_id = $1', .from('server_config')
[guildId] .select('*')
); .eq('guild_id', guildId)
return result.rows[0] || null; .single();
return data || null;
} catch (err) { } catch (err) {
console.error('Failed to get server config:', err.message); console.error('Failed to get server config:', err.message);
return null; return null;
} }
} }
// Federation mappings with PostgreSQL // Federation mappings with Supabase
async function saveFederationMappingToDB(guildId, roleId, roleName) { async function saveFederationMappingToDB(guildId, roleId, roleName) {
if (!pgPool) return false; if (!supabase) return false;
try { try {
await pgPool.query( await supabase.from('federation_mappings').upsert({
`INSERT INTO federation_mappings (guild_id, role_id, role_name, linked_at) guild_id: guildId,
VALUES ($1, $2, $3, NOW()) role_id: roleId,
ON CONFLICT (guild_id, role_id) DO UPDATE SET role_name = EXCLUDED.role_name`, role_name: roleName,
[guildId, roleId, roleName] linked_at: new Date().toISOString()
); });
return true; return true;
} catch (err) { } catch (err) {
console.error('Failed to save federation mapping:', err.message); console.error('Failed to save federation mapping:', err.message);
@ -233,10 +212,13 @@ async function saveFederationMappingToDB(guildId, roleId, roleName) {
} }
async function getFederationMappingsFromDB() { async function getFederationMappingsFromDB() {
if (!pgPool) return []; if (!supabase) return [];
try { try {
const result = await pgPool.query('SELECT * FROM federation_mappings ORDER BY linked_at DESC'); const { data } = await supabase
return result.rows; .from('federation_mappings')
.select('*')
.order('linked_at', { ascending: false });
return data || [];
} catch (err) { } catch (err) {
console.error('Failed to get federation mappings:', err.message); console.error('Failed to get federation mappings:', err.message);
return []; return [];