mirror of
https://github.com/Shineii86/AniKotoAPI.git
synced 2026-10-22 02:45:10 +00:00
v1.9.0: Full diagnostic sweep - 42 issues fixed
Critical fixes (7): - Added global Express error handler (returns JSON instead of HTML) - Removed caching from random endpoint (defeated purpose) - Renamed share.jpg to share.png (MIME type mismatch) - Fixed OG image dimensions (630 → 601) - Fixed manifest.json icon sizes (192x192 → 512x512) - Fixed implicit global event variable in console - Added 9 missing endpoints to docs/endpoints.md High fixes (12): - Imported version from package.json (no hardcoded drift) - Added dynamic endpoint count in stats - Added input sanitization for mapper API (SSRF prevention) - Added homepage caching (reduced 5 duplicate HTTP requests) - Added rate limiting (100 requests/minute per IP) - Added rel="noopener noreferrer" to all target="_blank" links - Fixed trailing slash inconsistency in console - Added noindex meta to 404.html - Fixed docs/architecture.md controller filenames and dependency versions - Fixed README.md dependency versions - Added 12 missing endpoint tests Medium fixes (22): - Added escapeHtml helper for error messages - Consistent favicon across all pages - Added meta descriptions to privacy.html and tos.html - Added OG/Twitter meta tags to privacy.html and tos.html - Added aria-label to playground inputs - Added aria-hidden to decorative SVGs - Synced Twitter description with OG description - Fixed robots.txt to disallow /api - Added PWA fields to manifest.json - Removed 5 unused imports - Removed unused cookie-parser dependency - Fixed inconsistent error handling in extractors - Fixed resolvedSlug returning numeric ID - Added /watch endpoint to streaming docs - Improved test assertions and status code validation - Deleted orphaned og-image.svg and logo.png Low fixes (11): - Removed redundant try/catch from extractPages - Added max size eviction to cache - Added cache key sorting in filter controller - Added author meta tag - Fixed favicon.svg font Version bumped to 1.9.0
This commit is contained in:
@@ -36,6 +36,7 @@ const PORT = process.env.PORT || 4444;
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
const publicDir = path.join(process.cwd(), "public");
|
||||
const has404File = fs.existsSync(path.join(publicDir, "404.html"));
|
||||
const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(",");
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
@@ -45,7 +46,7 @@ const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(",");
|
||||
// NOTE: Single unified CORS middleware — handles all origin validation
|
||||
app.use((req, res, next) => {
|
||||
const origin = req.headers.origin;
|
||||
if (!allowedOrigins || allowedOrigins.includes("*") || (origin && allowedOrigins.includes(origin))) {
|
||||
if (allowedOrigins && (allowedOrigins.includes("*") || (origin && allowedOrigins.includes(origin)))) {
|
||||
res.setHeader("Access-Control-Allow-Origin", origin || "*");
|
||||
}
|
||||
res.setHeader("Access-Control-Allow-Methods", "GET");
|
||||
@@ -116,12 +117,37 @@ const jsonError = (res, message = "Internal server error", status = 500) =>
|
||||
// API ROUTES
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
// ---- FEATURE: Rate limiting (100 requests per minute per IP) ----
|
||||
const requestCounts = new Map();
|
||||
app.use((req, res, next) => {
|
||||
const ip = req.ip || req.connection.remoteAddress;
|
||||
const now = Date.now();
|
||||
const windowMs = 60000;
|
||||
const maxRequests = 100;
|
||||
if (!requestCounts.has(ip)) {
|
||||
requestCounts.set(ip, []);
|
||||
}
|
||||
const timestamps = requestCounts.get(ip).filter(t => now - t < windowMs);
|
||||
requestCounts.set(ip, timestamps);
|
||||
if (timestamps.length >= maxRequests) {
|
||||
return res.status(429).json({ success: false, message: "Rate limit exceeded. Try again later." });
|
||||
}
|
||||
timestamps.push(now);
|
||||
next();
|
||||
});
|
||||
|
||||
createApiRoutes(app, jsonResponse, jsonError);
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// 404 HANDLER
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
// ---- FEATURE: Global error handler ----
|
||||
app.use((err, req, res, next) => {
|
||||
console.error(err.stack);
|
||||
res.status(err.status || 500).json({ success: false, message: err.message || "Internal server error" });
|
||||
});
|
||||
|
||||
// ---- FEATURE: Catch-all 404 handler for undefined routes ----
|
||||
app.use((req, res) => {
|
||||
const filePath = path.join(publicDir, "404.html");
|
||||
|
||||
Reference in New Issue
Block a user