This repository has been archived on 2026-07-27. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
aniwatch-api/src/server.ts
T

136 lines
4.4 KiB
TypeScript

import https from "https";
import { Hono } from "hono";
import { serve } from "@hono/node-server";
import { serveStatic } from "@hono/node-server/serve-static";
import { log } from "./config/logger.js";
import { corsConfig } from "./config/cors.js";
import { ratelimit } from "./config/ratelimit.js";
import { execGracefulShutdown } from "./utils.js";
import { DeploymentEnv, env, SERVERLESS_ENVIRONMENTS } from "./config/env.js";
import { errorHandler, notFoundHandler } from "./config/errorHandler.js";
import type { ServerContext } from "./config/context.js";
import { hianimeRouter } from "./routes/hianime.js";
import { logging } from "./middleware/logging.js";
import { cacheConfigSetter, cacheControl } from "./middleware/cache.js";
import pkgJson from "../package.json" with { type: "json" };
//
const BASE_PATH = "/api/v2" as const;
const app = new Hono<ServerContext>();
app.use(logging);
app.use(corsConfig);
app.use(cacheControl);
/*
CAUTION:
Having the "ANIWATCH_API_HOSTNAME" env will
enable rate limitting for the deployment.
WARNING:
If you are using any serverless environment, you must set the
"ANIWATCH_API_DEPLOYMENT_ENV" to that environment's name,
otherwise you may face issues.
*/
const isPersonalDeployment = Boolean(env.ANIWATCH_API_HOSTNAME);
if (isPersonalDeployment) {
app.use(ratelimit);
}
if (env.ANIWATCH_API_DEPLOYMENT_ENV === DeploymentEnv.NODEJS) {
app.use("/", serveStatic({ root: "public" }));
}
app.get("/health", (c) => c.text("daijoubu", { status: 200 }));
app.get("/v", async (c) =>
c.text(
`aniwatch-api: v${"version" in pkgJson && pkgJson?.version ? pkgJson.version : "-1"}\n` +
`aniwatch-package: v${"dependencies" in pkgJson && pkgJson?.dependencies?.aniwatch ? pkgJson?.dependencies?.aniwatch : "-1"}`
)
);
app.use(cacheConfigSetter(BASE_PATH.length));
app.basePath(BASE_PATH).route("/hianime", hianimeRouter);
app.basePath(BASE_PATH).get("/anicrush", (c) =>
c.text("Anicrush could be implemented in future.")
);
app.notFound(notFoundHandler);
app.onError(errorHandler);
//
(function () {
/*
NOTE:
"ANIWATCH_API_DEPLOYMENT_MODE" env must be set to
its supported name for serverless deployments
Eg: "vercel" for vercel deployments
*/
if (SERVERLESS_ENVIRONMENTS.includes(env.ANIWATCH_API_DEPLOYMENT_ENV)) {
return;
}
const server = serve({
port: env.ANIWATCH_API_PORT,
fetch: app.fetch,
}).addListener("listening", () =>
log.info(
`aniwatch-api RUNNING at http://localhost:${env.ANIWATCH_API_PORT}`
)
);
process.on("SIGINT", () => execGracefulShutdown(server));
process.on("SIGTERM", () => execGracefulShutdown(server));
process.on("uncaughtException", (err) => {
log.error(`Uncaught Exception: ${err.message}`);
execGracefulShutdown(server);
});
process.on("unhandledRejection", (reason, promise) => {
log.error(
`Unhandled Rejection at: ${promise}, reason: ${reason instanceof Error ? reason.message : reason}`
);
execGracefulShutdown(server);
});
/*
CAUTION:
The `if` below block is for `render free deployments` only,
as their free tier has an approx 10 or 15 minute sleep time.
This is to keep the server awake and prevent it from sleeping.
You can enable the automatic health check by setting the
environment variables "ANIWATCH_API_HOSTNAME" to your deployment's hostname,
and "ANIWATCH_API_DEPLOYMENT_ENV" to "render" in your environment variables.
If you are not using render, you can remove the below `if` block.
*/
if (
isPersonalDeployment &&
env.ANIWATCH_API_DEPLOYMENT_ENV === DeploymentEnv.RENDER
) {
const INTERVAL_DELAY = 8 * 60 * 1000; // 8mins
const url = new URL(`https://${env.ANIWATCH_API_HOSTNAME}/health`);
// don't sleep
setInterval(() => {
https
.get(url.href)
.on("response", () => {
log.info(
`aniwatch-api HEALTH_CHECK at ${new Date().toISOString()}`
);
})
.on("error", (err) =>
log.warn(
`aniwatch-api HEALTH_CHECK failed; ${err.message.trim()}`
)
);
}, INTERVAL_DELAY);
}
})();
export default app;