refactor: centralize platform infra ops helpers (#355)

Co-authored-by: Codex <codex@noreply.local>
This commit is contained in:
Lyon
2026-06-14 12:20:25 +08:00
committed by GitHub
parent adffc9d818
commit c3da6f5870
3 changed files with 56 additions and 90 deletions
+53 -1
View File
@@ -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<SshCaptureResult> {
return await runSshCommandCapture(config, route, args, stdin);
}
export function parseJsonOutput(stdout: string): Record<string, unknown> | 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<string, unknown> : null;
} catch {
return null;
}
}
export function compactCapture(result: SshCaptureResult, options: { full?: boolean } = {}): Record<string, unknown> {
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_<redacted>")
.replace(/(postgres(?:ql)?:\/\/)[^@\s"']+@/giu, "$1<redacted>@")
.replace(/(Bearer\s+)[A-Za-z0-9._~+/=-]+/giu, "$1<redacted>")
.replace(/(["']?(?:N8N_ENCRYPTION_KEY|PASSWORD|SECRET|TOKEN|API[_-]?KEY|APIKEY|JWT[_-]?SECRET|DATABASE[_-]?URL)["']?\s*[:=]\s*["']?)[^"',\s}]+(["']?)/giu, "$1<redacted>$2");
}
export function fingerprintValues(values: Record<string, string>, 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<string, string | boolean> {
const stringOptions = new Set(["--target", ...(spec.stringOptions ?? [])]);
const flagOptions = new Set(["--full", "--raw", ...(spec.flagOptions ?? [])]);