From e8d4994cec106bf6df887c3d07b877dfa4e79377 Mon Sep 17 00:00:00 2001 From: CPTProgrammer <46586216+CPTProgrammer@users.noreply.github.com> Date: Fri, 10 Jul 2026 06:54:14 +0800 Subject: [PATCH] Add TTL cache for game version/loader APIs and enrich upload metadata --- src/lib/curseforge/game.ts | 41 ++++++++++++++++------------ src/lib/modrinth/tag.ts | 33 ++++++++++++---------- src/lib/utils/cache.ts | 16 +++++++++++ src/services/platforms/curseforge.ts | 15 +++++++++- src/services/platforms/modrinth.ts | 1 + 5 files changed, 73 insertions(+), 33 deletions(-) create mode 100644 src/lib/utils/cache.ts diff --git a/src/lib/curseforge/game.ts b/src/lib/curseforge/game.ts index 8e3405a..e1ca387 100644 --- a/src/lib/curseforge/game.ts +++ b/src/lib/curseforge/game.ts @@ -5,31 +5,36 @@ import { type GameVersionType, type GameVersion, } from "@/types"; +import { createTtlCache } from "@/lib/utils/cache"; + +const TTL = 3_600_000; // 1 hour /** - * 获取 CurseForge 支持的 Game Version Type 列表。 + * 获取 CurseForge 支持的 Game Version Type 列表(1h 缓存)。 * GET /api/game/version-types */ -export async function getVersionTypes( - client: CurseForgeClient, -): Promise { - const data = await client.uploadGet( - "/api/game/version-types", - ); - return GameVersionTypeSchema.array().parse(data); -} +export const getVersionTypes = createTtlCache( + async (client: CurseForgeClient): Promise => { + const data = await client.uploadGet( + "/api/game/version-types", + ); + return GameVersionTypeSchema.array().parse(data); + }, + TTL, +); /** - * 获取 CurseForge 支持的 Game Version 列表。 + * 获取 CurseForge 支持的 Game Version 列表(1h 缓存)。 * 包含 Minecraft 版本、Mod Loader、Environment、Java 等。 * 通过 gameVersionTypeID 区分类型。 * GET /api/game/versions */ -export async function getGameVersions( - client: CurseForgeClient, -): Promise { - const data = await client.uploadGet( - "/api/game/versions", - ); - return GameVersionSchema.array().parse(data); -} +export const getGameVersions = createTtlCache( + async (client: CurseForgeClient): Promise => { + const data = await client.uploadGet( + "/api/game/versions", + ); + return GameVersionSchema.array().parse(data); + }, + TTL, +); diff --git a/src/lib/modrinth/tag.ts b/src/lib/modrinth/tag.ts index 4333977..12a17c9 100644 --- a/src/lib/modrinth/tag.ts +++ b/src/lib/modrinth/tag.ts @@ -5,25 +5,30 @@ import { type LoaderTag, type GameVersionTag, } from "@/types"; +import { createTtlCache } from "@/lib/utils/cache"; + +const TTL = 3_600_000; // 1 hour /** - * 获取 Modrinth 支持的全部模组加载器列表。 + * 获取 Modrinth 支持的全部模组加载器列表(1h 缓存)。 * GET /tag/loader */ -export async function getLoaders( - client: ModrinthClient, -): Promise { - const data = await client.get("/tag/loader"); - return LoaderTagSchema.array().parse(data); -} +export const getLoaders = createTtlCache( + async (client: ModrinthClient): Promise => { + const data = await client.get("/tag/loader"); + return LoaderTagSchema.array().parse(data); + }, + TTL, +); /** - * 获取 Modrinth 支持的全部 Minecraft 版本列表。 + * 获取 Modrinth 支持的全部 Minecraft 版本列表(1h 缓存)。 * GET /tag/game_version */ -export async function getGameVersions( - client: ModrinthClient, -): Promise { - const data = await client.get("/tag/game_version"); - return GameVersionTagSchema.array().parse(data); -} +export const getGameVersions = createTtlCache( + async (client: ModrinthClient): Promise => { + const data = await client.get("/tag/game_version"); + return GameVersionTagSchema.array().parse(data); + }, + TTL, +); diff --git a/src/lib/utils/cache.ts b/src/lib/utils/cache.ts new file mode 100644 index 0000000..89e5030 --- /dev/null +++ b/src/lib/utils/cache.ts @@ -0,0 +1,16 @@ +export function createTtlCache( + factory: (...args: any[]) => Promise, + ttlMs: number, + keyFn?: (...args: any[]) => string, +): (...args: any[]) => Promise { + const cache = new Map(); + + return async (...args) => { + const key = keyFn ? keyFn(...args) : "_"; + const entry = cache.get(key); + if (entry && Date.now() - entry.ts < ttlMs) return entry.data; + const data = await factory(...args); + cache.set(key, { data, ts: Date.now() }); + return data; + }; +} diff --git a/src/services/platforms/curseforge.ts b/src/services/platforms/curseforge.ts index 1bb50fb..6aefd92 100644 --- a/src/services/platforms/curseforge.ts +++ b/src/services/platforms/curseforge.ts @@ -1,5 +1,5 @@ import { curseforgeClient } from "@/services/clients"; -import { uploadFile } from "@/lib/curseforge"; +import { uploadFile, getGameVersions } from "@/lib/curseforge"; import { resolveVersionName, buildJarPath } from "@/lib/utils/format"; import { readJarBlob } from "@/lib/utils/file"; import type { UploadMetadata } from "@/types"; @@ -26,11 +26,24 @@ export async function publishCurseForgeVersion( const primaryBlob = readJarBlob(primaryPath); + const gameVersions = await getGameVersions(curseforgeClient); + + const resolveId = (name: string): number => { + const found = gameVersions.find((v) => v.name === name); + if (!found) throw new Error(`Game version "${name}" not found in CurseForge`); + return found.id; + }; + const metadata: UploadMetadata = { changelog: input.changelog, changelogType: "markdown", displayName, releaseType: input.versionType, + gameVersions: [ + resolveId(mcVersion), + ...config.curseforge.loaders.map(resolveId), + ...config.curseforge.environment.map(resolveId), + ], relations: config.curseforge.relations, }; diff --git a/src/services/platforms/modrinth.ts b/src/services/platforms/modrinth.ts index b1fde02..03c123c 100644 --- a/src/services/platforms/modrinth.ts +++ b/src/services/platforms/modrinth.ts @@ -47,6 +47,7 @@ export async function publishModrinthVersion( project_id: config.modrinth.project_id, name: versionName, version_number: versionNumber, + changelog: input.changelog, game_versions: [mcVersion], version_type: input.versionType, loaders: config.modrinth.loaders,