Deploy to node
This commit is contained in:
@@ -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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
+11
-60
@@ -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(),
|
}
|
||||||
};
|
)
|
||||||
|
).data.applications;
|
||||||
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,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getApplications(): Promise<Application[]> {
|
export async function getApplications(): Promise<Application[]> {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user