Compare commits
10
Commits
32fd259ae2
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8cf75ce1d5 | ||
|
|
f700ccb855 | ||
|
|
e4060d0fba | ||
|
|
d4b9b8c75b | ||
|
|
52fc6c8831 | ||
|
|
efcbc58272 | ||
|
|
6fb54da4bf | ||
|
|
1331e36177 | ||
|
|
a7e29867b8 | ||
|
|
ed9d76798e |
+5
-2
@@ -26,6 +26,11 @@ else
|
|||||||
echo "DOCKSHIP_LITE=false" > .env
|
echo "DOCKSHIP_LITE=false" > .env
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Prompt for port
|
||||||
|
read -p "Enter the port DockShip should run on [3000]: " PORT
|
||||||
|
PORT=${PORT:-3000}
|
||||||
|
echo "PORT=$PORT" >> .env
|
||||||
|
|
||||||
# Prompt for ORIGIN type
|
# Prompt for ORIGIN type
|
||||||
echo ""
|
echo ""
|
||||||
echo "Choose the origin IP for DockShip:"
|
echo "Choose the origin IP for DockShip:"
|
||||||
@@ -33,8 +38,6 @@ echo "1) Local IP (Recommended)"
|
|||||||
echo "2) Public IP (Unsafe)"
|
echo "2) Public IP (Unsafe)"
|
||||||
read -p "Enter choice [1/2]: " ip_choice
|
read -p "Enter choice [1/2]: " ip_choice
|
||||||
|
|
||||||
PORT=3000 # Default port
|
|
||||||
|
|
||||||
if [[ "$ip_choice" == "1" ]]; then
|
if [[ "$ip_choice" == "1" ]]; then
|
||||||
# Get local IP (first non-loopback IP)
|
# Get local IP (first non-loopback IP)
|
||||||
LOCAL_IP=$(hostname -I | awk '{print $1}')
|
LOCAL_IP=$(hostname -I | awk '{print $1}')
|
||||||
|
|||||||
@@ -33,9 +33,7 @@ export async function GET(request: NextRequest) {
|
|||||||
|
|
||||||
const c = await cookies();
|
const c = await cookies();
|
||||||
c.set("github_access", accessToken, {
|
c.set("github_access", accessToken, {
|
||||||
httpOnly: true,
|
|
||||||
path: "/",
|
path: "/",
|
||||||
secure: process.env.NODE_ENV === "production",
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return NextResponse.redirect(`${process.env.ORIGIN}/overview`);
|
return NextResponse.redirect(`${process.env.ORIGIN}/overview`);
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
import { version } from "@/../package.json";
|
import { version } from "@/../package.json";
|
||||||
import testAuth from "../auth";
|
import testAuth from "../../../lib/server/auth";
|
||||||
import { StatusCodes } from "http-status-codes";
|
import { StatusCodes } from "http-status-codes";
|
||||||
import { read } from "@/app/api/file";
|
import { read } from "@/lib/server/file";
|
||||||
import { NodeJsonTemplate } from "../types";
|
import { NodeJsonTemplate } from "../../../lib/server/types";
|
||||||
import osName from "os-name";
|
import osName from "os-name";
|
||||||
|
|
||||||
export async function GET(req: NextRequest): Promise<NextResponse> {
|
export async function GET(req: NextRequest): Promise<NextResponse> {
|
||||||
@@ -12,7 +12,7 @@ export async function GET(req: NextRequest): Promise<NextResponse> {
|
|||||||
if (auth) {
|
if (auth) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ message: auth },
|
{ message: auth },
|
||||||
{ status: StatusCodes.UNAUTHORIZED }
|
{ status: StatusCodes.UNAUTHORIZED },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
import testAuth from "../auth";
|
import testAuth from "../../../lib/server/auth";
|
||||||
import { StatusCodes } from "http-status-codes";
|
import { StatusCodes } from "http-status-codes";
|
||||||
import { getDocker, removeContainer } from "@/core/docker";
|
import { getDocker, removeContainer } from "@/core/docker";
|
||||||
import { Docker } from "@/lib/types";
|
import { Docker } from "@/lib/types";
|
||||||
@@ -10,7 +10,7 @@ export async function GET(req: NextRequest): Promise<NextResponse> {
|
|||||||
if (auth) {
|
if (auth) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ message: auth },
|
{ message: auth },
|
||||||
{ status: StatusCodes.UNAUTHORIZED }
|
{ status: StatusCodes.UNAUTHORIZED },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,14 +30,14 @@ export async function DELETE(req: NextRequest): Promise<NextResponse> {
|
|||||||
if (!id) {
|
if (!id) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ message: "Invalid containerId param" },
|
{ message: "Invalid containerId param" },
|
||||||
{ status: StatusCodes.BAD_REQUEST }
|
{ status: StatusCodes.BAD_REQUEST },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auth) {
|
if (auth) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ message: auth },
|
{ message: auth },
|
||||||
{ status: StatusCodes.UNAUTHORIZED }
|
{ status: StatusCodes.UNAUTHORIZED },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
import testAuth from "../../auth";
|
import testAuth from "../../../../lib/server/auth";
|
||||||
import { StatusCodes } from "http-status-codes";
|
import { StatusCodes } from "http-status-codes";
|
||||||
import { startContainer, stopContainer } from "@/core/docker";
|
import { startContainer, stopContainer } from "@/core/docker";
|
||||||
|
|
||||||
@@ -10,14 +10,14 @@ export async function POST(req: NextRequest): Promise<NextResponse> {
|
|||||||
if (!id) {
|
if (!id) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ message: "Invalid containerId param" },
|
{ message: "Invalid containerId param" },
|
||||||
{ status: StatusCodes.BAD_REQUEST }
|
{ status: StatusCodes.BAD_REQUEST },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auth) {
|
if (auth) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ message: auth },
|
{ message: auth },
|
||||||
{ status: StatusCodes.UNAUTHORIZED }
|
{ status: StatusCodes.UNAUTHORIZED },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,14 +35,14 @@ export async function DELETE(req: NextRequest): Promise<NextResponse> {
|
|||||||
if (!id) {
|
if (!id) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ message: "Invalid containerId param" },
|
{ message: "Invalid containerId param" },
|
||||||
{ status: StatusCodes.BAD_REQUEST }
|
{ status: StatusCodes.BAD_REQUEST },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auth) {
|
if (auth) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ message: auth },
|
{ message: auth },
|
||||||
{ status: StatusCodes.UNAUTHORIZED }
|
{ status: StatusCodes.UNAUTHORIZED },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
import testAuth from "../../auth";
|
import testAuth from "../../../../lib/server/auth";
|
||||||
import { StatusCodes } from "http-status-codes";
|
import { StatusCodes } from "http-status-codes";
|
||||||
import { getContainerStats } from "@/core/docker";
|
import { getContainerStats } from "@/core/docker";
|
||||||
|
|
||||||
@@ -10,14 +10,14 @@ export async function GET(req: NextRequest): Promise<NextResponse> {
|
|||||||
if (!id) {
|
if (!id) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ message: "Invalid containerId param" },
|
{ message: "Invalid containerId param" },
|
||||||
{ status: StatusCodes.BAD_REQUEST }
|
{ status: StatusCodes.BAD_REQUEST },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auth) {
|
if (auth) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ message: auth },
|
{ message: auth },
|
||||||
{ status: StatusCodes.UNAUTHORIZED }
|
{ status: StatusCodes.UNAUTHORIZED },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
import testAuth from "../auth";
|
import testAuth from "../../../lib/server/auth";
|
||||||
import { StatusCodes } from "http-status-codes";
|
import { StatusCodes } from "http-status-codes";
|
||||||
import { deployApp, toggleDeployment } from "@/core/deployment";
|
import {
|
||||||
|
deployApp,
|
||||||
|
removeDeployment,
|
||||||
|
toggleDeployment,
|
||||||
|
} from "@/core/deployment";
|
||||||
import { Application, Port } from "@/lib/types";
|
import { Application, Port } from "@/lib/types";
|
||||||
|
|
||||||
export async function POST(req: NextRequest): Promise<NextResponse> {
|
export async function POST(req: NextRequest): Promise<NextResponse> {
|
||||||
@@ -59,3 +63,28 @@ export async function PUT(req: NextRequest): Promise<NextResponse> {
|
|||||||
message: "ok",
|
message: "ok",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function DELETE(req: NextRequest): Promise<NextResponse> {
|
||||||
|
const deployId = req.nextUrl.searchParams.get("deployId");
|
||||||
|
const auth = await testAuth(req);
|
||||||
|
|
||||||
|
if (auth) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ message: auth },
|
||||||
|
{ status: StatusCodes.UNAUTHORIZED },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!deployId) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ message: "Body missing deployId" },
|
||||||
|
{ status: StatusCodes.BAD_REQUEST },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await removeDeployment(deployId);
|
||||||
|
|
||||||
|
return NextResponse.json({
|
||||||
|
message: "ok",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
import testAuth from "../auth";
|
import testAuth from "../../../lib/server/auth";
|
||||||
import { StatusCodes } from "http-status-codes";
|
import { StatusCodes } from "http-status-codes";
|
||||||
import { getDeployments } from "@/core/deployment";
|
import { getDeployments } from "@/core/deployment";
|
||||||
|
|
||||||
@@ -10,14 +10,14 @@ export async function POST(req: NextRequest): Promise<NextResponse> {
|
|||||||
if (auth) {
|
if (auth) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ message: auth },
|
{ message: auth },
|
||||||
{ status: StatusCodes.UNAUTHORIZED }
|
{ status: StatusCodes.UNAUTHORIZED },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!apps)
|
if (!apps)
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ message: "Body should be Application[]" },
|
{ message: "Body should be Application[]" },
|
||||||
{ status: StatusCodes.UNAUTHORIZED }
|
{ status: StatusCodes.UNAUTHORIZED },
|
||||||
);
|
);
|
||||||
|
|
||||||
const deployments = await getDeployments(apps);
|
const deployments = await getDeployments(apps);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
import testAuth from "../auth";
|
import testAuth from "../../../lib/server/auth";
|
||||||
import { StatusCodes } from "http-status-codes";
|
import { StatusCodes } from "http-status-codes";
|
||||||
import { getAllContainerLogs } from "@/core/docker";
|
import { getAllContainerLogs } from "@/core/docker";
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ export async function GET(req: NextRequest): Promise<NextResponse> {
|
|||||||
if (auth) {
|
if (auth) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ message: auth },
|
{ message: auth },
|
||||||
{ status: StatusCodes.UNAUTHORIZED }
|
{ status: StatusCodes.UNAUTHORIZED },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
import testAuth from "../auth";
|
import testAuth from "../../../lib/server/auth";
|
||||||
import { StatusCodes } from "http-status-codes";
|
import { StatusCodes } from "http-status-codes";
|
||||||
import si from "systeminformation";
|
import si from "systeminformation";
|
||||||
import { average } from "@/lib/format";
|
import { average } from "@/lib/format";
|
||||||
@@ -93,7 +93,7 @@ export async function GET(req: NextRequest): Promise<NextResponse> {
|
|||||||
if (auth) {
|
if (auth) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ message: auth },
|
{ message: auth },
|
||||||
{ status: StatusCodes.UNAUTHORIZED }
|
{ status: StatusCodes.UNAUTHORIZED },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
import testAuth from "../auth";
|
import testAuth from "../../../lib/server/auth";
|
||||||
import { StatusCodes } from "http-status-codes";
|
import { StatusCodes } from "http-status-codes";
|
||||||
import si from "systeminformation";
|
import si from "systeminformation";
|
||||||
import { NodeLiveData } from "@/lib/types";
|
import { NodeLiveData } from "@/lib/types";
|
||||||
@@ -10,7 +10,7 @@ export async function GET(req: NextRequest): Promise<NextResponse> {
|
|||||||
if (auth) {
|
if (auth) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ message: auth },
|
{ message: auth },
|
||||||
{ status: StatusCodes.UNAUTHORIZED }
|
{ status: StatusCodes.UNAUTHORIZED },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export default function ApplicationsPage() {
|
|||||||
</RegisterAppDialog>
|
</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.toReversed().map((app) => (
|
||||||
<Card key={app.id}>
|
<Card key={app.id}>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>{app.name}</CardTitle>
|
<CardTitle>{app.name}</CardTitle>
|
||||||
|
|||||||
@@ -11,7 +11,11 @@ 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 { Plus, Play, Pause, Square, RotateCcw } from "lucide-react";
|
import { Plus, Play, Pause, Square, RotateCcw } from "lucide-react";
|
||||||
import { getDeployments, toggleDeployment } from "@/lib/dockship/deploy";
|
import {
|
||||||
|
getDeployments,
|
||||||
|
removeDeployment,
|
||||||
|
toggleDeployment,
|
||||||
|
} from "@/lib/dockship/deploy";
|
||||||
import { getApps } 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";
|
||||||
@@ -53,6 +57,11 @@ export default function DeploymentsPage() {
|
|||||||
await toggleDeployment(deployId, action);
|
await toggleDeployment(deployId, action);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const remove = async (deployId: string) => {
|
||||||
|
console.log(`Removing deployment ${deployId}`);
|
||||||
|
await removeDeployment(deployId);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardLayout
|
<DashboardLayout
|
||||||
title="Deployments"
|
title="Deployments"
|
||||||
@@ -74,7 +83,7 @@ export default function DeploymentsPage() {
|
|||||||
</DeployAppDialog>
|
</DeployAppDialog>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="divide-y divide-border flex flex-col gap-5">
|
<div className="divide-y divide-border flex flex-col-reverse gap-5">
|
||||||
{deployments.map((dep) => {
|
{deployments.map((dep) => {
|
||||||
return (
|
return (
|
||||||
<Card key={`${dep.image}-${dep.container ?? "latest"}`}>
|
<Card key={`${dep.image}-${dep.container ?? "latest"}`}>
|
||||||
@@ -138,7 +147,7 @@ export default function DeploymentsPage() {
|
|||||||
<RotateCcw className="w-3 h-3" />
|
<RotateCcw className="w-3 h-3" />
|
||||||
</Button>
|
</Button>
|
||||||
</DeployAppDialog>
|
</DeployAppDialog>
|
||||||
<RemoveDialog remove={() => {}} />
|
<RemoveDialog remove={() => remove(dep.container)} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export default function MonitoringPage() {
|
|||||||
|
|
||||||
// Live nodes polling every 3 seconds
|
// Live nodes polling every 3 seconds
|
||||||
const { value: liveData } = useAsyncInterval(
|
const { value: liveData } = useAsyncInterval(
|
||||||
"liveNodes",
|
"liveNodeUsage",
|
||||||
{ cpuUsage: 0, ramUsage: 0 },
|
{ cpuUsage: 0, ramUsage: 0 },
|
||||||
async () => {
|
async () => {
|
||||||
const nodes = await getLiveNodes();
|
const nodes = await getLiveNodes();
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ export default function OverviewPage() {
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="overflow-x-auto">
|
<CardContent className="overflow-x-auto">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 min-w-75">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 min-w-75">
|
||||||
{nodes.map((node, i) => (
|
{nodes.toReversed().map((node, i) => (
|
||||||
<div key={i} className="border border-border p-4 bg-secondary/30">
|
<div key={i} className="border border-border p-4 bg-secondary/30">
|
||||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between mb-3 gap-2">
|
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between mb-3 gap-2">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
"use server";
|
"use server";
|
||||||
import { read, write } from "@/app/api/file";
|
import testAuth from "@/lib/server/auth";
|
||||||
|
import { read, write } from "@/lib/server/file";
|
||||||
|
import { NodeJsonTemplate } from "@/lib/server/types";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { createHash } from "crypto";
|
import { createHash } from "crypto";
|
||||||
import { cookies } from "next/headers";
|
import { cookies } from "next/headers";
|
||||||
@@ -20,7 +22,7 @@ export async function getSession(): Promise<
|
|||||||
const tokens: Record<string, string> = await read(
|
const tokens: Record<string, string> = await read(
|
||||||
() => ({}),
|
() => ({}),
|
||||||
"data",
|
"data",
|
||||||
"sessions.json"
|
"sessions.json",
|
||||||
);
|
);
|
||||||
|
|
||||||
const tokenHash = createHash("sha256").update(accessToken).digest("hex");
|
const tokenHash = createHash("sha256").update(accessToken).digest("hex");
|
||||||
@@ -36,7 +38,7 @@ export async function getSession(): Promise<
|
|||||||
|
|
||||||
// Test the token
|
// Test the token
|
||||||
const users = Object.keys(
|
const users = Object.keys(
|
||||||
await read<Users>(() => ({}), "data", "users.json")
|
await read<Users>(() => ({}), "data", "users.json"),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!users.includes(String(userId))) {
|
if (!users.includes(String(userId))) {
|
||||||
@@ -44,8 +46,10 @@ export async function getSession(): Promise<
|
|||||||
await write(
|
await write(
|
||||||
{ [userId]: { roles: ["admin"] } } as Users, // First user is always an admin
|
{ [userId]: { roles: ["admin"] } } as Users, // First user is always an admin
|
||||||
"data",
|
"data",
|
||||||
"users.json"
|
"users.json",
|
||||||
);
|
);
|
||||||
|
const { key } = await read(NodeJsonTemplate, "data", "node.json");
|
||||||
|
console.log(`Node key: ${key}\ncwd: ${process.cwd()}`);
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Invalid user");
|
throw new Error("Invalid user");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import pLimit from "p-limit";
|
|||||||
import { randomUUID } from "crypto";
|
import { randomUUID } from "crypto";
|
||||||
import { cloneRepo } from "./git";
|
import { cloneRepo } from "./git";
|
||||||
import tar from "tar-fs";
|
import tar from "tar-fs";
|
||||||
|
import { read, write } from "@/lib/server/file";
|
||||||
|
|
||||||
const docker = new Docker();
|
const docker = new Docker();
|
||||||
|
|
||||||
@@ -180,3 +181,21 @@ export async function toggleDeployment(
|
|||||||
console.error(`Failed to ${action} container ${containerId}:`, err);
|
console.error(`Failed to ${action} container ${containerId}:`, err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function removeDeployment(deployId: string) {
|
||||||
|
const apps = await read(() => [] as Application[], "data", "apps.json");
|
||||||
|
const app = apps.find((a) => a.deployments.includes(deployId));
|
||||||
|
if (app) {
|
||||||
|
app.deployments = app.deployments.filter((id) => id !== deployId);
|
||||||
|
await write(apps, "data", "apps.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const container = docker.getContainer(deployId);
|
||||||
|
await container.stop();
|
||||||
|
await container.remove({ force: true });
|
||||||
|
await docker.pruneImages();
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Failed to remove container ${deployId}:`, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { verifySession } from "../../core/auth/session";
|
import { verifySession } from "../../core/auth/session";
|
||||||
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 "@/lib/server/file";
|
||||||
import { randomUUID } from "crypto";
|
import { randomUUID } from "crypto";
|
||||||
|
|
||||||
export async function getApps(): Promise<Application[]> {
|
export async function getApps(): Promise<Application[]> {
|
||||||
@@ -19,7 +19,7 @@ export async function getApp(appId: string): Promise<Application | undefined> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (await read<Application[]>(() => [], "data", "apps.json")).find(
|
return (await read<Application[]>(() => [], "data", "apps.json")).find(
|
||||||
(app) => app.id === appId
|
(app) => app.id === appId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ export async function getEnv(): Promise<Record<string, Env>> {
|
|||||||
|
|
||||||
export async function setEnv(
|
export async function setEnv(
|
||||||
appId: string,
|
appId: string,
|
||||||
variables: Record<string, EnvVariable>
|
variables: Record<string, EnvVariable>,
|
||||||
) {
|
) {
|
||||||
if (await verifySession()) {
|
if (await verifySession()) {
|
||||||
throw new Error("Unauthorized");
|
throw new Error("Unauthorized");
|
||||||
|
|||||||
@@ -16,9 +16,13 @@ export async function getDeployments(): Promise<
|
|||||||
nodes.map(
|
nodes.map(
|
||||||
async (n) =>
|
async (n) =>
|
||||||
await (
|
await (
|
||||||
await axios.post(`http://${n.ip}:3000/api/get-deployments`, apps, {
|
await axios.post(
|
||||||
headers: { Authorization: `ApiKey ${n.key}` },
|
`http://${n.ip}:${process.env.PORT}/api/get-deployments`,
|
||||||
})
|
apps,
|
||||||
|
{
|
||||||
|
headers: { Authorization: `ApiKey ${n.key}` },
|
||||||
|
},
|
||||||
|
)
|
||||||
).data.deployments.map((d: any) => ({ ...d, nodeId: n.node_id })),
|
).data.deployments.map((d: any) => ({ ...d, nodeId: n.node_id })),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -42,7 +46,7 @@ export async function deployApp(
|
|||||||
|
|
||||||
const deployId = await (
|
const deployId = await (
|
||||||
await axios.post(
|
await axios.post(
|
||||||
`http://${node.ip}:3000/api/deploy`,
|
`http://${node.ip}:${process.env.PORT}/api/deploy`,
|
||||||
{ name, app, ports },
|
{ name, app, ports },
|
||||||
{
|
{
|
||||||
headers: { Authorization: `ApiKey ${node.key}` },
|
headers: { Authorization: `ApiKey ${node.key}` },
|
||||||
@@ -67,7 +71,7 @@ export async function toggleDeployment(
|
|||||||
nodes.map(
|
nodes.map(
|
||||||
async (node) =>
|
async (node) =>
|
||||||
await axios.put(
|
await axios.put(
|
||||||
`http://${node.ip}:3000/api/deploy`,
|
`http://${node.ip}:${process.env.PORT}/api/deploy`,
|
||||||
{ deployId, action },
|
{ deployId, action },
|
||||||
{
|
{
|
||||||
headers: { Authorization: `ApiKey ${node.key}` },
|
headers: { Authorization: `ApiKey ${node.key}` },
|
||||||
@@ -78,3 +82,21 @@ export async function toggleDeployment(
|
|||||||
|
|
||||||
return "ok";
|
return "ok";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function removeDeployment(deployId: string) {
|
||||||
|
const nodes = await getNodes();
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
nodes.map(
|
||||||
|
async (node) =>
|
||||||
|
await axios.delete(
|
||||||
|
`http://${node.ip}:${process.env.PORT}/api/deploy?deployId=${deployId}`,
|
||||||
|
{
|
||||||
|
headers: { Authorization: `ApiKey ${node.key}` },
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return "ok";
|
||||||
|
}
|
||||||
|
|||||||
+34
-25
@@ -11,18 +11,21 @@ export async function getDocker(nodes: Node[]): Promise<Docker[]> {
|
|||||||
return await Promise.all(
|
return await Promise.all(
|
||||||
nodes.map(async (node) => {
|
nodes.map(async (node) => {
|
||||||
const data = await (
|
const data = await (
|
||||||
await axios.get(`http://${node.ip}:3000/api/container/`, {
|
await axios.get(
|
||||||
headers: { Authorization: `ApiKey ${node.key}` },
|
`http://${node.ip}:${process.env.PORT}/api/container/`,
|
||||||
})
|
{
|
||||||
|
headers: { Authorization: `ApiKey ${node.key}` },
|
||||||
|
},
|
||||||
|
)
|
||||||
).data;
|
).data;
|
||||||
|
|
||||||
return { ...node, containers: data.containers, version: data.version };
|
return { ...node, containers: data.containers, version: data.version };
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getContainerStats(
|
export async function getContainerStats(
|
||||||
containerId: string
|
containerId: string,
|
||||||
): Promise<SimpleStats | undefined> {
|
): Promise<SimpleStats | undefined> {
|
||||||
const nodes = await getNodes();
|
const nodes = await getNodes();
|
||||||
|
|
||||||
@@ -31,12 +34,15 @@ export async function getContainerStats(
|
|||||||
nodes.map(
|
nodes.map(
|
||||||
async (n) =>
|
async (n) =>
|
||||||
await (
|
await (
|
||||||
await axios.get(`http://${n.ip}:3000/api/container/stats`, {
|
await axios.get(
|
||||||
headers: { Authorization: `ApiKey ${n.key}` },
|
`http://${n.ip}:${process.env.PORT}/api/container/stats`,
|
||||||
params: { containerId },
|
{
|
||||||
})
|
headers: { Authorization: `ApiKey ${n.key}` },
|
||||||
).data?.stats
|
params: { containerId },
|
||||||
)
|
},
|
||||||
|
)
|
||||||
|
).data?.stats,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
).find((s) => s);
|
).find((s) => s);
|
||||||
}
|
}
|
||||||
@@ -48,14 +54,14 @@ export async function startContainer(containerId: string) {
|
|||||||
async (n) =>
|
async (n) =>
|
||||||
await (
|
await (
|
||||||
await axios.post(
|
await axios.post(
|
||||||
`http://${n.ip}:3000/api/container/start`,
|
`http://${n.ip}:${process.env.PORT}/api/container/start`,
|
||||||
{ containerId },
|
{ containerId },
|
||||||
{
|
{
|
||||||
headers: { Authorization: `ApiKey ${n.key}` },
|
headers: { Authorization: `ApiKey ${n.key}` },
|
||||||
params: { containerId },
|
params: { containerId },
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
).data
|
).data,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,11 +71,14 @@ export async function stopContainer(containerId: string) {
|
|||||||
nodes.forEach(
|
nodes.forEach(
|
||||||
async (n) =>
|
async (n) =>
|
||||||
await (
|
await (
|
||||||
await axios.delete(`http://${n.ip}:3000/api/container/start`, {
|
await axios.delete(
|
||||||
headers: { Authorization: `ApiKey ${n.key}` },
|
`http://${n.ip}:${process.env.PORT}/api/container/start`,
|
||||||
params: { containerId },
|
{
|
||||||
})
|
headers: { Authorization: `ApiKey ${n.key}` },
|
||||||
).data
|
params: { containerId },
|
||||||
|
},
|
||||||
|
)
|
||||||
|
).data,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,11 +88,11 @@ export async function removeContainer(containerId: string) {
|
|||||||
nodes.forEach(
|
nodes.forEach(
|
||||||
async (n) =>
|
async (n) =>
|
||||||
await (
|
await (
|
||||||
await axios.delete(`http://${n.ip}:3000/api/container`, {
|
await axios.delete(`http://${n.ip}:${process.env.PORT}/api/container`, {
|
||||||
headers: { Authorization: `ApiKey ${n.key}` },
|
headers: { Authorization: `ApiKey ${n.key}` },
|
||||||
params: { containerId },
|
params: { containerId },
|
||||||
})
|
})
|
||||||
).data
|
).data,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,16 +104,16 @@ export async function getAllContainerLogs(): Promise<Log[]> {
|
|||||||
nodes.map(
|
nodes.map(
|
||||||
async (n) =>
|
async (n) =>
|
||||||
await (
|
await (
|
||||||
await axios.get(`http://${n.ip}:3000/api/logs`, {
|
await axios.get(`http://${n.ip}:${process.env.PORT}/api/logs`, {
|
||||||
headers: { Authorization: `ApiKey ${n.key}` },
|
headers: { Authorization: `ApiKey ${n.key}` },
|
||||||
})
|
})
|
||||||
).data
|
).data,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.flat()
|
.flat()
|
||||||
.sort(
|
.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
|
new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export async function getMetrics(): Promise<
|
|||||||
acc.out += m.out;
|
acc.out += m.out;
|
||||||
return acc;
|
return acc;
|
||||||
},
|
},
|
||||||
{ cpu: 0, memory: 0, in: 0, out: 0 }
|
{ cpu: 0, memory: 0, in: 0, out: 0 },
|
||||||
);
|
);
|
||||||
|
|
||||||
averaged.push({
|
averaged.push({
|
||||||
@@ -58,7 +58,7 @@ export async function getNodeMetrics(node: Node): Promise<
|
|||||||
}[]
|
}[]
|
||||||
> {
|
> {
|
||||||
const data = await (
|
const data = await (
|
||||||
await axios.get(`http://${node.ip}:3000/api/metrics`, {
|
await axios.get(`http://${node.ip}:${process.env.PORT}/api/metrics`, {
|
||||||
headers: { Authorization: `ApiKey ${node.key}` },
|
headers: { Authorization: `ApiKey ${node.key}` },
|
||||||
})
|
})
|
||||||
).data;
|
).data;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
import { read, write } from "@/app/api/file";
|
import { read, write } from "@/lib/server/file";
|
||||||
import { Node, NodeLiveData } from "@/lib/types";
|
import { Node, NodeLiveData } from "@/lib/types";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { verifySession } from "../../core/auth/session";
|
import { verifySession } from "../../core/auth/session";
|
||||||
@@ -26,11 +26,11 @@ export async function getLiveNodes(): Promise<(NodeLiveData & Node)[]> {
|
|||||||
nodes.map(async (node) => ({
|
nodes.map(async (node) => ({
|
||||||
...node,
|
...node,
|
||||||
liveData: await (
|
liveData: await (
|
||||||
await axios.get(`http://${node.ip}:3000/api/status`, {
|
await axios.get(`http://${node.ip}:${process.env.PORT}/api/status`, {
|
||||||
headers: { Authorization: `ApiKey ${node.key}` },
|
headers: { Authorization: `ApiKey ${node.key}` },
|
||||||
})
|
})
|
||||||
).data.liveData,
|
).data.liveData,
|
||||||
}))
|
})),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ export async function authNode(ip: string, key: string) {
|
|||||||
if (await verifySession()) return;
|
if (await verifySession()) return;
|
||||||
|
|
||||||
const data = await (
|
const data = await (
|
||||||
await axios.get(`http://${ip}:3000/api/auth`, {
|
await axios.get(`http://${ip}:${process.env.PORT}/api/auth`, {
|
||||||
headers: { Authorization: `ApiKey ${key}` },
|
headers: { Authorization: `ApiKey ${key}` },
|
||||||
})
|
})
|
||||||
).data;
|
).data;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { read } from "@/app/api/file";
|
import { read } from "@/lib/server/file";
|
||||||
import { NodeJsonTemplate } from "@/app/api/types";
|
import { NodeJsonTemplate } from "@/lib/server/types";
|
||||||
import { NextRequest } from "next/server";
|
import { NextRequest } from "next/server";
|
||||||
|
|
||||||
export default async function testAuth(
|
export default async function testAuth(
|
||||||
req: NextRequest
|
req: NextRequest,
|
||||||
): Promise<null | string> {
|
): Promise<null | string> {
|
||||||
const auth = req.headers
|
const auth = req.headers
|
||||||
.get("Authorization")
|
.get("Authorization")
|
||||||
Reference in New Issue
Block a user