-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.js
More file actions
108 lines (97 loc) · 3.16 KB
/
Copy pathserver.js
File metadata and controls
108 lines (97 loc) · 3.16 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
const http = require("http");
const fs = require("fs");
const path = require("path");
const { URL } = require("url");
const root = __dirname;
const publicDir = path.join(root, "public");
const port = Number(process.env.PORT || 3000);
const mimeTypes = {
".html": "text/html; charset=utf-8",
".css": "text/css; charset=utf-8",
".js": "application/javascript; charset=utf-8",
".json": "application/json; charset=utf-8",
".svg": "image/svg+xml",
".png": "image/png",
".ico": "image/x-icon"
};
function send(res, status, body, type = "application/json; charset=utf-8") {
res.writeHead(status, {
"Content-Type": type,
"Cache-Control": "no-store",
"Access-Control-Allow-Origin": "*"
});
res.end(body);
}
function normalizeRedditUrl(input) {
const url = new URL(input);
if (!["www.reddit.com", "old.reddit.com", "reddit.com"].includes(url.hostname)) {
throw new Error("Only reddit.com links are supported.");
}
url.hostname = "www.reddit.com";
url.searchParams.set("limit", "500");
url.searchParams.set("sort", "confidence");
if (!url.pathname.endsWith(".json")) {
url.pathname = url.pathname.replace(/\/?$/, ".json");
}
return url.toString();
}
async function fetchReddit(url) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 15000);
try {
const response = await fetch(url, {
signal: controller.signal,
headers: {
"User-Agent": "RedditDebateAnalyzer/1.0 hackathon demo by local user",
"Accept": "application/json"
}
});
const text = await response.text();
if (!response.ok) {
throw new Error(`Reddit returned ${response.status}: ${text.slice(0, 160)}`);
}
JSON.parse(text);
return text;
} finally {
clearTimeout(timeout);
}
}
function serveStatic(req, res) {
const requested = decodeURIComponent(new URL(req.url, `http://localhost:${port}`).pathname);
const cleanPath = requested === "/" ? "/index.html" : requested;
const filePath = path.normalize(path.join(publicDir, cleanPath));
if (!filePath.startsWith(publicDir)) {
send(res, 403, "Forbidden", "text/plain; charset=utf-8");
return;
}
fs.readFile(filePath, (err, data) => {
if (err) {
send(res, 404, "Not found", "text/plain; charset=utf-8");
return;
}
const type = mimeTypes[path.extname(filePath)] || "application/octet-stream";
send(res, 200, data, type);
});
}
const server = http.createServer(async (req, res) => {
const requestUrl = new URL(req.url, `http://localhost:${port}`);
if (requestUrl.pathname === "/api/reddit") {
try {
const target = requestUrl.searchParams.get("url");
if (!target) {
send(res, 400, JSON.stringify({ error: "Missing url parameter." }));
return;
}
const redditUrl = normalizeRedditUrl(target);
const body = await fetchReddit(redditUrl);
send(res, 200, body);
} catch (error) {
send(res, 502, JSON.stringify({ error: error.message || "Unable to fetch Reddit thread." }));
}
return;
}
serveStatic(req, res);
});
server.listen(port, () => {
console.log(`Reddit Debate Analyzer running at http://localhost:${port}`);
});