61 lines
2.5 KiB
TypeScript
61 lines
2.5 KiB
TypeScript
import { createConnection } from "node:net";
|
|
|
|
const API_CREDENTIALS = ["Dada/P0A/api/resend", "Dada/P0A/api/amap", "Dada/P0A/admin/pepper"] as const;
|
|
|
|
export async function receiveApiCredentials(input: NodeJS.ReadableStream = process.stdin) {
|
|
const chunks: Buffer[] = [];
|
|
for await (const chunk of input) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
const payload = Buffer.concat(chunks);
|
|
try {
|
|
const parsed = JSON.parse(payload.toString("utf8")) as Record<string, unknown>;
|
|
const names = Object.keys(parsed).sort();
|
|
const expected = [...API_CREDENTIALS].sort();
|
|
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] === "")) {
|
|
throw new Error("API credential channel contains an invalid credential value.");
|
|
}
|
|
return parsed as Record<(typeof API_CREDENTIALS)[number], string>;
|
|
} finally {
|
|
payload.fill(0);
|
|
for (const chunk of chunks) chunk.fill(0);
|
|
chunks.length = 0;
|
|
}
|
|
}
|
|
|
|
export function initializeApiCredentialClients(credentials: Record<(typeof API_CREDENTIALS)[number], string>) {
|
|
const configured = API_CREDENTIALS.every((name) => credentials[name].length > 0);
|
|
const adminPepperValue = credentials["Dada/P0A/admin/pepper"];
|
|
for (const name of API_CREDENTIALS) credentials[name] = "";
|
|
if (!configured) throw new Error("API credential client initialization failed.");
|
|
return { adminAllowlistPepper: Buffer.from(adminPepperValue, "utf8") };
|
|
}
|
|
|
|
export function attachApiSupervisorControl(pipeName: string, shutdown: () => Promise<void>) {
|
|
const socket = createConnection(`\\\\.\\pipe\\${pipeName}`);
|
|
let pending = "";
|
|
let connected = false;
|
|
const pendingStatuses: string[] = [];
|
|
socket.setEncoding("utf8");
|
|
socket.on("connect", () => {
|
|
connected = true;
|
|
socket.write("ready\n");
|
|
for (const status of pendingStatuses.splice(0)) socket.write(`${status}\n`);
|
|
});
|
|
socket.on("data", (chunk) => {
|
|
pending += chunk;
|
|
if (!pending.includes("\n")) return;
|
|
const [command] = pending.split("\n", 1);
|
|
pending = "";
|
|
if (command === "shutdown") void shutdown().finally(() => socket.end());
|
|
});
|
|
return {
|
|
reportStatus(status: "storage_unavailable") {
|
|
if (socket.destroyed) return;
|
|
if (connected) socket.write(`${status}\n`); else pendingStatuses.push(status);
|
|
},
|
|
socket,
|
|
};
|
|
}
|