Start/stop deployments
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import testAuth from "../auth";
|
||||
import { StatusCodes } from "http-status-codes";
|
||||
import { deployApp } from "@/core/deployment";
|
||||
import { deployApp, toggleDeployment } from "@/core/deployment";
|
||||
import { Application, Port } from "@/lib/types";
|
||||
|
||||
export async function POST(req: NextRequest): Promise<NextResponse> {
|
||||
@@ -15,14 +15,14 @@ export async function POST(req: NextRequest): Promise<NextResponse> {
|
||||
if (auth) {
|
||||
return NextResponse.json(
|
||||
{ message: auth },
|
||||
{ status: StatusCodes.UNAUTHORIZED }
|
||||
{ status: StatusCodes.UNAUTHORIZED },
|
||||
);
|
||||
}
|
||||
|
||||
if (!name || !app) {
|
||||
return NextResponse.json(
|
||||
{ message: "Body missing name, appId, nodeId" },
|
||||
{ status: StatusCodes.BAD_REQUEST }
|
||||
{ status: StatusCodes.BAD_REQUEST },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -33,3 +33,29 @@ export async function POST(req: NextRequest): Promise<NextResponse> {
|
||||
deployId,
|
||||
});
|
||||
}
|
||||
|
||||
export async function PUT(req: NextRequest): Promise<NextResponse> {
|
||||
const auth = await testAuth(req);
|
||||
const { deployId, action }: { deployId: string; action: "start" | "stop" } =
|
||||
await req.json();
|
||||
|
||||
if (auth) {
|
||||
return NextResponse.json(
|
||||
{ message: auth },
|
||||
{ status: StatusCodes.UNAUTHORIZED },
|
||||
);
|
||||
}
|
||||
|
||||
if (!deployId || !action) {
|
||||
return NextResponse.json(
|
||||
{ message: "Body missing deployId or action" },
|
||||
{ status: StatusCodes.BAD_REQUEST },
|
||||
);
|
||||
}
|
||||
|
||||
await toggleDeployment(deployId, action);
|
||||
|
||||
return NextResponse.json({
|
||||
message: "ok",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ import {
|
||||
} from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Plus, Play, Pause } from "lucide-react";
|
||||
import { getDeployments } from "@/lib/dockship/deploy";
|
||||
import { Plus, Play, Pause, Square } from "lucide-react";
|
||||
import { getDeployments, toggleDeployment } from "@/lib/dockship/deploy";
|
||||
import { getApps } from "@/lib/dockship/application";
|
||||
import { cn } from "@/lib/utils";
|
||||
import DeployAppDialog from "@/components/dialogs/DeployApp";
|
||||
@@ -39,6 +39,14 @@ export default function DeploymentsPage() {
|
||||
return map;
|
||||
}, [apps]);
|
||||
|
||||
const changeDeploymentStatus = async (
|
||||
deployId: string,
|
||||
action: "start" | "stop",
|
||||
) => {
|
||||
console.log(`Toggling deployment ${deployId} with action ${action}`);
|
||||
await toggleDeployment(deployId, action);
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardLayout
|
||||
title="Deployments"
|
||||
@@ -90,13 +98,22 @@ export default function DeploymentsPage() {
|
||||
{dep.image}
|
||||
</p>
|
||||
<div className="flex justify-end gap-1">
|
||||
<Button variant="ghost" size="icon-sm">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
onClick={async () =>
|
||||
await changeDeploymentStatus(
|
||||
dep.container,
|
||||
dep.status === "stopped" ? "start" : "stop",
|
||||
)
|
||||
}
|
||||
>
|
||||
{dep.status === "stopped" ? (
|
||||
<Play className="w-3 h-3" />
|
||||
) : dep.status === "running" ? (
|
||||
<Pause className="w-3 h-3" />
|
||||
<Square className="w-3 h-3" />
|
||||
) : (
|
||||
<Pause className="w-3 h-3 text-primary" />
|
||||
<Square className="w-3 h-3 text-primary" />
|
||||
)}
|
||||
</Button>
|
||||
<RemoveDialog remove={() => {}} />
|
||||
|
||||
@@ -164,3 +164,20 @@ export async function deployApp(
|
||||
fs.rmSync(deployPath, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
export async function toggleDeployment(
|
||||
containerId: string,
|
||||
action: "start" | "stop",
|
||||
) {
|
||||
const container = docker.getContainer(containerId);
|
||||
|
||||
try {
|
||||
if (action === "start") {
|
||||
await container.start();
|
||||
} else {
|
||||
await container.stop();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`Failed to ${action} container ${containerId}:`, err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@ export async function getDeployments(): Promise<Deployment[]> {
|
||||
await axios.post(`http://${n.ip}:3000/api/get-deployments`, apps, {
|
||||
headers: { Authorization: `ApiKey ${n.key}` },
|
||||
})
|
||||
).data.deployments
|
||||
)
|
||||
).data.deployments,
|
||||
),
|
||||
)
|
||||
).flat();
|
||||
}
|
||||
@@ -27,7 +27,7 @@ export async function deployApp(
|
||||
name: string,
|
||||
appId: string,
|
||||
nodeId: string,
|
||||
ports: Port[]
|
||||
ports: Port[],
|
||||
): Promise<string | undefined> {
|
||||
const nodes = await getNodes();
|
||||
|
||||
@@ -44,7 +44,7 @@ export async function deployApp(
|
||||
{ name, app, ports },
|
||||
{
|
||||
headers: { Authorization: `ApiKey ${node.key}` },
|
||||
}
|
||||
},
|
||||
)
|
||||
).data.deployId;
|
||||
|
||||
@@ -54,3 +54,25 @@ export async function deployApp(
|
||||
|
||||
return deployId;
|
||||
}
|
||||
|
||||
export async function toggleDeployment(
|
||||
deployId: string,
|
||||
action: "start" | "stop",
|
||||
) {
|
||||
const nodes = await getNodes();
|
||||
|
||||
await Promise.all(
|
||||
nodes.map(
|
||||
async (node) =>
|
||||
await axios.put(
|
||||
`http://${node.ip}:3000/api/deploy`,
|
||||
{ deployId, action },
|
||||
{
|
||||
headers: { Authorization: `ApiKey ${node.key}` },
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return "ok";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user