Split CurseForge API token into separate legacy and new tokens

This commit is contained in:
CPTProgrammer 2026-07-13 18:42:29 +08:00
parent 691ae86e00
commit 296b69bf1c
No known key found for this signature in database
4 changed files with 23 additions and 11 deletions

View File

@ -2,20 +2,27 @@ import { readErrorBody, summarizeFormData } from "../utils/fetch";
/** CurseForge API 客户端,封装两套 API 的鉴权与 User-Agent。 */ /** CurseForge API 客户端,封装两套 API 的鉴权与 User-Agent。 */
export class CurseForgeClient { export class CurseForgeClient {
/** GET /v1/mods/{modId}/files 等只读接口 */ /** V1 API (api.curseforge.com)GET /v1/mods/{modId}/files 等只读接口 */
private apiBase = "https://api.curseforge.com"; private apiBase = "https://api.curseforge.com";
/** 上传 / 更新文件、Game Version Types / Versions 等接口 */ /** Legacy API (minecraft.curseforge.com)上传 / 更新文件、Game Version Types / Versions 等接口 */
private uploadBase = "https://minecraft.curseforge.com"; private uploadBase = "https://minecraft.curseforge.com";
private token: string; /** 新 API (api.curseforge.com) 的 token */
private apiToken: string;
/** 旧 API (minecraft.curseforge.com) 的 token */
private legacyToken: string;
private userAgent: string; private userAgent: string;
constructor( constructor(
token: string, apiToken: string,
legacyToken: string,
userAgent?: string, userAgent?: string,
) { ) {
this.token = token; this.apiToken = apiToken;
this.legacyToken = legacyToken;
this.userAgent = this.userAgent =
userAgent ?? userAgent ??
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"; "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36";
@ -23,13 +30,13 @@ export class CurseForgeClient {
/** V1 API 请求头x-api-key。 */ /** V1 API 请求头x-api-key。 */
private apiHeaders(): Record<string, string> { private apiHeaders(): Record<string, string> {
return { "x-api-key": this.token }; return { "x-api-key": this.apiToken };
} }
/** Upload API 请求头X-Api-Token + browser User-Agent。 */ /** Legacy API 请求头X-Api-Token + browser User-Agent。 */
private uploadHeaders(): Record<string, string> { private uploadHeaders(): Record<string, string> {
return { return {
"X-Api-Token": this.token, "X-Api-Token": this.legacyToken,
"User-Agent": this.userAgent, "User-Agent": this.userAgent,
}; };
} }

View File

@ -3,7 +3,7 @@ import { CurseForgeClient } from "./client";
import { getVersionTypes, getGameVersions, getMinecraftVersions } from "./game"; import { getVersionTypes, getGameVersions, getMinecraftVersions } from "./game";
import secrets from "../../../secrets.json"; import secrets from "../../../secrets.json";
const client = new CurseForgeClient(secrets.curseforge); const client = new CurseForgeClient(secrets.curseforge, secrets.curseforge_legacy);
describe("getVersionTypes", () => { describe("getVersionTypes", () => {
test("GET /api/game/version-types", async () => { test("GET /api/game/version-types", async () => {

View File

@ -33,4 +33,7 @@ export const modrinthClient = new ModrinthClient(
"ModReleaser (local)", "ModReleaser (local)",
); );
export const curseforgeClient = new CurseForgeClient(secrets.curseforge); export const curseforgeClient = new CurseForgeClient(
secrets.curseforge,
secrets.curseforge_legacy,
);

View File

@ -73,8 +73,10 @@ export type Config = z.infer<typeof ConfigSchema>;
// region Secrets — secrets.json 结构 // region Secrets — secrets.json 结构
export const SecretsSchema = z.object({ export const SecretsSchema = z.object({
/** CurseForge API Token */ /** CurseForge API (api.curseforge.com) Token */
curseforge: z.string().min(1), curseforge: z.string().min(1),
/** CurseForge 旧 API (minecraft.curseforge.com) Token */
curseforge_legacy: z.string().min(1),
/** Modrinth Personal Access Token */ /** Modrinth Personal Access Token */
modrinth: z.string().min(1), modrinth: z.string().min(1),
}); });