diff --git a/src/app/api/applications/route.ts b/src/app/api/applications/route.ts deleted file mode 100644 index 6dab73d..0000000 --- a/src/app/api/applications/route.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; -import testAuth from "../auth"; -import { StatusCodes } from "http-status-codes"; -import { installApp, getApplications, removeApp } from "@/core/application"; - -export async function GET(req: NextRequest): Promise { - const auth = await testAuth(req); - - if (auth) { - return NextResponse.json( - { message: auth }, - { status: StatusCodes.UNAUTHORIZED } - ); - } - - const applications = await getApplications(); - - return NextResponse.json({ - message: "ok", - applications, - }); -} - -export async function POST(req: NextRequest): Promise { - const auth = await testAuth(req); - const { name, repo, nodeId }: { name: string; repo: string; nodeId: string } = - await req.json(); - - if (auth) { - return NextResponse.json( - { message: auth }, - { status: StatusCodes.UNAUTHORIZED } - ); - } - - if (!name || !repo || !nodeId) { - return NextResponse.json( - { message: "Body missing name, repo, nodeId" }, - { status: StatusCodes.BAD_REQUEST } - ); - } - - const res = await installApp(name, repo, nodeId); - - return NextResponse.json({ - message: "ok", - ...res, - }); -} - -export async function DELETE(req: NextRequest): Promise { - 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", - }); -} diff --git a/src/app/api/deploy/route.ts b/src/app/api/deploy/route.ts index 6898a0c..2478a8b 100644 --- a/src/app/api/deploy/route.ts +++ b/src/app/api/deploy/route.ts @@ -2,7 +2,6 @@ import { NextRequest, NextResponse } from "next/server"; import testAuth from "../auth"; import { StatusCodes } from "http-status-codes"; import { deployApp, getDeployments } from "@/core/deployment"; -import { getApplications } from "@/core/application"; export async function GET(req: NextRequest): Promise { const auth = await testAuth(req); @@ -14,9 +13,9 @@ export async function GET(req: NextRequest): Promise { ); } - const apps = await getApplications(); + // const apps = await getApplications(); - const applications = await getDeployments(apps); + const applications = await getDeployments([]); return NextResponse.json({ message: "ok", diff --git a/src/app/api/env/route.ts b/src/app/api/env/route.ts deleted file mode 100644 index bff743a..0000000 --- a/src/app/api/env/route.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; -import testAuth from "../auth"; -import { StatusCodes } from "http-status-codes"; -import { getEnv, setEnv } from "@/core/application"; - -export async function GET(req: NextRequest): Promise { - const auth = await testAuth(req); - - if (auth) { - return NextResponse.json( - { message: auth }, - { status: StatusCodes.UNAUTHORIZED } - ); - } - - return NextResponse.json(await getEnv()); -} - -export async function POST(req: NextRequest): Promise { - const auth = await testAuth(req); - - if (auth) { - return NextResponse.json( - { message: auth }, - { status: StatusCodes.UNAUTHORIZED } - ); - } - - const { appId, variables } = await req.json(); - - if (!appId || !variables) { - return NextResponse.json( - { message: "Missing appId, variables in body" }, - { status: StatusCodes.BAD_REQUEST } - ); - } - - await setEnv(appId, variables); - - return NextResponse.json({ message: "ok", success: true }); -} diff --git a/src/app/applications/page.tsx b/src/app/applications/page.tsx deleted file mode 100644 index 2b71156..0000000 --- a/src/app/applications/page.tsx +++ /dev/null @@ -1,87 +0,0 @@ -"use client"; - -import { DashboardLayout } from "@/components/dashboard/DashboardLayout"; -import { - Card, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from "@/components/ui/card"; -import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; -import { MoreVertical, Plus, Rocket } from "lucide-react"; -import InstallApplicationDialog from "@/components/dialogs/InstallApplication"; -import { getApplications, removeApp } from "@/lib/dockship/application"; -import RemoveDialog from "@/components/dialogs/Remove"; -import DeployAppDialog from "@/components/dialogs/DeployApp"; -import { useAsyncInterval } from "@/hooks/use-async"; - -export default function ApplicationsPage() { - const { value: applications, setValue: setApplications } = useAsyncInterval( - [], - getApplications, - 3000 - ); - - const remove = async (appId: string) => { - await removeApp(appId); - setApplications((prev) => prev.filter((app) => app.id !== appId)); - }; - - return ( - - {/* Header */} -
-
- {applications.length} apps -
- - - -
-
- {applications.map((app) => ( - - - {app.name} - {app.repo} - - - - {/* Deployments */} -
- - Deployments - - - {app.deployments?.length ?? 0} - -
-
- - - - - - - remove(app.id)} /> - - - -
- ))} -
-
- ); -} diff --git a/src/components/dialogs/DeployApp.tsx b/src/components/dialogs/DeployApp.tsx index 29bc7fe..05f67ed 100644 --- a/src/components/dialogs/DeployApp.tsx +++ b/src/components/dialogs/DeployApp.tsx @@ -15,9 +15,9 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { getLiveNodes } from "@/lib/dockship/node"; import { Combobox } from "../ui/combobox"; -import { getApplications } from "@/core/application"; import { deployApp } from "@/lib/dockship/deploy"; import { useAsync } from "@/hooks/use-async"; +import { Application } from "@/lib/types"; export default function DeployAppDialog({ defaultApp, @@ -27,7 +27,7 @@ export default function DeployAppDialog({ children: ReactNode; }) { const { value: nodes } = useAsync([], getLiveNodes); - const { value: applications } = useAsync([], getApplications); + const { value: applications } = useAsync([], async () => []); const [name, setName] = useState(""); const [app, setApp] = useState(defaultApp || ""); const [node, setNode] = useState(""); diff --git a/src/core/application.ts b/src/core/application.ts deleted file mode 100644 index 6d5b58b..0000000 --- a/src/core/application.ts +++ /dev/null @@ -1,128 +0,0 @@ -"use server"; - -import Docker from "dockerode"; -import { randomUUID } from "crypto"; -import fs from "fs"; -import path from "path"; -import { exec } from "child_process"; -import util from "util"; -import { Application, Env, EnvVariable } from "@/lib/types"; - -const docker = new Docker(); -const execAsync = util.promisify(exec); -const DATA_DIR = path.join(process.cwd(), "data", "apps"); - -export async function getApplications(): Promise { - return fs - .readdirSync(DATA_DIR) - .map((appId) => - JSON.parse( - fs.readFileSync(path.join(DATA_DIR, appId, "app.json")).toString() - ) - ); -} - -export async function installApp(name: string, repo: string, nodeId: string) { - // Create app ID - const appId = `app_${randomUUID()}`; - const appDir = path.join(DATA_DIR, appId); - const repoDir = path.join(appDir, "repo"); - - // Create directories - fs.mkdirSync(repoDir, { recursive: true }); - - // Write app.json - const appConfig: Application = { - id: appId, - name, - repo, - nodeId, - createdAt: new Date().toISOString(), - deployments: [], - }; - - fs.writeFileSync( - path.join(appDir, "app.json"), - JSON.stringify(appConfig, null, 2) - ); - - // Write env.json - fs.writeFileSync(path.join(appDir, "env.json"), JSON.stringify([], null, 2)); - - // Clone repository - try { - await execAsync(`git clone ${repo} ${repoDir}`); - } catch (err) { - throw new Error("Failed to clone repository"); - } - - // Validate Dockerfile - const dockerfilePath = path.join(repoDir, "Dockerfile"); - - try { - fs.accessSync(dockerfilePath); - } catch { - fs.rmSync(appDir, { recursive: true }); - throw new Error("Dockerfile not found in repository root"); - } - - return { - success: true, - appId, - }; -} - -export async function getEnv(): Promise> { - const apps = fs.readdirSync(DATA_DIR); - - return apps.reduce((acc, appId) => { - const appPath = path.join(DATA_DIR, appId, "app.json"); - const name = JSON.parse(fs.readFileSync(appPath).toString()).name; - - const envPath = path.join(DATA_DIR, appId, "env.json"); - if (fs.existsSync(envPath)) { - const envData: Record = JSON.parse( - fs.readFileSync(envPath, "utf-8") - ); - acc[appId] = { variables: envData, id: appId, name }; - } - return acc; - }, {} as Record); -} - -export async function setEnv(appId: string, variables: EnvVariable[]) { - const apps = fs.readdirSync(DATA_DIR); - - apps.forEach((id) => { - if (id === appId) { - const envPath = path.join(DATA_DIR, appId, "env.json"); - - fs.writeFileSync(envPath, JSON.stringify(variables)); - } - }); -} - -export async function removeApp(appId: string) { - const appPath = path.join(DATA_DIR, appId); - - const appMeta: Application = JSON.parse( - fs.readFileSync(path.join(appPath, "app.json")).toString() - ); - - appMeta.deployments.forEach(async (imgId) => { - // Remove containers - (await docker.listContainers({ all: true })) - .filter((con) => con.ImageID === imgId) - .forEach((con) => { - const container = docker.getContainer(con.Id); - container.stop(); - container.remove(); - }); - - // Remove image - const image = docker.getImage(imgId); - image.remove(); - }); - - fs.rmSync(appPath, { recursive: true }); -}