feat: 完整游戏运维功能

This commit is contained in:
npc0-hue
2026-07-18 09:04:01 +08:00
parent f3b14b7945
commit 48b8ad8d6c
187 changed files with 16607 additions and 1140 deletions
+202
View File
@@ -0,0 +1,202 @@
import type {
DependencyCatalogResponse,
DependencyPlanStepViewResponse,
DependencyPlanViewResponse,
DependencyProbeViewResponse,
DependencyState,
RunUpdateJobListResponse,
RunUpdateJobResponse,
RunUpdatePhase
} from "../api/types";
const dependencyStates = new Set<DependencyState>(["unknown", "present", "missing", "installing", "failed"]);
const updatePhases = new Set<RunUpdatePhase>(["queued", "downloading", "staged", "restart-requested", "activating", "succeeded", "rolled-back", "failed"]);
const updateStatuses = new Set(["queued", "running", "succeeded", "failed", "denied"]);
const forbiddenProjectionKeys = new Set([
"leasetoken",
"leasetokenhash",
"leasesessiongeneration",
"sessiontoken",
"runtoken",
"secretref",
"hostpath",
"executablepath",
"stagingpath",
"backuppath",
"socket",
"credential",
"pid",
"content",
"payload",
"bindings",
"downloadref"
]);
const unsafeProjectionText = /(?:\/Users\/|\/home\/|\/var\/run\/|[A-Za-z]:\\|unix:\/\/|tcp:\/\/|Bearer\s+|password=|token=|sk-[A-Za-z0-9_-]+)/i;
export function parseSafeDependencyCatalog(value: unknown): DependencyCatalogResponse {
const record = requiredRecordValue(value, "dependency catalog");
rejectForbiddenProjection(record);
return {
serverInstanceId: requiredString(record, "serverInstanceId"),
pluginId: requiredString(record, "pluginId"),
pluginVersion: requiredString(record, "pluginVersion"),
profileKey: requiredString(record, "profileKey"),
targetOs: requiredString(record, "targetOs"),
targetArch: requiredString(record, "targetArch"),
probes: requiredArray(record, "probes").map(parseDependencyProbe),
plans: requiredArray(record, "plans").map(parseDependencyPlan),
updatedAt: requiredString(record, "updatedAt")
};
}
export function parseSafeRunUpdateList(value: unknown): RunUpdateJobListResponse {
const record = requiredRecordValue(value, "Run update list");
rejectForbiddenProjection(record);
const items = requiredArray(record, "items").map((item) => parseRunUpdate(requiredRecordValue(item, "Run update")));
const count = requiredNumber(record, "count");
if (count !== items.length) throw new Error("Run update count does not match items");
return { items, count };
}
export function parseSafeRunUpdate(value: unknown): RunUpdateJobResponse {
const record = requiredRecordValue(value, "Run update");
rejectForbiddenProjection(record);
return parseRunUpdate(record);
}
function parseDependencyProbe(value: unknown): DependencyProbeViewResponse {
const record = requiredRecordValue(value, "dependency probe");
const state = requiredString(record, "state") as DependencyState;
if (!dependencyStates.has(state)) throw new Error("dependency state is invalid");
return {
key: requiredString(record, "key"),
kind: requiredString(record, "kind"),
required: requiredBoolean(record, "required"),
minimumVersion: optionalSafeString(record, "minimumVersion"),
state,
evidence: optionalSafeString(record, "evidence"),
installPlanKey: optionalString(record, "installPlanKey")
};
}
function parseDependencyPlan(value: unknown): DependencyPlanViewResponse {
const record = requiredRecordValue(value, "dependency plan");
return {
key: requiredString(record, "key"),
title: requiredString(record, "title"),
targetOs: requiredString(record, "targetOs"),
targetArch: requiredString(record, "targetArch"),
digest: requiredChecksum(record, "digest"),
steps: requiredArray(record, "steps").map(parseDependencyStep)
};
}
function parseDependencyStep(value: unknown): DependencyPlanStepViewResponse {
const record = requiredRecordValue(value, "dependency plan step");
const downloadHost = optionalString(record, "downloadHost");
if (downloadHost && (downloadHost.includes("/") || downloadHost.includes("@") || downloadHost.includes(":"))) throw new Error("dependency download host is invalid");
return {
type: requiredString(record, "type"),
targetKey: requiredString(record, "targetKey"),
packageManager: optionalString(record, "packageManager"),
packageName: optionalString(record, "packageName"),
version: optionalString(record, "version"),
downloadHost,
sizeBytes: optionalNumber(record, "sizeBytes")
};
}
function parseRunUpdate(record: Record<string, unknown>): RunUpdateJobResponse {
const phase = requiredString(record, "phase") as RunUpdatePhase;
if (!updatePhases.has(phase)) throw new Error("Run update phase is invalid");
const status = requiredString(record, "status");
if (!updateStatuses.has(status)) throw new Error("Run update status is invalid");
return {
id: requiredString(record, "id"),
serverInstanceId: requiredString(record, "serverInstanceId"),
runEndpointId: requiredString(record, "runEndpointId"),
artifactId: requiredString(record, "artifactId"),
checksum: requiredChecksum(record, "checksum"),
targetOs: requiredString(record, "targetOs"),
targetArch: requiredString(record, "targetArch"),
targetRelease: optionalString(record, "targetRelease"),
previousVersion: optionalString(record, "previousVersion"),
jobId: optionalString(record, "jobId"),
idempotencyKey: optionalString(record, "idempotencyKey"),
status,
phase,
message: optionalSafeString(record, "message"),
rollback: requiredBoolean(record, "rollback"),
createdAt: requiredString(record, "createdAt"),
updatedAt: requiredString(record, "updatedAt")
};
}
function rejectForbiddenProjection(value: unknown): void {
if (Array.isArray(value)) {
value.forEach(rejectForbiddenProjection);
return;
}
if (!isRecord(value)) return;
for (const [key, field] of Object.entries(value)) {
if (forbiddenProjectionKeys.has(key.toLowerCase())) throw new Error(`runtime projection contains forbidden field ${key}`);
rejectForbiddenProjection(field);
}
}
function requiredRecordValue(value: unknown, label: string): Record<string, unknown> {
if (!isRecord(value)) throw new Error(`${label} must be an object`);
return value;
}
function requiredArray(value: Record<string, unknown>, key: string): unknown[] {
const field = value[key];
if (!Array.isArray(field)) throw new Error(`${key} must be an array`);
return field;
}
function requiredString(value: Record<string, unknown>, key: string): string {
const field = value[key];
if (typeof field !== "string" || field.trim() === "") throw new Error(`${key} must be a non-empty string`);
return field;
}
function optionalString(value: Record<string, unknown>, key: string): string | undefined {
const field = value[key];
if (field === undefined) return undefined;
if (typeof field !== "string") throw new Error(`${key} must be a string`);
return field;
}
function optionalSafeString(value: Record<string, unknown>, key: string): string | undefined {
const field = optionalString(value, key);
if (field && unsafeProjectionText.test(field)) throw new Error(`${key} contains unsafe runtime details`);
return field;
}
function requiredChecksum(value: Record<string, unknown>, key: string): string {
const field = requiredString(value, key);
if (!/^sha256:[a-f0-9]{64}$/.test(field)) throw new Error(`${key} must be a SHA-256 checksum`);
return field;
}
function requiredNumber(value: Record<string, unknown>, key: string): number {
const field = value[key];
if (typeof field !== "number" || !Number.isFinite(field) || field < 0) throw new Error(`${key} must be a non-negative number`);
return field;
}
function optionalNumber(value: Record<string, unknown>, key: string): number | undefined {
if (value[key] === undefined) return undefined;
return requiredNumber(value, key);
}
function requiredBoolean(value: Record<string, unknown>, key: string): boolean {
const field = value[key];
if (typeof field !== "boolean") throw new Error(`${key} must be a boolean`);
return field;
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}