Files
firefrost-services/services/arbiter/src/index.js
Claude (The Golden Chronicler #50) 04e9b407d5 feat: Migrate Arbiter and Modpack Version Checker to monorepo
WHAT WAS DONE:
- Migrated Arbiter (discord-oauth-arbiter) code to services/arbiter/
- Migrated Modpack Version Checker code to services/modpack-version-checker/
- Created .env.example for Arbiter with all required environment variables
- Moved systemd service file to services/arbiter/deploy/
- Organized directory structure per Gemini monorepo recommendations

WHY:
- Consolidate all service code in one repository
- Prepare for Gemini code review (Panel v1.12 compatibility check)
- Enable service-prefixed Git tagging (arbiter-v2.1.0, modpack-v1.0.0)
- Support npm workspaces for shared dependencies

SERVICES MIGRATED:
1. Arbiter (Discord OAuth bot) - Originally written by Gemini + Claude
   - Full source code from ops-manual docs/implementation/
   - Created comprehensive .env.example
   - Ready for Panel v1.12 compatibility verification

2. Modpack Version Checker (Python CLI tool)
   - Full source code from ops-manual docs/tasks/
   - Written for Panel v1.11, needs Gemini review for v1.12
   - Never had code review before

STILL TODO:
- Whitelist Manager - Pull from Billing VPS (38.68.14.188)
  - Currently deployed and running
  - Needs Panel v1.12 API compatibility fix (Task #86)
  - Requires SSH access to pull code

NEXT STEPS:
- Gemini code review for Panel v1.12 API compatibility
- Create package.json for each service
- Test npm workspaces integration
- Deploy after verification

FILES:
- services/arbiter/ (25 new files, full application)
- services/modpack-version-checker/ (21 new files, full application)

Signed-off-by: The Golden Chronicler <claude@firefrostgaming.com>
2026-03-31 21:52:42 +00:00

102 lines
2.9 KiB
JavaScript

// Firefrost Arbiter v2.0.0
// Discord Role Management & OAuth Gateway
// Built: March 30, 2026
const VERSION = '2.0.0';
require('dotenv').config();
const express = require('express');
const session = require('express-session');
const SQLiteStore = require('connect-sqlite3')(session);
const rateLimit = require('express-rate-limit');
const { client } = require('./discordService');
const db = require('./database');
const app = express();
const PORT = process.env.PORT || 3500;
// Trust reverse proxy (Nginx) for secure cookies
app.set('trust proxy', 1);
// Middleware - Body Parsers
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Middleware - Session Configuration
app.use(session({
store: new SQLiteStore({ db: 'sessions.db', dir: './' }),
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
secure: process.env.NODE_ENV === 'production', // true if HTTPS
httpOnly: true,
maxAge: 1000 * 60 * 60 * 24 * 7 // 1 week
}
}));
// Middleware - Rate Limiting
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Limit each IP to 100 requests per window
message: 'Too many requests from this IP, please try again after 15 minutes',
standardHeaders: true,
legacyHeaders: false,
});
// Apply rate limiting to specific routes
app.use('/auth', apiLimiter);
app.use('/webhook', apiLimiter);
app.use('/admin/api', apiLimiter);
// Routes
app.use('/webhook', require('./routes/webhook'));
app.use('/auth', require('./routes/oauth'));
app.use('/admin', require('./routes/adminAuth'));
app.use('/admin', require('./routes/admin'));
// Health Check Endpoint
app.get('/health', async (req, res) => {
let dbStatus = 'down';
try {
db.prepare('SELECT 1').get();
dbStatus = 'ok';
} catch (e) {
console.error('[Health] Database check failed:', e);
}
const status = {
uptime: process.uptime(),
discord: client.isReady() ? 'ok' : 'down',
database: dbStatus,
timestamp: new Date().toISOString()
};
const httpStatus = (status.discord === 'ok' && status.database === 'ok') ? 200 : 503;
res.status(httpStatus).json(status);
});
// Root endpoint
app.get('/', (req, res) => {
res.send('Firefrost Arbiter - Discord Role Management System');
});
// Start Server
app.listen(PORT, () => {
console.log(`[Server] Listening on port ${PORT}`);
console.log(`[Server] Environment: ${process.env.NODE_ENV || 'development'}`);
console.log(`[Server] Health check: http://localhost:${PORT}/health`);
});
// Discord Bot Ready Event
client.on('ready', () => {
console.log(`[Discord] Bot ready as ${client.user.tag}`);
});
// Graceful Shutdown
process.on('SIGTERM', () => {
console.log('[Server] SIGTERM received, shutting down gracefully...');
client.destroy();
process.exit(0);
});