139 lines
4.3 KiB
TypeScript
139 lines
4.3 KiB
TypeScript
import { DAEMON_RELEASE_POLICY } from "../release/daemon-policy.mjs";
|
|
import {
|
|
compareStableVersions,
|
|
MINIMUM_CLOUD_DAEMON_VERSION,
|
|
parseStableVersion,
|
|
} from "../release/daemon-version.ts";
|
|
|
|
export { MINIMUM_CLOUD_DAEMON_VERSION };
|
|
|
|
type ReleaseEnvironment = Partial<{
|
|
NEKONEST_CLOUD_DAEMON_RELEASE_VERSION: string;
|
|
NEKONEST_CLOUD_DAEMON_RELEASE_BASE_URL: string;
|
|
NEKONEST_CLOUD_DAEMON_WINDOWS_AMD64_SHA256: string;
|
|
NEKONEST_CLOUD_DAEMON_LINUX_AMD64_SHA256: string;
|
|
NEKONEST_CLOUD_DAEMON_LINUX_ARM64_SHA256: string;
|
|
}>;
|
|
|
|
export type DaemonReleaseAsset = {
|
|
platform: "windows" | "linux";
|
|
architecture: "amd64" | "arm64";
|
|
label: string;
|
|
filename: string;
|
|
downloadUrl: string;
|
|
sha256: string;
|
|
};
|
|
|
|
export type DaemonReleaseState =
|
|
| {
|
|
available: false;
|
|
reason: "not_configured" | "invalid_config" | "incompatible_version";
|
|
minimumVersion: string;
|
|
}
|
|
| {
|
|
available: true;
|
|
version: string;
|
|
minimumVersion: string;
|
|
releasePageUrl: string;
|
|
checksumsUrl: string;
|
|
assets: DaemonReleaseAsset[];
|
|
};
|
|
|
|
const ASSETS = DAEMON_RELEASE_POLICY.assets;
|
|
|
|
function safeReleaseBaseUrl(raw: string | undefined, version: string): string {
|
|
const fallback = `https://github.com/${DAEMON_RELEASE_POLICY.repository}/releases/download/v${version}`;
|
|
const parsed = new URL(raw?.trim() || fallback);
|
|
if (
|
|
parsed.protocol !== "https:" ||
|
|
parsed.username ||
|
|
parsed.password ||
|
|
parsed.search ||
|
|
parsed.hash
|
|
) {
|
|
throw new Error("unsafe_release_base_url");
|
|
}
|
|
parsed.pathname = parsed.pathname.replace(/\/+$/, "");
|
|
return parsed.toString().replace(/\/$/, "");
|
|
}
|
|
|
|
function normalizedChecksum(value: string | undefined): string {
|
|
const checksum = value?.trim().toLowerCase() ?? "";
|
|
if (!/^[0-9a-f]{64}$/.test(checksum)) throw new Error("invalid_release_checksum");
|
|
return checksum;
|
|
}
|
|
|
|
export function parseDaemonReleaseEnvironment(config: ReleaseEnvironment): DaemonReleaseState {
|
|
const version = config.NEKONEST_CLOUD_DAEMON_RELEASE_VERSION?.trim() ?? "";
|
|
if (!version) {
|
|
return {
|
|
available: false,
|
|
reason: "not_configured",
|
|
minimumVersion: MINIMUM_CLOUD_DAEMON_VERSION,
|
|
};
|
|
}
|
|
|
|
const parsedVersion = parseStableVersion(version);
|
|
const minimumVersion = parseStableVersion(MINIMUM_CLOUD_DAEMON_VERSION);
|
|
if (!parsedVersion || !minimumVersion) {
|
|
return {
|
|
available: false,
|
|
reason: "invalid_config",
|
|
minimumVersion: MINIMUM_CLOUD_DAEMON_VERSION,
|
|
};
|
|
}
|
|
if (compareStableVersions(parsedVersion, minimumVersion) < 0) {
|
|
return {
|
|
available: false,
|
|
reason: "incompatible_version",
|
|
minimumVersion: MINIMUM_CLOUD_DAEMON_VERSION,
|
|
};
|
|
}
|
|
|
|
try {
|
|
const baseUrl = safeReleaseBaseUrl(
|
|
config.NEKONEST_CLOUD_DAEMON_RELEASE_BASE_URL,
|
|
version,
|
|
);
|
|
const assets = ASSETS.map((asset) => ({
|
|
platform: asset.platform as DaemonReleaseAsset["platform"],
|
|
architecture: asset.architecture as DaemonReleaseAsset["architecture"],
|
|
label: asset.label,
|
|
filename: asset.filename,
|
|
downloadUrl: `${baseUrl}/${asset.filename}`,
|
|
sha256: normalizedChecksum(config[asset.checksumEnv as keyof ReleaseEnvironment]),
|
|
}));
|
|
return {
|
|
available: true,
|
|
version,
|
|
minimumVersion: MINIMUM_CLOUD_DAEMON_VERSION,
|
|
releasePageUrl: `https://github.com/${DAEMON_RELEASE_POLICY.repository}/releases/tag/v${version}`,
|
|
checksumsUrl: `${baseUrl}/checksums.txt`,
|
|
assets,
|
|
};
|
|
} catch {
|
|
return {
|
|
available: false,
|
|
reason: "invalid_config",
|
|
minimumVersion: MINIMUM_CLOUD_DAEMON_VERSION,
|
|
};
|
|
}
|
|
}
|
|
|
|
export async function getDaemonReleaseState(): Promise<DaemonReleaseState> {
|
|
const { env } = await import("cloudflare:workers");
|
|
return parseDaemonReleaseEnvironment(env);
|
|
}
|
|
|
|
export function checksumVerificationCommand(asset: DaemonReleaseAsset): string {
|
|
if (asset.platform === "windows") {
|
|
return [
|
|
`$expected = '${asset.sha256}'`,
|
|
`$actual = (Get-FileHash -LiteralPath '.\\${asset.filename}' -Algorithm SHA256).Hash.ToLowerInvariant()`,
|
|
`if ($actual -ne $expected) { throw 'SHA-256 不匹配,请删除文件' }`,
|
|
`Write-Host 'SHA-256 校验通过'`,
|
|
].join("\n");
|
|
}
|
|
return `printf '%s %s\\n' '${asset.sha256}' '${asset.filename}' | sha256sum -c -`;
|
|
}
|