95 lines
3.1 KiB
JavaScript
95 lines
3.1 KiB
JavaScript
const express = require('express');
|
|
const { promisify } = require('util');
|
|
const { exec } = require('child_process');
|
|
const rateLimit = require('express-rate-limit');
|
|
const app = express();
|
|
const port = 3066;
|
|
require('dotenv').config();
|
|
|
|
const execPromise = promisify(exec);
|
|
|
|
// Custom key generator to support Cloudflare's CF-Connecting-IP header
|
|
const getClientIp = (req) => {
|
|
return req.headers['cf-connecting-ip'] || req.ip;
|
|
};
|
|
|
|
// Rate limiter configuration: 1 request per second with exponential backoff
|
|
const limiter = rateLimit({
|
|
windowMs: 1000, // 1 second
|
|
max: 1, // 1 request per IP
|
|
standardHeaders: true, // Return rate limit info in headers
|
|
legacyHeaders: false, // Disable X-RateLimit headers
|
|
keyGenerator: getClientIp, // Use CF-Connecting-IP or fallback to req.ip
|
|
handler: (req, res) => {
|
|
// Calculate backoff time (exponential, starting at 2 seconds)
|
|
const retryAfter = Math.pow(2, Math.min(req.rateLimit.current - req.rateLimit.limit, 5));
|
|
res.setHeader('Retry-After', retryAfter);
|
|
res.status(429).json({
|
|
error: 'Too many requests, please try again later.',
|
|
retryAfter: retryAfter
|
|
});
|
|
}
|
|
});
|
|
|
|
// Apply rate limiting to API routes only
|
|
app.use('/java', limiter);
|
|
app.use('/bedrock', limiter);
|
|
|
|
async function checkConnectionStatus(hostname, port) {
|
|
try {
|
|
const command = `${process.env.STATUS_CHECK_PATH} -host ${hostname} -port ${port}`;
|
|
const { stdout, stderr } = await execPromise(command);
|
|
if (stderr) {
|
|
return { isOnline: false, error: stderr };
|
|
}
|
|
const data = JSON.parse(stdout);
|
|
return { isOnline: true, data };
|
|
} catch (error) {
|
|
return { isOnline: false, error: error.message };
|
|
}
|
|
}
|
|
|
|
async function checkGeyserStatus(hostname, port) {
|
|
try {
|
|
const command = `${process.env.GEYSER_STATUS_CHECK_PATH} -host ${hostname} -port ${port}`;
|
|
const { stdout, stderr } = await execPromise(command);
|
|
if (stderr) {
|
|
return { isOnline: false, error: stderr };
|
|
}
|
|
const data = JSON.parse(stdout);
|
|
return { isOnline: true, data };
|
|
} catch (error) {
|
|
return { isOnline: false, error: error.message };
|
|
}
|
|
}
|
|
|
|
app.use(express.static('public'));
|
|
|
|
app.get('/', (req, res) => {
|
|
res.sendFile(__dirname + '/public/index.html');
|
|
});
|
|
|
|
app.get('/java/:host/:port', async (req, res) => {
|
|
const { host, port } = req.params;
|
|
try {
|
|
const result = await checkConnectionStatus(host, port);
|
|
res.json(result);
|
|
} catch (error) {
|
|
res.status(500).json({ isOnline: false, error: `Server error: ${error.message}` });
|
|
}
|
|
});
|
|
|
|
app.get('/bedrock/:host/:port', async (req, res) => {
|
|
const { host, port } = req.params;
|
|
try {
|
|
const result = await checkGeyserStatus(host, port);
|
|
console.log(result);
|
|
res.json(result);
|
|
} catch (error) {
|
|
res.status(500).json({ isOnline: false, error: `Server error: ${error.message}` });
|
|
}
|
|
});
|
|
|
|
app.listen(port, () => {
|
|
console.log(`Server running at http://localhost:${port}`);
|
|
}); |