diff --git a/services/arbiter-3.0/src/routes/admin/about.js b/services/arbiter-3.0/src/routes/admin/about.js new file mode 100644 index 0000000..41cedb3 --- /dev/null +++ b/services/arbiter-3.0/src/routes/admin/about.js @@ -0,0 +1,89 @@ +const express = require('express'); +const router = express.Router(); +const { exec } = require('child_process'); +const fs = require('fs'); + +/** + * About Module โ Trinity Console + * + * Console version, module versions, deploy button, system meta. + * + * GET /admin/about โ About page + * POST /admin/about/deploy โ Deploy Arbiter (moved from sidebar) + * GET /admin/about/status โ Arbiter health check + * + * Chronicler #78 | April 11, 2026 + */ + +const MODULES = [ + { name: 'Dashboard', version: '1.0.0', path: '/admin/dashboard', icon: '๐', status: 'stable' }, + { name: 'Servers', version: '1.0.0', path: '/admin/servers', icon: '๐ฅ๏ธ', status: 'stable' }, + { name: 'Players', version: '1.0.0', path: '/admin/players', icon: '๐ฅ', status: 'stable' }, + { name: 'Financials', version: '1.0.0', path: '/admin/financials', icon: '๐ฐ', status: 'stable' }, + { name: 'Grace Period', version: '1.0.0', path: '/admin/grace', icon: 'โณ', status: 'stable' }, + { name: 'Discord', version: '1.0.0', path: '/admin/discord', icon: '๐ฌ', status: 'stable' }, + { name: 'Social', version: '1.0.0', path: '/admin/social', icon: '๐', status: 'stable' }, + { name: 'Infrastructure', version: '1.0.0', path: '/admin/infrastructure', icon: '๐', status: 'new' }, + { name: 'Restart Scheduler', version: '1.0.0', path: '/admin/scheduler', icon: 'โฐ', status: 'stable' }, + { name: 'Audit Log', version: '1.0.0', path: '/admin/audit', icon: '๐', status: 'stable' }, + { name: 'Role Audit', version: '1.0.0', path: '/admin/roles', icon: '๐', status: 'stable' }, +]; + +function getNodeVersion() { + return process.version; +} + +function getArbiterUptime() { + return process.uptime(); +} + +function formatUptime(seconds) { + const days = Math.floor(seconds / 86400); + const hours = Math.floor((seconds % 86400) / 3600); + const mins = Math.floor((seconds % 3600) / 60); + if (days > 0) return `${days}d ${hours}h ${mins}m`; + if (hours > 0) return `${hours}h ${mins}m`; + return `${mins}m`; +} + +// GET /admin/about +router.get('/', (req, res) => { + const pkg = JSON.parse(fs.readFileSync(require.resolve('../../../package.json'), 'utf8')); + + res.render('admin/about/index', { + title: 'About', + currentPath: '/about', + consoleVersion: pkg.version || '3.5.0', + nodeVersion: getNodeVersion(), + arbiterUptime: formatUptime(getArbiterUptime()), + modules: MODULES, + totalModules: MODULES.length, + adminUser: req.user, + layout: 'layout' + }); +}); + +// POST /admin/about/deploy โ Deploy Arbiter +router.post('/deploy', (req, res) => { + const username = req.user?.username || 'unknown'; + console.log(`[DEPLOY] Deployment initiated by ${username} from About page`); + exec(`nohup sudo /opt/scripts/deploy-arbiter.sh "${username}" > /tmp/deploy.log 2>&1 &`); + res.json({ + success: true, + message: 'Deploy started. Arbiter will restart momentarily.' + }); +}); + +// GET /admin/about/status โ Health check +router.get('/status', (req, res) => { + exec('systemctl is-active arbiter-3', (error, stdout) => { + const isRunning = stdout.trim() === 'active'; + res.json({ + arbiter: isRunning ? 'running' : 'stopped', + uptime: formatUptime(getArbiterUptime()), + deployAvailable: fs.existsSync('/opt/scripts/deploy-arbiter.sh') + }); + }); +}); + +module.exports = router; diff --git a/services/arbiter-3.0/src/routes/admin/index.js b/services/arbiter-3.0/src/routes/admin/index.js index 8efc445..435a772 100644 --- a/services/arbiter-3.0/src/routes/admin/index.js +++ b/services/arbiter-3.0/src/routes/admin/index.js @@ -16,6 +16,7 @@ const discordAuditRouter = require('./discord-audit'); const systemRouter = require('./system'); const socialRouter = require('./social'); const infrastructureRouter = require('./infrastructure'); +const aboutRouter = require('./about'); router.use(requireTrinityAccess); @@ -119,5 +120,6 @@ router.use('/discord', discordAuditRouter); router.use('/system', systemRouter); router.use('/social', socialRouter); router.use('/infrastructure', infrastructureRouter); +router.use('/about', aboutRouter); module.exports = router; diff --git a/services/arbiter-3.0/src/views/admin/about/index.ejs b/services/arbiter-3.0/src/views/admin/about/index.ejs new file mode 100644 index 0000000..1acbac8 --- /dev/null +++ b/services/arbiter-3.0/src/views/admin/about/index.ejs @@ -0,0 +1,232 @@ + + + + + +
+ Pull latest code from Gitea and restart Arbiter. The console will briefly disconnect during restart. +
+ + +