Shortened applictions for apps in code

This commit is contained in:
2026-01-03 19:51:39 +01:00
parent b39f32d006
commit 1a1dc90c01
7 changed files with 32 additions and 35 deletions
+3 -3
View File
@@ -13,13 +13,13 @@ export async function GET(req: NextRequest): Promise<NextResponse> {
); );
} }
// const apps = await getApplications(); // const apps = await getApps();
const applications = await getDeployments([]); const apps = await getDeployments([]);
return NextResponse.json({ return NextResponse.json({
message: "ok", message: "ok",
applications, apps,
}); });
} }
+7 -10
View File
@@ -13,32 +13,29 @@ 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 InstallApplicationDialog from "@/components/dialogs/InstallApplication";
import { getApplications, 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";
import { useAsyncInterval } from "@/hooks/use-async"; import { useAsyncInterval } from "@/hooks/use-async";
export default function ApplicationsPage() { export default function ApplicationsPage() {
const { value: applications, setValue: setApplications } = useAsyncInterval( const { value: apps, setValue: setApps } = useAsyncInterval(
[], [],
getApplications, getApps,
3000 3000
); );
const remove = async (appId: string) => { const remove = async (appId: string) => {
await removeApp(appId); await removeApp(appId);
setApplications((prev) => prev.filter((app) => app.id !== appId)); setApps((prev) => prev.filter((app) => app.id !== appId));
}; };
return ( return (
<DashboardLayout <DashboardLayout title="Applications" subtitle="Manage dockship applications">
title="Applications"
subtitle="Manage installed 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>{applications.length} apps</Badge> <Badge>{apps.length} apps</Badge>
</div> </div>
<InstallApplicationDialog> <InstallApplicationDialog>
<Button size="sm" className="gap-2 w-full sm:w-auto"> <Button size="sm" className="gap-2 w-full sm:w-auto">
@@ -47,7 +44,7 @@ export default function ApplicationsPage() {
</InstallApplicationDialog> </InstallApplicationDialog>
</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">
{applications.map((app) => ( {apps.map((app) => (
<Card key={app.id}> <Card key={app.id}>
<CardHeader> <CardHeader>
<CardTitle>{app.name}</CardTitle> <CardTitle>{app.name}</CardTitle>
+5 -5
View File
@@ -8,7 +8,7 @@ import { Button } from "@/components/ui/button";
import { Rocket, RotateCcw, Eye, MoreVertical, Plus } from "lucide-react"; import { Rocket, RotateCcw, Eye, MoreVertical, Plus } from "lucide-react";
import { Application, Deployment } from "@/lib/types"; import { Application, Deployment } from "@/lib/types";
import { getDeployments } from "@/lib/dockship/deploy"; import { getDeployments } from "@/lib/dockship/deploy";
import { getApplications } 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";
import { useAsync } from "@/hooks/use-async"; import { useAsync } from "@/hooks/use-async";
@@ -20,23 +20,23 @@ const statusDot = {
} as const; } as const;
export default function DeploymentsPage() { export default function DeploymentsPage() {
const { value: applications } = useAsync([], getApplications); const { value: apps } = useAsync([], getApps);
const { value: deployments, loading } = useAsync( const { value: deployments, loading } = useAsync(
[], [],
async () => (await getDeployments()).filter((d) => d !== null), async () => (await getDeployments()).filter((d) => d !== null),
[applications] [apps]
); );
const appNameByContainer = useMemo(() => { const appNameByContainer = useMemo(() => {
const map = new Map<string, string>(); const map = new Map<string, string>();
for (const app of applications) { for (const app of apps) {
for (const container of app.deployments) { for (const container of app.deployments) {
map.set(container, app.name); map.set(container, app.name);
} }
} }
return map; return map;
}, [applications]); }, [apps]);
return ( return (
<DashboardLayout <DashboardLayout
+8 -8
View File
@@ -26,7 +26,7 @@ import {
NodeLiveData, NodeLiveData,
} from "@/lib/types"; } from "@/lib/types";
import { getDocker } from "@/lib/dockship/docker"; import { getDocker } from "@/lib/dockship/docker";
import { getApplications } from "@/lib/dockship/application"; import { getApps } from "@/lib/dockship/application";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { getDeployments } from "@/lib/dockship/deploy"; import { getDeployments } from "@/lib/dockship/deploy";
import { useAsync, useAsyncInterval } from "@/hooks/use-async"; import { useAsync, useAsyncInterval } from "@/hooks/use-async";
@@ -36,7 +36,7 @@ export default function OverviewPage() {
const { value: dockerNodes } = useAsync([], async () => getDocker(nodes), [ const { value: dockerNodes } = useAsync([], async () => getDocker(nodes), [
nodes, nodes,
]); ]);
const { value: applications } = useAsyncInterval([], getApplications, 3000); const { value: apps } = useAsyncInterval([], getApps, 3000);
const { value: deployments } = useAsyncInterval( const { value: deployments } = useAsyncInterval(
[], [],
async () => (await getDeployments()).filter((d) => d !== null), async () => (await getDeployments()).filter((d) => d !== null),
@@ -45,13 +45,13 @@ export default function OverviewPage() {
const appNameByContainer = useMemo(() => { const appNameByContainer = useMemo(() => {
const map = new Map<string, string>(); const map = new Map<string, string>();
for (const app of applications) { for (const app of apps) {
for (const container of app.deployments) { for (const container of app.deployments) {
map.set(container, app.name); map.set(container, app.name);
} }
} }
return map; return map;
}, [applications]); }, [apps]);
return ( return (
<DashboardLayout title="Overview" subtitle="System status and metrics"> <DashboardLayout title="Overview" subtitle="System status and metrics">
@@ -59,9 +59,9 @@ export default function OverviewPage() {
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4 mb-6"> <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4 mb-6">
<StatCard <StatCard
title="Applications" title="Applications"
value={applications.length} value={apps.length}
subtitle={`${ subtitle={`${
applications.filter((app) => app.deployments.length > 0).length apps.filter((app) => app.deployments.length > 0).length
} Deployed`} } Deployed`}
icon={<Box className="w-4 h-4" />} icon={<Box className="w-4 h-4" />}
/> />
@@ -111,12 +111,12 @@ export default function OverviewPage() {
</div> </div>
{/* Rows */} {/* Rows */}
{applications.slice(-5).length === 0 ? ( {apps.slice(-5).length === 0 ? (
<div className="py-6 text-sm text-muted-foreground text-center"> <div className="py-6 text-sm text-muted-foreground text-center">
No applications yet No applications yet
</div> </div>
) : ( ) : (
applications.slice(-5).map((app) => ( apps.slice(-5).map((app) => (
<div <div
key={app.id} key={app.id}
className=" className="
+2 -2
View File
@@ -27,7 +27,7 @@ export default function DeployAppDialog({
children: ReactNode; children: ReactNode;
}) { }) {
const { value: nodes } = useAsync([], getLiveNodes); const { value: nodes } = useAsync([], getLiveNodes);
const { value: applications } = useAsync<Application[]>([], async () => []); const { value: apps } = useAsync<Application[]>([], async () => []);
const [name, setName] = useState(""); const [name, setName] = useState("");
const [app, setApp] = useState(defaultApp || ""); const [app, setApp] = useState(defaultApp || "");
const [node, setNode] = useState(""); const [node, setNode] = useState("");
@@ -66,7 +66,7 @@ export default function DeployAppDialog({
<Combobox <Combobox
value={app} value={app}
setValue={setApp} setValue={setApp}
data={applications.map((app) => ({ data={apps.map((app) => ({
label: app.name, label: app.name,
value: app.id, value: app.id,
}))} }))}
+6 -6
View File
@@ -23,10 +23,10 @@ export async function installApp(name: string, repo: string, nodeId: string) {
headers: { Authorization: `ApiKey ${targetNode?.key}` }, headers: { Authorization: `ApiKey ${targetNode?.key}` },
} }
) )
).data.applications; ).data.apps;
} }
export async function getApplications(): Promise<Application[]> { export async function getApps(): Promise<Application[]> {
if (await verifySession()) { if (await verifySession()) {
throw new Error("Unauthorized"); throw new Error("Unauthorized");
} }
@@ -35,7 +35,7 @@ export async function getApplications(): Promise<Application[]> {
} }
export async function getEnv(): Promise<Record<string, Env>> { export async function getEnv(): Promise<Record<string, Env>> {
const apps = await getApplications(); const apps = await getApps();
return apps.reduce<Record<string, Env>>((acc, app) => { return apps.reduce<Record<string, Env>>((acc, app) => {
acc[app.id] = { acc[app.id] = {
@@ -56,14 +56,14 @@ export async function setEnv(
throw new Error("Unauthorized"); throw new Error("Unauthorized");
} }
const apps = await getApplications(); const apps = await getApps();
const app = apps.find((app) => app.id === appId); const app = apps.find((app) => app.id === appId);
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) { export async function removeApp(appId: string) {
const apps = await getApplications(); const apps = await getApps();
const nextApps = apps.filter((app) => app.id !== appId); const nextApps = apps.filter((app) => app.id !== appId);
@@ -71,7 +71,7 @@ export async function removeApp(appId: string) {
} }
export async function editApp(appId: string, updatedApp: Application) { export async function editApp(appId: string, updatedApp: Application) {
const apps = await getApplications(); const apps = await getApps();
const nextApps = apps.map((app) => (app.id === appId ? updatedApp : app)); const nextApps = apps.map((app) => (app.id === appId ? updatedApp : app));
+1 -1
View File
@@ -15,7 +15,7 @@ export async function getDeployments(): Promise<(null | Deployment)[]> {
await axios.get(`http://${n.ip}:3000/api/deploy`, { await axios.get(`http://${n.ip}:3000/api/deploy`, {
headers: { Authorization: `ApiKey ${n.key}` }, headers: { Authorization: `ApiKey ${n.key}` },
}) })
).data.applications ).data.apps
) )
) )
).flat(); ).flat();