Merge pull request #17 from orus-dev/small-changes

Small changes
This commit is contained in:
2026-02-15 08:41:52 -08:00
committed by GitHub
6 changed files with 81 additions and 6 deletions
+30 -1
View File
@@ -1,7 +1,11 @@
import { NextRequest, NextResponse } from "next/server"; import { NextRequest, NextResponse } from "next/server";
import testAuth from "../../../lib/server/auth"; import testAuth from "../../../lib/server/auth";
import { StatusCodes } from "http-status-codes"; import { StatusCodes } from "http-status-codes";
import { deployApp, toggleDeployment } from "@/core/deployment"; import {
deployApp,
removeDeployment,
toggleDeployment,
} from "@/core/deployment";
import { Application, Port } from "@/lib/types"; import { Application, Port } from "@/lib/types";
export async function POST(req: NextRequest): Promise<NextResponse> { export async function POST(req: NextRequest): Promise<NextResponse> {
@@ -59,3 +63,28 @@ export async function PUT(req: NextRequest): Promise<NextResponse> {
message: "ok", message: "ok",
}); });
} }
export async function DELETE(req: NextRequest): Promise<NextResponse> {
const deployId = req.nextUrl.searchParams.get("deployId");
const auth = await testAuth(req);
if (auth) {
return NextResponse.json(
{ message: auth },
{ status: StatusCodes.UNAUTHORIZED },
);
}
if (!deployId) {
return NextResponse.json(
{ message: "Body missing deployId" },
{ status: StatusCodes.BAD_REQUEST },
);
}
await removeDeployment(deployId);
return NextResponse.json({
message: "ok",
});
}
+1 -1
View File
@@ -48,7 +48,7 @@ export default function ApplicationsPage() {
</RegisterAppDialog> </RegisterAppDialog>
</div> </div>
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3"> <div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{apps.map((app) => ( {apps.toReversed().map((app) => (
<Card key={app.id}> <Card key={app.id}>
<CardHeader> <CardHeader>
<CardTitle>{app.name}</CardTitle> <CardTitle>{app.name}</CardTitle>
+12 -3
View File
@@ -11,7 +11,11 @@ import {
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Plus, Play, Pause, Square, RotateCcw } from "lucide-react"; import { Plus, Play, Pause, Square, RotateCcw } from "lucide-react";
import { getDeployments, toggleDeployment } from "@/lib/dockship/deploy"; import {
getDeployments,
removeDeployment,
toggleDeployment,
} from "@/lib/dockship/deploy";
import { getApps } from "@/lib/dockship/application"; import { getApps } from "@/lib/dockship/application";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import DeployAppDialog from "@/components/dialogs/DeployApp"; import DeployAppDialog from "@/components/dialogs/DeployApp";
@@ -53,6 +57,11 @@ export default function DeploymentsPage() {
await toggleDeployment(deployId, action); await toggleDeployment(deployId, action);
}; };
const remove = async (deployId: string) => {
console.log(`Removing deployment ${deployId}`);
await removeDeployment(deployId);
};
return ( return (
<DashboardLayout <DashboardLayout
title="Deployments" title="Deployments"
@@ -74,7 +83,7 @@ export default function DeploymentsPage() {
</DeployAppDialog> </DeployAppDialog>
</div> </div>
<div className="divide-y divide-border flex flex-col gap-5"> <div className="divide-y divide-border flex flex-col-reverse gap-5">
{deployments.map((dep) => { {deployments.map((dep) => {
return ( return (
<Card key={`${dep.image}-${dep.container ?? "latest"}`}> <Card key={`${dep.image}-${dep.container ?? "latest"}`}>
@@ -138,7 +147,7 @@ export default function DeploymentsPage() {
<RotateCcw className="w-3 h-3" /> <RotateCcw className="w-3 h-3" />
</Button> </Button>
</DeployAppDialog> </DeployAppDialog>
<RemoveDialog remove={() => {}} /> <RemoveDialog remove={() => remove(dep.container)} />
</div> </div>
</div> </div>
</CardContent> </CardContent>
+1 -1
View File
@@ -192,7 +192,7 @@ export default function OverviewPage() {
</CardHeader> </CardHeader>
<CardContent className="overflow-x-auto"> <CardContent className="overflow-x-auto">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 min-w-75"> <div className="grid grid-cols-1 md:grid-cols-3 gap-4 min-w-75">
{nodes.map((node, i) => ( {nodes.toReversed().map((node, i) => (
<div key={i} className="border border-border p-4 bg-secondary/30"> <div key={i} className="border border-border p-4 bg-secondary/30">
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between mb-3 gap-2"> <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between mb-3 gap-2">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
+19
View File
@@ -9,6 +9,7 @@ import pLimit from "p-limit";
import { randomUUID } from "crypto"; import { randomUUID } from "crypto";
import { cloneRepo } from "./git"; import { cloneRepo } from "./git";
import tar from "tar-fs"; import tar from "tar-fs";
import { read, write } from "@/lib/server/file";
const docker = new Docker(); const docker = new Docker();
@@ -180,3 +181,21 @@ export async function toggleDeployment(
console.error(`Failed to ${action} container ${containerId}:`, err); console.error(`Failed to ${action} container ${containerId}:`, err);
} }
} }
export async function removeDeployment(deployId: string) {
const apps = await read(() => [] as Application[], "data", "apps.json");
const app = apps.find((a) => a.deployments.includes(deployId));
if (app) {
app.deployments = app.deployments.filter((id) => id !== deployId);
await write(apps, "data", "apps.json");
}
try {
const container = docker.getContainer(deployId);
await container.stop();
await container.remove({ force: true });
await docker.pruneImages();
} catch (err) {
console.error(`Failed to remove container ${deployId}:`, err);
}
}
+18
View File
@@ -82,3 +82,21 @@ export async function toggleDeployment(
return "ok"; return "ok";
} }
export async function removeDeployment(deployId: string) {
const nodes = await getNodes();
await Promise.all(
nodes.map(
async (node) =>
await axios.delete(
`http://${node.ip}:${process.env.PORT}/api/deploy?deployId=${deployId}`,
{
headers: { Authorization: `ApiKey ${node.key}` },
},
),
),
);
return "ok";
}