diff --git a/CHANGELOG.md b/CHANGELOG.md index 5096897..2e9b8f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [2.0.0] - 2026-07-04 ### Added +- **Creator Info Middleware**: Injects creator attribution (name, GitHub, Telegram, timestamp) into every JSON response - **Multi-Mirror Fallback System**: Automatic failover across 5 mirror domains (anikototv.to, anikoto.cz, anikoto.me, anikoto.net, anikototv.se) - **LRU Cache with Configurable TTL**: Replaced basic Map cache with LRU cache supporting per-endpoint TTL (3min to 60min) - **Response Compression**: Added gzip compression middleware (level 6, 1024 byte threshold) diff --git a/README.md b/README.md index 6ac8334..44c170e 100644 --- a/README.md +++ b/README.md @@ -339,6 +339,9 @@ AniKotoAPI/ │ │ ├── 📄 extractPages.helper.js # 📃 Page fetcher │ │ └── 📄 formatTitle.helper.js # 🔤 Title formatter │ │ +│ ├── 📂 middleware/ # 🔧 Express middleware +│ │ └── 📄 creatorInfo.js # ✍️ Creator attribution injection +│ │ │ └── 📂 routes/ # 🛤️ Express routes │ ├── 📄 apiRoutes.js # 🌐 Main API routes (33 endpoints) │ └── 📄 category.route.js # 🏷️ Category routes diff --git a/docs/architecture.md b/docs/architecture.md index d2363fa..fd32151 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -84,6 +84,9 @@ AniKotoAPI/ │ ├── mirror.helper.js # Multi-mirror fallback (5 domains) │ └── extractPages.helper.js # Page fetching with mirror fallback │ +│ └── middleware/ +│ └── creatorInfo.js # Creator attribution injection +│ ├── test.js # Test suite (27 tests) ├── agents/ # AI agent prompts (6 agents) └── CHANGELOG.md # Version history @@ -100,6 +103,7 @@ AniKotoAPI/ | Caching | LRU cache with configurable TTL | | Compression | gzip (level 6, 1024 byte threshold) | | Mirror Fallback | 5 mirror domains with health checks | +| Middleware | Creator info attribution injection | | Static Files | Express static middleware | ## Request Flow @@ -109,6 +113,14 @@ Client Request ↓ Vercel Routes (/api/* → server.js) ↓ +Express Middleware + ↓ +├── CORS +├── Security Headers +├── Compression (gzip) +├── Rate Limiting +├── Creator Info Injection + ↓ Express Router (apiRoutes.js) ↓ Controller (e.g., search.controller.js) @@ -119,7 +131,7 @@ HTTP Request to anikototv.to (with headers) ↓ Cheerio parses HTML response ↓ -Returns structured JSON +Returns structured JSON with Creator Attribution ↓ Client Response ``` diff --git a/server.js b/server.js index 462071c..515f763 100644 --- a/server.js +++ b/server.js @@ -26,6 +26,7 @@ import { fileURLToPath } from "url"; import { dirname } from "path"; import { createApiRoutes } from "./src/routes/apiRoutes.js"; import { getCacheStats } from "./src/helper/cache.helper.js"; +import { addCreatorInfo } from "./src/middleware/creatorInfo.js"; dotenv.config(); @@ -160,6 +161,9 @@ app.get("/api/cache/stats", (req, res) => { res.json({ success: true, results: getCacheStats() }); }); +// ---- FEATURE: Creator info middleware (injects attribution into all responses) ---- +app.use(addCreatorInfo); + createApiRoutes(app, jsonResponse, jsonError); // ══════════════════════════════════════════════════════════════ diff --git a/src/middleware/creatorInfo.js b/src/middleware/creatorInfo.js new file mode 100644 index 0000000..ae95823 --- /dev/null +++ b/src/middleware/creatorInfo.js @@ -0,0 +1,74 @@ +/* + * ======= • ======= • ======= • ======= • =======• ======= + * AniKotoAPI — creatorInfo.js + * Repository: https://github.com/Shineii86/AniKotoAPI + * + * @description + * Express middleware that injects creator attribution metadata + * into every JSON API response. Overrides res.json() to append + * author details and a server-side timestamp, unless the + * response already contains creator information. + * + * @exports + * addCreatorInfo — Express middleware function + * creatorInfo — Static creator metadata object + * + * @author Shinei Nouzen + * @license MIT + * ======= • ======= • ======= • ======= • =======• ======= + */ + +// ══════════════════════════════════════════════════════════════ +// CREATOR METADATA +// ══════════════════════════════════════════════════════════════ + +// NOTE: Injected into every response unless `data.creator` already exists +const creatorInfo = { + creator: "Shinei Nouzen", + github: "https://github.com/Shineii86", + telegram: "https://telegram.me/Shineii86", + message: "Built with ❤️ by Shinei Nouzen", +}; + +// ══════════════════════════════════════════════════════════════ +// MIDDLEWARE +// ══════════════════════════════════════════════════════════════ + +/** + * Express middleware that enriches every JSON response with + * creator attribution and an IN-formatted timestamp. + * + * @param {import('express').Request} req - Express request object + * @param {import('express').Response} res - Express response object + * @param {import('express').NextFunction} next - Passes control forward + * @returns {void} + * + * @example + * app.use(addCreatorInfo); + * res.json({ anime: [...] }); + * // Response includes creator, github, telegram, message, and timestamp + */ +const addCreatorInfo = (req, res, next) => { + // NOTE: Preserve the original json method so we can wrap it + const originalJson = res.json.bind(res); + + // NOTE: Monkey-patch res.json to inject creator info + res.json = (data) => { + // NOTE: Only inject if data is a plain object and missing creator field + if (typeof data === "object" && data !== null && !data.creator) { + data = { + ...data, + creator: creatorInfo.creator, + github: creatorInfo.github, + telegram: creatorInfo.telegram, + timestamp: new Date().toLocaleString("en-IN", { timeZone: "Asia/Kolkata", hour12: true }), + }; + } + return originalJson(data); + }; + + next(); +}; + +export { addCreatorInfo, creatorInfo }; +// ══════════════════════════════════════════════════════════════ END: creatorInfo.js