A few fixes to containers and apps, also added remove app
This commit is contained in:
@@ -1,7 +1,11 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import testAuth from "../auth";
|
||||
import { StatusCodes } from "http-status-codes";
|
||||
import { deployNewApp, getApplications } from "@/core/server/application";
|
||||
import {
|
||||
deployNewApp,
|
||||
getApplications,
|
||||
removeApp,
|
||||
} from "@/core/server/application";
|
||||
|
||||
export async function GET(req: NextRequest): Promise<NextResponse> {
|
||||
const auth = await testAuth(req);
|
||||
@@ -47,3 +51,28 @@ export async function POST(req: NextRequest): Promise<NextResponse> {
|
||||
...res,
|
||||
});
|
||||
}
|
||||
|
||||
export async function DELETE(req: NextRequest): Promise<NextResponse> {
|
||||
const auth = await testAuth(req);
|
||||
const appId = req.nextUrl.searchParams.get("appId");
|
||||
|
||||
if (auth) {
|
||||
return NextResponse.json(
|
||||
{ message: auth },
|
||||
{ status: StatusCodes.UNAUTHORIZED }
|
||||
);
|
||||
}
|
||||
|
||||
if (!appId) {
|
||||
return NextResponse.json(
|
||||
{ message: "Missing param appId" },
|
||||
{ status: StatusCodes.BAD_REQUEST }
|
||||
);
|
||||
}
|
||||
|
||||
await removeApp(appId);
|
||||
|
||||
return NextResponse.json({
|
||||
message: "ok",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -25,9 +25,6 @@ export async function GET(req: NextRequest): Promise<NextResponse> {
|
||||
|
||||
return NextResponse.json({
|
||||
message: "ok",
|
||||
stats: {
|
||||
cpu_stats: stats?.cpu_stats,
|
||||
memory_stats: stats?.memory_stats,
|
||||
},
|
||||
stats,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,12 +4,13 @@ import { DashboardLayout } from "@/components/dashboard/DashboardLayout";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Play, Square, RotateCcw, MoreVertical, Plus } from "lucide-react";
|
||||
import { Play, Square, MoreVertical, Plus } from "lucide-react";
|
||||
import { Progress } from "@/components/ui/progress";
|
||||
import DeployApplication from "@/components/dialogs/DeployApplication";
|
||||
import { useEffect, useState } from "react";
|
||||
import { ImageApp } from "@/lib/types";
|
||||
import { getApplications } from "@/core/application";
|
||||
import { getApplications, removeApp } from "@/core/application";
|
||||
import RemoveDialog from "@/components/dialogs/Remove";
|
||||
|
||||
export default function ApplicationsPage() {
|
||||
const [applications, setApplications] = useState<ImageApp[]>([]);
|
||||
@@ -25,6 +26,11 @@ export default function ApplicationsPage() {
|
||||
return () => clearInterval(iid);
|
||||
}, []);
|
||||
|
||||
const remove = async (appId: string) => {
|
||||
await removeApp(appId);
|
||||
setApplications((prev) => prev.filter((app) => app.id !== appId));
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardLayout
|
||||
title="Applications"
|
||||
@@ -134,9 +140,9 @@ export default function ApplicationsPage() {
|
||||
<Play className="w-3 h-3" />
|
||||
</Button>
|
||||
)}
|
||||
<Button variant="ghost" size="icon-sm">
|
||||
<RotateCcw className="w-3 h-3" />
|
||||
</Button>
|
||||
<RemoveDialog
|
||||
remove={() => app.app?.id && remove(app.app?.id)}
|
||||
/>
|
||||
<Button variant="ghost" size="icon-sm">
|
||||
<MoreVertical className="w-3 h-3" />
|
||||
</Button>
|
||||
|
||||
@@ -15,17 +15,7 @@ import {
|
||||
startContainer,
|
||||
stopContainer,
|
||||
} from "@/core/docker";
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogTrigger,
|
||||
AlertDialogContent,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogCancel,
|
||||
AlertDialogAction,
|
||||
} from "@/components/ui/alert-dialog";
|
||||
import RemoveDialog from "@/components/dialogs/Remove";
|
||||
|
||||
type ContainerSummary = {
|
||||
id: string;
|
||||
@@ -68,39 +58,6 @@ async function summarizeContainer(
|
||||
};
|
||||
}
|
||||
|
||||
function RemoveDialog({ container }: { container: ContainerSummary }) {
|
||||
return (
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button variant="ghost" size="icon-sm">
|
||||
<Trash2 className="w-3 h-3 text-destructive" />
|
||||
</Button>
|
||||
</AlertDialogTrigger>
|
||||
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
This action cannot be undone. This will permanently remove the
|
||||
container.
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
|
||||
<AlertDialogAction
|
||||
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
||||
onClick={() => removeContainer(container.id)}
|
||||
>
|
||||
Delete
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ContainersPage() {
|
||||
const [containers, setContainers] = useState<ContainerSummary[]>([]);
|
||||
|
||||
@@ -121,6 +78,9 @@ export default function ContainersPage() {
|
||||
};
|
||||
|
||||
fetchNodes();
|
||||
|
||||
const iid = setInterval(fetchNodes, 3000);
|
||||
return () => clearInterval(iid);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@@ -221,7 +181,9 @@ export default function ContainersPage() {
|
||||
<Play className="w-3 h-3" />
|
||||
</Button>
|
||||
)}
|
||||
<RemoveDialog container={container} />
|
||||
<RemoveDialog
|
||||
remove={() => removeContainer(container.id)}
|
||||
/>
|
||||
<Button variant="ghost" size="icon-sm">
|
||||
<MoreVertical className="w-3 h-3" />
|
||||
</Button>
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogTrigger,
|
||||
AlertDialogContent,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogCancel,
|
||||
AlertDialogAction,
|
||||
} from "@/components/ui/alert-dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Trash2 } from "lucide-react";
|
||||
|
||||
export default function RemoveDialog<T>({ remove }: { remove: () => T }) {
|
||||
return (
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button variant="ghost" size="icon-sm">
|
||||
<Trash2 className="w-3 h-3 text-destructive" />
|
||||
</Button>
|
||||
</AlertDialogTrigger>
|
||||
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
This action cannot be undone. This will permanently remove the item.
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
|
||||
<AlertDialogAction
|
||||
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
||||
onClick={remove}
|
||||
>
|
||||
Delete
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
);
|
||||
}
|
||||
@@ -72,3 +72,14 @@ export async function setEnv(appId: string, variables: EnvVariable[]) {
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export async function removeApp(appId: string) {
|
||||
const nodes = await getNodes();
|
||||
|
||||
nodes.forEach(async (n) => {
|
||||
await axios.delete(`http://${n.ip}:3000/api/applications`, {
|
||||
params: { appId },
|
||||
headers: { Authorization: `ApiKey ${n.key}` },
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ export async function getContainerStats(
|
||||
headers: { Authorization: `ApiKey ${n.key}` },
|
||||
params: { containerId },
|
||||
})
|
||||
).data
|
||||
).data?.stats
|
||||
)
|
||||
)
|
||||
).find((s) => s);
|
||||
|
||||
@@ -164,3 +164,11 @@ export async function setEnv(appId: string, variables: EnvVariable[]) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function removeApp(appId: string) {
|
||||
const imageTag = `dockship/${appId}:latest`;
|
||||
const image = docker.getImage(imageTag);
|
||||
await image.remove();
|
||||
const appPath = path.join(DATA_DIR, appId);
|
||||
fs.rmSync(appPath, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -15,7 +15,15 @@ export async function getDocker() {
|
||||
export async function getContainerStats(
|
||||
containerId: string
|
||||
): Promise<SimpleStats | undefined> {
|
||||
const stats = await docker.getContainer(containerId).stats({ stream: false });
|
||||
const container = docker.getContainer(containerId);
|
||||
|
||||
const data = await container.inspect();
|
||||
|
||||
if (!data.State.Running) {
|
||||
return;
|
||||
}
|
||||
|
||||
const stats = await container.stats({ stream: false });
|
||||
|
||||
return {
|
||||
cpu: CPUusage(stats),
|
||||
|
||||
Reference in New Issue
Block a user