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>
102 lines
2.9 KiB
JavaScript
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);
|
|
});
|