-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathindex.js
More file actions
117 lines (103 loc) · 4.55 KB
/
Copy pathindex.js
File metadata and controls
117 lines (103 loc) · 4.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/*
* ======= • ======= • ======= • ======= • =======• =======
* AniNewsAPI — index.js
* Repository: https://github.com/Shineii86/AniNewsAPI
*
* @description
* Vercel serverless entry point. Serves the landing page
* (cached in memory) for HTML requests, or returns the
* API index JSON for API clients. The landing page is
* only re-read from disk when the file's mtime changes.
*
* @exports Vercel serverless function handler
*
* @version 4.1.6
* @author Shinei Nouzen
* @license MIT
* ======= • ======= • ======= • ======= • =======• =======
*/
const fs = require('fs');
const path = require('path');
const { APP_NAME, APP_VERSION, APP_DESCRIPTION } = require('./utils/constants');
// ══════════════════════════════════════════════════════════════
// IN-MEMORY LANDING PAGE CACHE
// ══════════════════════════════════════════════════════════════
// ---- FEATURE: Landing page memory cache ----
/**
* Cached HTML content and its file modification time.
* NOTE: Avoids disk read on every request — only re-reads
* when the file's mtime changes (hot reload in dev).
*
* @type {string|null}
*/
let cachedHtml = null;
/** @type {number} Last known mtimeMs of index.html */
let cachedHtmlMtime = 0;
// ══════════════════════════════════════════════════════════════
// REQUEST HANDLER
// ══════════════════════════════════════════════════════════════
// ---- FEATURE: Vercel serverless handler ----
/**
* Vercel serverless function handler.
*
* Behavior:
* - HTML request (Accept: text/html or /) → serve cached landing page
* - API request (Accept: application/json) → return API index JSON
*
* @param {Object} req - Vercel request object
* @param {Object} res - Vercel response object
*/
module.exports = (req, res) => {
try {
const accept = req.headers.accept || '';
// ─── Landing page (cached in memory) ───
if (accept.includes('text/html') || req.url === '/' || req.url === '') {
const filePath = path.join(__dirname, 'public', 'index.html');
const stat = fs.statSync(filePath);
// Re-read only if file changed (hot reload support)
if (!cachedHtml || stat.mtimeMs !== cachedHtmlMtime) {
cachedHtml = fs.readFileSync(filePath, 'utf8');
cachedHtmlMtime = stat.mtimeMs;
}
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.setHeader('Cache-Control', 'public, s-maxage=60, stale-while-revalidate=300');
return res.status(200).send(cachedHtml);
}
// ─── API index (JSON) ───
res.setHeader('Content-Type', 'application/json');
res.setHeader('Cache-Control', 'public, s-maxage=300');
res.status(200).json({
name: APP_NAME,
version: APP_VERSION,
description: APP_DESCRIPTION,
documentation: 'https://aninews.vercel.app',
openapi: 'https://aninews.vercel.app/api/openapi',
endpoints: {
'GET /api/news': 'Latest anime news with pagination, sorting, source filtering',
'GET /api/news/tags': 'Tag listing with counts, or filter by tag',
'GET /api/news/:slug': 'Full article content extraction',
'GET /api/search': 'Full-text search with relevance scoring',
'GET /api/sources': 'Source health & stats per source',
'GET /api/rss': 'RSS 2.0 XML feed',
'GET /api/health': 'Health check',
'GET /api/stats': 'Cache statistics',
'GET /api/stream': 'SSE initial burst',
'GET /api/openapi': 'OpenAPI 3.0.3 spec',
'POST /api/cache/clear': 'Flush cache'
},
sources: [
'Anime News Network',
'Anime Corner',
'MyAnimeList',
'Otaku USA Magazine',
'Crunchyroll',
'Anime Herald',
'Comic Book'
],
timestamp: new Date().toISOString()
});
} catch (error) {
res.status(500).json({ error: 'Internal server error', message: error.message });
}
};
// ══════════════════════════════════════════════════════════════ END: index.js