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>
This commit is contained in:
101
services/arbiter/src/index.js
Normal file
101
services/arbiter/src/index.js
Normal file
@@ -0,0 +1,101 @@
|
||||
// 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);
|
||||
});
|
||||
Reference in New Issue
Block a user