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
externalPort = 8080
[[ports]]
localPort = 33401
externalPort = 3000
[workflows]
runButton = "Project"

View file

@ -12,7 +12,6 @@ const { createClient } = require("@supabase/supabase-js");
const http = require("http");
const fs = require("fs");
const path = require("path");
const { Pool } = require("pg");
const WebSocket = require("ws");
// 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)
// =============================================================================
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
// COMMAND LOGGING SYSTEM (Supabase-based)
// =============================================================================
async function logCommand(data) {
if (!pgPool) return;
if (!supabase) return;
try {
await pgPool.query(
`INSERT INTO command_logs (command_name, user_id, user_tag, guild_id, guild_name, channel_id, success, error_message, execution_time_ms)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`,
[
data.commandName,
data.userId,
data.userTag,
data.guildId,
data.guildName,
data.channelId,
data.success,
data.errorMessage || null,
data.executionTime || null
]
);
await supabase.from('command_logs').insert({
command_name: data.commandName,
user_id: data.userId,
user_tag: data.userTag,
guild_id: data.guildId,
guild_name: data.guildName,
channel_id: data.channelId,
success: data.success,
error_message: data.errorMessage || null,
execution_time_ms: data.executionTime || null
});
} catch (err) {
console.error('Failed to log command:', err.message);
}
}
async function getCommandAnalytics(days = 7) {
if (!pgPool) return { commands: [], hourly: [], daily: [], topUsers: [] };
if (!supabase) return { commands: [], hourly: [], daily: [], topUsers: [] };
try {
const commandsResult = await pgPool.query(
`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 cutoffDate = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
const hourlyResult = await pgPool.query(
`SELECT EXTRACT(HOUR FROM created_at) as hour, COUNT(*) as count
FROM command_logs
WHERE created_at > NOW() - INTERVAL '24 hours'
GROUP BY hour
ORDER BY hour`
);
const { data: logs } = await supabase
.from('command_logs')
.select('*')
.gte('created_at', cutoffDate);
const dailyResult = await pgPool.query(
`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`
);
if (!logs) return { commands: [], hourly: [], daily: [], topUsers: [] };
const topUsersResult = await pgPool.query(
`SELECT user_id, user_tag, COUNT(*) as command_count
FROM command_logs
WHERE created_at > NOW() - INTERVAL '${days} days'
GROUP BY user_id, user_tag
ORDER BY command_count DESC
LIMIT 10`
);
// Calculate command usage
const commandCounts = {};
const userCounts = {};
const hourlyActivity = Array(24).fill(0);
const dailyActivity = {};
return {
commands: commandsResult.rows,
hourly: hourlyResult.rows,
daily: dailyResult.rows,
topUsers: topUsersResult.rows
};
for (const log of logs) {
// Command counts
commandCounts[log.command_name] = (commandCounts[log.command_name] || 0) + 1;
// 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) {
console.error('Failed to get command analytics:', err.message);
return { commands: [], hourly: [], daily: [], topUsers: [] };
@ -167,33 +149,29 @@ async function getCommandAnalytics(days = 7) {
}
async function getTotalCommandCount() {
if (!pgPool) return 0;
if (!supabase) return 0;
try {
const result = await pgPool.query('SELECT COUNT(*) as count FROM command_logs');
return parseInt(result.rows[0].count) || 0;
const { count } = await supabase.from('command_logs').select('*', { count: 'exact', head: true });
return count || 0;
} catch (err) {
return 0;
}
}
// PostgreSQL-based server config functions
// Supabase-based server config functions
async function saveServerConfigToDB(guildId, config) {
if (!pgPool) return false;
if (!supabase) return false;
try {
await pgPool.query(
`INSERT INTO server_config (guild_id, welcome_channel, goodbye_channel, modlog_channel, level_up_channel, auto_role, verified_role, updated_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, NOW())
ON CONFLICT (guild_id) DO UPDATE SET
welcome_channel = EXCLUDED.welcome_channel,
goodbye_channel = EXCLUDED.goodbye_channel,
modlog_channel = EXCLUDED.modlog_channel,
level_up_channel = EXCLUDED.level_up_channel,
auto_role = EXCLUDED.auto_role,
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]
);
await supabase.from('server_config').upsert({
guild_id: guildId,
welcome_channel: config.welcome_channel,
goodbye_channel: config.goodbye_channel,
modlog_channel: config.modlog_channel,
level_up_channel: config.level_up_channel,
auto_role: config.auto_role,
verified_role: config.verified_role,
updated_at: new Date().toISOString()
});
return true;
} catch (err) {
console.error('Failed to save server config:', err.message);
@ -202,29 +180,30 @@ async function saveServerConfigToDB(guildId, config) {
}
async function getServerConfigFromDB(guildId) {
if (!pgPool) return null;
if (!supabase) return null;
try {
const result = await pgPool.query(
'SELECT * FROM server_config WHERE guild_id = $1',
[guildId]
);
return result.rows[0] || null;
const { data } = await supabase
.from('server_config')
.select('*')
.eq('guild_id', guildId)
.single();
return data || null;
} catch (err) {
console.error('Failed to get server config:', err.message);
return null;
}
}
// Federation mappings with PostgreSQL
// Federation mappings with Supabase
async function saveFederationMappingToDB(guildId, roleId, roleName) {
if (!pgPool) return false;
if (!supabase) return false;
try {
await pgPool.query(
`INSERT INTO federation_mappings (guild_id, role_id, role_name, linked_at)
VALUES ($1, $2, $3, NOW())
ON CONFLICT (guild_id, role_id) DO UPDATE SET role_name = EXCLUDED.role_name`,
[guildId, roleId, roleName]
);
await supabase.from('federation_mappings').upsert({
guild_id: guildId,
role_id: roleId,
role_name: roleName,
linked_at: new Date().toISOString()
});
return true;
} catch (err) {
console.error('Failed to save federation mapping:', err.message);
@ -233,10 +212,13 @@ async function saveFederationMappingToDB(guildId, roleId, roleName) {
}
async function getFederationMappingsFromDB() {
if (!pgPool) return [];
if (!supabase) return [];
try {
const result = await pgPool.query('SELECT * FROM federation_mappings ORDER BY linked_at DESC');
return result.rows;
const { data } = await supabase
.from('federation_mappings')
.select('*')
.order('linked_at', { ascending: false });
return data || [];
} catch (err) {
console.error('Failed to get federation mappings:', err.message);
return [];