feat: Complete admin panel implementation
- 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.
This commit is contained in:
146
middleware/maintenance.js
Normal file
146
middleware/maintenance.js
Normal file
@@ -0,0 +1,146 @@
|
||||
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;
|
||||
};
|
||||
Reference in New Issue
Block a user