Deploy to node

This commit is contained in:
2025-12-30 14:46:57 +01:00
parent fb05b455d5
commit a0e04f7fbe
3 changed files with 104 additions and 62 deletions
+28 -1
View File
@@ -1,7 +1,7 @@
import { NextRequest, NextResponse } from "next/server"; import { NextRequest, NextResponse } from "next/server";
import testAuth from "../auth"; import testAuth from "../auth";
import { StatusCodes } from "http-status-codes"; import { StatusCodes } from "http-status-codes";
import { getApplications } from "@/core/server/application"; import { deployNewApp, getApplications } from "@/core/server/application";
export async function GET(req: NextRequest): Promise<NextResponse> { export async function GET(req: NextRequest): Promise<NextResponse> {
const auth = await testAuth(req); const auth = await testAuth(req);
@@ -20,3 +20,30 @@ export async function GET(req: NextRequest): Promise<NextResponse> {
applications, applications,
}); });
} }
export async function POST(req: NextRequest): Promise<NextResponse> {
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 deployNewApp(name, repo, nodeId);
return NextResponse.json({
message: "ok",
...res,
});
}
+10 -59
View File
@@ -1,77 +1,28 @@
"use server"; "use server";
import { verifySession } from "./auth/session"; import { verifySession } from "./auth/session";
import { randomUUID } from "crypto";
import fs from "fs/promises";
import path from "path";
import { exec } from "child_process";
import util from "util";
import { getNodes } from "./node"; import { getNodes } from "./node";
import axios from "axios"; import axios from "axios";
import { Application } from "@/lib/types"; import { Application } from "@/lib/types";
const execAsync = util.promisify(exec);
const DATA_DIR = path.join(process.cwd(), "data", "apps");
export async function deployNewApp(name: string, repo: string, nodeId: string) { export async function deployNewApp(name: string, repo: string, nodeId: string) {
if (await verifySession()) { if (await verifySession()) {
throw new Error("Unauthorized"); throw new Error("Unauthorized");
} }
// Create app ID const nodes = await getNodes();
const appId = `app_${randomUUID()}`;
const appDir = path.join(DATA_DIR, appId);
const repoDir = path.join(appDir, "repo");
// Create directories const targetNode = nodes.find((node) => node.node_id === nodeId);
await fs.mkdir(repoDir, { recursive: true });
// Write app.json return await (
const appConfig = { await axios.post(
id: appId, `http://${targetNode?.ip}:3000/api/applications`,
name, { name, repo, nodeId },
repo, {
nodeId, headers: { Authorization: `ApiKey ${targetNode?.key}` },
createdAt: new Date().toISOString(),
};
await fs.writeFile(
path.join(appDir, "app.json"),
JSON.stringify(appConfig, null, 2)
);
// Clone repository
try {
await execAsync(`git clone ${repo} ${repoDir}`);
} catch (err) {
throw new Error("Failed to clone repository");
} }
)
// Validate Dockerfile ).data.applications;
const dockerfilePath = path.join(repoDir, "Dockerfile");
try {
await fs.access(dockerfilePath);
} catch {
throw new Error("Dockerfile not found in repository root");
}
const imageTag = `dockship/${appId}:latest`;
try {
await execAsync(`docker build -t ${imageTag} .`, {
cwd: repoDir,
});
} catch {
throw new Error("Docker build failed");
}
return {
success: true,
appId,
image: imageTag,
};
} }
export async function getApplications(): Promise<Application[]> { export async function getApplications(): Promise<Application[]> {
+65 -1
View File
@@ -1,9 +1,17 @@
"use server"; "use server";
import { Application } from "@/lib/types";
import Docker from "dockerode"; import Docker from "dockerode";
import { randomUUID } from "crypto";
import fs from "fs/promises";
import path from "path";
import { exec } from "child_process";
import util from "util";
import axios from "axios";
import { Application } from "@/lib/types";
const docker = new Docker(); const docker = new Docker();
const execAsync = util.promisify(exec);
const DATA_DIR = path.join(process.cwd(), "data", "apps");
export async function getApplications(): Promise<Application[]> { export async function getApplications(): Promise<Application[]> {
const images = await docker.listImages(); const images = await docker.listImages();
@@ -54,3 +62,59 @@ export async function getApplications(): Promise<Application[]> {
return apps; return apps;
} }
export async function deployNewApp(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
await fs.mkdir(repoDir, { recursive: true });
// Write app.json
const appConfig = {
id: appId,
name,
repo,
nodeId,
createdAt: new Date().toISOString(),
};
await fs.writeFile(
path.join(appDir, "app.json"),
JSON.stringify(appConfig, 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 {
await fs.access(dockerfilePath);
} catch {
throw new Error("Dockerfile not found in repository root");
}
const imageTag = `dockship/${appId}:latest`;
try {
await execAsync(`docker build -t ${imageTag} .`, {
cwd: repoDir,
});
} catch {
throw new Error("Docker build failed");
}
return {
success: true,
appId,
image: imageTag,
};
}