diff --git a/services/arbiter-3.0/src/routes/admin/index.js b/services/arbiter-3.0/src/routes/admin/index.js index 435a772..9b86102 100644 --- a/services/arbiter-3.0/src/routes/admin/index.js +++ b/services/arbiter-3.0/src/routes/admin/index.js @@ -17,6 +17,7 @@ const systemRouter = require('./system'); const socialRouter = require('./social'); const infrastructureRouter = require('./infrastructure'); const aboutRouter = require('./about'); +const mcpLogsRouter = require('./mcp-logs'); router.use(requireTrinityAccess); @@ -121,5 +122,6 @@ router.use('/system', systemRouter); router.use('/social', socialRouter); router.use('/infrastructure', infrastructureRouter); router.use('/about', aboutRouter); +router.use('/mcp-logs', mcpLogsRouter); module.exports = router; diff --git a/services/arbiter-3.0/src/routes/admin/mcp-logs.js b/services/arbiter-3.0/src/routes/admin/mcp-logs.js new file mode 100644 index 0000000..07e0b20 --- /dev/null +++ b/services/arbiter-3.0/src/routes/admin/mcp-logs.js @@ -0,0 +1,98 @@ +const express = require('express'); +const router = express.Router(); +const db = require('../../database'); + +/** + * MCP Logs Module — Trinity Console + * + * View and filter command execution logs from Trinity Core. + * + * GET /admin/mcp-logs — Main logs page with filters + * + * Chronicler #78 | April 11, 2026 + */ + +const SERVERS = [ + 'command-center', 'tx1-dallas', 'nc1-charlotte', + 'panel-vps', 'dev-panel', 'wiki-vps', 'services-vps', 'trinity-core' +]; + +router.get('/', async (req, res) => { + try { + const { server, success, limit = 50, offset = 0 } = req.query; + + // Build filtered query + let where = 'WHERE 1=1'; + const params = []; + let p = 0; + + if (server) { + p++; + where += ` AND server = $${p}`; + params.push(server); + } + + if (success !== undefined && success !== '') { + p++; + where += ` AND success = $${p}`; + params.push(success === 'true'); + } + + // Get logs + const logsResult = await db.query( + `SELECT * FROM mcp_logs ${where} ORDER BY executed_at DESC LIMIT $${p + 1} OFFSET $${p + 2}`, + [...params, parseInt(limit), parseInt(offset)] + ); + + // Get total count + const countResult = await db.query( + `SELECT COUNT(*) FROM mcp_logs ${where}`, + params + ); + const total = parseInt(countResult.rows[0].count); + + // Get stats + const statsResult = await db.query(` + SELECT + COUNT(*) as total, + COUNT(*) FILTER (WHERE success = true) as success_count, + COUNT(*) FILTER (WHERE success = false) as fail_count, + COALESCE(ROUND(AVG(execution_time_ms)), 0) as avg_time + FROM mcp_logs + `); + + const stats = statsResult.rows[0]; + + res.render('admin/mcp-logs/index', { + title: 'MCP Logs', + currentPath: '/mcp-logs', + logs: logsResult.rows, + total, + limit: parseInt(limit), + offset: parseInt(offset), + query: req.query, + stats, + servers: SERVERS, + adminUser: req.user, + layout: 'layout' + }); + } catch (err) { + console.error('[MCP Logs] Route error:', err); + res.render('admin/mcp-logs/index', { + title: 'MCP Logs', + currentPath: '/mcp-logs', + logs: [], + total: 0, + limit: 50, + offset: 0, + query: {}, + stats: { total: 0, success_count: 0, fail_count: 0, avg_time: 0 }, + servers: SERVERS, + error: err.message, + adminUser: req.user, + layout: 'layout' + }); + } +}); + +module.exports = router; diff --git a/services/arbiter-3.0/src/routes/api.js b/services/arbiter-3.0/src/routes/api.js index 03a72e4..da20b66 100644 --- a/services/arbiter-3.0/src/routes/api.js +++ b/services/arbiter-3.0/src/routes/api.js @@ -368,4 +368,31 @@ router.get('/social/digest', async (req, res) => { } }); +// ============================================================================= +// POST /api/internal/mcp/log +// Log a command execution from Trinity Core +// ============================================================================= + +router.post('/mcp/log', async (req, res) => { + try { + const { server, command, success, stdout, stderr, error, execution_time_ms } = req.body; + + if (!server || !command || success === undefined) { + return res.status(400).json({ error: 'Missing required fields: server, command, success' }); + } + + const result = await db.query( + `INSERT INTO mcp_logs (server, command, success, stdout, stderr, error, execution_time_ms) + VALUES ($1, $2, $3, $4, $5, $6, $7) + RETURNING id`, + [server, command, success, stdout || '', stderr || '', error || null, execution_time_ms || null] + ); + + res.json({ success: true, id: result.rows[0].id }); + } catch (err) { + console.error('❌ [MCP Log] Error:', err); + res.status(500).json({ error: 'Failed to log command' }); + } +}); + module.exports = router; diff --git a/services/arbiter-3.0/src/views/admin/mcp-logs/index.ejs b/services/arbiter-3.0/src/views/admin/mcp-logs/index.ejs new file mode 100644 index 0000000..ebd847e --- /dev/null +++ b/services/arbiter-3.0/src/views/admin/mcp-logs/index.ejs @@ -0,0 +1,161 @@ + + + + +
| Time | +Server | +Command | +Status | +Duration | ++ |
|---|---|---|---|---|---|
| + <% if (stats.total == 0) { %> + No commands logged yet. Commands executed via Trinity Core will appear here. + <% } else { %> + No logs match your filters. + <% } %> + | +|||||
| <%= timeStr %> | ++ <%= log.server %> + | +<%= cmdShort %> |
+ + <% if (log.success) { %> + ✓ OK + <% } else { %> + ✗ Fail + <% } %> + | +<%= log.execution_time_ms || '—' %>ms | +▼ | +