feat: complete 2.0.7.12 platform overhaul

This commit is contained in:
2026-08-16 19:33:03 +08:00
parent 73555cd04c
commit c9fa6f7a88
159 changed files with 13243 additions and 2539 deletions
@@ -7,6 +7,15 @@ param(
$ErrorActionPreference = "Stop"
$Root = Resolve-Path (Join-Path $PSScriptRoot "..")
$Out = Join-Path $Root $OutDir
$ExistingAdminMetadata = Join-Path $Root "web\admin\dist\admin-build.json"
$AdminBuildId = if ($env:YMHUT_ADMIN_BUILD_ID) {
$env:YMHUT_ADMIN_BUILD_ID
} elseif ($SkipFrontend -and (Test-Path $ExistingAdminMetadata)) {
(Get-Content $ExistingAdminMetadata -Raw | ConvertFrom-Json).buildId
} else {
"$Version-$([DateTimeOffset]::UtcNow.ToUnixTimeSeconds())"
}
$env:YMHUT_ADMIN_BUILD_ID = $AdminBuildId
$env:GOPROXY = if ($env:YMHUT_GOPROXY) { $env:YMHUT_GOPROXY } elseif ($env:GOPROXY) { $env:GOPROXY } else { "https://goproxy.io,https://mirrors.aliyun.com/goproxy,direct" }
function Run-Step {
@@ -39,6 +48,8 @@ if (-not $SkipFrontend) {
}
}
Run-Step (Join-Path $Root "web\admin") "npm" @("run", "validate:build")
New-Item -ItemType Directory -Force -Path $Out | Out-Null
function Copy-IfExists {
@@ -79,7 +90,7 @@ foreach ($Target in $Targets) {
$env:GOARCH = $Target.GOARCH
$env:CGO_ENABLED = "0"
$Output = Join-Path $Out $Target.Name
Run-Step $Root "go" @("build", "-tags", "embed_web", "-buildvcs=false", "-trimpath", "-ldflags", "-s -w -X ymhut-box/server/unified-management/internal/config.Version=$Version", "-o", $Output, ".\cmd\unified-management")
Run-Step $Root "go" @("build", "-tags", "embed_web", "-buildvcs=false", "-trimpath", "-ldflags", "-s -w -X ymhut-box/server/unified-management/internal/config.Version=$Version -X ymhut-box/server/unified-management/internal/config.AdminBuildID=$AdminBuildId", "-o", $Output, ".\cmd\unified-management")
}
Remove-Item Env:\GOOS -ErrorAction SilentlyContinue
@@ -6,6 +6,14 @@ OUT_DIR="${OUT_DIR:-dist-release}"
SKIP_FRONTEND="${SKIP_FRONTEND:-0}"
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
OUT="$ROOT/$OUT_DIR"
if [[ -n "${YMHUT_ADMIN_BUILD_ID:-}" ]]; then
ADMIN_BUILD_ID="$YMHUT_ADMIN_BUILD_ID"
elif [[ "$SKIP_FRONTEND" == "1" && -f "$ROOT/web/admin/dist/admin-build.json" ]]; then
ADMIN_BUILD_ID="$(node -e "const fs=require('fs');process.stdout.write(JSON.parse(fs.readFileSync(process.argv[1],'utf8')).buildId||'')" "$ROOT/web/admin/dist/admin-build.json")"
else
ADMIN_BUILD_ID="$VERSION-$(date -u +%s)"
fi
export YMHUT_ADMIN_BUILD_ID="$ADMIN_BUILD_ID"
export GOPROXY="${YMHUT_GOPROXY:-${GOPROXY:-https://goproxy.io,https://mirrors.aliyun.com/goproxy,direct}}"
(cd "$ROOT" && go mod download all && go mod verify)
@@ -20,6 +28,8 @@ if [[ "$SKIP_FRONTEND" != "1" ]]; then
done
fi
(cd "$ROOT/web/admin" && npm run validate:build)
mkdir -p "$OUT"
copy_if_exists() {
@@ -50,7 +60,7 @@ build_target() {
local name="$3"
(cd "$ROOT" && \
GOOS="$goos" GOARCH="$goarch" CGO_ENABLED=0 \
go build -tags embed_web -buildvcs=false -trimpath -ldflags "-s -w -X ymhut-box/server/unified-management/internal/config.Version=$VERSION" \
go build -tags embed_web -buildvcs=false -trimpath -ldflags "-s -w -X ymhut-box/server/unified-management/internal/config.Version=$VERSION -X ymhut-box/server/unified-management/internal/config.AdminBuildID=$ADMIN_BUILD_ID" \
-o "$OUT/$name" ./cmd/unified-management)
}
@@ -0,0 +1,48 @@
import { readFile, stat } from "node:fs/promises";
import path from "node:path";
import process from "node:process";
const root = path.resolve(process.argv[2] || "web/admin/dist");
function resolveOutput(name) {
const normalized = String(name || "").replaceAll("\\", "/");
if (!normalized || normalized.startsWith("/") || normalized.split("/").includes("..")) {
throw new Error(`Invalid asset path in manifest: ${JSON.stringify(name)}`);
}
const resolved = path.resolve(root, ...normalized.split("/"));
if (resolved !== root && !resolved.startsWith(`${root}${path.sep}`)) {
throw new Error(`Asset path escapes dist: ${JSON.stringify(name)}`);
}
return resolved;
}
async function requireFile(name) {
const file = resolveOutput(name);
const info = await stat(file);
if (!info.isFile() || info.size === 0) {
throw new Error(`Missing or empty admin asset: ${name}`);
}
}
const manifest = JSON.parse(await readFile(path.join(root, "asset-manifest.json"), "utf8"));
const build = JSON.parse(await readFile(path.join(root, "admin-build.json"), "utf8"));
if (!build.buildId || typeof build.buildId !== "string") {
throw new Error("admin-build.json does not contain a buildId");
}
if (!manifest || typeof manifest !== "object" || Object.keys(manifest).length === 0) {
throw new Error("asset-manifest.json is empty");
}
await requireFile("index.html");
for (const [key, entry] of Object.entries(manifest)) {
if (!entry?.file) throw new Error(`Manifest entry ${key} has no output file`);
for (const dependency of [...(entry.imports || []), ...(entry.dynamicImports || [])]) {
if (!manifest[dependency]) throw new Error(`Manifest entry ${key} references missing entry ${dependency}`);
}
for (const file of [entry.file, ...(entry.css || []), ...(entry.assets || [])]) {
await requireFile(file);
}
}
process.stdout.write(`Admin build ${build.buildId}: ${Object.keys(manifest).length} manifest entries verified.\n`);