fix: wallet display bug, add test email endpoint

- Fix wallet balance always showing 'Disconnected' in admin dashboard
- Always attempt balance fetch even if isHealthy=false
- Show error state with message instead of generic 'Disconnected'
- Add POST /api/admin/alerts/test-email endpoint to verify Resend config
- Move alertService to module scope for endpoint access
This commit is contained in:
jwinterm
2026-02-01 15:59:30 -05:00
parent a81c19c936
commit 6e0f3e9012
2 changed files with 70 additions and 21 deletions
+19 -8
View File
@@ -498,17 +498,28 @@
// Wallet balance
if (overview.walletBalance) {
const bal = overview.walletBalance.unlocked_balance || 0;
const formatted = (bal / 1e12).toFixed(4);
document.getElementById('statBalance').textContent = formatted;
document.getElementById('statUnlocked').textContent = `Unlocked: ${formatted}`;
const walletEl = document.getElementById('walletStatus');
walletEl.className = 'wallet-status' + (bal < 100000000000 ? ' warning' : ' healthy');
document.getElementById('walletBalance').textContent = `${formatted} ${window.location.hostname.includes('monerogue') ? 'XMR' : 'WOW'}`;
const cryptoType = window.location.hostname.includes('monerogue') ? 'XMR' : 'WOW';
if (overview.walletBalance.error) {
// Wallet RPC error - show error state
document.getElementById('statBalance').textContent = 'Error';
document.getElementById('statUnlocked').textContent = overview.walletBalance.error;
document.getElementById('statUnlocked').title = overview.walletBalance.error;
walletEl.className = 'wallet-status disconnected';
document.getElementById('walletBalance').textContent = 'RPC Error';
} else {
// Wallet connected - show balance
const bal = overview.walletBalance.unlocked_balance || 0;
const formatted = (bal / 1e12).toFixed(4);
document.getElementById('statBalance').textContent = formatted;
document.getElementById('statUnlocked').textContent = `Unlocked: ${formatted}`;
walletEl.className = 'wallet-status' + (bal < 100000000000 ? ' warning' : ' healthy');
document.getElementById('walletBalance').textContent = `${formatted} ${cryptoType}`;
}
} else {
document.getElementById('statBalance').textContent = '-';
document.getElementById('walletBalance').textContent = 'Disconnected';
document.getElementById('walletBalance').textContent = 'Not configured';
document.getElementById('walletStatus').className = 'wallet-status disconnected';
}
+51 -13
View File
@@ -49,6 +49,7 @@ gameModeManager.io = io;
// Initialize remaining components
const activeGames = new Map(); // Maps socketId to Game objects
let alertService = null; // Initialized later when payment system starts
const socketHandlers = new SocketHandlers(io, activeGames, broadcastManager, debugManager, gameModeManager, walletRPCService);
// Configure static file serving
const htmlPath = path.join(__dirname, '../html');
@@ -201,18 +202,21 @@ app.post('/api/user/:socketId/address', asyncHandler(async (req, res) => {
app.get('/health', async (req, res) => {
const memUsage = process.memoryUsage();
// Get wallet balance if healthy
// Get wallet balance - always attempt
let walletBalance = null;
let lowBalanceWarning = false;
const lowBalanceThreshold = parseInt(process.env.LOW_BALANCE_THRESHOLD) || 100000000000; // 0.1 XMR default
if (walletRPCService.isHealthy) {
try {
walletBalance = await walletRPCService.getBalance();
try {
walletBalance = await walletRPCService.getBalance();
if (walletBalance.error) {
console.warn('Wallet balance returned error:', walletBalance.error);
} else {
lowBalanceWarning = walletBalance.unlocked_balance < lowBalanceThreshold;
} catch (e) {
// Ignore balance fetch errors
}
} catch (e) {
console.error('Failed to get wallet balance for health check:', e.message);
walletBalance = { balance: 0, unlocked_balance: 0, error: e.message };
}
const health = {
@@ -822,6 +826,38 @@ app.post('/api/admin/credits/adjust', adminAuth, asyncHandler(async (req, res) =
});
}));
/**
* POST /api/admin/alerts/test-email
* Send a test email to verify alert service configuration
* Requires: X-Admin-Key header
*/
app.post('/api/admin/alerts/test-email', adminAuth, asyncHandler(async (req, res) => {
if (!alertService) {
return res.status(503).json({
success: false,
message: 'Alert service not initialized. Payment system may not be enabled.'
});
}
const result = await alertService.sendAlert('test_email', {
subject: `🧪 Wownerogue Alert Test - ${process.env.NODE_ENV || 'development'}`,
html: `
<h2>Alert Service Test</h2>
<p>If you received this email, the alert notifications are working correctly!</p>
<hr>
<p><strong>Environment:</strong> ${process.env.NODE_ENV || 'development'}</p>
<p><strong>Time:</strong> ${new Date().toISOString()}</p>
<p><strong>Server:</strong> ${process.env.CRYPTO_TYPE || 'WOW'}</p>
`
});
res.json({
success: result.sent,
message: result.sent ? 'Test email sent successfully!' : (result.reason || 'Failed to send'),
details: result
});
}));
/**
* GET /api/admin/users/search
* Search for users by socket ID prefix or payout address
@@ -896,14 +932,16 @@ app.get('/api/admin/stats/overview', adminAuth, asyncHandler(async (req, res) =>
WHERE created_at > NOW() - INTERVAL '24 hours'
`);
// Get wallet balance
// Get wallet balance - always attempt, even if isHealthy is false
let walletBalance = null;
if (walletRPCService.isHealthy) {
try {
walletBalance = await walletRPCService.getBalance();
} catch (e) {
// Ignore
try {
walletBalance = await walletRPCService.getBalance();
if (walletBalance.error) {
console.warn('Wallet balance returned error:', walletBalance.error);
}
} catch (e) {
console.error('Failed to get wallet balance for admin stats:', e.message);
walletBalance = { balance: 0, unlocked_balance: 0, error: e.message };
}
const user = userStats.rows[0];
@@ -1515,7 +1553,7 @@ async function startServer() {
const maxRetries = Number(process.env.PAYOUT_MAX_RETRIES) || 3;
const retryIntervalMs = Number(process.env.PAYOUT_RETRY_INTERVAL_MS) || 300000; // 5 minutes
// Initialize alert service for email notifications
const alertService = new AlertService({
alertService = new AlertService({
walletService: walletRPCService,
db: databaseManager,
debugManager