From c3da6f5870429b8d5bd0afe167d7c27425cd20bb Mon Sep 17 00:00:00 2001 From: Lyon <88232613+pikasTech@users.noreply.github.com> Date: Sun, 14 Jun 2026 12:20:25 +0800 Subject: [PATCH] refactor: centralize platform infra ops helpers (#355) Co-authored-by: Codex --- scripts/src/platform-infra-ops-library.ts | 54 ++++++++++++++++++- scripts/src/platform-infra-public-service.ts | 56 +------------------- scripts/src/platform-infra.ts | 36 +------------ 3 files changed, 56 insertions(+), 90 deletions(-) diff --git a/scripts/src/platform-infra-ops-library.ts b/scripts/src/platform-infra-ops-library.ts index d9ca91ef..8b6f1793 100644 --- a/scripts/src/platform-infra-ops-library.ts +++ b/scripts/src/platform-infra-ops-library.ts @@ -5,7 +5,7 @@ import pathPosix from "node:path/posix"; import type { UniDeskConfig } from "./config"; import { rootPath } from "./config"; import { coreInternalFetch } from "./microservices"; -import { capture, compactCapture, parseJsonOutput, redactText, shQuote } from "./platform-infra-public-service"; +import { runSshCommandCapture, type SshCaptureResult } from "./ssh"; export interface OpsCommonOptions { targetId: string; @@ -24,6 +24,58 @@ export interface OpsCommandOptionSpec { flagOptions?: string[]; } +export async function capture(config: UniDeskConfig, route: string, args: string[], stdin: string): Promise { + return await runSshCommandCapture(config, route, args, stdin); +} + +export function parseJsonOutput(stdout: string): Record | null { + const trimmed = stdout.trim(); + if (trimmed.length === 0) return null; + const start = trimmed.indexOf("{"); + const end = trimmed.lastIndexOf("}"); + if (start === -1 || end === -1 || end <= start) return null; + try { + const parsed = JSON.parse(trimmed.slice(start, end + 1)) as unknown; + return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed as Record : null; + } catch { + return null; + } +} + +export function compactCapture(result: SshCaptureResult, options: { full?: boolean } = {}): Record { + const full = options.full ?? false; + return { + exitCode: result.exitCode, + stdoutBytes: Buffer.byteLength(result.stdout, "utf8"), + stderrBytes: Buffer.byteLength(result.stderr, "utf8"), + stdoutTail: full || result.exitCode !== 0 ? redactText(result.stdout).slice(-8000) : "", + stderrTail: full || result.exitCode !== 0 ? redactText(result.stderr).slice(-4000) : "", + }; +} + +export function redactText(text: string): string { + return text + .replace(/lbk_[A-Za-z0-9_-]+/gu, "lbk_") + .replace(/(postgres(?:ql)?:\/\/)[^@\s"']+@/giu, "$1@") + .replace(/(Bearer\s+)[A-Za-z0-9._~+/=-]+/giu, "$1") + .replace(/(["']?(?:N8N_ENCRYPTION_KEY|PASSWORD|SECRET|TOKEN|API[_-]?KEY|APIKEY|JWT[_-]?SECRET|DATABASE[_-]?URL)["']?\s*[:=]\s*["']?)[^"',\s}]+(["']?)/giu, "$1$2"); +} + +export function fingerprintValues(values: Record, keys: string[]): string { + const hash = createHash("sha256"); + for (const key of keys.slice().sort()) { + hash.update(key); + hash.update("\0"); + hash.update(values[key] ?? ""); + hash.update("\0"); + } + return `sha256:${hash.digest("hex")}`; +} + +export function shQuote(value: string): string { + return `'${value.replaceAll("'", "'\"'\"'")}'`; +} + export function parseOpsCommonOptions(args: string[], spec: OpsCommandOptionSpec = {}): OpsCommonOptions & Record { const stringOptions = new Set(["--target", ...(spec.stringOptions ?? [])]); const flagOptions = new Set(["--full", "--raw", ...(spec.flagOptions ?? [])]); diff --git a/scripts/src/platform-infra-public-service.ts b/scripts/src/platform-infra-public-service.ts index 516f9e35..b1bec3f9 100644 --- a/scripts/src/platform-infra-public-service.ts +++ b/scripts/src/platform-infra-public-service.ts @@ -1,8 +1,8 @@ -import { createHash } from "node:crypto"; import { Buffer } from "node:buffer"; import type { UniDeskConfig } from "./config"; import { applyPk01CaddyManagedBlock, caddyManagedBlockMarkers } from "./pk01-caddy"; -import { runSshCommandCapture, type SshCaptureResult } from "./ssh"; +import { capture, compactCapture, fingerprintValues, parseJsonOutput, redactText, shQuote } from "./platform-infra-ops-library"; +export { capture, compactCapture, fingerprintValues, parseJsonOutput, redactText, shQuote }; export interface PublicServiceExposure { enabled: boolean; @@ -48,10 +48,6 @@ export interface FrpcSecretMaterial { valuesPrinted: false; } -export async function capture(config: UniDeskConfig, route: string, args: string[], stdin: string): Promise { - return await runSshCommandCapture(config, route, args, stdin); -} - export async function applyPk01CaddyBlock( config: UniDeskConfig, serviceId: string, @@ -202,31 +198,6 @@ PY `; } -export function parseJsonOutput(stdout: string): Record | null { - const trimmed = stdout.trim(); - if (trimmed.length === 0) return null; - const start = trimmed.indexOf("{"); - const end = trimmed.lastIndexOf("}"); - if (start === -1 || end === -1 || end <= start) return null; - try { - const parsed = JSON.parse(trimmed.slice(start, end + 1)) as unknown; - return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed as Record : null; - } catch { - return null; - } -} - -export function compactCapture(result: SshCaptureResult, options: { full?: boolean } = {}): Record { - const full = options.full ?? false; - return { - exitCode: result.exitCode, - stdoutBytes: Buffer.byteLength(result.stdout, "utf8"), - stderrBytes: Buffer.byteLength(result.stderr, "utf8"), - stdoutTail: full || result.exitCode !== 0 ? redactText(result.stdout).slice(-8000) : "", - stderrTail: full || result.exitCode !== 0 ? redactText(result.stderr).slice(-4000) : "", - }; -} - export function publicHttpProbe(baseUrl: string, path: string, options: { headers?: string[] } = {}): Record { const url = `${baseUrl.replace(/\/+$/u, "")}${path}`; const args = ["-fsS", "--connect-timeout", "10", "--max-time", "30", "-o", "-", "-w", "\n%{http_code}"]; @@ -254,29 +225,6 @@ export function publicHttpProbe(baseUrl: string, path: string, options: { header }; } -export function redactText(text: string): string { - return text - .replace(/lbk_[A-Za-z0-9_-]+/gu, "lbk_") - .replace(/(postgres(?:ql)?:\/\/)[^@\s"']+@/giu, "$1@") - .replace(/(Bearer\s+)[A-Za-z0-9._~+/=-]+/giu, "$1") - .replace(/(["']?(?:N8N_ENCRYPTION_KEY|PASSWORD|SECRET|TOKEN|API[_-]?KEY|APIKEY|JWT[_-]?SECRET|DATABASE[_-]?URL)["']?\s*[:=]\s*["']?)[^"',\s}]+(["']?)/giu, "$1$2"); -} - -export function fingerprintValues(values: Record, keys: string[]): string { - const hash = createHash("sha256"); - for (const key of keys.slice().sort()) { - hash.update(key); - hash.update("\0"); - hash.update(values[key] ?? ""); - hash.update("\0"); - } - return `sha256:${hash.digest("hex")}`; -} - -export function shQuote(value: string): string { - return `'${value.replaceAll("'", "'\"'\"'")}'`; -} - export function escapeTomlString(value: string): string { return value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\""); } diff --git a/scripts/src/platform-infra.ts b/scripts/src/platform-infra.ts index 64bf5df0..dc910038 100644 --- a/scripts/src/platform-infra.ts +++ b/scripts/src/platform-infra.ts @@ -6,9 +6,8 @@ import { rootPath } from "./config"; import { startJob } from "./jobs"; import type { RenderedCliResult } from "./output"; import { pk01CaddyMergeManagedBlocksPython, renderCaddyManagedBlock, renderSimpleReverseProxyCaddySiteBlock } from "./pk01-caddy"; -import { prepareFrpcSecret } from "./platform-infra-public-service"; +import { capture, compactCapture, parseJsonOutput, prepareFrpcSecret, shQuote } from "./platform-infra-public-service"; import { fingerprintSecretValues, parseEnvFile, readEnvSourceFile, readTextFile, redactRepoPath, requiredEnvValue } from "./secrets"; -import { runSshCommandCapture, type SshCaptureResult } from "./ssh"; const serviceName = "sub2api"; const fieldManager = "unidesk-platform-infra"; @@ -2701,10 +2700,6 @@ function baseDomain(hostname: string): string { return parts.length <= 2 ? hostname : parts.slice(-2).join("."); } -function shQuote(value: string): string { - return `'${value.replaceAll("'", "'\"'\"'")}'`; -} - function secretRoot(sub2api: Sub2ApiConfig): string { const root = sub2api.runtime.secrets.root; return isAbsolute(root) ? root : rootPath(root); @@ -4150,24 +4145,6 @@ PY `; } -async function capture(config: UniDeskConfig, target: string, args: string[], input?: string): Promise { - return await runSshCommandCapture(config, target, args, input); -} - -function parseJsonOutput(stdout: string): Record | null { - const trimmed = stdout.trim(); - if (trimmed.length === 0) return null; - const start = trimmed.indexOf("{"); - const end = trimmed.lastIndexOf("}"); - if (start === -1 || end === -1 || end <= start) return null; - try { - const parsed = JSON.parse(trimmed.slice(start, end + 1)) as unknown; - return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed as Record : null; - } catch { - return null; - } -} - function boolField(value: Record | null, key: string, defaultValue: boolean): boolean { if (value === null) return defaultValue; const field = value[key]; @@ -4177,14 +4154,3 @@ function boolField(value: Record | null, key: string, defaultVa function asRecordOrNull(value: unknown): Record | null { return typeof value === "object" && value !== null && !Array.isArray(value) ? value as Record : null; } - -function compactCapture(result: SshCaptureResult, options: { full?: boolean } = {}): Record { - const full = options.full ?? false; - return { - exitCode: result.exitCode, - stdoutBytes: Buffer.byteLength(result.stdout, "utf8"), - stderrBytes: Buffer.byteLength(result.stderr, "utf8"), - stdoutTail: full || result.exitCode !== 0 ? result.stdout.slice(-8000) : "", - stderrTail: full || result.exitCode !== 0 ? result.stderr.slice(-4000) : "", - }; -}