-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Expand file tree
/
Copy pathprovisioning.server.ts
More file actions
96 lines (77 loc) · 2.69 KB
/
provisioning.server.ts
File metadata and controls
96 lines (77 loc) · 2.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import { spawn } from "child_process";
import { prisma } from "~/db.server";
import { logger } from "~/services/logger.server";
export class AgentProvisioningService {
private static VPS_IP = "178.128.150.129";
private static SSH_USER = "root";
static async provision(agentId: string) {
const agent = await prisma.agentConfig.findUnique({
where: { id: agentId },
});
if (!agent || !agent.containerName || !agent.containerPort) {
throw new Error("Invalid agent config for provisioning");
}
const { containerName, containerPort } = agent;
logger.info("Starting Docker provisioning on VPS", {
agentId,
containerName,
port: containerPort,
});
// 1. Stop and remove existing container if it exists
await this.runSshCommand(`docker rm -f ${containerName} || true`);
// 2. Run new container
// Note: This assumes the 'openclaw' image is available on the VPS
const dockerRunCmd = `docker run -d --name ${containerName} -p ${containerPort}:8000 --restart always openclaw:latest`;
try {
await this.runSshCommand(dockerRunCmd);
// 3. Update status to healthy if provisioning command succeeded
await prisma.agentConfig.update({
where: { id: agentId },
data: { status: "healthy" },
});
logger.info("Agent provisioning completed successfully", { agentId });
} catch (error) {
logger.error("Docker run failed", { agentId, error });
await prisma.agentConfig.update({
where: { id: agentId },
data: { status: "unhealthy" },
});
await prisma.agentHealthCheck.create({
data: {
agentId,
isHealthy: false,
errorMessage: error instanceof Error ? error.message : "Docker run failed",
},
});
throw error;
}
}
private static runSshCommand(command: string): Promise<string> {
return new Promise((resolve, reject) => {
// Assumes SSH key is configured for the root user on the host running the webapp
const ssh = spawn("ssh", [
"-o", "StrictHostKeyChecking=no",
`${this.SSH_USER}@${this.VPS_IP}`,
command
]);
let stdout = "";
let stderr = "";
ssh.stdout.on("data", (data: Buffer) => {
stdout += data.toString();
});
ssh.stderr.on("data", (data: Buffer) => {
stderr += data.toString();
});
ssh.on("close", (code: number | null) => {
if (code === 0) {
resolve(stdout.trim());
} else {
reject(new Error(`SSH command failed with code ${code}: ${stderr.trim()}`));
}
});
ssh.on("error", (err: Error) => {
reject(err);
});
});
}
}