Extract platform publishing logic into dedicated modules
This commit is contained in:
parent
db1cb28296
commit
e57a1ac993
3
src/lib/utils/error.ts
Normal file
3
src/lib/utils/error.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export function errorMessage(err: unknown): string {
|
||||
return err instanceof Error ? err.message : String(err);
|
||||
}
|
||||
11
src/lib/utils/file.ts
Normal file
11
src/lib/utils/file.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
|
||||
export function readJarBlob(filePath: string): Blob {
|
||||
try {
|
||||
const buffer = readFileSync(filePath);
|
||||
return new Blob([buffer]);
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
throw new Error(`Failed to read file "${filePath}": ${msg}`);
|
||||
}
|
||||
}
|
||||
22
src/lib/utils/format.ts
Normal file
22
src/lib/utils/format.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import path from "node:path";
|
||||
import { Template } from "@/lib/utils/template";
|
||||
|
||||
export function resolveVersionName(
|
||||
template: string,
|
||||
templates: Record<string, string> | undefined,
|
||||
values: Record<string, string>,
|
||||
): string {
|
||||
const tmpl = new Template(template, templates);
|
||||
return tmpl.format(values);
|
||||
}
|
||||
|
||||
export function buildJarPath(
|
||||
projectDir: string,
|
||||
format: string,
|
||||
version: string,
|
||||
mcVersion: string,
|
||||
): string {
|
||||
const tmpl = new Template(format);
|
||||
const filename = tmpl.format({ version, mc_version: mcVersion });
|
||||
return path.join(projectDir, "build", "libs", filename);
|
||||
}
|
||||
43
src/services/platforms/curseforge.ts
Normal file
43
src/services/platforms/curseforge.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import { curseforgeClient } from "@/services/clients";
|
||||
import { uploadFile } from "@/lib/curseforge";
|
||||
import { resolveVersionName, buildJarPath } from "@/lib/utils/format";
|
||||
import { readJarBlob } from "@/lib/utils/file";
|
||||
import type { UploadMetadata } from "@/types";
|
||||
import type { PlatformContext } from "./types";
|
||||
|
||||
export async function publishCurseForgeVersion(
|
||||
ctx: PlatformContext,
|
||||
mcVersion: string,
|
||||
): Promise<void> {
|
||||
const { input, config } = ctx;
|
||||
|
||||
const displayName = resolveVersionName(
|
||||
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 primaryBlob = readJarBlob(primaryPath);
|
||||
|
||||
const metadata: UploadMetadata = {
|
||||
changelog: input.changelog,
|
||||
changelogType: "markdown",
|
||||
displayName,
|
||||
releaseType: input.versionType,
|
||||
relations: config.curseforge.relations,
|
||||
};
|
||||
|
||||
await uploadFile(
|
||||
curseforgeClient,
|
||||
config.curseforge.project_id,
|
||||
metadata,
|
||||
primaryBlob,
|
||||
);
|
||||
}
|
||||
70
src/services/platforms/modrinth.ts
Normal file
70
src/services/platforms/modrinth.ts
Normal file
@ -0,0 +1,70 @@
|
||||
import { modrinthClient } from "@/services/clients";
|
||||
import { createVersion } from "@/lib/modrinth";
|
||||
import { resolveVersionName, buildJarPath } from "@/lib/utils/format";
|
||||
import { readJarBlob } from "@/lib/utils/file";
|
||||
import type { CreatableVersion } from "@/types";
|
||||
import type { PlatformContext } from "./types";
|
||||
|
||||
export async function publishModrinthVersion(
|
||||
ctx: PlatformContext,
|
||||
mcVersion: string,
|
||||
): Promise<void> {
|
||||
const { input, config } = ctx;
|
||||
|
||||
const versionNumber = resolveVersionName(
|
||||
config.modrinth.version,
|
||||
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 primaryBlob = readJarBlob(primaryPath);
|
||||
const sourceBlob = readJarBlob(sourcePath);
|
||||
|
||||
const data: CreatableVersion = {
|
||||
project_id: config.modrinth.project_id,
|
||||
name: versionName,
|
||||
version_number: versionNumber,
|
||||
game_versions: [mcVersion],
|
||||
version_type: input.versionType,
|
||||
loaders: config.modrinth.loaders,
|
||||
featured: false,
|
||||
dependencies: config.modrinth.dependencies.map((d) => ({
|
||||
version_id: null,
|
||||
project_id: d.project_id,
|
||||
file_name: null,
|
||||
dependency_type: d.dependency_type,
|
||||
})),
|
||||
file_parts: ["primary", "sources"],
|
||||
primary_file: "primary",
|
||||
file_types: { sources: "sources-jar" },
|
||||
environment: config.modrinth.environment,
|
||||
};
|
||||
|
||||
await createVersion(modrinthClient, data, {
|
||||
primary: primaryBlob,
|
||||
sources: sourceBlob,
|
||||
});
|
||||
}
|
||||
7
src/services/platforms/types.ts
Normal file
7
src/services/platforms/types.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import type { PublishInput } from "../publish.schemas";
|
||||
import type { getConfig } from "@/services/config";
|
||||
|
||||
export interface PlatformContext {
|
||||
input: PublishInput;
|
||||
config: Awaited<ReturnType<typeof getConfig>>;
|
||||
}
|
||||
40
src/services/publish.schemas.ts
Normal file
40
src/services/publish.schemas.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { z } from "zod";
|
||||
|
||||
// ── Schemas ────────────────────────────────────────────
|
||||
|
||||
export const PublishInputSchema = z.object({
|
||||
configName: z.string(),
|
||||
version: z.string(),
|
||||
mcVersions: z.object({
|
||||
modrinth: z.array(z.object({ mc_version: z.string() })),
|
||||
curseforge: z.array(z.object({ mc_version: z.string() })),
|
||||
}),
|
||||
changelog: z.string(),
|
||||
versionType: z.enum(["release", "beta", "alpha"]),
|
||||
});
|
||||
|
||||
export const ProgressEventSchema = z.object({
|
||||
platform: z.enum(["modrinth", "curseforge"]),
|
||||
current: z.number().int().min(0),
|
||||
total: z.number().int().min(0),
|
||||
status: z.enum(["running", "completed", "failed"]),
|
||||
errors: z.array(z.string()),
|
||||
});
|
||||
|
||||
export const PlatformResultSchema = z.object({
|
||||
success: z.number().int().min(0),
|
||||
fail: z.number().int().min(0),
|
||||
errors: z.array(z.string()),
|
||||
});
|
||||
|
||||
export const PublishResultSchema = z.object({
|
||||
modrinth: PlatformResultSchema,
|
||||
curseforge: PlatformResultSchema,
|
||||
});
|
||||
|
||||
// ── Inferred types ─────────────────────────────────────
|
||||
|
||||
export type PublishInput = z.infer<typeof PublishInputSchema>;
|
||||
export type ProgressEvent = z.infer<typeof ProgressEventSchema>;
|
||||
export type PlatformResult = z.infer<typeof PlatformResultSchema>;
|
||||
export type PublishResult = z.infer<typeof PublishResultSchema>;
|
||||
@ -1,53 +1,16 @@
|
||||
import { initTRPC } from "@trpc/server";
|
||||
import { z } from "zod";
|
||||
import EventEmitter, { on } from "node:events";
|
||||
import { readFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { getConfig } from "./config";
|
||||
import { modrinthClient, curseforgeClient } from "./clients";
|
||||
import { createVersion } from "@/lib/modrinth";
|
||||
import { uploadFile } from "@/lib/curseforge";
|
||||
import { Template } from "@/lib/utils/template";
|
||||
import type { CreatableVersion, UploadMetadata } from "@/types";
|
||||
|
||||
// ── Zod schemas ────────────────────────────────────────
|
||||
|
||||
const PublishInputSchema = z.object({
|
||||
configName: z.string(),
|
||||
version: z.string(),
|
||||
mcVersions: z.object({
|
||||
modrinth: z.array(z.object({ mc_version: z.string() })),
|
||||
curseforge: z.array(z.object({ mc_version: z.string() })),
|
||||
}),
|
||||
changelog: z.string(),
|
||||
versionType: z.enum(["release", "beta", "alpha"]),
|
||||
});
|
||||
|
||||
const ProgressEventSchema = z.object({
|
||||
platform: z.enum(["modrinth", "curseforge"]),
|
||||
current: z.number().int().min(0),
|
||||
total: z.number().int().min(0),
|
||||
status: z.enum(["running", "completed", "failed"]),
|
||||
errors: z.array(z.string()),
|
||||
});
|
||||
|
||||
const PlatformResultSchema = z.object({
|
||||
success: z.number().int().min(0),
|
||||
fail: z.number().int().min(0),
|
||||
errors: z.array(z.string()),
|
||||
});
|
||||
|
||||
const PublishResultSchema = z.object({
|
||||
modrinth: PlatformResultSchema,
|
||||
curseforge: PlatformResultSchema,
|
||||
});
|
||||
|
||||
// ── Inferred types ─────────────────────────────────────
|
||||
|
||||
type PublishInput = z.infer<typeof PublishInputSchema>;
|
||||
type ProgressEvent = z.infer<typeof ProgressEventSchema>;
|
||||
type PlatformResult = z.infer<typeof PlatformResultSchema>;
|
||||
type PublishResult = z.infer<typeof PublishResultSchema>;
|
||||
import { errorMessage } from "@/lib/utils/error";
|
||||
import {
|
||||
PublishInputSchema,
|
||||
type ProgressEvent,
|
||||
type PlatformResult,
|
||||
type PublishResult,
|
||||
} from "./publish.schemas";
|
||||
import { publishModrinthVersion } from "./platforms/modrinth";
|
||||
import { publishCurseForgeVersion } from "./platforms/curseforge";
|
||||
import type { PlatformContext } from "./platforms/types";
|
||||
|
||||
// ── tRPC init ──────────────────────────────────────────
|
||||
|
||||
@ -57,149 +20,7 @@ const t = initTRPC.create();
|
||||
|
||||
const ee = new EventEmitter();
|
||||
|
||||
// ── Helpers ────────────────────────────────────────────
|
||||
|
||||
function resolveVersionName(
|
||||
template: string,
|
||||
templates: Record<string, string> | undefined,
|
||||
values: Record<string, string>,
|
||||
): string {
|
||||
const tmpl = new Template(template, templates);
|
||||
return tmpl.format(values);
|
||||
}
|
||||
|
||||
function buildJarPath(
|
||||
projectDir: string,
|
||||
format: string,
|
||||
version: string,
|
||||
mcVersion: string,
|
||||
): string {
|
||||
const tmpl = new Template(format);
|
||||
const filename = tmpl.format({ version, mc_version: mcVersion });
|
||||
return path.join(projectDir, "build", "libs", filename);
|
||||
}
|
||||
|
||||
function readJarBlob(filePath: string): Blob {
|
||||
try {
|
||||
const buffer = readFileSync(filePath);
|
||||
return new Blob([buffer]);
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
throw new Error(`Failed to read file "${filePath}": ${msg}`);
|
||||
}
|
||||
}
|
||||
|
||||
function errorMessage(err: unknown): string {
|
||||
return err instanceof Error ? err.message : String(err);
|
||||
}
|
||||
|
||||
// ── Platform publish logic ─────────────────────────────
|
||||
|
||||
interface PlatformContext {
|
||||
input: PublishInput;
|
||||
config: Awaited<ReturnType<typeof getConfig>>;
|
||||
}
|
||||
|
||||
async function publishModrinthVersion(
|
||||
ctx: PlatformContext,
|
||||
mcVersion: string,
|
||||
): Promise<void> {
|
||||
const { input, config } = ctx;
|
||||
|
||||
const versionNumber = resolveVersionName(
|
||||
config.modrinth.version,
|
||||
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 primaryBlob = readJarBlob(primaryPath);
|
||||
const sourceBlob = readJarBlob(sourcePath);
|
||||
|
||||
const data: CreatableVersion = {
|
||||
project_id: config.modrinth.project_id,
|
||||
name: versionName,
|
||||
version_number: versionNumber,
|
||||
game_versions: [mcVersion],
|
||||
version_type: input.versionType,
|
||||
loaders: config.modrinth.loaders,
|
||||
featured: false,
|
||||
dependencies: config.modrinth.dependencies.map((d) => ({
|
||||
version_id: null,
|
||||
project_id: d.project_id,
|
||||
file_name: null,
|
||||
dependency_type: d.dependency_type,
|
||||
})),
|
||||
file_parts: ["primary", "sources"],
|
||||
primary_file: "primary",
|
||||
file_types: { sources: "sources-jar" },
|
||||
environment: config.modrinth.environment,
|
||||
};
|
||||
|
||||
await createVersion(modrinthClient, data, {
|
||||
primary: primaryBlob,
|
||||
sources: sourceBlob,
|
||||
});
|
||||
}
|
||||
|
||||
async function publishCurseForgeVersion(
|
||||
ctx: PlatformContext,
|
||||
mcVersion: string,
|
||||
): Promise<void> {
|
||||
const { input, config } = ctx;
|
||||
|
||||
const displayName = resolveVersionName(
|
||||
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 primaryBlob = readJarBlob(primaryPath);
|
||||
|
||||
const metadata: UploadMetadata = {
|
||||
changelog: input.changelog,
|
||||
changelogType: "markdown",
|
||||
displayName,
|
||||
releaseType: input.versionType,
|
||||
relations: config.curseforge.relations,
|
||||
};
|
||||
|
||||
await uploadFile(
|
||||
curseforgeClient,
|
||||
config.curseforge.project_id,
|
||||
metadata,
|
||||
primaryBlob,
|
||||
);
|
||||
}
|
||||
// ── Platform orchestrator ──────────────────────────────
|
||||
|
||||
async function runPlatform(
|
||||
platform: "modrinth" | "curseforge",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user