fix(POSTV1-01): 允许非关键外部服务降级启动
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { createConnection } from "node:net";
|
||||
|
||||
import { RealAmapAdapter } from "./amap-adapter.js";
|
||||
import { MockAmapAdapter, RealAmapAdapter } from "./amap-adapter.js";
|
||||
|
||||
const API_CREDENTIALS = ["Dada/P0A/api/resend", "Dada/P0A/api/amap", "Dada/P0A/admin/pepper"] as const;
|
||||
|
||||
@@ -15,7 +15,7 @@ export async function receiveApiCredentials(input: NodeJS.ReadableStream = proce
|
||||
if (names.length !== expected.length || names.some((name, index) => name !== expected[index])) {
|
||||
throw new Error("API credential channel contains an unexpected credential scope.");
|
||||
}
|
||||
if (expected.some((name) => typeof parsed[name] !== "string" || parsed[name] === "")) {
|
||||
if (expected.some((name) => typeof parsed[name] !== "string")) {
|
||||
throw new Error("API credential channel contains an invalid credential value.");
|
||||
}
|
||||
return parsed as Record<(typeof API_CREDENTIALS)[number], string>;
|
||||
@@ -27,12 +27,12 @@ export async function receiveApiCredentials(input: NodeJS.ReadableStream = proce
|
||||
}
|
||||
|
||||
export function initializeApiCredentialClients(credentials: Record<(typeof API_CREDENTIALS)[number], string>) {
|
||||
const configured = API_CREDENTIALS.every((name) => credentials[name].length > 0);
|
||||
try {
|
||||
if (!configured) throw new Error("API credential client initialization failed.");
|
||||
try {
|
||||
const adminPepper = credentials["Dada/P0A/admin/pepper"];
|
||||
if (!adminPepper) throw new Error("admin_pepper_not_configured");
|
||||
return {
|
||||
adminAllowlistPepper: Buffer.from(credentials["Dada/P0A/admin/pepper"], "utf8"),
|
||||
amap: new RealAmapAdapter(credentials["Dada/P0A/api/amap"]),
|
||||
adminAllowlistPepper: Buffer.from(adminPepper, "utf8"),
|
||||
amap: credentials["Dada/P0A/api/amap"] ? new RealAmapAdapter(credentials["Dada/P0A/api/amap"]) : new MockAmapAdapter(),
|
||||
};
|
||||
} finally {
|
||||
for (const name of API_CREDENTIALS) credentials[name] = "";
|
||||
|
||||
@@ -13,7 +13,7 @@ export async function receiveWorkerCredentials(input: NodeJS.ReadableStream = pr
|
||||
if (names.length !== expected.length || names.some((name, index) => name !== expected[index])) {
|
||||
throw new Error("Worker credential channel contains an unexpected credential scope.");
|
||||
}
|
||||
if (expected.some((name) => typeof parsed[name] !== "string" || parsed[name] === "")) {
|
||||
if (expected.some((name) => typeof parsed[name] !== "string")) {
|
||||
throw new Error("Worker credential channel contains an invalid credential value.");
|
||||
}
|
||||
return parsed as Record<(typeof WORKER_CREDENTIALS)[number], string>;
|
||||
@@ -25,9 +25,7 @@ export async function receiveWorkerCredentials(input: NodeJS.ReadableStream = pr
|
||||
}
|
||||
|
||||
export function initializeWorkerCredentialClient(credentials: Record<(typeof WORKER_CREDENTIALS)[number], string>) {
|
||||
const configured = WORKER_CREDENTIALS.every((name) => credentials[name].length > 0);
|
||||
for (const name of WORKER_CREDENTIALS) credentials[name] = "";
|
||||
if (!configured) throw new Error("Worker credential client initialization failed.");
|
||||
}
|
||||
|
||||
export function attachWorkerSupervisorControl(pipeName: string, shutdown: () => Promise<void> | void) {
|
||||
|
||||
@@ -50,7 +50,7 @@ internal static class CredentialProcessLauncher
|
||||
var credentials = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
foreach (var target in CredentialCatalog.RequiredFor(role))
|
||||
{
|
||||
credentials[target] = store.Read(target) ?? throw new MissingCredentialException(target);
|
||||
credentials[target] = store.Read(target) ?? string.Empty;
|
||||
}
|
||||
|
||||
startInfo.UseShellExecute = false;
|
||||
@@ -98,7 +98,7 @@ internal static class CredentialProcessLauncher
|
||||
var credentials = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
foreach (var target in CredentialCatalog.RequiredFor(role))
|
||||
{
|
||||
credentials[target] = store.Read(target) ?? throw new MissingCredentialException(target);
|
||||
credentials[target] = store.Read(target) ?? string.Empty;
|
||||
}
|
||||
|
||||
startInfo.UseShellExecute = false;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Diagnostics;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Dada.Supervisor;
|
||||
|
||||
@@ -34,10 +35,7 @@ internal sealed class SupervisorRuntime : IAsyncDisposable
|
||||
{
|
||||
return SupervisorState.StorageUnavailable;
|
||||
}
|
||||
if (CredentialCatalog.RequiredFor(ChildRole.Api).Concat(CredentialCatalog.RequiredFor(ChildRole.Worker)).Any(target => !credentials.IsConfigured(target)))
|
||||
{
|
||||
return SupervisorState.StartupFailed;
|
||||
}
|
||||
EnsureAdminPepper();
|
||||
|
||||
var node = Path.Combine(AppContext.BaseDirectory, "runtime", "node.exe");
|
||||
var apiEntry = Path.Combine(AppContext.BaseDirectory, "server", "api.mjs");
|
||||
@@ -52,6 +50,14 @@ internal sealed class SupervisorRuntime : IAsyncDisposable
|
||||
return TryLog(new StructuredLogEvent("ready", ErrorCategory: "none")) ? SupervisorState.Ready : SupervisorState.StorageUnavailable;
|
||||
}
|
||||
|
||||
private void EnsureAdminPepper()
|
||||
{
|
||||
if (credentials.IsConfigured(CredentialCatalog.AdminPepper)) return;
|
||||
var pepper = Convert.ToBase64String(RandomNumberGenerator.GetBytes(32));
|
||||
credentials.Write(CredentialCatalog.AdminPepper, pepper);
|
||||
Array.Clear(System.Text.Encoding.UTF8.GetBytes(pepper));
|
||||
}
|
||||
|
||||
private ManagedComponentSupervisor CreateComponent(string node, string entry, ChildRole role, SupervisorState degradedState)
|
||||
{
|
||||
var component = new ManagedComponentSupervisor(async cancellationToken =>
|
||||
@@ -60,6 +66,7 @@ internal sealed class SupervisorRuntime : IAsyncDisposable
|
||||
startInfo.WorkingDirectory = AppContext.BaseDirectory;
|
||||
startInfo.Environment["DADA_SQLITE_NATIVE_BINDING"] = Path.Combine(AppContext.BaseDirectory, "server", "native", "better_sqlite3.node");
|
||||
startInfo.Environment["DADA_SUPPORT_GATE_ROOT"] = Path.Combine(AppContext.BaseDirectory, "web", "support-gate");
|
||||
startInfo.Environment["DADA_WEB_ROOT"] = Path.Combine(AppContext.BaseDirectory, "web");
|
||||
startInfo.Environment["DADA_INSTANCE_CONFIG_PATH"] = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Dada", "P0A", "config", "instance.json");
|
||||
startInfo.ArgumentList.Add(entry);
|
||||
var child = await ManagedChildProcess.StartAsync(startInfo, role, credentials, cancellationToken);
|
||||
|
||||
Reference in New Issue
Block a user