import { DrizzleClient } from "@shared/db/DrizzleClient"; import { users, transactions, moderationCases, inventory } from "@db/schema"; import { desc, sql, and, gte } from "drizzle-orm"; import type { RecentEvent } from "./dashboard.types"; export const dashboardService = { /** * Get count of active users from database */ getActiveUserCount: async (): Promise => { const result = await DrizzleClient .select({ count: sql`COUNT(*)` }) .from(users) .where(sql`${users.isActive} = true`); return Number(result[0]?.count || 0); }, /** * Get total user count */ getTotalUserCount: async (): Promise => { const result = await DrizzleClient .select({ count: sql`COUNT(*)` }) .from(users); return Number(result[0]?.count || 0); }, /** * Get economy statistics */ getEconomyStats: async (): Promise<{ totalWealth: bigint; avgLevel: number; topStreak: number; }> => { const allUsers = await DrizzleClient.select().from(users); const totalWealth = allUsers.reduce( (acc: bigint, u: any) => acc + (u.balance || 0n), 0n ); const avgLevel = allUsers.length > 0 ? Math.round( allUsers.reduce((acc: number, u: any) => acc + (u.level || 1), 0) / allUsers.length ) : 1; const topStreak = allUsers.reduce( (max: number, u: any) => Math.max(max, u.dailyStreak || 0), 0 ); return { totalWealth, avgLevel, topStreak }; }, /** * Get total items in circulation */ getTotalItems: async (): Promise => { const result = await DrizzleClient .select({ total: sql`COALESCE(SUM(${inventory.quantity}), 0)` }) .from(inventory); return Number(result[0]?.total || 0); }, /** * Get recent transactions as events (last 24 hours) */ getRecentTransactions: async (limit: number = 10): Promise => { const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000); const recentTx = await DrizzleClient.query.transactions.findMany({ limit, orderBy: [desc(transactions.createdAt)], where: gte(transactions.createdAt, oneDayAgo), with: { user: true, }, }); return recentTx.map((tx: any) => ({ type: 'info' as const, message: `${tx.user?.username || 'Unknown'}: ${tx.description || 'Transaction'}`, timestamp: tx.createdAt || new Date(), icon: getTransactionIcon(tx.type), })); }, /** * Get recent moderation cases as events (last 24 hours) */ getRecentModerationCases: async (limit: number = 10): Promise => { const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000); const recentCases = await DrizzleClient.query.moderationCases.findMany({ limit, orderBy: [desc(moderationCases.createdAt)], where: gte(moderationCases.createdAt, oneDayAgo), }); return recentCases.map((modCase: any) => ({ type: modCase.type === 'warn' || modCase.type === 'ban' ? 'error' : 'info', message: `${modCase.type.toUpperCase()}: ${modCase.username} - ${modCase.reason}`, timestamp: modCase.createdAt || new Date(), icon: getModerationIcon(modCase.type), })); }, /** * Get combined recent events (transactions + moderation) */ getRecentEvents: async (limit: number = 10): Promise => { const [txEvents, modEvents] = await Promise.all([ dashboardService.getRecentTransactions(limit), dashboardService.getRecentModerationCases(limit), ]); // Combine and sort by timestamp const allEvents = [...txEvents, ...modEvents] .sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime()) .slice(0, limit); return allEvents; }, }; /** * Helper to get icon for transaction type */ function getTransactionIcon(type: string): string { if (type.includes("LOOT")) return "🌠"; if (type.includes("GIFT")) return "🎁"; if (type.includes("SHOP")) return "πŸ›’"; if (type.includes("DAILY")) return "β˜€οΈ"; if (type.includes("QUEST")) return "πŸ“œ"; if (type.includes("TRANSFER")) return "πŸ’Έ"; return "πŸ’«"; } /** * Helper to get icon for moderation type */ function getModerationIcon(type: string): string { switch (type) { case 'warn': return '⚠️'; case 'timeout': return '⏸️'; case 'kick': return 'πŸ‘’'; case 'ban': return 'πŸ”¨'; case 'note': return 'πŸ“'; case 'prune': return '🧹'; default: return 'πŸ›‘οΈ'; } }