From 01f5e57864090a670d1c100a74331f67b81f2611 Mon Sep 17 00:00:00 2001 From: alikia2x Date: Sat, 10 May 2025 00:20:20 +0800 Subject: [PATCH] ref: dir structure of backend package --- packages/backend/db/config.ts | 2 +- packages/backend/db/db.ts | 2 +- .../db/{videoSnapshot.ts => snapshots.ts} | 0 .../{routes/root => lib/const}/singers.ts | 0 packages/backend/middleware/contentType.ts | 2 +- packages/backend/middleware/index.ts | 4 +- packages/backend/middleware/logger.ts | 2 +- .../backend/middleware/preetifyResponse.ts | 2 +- packages/backend/middleware/rateLimiters.ts | 2 +- packages/backend/routes/404.ts | 2 +- packages/backend/routes/index.ts | 44 ++++++++++++------- packages/backend/routes/ping.ts | 24 ---------- packages/backend/routes/ping/index.ts | 24 ++++++++++ packages/backend/routes/root/root.ts | 29 ------------ packages/backend/routes/user/index.ts | 1 + .../routes/{user.ts => user/register.ts} | 8 ++-- .../video/[id]/info.ts} | 24 +++++----- .../routes/{ => video/[id]}/snapshots.ts | 6 +-- packages/backend/routes/video/index.ts | 2 + packages/backend/src/main.ts | 2 +- packages/backend/src/routing.ts | 16 +++++++ packages/backend/src/schema.d.ts | 4 +- packages/backend/src/startServer.ts | 2 +- 23 files changed, 103 insertions(+), 101 deletions(-) rename packages/backend/db/{videoSnapshot.ts => snapshots.ts} (100%) rename packages/backend/{routes/root => lib/const}/singers.ts (100%) delete mode 100644 packages/backend/routes/ping.ts create mode 100644 packages/backend/routes/ping/index.ts delete mode 100644 packages/backend/routes/root/root.ts create mode 100644 packages/backend/routes/user/index.ts rename packages/backend/routes/{user.ts => user/register.ts} (99%) rename packages/backend/{db/videoInfo.ts => routes/video/[id]/info.ts} (84%) rename packages/backend/routes/{ => video/[id]}/snapshots.ts (94%) create mode 100644 packages/backend/routes/video/index.ts create mode 100644 packages/backend/src/routing.ts diff --git a/packages/backend/db/config.ts b/packages/backend/db/config.ts index 59ab5fa..9d0e514 100644 --- a/packages/backend/db/config.ts +++ b/packages/backend/db/config.ts @@ -35,7 +35,7 @@ export const postgresCredConfigNpm = { database: databaseNameCred, username: databaseUser, password: databasePassword -} +}; export const postgresConfigCred = { hostname: databaseHost, diff --git a/packages/backend/db/db.ts b/packages/backend/db/db.ts index 9643fa5..6dd488b 100644 --- a/packages/backend/db/db.ts +++ b/packages/backend/db/db.ts @@ -2,4 +2,4 @@ import postgres from "postgres"; import { postgresConfigNpm, postgresCredConfigNpm } from "./config"; export const sql = postgres(postgresConfigNpm); -export const sqlCred = postgres(postgresCredConfigNpm) \ No newline at end of file +export const sqlCred = postgres(postgresCredConfigNpm); diff --git a/packages/backend/db/videoSnapshot.ts b/packages/backend/db/snapshots.ts similarity index 100% rename from packages/backend/db/videoSnapshot.ts rename to packages/backend/db/snapshots.ts diff --git a/packages/backend/routes/root/singers.ts b/packages/backend/lib/const/singers.ts similarity index 100% rename from packages/backend/routes/root/singers.ts rename to packages/backend/lib/const/singers.ts diff --git a/packages/backend/middleware/contentType.ts b/packages/backend/middleware/contentType.ts index 7e47d75..edd4908 100644 --- a/packages/backend/middleware/contentType.ts +++ b/packages/backend/middleware/contentType.ts @@ -3,4 +3,4 @@ import { Context, Next } from "hono"; export const contentType = async (c: Context, next: Next) => { await next(); c.header("Content-Type", "application/json; charset=utf-8"); -}; \ No newline at end of file +}; diff --git a/packages/backend/middleware/index.ts b/packages/backend/middleware/index.ts index a43357e..4bd98af 100644 --- a/packages/backend/middleware/index.ts +++ b/packages/backend/middleware/index.ts @@ -8,7 +8,7 @@ import { logger } from "./logger.ts"; import { timing } from "hono/timing"; import { contentType } from "./contentType.ts"; -export function configureMiddleWares(app: Hono<{Variables: Variables }>) { +export function configureMiddleWares(app: Hono<{ Variables: Variables }>) { app.use("*", contentType); app.use(timing()); app.use("*", preetifyResponse); @@ -16,4 +16,4 @@ export function configureMiddleWares(app: Hono<{Variables: Variables }>) { app.post("/user", registerRateLimiter); app.all("/ping", bodyLimitForPing, ...pingHandler); -} \ No newline at end of file +} diff --git a/packages/backend/middleware/logger.ts b/packages/backend/middleware/logger.ts index 0625157..59a52b7 100644 --- a/packages/backend/middleware/logger.ts +++ b/packages/backend/middleware/logger.ts @@ -77,7 +77,7 @@ const defaultFormatter = (params) => { `${methodColor} ${params.method.padEnd(6)}${reset} ${params.path}` ); }; -type Ctx = Context +type Ctx = Context; export const logger = (config) => { const { formatter = defaultFormatter, output = console, skipPaths = [], skip = null } = config; diff --git a/packages/backend/middleware/preetifyResponse.ts b/packages/backend/middleware/preetifyResponse.ts index 5cf289b..ccc5944 100644 --- a/packages/backend/middleware/preetifyResponse.ts +++ b/packages/backend/middleware/preetifyResponse.ts @@ -15,4 +15,4 @@ export const preetifyResponse = async (c: Context, next: Next) => { endTime(c, "seralize"); c.res = new Response(prettyJson, { headers: { "Content-Type": "text/plain; charset=utf-8" } }); } -}; \ No newline at end of file +}; diff --git a/packages/backend/middleware/rateLimiters.ts b/packages/backend/middleware/rateLimiters.ts index 3c62d7f..c9e444a 100644 --- a/packages/backend/middleware/rateLimiters.ts +++ b/packages/backend/middleware/rateLimiters.ts @@ -24,4 +24,4 @@ export const registerRateLimiter = rateLimiter({ // @ts-expect-error - Known issue: the `c`all` function is not present in @types/ioredis sendCommand: (...args: string[]) => redis.call(...args) }) as unknown as Store -}); \ No newline at end of file +}); diff --git a/packages/backend/routes/404.ts b/packages/backend/routes/404.ts index 910b9f9..4ff883a 100644 --- a/packages/backend/routes/404.ts +++ b/packages/backend/routes/404.ts @@ -7,4 +7,4 @@ export const notFoundRoute = (c: Context) => { }, 404 ); -}; \ No newline at end of file +}; diff --git a/packages/backend/routes/index.ts b/packages/backend/routes/index.ts index 86ebf07..de83d72 100644 --- a/packages/backend/routes/index.ts +++ b/packages/backend/routes/index.ts @@ -1,17 +1,29 @@ -import { rootHandler } from "./root/root.ts"; -import { pingHandler } from "./ping.ts"; -import { getSnapshotsHanlder } from "./snapshots.ts"; -import { registerHandler } from "./user.ts"; -import { videoInfoHandler } from "db/videoInfo.ts"; -import { Hono } from "hono"; -import { Variables } from "hono/types"; +import { getSingerForBirthday, pickSinger, pickSpecialSinger, type Singer } from "lib/const/singers.ts"; +import { VERSION } from "src/main.ts"; +import { createHandlers } from "src/utils.ts"; -export function configureRoutes(app: Hono<{Variables: Variables }>) { - app.get("/", ...rootHandler); - app.all("/ping", ...pingHandler); - - app.get("/video/:id/snapshots", ...getSnapshotsHanlder); - app.post("/user", ...registerHandler); - - app.get("/video/:id/info", ...videoInfoHandler); -} \ No newline at end of file +export const rootHandler = createHandlers((c) => { + let singer: Singer | Singer[]; + const shouldShowSpecialSinger = Math.random() < 0.016; + if (getSingerForBirthday().length !== 0) { + singer = JSON.parse(JSON.stringify(getSingerForBirthday())) as Singer[]; + for (const s of singer) { + delete s.birthday; + s.message = `祝${s.name}生日快乐~`; + } + } else if (shouldShowSpecialSinger) { + singer = pickSpecialSinger(); + } else { + singer = pickSinger(); + } + return c.json({ + project: { + name: "中V档案馆", + motto: "一起唱吧,心中的歌!" + }, + status: 200, + version: VERSION, + time: Date.now(), + singer: singer + }); +}); diff --git a/packages/backend/routes/ping.ts b/packages/backend/routes/ping.ts deleted file mode 100644 index a84b588..0000000 --- a/packages/backend/routes/ping.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { getClientIP } from "middleware/logger.ts"; -import { createHandlers } from "../src/utils.ts"; -import { VERSION } from "../src/main.ts"; - -export const pingHandler = createHandlers(async (c) => { - const requestHeaders = c.req.raw.headers; - return c.json({ - "message": "pong", - "request": { - "headers": requestHeaders, - "ip": getClientIP(c), - "mode": c.req.raw.mode, - "method": c.req.method, - "query": new URL(c.req.url).searchParams, - "body": await c.req.text(), - "url": c.req.raw.url - }, - "response": { - "time": new Date().getTime(), - "status": 200, - "version": VERSION, - } - }); -}); \ No newline at end of file diff --git a/packages/backend/routes/ping/index.ts b/packages/backend/routes/ping/index.ts new file mode 100644 index 0000000..c1caecb --- /dev/null +++ b/packages/backend/routes/ping/index.ts @@ -0,0 +1,24 @@ +import { getClientIP } from "middleware/logger.ts"; +import { createHandlers } from "src/utils.ts"; +import { VERSION } from "src/main.ts"; + +export const pingHandler = createHandlers(async (c) => { + const requestHeaders = c.req.raw.headers; + return c.json({ + message: "pong", + request: { + headers: requestHeaders, + ip: getClientIP(c), + mode: c.req.raw.mode, + method: c.req.method, + query: new URL(c.req.url).searchParams, + body: await c.req.text(), + url: c.req.raw.url + }, + response: { + time: new Date().getTime(), + status: 200, + version: VERSION + } + }); +}); diff --git a/packages/backend/routes/root/root.ts b/packages/backend/routes/root/root.ts deleted file mode 100644 index d570bce..0000000 --- a/packages/backend/routes/root/root.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { getSingerForBirthday, pickSinger, pickSpecialSinger, type Singer } from "./singers.ts"; -import { VERSION } from "../../src/main.ts"; -import { createHandlers } from "../../src/utils.ts"; - -export const rootHandler = createHandlers((c) => { - let singer: Singer | Singer[]; - const shouldShowSpecialSinger = Math.random() < 0.016; - if (getSingerForBirthday().length !== 0) { - singer = JSON.parse(JSON.stringify(getSingerForBirthday())) as Singer[]; - for (const s of singer) { - delete s.birthday; - s.message = `祝${s.name}生日快乐~`; - } - } else if (shouldShowSpecialSinger) { - singer = pickSpecialSinger(); - } else { - singer = pickSinger(); - } - return c.json({ - project: { - name: "中V档案馆", - motto: "一起唱吧,心中的歌!" - }, - status: 200, - version: VERSION, - time: Date.now(), - singer: singer - }); -}); diff --git a/packages/backend/routes/user/index.ts b/packages/backend/routes/user/index.ts new file mode 100644 index 0000000..d9f7bf7 --- /dev/null +++ b/packages/backend/routes/user/index.ts @@ -0,0 +1 @@ +export * from "./register.ts"; diff --git a/packages/backend/routes/user.ts b/packages/backend/routes/user/register.ts similarity index 99% rename from packages/backend/routes/user.ts rename to packages/backend/routes/user/register.ts index 15cca30..bf1aeeb 100644 --- a/packages/backend/routes/user.ts +++ b/packages/backend/routes/user/register.ts @@ -44,7 +44,7 @@ export const registerHandler = createHandlers(async (c: ContextType) => { const response: StatusResponse = { message: `User '${username}' registered successfully.` - } + }; return c.json(response, 201); } catch (e) { @@ -53,21 +53,21 @@ export const registerHandler = createHandlers(async (c: ContextType) => { message: "Invalid registration data.", errors: e.errors, code: "INVALID_PAYLOAD" - } + }; return c.json>(response, 400); } else if (e instanceof SyntaxError) { const response: ErrorResponse = { message: "Invalid JSON payload.", errors: [e.message], code: "INVALID_FORMAT" - } + }; return c.json>(response, 400); } else { const response: ErrorResponse = { message: "Invalid JSON payload.", errors: [(e as Error).message], code: "UNKNOWN_ERR" - } + }; return c.json>(response, 500); } } diff --git a/packages/backend/db/videoInfo.ts b/packages/backend/routes/video/[id]/info.ts similarity index 84% rename from packages/backend/db/videoInfo.ts rename to packages/backend/routes/video/[id]/info.ts index e9fee88..ad64045 100644 --- a/packages/backend/db/videoInfo.ts +++ b/packages/backend/routes/video/[id]/info.ts @@ -1,15 +1,15 @@ import logger from "@core/log/logger.ts"; import { redis } from "@core/db/redis.ts"; -import { sql } from "./db.ts"; +import { sql } from "../../../db/db.ts"; import { number, ValidationError } from "yup"; -import { createHandlers } from "../src/utils.ts"; +import { createHandlers } from "../../../src/utils.ts"; import { getVideoInfo, getVideoInfoByBV } from "@core/net/getVideoInfo.ts"; -import { idSchema } from "../routes/snapshots.ts"; +import { idSchema } from "./snapshots.ts"; import { NetSchedulerError } from "@core/net/delegate.ts"; import type { Context } from "hono"; import type { BlankEnv, BlankInput } from "hono/types"; import type { VideoInfoData } from "@core/net/bilibili.d.ts"; -import { startTime, endTime } from 'hono/timing' +import { startTime, endTime } from "hono/timing"; const CACHE_EXPIRATION_SECONDS = 60; @@ -34,7 +34,7 @@ async function insertVideoSnapshot(data: VideoInfoData) { } export const videoInfoHandler = createHandlers(async (c: ContextType) => { - startTime(c, 'parse', 'Parse the request'); + startTime(c, "parse", "Parse the request"); try { const id = await idSchema.validate(c.req.param("id")); let videoId: string | number = id as string; @@ -45,33 +45,33 @@ export const videoInfoHandler = createHandlers(async (c: ContextType) => { } const cacheKey = `cvsa:videoInfo:${videoId}`; - endTime(c, 'parse'); - startTime(c, 'cache', 'Check for cached data'); + endTime(c, "parse"); + startTime(c, "cache", "Check for cached data"); const cachedData = await redis.get(cacheKey); - endTime(c, 'cache'); + endTime(c, "cache"); if (cachedData) { return c.json(JSON.parse(cachedData)); } - startTime(c, 'net', 'Fetch data'); + startTime(c, "net", "Fetch data"); let result: VideoInfoData | number; if (typeof videoId === "number") { result = await getVideoInfo(videoId, "getVideoInfo"); } else { result = await getVideoInfoByBV(videoId, "getVideoInfo"); } - endTime(c, 'net'); + endTime(c, "net"); if (typeof result === "number") { return c.json({ message: "Error fetching video info", code: result }, 500); } - startTime(c, 'db', 'Write data to database'); + startTime(c, "db", "Write data to database"); await redis.setex(cacheKey, CACHE_EXPIRATION_SECONDS, JSON.stringify(result)); await insertVideoSnapshot(result); - endTime(c, 'db'); + endTime(c, "db"); return c.json(result); } catch (e) { if (e instanceof ValidationError) { diff --git a/packages/backend/routes/snapshots.ts b/packages/backend/routes/video/[id]/snapshots.ts similarity index 94% rename from packages/backend/routes/snapshots.ts rename to packages/backend/routes/video/[id]/snapshots.ts index 956a452..a946e75 100644 --- a/packages/backend/routes/snapshots.ts +++ b/packages/backend/routes/video/[id]/snapshots.ts @@ -1,10 +1,10 @@ import type { Context } from "hono"; -import { createHandlers } from "../src/utils.ts"; +import { createHandlers } from "src/utils.ts"; import type { BlankEnv, BlankInput } from "hono/types"; -import { getVideoSnapshots, getVideoSnapshotsByBV } from "../db/videoSnapshot.ts"; +import { getVideoSnapshots, getVideoSnapshotsByBV } from "db/snapshots.ts"; import type { VideoSnapshotType } from "@core/db/schema.d.ts"; import { boolean, mixed, number, object, ValidationError } from "yup"; -import { ErrorResponse } from "../src/schema"; +import { ErrorResponse } from "src/schema"; import { startTime, endTime } from "hono/timing"; const SnapshotQueryParamsSchema = object({ diff --git a/packages/backend/routes/video/index.ts b/packages/backend/routes/video/index.ts new file mode 100644 index 0000000..4c7abb2 --- /dev/null +++ b/packages/backend/routes/video/index.ts @@ -0,0 +1,2 @@ +export * from "./[id]/info"; +export * from "./[id]/snapshots"; diff --git a/packages/backend/src/main.ts b/packages/backend/src/main.ts index 06285a7..61ababf 100644 --- a/packages/backend/src/main.ts +++ b/packages/backend/src/main.ts @@ -1,7 +1,7 @@ import { Hono } from "hono"; import type { TimingVariables } from "hono/timing"; import { startServer } from "./startServer.ts"; -import { configureRoutes } from "routes"; +import { configureRoutes } from "./routing.ts"; import { configureMiddleWares } from "middleware"; import { notFoundRoute } from "routes/404.ts"; diff --git a/packages/backend/src/routing.ts b/packages/backend/src/routing.ts new file mode 100644 index 0000000..300e19d --- /dev/null +++ b/packages/backend/src/routing.ts @@ -0,0 +1,16 @@ +import { rootHandler } from "routes"; +import { pingHandler } from "routes/ping"; +import { registerHandler } from "routes/user"; +import { videoInfoHandler, getSnapshotsHanlder } from "routes/video"; +import { Hono } from "hono"; +import { Variables } from "hono/types"; + +export function configureRoutes(app: Hono<{ Variables: Variables }>) { + app.get("/", ...rootHandler); + app.all("/ping", ...pingHandler); + + app.get("/video/:id/snapshots", ...getSnapshotsHanlder); + app.post("/user", ...registerHandler); + + app.get("/video/:id/info", ...videoInfoHandler); +} diff --git a/packages/backend/src/schema.d.ts b/packages/backend/src/schema.d.ts index 279a83d..b80ea49 100644 --- a/packages/backend/src/schema.d.ts +++ b/packages/backend/src/schema.d.ts @@ -1,11 +1,11 @@ type ErrorCode = "INVALID_QUERY_PARAMS" | "UNKNOWN_ERR" | "INVALID_PAYLOAD" | "INVALID_FORMAT" | "BODY_TOO_LARGE"; export interface ErrorResponse { - code: ErrorCode + code: ErrorCode; message: string; errors: E[]; } export interface StatusResponse { message: string; -} \ No newline at end of file +} diff --git a/packages/backend/src/startServer.ts b/packages/backend/src/startServer.ts index 1719393..4df0c07 100644 --- a/packages/backend/src/startServer.ts +++ b/packages/backend/src/startServer.ts @@ -32,7 +32,7 @@ function logStartup(hostname: string, port: number, wasAutoIncremented: boolean, console.log("\nPress Ctrl+C to quit."); } -export async function startServer(app: Hono<{Variables: Variables }>) { +export async function startServer(app: Hono<{ Variables: Variables }>) { const NODE_ENV = process.env.NODE_ENV || "production"; const HOST = process.env.HOST ?? (NODE_ENV === "development" ? "0.0.0.0" : "127.0.0.1"); const PORT = process.env.PORT ? parseInt(process.env.PORT, 10) : undefined;