feat: move code queue to d601

This commit is contained in:
Codex
2026-05-14 19:13:43 +00:00
parent cbbed004a6
commit c930607316
28 changed files with 359 additions and 242 deletions
+69 -9
View File
@@ -19,7 +19,7 @@ export interface ContainerStatus {
ports: string;
}
const rebuildableServices = ["backend-core", "frontend", "provider-gateway", "todo-note", "code-queue", "project-manager", "baidu-netdisk", "oa-event-flow"] as const;
const rebuildableServices = ["backend-core", "frontend", "provider-gateway", "todo-note", "project-manager", "baidu-netdisk", "oa-event-flow"] as const;
export type RebuildableService = typeof rebuildableServices[number];
export function isRebuildableService(value: string | undefined): value is RebuildableService {
@@ -60,7 +60,7 @@ export function writeComposeEnv(config: UniDeskConfig, freshLogPrefix: boolean):
const previousValue = (key: string): string => previousRaw.match(new RegExp(`^${key}=(.*)$`, "m"))?.[1]?.replace(/^"|"$/g, "") ?? "";
const runtimeSecret = (key: string): string => process.env[key] ?? previousValue(key);
const runtimeSecretWithDefault = (key: string, defaultValue: string, legacyDefault = ""): string => {
if (process.env[key] !== undefined) return process.env[key] ?? defaultValue;
if (process.env[key] !== undefined) return process.env[key] || defaultValue;
const previous = previousValue(key);
if (previous.length > 0 && previous !== legacyDefault) return previous;
return defaultValue;
@@ -86,11 +86,13 @@ export function writeComposeEnv(config: UniDeskConfig, freshLogPrefix: boolean):
chmodSync(join(logRoot, logDay), 0o777);
const labels = JSON.stringify(config.providerGateway.labels);
const microservices = JSON.stringify(config.microservices);
const restrictedHostBind = config.network.restrictedHostAccess?.bindHost || "127.0.0.1";
const lines = {
UNIDESK_PUBLIC_HOST: config.network.publicHost,
UNIDESK_CORE_PORT: String(config.network.core.port),
UNIDESK_FRONTEND_PORT: String(config.network.frontend.port),
UNIDESK_DATABASE_PORT: String(config.network.database.port),
UNIDESK_DATABASE_BIND_HOST: runtimeSecretWithDefault("UNIDESK_DATABASE_BIND_HOST", restrictedHostBind, "127.0.0.1"),
UNIDESK_PROVIDER_INGRESS_PORT: String(config.network.providerIngress.port),
UNIDESK_DATABASE_USER: config.database.user,
UNIDESK_DATABASE_PASSWORD: config.database.password,
@@ -147,6 +149,8 @@ export function writeComposeEnv(config: UniDeskConfig, freshLogPrefix: boolean):
UNIDESK_CODE_QUEUE_DEV_CONTAINER_IMAGE: runtimeSecret("UNIDESK_CODE_QUEUE_DEV_CONTAINER_IMAGE"),
UNIDESK_CODE_QUEUE_DEV_CONTAINER_WORKDIR: runtimeSecret("UNIDESK_CODE_QUEUE_DEV_CONTAINER_WORKDIR") || "/home/ubuntu",
UNIDESK_OA_EVENT_FLOW_BASE_URL: runtimeSecret("UNIDESK_OA_EVENT_FLOW_BASE_URL") || "http://oa-event-flow:4255",
UNIDESK_OA_EVENT_FLOW_PORT: runtimeSecret("UNIDESK_OA_EVENT_FLOW_PORT") || "4255",
UNIDESK_OA_EVENT_FLOW_BIND_HOST: runtimeSecretWithDefault("UNIDESK_OA_EVENT_FLOW_BIND_HOST", restrictedHostBind, "127.0.0.1"),
UNIDESK_CODE_QUEUE_NOTIFY_CLAUDEQQ_ENABLED: runtimeSecret("UNIDESK_CODE_QUEUE_NOTIFY_CLAUDEQQ_ENABLED") || "true",
UNIDESK_CODE_QUEUE_NOTIFY_CLAUDEQQ_BASE_URL: runtimeSecret("UNIDESK_CODE_QUEUE_NOTIFY_CLAUDEQQ_BASE_URL") || "http://backend-core:8080/api/microservices/claudeqq/proxy",
UNIDESK_CODE_QUEUE_NOTIFY_CLAUDEQQ_TARGET_TYPE: runtimeSecret("UNIDESK_CODE_QUEUE_NOTIFY_CLAUDEQQ_TARGET_TYPE") || "private",
@@ -182,7 +186,12 @@ export function startStack(config: UniDeskConfig): unknown {
throw new Error(`Fixed UniDesk port is occupied before start: ${occupiedPorts.map((p) => `${p.name}:${p.port}`).join(", ")}`);
}
const upCommand = [...compose, "up", "-d", "--build", "--remove-orphans"];
const command = ["bash", "-lc", composeLockedScript(`set -euo pipefail; ${shellJoin(upCommand)}`)];
const script = [
"set -euo pipefail",
restrictedHostAccessScript(config),
shellJoin(upCommand),
].filter((line) => line.length > 0).join("\n");
const command = ["bash", "-lc", composeLockedScript(script)];
const job = startJob("server_start", command, "Build and start UniDesk services without tearing down running queue containers");
return { job, runtimeEnv, command, ports: fixedPorts(config) };
}
@@ -246,14 +255,14 @@ export function rebuildService(config: UniDeskConfig, service: RebuildableServic
const script = [
"set -euo pipefail",
`echo ${shellJoin(["rebuild_service", service, "build_first_then_force_recreate_with_validation"])}`,
restrictedHostAccessScript(config),
shellJoin(buildCommand),
`nohup bash -lc ${shellQuote(watchdogScript)} >/dev/null 2>&1 &`,
shellJoin(upCommand),
validateScript,
].join("\n");
const command = ["bash", "-lc", composeLockedScript(script)];
const jobRunner = service === "code-queue" ? { runner: "docker" as const, dockerImage: "unidesk-code-queue:latest" } : {};
const job = startJob("server_rebuild", command, `Rebuild and validate UniDesk ${service} with serialized Docker Compose mutation`, jobRunner);
const job = startJob("server_rebuild", command, `Rebuild and validate UniDesk ${service} with serialized Docker Compose mutation`);
return {
job,
runtimeEnv,
@@ -268,7 +277,7 @@ export function rebuildService(config: UniDeskConfig, service: RebuildableServic
noDeps: true,
forceRecreate: true,
composeMutationLock: rootPath(".state", "locks", "server-compose.lock"),
jobRunner: service === "code-queue" ? "docker" : "local",
jobRunner: "local",
postUpValidation: true,
namedVolumesPreserved: true,
},
@@ -282,6 +291,36 @@ function fixedPorts(config: UniDeskConfig): Array<{ name: string; port: number;
];
}
function restrictedHostAccessScript(config: UniDeskConfig): string {
const access = config.network.restrictedHostAccess;
if (access === undefined || access.bindHost === "127.0.0.1") return "";
const ports = [
{ name: "database", containerPort: config.network.database.containerPort },
{ name: "oa-event-flow", containerPort: 4255 },
];
return [
"iptables -N DOCKER-USER 2>/dev/null || true",
...ports.flatMap((port) => [
...access.allowedSourceCidrs.map((source) => [
`iptables -C DOCKER-USER -s ${shellQuote(source)} -p tcp --dport ${port.containerPort} -j ACCEPT 2>/dev/null`,
` || iptables -I DOCKER-USER 1 -s ${shellQuote(source)} -p tcp --dport ${port.containerPort} -j ACCEPT`,
].join(" \\\n")),
[
`iptables -C DOCKER-USER -p tcp --dport ${port.containerPort} -j DROP 2>/dev/null`,
" || {",
" return_line=$(iptables -L DOCKER-USER --line-numbers | awk '$2==\"RETURN\" {print $1; exit}')",
" if [ -n \"$return_line\" ]; then",
` iptables -I DOCKER-USER "$return_line" -p tcp --dport ${port.containerPort} -j DROP`,
" else",
` iptables -A DOCKER-USER -p tcp --dport ${port.containerPort} -j DROP`,
" fi",
" }",
].join("\n"),
`echo ${shellJoin(["restricted_host_access", port.name, String(port.containerPort), access.allowedSourceCidrs.join(",")])}`,
]),
].join("\n");
}
function shellJoin(args: string[]): string {
return args.map(shellQuote).join(" ");
}
@@ -366,6 +405,11 @@ function dockerExec(config: UniDeskConfig, container: string, command: string[])
export async function stackStatus(config: UniDeskConfig): Promise<unknown> {
const runtimeEnv = writeComposeEnv(config, false);
const runtimeRaw = existsSync(runtimeEnv.envFile) ? readFileSync(runtimeEnv.envFile, "utf8") : "";
const runtimeValue = (key: string): string => runtimeRaw.match(new RegExp(`^${key}=(.*)$`, "m"))?.[1]?.replace(/^"|"$/g, "") ?? "";
const databaseBindHost = runtimeValue("UNIDESK_DATABASE_BIND_HOST") || "127.0.0.1";
const oaEventFlowBindHost = runtimeValue("UNIDESK_OA_EVENT_FLOW_BIND_HOST") || "127.0.0.1";
const oaEventFlowPort = Number(runtimeValue("UNIDESK_OA_EVENT_FLOW_PORT") || "4255");
const coreHealth = dockerExecJson("unidesk-backend-core", "fetch('http://127.0.0.1:8080/health').then(r=>r.json()).then(j=>console.log(JSON.stringify({ok:true,status:200,body:j}))).catch(e=>{console.log(JSON.stringify({ok:false,error:String(e)}));process.exit(1)})");
const overview = dockerExecJson("unidesk-backend-core", "fetch('http://127.0.0.1:8080/api/overview').then(r=>r.json()).then(j=>console.log(JSON.stringify({ok:true,status:200,body:j}))).catch(e=>{console.log(JSON.stringify({ok:false,error:String(e)}));process.exit(1)})");
return {
@@ -373,12 +417,28 @@ export async function stackStatus(config: UniDeskConfig): Promise<unknown> {
publicPorts: fixedPorts(config),
blockedPublicPorts: [
{ name: "backend-core-rest", port: config.network.core.port, listening: isPortListening(config.network.core.port), expected: "not-listening" },
{ name: "database", port: config.network.database.port, listening: isPortListening(config.network.database.port), expected: "not-listening" },
],
restrictedHostPorts: [
{
name: "database",
bindHost: databaseBindHost,
hostPort: config.network.database.port,
containerPort: config.network.database.containerPort,
listening: isPortListening(config.network.database.port),
expected: databaseBindHost === "127.0.0.1" ? "local-only" : "restricted-to-code-queue-provider",
},
{
name: "oa-event-flow",
bindHost: oaEventFlowBindHost,
hostPort: oaEventFlowPort,
containerPort: 4255,
listening: isPortListening(oaEventFlowPort),
expected: oaEventFlowBindHost === "127.0.0.1" ? "local-only" : "restricted-to-code-queue-provider",
},
],
internalPorts: [
{ name: "backend-core", containerPort: config.network.core.containerPort, hostPort: null },
{ name: "database", containerPort: config.network.database.containerPort, hostPort: null },
{ name: "code-queue", containerPort: 4222, hostPort: null },
{ name: "project-manager", containerPort: 4233, hostPort: null },
{ name: "baidu-netdisk", containerPort: 4244, hostPort: null },
{ name: "oa-event-flow", containerPort: 4255, hostPort: null },
@@ -419,7 +479,7 @@ export function stackLogs(config: UniDeskConfig, tailBytes: number): unknown {
const currentFiles = allFiles.filter((path) => basename(path).startsWith(runtimeEnv.logPrefix));
const selectedFiles = (currentFiles.length > 0 ? currentFiles : allFiles.slice(-12)).slice(-12);
const files = selectedFiles.map((path) => ({ path, name: basename(path), tail: tailFile(path, tailBytes) }));
const containerNames = ["unidesk-database", "unidesk-backend-core", "unidesk-frontend", "unidesk-provider-gateway-main", "todo-note-backend", "code-queue-backend", "project-manager-backend", "baidu-netdisk-backend", "oa-event-flow-backend"];
const containerNames = ["unidesk-database", "unidesk-backend-core", "unidesk-frontend", "unidesk-provider-gateway-main", "todo-note-backend", "project-manager-backend", "baidu-netdisk-backend", "oa-event-flow-backend"];
const docker = containerNames.map((name) => {
const result = runCommand(["docker", "logs", "--tail", "40", name], repoRoot);
return { name, exitCode: result.exitCode, stdoutTail: result.stdout.slice(-tailBytes), stderrTail: result.stderr.slice(-tailBytes) };