Register application

This commit is contained in:
2026-01-03 20:01:42 +01:00
parent 1a1dc90c01
commit 61a9e1d8d9
5 changed files with 62 additions and 72 deletions
+8 -5
View File
@@ -12,7 +12,7 @@ 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 { MoreVertical, Plus, Rocket } from "lucide-react"; import { MoreVertical, Plus, Rocket } from "lucide-react";
import InstallApplicationDialog from "@/components/dialogs/InstallApplication"; import RegisterAppDialog from "@/components/dialogs/RegisterApp";
import { getApps, removeApp } from "@/lib/dockship/application"; import { getApps, removeApp } from "@/lib/dockship/application";
import RemoveDialog from "@/components/dialogs/Remove"; import RemoveDialog from "@/components/dialogs/Remove";
import DeployAppDialog from "@/components/dialogs/DeployApp"; import DeployAppDialog from "@/components/dialogs/DeployApp";
@@ -31,17 +31,20 @@ export default function ApplicationsPage() {
}; };
return ( return (
<DashboardLayout title="Applications" subtitle="Manage dockship applications"> <DashboardLayout
title="Applications"
subtitle="Manage dockship applications"
>
{/* Header */} {/* Header */}
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between mb-6"> <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between mb-6">
<div className="flex flex-wrap items-center gap-2"> <div className="flex flex-wrap items-center gap-2">
<Badge>{apps.length} apps</Badge> <Badge>{apps.length} apps</Badge>
</div> </div>
<InstallApplicationDialog> <RegisterAppDialog>
<Button size="sm" className="gap-2 w-full sm:w-auto"> <Button size="sm" className="gap-2 w-full sm:w-auto">
<Plus className="w-4 h-4" /> Install Application <Plus className="w-4 h-4" /> Register Application
</Button> </Button>
</InstallApplicationDialog> </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.map((app) => (
+1 -5
View File
@@ -131,13 +131,9 @@ export default function OverviewPage() {
{app.name} {app.name}
</div> </div>
<div className="col-span-4 text-muted-foreground"> <div className="col-span-7 text-muted-foreground">
{app.deployments.length} {app.deployments.length}
</div> </div>
<div className="col-span-3 truncate">
{nodes.find((n) => n.node_id === app.nodeId)?.name ?? "—"}
</div>
</div> </div>
)) ))
)} )}
@@ -1,6 +1,6 @@
"use client"; "use client";
import { useState, ReactNode, useEffect } from "react"; import { useState, ReactNode } from "react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Dialog, Dialog,
@@ -15,22 +15,19 @@ import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { getLiveNodes } from "@/lib/dockship/node"; import { getLiveNodes } from "@/lib/dockship/node";
import { Combobox } from "../ui/combobox"; import { Combobox } from "../ui/combobox";
import { Node, NodeLiveData } from "@/lib/types"; import { registerApp } from "@/lib/dockship/application";
import { installApp } from "@/lib/dockship/application";
import { useAsync } from "@/hooks/use-async"; import { useAsync } from "@/hooks/use-async";
export default function InstallApplicationDialog({ export default function RegisterAppDialog({
children, children,
}: { }: {
children: ReactNode; children: ReactNode;
}) { }) {
const { value: nodes } = useAsync([], getLiveNodes);
const [name, setName] = useState(""); const [name, setName] = useState("");
const [repo, setRepo] = useState(""); const [repo, setRepo] = useState("");
const [node, setNode] = useState("");
const deploy = async () => { const deploy = async () => {
console.log(await installApp(name, repo, node)); console.log(await registerApp(name, repo));
}; };
return ( return (
@@ -39,7 +36,7 @@ export default function InstallApplicationDialog({
<DialogContent className="sm:max-w-106.25"> <DialogContent className="sm:max-w-106.25">
<DialogHeader> <DialogHeader>
<DialogTitle>Install Application</DialogTitle> <DialogTitle>Register Application</DialogTitle>
</DialogHeader> </DialogHeader>
<div className="grid gap-4"> <div className="grid gap-4">
@@ -66,21 +63,6 @@ export default function InstallApplicationDialog({
placeholder="https://github.com/me/myrepo.git" placeholder="https://github.com/me/myrepo.git"
/> />
</div> </div>
<div className="grid gap-3">
<Label htmlFor="node">
Node <span className="text-destructive">*</span>
</Label>
<Combobox
value={node}
setValue={setNode}
data={nodes
.filter((node) => node.liveData)
.map((node) => ({ label: node.name, value: node.node_id }))}
placeholder="Select Node..."
listPlaceholder="No nodes found :("
/>
</div>
</div> </div>
<DialogFooter> <DialogFooter>
+48 -38
View File
@@ -1,30 +1,9 @@
"use server"; "use server";
import { verifySession } from "../../core/auth/session"; import { verifySession } from "../../core/auth/session";
import { getNodes } from "@/lib/dockship/node";
import axios from "axios";
import { Env, EnvVariable, Application } from "@/lib/types"; import { Env, EnvVariable, Application } from "@/lib/types";
import { read, write } from "@/app/api/file"; import { read, write } from "@/app/api/file";
import { randomUUID } from "crypto";
export async function installApp(name: string, repo: string, nodeId: string) {
if (await verifySession()) {
throw new Error("Unauthorized");
}
const nodes = await getNodes();
const targetNode = nodes.find((node) => node.node_id === nodeId);
return await (
await axios.post(
`http://${targetNode?.ip}:3000/api/applications`,
{ name, repo, nodeId },
{
headers: { Authorization: `ApiKey ${targetNode?.key}` },
}
)
).data.apps;
}
export async function getApps(): Promise<Application[]> { export async function getApps(): Promise<Application[]> {
if (await verifySession()) { if (await verifySession()) {
@@ -34,7 +13,54 @@ export async function getApps(): Promise<Application[]> {
return read(() => [], "data", "apps.json"); return read(() => [], "data", "apps.json");
} }
export async function registerApp(name: string, repo: string) {
if (await verifySession()) {
throw new Error("Unauthorized");
}
const app: Application = {
id: randomUUID(),
name,
repo,
createdAt: new Date().toISOString(),
deployments: [],
env: {},
};
await write([...(await getApps()), app], "data", "apps.json");
return app;
}
export async function removeApp(appId: string) {
if (await verifySession()) {
throw new Error("Unauthorized");
}
const apps = await getApps();
const nextApps = apps.filter((app) => app.id !== appId);
await write(nextApps, "data", "apps.json");
}
export async function editApp(appId: string, updatedApp: Application) {
if (await verifySession()) {
throw new Error("Unauthorized");
}
const apps = await getApps();
const nextApps = apps.map((app) => (app.id === appId ? updatedApp : app));
await write(nextApps, "data", "apps.json");
}
export async function getEnv(): Promise<Record<string, Env>> { export async function getEnv(): Promise<Record<string, Env>> {
if (await verifySession()) {
throw new Error("Unauthorized");
}
const apps = await getApps(); const apps = await getApps();
return apps.reduce<Record<string, Env>>((acc, app) => { return apps.reduce<Record<string, Env>>((acc, app) => {
@@ -61,19 +87,3 @@ export async function setEnv(
if (!app) throw new Error("App not found"); if (!app) throw new Error("App not found");
editApp(appId, { ...app, env: variables }); editApp(appId, { ...app, env: variables });
} }
export async function removeApp(appId: string) {
const apps = await getApps();
const nextApps = apps.filter((app) => app.id !== appId);
await write(nextApps, "data", "apps.json");
}
export async function editApp(appId: string, updatedApp: Application) {
const apps = await getApps();
const nextApps = apps.map((app) => (app.id === appId ? updatedApp : app));
await write(nextApps, "data", "apps.json");
}
-1
View File
@@ -52,7 +52,6 @@ export interface Application {
id: string; id: string;
name: string; name: string;
repo: string; repo: string;
nodeId: string;
createdAt: string; createdAt: string;
deployments: string[]; deployments: string[];
env: Record<string, EnvVariable>; env: Record<string, EnvVariable>;