- Add user management system with all CRUD operations - Add promotion statistics dashboard with export - Simplify Trading & Market settings UI - Fix promotion schema (dates now optional) - Add missing API endpoints and PATCH support - Add comprehensive documentation - Fix critical bugs (deletePromotion, duplicate endpoints) All features tested and production-ready.
147 lines
4.4 KiB
JavaScript
147 lines
4.4 KiB
JavaScript
import SiteConfig from "../models/SiteConfig.js";
|
|
import { verifyAccessToken } from "../utils/jwt.js";
|
|
import User from "../models/User.js";
|
|
|
|
/**
|
|
* Middleware to check if site is in maintenance mode
|
|
* Allows admins and whitelisted users to access during maintenance
|
|
*/
|
|
export const checkMaintenance = async (request, reply) => {
|
|
try {
|
|
const currentPath = request.url.split("?")[0]; // Remove query params
|
|
|
|
// Skip maintenance check for public health endpoints and auth routes
|
|
const publicEndpoints = [
|
|
"/health",
|
|
"/api/health",
|
|
"/api/config/public",
|
|
"/api/config/announcements",
|
|
"/api/config/status",
|
|
"/api/config/promotions",
|
|
];
|
|
|
|
// Allow all auth routes (needed for Steam OAuth flow)
|
|
const authRoutes = [
|
|
"/auth/steam",
|
|
"/auth/steam/return",
|
|
"/api/auth/steam",
|
|
"/api/auth/steam/return",
|
|
"/api/auth/me",
|
|
"/api/auth/verify",
|
|
"/api/auth/refresh",
|
|
"/api/auth/logout",
|
|
];
|
|
|
|
if (
|
|
publicEndpoints.includes(currentPath) ||
|
|
authRoutes.includes(currentPath) ||
|
|
currentPath.startsWith("/auth/") ||
|
|
currentPath.startsWith("/api/auth/")
|
|
) {
|
|
return;
|
|
}
|
|
|
|
const config = await SiteConfig.getConfig();
|
|
|
|
// If maintenance mode is not enabled, allow all requests
|
|
if (!config.isMaintenanceActive()) {
|
|
return;
|
|
}
|
|
|
|
// Try to verify user authentication manually if not already done
|
|
let authenticatedUser = request.user;
|
|
|
|
if (!authenticatedUser) {
|
|
// Try to get token from cookies or Authorization header
|
|
let token = null;
|
|
|
|
// Check Authorization header
|
|
const authHeader = request.headers.authorization;
|
|
if (authHeader && authHeader.startsWith("Bearer ")) {
|
|
token = authHeader.substring(7);
|
|
}
|
|
|
|
// Check cookies if no header
|
|
if (!token && request.cookies && request.cookies.accessToken) {
|
|
token = request.cookies.accessToken;
|
|
}
|
|
|
|
// If we have a token, verify it
|
|
if (token) {
|
|
try {
|
|
const decoded = verifyAccessToken(token);
|
|
if (decoded && decoded.userId) {
|
|
// Fetch user from database
|
|
authenticatedUser = await User.findById(decoded.userId);
|
|
}
|
|
} catch (error) {
|
|
// Token invalid or expired - user will be treated as unauthenticated
|
|
console.log(
|
|
"⚠️ Token verification failed in maintenance check:",
|
|
error.message
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If user is authenticated, check if they're allowed during maintenance
|
|
if (authenticatedUser) {
|
|
// Check if user is admin (staff level 3+)
|
|
if (authenticatedUser.staffLevel >= 3) {
|
|
console.log(
|
|
`✅ Admin ${authenticatedUser.username} bypassing maintenance mode for ${currentPath}`
|
|
);
|
|
return; // Allow all admin access
|
|
}
|
|
|
|
// Check if user's steamId is in the allowed list
|
|
if (config.canAccessDuringMaintenance(authenticatedUser.steamId)) {
|
|
console.log(
|
|
`✅ Whitelisted user ${authenticatedUser.username} bypassing maintenance mode`
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// User is not allowed during maintenance
|
|
console.log(`⚠️ Blocking request during maintenance: ${currentPath}`);
|
|
|
|
// For API calls, return JSON
|
|
if (currentPath.startsWith("/api/")) {
|
|
return reply.status(503).send({
|
|
success: false,
|
|
maintenance: true,
|
|
message:
|
|
config.maintenance.message ||
|
|
"We're currently performing maintenance. Please check back soon!",
|
|
scheduledEnd: config.maintenance.scheduledEnd,
|
|
redirectTo: "/maintenance",
|
|
});
|
|
}
|
|
|
|
// For regular page requests, redirect to maintenance page
|
|
// (This will be handled by frontend router)
|
|
return reply.status(503).send({
|
|
success: false,
|
|
maintenance: true,
|
|
message:
|
|
config.maintenance.message ||
|
|
"We're currently performing maintenance. Please check back soon!",
|
|
scheduledEnd: config.maintenance.scheduledEnd,
|
|
redirectTo: "/maintenance",
|
|
});
|
|
} catch (error) {
|
|
console.error("❌ Maintenance check error:", error);
|
|
// On error, allow the request to proceed (fail open)
|
|
return;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Optional middleware - only check maintenance for specific routes
|
|
*/
|
|
export const maintenanceExempt = async (request, reply) => {
|
|
// This middleware does nothing - it's used to mark routes that should bypass maintenance
|
|
return;
|
|
};
|