feat: add dev pod management page with start/stop/restart controls
All checks were successful
Build & Release / build (push) Successful in 12s
All checks were successful
Build & Release / build (push) Successful in 12s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@ import { config } from "./lib/config";
|
||||
import { migrate } from "./db/manager";
|
||||
import { authRoutes } from "./routes/auth";
|
||||
import { customerRoutes } from "./routes/customers";
|
||||
import { devpodRoutes } from "./routes/devpod";
|
||||
import { startSizeCollector } from "./services/sizeCollector";
|
||||
|
||||
const app = Fastify({ logger: true });
|
||||
@@ -42,6 +43,11 @@ app.register(customerRoutes, {
|
||||
onRequest: [app.authenticate],
|
||||
} as any);
|
||||
|
||||
app.register(devpodRoutes, {
|
||||
prefix: "/api",
|
||||
onRequest: [app.authenticate],
|
||||
} as any);
|
||||
|
||||
await migrate();
|
||||
startSizeCollector(app.log);
|
||||
|
||||
|
||||
63
src/routes/devpod.ts
Normal file
63
src/routes/devpod.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import type { FastifyInstance } from "fastify";
|
||||
import { k8sFetch, rolloutRestart } from "../lib/k8s";
|
||||
|
||||
const NAMESPACE = "dev";
|
||||
const DEPLOYMENT = "dev";
|
||||
|
||||
export async function devpodRoutes(app: FastifyInstance) {
|
||||
app.get("/devpod/status", async (_req, reply) => {
|
||||
const [deployRes, podsRes] = await Promise.allSettled([
|
||||
k8sFetch(`/apis/apps/v1/namespaces/${NAMESPACE}/deployments/${DEPLOYMENT}`).then(r => r.json()),
|
||||
k8sFetch(`/api/v1/namespaces/${NAMESPACE}/pods?labelSelector=app=${DEPLOYMENT}`).then(r => r.json()),
|
||||
]);
|
||||
|
||||
const deploy = deployRes.status === "fulfilled" ? deployRes.value : null;
|
||||
const podsRaw = podsRes.status === "fulfilled" ? podsRes.value : null;
|
||||
|
||||
const replicas = deploy?.spec?.replicas ?? 0;
|
||||
const readyReplicas = deploy?.status?.readyReplicas ?? 0;
|
||||
const image = deploy?.spec?.template?.spec?.containers?.[0]?.image ?? null;
|
||||
|
||||
const pods = (podsRaw?.items ?? []).map((pod: any) => ({
|
||||
name: pod.metadata.name,
|
||||
ready: (pod.status?.containerStatuses ?? []).every((c: any) => c.ready),
|
||||
readyCount: (pod.status?.containerStatuses ?? []).filter((c: any) => c.ready).length,
|
||||
totalCount: (pod.status?.containerStatuses ?? []).length,
|
||||
status: pod.status?.phase ?? "Unknown",
|
||||
restarts: (pod.status?.containerStatuses ?? []).reduce((s: number, c: any) => s + (c.restartCount ?? 0), 0),
|
||||
startedAt: pod.status?.startTime ?? null,
|
||||
}));
|
||||
|
||||
// Determine overall state
|
||||
let state: "stopped" | "running" | "starting" | "error";
|
||||
if (replicas === 0) state = "stopped";
|
||||
else if (readyReplicas > 0 && pods.some((p: any) => p.ready)) state = "running";
|
||||
else if (pods.some((p: any) => p.status === "Failed")) state = "error";
|
||||
else state = "starting";
|
||||
|
||||
return reply.send({ state, replicas, readyReplicas, image, pods });
|
||||
});
|
||||
|
||||
app.post("/devpod/start", async (_req, reply) => {
|
||||
await k8sFetch(`/apis/apps/v1/namespaces/${NAMESPACE}/deployments/${DEPLOYMENT}`, {
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/strategic-merge-patch+json" },
|
||||
body: JSON.stringify({ spec: { replicas: 1 } }),
|
||||
});
|
||||
return reply.send({ message: "Dev pod starting" });
|
||||
});
|
||||
|
||||
app.post("/devpod/stop", async (_req, reply) => {
|
||||
await k8sFetch(`/apis/apps/v1/namespaces/${NAMESPACE}/deployments/${DEPLOYMENT}`, {
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/strategic-merge-patch+json" },
|
||||
body: JSON.stringify({ spec: { replicas: 0 } }),
|
||||
});
|
||||
return reply.send({ message: "Dev pod stopped" });
|
||||
});
|
||||
|
||||
app.post("/devpod/restart", async (_req, reply) => {
|
||||
await rolloutRestart(NAMESPACE, DEPLOYMENT);
|
||||
return reply.send({ message: "Dev pod restarting" });
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user