-
-
Notifications
You must be signed in to change notification settings - Fork 217
/
Copy pathserver.ts
110 lines (88 loc) · 3.06 KB
/
server.ts
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
import https from "https";
import { config } from "dotenv";
import corsConfig from "./config/cors.js";
import { ratelimit } from "./config/ratelimit.js";
import { hianimeRouter } from "./routes/hianime.js";
import cacheControlMiddleware from "./config/cache_control_middleware.js";
import { Hono } from "hono";
import { logger } from "hono/logger";
import { serve } from "@hono/node-server";
import { serveStatic } from "@hono/node-server/serve-static";
import { HiAnimeError } from "aniwatch";
import { AniwatchAPICache } from "./config/cache.js";
import pkgJson from "../package.json" with { type: "json" };
import type { AniwatchAPIVariables } from "./config/variables.js";
config();
const BASE_PATH = "/api/v2" as const;
const PORT: number = Number(process.env.ANIWATCH_API_PORT) || 4000;
const ANIWATCH_API_HOSTNAME = process.env?.ANIWATCH_API_HOSTNAME;
const app = new Hono<{ Variables: AniwatchAPIVariables }>();
app.use(logger());
app.use(corsConfig);
app.use(cacheControlMiddleware);
// CAUTION: For personal deployments, "refrain" from having an env
// named "ANIWATCH_API_HOSTNAME". You may face rate limitting
// or other issues if you do.
const ISNT_PERSONAL_DEPLOYMENT = Boolean(ANIWATCH_API_HOSTNAME);
if (ISNT_PERSONAL_DEPLOYMENT) {
app.use(ratelimit);
}
app.use("/", serveStatic({ root: "public" }));
app.get("/health", (c) => c.text("OK", { status: 200 }));
app.get("/v", async (c) =>
c.text(
`v${"version" in pkgJson && pkgJson?.version ? pkgJson.version : "-1"}`
)
);
app.use(async (c, next) => {
const { pathname, search } = new URL(c.req.url);
c.set("CACHE_CONFIG", {
key: `${pathname.slice(BASE_PATH.length) + search}`,
duration: Number(
c.req.header(AniwatchAPICache.CACHE_EXPIRY_HEADER_NAME) ||
AniwatchAPICache.DEFAULT_CACHE_EXPIRY_SECONDS
),
});
await next();
});
app.basePath(BASE_PATH).route("/hianime", hianimeRouter);
app
.basePath(BASE_PATH)
.get("/anicrush", (c) => c.text("Anicrush could be implemented in future."));
app.notFound((c) =>
c.json({ status: 404, message: "Resource Not Found" }, 404)
);
app.onError((err, c) => {
console.error(err);
const res = { status: 500, message: "Internal Server Error" };
if (err instanceof HiAnimeError) {
res.status = err.status;
res.message = err.message;
}
return c.json(res, { status: res.status });
});
// NOTE: this env is "required" for vercel deployments
if (!Boolean(process.env?.ANIWATCH_API_VERCEL_DEPLOYMENT)) {
serve({
port: PORT,
fetch: app.fetch,
}).addListener("listening", () =>
console.info(
"\x1b[1;36m" + `aniwatch-api at http://localhost:${PORT}` + "\x1b[0m"
)
);
// NOTE: remove the `if` block below for personal deployments
if (ISNT_PERSONAL_DEPLOYMENT) {
const interval = 9 * 60 * 1000; // 9mins
// don't sleep
setInterval(() => {
console.log("aniwatch-api HEALTH_CHECK at", new Date().toISOString());
https
.get(`https://${ANIWATCH_API_HOSTNAME}/health`)
.on("error", (err) => {
console.error(err.message);
});
}, interval);
}
}
export default app;