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:
parent
734e2f251f
commit
cf175b87e9
2 changed files with 95 additions and 117 deletions
4
.replit
4
.replit
|
|
@ -22,10 +22,6 @@ externalPort = 80
|
|||
localPort = 8080
|
||||
externalPort = 8080
|
||||
|
||||
[[ports]]
|
||||
localPort = 33401
|
||||
externalPort = 3000
|
||||
|
||||
[workflows]
|
||||
runButton = "Project"
|
||||
|
||||
|
|
|
|||
|
|
@ -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 [];
|
||||
|
|
|
|||
Loading…
Reference in a new issue