Refactor platform publishing
Introduce a new `getMinecraftVersions` function to fetch Minecraft versions from CurseForge with caching, along with its Zod schema and exports. Refactor the publishing system to use a unified `PlatformAdaptor` interface, replace the old `PlatformContext` type, and update the publish flow to compute version ranges and pass files as structured objects. Improve `resolveVersionName` to accept a full config object and rework `buildJarPath` to use a Template instance. Add server functions to retrieve filtered MC version lists for both Modrinth and CurseForge.
This commit is contained in:
parent
e8bcd18c76
commit
ffcfa9c4f5
@ -1,6 +1,6 @@
|
|||||||
import { describe, expect, test } from "vitest";
|
import { describe, expect, test } from "vitest";
|
||||||
import { CurseForgeClient } from "./client";
|
import { CurseForgeClient } from "./client";
|
||||||
import { getVersionTypes, getGameVersions } 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);
|
||||||
@ -18,3 +18,14 @@ describe("getGameVersions", () => {
|
|||||||
expect(versions.length).toBeGreaterThan(0);
|
expect(versions.length).toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("getMinecraftVersions", () => {
|
||||||
|
test("GET /v1/minecraft/version", async () => {
|
||||||
|
const versions = await getMinecraftVersions(client);
|
||||||
|
expect(versions.length).toBeGreaterThan(0);
|
||||||
|
for (const v of versions) {
|
||||||
|
expect(v.versionString).toBeTruthy();
|
||||||
|
expect(v.versionString).toMatch(/^\d+\.\d+/);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@ -1,9 +1,12 @@
|
|||||||
|
import { z } from "zod";
|
||||||
import { CurseForgeClient } from "./client";
|
import { CurseForgeClient } from "./client";
|
||||||
import {
|
import {
|
||||||
GameVersionTypeSchema,
|
GameVersionTypeSchema,
|
||||||
GameVersionSchema,
|
GameVersionSchema,
|
||||||
|
MinecraftGameVersionSchema,
|
||||||
type GameVersionType,
|
type GameVersionType,
|
||||||
type GameVersion,
|
type GameVersion,
|
||||||
|
type MinecraftGameVersion,
|
||||||
} from "@/types";
|
} from "@/types";
|
||||||
import { createTtlCache } from "@/lib/utils/cache";
|
import { createTtlCache } from "@/lib/utils/cache";
|
||||||
|
|
||||||
@ -38,3 +41,16 @@ export const getGameVersions = createTtlCache(
|
|||||||
},
|
},
|
||||||
TTL,
|
TTL,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 CurseForge 支持的 Minecraft 版本列表(1h 缓存)。
|
||||||
|
* GET /v1/minecraft/version
|
||||||
|
*/
|
||||||
|
export const getMinecraftVersions = createTtlCache(
|
||||||
|
async (client: CurseForgeClient): Promise<MinecraftGameVersion[]> => {
|
||||||
|
const data = await client.get<unknown>("/v1/minecraft/version");
|
||||||
|
const parsed = z.object({ data: MinecraftGameVersionSchema.array() }).parse(data);
|
||||||
|
return parsed.data;
|
||||||
|
},
|
||||||
|
TTL,
|
||||||
|
);
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
export { CurseForgeClient } from "./client";
|
export { CurseForgeClient } from "./client";
|
||||||
export { getVersionTypes, getGameVersions } from "./game";
|
export { getVersionTypes, getGameVersions, getMinecraftVersions } from "./game";
|
||||||
export { getFiles, uploadFile, updateFile } from "./file";
|
export { getFiles, uploadFile, updateFile } from "./file";
|
||||||
|
|||||||
@ -1,22 +1,30 @@
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { Template } from "@/lib/utils/template";
|
import { Template } from "@/lib/utils/template";
|
||||||
|
import type { Config } from "@/types";
|
||||||
|
import type { McVersionEntry } from "@/lib/utils/mcVersion";
|
||||||
|
|
||||||
export function resolveVersionName(
|
export function resolveVersionName(
|
||||||
template: string,
|
template: string,
|
||||||
templates: Record<string, string> | undefined,
|
config: Config,
|
||||||
values: Record<string, string>,
|
version: string,
|
||||||
|
entry: McVersionEntry,
|
||||||
): string {
|
): string {
|
||||||
const tmpl = new Template(template, templates);
|
const tmpl = new Template(template, {
|
||||||
return tmpl.format(values);
|
filename_format: config.filename_format,
|
||||||
|
});
|
||||||
|
return tmpl.format({
|
||||||
|
version,
|
||||||
|
mc_version: entry.version,
|
||||||
|
mc_version_range: entry.explicit,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildJarPath(
|
export function buildJarPath(
|
||||||
projectDir: string,
|
projectDir: string,
|
||||||
format: string,
|
template: Template,
|
||||||
version: string,
|
version: string,
|
||||||
mcVersion: string,
|
mcVersion: string,
|
||||||
): string {
|
): string {
|
||||||
const tmpl = new Template(format);
|
const filename = template.format({ version, mc_version: mcVersion });
|
||||||
const filename = tmpl.format({ version, mc_version: mcVersion });
|
|
||||||
return path.join(projectDir, "build", "libs", filename);
|
return path.join(projectDir, "build", "libs", filename);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
import { modrinthClient, curseforgeClient } from "./clients";
|
import { modrinthClient, curseforgeClient } from "./clients";
|
||||||
import { getLoaders } from "@/lib/modrinth";
|
import { getLoaders, getGameVersions as getModrinthGameVersions } from "@/lib/modrinth";
|
||||||
import { getVersionTypes, getGameVersions } from "@/lib/curseforge";
|
import { getVersionTypes, getGameVersions, getMinecraftVersions } from "@/lib/curseforge";
|
||||||
|
import { SemVer } from "@/lib/utils/templates/semver";
|
||||||
import { EnvironmentSchema } from "@/types";
|
import { EnvironmentSchema } from "@/types";
|
||||||
|
|
||||||
const ENVIRONMENT_LABELS: Record<string, string> = {
|
const ENVIRONMENT_LABELS: Record<string, string> = {
|
||||||
@ -27,6 +28,17 @@ export async function getModrinthLoaders(): Promise<string[]> {
|
|||||||
return loaders.map((l) => l.name);
|
return loaders.map((l) => l.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 Modrinth 支持的 Minecraft 版本号列表。
|
||||||
|
* 用于 ConfigModal 中 game_versions 多选下拉框的选项。
|
||||||
|
*/
|
||||||
|
export async function getModrinthMcVersions(): Promise<string[]> {
|
||||||
|
const versions = await getModrinthGameVersions(modrinthClient);
|
||||||
|
return versions
|
||||||
|
.filter((v) => v.version_type === "release")
|
||||||
|
.map((v) => v.version);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取 Modrinth 运行环境枚举值,转为 Ant Design Select 的选项格式。
|
* 获取 Modrinth 运行环境枚举值,转为 Ant Design Select 的选项格式。
|
||||||
* 前端使用 OptGroup 分组显示(Client / Server / Both / Other)。
|
* 前端使用 OptGroup 分组显示(Client / Server / Both / Other)。
|
||||||
@ -77,3 +89,18 @@ export async function getCurseForgeMeta(): Promise<{
|
|||||||
|
|
||||||
return { environments, loaders };
|
return { environments, loaders };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 CurseForge 支持的 Minecraft 版本号列表。
|
||||||
|
* 通过 /v1/minecraft/version 接口获取,
|
||||||
|
* 并排除 versionString 包含 "preRelease" 的预发布版本。
|
||||||
|
*/
|
||||||
|
export async function getCurseForgeMcVersions(): Promise<string[]> {
|
||||||
|
const versions = await getMinecraftVersions(curseforgeClient);
|
||||||
|
return versions
|
||||||
|
.filter((v) => {
|
||||||
|
const sv = SemVer.parse(v.versionString);
|
||||||
|
return sv.preRelease === null;
|
||||||
|
})
|
||||||
|
.map((v) => v.versionString);
|
||||||
|
}
|
||||||
|
|||||||
@ -1,30 +1,13 @@
|
|||||||
import { curseforgeClient } from "@/services/clients";
|
import { curseforgeClient } from "@/services/clients";
|
||||||
import { uploadFile, getGameVersions } from "@/lib/curseforge";
|
import { uploadFile, getGameVersions } from "@/lib/curseforge";
|
||||||
import { resolveVersionName, buildJarPath } from "@/lib/utils/format";
|
import { resolveVersionName } from "@/lib/utils/format";
|
||||||
import { readAsFile } from "@/lib/utils/file";
|
|
||||||
import type { UploadMetadata } from "@/types";
|
import type { UploadMetadata } from "@/types";
|
||||||
import type { PlatformContext } from "./types";
|
import { PlatformPublishFn } from "../publish.schemas";
|
||||||
|
|
||||||
export async function publishCurseForgeVersion(
|
export const publishCurseForgeVersion: PlatformPublishFn = async (ctx, mcVersion, files) => {
|
||||||
ctx: PlatformContext,
|
|
||||||
mcVersion: string,
|
|
||||||
): Promise<void> {
|
|
||||||
const { input, config } = ctx;
|
const { input, config } = ctx;
|
||||||
|
|
||||||
const displayName = resolveVersionName(
|
const displayName = resolveVersionName(config.curseforge.version_name, config, input.version, mcVersion);
|
||||||
config.curseforge.version_name,
|
|
||||||
{ filename_format: config.filename_format },
|
|
||||||
{ version: input.version, mc_version: mcVersion },
|
|
||||||
);
|
|
||||||
|
|
||||||
const primaryPath = buildJarPath(
|
|
||||||
config.project_dir,
|
|
||||||
config.filename_format,
|
|
||||||
input.version,
|
|
||||||
mcVersion,
|
|
||||||
);
|
|
||||||
|
|
||||||
const primaryFile = await readAsFile(primaryPath);
|
|
||||||
|
|
||||||
const gameVersions = await getGameVersions(curseforgeClient);
|
const gameVersions = await getGameVersions(curseforgeClient);
|
||||||
|
|
||||||
@ -40,9 +23,9 @@ export async function publishCurseForgeVersion(
|
|||||||
displayName,
|
displayName,
|
||||||
releaseType: input.versionType,
|
releaseType: input.versionType,
|
||||||
gameVersions: [
|
gameVersions: [
|
||||||
resolveId(mcVersion),
|
|
||||||
...config.curseforge.loaders.map(resolveId),
|
...config.curseforge.loaders.map(resolveId),
|
||||||
...config.curseforge.environment.map(resolveId),
|
...config.curseforge.environment.map(resolveId),
|
||||||
|
...mcVersion.range.map(resolveId),
|
||||||
],
|
],
|
||||||
relations: config.curseforge.relations,
|
relations: config.curseforge.relations,
|
||||||
};
|
};
|
||||||
@ -51,6 +34,6 @@ export async function publishCurseForgeVersion(
|
|||||||
curseforgeClient,
|
curseforgeClient,
|
||||||
config.curseforge.project_id,
|
config.curseforge.project_id,
|
||||||
metadata,
|
metadata,
|
||||||
primaryFile,
|
files.primary,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,54 +1,21 @@
|
|||||||
import { modrinthClient } from "@/services/clients";
|
import { modrinthClient } from "@/services/clients";
|
||||||
import { createVersion } from "@/lib/modrinth";
|
import { createVersion } from "@/lib/modrinth";
|
||||||
import { resolveVersionName, buildJarPath } from "@/lib/utils/format";
|
import { resolveVersionName } from "@/lib/utils/format";
|
||||||
import { readAsFile } from "@/lib/utils/file";
|
|
||||||
import type { CreatableVersion } from "@/types";
|
import type { CreatableVersion } from "@/types";
|
||||||
import type { PlatformContext } from "./types";
|
import { PlatformPublishFn } from "../publish.schemas";
|
||||||
|
|
||||||
export async function publishModrinthVersion(
|
export const publishModrinthVersion: PlatformPublishFn = async (ctx, mcVersion, files) => {
|
||||||
ctx: PlatformContext,
|
|
||||||
mcVersion: string,
|
|
||||||
): Promise<void> {
|
|
||||||
const { input, config } = ctx;
|
const { input, config } = ctx;
|
||||||
|
|
||||||
const versionNumber = resolveVersionName(
|
const versionNumber = resolveVersionName(config.modrinth.version, ctx.config, input.version, mcVersion);
|
||||||
config.modrinth.version,
|
const versionName = resolveVersionName(config.modrinth.version_name, ctx.config, input.version, mcVersion);
|
||||||
undefined,
|
|
||||||
{ version: input.version, mc_version: mcVersion },
|
|
||||||
);
|
|
||||||
|
|
||||||
const versionName = resolveVersionName(
|
|
||||||
config.modrinth.version_name,
|
|
||||||
undefined,
|
|
||||||
{
|
|
||||||
version: input.version,
|
|
||||||
mc_version: mcVersion,
|
|
||||||
mc_version_range: mcVersion,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const primaryPath = buildJarPath(
|
|
||||||
config.project_dir,
|
|
||||||
config.filename_format,
|
|
||||||
input.version,
|
|
||||||
mcVersion,
|
|
||||||
);
|
|
||||||
const sourcePath = buildJarPath(
|
|
||||||
config.project_dir,
|
|
||||||
config.source_filename_format,
|
|
||||||
input.version,
|
|
||||||
mcVersion,
|
|
||||||
);
|
|
||||||
|
|
||||||
const primaryFile = await readAsFile(primaryPath);
|
|
||||||
const sourceFile = await readAsFile(sourcePath);
|
|
||||||
|
|
||||||
const data: CreatableVersion = {
|
const data: CreatableVersion = {
|
||||||
project_id: config.modrinth.project_id,
|
project_id: config.modrinth.project_id,
|
||||||
name: versionName,
|
name: versionName,
|
||||||
version_number: versionNumber,
|
version_number: versionNumber,
|
||||||
changelog: input.changelog,
|
changelog: input.changelog,
|
||||||
game_versions: [mcVersion],
|
game_versions: mcVersion.range,
|
||||||
version_type: input.versionType,
|
version_type: input.versionType,
|
||||||
loaders: config.modrinth.loaders,
|
loaders: config.modrinth.loaders,
|
||||||
featured: false,
|
featured: false,
|
||||||
@ -65,7 +32,7 @@ export async function publishModrinthVersion(
|
|||||||
};
|
};
|
||||||
|
|
||||||
await createVersion(modrinthClient, data, {
|
await createVersion(modrinthClient, data, {
|
||||||
primary: primaryFile,
|
primary: files.primary,
|
||||||
sources: sourceFile,
|
sources: files.source,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +0,0 @@
|
|||||||
import { Config } from "@/types";
|
|
||||||
import type { PublishInput } from "../publish.schemas";
|
|
||||||
|
|
||||||
export interface PlatformContext {
|
|
||||||
input: PublishInput;
|
|
||||||
config: Config;
|
|
||||||
}
|
|
||||||
@ -1,20 +1,44 @@
|
|||||||
|
import { McVersionEntry } from "@/lib/utils/mcVersion";
|
||||||
|
import { Config } from "@/types";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const platforms = ["modrinth", "curseforge"] as const;
|
||||||
|
|
||||||
|
export interface PlatformContext {
|
||||||
|
input: PublishInput;
|
||||||
|
config: Config;
|
||||||
|
}
|
||||||
|
export type PlatformPublishFn = (
|
||||||
|
ctx: PlatformContext,
|
||||||
|
mcVersion: McVersionEntry,
|
||||||
|
files: {
|
||||||
|
primary: File,
|
||||||
|
source: File,
|
||||||
|
}
|
||||||
|
) => Promise<void>;
|
||||||
|
export type PlatformGetGameVersionsFn = () => Promise<string[]>;
|
||||||
|
|
||||||
|
export type PlatformAdaptor = {
|
||||||
|
publish: PlatformPublishFn,
|
||||||
|
getGameVersions: PlatformGetGameVersionsFn,
|
||||||
|
}
|
||||||
|
|
||||||
// ── Schemas ────────────────────────────────────────────
|
// ── Schemas ────────────────────────────────────────────
|
||||||
|
|
||||||
export const PublishInputSchema = z.object({
|
export const PublishInputSchema = z.object({
|
||||||
configName: z.string(),
|
configName: z.string(),
|
||||||
version: z.string(),
|
version: z.string(),
|
||||||
mcVersions: z.object({
|
mcVersions: z.object(Object.fromEntries(platforms.map(platform => [
|
||||||
modrinth: z.array(z.object({ mc_version: z.string() })),
|
platform,
|
||||||
curseforge: z.array(z.object({ mc_version: z.string() })),
|
z.array(z.object({ mc_version: z.string() }))
|
||||||
}),
|
]))),
|
||||||
|
cutoffMcVersion: z.string(), // 最后一个版本的截止 MC 版本,两个平台共用
|
||||||
changelog: z.string(),
|
changelog: z.string(),
|
||||||
versionType: z.enum(["release", "beta", "alpha"]),
|
versionType: z.enum(["release", "beta", "alpha"]),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ProgressEventSchema = z.object({
|
export const ProgressEventSchema = z.object({
|
||||||
platform: z.enum(["modrinth", "curseforge"]),
|
platform: z.enum(platforms),
|
||||||
current: z.number().int().min(0),
|
current: z.number().int().min(0),
|
||||||
total: z.number().int().min(0),
|
total: z.number().int().min(0),
|
||||||
status: z.enum(["running", "completed", "failed"]),
|
status: z.enum(["running", "completed", "failed"]),
|
||||||
|
|||||||
@ -7,10 +7,30 @@ import {
|
|||||||
type ProgressEvent,
|
type ProgressEvent,
|
||||||
type PlatformResult,
|
type PlatformResult,
|
||||||
type PublishResult,
|
type PublishResult,
|
||||||
|
platforms,
|
||||||
|
type PlatformContext,
|
||||||
|
type PlatformAdaptor,
|
||||||
} from "./publish.schemas";
|
} from "./publish.schemas";
|
||||||
import { publishModrinthVersion } from "./platforms/modrinth";
|
import { publishModrinthVersion } from "./platforms/modrinth";
|
||||||
import { publishCurseForgeVersion } from "./platforms/curseforge";
|
import { publishCurseForgeVersion } from "./platforms/curseforge";
|
||||||
import type { PlatformContext } from "./platforms/types";
|
import { computeVersionRanges } from "@/lib/utils/mcVersion";
|
||||||
|
import { getCurseForgeMcVersions, getModrinthMcVersions } from "./meta";
|
||||||
|
import { readAsFile } from "@/lib/utils/file";
|
||||||
|
import { Template } from "@/lib/utils/template";
|
||||||
|
import { buildJarPath } from "@/lib/utils/format";
|
||||||
|
|
||||||
|
export const platformAdaptors: {
|
||||||
|
[key in typeof platforms[number]]: PlatformAdaptor
|
||||||
|
} = {
|
||||||
|
modrinth: {
|
||||||
|
publish: publishModrinthVersion,
|
||||||
|
getGameVersions: getModrinthMcVersions,
|
||||||
|
},
|
||||||
|
curseforge: {
|
||||||
|
publish: publishCurseForgeVersion,
|
||||||
|
getGameVersions: getCurseForgeMcVersions,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
// ── tRPC init ──────────────────────────────────────────
|
// ── tRPC init ──────────────────────────────────────────
|
||||||
|
|
||||||
@ -23,7 +43,7 @@ const ee = new EventEmitter();
|
|||||||
// ── Platform orchestrator ──────────────────────────────
|
// ── Platform orchestrator ──────────────────────────────
|
||||||
|
|
||||||
async function runPlatform(
|
async function runPlatform(
|
||||||
platform: "modrinth" | "curseforge",
|
platform: typeof platforms[number],
|
||||||
mcVersions: { mc_version: string }[],
|
mcVersions: { mc_version: string }[],
|
||||||
ctx: PlatformContext,
|
ctx: PlatformContext,
|
||||||
): Promise<PlatformResult> {
|
): Promise<PlatformResult> {
|
||||||
@ -50,16 +70,29 @@ async function runPlatform(
|
|||||||
errors: [],
|
errors: [],
|
||||||
} satisfies ProgressEvent);
|
} satisfies ProgressEvent);
|
||||||
|
|
||||||
const publishFn =
|
const publishFn = platformAdaptors[platform].publish;
|
||||||
platform === "modrinth"
|
const getGameVersionsFn = platformAdaptors[platform].getGameVersions;
|
||||||
? publishModrinthVersion
|
|
||||||
: publishCurseForgeVersion;
|
|
||||||
|
|
||||||
for (let i = 0; i < mcVersions.length; i++) {
|
const mcVersionEntries = computeVersionRanges(
|
||||||
const { mc_version } = mcVersions[i];
|
mcVersions.map(v => v.mc_version),
|
||||||
|
await getGameVersionsFn(),
|
||||||
|
ctx.input.cutoffMcVersion
|
||||||
|
);
|
||||||
|
|
||||||
|
const primaryFileTemplate = new Template(ctx.config.filename_format);
|
||||||
|
const sourceFileTemplate = new Template(ctx.config.source_filename_format);
|
||||||
|
|
||||||
|
for (let i = 0; i < mcVersionEntries.length; i++) {
|
||||||
|
const mcVersionEntry = mcVersionEntries[i];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await publishFn(ctx, mc_version);
|
const primaryFile = await readAsFile(buildJarPath(ctx.config.project_dir, primaryFileTemplate, ctx.input.version, mcVersionEntry.version));
|
||||||
|
const sourceFile = await readAsFile(buildJarPath(ctx.config.project_dir, sourceFileTemplate, ctx.input.version, mcVersionEntry.version));
|
||||||
|
|
||||||
|
await publishFn(ctx, mcVersionEntry, {
|
||||||
|
primary: primaryFile,
|
||||||
|
source: sourceFile,
|
||||||
|
});
|
||||||
result.success++;
|
result.success++;
|
||||||
|
|
||||||
const isLast = i + 1 === total;
|
const isLast = i + 1 === total;
|
||||||
@ -72,7 +105,7 @@ async function runPlatform(
|
|||||||
} satisfies ProgressEvent);
|
} satisfies ProgressEvent);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
result.fail++;
|
result.fail++;
|
||||||
const msg = `${mc_version}: ${errorMessage(err)}`;
|
const msg = `${mcVersionEntry.version}: ${errorMessage(err)}`;
|
||||||
result.errors.push(msg);
|
result.errors.push(msg);
|
||||||
|
|
||||||
ee.emit("progress", {
|
ee.emit("progress", {
|
||||||
@ -101,10 +134,9 @@ export const appRouter = t.router({
|
|||||||
|
|
||||||
const ctx: PlatformContext = { input, config };
|
const ctx: PlatformContext = { input, config };
|
||||||
|
|
||||||
const [modrinthSettled, curseforgeSettled] = await Promise.allSettled([
|
const [modrinthSettled, curseforgeSettled] = await Promise.allSettled(
|
||||||
runPlatform("modrinth", input.mcVersions.modrinth, ctx),
|
platforms.map(platform => runPlatform(platform, input.mcVersions[platform], ctx))
|
||||||
runPlatform("curseforge", input.mcVersions.curseforge, ctx),
|
);
|
||||||
]);
|
|
||||||
|
|
||||||
const modrinth: PlatformResult = modrinthSettled.status === "fulfilled"
|
const modrinth: PlatformResult = modrinthSettled.status === "fulfilled"
|
||||||
? modrinthSettled.value
|
? modrinthSettled.value
|
||||||
|
|||||||
@ -27,3 +27,29 @@ export const GameVersionSchema = z.object({
|
|||||||
});
|
});
|
||||||
export type GameVersion = z.infer<typeof GameVersionSchema>;
|
export type GameVersion = z.infer<typeof GameVersionSchema>;
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
|
// region MinecraftGameVersion — GET /v1/minecraft/version 响应元素
|
||||||
|
export const MinecraftGameVersionSchema = z.object({
|
||||||
|
/** 版本记录 ID */
|
||||||
|
id: z.number().int(),
|
||||||
|
/** Game Version ID */
|
||||||
|
gameVersionId: z.number().int(),
|
||||||
|
/** 版本号字符串,如 "1.21.5" */
|
||||||
|
versionString: z.string(),
|
||||||
|
/** JAR 文件下载地址 */
|
||||||
|
jarDownloadUrl: z.string(),
|
||||||
|
/** JSON 文件下载地址 */
|
||||||
|
jsonDownloadUrl: z.string(),
|
||||||
|
/** 是否已审核通过 */
|
||||||
|
approved: z.boolean(),
|
||||||
|
/** 最后修改时间(ISO 8601) */
|
||||||
|
dateModified: z.string(),
|
||||||
|
/** Game Version Type ID */
|
||||||
|
gameVersionTypeId: z.number().int(),
|
||||||
|
/** 版本状态:1=Approved 2=Deleted 3=New */
|
||||||
|
gameVersionStatus: z.union([z.literal(1), z.literal(2), z.literal(3)]),
|
||||||
|
/** 版本类型状态:1=Normal 2=Deleted */
|
||||||
|
gameVersionTypeStatus: z.union([z.literal(1), z.literal(2)]),
|
||||||
|
});
|
||||||
|
export type MinecraftGameVersion = z.infer<typeof MinecraftGameVersionSchema>;
|
||||||
|
// endregion
|
||||||
|
|||||||
@ -30,8 +30,10 @@ export {
|
|||||||
export {
|
export {
|
||||||
GameVersionTypeSchema,
|
GameVersionTypeSchema,
|
||||||
GameVersionSchema,
|
GameVersionSchema,
|
||||||
|
MinecraftGameVersionSchema,
|
||||||
type GameVersionType,
|
type GameVersionType,
|
||||||
type GameVersion,
|
type GameVersion,
|
||||||
|
type MinecraftGameVersion,
|
||||||
} from "./game";
|
} from "./game";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|||||||
@ -19,7 +19,7 @@ export const GameVersionTagSchema = z.object({
|
|||||||
/** 版本类型:release | snapshot | alpha | beta */
|
/** 版本类型:release | snapshot | alpha | beta */
|
||||||
version_type: z.enum(["release", "snapshot", "alpha", "beta"]),
|
version_type: z.enum(["release", "snapshot", "alpha", "beta"]),
|
||||||
/** 版本发布日期(ISO-8601) */
|
/** 版本发布日期(ISO-8601) */
|
||||||
date: z.string().datetime(),
|
date: z.string(),
|
||||||
/** 是否为主要版本,用于 Featured Versions 标记 */
|
/** 是否为主要版本,用于 Featured Versions 标记 */
|
||||||
major: z.boolean(),
|
major: z.boolean(),
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user