Files
firefrost-services/services/arbiter-3.0/src/routes/admin/index.js
Claude (Chronicler #78) b8f9926e9b feat: Task #116 — Trinity Console Tasks Module
- Tasks page at /admin/tasks with filterable table
- Status/owner inline dropdowns (change via form submit)
- + New Task modal with title, description, priority, owner
- ✓ Done button on hover per row
- Stats bar: active, in progress, blocked, high priority, completed
- Show All toggle for done/obsolete tasks
- Sidebar link under Operations (right after Dashboard)
- Added to About page module registry

Source of truth: PostgreSQL tasks table (shared with Discord /tasks)

Chronicler #78 | firefrost-services
2026-04-11 14:22:21 +00:00

130 lines
4.4 KiB
JavaScript

const express = require('express');
const router = express.Router();
const { requireTrinityAccess } = require('./middleware');
const { getMinecraftServers } = require('../../panel/discovery');
const db = require('../../database');
// Sub-routers
const playersRouter = require('./players');
const serversRouter = require('./servers');
const financialsRouter = require('./financials');
const graceRouter = require('./grace');
const auditRouter = require('./audit');
const rolesRouter = require('./roles');
const schedulerRouter = require('./scheduler');
const discordAuditRouter = require('./discord-audit');
const systemRouter = require('./system');
const socialRouter = require('./social');
const infrastructureRouter = require('./infrastructure');
const aboutRouter = require('./about');
const mcpLogsRouter = require('./mcp-logs');
const tasksRouter = require('./tasks');
router.use(requireTrinityAccess);
// Make CSRF token available to all admin views
router.use((req, res, next) => {
res.locals.csrfToken = req.csrfToken();
next();
});
router.get('/', (req, res) => {
res.redirect('/admin/dashboard');
});
router.get('/dashboard', async (req, res) => {
try {
// Fetch server count from Pterodactyl
const servers = await getMinecraftServers();
const serversOnline = servers.length;
// Fetch subscriber stats from database
const { rows: subStats } = await db.query(`
SELECT
COUNT(*) FILTER (WHERE status IN ('active', 'grace_period') OR is_lifetime = true) as active_count,
COALESCE(SUM(mrr_value) FILTER (WHERE status = 'active'), 0) as mrr
FROM subscriptions
`);
const activeSubscribers = parseInt(subStats[0]?.active_count || 0);
const totalMRR = parseFloat(subStats[0]?.mrr || 0);
// Fetch most recent successful sync time
const { rows: syncRows } = await db.query(`
SELECT MAX(last_successful_sync) as last_sync
FROM server_sync_log
WHERE is_online = true
`);
const lastSyncTime = syncRows[0]?.last_sync || null;
// Fetch social stats across all platforms
const { rows: socialStats } = await db.query(`
SELECT
platform,
COUNT(*) as post_count,
COALESCE(SUM(views), 0) as total_views,
COALESCE(SUM(likes), 0) as total_likes,
COALESCE(SUM(comments), 0) as total_comments
FROM social_posts
GROUP BY platform
`);
const socialTotals = {
posts: 0,
views: 0,
likes: 0,
comments: 0,
platforms: {}
};
for (const row of socialStats) {
socialTotals.posts += parseInt(row.post_count);
socialTotals.views += parseInt(row.total_views);
socialTotals.likes += parseInt(row.total_likes);
socialTotals.comments += parseInt(row.total_comments);
socialTotals.platforms[row.platform] = {
posts: parseInt(row.post_count),
views: parseInt(row.total_views),
likes: parseInt(row.total_likes)
};
}
res.render('admin/dashboard', {
title: 'Command Bridge',
serversOnline,
activeSubscribers,
totalMRR,
lastSyncTime,
socialTotals
});
} catch (error) {
console.error('Dashboard data fetch error:', error);
// Fallback to zeros on error
res.render('admin/dashboard', {
title: 'Command Bridge',
serversOnline: 0,
activeSubscribers: 0,
totalMRR: 0,
lastSyncTime: null,
socialTotals: { posts: 0, views: 0, likes: 0, comments: 0, platforms: {} }
});
}
});
router.use('/players', playersRouter);
router.use('/servers', serversRouter);
router.use('/financials', financialsRouter);
router.use('/grace', graceRouter);
router.use('/audit', auditRouter);
router.use('/roles', rolesRouter);
router.use('/scheduler', schedulerRouter);
router.use('/discord', discordAuditRouter);
router.use('/system', systemRouter);
router.use('/social', socialRouter);
router.use('/infrastructure', infrastructureRouter);
router.use('/about', aboutRouter);
router.use('/mcp-logs', mcpLogsRouter);
router.use('/tasks', tasksRouter);
module.exports = router;