Moved to services/_archived/: - arbiter/ (v2.0.0) - superseded by arbiter-3.0/ - whitelist-manager/ - merged into Trinity Console Added README explaining what's archived and why. DO NOT DEPLOY archived services - kept for historical reference only. Chronicler #76
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);
|
|
});
|