mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
refactor(multi-server): adapt paths on server and in dokploy ui
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { APPLICATIONS_PATH } from "@/server/constants";
|
import { paths } from "@/server/constants";
|
||||||
import { unzipDrop } from "@/server/utils/builders/drop";
|
import { unzipDrop } from "@/server/utils/builders/drop";
|
||||||
import AdmZip from "adm-zip";
|
import AdmZip from "adm-zip";
|
||||||
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
||||||
@@ -10,7 +10,7 @@ if (typeof window === "undefined") {
|
|||||||
globalThis.File = undici.File as any;
|
globalThis.File = undici.File as any;
|
||||||
globalThis.FileList = undici.FileList as any;
|
globalThis.FileList = undici.FileList as any;
|
||||||
}
|
}
|
||||||
|
const { APPLICATIONS_PATH } = paths();
|
||||||
vi.mock("@/server/constants", () => ({
|
vi.mock("@/server/constants", () => ({
|
||||||
APPLICATIONS_PATH: "./__test__/drop/zips/output",
|
APPLICATIONS_PATH: "./__test__/drop/zips/output",
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export const ShowDeployment = ({ logPath, open, onClose, serverId }: Props) => {
|
|||||||
|
|
||||||
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
||||||
|
|
||||||
const wsUrl = `${protocol}//${window.location.host}/listen-deployment?logPath=${logPath}&serverId=${serverId}`;
|
const wsUrl = `${protocol}//${window.location.host}/listen-deployment?logPath=${logPath}${serverId ? `&serverId=${serverId}` : ""}`;
|
||||||
const ws = new WebSocket(wsUrl);
|
const ws = new WebSocket(wsUrl);
|
||||||
|
|
||||||
ws.onmessage = (e) => {
|
ws.onmessage = (e) => {
|
||||||
|
|||||||
@@ -29,6 +29,12 @@ import {
|
|||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
|
import {
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipProvider,
|
||||||
|
TooltipTrigger,
|
||||||
|
} from "@/components/ui/tooltip";
|
||||||
import { slugify } from "@/lib/slug";
|
import { slugify } from "@/lib/slug";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
@@ -37,12 +43,6 @@ import { useEffect } from "react";
|
|||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipProvider,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "@/components/ui/tooltip";
|
|
||||||
|
|
||||||
const AddComposeSchema = z.object({
|
const AddComposeSchema = z.object({
|
||||||
composeType: z.enum(["docker-compose", "stack"]).optional(),
|
composeType: z.enum(["docker-compose", "stack"]).optional(),
|
||||||
|
|||||||
@@ -161,6 +161,7 @@ export const SetupServer = ({ serverId }: Props) => {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<ShowDeployment
|
<ShowDeployment
|
||||||
open={activeLog !== null}
|
open={activeLog !== null}
|
||||||
onClose={() => setActiveLog(null)}
|
onClose={() => setActiveLog(null)}
|
||||||
|
|||||||
@@ -154,10 +154,7 @@ const Service = (
|
|||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent value="monitoring">
|
<TabsContent value="monitoring">
|
||||||
<div className="flex flex-col gap-4 pt-2.5">
|
<div className="flex flex-col gap-4 pt-2.5">
|
||||||
<DockerMonitoring
|
<DockerMonitoring appName={data?.appName || ""} />
|
||||||
appName={data?.appName || ""}
|
|
||||||
serverId={data?.serverId || ""}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent value="logs">
|
<TabsContent value="logs">
|
||||||
|
|||||||
@@ -45,9 +45,13 @@ export const domainRouter = createTRPCRouter({
|
|||||||
return await findDomainsByComposeId(input.composeId);
|
return await findDomainsByComposeId(input.composeId);
|
||||||
}),
|
}),
|
||||||
generateDomain: protectedProcedure
|
generateDomain: protectedProcedure
|
||||||
.input(z.object({ serverId: z.string(), appName: z.string() }))
|
.input(z.object({ appName: z.string(), serverId: z.string().optional() }))
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
return generateTraefikMeDomain(input.serverId, input.appName);
|
return generateTraefikMeDomain(
|
||||||
|
input.appName,
|
||||||
|
ctx.user.adminId,
|
||||||
|
input.serverId,
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
update: protectedProcedure
|
update: protectedProcedure
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { MAIN_TRAEFIK_PATH, MONITORING_PATH } from "@/server/constants";
|
|
||||||
import {
|
import {
|
||||||
apiAssignDomain,
|
apiAssignDomain,
|
||||||
apiEnableDashboard,
|
apiEnableDashboard,
|
||||||
@@ -54,6 +53,7 @@ import {
|
|||||||
} from "../services/settings";
|
} from "../services/settings";
|
||||||
import { canAccessToTraefikFiles } from "../services/user";
|
import { canAccessToTraefikFiles } from "../services/user";
|
||||||
import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc";
|
import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc";
|
||||||
|
import { paths } from "@/server/constants";
|
||||||
|
|
||||||
export const settingsRouter = createTRPCRouter({
|
export const settingsRouter = createTRPCRouter({
|
||||||
reloadServer: adminProcedure.mutation(async () => {
|
reloadServer: adminProcedure.mutation(async () => {
|
||||||
@@ -111,6 +111,7 @@ export const settingsRouter = createTRPCRouter({
|
|||||||
return true;
|
return true;
|
||||||
}),
|
}),
|
||||||
cleanMonitoring: adminProcedure.mutation(async () => {
|
cleanMonitoring: adminProcedure.mutation(async () => {
|
||||||
|
const { MONITORING_PATH } = paths();
|
||||||
await recreateDirectory(MONITORING_PATH);
|
await recreateDirectory(MONITORING_PATH);
|
||||||
return true;
|
return true;
|
||||||
}),
|
}),
|
||||||
@@ -237,6 +238,7 @@ export const settingsRouter = createTRPCRouter({
|
|||||||
throw new TRPCError({ code: "UNAUTHORIZED" });
|
throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const { MAIN_TRAEFIK_PATH } = paths();
|
||||||
const result = readDirectory(MAIN_TRAEFIK_PATH);
|
const result = readDirectory(MAIN_TRAEFIK_PATH);
|
||||||
return result || [];
|
return result || [];
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { CERTIFICATES_PATH } from "@/server/constants";
|
import { paths } from "@/server/constants";
|
||||||
import { db } from "@/server/db";
|
import { db } from "@/server/db";
|
||||||
import { type apiCreateCertificate, certificates } from "@/server/db/schema";
|
import { type apiCreateCertificate, certificates } from "@/server/db/schema";
|
||||||
import { removeDirectoryIfExistsContent } from "@/server/utils/filesystem/directory";
|
import { removeDirectoryIfExistsContent } from "@/server/utils/filesystem/directory";
|
||||||
@@ -50,6 +50,7 @@ export const createCertificate = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const removeCertificateById = async (certificateId: string) => {
|
export const removeCertificateById = async (certificateId: string) => {
|
||||||
|
const { CERTIFICATES_PATH } = paths();
|
||||||
const certificate = await findCertificateById(certificateId);
|
const certificate = await findCertificateById(certificateId);
|
||||||
const certDir = path.join(CERTIFICATES_PATH, certificate.certificatePath);
|
const certDir = path.join(CERTIFICATES_PATH, certificate.certificatePath);
|
||||||
|
|
||||||
@@ -74,6 +75,7 @@ export const findCertificates = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const createCertificateFiles = (certificate: Certificate) => {
|
const createCertificateFiles = (certificate: Certificate) => {
|
||||||
|
const { CERTIFICATES_PATH } = paths();
|
||||||
const dockerPath = "/etc/traefik";
|
const dockerPath = "/etc/traefik";
|
||||||
const certDir = path.join(CERTIFICATES_PATH, certificate.certificatePath);
|
const certDir = path.join(CERTIFICATES_PATH, certificate.certificatePath);
|
||||||
const crtPath = path.join(certDir, "chain.crt");
|
const crtPath = path.join(certDir, "chain.crt");
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { COMPOSE_PATH } from "@/server/constants";
|
import { paths } from "@/server/constants";
|
||||||
import { db } from "@/server/db";
|
import { db } from "@/server/db";
|
||||||
import { type apiCreateCompose, compose } from "@/server/db/schema";
|
import { type apiCreateCompose, compose } from "@/server/db/schema";
|
||||||
import { generateAppName } from "@/server/db/schema/utils";
|
import { generateAppName } from "@/server/db/schema/utils";
|
||||||
@@ -416,6 +416,7 @@ export const rebuildRemoteCompose = async ({
|
|||||||
|
|
||||||
export const removeCompose = async (compose: Compose) => {
|
export const removeCompose = async (compose: Compose) => {
|
||||||
try {
|
try {
|
||||||
|
const { COMPOSE_PATH } = paths(!!compose.serverId);
|
||||||
const projectPath = join(COMPOSE_PATH, compose.appName);
|
const projectPath = join(COMPOSE_PATH, compose.appName);
|
||||||
|
|
||||||
if (compose.composeType === "stack") {
|
if (compose.composeType === "stack") {
|
||||||
@@ -448,6 +449,7 @@ export const removeCompose = async (compose: Compose) => {
|
|||||||
export const stopCompose = async (composeId: string) => {
|
export const stopCompose = async (composeId: string) => {
|
||||||
const compose = await findComposeById(composeId);
|
const compose = await findComposeById(composeId);
|
||||||
try {
|
try {
|
||||||
|
const { COMPOSE_PATH } = paths(!!compose.serverId);
|
||||||
if (compose.composeType === "docker-compose") {
|
if (compose.composeType === "docker-compose") {
|
||||||
console.log(
|
console.log(
|
||||||
`cd ${join(COMPOSE_PATH, compose.appName)} && docker compose -p ${compose.appName} stop`,
|
`cd ${join(COMPOSE_PATH, compose.appName)} && docker compose -p ${compose.appName} stop`,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { existsSync, promises as fsPromises } from "node:fs";
|
import { existsSync, promises as fsPromises } from "node:fs";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { LOGS_PATH } from "@/server/constants";
|
import { paths } from "@/server/constants";
|
||||||
import { db } from "@/server/db";
|
import { db } from "@/server/db";
|
||||||
import {
|
import {
|
||||||
type apiCreateDeployment,
|
type apiCreateDeployment,
|
||||||
@@ -46,6 +46,7 @@ export const createDeployment = async (
|
|||||||
const application = await findApplicationById(deployment.applicationId);
|
const application = await findApplicationById(deployment.applicationId);
|
||||||
|
|
||||||
// await removeLastTenDeployments(deployment.applicationId);
|
// await removeLastTenDeployments(deployment.applicationId);
|
||||||
|
const { LOGS_PATH } = paths(!!application.serverId);
|
||||||
const formattedDateTime = format(new Date(), "yyyy-MM-dd:HH:mm:ss");
|
const formattedDateTime = format(new Date(), "yyyy-MM-dd:HH:mm:ss");
|
||||||
const fileName = `${application.appName}-${formattedDateTime}.log`;
|
const fileName = `${application.appName}-${formattedDateTime}.log`;
|
||||||
const logFilePath = path.join(LOGS_PATH, application.appName, fileName);
|
const logFilePath = path.join(LOGS_PATH, application.appName, fileName);
|
||||||
@@ -102,6 +103,7 @@ export const createDeploymentCompose = async (
|
|||||||
const compose = await findComposeById(deployment.composeId);
|
const compose = await findComposeById(deployment.composeId);
|
||||||
|
|
||||||
// await removeLastTenComposeDeployments(deployment.composeId);
|
// await removeLastTenComposeDeployments(deployment.composeId);
|
||||||
|
const { LOGS_PATH } = paths(!!compose.serverId);
|
||||||
const formattedDateTime = format(new Date(), "yyyy-MM-dd:HH:mm:ss");
|
const formattedDateTime = format(new Date(), "yyyy-MM-dd:HH:mm:ss");
|
||||||
const fileName = `${compose.appName}-${formattedDateTime}.log`;
|
const fileName = `${compose.appName}-${formattedDateTime}.log`;
|
||||||
const logFilePath = path.join(LOGS_PATH, compose.appName, fileName);
|
const logFilePath = path.join(LOGS_PATH, compose.appName, fileName);
|
||||||
@@ -208,6 +210,7 @@ const removeLastTenComposeDeployments = async (composeId: string) => {
|
|||||||
|
|
||||||
export const removeDeployments = async (application: Application) => {
|
export const removeDeployments = async (application: Application) => {
|
||||||
const { appName, applicationId } = application;
|
const { appName, applicationId } = application;
|
||||||
|
const { LOGS_PATH } = paths(!!application.serverId);
|
||||||
const logsPath = path.join(LOGS_PATH, appName);
|
const logsPath = path.join(LOGS_PATH, appName);
|
||||||
if (application.serverId) {
|
if (application.serverId) {
|
||||||
await execAsyncRemote(application.serverId, `rm -rf ${logsPath}`);
|
await execAsyncRemote(application.serverId, `rm -rf ${logsPath}`);
|
||||||
@@ -219,6 +222,7 @@ export const removeDeployments = async (application: Application) => {
|
|||||||
|
|
||||||
export const removeDeploymentsByComposeId = async (compose: Compose) => {
|
export const removeDeploymentsByComposeId = async (compose: Compose) => {
|
||||||
const { appName } = compose;
|
const { appName } = compose;
|
||||||
|
const { LOGS_PATH } = paths(!!compose.serverId);
|
||||||
const logsPath = path.join(LOGS_PATH, appName);
|
const logsPath = path.join(LOGS_PATH, appName);
|
||||||
if (compose.serverId) {
|
if (compose.serverId) {
|
||||||
await execAsyncRemote(compose.serverId, `rm -rf ${logsPath}`);
|
await execAsyncRemote(compose.serverId, `rm -rf ${logsPath}`);
|
||||||
@@ -287,8 +291,9 @@ export const createServerDeployment = async (
|
|||||||
>,
|
>,
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
const server = await findServerById(deployment.serverId);
|
const { LOGS_PATH } = paths();
|
||||||
|
|
||||||
|
const server = await findServerById(deployment.serverId);
|
||||||
await removeLastFiveDeployments(deployment.serverId);
|
await removeLastFiveDeployments(deployment.serverId);
|
||||||
const formattedDateTime = format(new Date(), "yyyy-MM-dd:HH:mm:ss");
|
const formattedDateTime = format(new Date(), "yyyy-MM-dd:HH:mm:ss");
|
||||||
const fileName = `${server.appName}-${formattedDateTime}.log`;
|
const fileName = `${server.appName}-${formattedDateTime}.log`;
|
||||||
@@ -341,6 +346,7 @@ export const removeLastFiveDeployments = async (serverId: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const removeDeploymentsByServerId = async (server: Server) => {
|
export const removeDeploymentsByServerId = async (server: Server) => {
|
||||||
|
const { LOGS_PATH } = paths();
|
||||||
const { appName } = server;
|
const { appName } = server;
|
||||||
const logsPath = path.join(LOGS_PATH, appName);
|
const logsPath = path.join(LOGS_PATH, appName);
|
||||||
await removeDirectoryIfExistsContent(logsPath);
|
await removeDirectoryIfExistsContent(logsPath);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { manageDomain } from "@/server/utils/traefik/domain";
|
|||||||
import { generateRandomDomain } from "@/templates/utils";
|
import { generateRandomDomain } from "@/templates/utils";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { findAdmin } from "./admin";
|
import { findAdmin, findAdminById } from "./admin";
|
||||||
import { findApplicationById } from "./application";
|
import { findApplicationById } from "./application";
|
||||||
import { findServerById } from "./server";
|
import { findServerById } from "./server";
|
||||||
|
|
||||||
@@ -35,8 +35,9 @@ export const createDomain = async (input: typeof apiCreateDomain._type) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const generateTraefikMeDomain = async (
|
export const generateTraefikMeDomain = async (
|
||||||
serverId: string,
|
|
||||||
appName: string,
|
appName: string,
|
||||||
|
adminId: string,
|
||||||
|
serverId?: string,
|
||||||
) => {
|
) => {
|
||||||
if (serverId) {
|
if (serverId) {
|
||||||
const server = await findServerById(serverId);
|
const server = await findServerById(serverId);
|
||||||
@@ -45,7 +46,14 @@ export const generateTraefikMeDomain = async (
|
|||||||
projectName: appName,
|
projectName: appName,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const admin = await findAdmin();
|
|
||||||
|
if (process.env.NODE_ENV === "development") {
|
||||||
|
return generateRandomDomain({
|
||||||
|
serverIp: "",
|
||||||
|
projectName: appName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const admin = await findAdminById(adminId);
|
||||||
return generateRandomDomain({
|
return generateRandomDomain({
|
||||||
serverIp: admin?.serverIp || "",
|
serverIp: admin?.serverIp || "",
|
||||||
projectName: appName,
|
projectName: appName,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { APPLICATIONS_PATH, COMPOSE_PATH } from "@/server/constants";
|
import { paths } from "@/server/constants";
|
||||||
import { db } from "@/server/db";
|
import { db } from "@/server/db";
|
||||||
import {
|
import {
|
||||||
type ServiceType,
|
type ServiceType,
|
||||||
@@ -214,23 +214,36 @@ export const deleteFileMount = async (mountId: string) => {
|
|||||||
export const getBaseFilesPath = async (mountId: string) => {
|
export const getBaseFilesPath = async (mountId: string) => {
|
||||||
const mount = await findMountById(mountId);
|
const mount = await findMountById(mountId);
|
||||||
|
|
||||||
let absoluteBasePath = path.resolve(APPLICATIONS_PATH);
|
let absoluteBasePath = "";
|
||||||
let appName = "";
|
let appName = "";
|
||||||
let directoryPath = "";
|
let directoryPath = "";
|
||||||
|
|
||||||
if (mount.serviceType === "application" && mount.application) {
|
if (mount.serviceType === "application" && mount.application) {
|
||||||
|
const { APPLICATIONS_PATH } = paths(!!mount.application.serverId);
|
||||||
|
absoluteBasePath = path.resolve(APPLICATIONS_PATH);
|
||||||
appName = mount.application.appName;
|
appName = mount.application.appName;
|
||||||
} else if (mount.serviceType === "postgres" && mount.postgres) {
|
} else if (mount.serviceType === "postgres" && mount.postgres) {
|
||||||
|
const { APPLICATIONS_PATH } = paths(!!mount.postgres.serverId);
|
||||||
|
absoluteBasePath = path.resolve(APPLICATIONS_PATH);
|
||||||
appName = mount.postgres.appName;
|
appName = mount.postgres.appName;
|
||||||
} else if (mount.serviceType === "mariadb" && mount.mariadb) {
|
} else if (mount.serviceType === "mariadb" && mount.mariadb) {
|
||||||
|
const { APPLICATIONS_PATH } = paths(!!mount.mariadb.serverId);
|
||||||
|
absoluteBasePath = path.resolve(APPLICATIONS_PATH);
|
||||||
appName = mount.mariadb.appName;
|
appName = mount.mariadb.appName;
|
||||||
} else if (mount.serviceType === "mongo" && mount.mongo) {
|
} else if (mount.serviceType === "mongo" && mount.mongo) {
|
||||||
|
const { APPLICATIONS_PATH } = paths(!!mount.mongo.serverId);
|
||||||
|
absoluteBasePath = path.resolve(APPLICATIONS_PATH);
|
||||||
appName = mount.mongo.appName;
|
appName = mount.mongo.appName;
|
||||||
} else if (mount.serviceType === "mysql" && mount.mysql) {
|
} else if (mount.serviceType === "mysql" && mount.mysql) {
|
||||||
|
const { APPLICATIONS_PATH } = paths(!!mount.mysql.serverId);
|
||||||
|
absoluteBasePath = path.resolve(APPLICATIONS_PATH);
|
||||||
appName = mount.mysql.appName;
|
appName = mount.mysql.appName;
|
||||||
} else if (mount.serviceType === "redis" && mount.redis) {
|
} else if (mount.serviceType === "redis" && mount.redis) {
|
||||||
|
const { APPLICATIONS_PATH } = paths(!!mount.redis.serverId);
|
||||||
|
absoluteBasePath = path.resolve(APPLICATIONS_PATH);
|
||||||
appName = mount.redis.appName;
|
appName = mount.redis.appName;
|
||||||
} else if (mount.serviceType === "compose" && mount.compose) {
|
} else if (mount.serviceType === "compose" && mount.compose) {
|
||||||
|
const { COMPOSE_PATH } = paths(!!mount.compose.serverId);
|
||||||
appName = mount.compose.appName;
|
appName = mount.compose.appName;
|
||||||
absoluteBasePath = path.resolve(COMPOSE_PATH);
|
absoluteBasePath = path.resolve(COMPOSE_PATH);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +1,39 @@
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import Docker from "dockerode";
|
import Docker from "dockerode";
|
||||||
|
|
||||||
// const IS_CLOUD = process.env.IS_CLOUD === "true";
|
export const IS_CLOUD = process.env.IS_CLOUD === "true";
|
||||||
|
|
||||||
export const BASE_PATH =
|
|
||||||
process.env.NODE_ENV === "production"
|
|
||||||
? "/etc/dokploy"
|
|
||||||
: path.join(process.cwd(), ".docker");
|
|
||||||
export const MAIN_TRAEFIK_PATH = `${BASE_PATH}/traefik`;
|
|
||||||
export const DYNAMIC_TRAEFIK_PATH = `/etc/dokploy/traefik/dynamic`;
|
|
||||||
export const LOGS_PATH = `/etc/dokploy/logs`;
|
|
||||||
export const APPLICATIONS_PATH = `/etc/dokploy/applications`;
|
|
||||||
export const COMPOSE_PATH = `/etc/dokploy/compose`;
|
|
||||||
export const SSH_PATH = `${BASE_PATH}/ssh`;
|
|
||||||
export const CERTIFICATES_PATH = `${DYNAMIC_TRAEFIK_PATH}/certificates`;
|
|
||||||
export const REGISTRY_PATH = `${DYNAMIC_TRAEFIK_PATH}/registry`;
|
|
||||||
export const MONITORING_PATH = `${BASE_PATH}/monitoring`;
|
|
||||||
export const docker = new Docker();
|
export const docker = new Docker();
|
||||||
|
|
||||||
export const getPaths = (basePath: string) => {
|
export const paths = (isServer = false) => {
|
||||||
// return [
|
if (isServer) {
|
||||||
// MAIN_TRAEFIK_PATH: `${basePath}/traefik`,
|
const BASE_PATH = "/etc/dokploy";
|
||||||
// DYNAMIC_TRAEFIK_PATH: `${basePath}/traefik/dynamic`,
|
return {
|
||||||
// LOGS_PATH: `${basePath}/logs`,
|
BASE_PATH,
|
||||||
// APPLICATIONS_PATH: `${basePath}/applications`,
|
MAIN_TRAEFIK_PATH: `${BASE_PATH}/traefik`,
|
||||||
// COMPOSE_PATH: `${basePath}/compose`,
|
DYNAMIC_TRAEFIK_PATH: `${BASE_PATH}/traefik/dynamic`,
|
||||||
// SSH_PATH: `${basePath}/ssh`,
|
LOGS_PATH: `${BASE_PATH}/logs`,
|
||||||
// CERTIFICATES_PATH: `${basePath}/certificates`,
|
APPLICATIONS_PATH: `${BASE_PATH}/applications`,
|
||||||
// MONITORING_PATH: `${basePath}/monitoring`,
|
COMPOSE_PATH: `${BASE_PATH}/compose`,
|
||||||
// ];
|
SSH_PATH: `${BASE_PATH}/ssh`,
|
||||||
|
CERTIFICATES_PATH: `${BASE_PATH}/certificates`,
|
||||||
return [
|
MONITORING_PATH: `${BASE_PATH}/monitoring`,
|
||||||
`${basePath}/traefik`,
|
REGISTRY_PATH: `${BASE_PATH}/registry`,
|
||||||
`${basePath}/traefik/dynamic`,
|
};
|
||||||
`${basePath}/logs`,
|
}
|
||||||
`${basePath}/applications`,
|
const BASE_PATH =
|
||||||
`${basePath}/compose`,
|
process.env.NODE_ENV === "production"
|
||||||
`${basePath}/ssh`,
|
? "/etc/dokploy"
|
||||||
`${basePath}/certificates`,
|
: path.join(process.cwd(), ".docker");
|
||||||
`${basePath}/monitoring`,
|
return {
|
||||||
];
|
BASE_PATH,
|
||||||
|
MAIN_TRAEFIK_PATH: `${BASE_PATH}/traefik`,
|
||||||
|
DYNAMIC_TRAEFIK_PATH: `${BASE_PATH}/traefik/dynamic`,
|
||||||
|
LOGS_PATH: `${BASE_PATH}/logs`,
|
||||||
|
APPLICATIONS_PATH: `${BASE_PATH}/applications`,
|
||||||
|
COMPOSE_PATH: `${BASE_PATH}/compose`,
|
||||||
|
SSH_PATH: `${BASE_PATH}/ssh`,
|
||||||
|
CERTIFICATES_PATH: `${BASE_PATH}/certificates`,
|
||||||
|
MONITORING_PATH: `${BASE_PATH}/monitoring`,
|
||||||
|
REGISTRY_PATH: `${BASE_PATH}/registry`,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { promises } from "node:fs";
|
import { promises } from "node:fs";
|
||||||
import type Dockerode from "dockerode";
|
import type Dockerode from "dockerode";
|
||||||
import osUtils from "node-os-utils";
|
import osUtils from "node-os-utils";
|
||||||
import { MONITORING_PATH } from "../constants";
|
import { paths } from "../constants";
|
||||||
|
|
||||||
export const recordAdvancedStats = async (
|
export const recordAdvancedStats = async (
|
||||||
stats: Dockerode.ContainerStats,
|
stats: Dockerode.ContainerStats,
|
||||||
appName: string,
|
appName: string,
|
||||||
) => {
|
) => {
|
||||||
|
const { MONITORING_PATH } = paths();
|
||||||
const path = `${MONITORING_PATH}/${appName}`;
|
const path = `${MONITORING_PATH}/${appName}`;
|
||||||
|
|
||||||
await promises.mkdir(path, { recursive: true });
|
await promises.mkdir(path, { recursive: true });
|
||||||
@@ -68,6 +69,7 @@ export const readStatsFile = async (
|
|||||||
statType: "cpu" | "memory" | "disk" | "network" | "block",
|
statType: "cpu" | "memory" | "disk" | "network" | "block",
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
|
const { MONITORING_PATH } = paths();
|
||||||
const filePath = `${MONITORING_PATH}/${appName}/${statType}.json`;
|
const filePath = `${MONITORING_PATH}/${appName}/${statType}.json`;
|
||||||
const data = await promises.readFile(filePath, "utf-8");
|
const data = await promises.readFile(filePath, "utf-8");
|
||||||
return JSON.parse(data);
|
return JSON.parse(data);
|
||||||
@@ -81,6 +83,7 @@ export const updateStatsFile = async (
|
|||||||
statType: "cpu" | "memory" | "disk" | "network" | "block",
|
statType: "cpu" | "memory" | "disk" | "network" | "block",
|
||||||
value: number | string | unknown,
|
value: number | string | unknown,
|
||||||
) => {
|
) => {
|
||||||
|
const { MONITORING_PATH } = paths();
|
||||||
const stats = await readStatsFile(appName, statType);
|
const stats = await readStatsFile(appName, statType);
|
||||||
stats.push({ value, time: new Date() });
|
stats.push({ value, time: new Date() });
|
||||||
|
|
||||||
@@ -100,6 +103,7 @@ export const readLastValueStatsFile = async (
|
|||||||
statType: "cpu" | "memory" | "disk" | "network" | "block",
|
statType: "cpu" | "memory" | "disk" | "network" | "block",
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
|
const { MONITORING_PATH } = paths();
|
||||||
const filePath = `${MONITORING_PATH}/${appName}/${statType}.json`;
|
const filePath = `${MONITORING_PATH}/${appName}/${statType}.json`;
|
||||||
const data = await promises.readFile(filePath, "utf-8");
|
const data = await promises.readFile(filePath, "utf-8");
|
||||||
const stats = JSON.parse(data);
|
const stats = JSON.parse(data);
|
||||||
|
|||||||
@@ -1,14 +1,5 @@
|
|||||||
import { chmodSync, existsSync, mkdirSync } from "node:fs";
|
import { chmodSync, existsSync, mkdirSync } from "node:fs";
|
||||||
import {
|
import { paths } from "../constants";
|
||||||
APPLICATIONS_PATH,
|
|
||||||
BASE_PATH,
|
|
||||||
CERTIFICATES_PATH,
|
|
||||||
DYNAMIC_TRAEFIK_PATH,
|
|
||||||
LOGS_PATH,
|
|
||||||
MAIN_TRAEFIK_PATH,
|
|
||||||
MONITORING_PATH,
|
|
||||||
SSH_PATH,
|
|
||||||
} from "../constants";
|
|
||||||
|
|
||||||
const createDirectoryIfNotExist = (dirPath: string) => {
|
const createDirectoryIfNotExist = (dirPath: string) => {
|
||||||
if (!existsSync(dirPath)) {
|
if (!existsSync(dirPath)) {
|
||||||
@@ -18,6 +9,16 @@ const createDirectoryIfNotExist = (dirPath: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const setupDirectories = () => {
|
export const setupDirectories = () => {
|
||||||
|
const {
|
||||||
|
APPLICATIONS_PATH,
|
||||||
|
BASE_PATH,
|
||||||
|
CERTIFICATES_PATH,
|
||||||
|
DYNAMIC_TRAEFIK_PATH,
|
||||||
|
LOGS_PATH,
|
||||||
|
MAIN_TRAEFIK_PATH,
|
||||||
|
MONITORING_PATH,
|
||||||
|
SSH_PATH,
|
||||||
|
} = paths();
|
||||||
const directories = [
|
const directories = [
|
||||||
BASE_PATH,
|
BASE_PATH,
|
||||||
MAIN_TRAEFIK_PATH,
|
MAIN_TRAEFIK_PATH,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { CreateServiceOptions } from "dockerode";
|
import type { CreateServiceOptions } from "dockerode";
|
||||||
import { generateRandomPassword } from "../auth/random-password";
|
import { generateRandomPassword } from "../auth/random-password";
|
||||||
import { REGISTRY_PATH, docker } from "../constants";
|
import { paths, docker } from "../constants";
|
||||||
import { pullImage } from "../utils/docker/utils";
|
import { pullImage } from "../utils/docker/utils";
|
||||||
import { execAsync } from "../utils/process/execAsync";
|
import { execAsync } from "../utils/process/execAsync";
|
||||||
|
|
||||||
@@ -8,6 +8,7 @@ export const initializeRegistry = async (
|
|||||||
username: string,
|
username: string,
|
||||||
password: string,
|
password: string,
|
||||||
) => {
|
) => {
|
||||||
|
const { REGISTRY_PATH } = paths();
|
||||||
const imageName = "registry:2.8.3";
|
const imageName = "registry:2.8.3";
|
||||||
const containerName = "dokploy-registry";
|
const containerName = "dokploy-registry";
|
||||||
await generateRegistryPassword(username, password);
|
await generateRegistryPassword(username, password);
|
||||||
@@ -78,6 +79,7 @@ export const initializeRegistry = async (
|
|||||||
|
|
||||||
const generateRegistryPassword = async (username: string, password: string) => {
|
const generateRegistryPassword = async (username: string, password: string) => {
|
||||||
try {
|
try {
|
||||||
|
const { REGISTRY_PATH } = paths();
|
||||||
const command = `htpasswd -nbB ${username} "${password}" > ${REGISTRY_PATH}/htpasswd`;
|
const command = `htpasswd -nbB ${username} "${password}" > ${REGISTRY_PATH}/htpasswd`;
|
||||||
const result = await execAsync(command);
|
const result = await execAsync(command);
|
||||||
console.log("Password generated ✅");
|
console.log("Password generated ✅");
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { chmodSync, existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import type { ContainerTaskSpec, CreateServiceOptions } from "dockerode";
|
import type { ContainerTaskSpec, CreateServiceOptions } from "dockerode";
|
||||||
import { dump } from "js-yaml";
|
import { dump } from "js-yaml";
|
||||||
import { DYNAMIC_TRAEFIK_PATH, MAIN_TRAEFIK_PATH, docker } from "../constants";
|
import { docker, paths } from "../constants";
|
||||||
import { pullImage } from "../utils/docker/utils";
|
import { pullImage } from "../utils/docker/utils";
|
||||||
import type { FileConfig } from "../utils/traefik/file-types";
|
import type { FileConfig } from "../utils/traefik/file-types";
|
||||||
import type { MainTraefikConfig } from "../utils/traefik/types";
|
import type { MainTraefikConfig } from "../utils/traefik/types";
|
||||||
@@ -20,6 +20,7 @@ export const initializeTraefik = async ({
|
|||||||
enableDashboard = false,
|
enableDashboard = false,
|
||||||
env,
|
env,
|
||||||
}: TraefikOptions = {}) => {
|
}: TraefikOptions = {}) => {
|
||||||
|
const { MAIN_TRAEFIK_PATH, DYNAMIC_TRAEFIK_PATH } = paths();
|
||||||
const imageName = "traefik:v3.1.2";
|
const imageName = "traefik:v3.1.2";
|
||||||
const containerName = "dokploy-traefik";
|
const containerName = "dokploy-traefik";
|
||||||
const settings: CreateServiceOptions = {
|
const settings: CreateServiceOptions = {
|
||||||
@@ -115,6 +116,7 @@ export const initializeTraefik = async ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const createDefaultServerTraefikConfig = () => {
|
export const createDefaultServerTraefikConfig = () => {
|
||||||
|
const { DYNAMIC_TRAEFIK_PATH } = paths();
|
||||||
const configFilePath = path.join(DYNAMIC_TRAEFIK_PATH, "dokploy.yml");
|
const configFilePath = path.join(DYNAMIC_TRAEFIK_PATH, "dokploy.yml");
|
||||||
|
|
||||||
if (existsSync(configFilePath)) {
|
if (existsSync(configFilePath)) {
|
||||||
@@ -265,6 +267,7 @@ export const getDefaultServerTraefikConfig = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const createDefaultTraefikConfig = () => {
|
export const createDefaultTraefikConfig = () => {
|
||||||
|
const { MAIN_TRAEFIK_PATH, DYNAMIC_TRAEFIK_PATH } = paths();
|
||||||
const mainConfig = path.join(MAIN_TRAEFIK_PATH, "traefik.yml");
|
const mainConfig = path.join(MAIN_TRAEFIK_PATH, "traefik.yml");
|
||||||
const acmeJsonPath = path.join(DYNAMIC_TRAEFIK_PATH, "acme.json");
|
const acmeJsonPath = path.join(DYNAMIC_TRAEFIK_PATH, "acme.json");
|
||||||
|
|
||||||
@@ -297,6 +300,7 @@ export const getDefaultMiddlewares = () => {
|
|||||||
return yamlStr;
|
return yamlStr;
|
||||||
};
|
};
|
||||||
export const createDefaultMiddlewares = () => {
|
export const createDefaultMiddlewares = () => {
|
||||||
|
const { DYNAMIC_TRAEFIK_PATH } = paths();
|
||||||
const middlewaresPath = path.join(DYNAMIC_TRAEFIK_PATH, "middlewares.yml");
|
const middlewaresPath = path.join(DYNAMIC_TRAEFIK_PATH, "middlewares.yml");
|
||||||
if (existsSync(middlewaresPath)) {
|
if (existsSync(middlewaresPath)) {
|
||||||
console.log("Default middlewares already exists");
|
console.log("Default middlewares already exists");
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { findAdmin, updateAdmin } from "@/server/api/services/admin";
|
import { findAdmin, updateAdmin } from "@/server/api/services/admin";
|
||||||
import { DYNAMIC_TRAEFIK_PATH } from "@/server/constants";
|
import { paths } from "@/server/constants";
|
||||||
import { type RotatingFileStream, createStream } from "rotating-file-stream";
|
import { type RotatingFileStream, createStream } from "rotating-file-stream";
|
||||||
import { execAsync } from "../process/execAsync";
|
import { execAsync } from "../process/execAsync";
|
||||||
|
|
||||||
@@ -39,6 +39,7 @@ class LogRotationManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async activateStream(): Promise<void> {
|
private async activateStream(): Promise<void> {
|
||||||
|
const { DYNAMIC_TRAEFIK_PATH } = paths();
|
||||||
if (this.stream) {
|
if (this.stream) {
|
||||||
await this.deactivateStream();
|
await this.deactivateStream();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
writeFileSync,
|
writeFileSync,
|
||||||
} from "node:fs";
|
} from "node:fs";
|
||||||
import { dirname, join } from "node:path";
|
import { dirname, join } from "node:path";
|
||||||
import { COMPOSE_PATH } from "@/server/constants";
|
import { paths } from "@/server/constants";
|
||||||
import type { InferResultType } from "@/server/types/with";
|
import type { InferResultType } from "@/server/types/with";
|
||||||
import boxen from "boxen";
|
import boxen from "boxen";
|
||||||
import {
|
import {
|
||||||
@@ -24,6 +24,7 @@ export const buildCompose = async (compose: ComposeNested, logPath: string) => {
|
|||||||
const writeStream = createWriteStream(logPath, { flags: "a" });
|
const writeStream = createWriteStream(logPath, { flags: "a" });
|
||||||
const { sourceType, appName, mounts, composeType, domains } = compose;
|
const { sourceType, appName, mounts, composeType, domains } = compose;
|
||||||
try {
|
try {
|
||||||
|
const { COMPOSE_PATH } = paths();
|
||||||
const command = createCommand(compose);
|
const command = createCommand(compose);
|
||||||
await writeDomainsToCompose(compose, domains);
|
await writeDomainsToCompose(compose, domains);
|
||||||
createEnvFile(compose);
|
createEnvFile(compose);
|
||||||
@@ -73,6 +74,7 @@ export const getBuildComposeCommand = async (
|
|||||||
compose: ComposeNested,
|
compose: ComposeNested,
|
||||||
logPath: string,
|
logPath: string,
|
||||||
) => {
|
) => {
|
||||||
|
const { COMPOSE_PATH } = paths(true);
|
||||||
const { sourceType, appName, mounts, composeType, domains, composePath } =
|
const { sourceType, appName, mounts, composeType, domains, composePath } =
|
||||||
compose;
|
compose;
|
||||||
const command = createCommand(compose);
|
const command = createCommand(compose);
|
||||||
@@ -158,6 +160,7 @@ export const createCommand = (compose: ComposeNested) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const createEnvFile = (compose: ComposeNested) => {
|
const createEnvFile = (compose: ComposeNested) => {
|
||||||
|
const { COMPOSE_PATH } = paths();
|
||||||
const { env, composePath, appName } = compose;
|
const { env, composePath, appName } = compose;
|
||||||
const composeFilePath =
|
const composeFilePath =
|
||||||
join(COMPOSE_PATH, appName, "code", composePath) ||
|
join(COMPOSE_PATH, appName, "code", composePath) ||
|
||||||
@@ -182,6 +185,7 @@ const createEnvFile = (compose: ComposeNested) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getCreateEnvFileCommand = (compose: ComposeNested) => {
|
export const getCreateEnvFileCommand = (compose: ComposeNested) => {
|
||||||
|
const { COMPOSE_PATH } = paths(true);
|
||||||
const { env, composePath, appName } = compose;
|
const { env, composePath, appName } = compose;
|
||||||
const composeFilePath =
|
const composeFilePath =
|
||||||
join(COMPOSE_PATH, appName, "code", composePath) ||
|
join(COMPOSE_PATH, appName, "code", composePath) ||
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import fs from "node:fs/promises";
|
|||||||
import path, { join } from "node:path";
|
import path, { join } from "node:path";
|
||||||
import type { Application } from "@/server/api/services/application";
|
import type { Application } from "@/server/api/services/application";
|
||||||
import { findServerById } from "@/server/api/services/server";
|
import { findServerById } from "@/server/api/services/server";
|
||||||
import { APPLICATIONS_PATH } from "@/server/constants";
|
import { paths } from "@/server/constants";
|
||||||
import AdmZip from "adm-zip";
|
import AdmZip from "adm-zip";
|
||||||
import { Client, type SFTPWrapper } from "ssh2";
|
import { Client, type SFTPWrapper } from "ssh2";
|
||||||
import {
|
import {
|
||||||
@@ -17,6 +17,7 @@ export const unzipDrop = async (zipFile: File, application: Application) => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const { appName } = application;
|
const { appName } = application;
|
||||||
|
const { APPLICATIONS_PATH } = paths(!!application.serverId);
|
||||||
const outputPath = join(APPLICATIONS_PATH, appName, "code");
|
const outputPath = join(APPLICATIONS_PATH, appName, "code");
|
||||||
if (application.serverId) {
|
if (application.serverId) {
|
||||||
await recreateDirectoryRemote(outputPath, application.serverId);
|
await recreateDirectoryRemote(outputPath, application.serverId);
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ export const mechanizeDockerContainer = async (
|
|||||||
} = generateConfigContainer(application);
|
} = generateConfigContainer(application);
|
||||||
|
|
||||||
const bindsMount = generateBindMounts(mounts);
|
const bindsMount = generateBindMounts(mounts);
|
||||||
const filesMount = generateFileMounts(appName, mounts);
|
const filesMount = generateFileMounts(appName, application);
|
||||||
const envVariables = prepareEnvironmentVariables(env);
|
const envVariables = prepareEnvironmentVariables(env);
|
||||||
|
|
||||||
const image = getImageName(application);
|
const image = getImageName(application);
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import type { Mariadb } from "@/server/api/services/mariadb";
|
|
||||||
import type { Mount } from "@/server/api/services/mount";
|
|
||||||
import type { CreateServiceOptions } from "dockerode";
|
import type { CreateServiceOptions } from "dockerode";
|
||||||
import {
|
import {
|
||||||
calculateResources,
|
calculateResources,
|
||||||
@@ -9,11 +7,10 @@ import {
|
|||||||
prepareEnvironmentVariables,
|
prepareEnvironmentVariables,
|
||||||
} from "../docker/utils";
|
} from "../docker/utils";
|
||||||
import { getRemoteDocker } from "../servers/remote-docker";
|
import { getRemoteDocker } from "../servers/remote-docker";
|
||||||
|
import type { InferResultType } from "@/server/types/with";
|
||||||
|
|
||||||
type MariadbWithMounts = Mariadb & {
|
export type MariadbNested = InferResultType<"mariadb", { mounts: true }>;
|
||||||
mounts: Mount[];
|
export const buildMariadb = async (mariadb: MariadbNested) => {
|
||||||
};
|
|
||||||
export const buildMariadb = async (mariadb: MariadbWithMounts) => {
|
|
||||||
const {
|
const {
|
||||||
appName,
|
appName,
|
||||||
env,
|
env,
|
||||||
@@ -43,7 +40,7 @@ export const buildMariadb = async (mariadb: MariadbWithMounts) => {
|
|||||||
const envVariables = prepareEnvironmentVariables(defaultMariadbEnv);
|
const envVariables = prepareEnvironmentVariables(defaultMariadbEnv);
|
||||||
const volumesMount = generateVolumeMounts(mounts);
|
const volumesMount = generateVolumeMounts(mounts);
|
||||||
const bindsMount = generateBindMounts(mounts);
|
const bindsMount = generateBindMounts(mounts);
|
||||||
const filesMount = generateFileMounts(appName, mounts);
|
const filesMount = generateFileMounts(appName, mariadb);
|
||||||
|
|
||||||
const docker = await getRemoteDocker(mariadb.serverId);
|
const docker = await getRemoteDocker(mariadb.serverId);
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,11 @@ import {
|
|||||||
prepareEnvironmentVariables,
|
prepareEnvironmentVariables,
|
||||||
} from "../docker/utils";
|
} from "../docker/utils";
|
||||||
import { getRemoteDocker } from "../servers/remote-docker";
|
import { getRemoteDocker } from "../servers/remote-docker";
|
||||||
|
import type { InferResultType } from "@/server/types/with";
|
||||||
|
|
||||||
type MongoWithMounts = Mongo & {
|
export type MongoNested = InferResultType<"mongo", { mounts: true }>;
|
||||||
mounts: Mount[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const buildMongo = async (mongo: MongoWithMounts) => {
|
export const buildMongo = async (mongo: MongoNested) => {
|
||||||
const {
|
const {
|
||||||
appName,
|
appName,
|
||||||
env,
|
env,
|
||||||
@@ -42,7 +41,7 @@ export const buildMongo = async (mongo: MongoWithMounts) => {
|
|||||||
const envVariables = prepareEnvironmentVariables(defaultMongoEnv);
|
const envVariables = prepareEnvironmentVariables(defaultMongoEnv);
|
||||||
const volumesMount = generateVolumeMounts(mounts);
|
const volumesMount = generateVolumeMounts(mounts);
|
||||||
const bindsMount = generateBindMounts(mounts);
|
const bindsMount = generateBindMounts(mounts);
|
||||||
const filesMount = generateFileMounts(appName, mounts);
|
const filesMount = generateFileMounts(appName, mongo);
|
||||||
|
|
||||||
const docker = await getRemoteDocker(mongo.serverId);
|
const docker = await getRemoteDocker(mongo.serverId);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
import type { Mount } from "@/server/api/services/mount";
|
|
||||||
import type { MySql } from "@/server/api/services/mysql";
|
|
||||||
import { docker } from "@/server/constants";
|
|
||||||
import type { CreateServiceOptions } from "dockerode";
|
import type { CreateServiceOptions } from "dockerode";
|
||||||
import {
|
import {
|
||||||
calculateResources,
|
calculateResources,
|
||||||
@@ -10,12 +7,11 @@ import {
|
|||||||
prepareEnvironmentVariables,
|
prepareEnvironmentVariables,
|
||||||
} from "../docker/utils";
|
} from "../docker/utils";
|
||||||
import { getRemoteDocker } from "../servers/remote-docker";
|
import { getRemoteDocker } from "../servers/remote-docker";
|
||||||
|
import type { InferResultType } from "@/server/types/with";
|
||||||
|
|
||||||
type MysqlWithMounts = MySql & {
|
export type MysqlNested = InferResultType<"mysql", { mounts: true }>;
|
||||||
mounts: Mount[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const buildMysql = async (mysql: MysqlWithMounts) => {
|
export const buildMysql = async (mysql: MysqlNested) => {
|
||||||
const {
|
const {
|
||||||
appName,
|
appName,
|
||||||
env,
|
env,
|
||||||
@@ -50,7 +46,7 @@ export const buildMysql = async (mysql: MysqlWithMounts) => {
|
|||||||
const envVariables = prepareEnvironmentVariables(defaultMysqlEnv);
|
const envVariables = prepareEnvironmentVariables(defaultMysqlEnv);
|
||||||
const volumesMount = generateVolumeMounts(mounts);
|
const volumesMount = generateVolumeMounts(mounts);
|
||||||
const bindsMount = generateBindMounts(mounts);
|
const bindsMount = generateBindMounts(mounts);
|
||||||
const filesMount = generateFileMounts(appName, mounts);
|
const filesMount = generateFileMounts(appName, mysql);
|
||||||
|
|
||||||
const docker = await getRemoteDocker(mysql.serverId);
|
const docker = await getRemoteDocker(mysql.serverId);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import type { Mount } from "@/server/api/services/mount";
|
|
||||||
import type { Postgres } from "@/server/api/services/postgres";
|
|
||||||
import type { CreateServiceOptions } from "dockerode";
|
import type { CreateServiceOptions } from "dockerode";
|
||||||
import {
|
import {
|
||||||
calculateResources,
|
calculateResources,
|
||||||
@@ -9,12 +7,10 @@ import {
|
|||||||
prepareEnvironmentVariables,
|
prepareEnvironmentVariables,
|
||||||
} from "../docker/utils";
|
} from "../docker/utils";
|
||||||
import { getRemoteDocker } from "../servers/remote-docker";
|
import { getRemoteDocker } from "../servers/remote-docker";
|
||||||
|
import type { InferResultType } from "@/server/types/with";
|
||||||
|
|
||||||
type PostgresWithMounts = Postgres & {
|
export type PostgresNested = InferResultType<"postgres", { mounts: true }>;
|
||||||
mounts: Mount[];
|
export const buildPostgres = async (postgres: PostgresNested) => {
|
||||||
};
|
|
||||||
|
|
||||||
export const buildPostgres = async (postgres: PostgresWithMounts) => {
|
|
||||||
const {
|
const {
|
||||||
appName,
|
appName,
|
||||||
env,
|
env,
|
||||||
@@ -43,7 +39,7 @@ export const buildPostgres = async (postgres: PostgresWithMounts) => {
|
|||||||
const envVariables = prepareEnvironmentVariables(defaultPostgresEnv);
|
const envVariables = prepareEnvironmentVariables(defaultPostgresEnv);
|
||||||
const volumesMount = generateVolumeMounts(mounts);
|
const volumesMount = generateVolumeMounts(mounts);
|
||||||
const bindsMount = generateBindMounts(mounts);
|
const bindsMount = generateBindMounts(mounts);
|
||||||
const filesMount = generateFileMounts(appName, mounts);
|
const filesMount = generateFileMounts(appName, postgres);
|
||||||
|
|
||||||
const docker = await getRemoteDocker(postgres.serverId);
|
const docker = await getRemoteDocker(postgres.serverId);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
import type { Mount } from "@/server/api/services/mount";
|
|
||||||
import type { Redis } from "@/server/api/services/redis";
|
|
||||||
import { docker } from "@/server/constants";
|
|
||||||
import type { CreateServiceOptions } from "dockerode";
|
import type { CreateServiceOptions } from "dockerode";
|
||||||
import {
|
import {
|
||||||
calculateResources,
|
calculateResources,
|
||||||
@@ -10,12 +7,10 @@ import {
|
|||||||
prepareEnvironmentVariables,
|
prepareEnvironmentVariables,
|
||||||
} from "../docker/utils";
|
} from "../docker/utils";
|
||||||
import { getRemoteDocker } from "../servers/remote-docker";
|
import { getRemoteDocker } from "../servers/remote-docker";
|
||||||
|
import type { InferResultType } from "@/server/types/with";
|
||||||
|
|
||||||
type RedisWithMounts = Redis & {
|
export type RedisNested = InferResultType<"redis", { mounts: true }>;
|
||||||
mounts: Mount[];
|
export const buildRedis = async (redis: RedisNested) => {
|
||||||
};
|
|
||||||
|
|
||||||
export const buildRedis = async (redis: RedisWithMounts) => {
|
|
||||||
const {
|
const {
|
||||||
appName,
|
appName,
|
||||||
env,
|
env,
|
||||||
@@ -42,7 +37,7 @@ export const buildRedis = async (redis: RedisWithMounts) => {
|
|||||||
const envVariables = prepareEnvironmentVariables(defaultRedisEnv);
|
const envVariables = prepareEnvironmentVariables(defaultRedisEnv);
|
||||||
const volumesMount = generateVolumeMounts(mounts);
|
const volumesMount = generateVolumeMounts(mounts);
|
||||||
const bindsMount = generateBindMounts(mounts);
|
const bindsMount = generateBindMounts(mounts);
|
||||||
const filesMount = generateFileMounts(appName, mounts);
|
const filesMount = generateFileMounts(appName, redis);
|
||||||
|
|
||||||
const docker = await getRemoteDocker(redis.serverId);
|
const docker = await getRemoteDocker(redis.serverId);
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { writeFile } from "node:fs/promises";
|
|||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import type { Compose } from "@/server/api/services/compose";
|
import type { Compose } from "@/server/api/services/compose";
|
||||||
import type { Domain } from "@/server/api/services/domain";
|
import type { Domain } from "@/server/api/services/domain";
|
||||||
import { COMPOSE_PATH } from "@/server/constants";
|
import { paths } from "@/server/constants";
|
||||||
import { dump, load } from "js-yaml";
|
import { dump, load } from "js-yaml";
|
||||||
import { execAsyncRemote } from "../process/execAsync";
|
import { execAsyncRemote } from "../process/execAsync";
|
||||||
import {
|
import {
|
||||||
@@ -63,6 +63,7 @@ export const cloneComposeRemote = async (compose: Compose) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getComposePath = (compose: Compose) => {
|
export const getComposePath = (compose: Compose) => {
|
||||||
|
const { COMPOSE_PATH } = paths(!!compose.serverId);
|
||||||
const { appName, sourceType, composePath } = compose;
|
const { appName, sourceType, composePath } = compose;
|
||||||
let path = "";
|
let path = "";
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import type { Readable } from "node:stream";
|
import type { Readable } from "node:stream";
|
||||||
import { APPLICATIONS_PATH, docker } from "@/server/constants";
|
import { docker, paths } from "@/server/constants";
|
||||||
import type { ContainerInfo, ResourceRequirements } from "dockerode";
|
import type { ContainerInfo, ResourceRequirements } from "dockerode";
|
||||||
import { parse } from "dotenv";
|
import { parse } from "dotenv";
|
||||||
import type { ApplicationNested } from "../builders";
|
import type { ApplicationNested } from "../builders";
|
||||||
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
||||||
import { getRemoteDocker } from "../servers/remote-docker";
|
import { getRemoteDocker } from "../servers/remote-docker";
|
||||||
|
import type { MongoNested } from "../databases/mongo";
|
||||||
|
import type { MariadbNested } from "../databases/mariadb";
|
||||||
|
import type { MysqlNested } from "../databases/mysql";
|
||||||
|
import type { PostgresNested } from "../databases/postgres";
|
||||||
|
import type { RedisNested } from "../databases/redis";
|
||||||
|
|
||||||
interface RegistryAuth {
|
interface RegistryAuth {
|
||||||
username: string;
|
username: string;
|
||||||
@@ -378,8 +383,16 @@ export const generateBindMounts = (mounts: ApplicationNested["mounts"]) => {
|
|||||||
|
|
||||||
export const generateFileMounts = (
|
export const generateFileMounts = (
|
||||||
appName: string,
|
appName: string,
|
||||||
mounts: ApplicationNested["mounts"],
|
service:
|
||||||
|
| ApplicationNested
|
||||||
|
| MongoNested
|
||||||
|
| MariadbNested
|
||||||
|
| MysqlNested
|
||||||
|
| PostgresNested
|
||||||
|
| RedisNested,
|
||||||
) => {
|
) => {
|
||||||
|
const { mounts } = service;
|
||||||
|
const { APPLICATIONS_PATH } = paths(!!service.serverId);
|
||||||
if (!mounts || mounts.length === 0) {
|
if (!mounts || mounts.length === 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
import fs, { promises as fsPromises } from "node:fs";
|
import fs, { promises as fsPromises } from "node:fs";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import type { Application } from "@/server/api/services/application";
|
import type { Application } from "@/server/api/services/application";
|
||||||
import {
|
import { paths } from "@/server/constants";
|
||||||
APPLICATIONS_PATH,
|
|
||||||
COMPOSE_PATH,
|
|
||||||
MONITORING_PATH,
|
|
||||||
} from "@/server/constants";
|
|
||||||
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
||||||
|
|
||||||
export const recreateDirectory = async (pathFolder: string): Promise<void> => {
|
export const recreateDirectory = async (pathFolder: string): Promise<void> => {
|
||||||
@@ -52,6 +48,7 @@ export const removeDirectoryCode = async (
|
|||||||
appName: string,
|
appName: string,
|
||||||
serverId?: string | null,
|
serverId?: string | null,
|
||||||
) => {
|
) => {
|
||||||
|
const { APPLICATIONS_PATH } = paths(!!serverId);
|
||||||
const directoryPath = path.join(APPLICATIONS_PATH, appName);
|
const directoryPath = path.join(APPLICATIONS_PATH, appName);
|
||||||
const command = `rm -rf ${directoryPath}`;
|
const command = `rm -rf ${directoryPath}`;
|
||||||
try {
|
try {
|
||||||
@@ -70,6 +67,7 @@ export const removeComposeDirectory = async (
|
|||||||
appName: string,
|
appName: string,
|
||||||
serverId?: string | null,
|
serverId?: string | null,
|
||||||
) => {
|
) => {
|
||||||
|
const { COMPOSE_PATH } = paths(!!serverId);
|
||||||
const directoryPath = path.join(COMPOSE_PATH, appName);
|
const directoryPath = path.join(COMPOSE_PATH, appName);
|
||||||
const command = `rm -rf ${directoryPath}`;
|
const command = `rm -rf ${directoryPath}`;
|
||||||
try {
|
try {
|
||||||
@@ -88,6 +86,7 @@ export const removeMonitoringDirectory = async (
|
|||||||
appName: string,
|
appName: string,
|
||||||
serverId?: string | null,
|
serverId?: string | null,
|
||||||
) => {
|
) => {
|
||||||
|
const { MONITORING_PATH } = paths(!!serverId);
|
||||||
const directoryPath = path.join(MONITORING_PATH, appName);
|
const directoryPath = path.join(MONITORING_PATH, appName);
|
||||||
const command = `rm -rf ${directoryPath}`;
|
const command = `rm -rf ${directoryPath}`;
|
||||||
try {
|
try {
|
||||||
@@ -103,6 +102,7 @@ export const removeMonitoringDirectory = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getBuildAppDirectory = (application: Application) => {
|
export const getBuildAppDirectory = (application: Application) => {
|
||||||
|
const { APPLICATIONS_PATH } = paths(!!application.serverId);
|
||||||
const { appName, buildType, sourceType, customGitBuildPath, dockerfile } =
|
const { appName, buildType, sourceType, customGitBuildPath, dockerfile } =
|
||||||
application;
|
application;
|
||||||
let buildPath = "";
|
let buildPath = "";
|
||||||
@@ -132,6 +132,7 @@ export const getBuildAppDirectory = (application: Application) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getDockerContextPath = (application: Application) => {
|
export const getDockerContextPath = (application: Application) => {
|
||||||
|
const { APPLICATIONS_PATH } = paths(!!application.serverId);
|
||||||
const { appName, dockerContextPath } = application;
|
const { appName, dockerContextPath } = application;
|
||||||
|
|
||||||
if (!dockerContextPath) {
|
if (!dockerContextPath) {
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import * as fs from "node:fs";
|
import * as fs from "node:fs";
|
||||||
import * as path from "node:path";
|
import * as path from "node:path";
|
||||||
import { SSH_PATH } from "@/server/constants";
|
import { paths } from "@/server/constants";
|
||||||
import { spawnAsync } from "../process/spawnAsync";
|
import { spawnAsync } from "../process/spawnAsync";
|
||||||
|
|
||||||
export const readSSHKey = async (id: string) => {
|
export const readSSHKey = async (id: string) => {
|
||||||
|
const { SSH_PATH } = paths();
|
||||||
try {
|
try {
|
||||||
if (!fs.existsSync(SSH_PATH)) {
|
if (!fs.existsSync(SSH_PATH)) {
|
||||||
fs.mkdirSync(SSH_PATH, { recursive: true });
|
fs.mkdirSync(SSH_PATH, { recursive: true });
|
||||||
@@ -27,6 +28,7 @@ export const saveSSHKey = async (
|
|||||||
publicKey: string,
|
publicKey: string,
|
||||||
privateKey: string,
|
privateKey: string,
|
||||||
) => {
|
) => {
|
||||||
|
const { SSH_PATH } = paths();
|
||||||
const applicationDirectory = SSH_PATH;
|
const applicationDirectory = SSH_PATH;
|
||||||
|
|
||||||
const privateKeyPath = path.join(applicationDirectory, `${id}_rsa`);
|
const privateKeyPath = path.join(applicationDirectory, `${id}_rsa`);
|
||||||
@@ -42,6 +44,7 @@ export const saveSSHKey = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const generateSSHKey = async (type: "rsa" | "ed25519" = "rsa") => {
|
export const generateSSHKey = async (type: "rsa" | "ed25519" = "rsa") => {
|
||||||
|
const { SSH_PATH } = paths();
|
||||||
const applicationDirectory = SSH_PATH;
|
const applicationDirectory = SSH_PATH;
|
||||||
|
|
||||||
if (!fs.existsSync(applicationDirectory)) {
|
if (!fs.existsSync(applicationDirectory)) {
|
||||||
@@ -85,6 +88,7 @@ export const generateSSHKey = async (type: "rsa" | "ed25519" = "rsa") => {
|
|||||||
|
|
||||||
export const removeSSHKey = async (id: string) => {
|
export const removeSSHKey = async (id: string) => {
|
||||||
try {
|
try {
|
||||||
|
const { SSH_PATH } = paths();
|
||||||
const publicKeyPath = path.join(SSH_PATH, `${id}_rsa.pub`);
|
const publicKeyPath = path.join(SSH_PATH, `${id}_rsa.pub`);
|
||||||
const privateKeyPath = path.join(SSH_PATH, `${id}_rsa`);
|
const privateKeyPath = path.join(SSH_PATH, `${id}_rsa`);
|
||||||
await fs.promises.unlink(publicKeyPath);
|
await fs.promises.unlink(publicKeyPath);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { createWriteStream } from "node:fs";
|
|||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { findBitbucketById } from "@/server/api/services/bitbucket";
|
import { findBitbucketById } from "@/server/api/services/bitbucket";
|
||||||
import type { Compose } from "@/server/api/services/compose";
|
import type { Compose } from "@/server/api/services/compose";
|
||||||
import { APPLICATIONS_PATH, COMPOSE_PATH } from "@/server/constants";
|
import { paths } from "@/server/constants";
|
||||||
import type {
|
import type {
|
||||||
apiBitbucketTestConnection,
|
apiBitbucketTestConnection,
|
||||||
apiFindBitbucketBranches,
|
apiFindBitbucketBranches,
|
||||||
@@ -28,6 +28,7 @@ export const cloneBitbucketRepository = async (
|
|||||||
logPath: string,
|
logPath: string,
|
||||||
isCompose = false,
|
isCompose = false,
|
||||||
) => {
|
) => {
|
||||||
|
const { COMPOSE_PATH, APPLICATIONS_PATH } = paths();
|
||||||
const writeStream = createWriteStream(logPath, { flags: "a" });
|
const writeStream = createWriteStream(logPath, { flags: "a" });
|
||||||
const {
|
const {
|
||||||
appName,
|
appName,
|
||||||
@@ -80,6 +81,7 @@ export const cloneBitbucketRepository = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const cloneRawBitbucketRepository = async (entity: Compose) => {
|
export const cloneRawBitbucketRepository = async (entity: Compose) => {
|
||||||
|
const { COMPOSE_PATH } = paths();
|
||||||
const {
|
const {
|
||||||
appName,
|
appName,
|
||||||
bitbucketRepository,
|
bitbucketRepository,
|
||||||
@@ -119,6 +121,7 @@ export const cloneRawBitbucketRepository = async (entity: Compose) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const cloneRawBitbucketRepositoryRemote = async (compose: Compose) => {
|
export const cloneRawBitbucketRepositoryRemote = async (compose: Compose) => {
|
||||||
|
const { COMPOSE_PATH } = paths(true);
|
||||||
const {
|
const {
|
||||||
appName,
|
appName,
|
||||||
bitbucketRepository,
|
bitbucketRepository,
|
||||||
@@ -163,6 +166,7 @@ export const getBitbucketCloneCommand = async (
|
|||||||
logPath: string,
|
logPath: string,
|
||||||
isCompose = false,
|
isCompose = false,
|
||||||
) => {
|
) => {
|
||||||
|
const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(true);
|
||||||
const {
|
const {
|
||||||
appName,
|
appName,
|
||||||
bitbucketRepository,
|
bitbucketRepository,
|
||||||
@@ -193,7 +197,7 @@ export const getBitbucketCloneCommand = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const bitbucketProvider = await findBitbucketById(bitbucketId);
|
const bitbucketProvider = await findBitbucketById(bitbucketId);
|
||||||
const basePath = COMPOSE_PATH;
|
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
|
||||||
const outputPath = join(basePath, appName, "code");
|
const outputPath = join(basePath, appName, "code");
|
||||||
await recreateDirectory(outputPath);
|
await recreateDirectory(outputPath);
|
||||||
const repoclone = `bitbucket.org/${bitbucketOwner}/${bitbucketRepository}.git`;
|
const repoclone = `bitbucket.org/${bitbucketOwner}/${bitbucketRepository}.git`;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { createWriteStream } from "node:fs";
|
|||||||
import path, { join } from "node:path";
|
import path, { join } from "node:path";
|
||||||
import type { Compose } from "@/server/api/services/compose";
|
import type { Compose } from "@/server/api/services/compose";
|
||||||
import { updateSSHKeyById } from "@/server/api/services/ssh-key";
|
import { updateSSHKeyById } from "@/server/api/services/ssh-key";
|
||||||
import { APPLICATIONS_PATH, COMPOSE_PATH, SSH_PATH } from "@/server/constants";
|
import { paths } from "@/server/constants";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { recreateDirectory } from "../filesystem/directory";
|
import { recreateDirectory } from "../filesystem/directory";
|
||||||
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
||||||
@@ -18,6 +18,7 @@ export const cloneGitRepository = async (
|
|||||||
logPath: string,
|
logPath: string,
|
||||||
isCompose = false,
|
isCompose = false,
|
||||||
) => {
|
) => {
|
||||||
|
const { SSH_PATH, COMPOSE_PATH, APPLICATIONS_PATH } = paths();
|
||||||
const { appName, customGitUrl, customGitBranch, customGitSSHKeyId } = entity;
|
const { appName, customGitUrl, customGitBranch, customGitSSHKeyId } = entity;
|
||||||
|
|
||||||
if (!customGitUrl || !customGitBranch) {
|
if (!customGitUrl || !customGitBranch) {
|
||||||
@@ -99,6 +100,7 @@ export const getCustomGitCloneCommand = async (
|
|||||||
logPath: string,
|
logPath: string,
|
||||||
isCompose = false,
|
isCompose = false,
|
||||||
) => {
|
) => {
|
||||||
|
const { SSH_PATH, COMPOSE_PATH, APPLICATIONS_PATH } = paths(true);
|
||||||
const {
|
const {
|
||||||
appName,
|
appName,
|
||||||
customGitUrl,
|
customGitUrl,
|
||||||
@@ -168,6 +170,7 @@ const isHttpOrHttps = (url: string): boolean => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const addHostToKnownHosts = async (repositoryURL: string) => {
|
const addHostToKnownHosts = async (repositoryURL: string) => {
|
||||||
|
const { SSH_PATH } = paths();
|
||||||
const { domain, port } = sanitizeRepoPathSSH(repositoryURL);
|
const { domain, port } = sanitizeRepoPathSSH(repositoryURL);
|
||||||
const knownHostsPath = path.join(SSH_PATH, "known_hosts");
|
const knownHostsPath = path.join(SSH_PATH, "known_hosts");
|
||||||
|
|
||||||
@@ -181,6 +184,7 @@ const addHostToKnownHosts = async (repositoryURL: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const addHostToKnownHostsCommand = (repositoryURL: string) => {
|
const addHostToKnownHostsCommand = (repositoryURL: string) => {
|
||||||
|
const { SSH_PATH } = paths();
|
||||||
const { domain, port } = sanitizeRepoPathSSH(repositoryURL);
|
const { domain, port } = sanitizeRepoPathSSH(repositoryURL);
|
||||||
const knownHostsPath = path.join(SSH_PATH, "known_hosts");
|
const knownHostsPath = path.join(SSH_PATH, "known_hosts");
|
||||||
|
|
||||||
@@ -237,6 +241,7 @@ export const cloneGitRawRepository = async (entity: {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { SSH_PATH, COMPOSE_PATH } = paths();
|
||||||
const keyPath = path.join(SSH_PATH, `${customGitSSHKeyId}_rsa`);
|
const keyPath = path.join(SSH_PATH, `${customGitSSHKeyId}_rsa`);
|
||||||
const basePath = COMPOSE_PATH;
|
const basePath = COMPOSE_PATH;
|
||||||
const outputPath = join(basePath, appName, "code");
|
const outputPath = join(basePath, appName, "code");
|
||||||
@@ -302,6 +307,7 @@ export const cloneRawGitRepositoryRemote = async (compose: Compose) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { SSH_PATH, COMPOSE_PATH } = paths(true);
|
||||||
const keyPath = path.join(SSH_PATH, `${customGitSSHKeyId}_rsa`);
|
const keyPath = path.join(SSH_PATH, `${customGitSSHKeyId}_rsa`);
|
||||||
const basePath = COMPOSE_PATH;
|
const basePath = COMPOSE_PATH;
|
||||||
const outputPath = join(basePath, appName, "code");
|
const outputPath = join(basePath, appName, "code");
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { createWriteStream } from "node:fs";
|
import { createWriteStream } from "node:fs";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { APPLICATIONS_PATH, COMPOSE_PATH } from "@/server/constants";
|
import { paths } from "@/server/constants";
|
||||||
import type { InferResultType } from "@/server/types/with";
|
import type { InferResultType } from "@/server/types/with";
|
||||||
import { createAppAuth } from "@octokit/auth-app";
|
import { createAppAuth } from "@octokit/auth-app";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
@@ -79,6 +79,7 @@ export const cloneGithubRepository = async (
|
|||||||
logPath: string,
|
logPath: string,
|
||||||
isCompose = false,
|
isCompose = false,
|
||||||
) => {
|
) => {
|
||||||
|
const { APPLICATIONS_PATH, COMPOSE_PATH } = paths();
|
||||||
const writeStream = createWriteStream(logPath, { flags: "a" });
|
const writeStream = createWriteStream(logPath, { flags: "a" });
|
||||||
const { appName, repository, owner, branch, githubId } = entity;
|
const { appName, repository, owner, branch, githubId } = entity;
|
||||||
|
|
||||||
@@ -191,7 +192,7 @@ export const getGithubCloneCommand = async (
|
|||||||
await execAsyncRemote(serverId, bashCommand);
|
await execAsyncRemote(serverId, bashCommand);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(true);
|
||||||
const githubProvider = await findGithubById(githubId);
|
const githubProvider = await findGithubById(githubId);
|
||||||
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
|
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
|
||||||
const outputPath = join(basePath, appName, "code");
|
const outputPath = join(basePath, appName, "code");
|
||||||
@@ -222,6 +223,7 @@ export const cloneRawGithubRepository = async (entity: Compose) => {
|
|||||||
message: "GitHub Provider not found",
|
message: "GitHub Provider not found",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const { COMPOSE_PATH } = paths();
|
||||||
const githubProvider = await findGithubById(githubId);
|
const githubProvider = await findGithubById(githubId);
|
||||||
const basePath = COMPOSE_PATH;
|
const basePath = COMPOSE_PATH;
|
||||||
const outputPath = join(basePath, appName, "code");
|
const outputPath = join(basePath, appName, "code");
|
||||||
@@ -261,6 +263,8 @@ export const cloneRawGithubRepositoryRemote = async (compose: Compose) => {
|
|||||||
message: "GitHub Provider not found",
|
message: "GitHub Provider not found",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { COMPOSE_PATH } = paths(true);
|
||||||
const githubProvider = await findGithubById(githubId);
|
const githubProvider = await findGithubById(githubId);
|
||||||
const basePath = COMPOSE_PATH;
|
const basePath = COMPOSE_PATH;
|
||||||
const outputPath = join(basePath, appName, "code");
|
const outputPath = join(basePath, appName, "code");
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
findGitlabById,
|
findGitlabById,
|
||||||
updateGitlab,
|
updateGitlab,
|
||||||
} from "@/server/api/services/gitlab";
|
} from "@/server/api/services/gitlab";
|
||||||
import { APPLICATIONS_PATH, COMPOSE_PATH } from "@/server/constants";
|
import { paths } from "@/server/constants";
|
||||||
import type { apiGitlabTestConnection } from "@/server/db/schema";
|
import type { apiGitlabTestConnection } from "@/server/db/schema";
|
||||||
import type { InferResultType } from "@/server/types/with";
|
import type { InferResultType } from "@/server/types/with";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
@@ -119,6 +119,8 @@ export const cloneGitlabRepository = async (
|
|||||||
message: "Error: GitLab repository information is incomplete.",
|
message: "Error: GitLab repository information is incomplete.",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { COMPOSE_PATH, APPLICATIONS_PATH } = paths();
|
||||||
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
|
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
|
||||||
const outputPath = join(basePath, appName, "code");
|
const outputPath = join(basePath, appName, "code");
|
||||||
await recreateDirectory(outputPath);
|
await recreateDirectory(outputPath);
|
||||||
@@ -212,6 +214,7 @@ export const getGitlabCloneCommand = async (
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(true);
|
||||||
await refreshGitlabToken(gitlabId);
|
await refreshGitlabToken(gitlabId);
|
||||||
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
|
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
|
||||||
const outputPath = join(basePath, appName, "code");
|
const outputPath = join(basePath, appName, "code");
|
||||||
@@ -343,7 +346,7 @@ export const cloneRawGitlabRepository = async (entity: Compose) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const gitlabProvider = await findGitlabById(gitlabId);
|
const gitlabProvider = await findGitlabById(gitlabId);
|
||||||
|
const { COMPOSE_PATH } = paths();
|
||||||
await refreshGitlabToken(gitlabId);
|
await refreshGitlabToken(gitlabId);
|
||||||
const basePath = COMPOSE_PATH;
|
const basePath = COMPOSE_PATH;
|
||||||
const outputPath = join(basePath, appName, "code");
|
const outputPath = join(basePath, appName, "code");
|
||||||
@@ -383,7 +386,7 @@ export const cloneRawGitlabRepositoryRemote = async (compose: Compose) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
const gitlabProvider = await findGitlabById(gitlabId);
|
const gitlabProvider = await findGitlabById(gitlabId);
|
||||||
|
const { COMPOSE_PATH } = paths(true);
|
||||||
await refreshGitlabToken(gitlabId);
|
await refreshGitlabToken(gitlabId);
|
||||||
const basePath = COMPOSE_PATH;
|
const basePath = COMPOSE_PATH;
|
||||||
const outputPath = join(basePath, appName, "code");
|
const outputPath = join(basePath, appName, "code");
|
||||||
|
|||||||
@@ -2,12 +2,13 @@ import { createWriteStream } from "node:fs";
|
|||||||
import { writeFile } from "node:fs/promises";
|
import { writeFile } from "node:fs/promises";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import type { Compose } from "@/server/api/services/compose";
|
import type { Compose } from "@/server/api/services/compose";
|
||||||
import { COMPOSE_PATH } from "@/server/constants";
|
import { paths } from "@/server/constants";
|
||||||
import { encodeBase64 } from "../docker/utils";
|
import { encodeBase64 } from "../docker/utils";
|
||||||
import { recreateDirectory } from "../filesystem/directory";
|
import { recreateDirectory } from "../filesystem/directory";
|
||||||
import { execAsyncRemote } from "../process/execAsync";
|
import { execAsyncRemote } from "../process/execAsync";
|
||||||
|
|
||||||
export const createComposeFile = async (compose: Compose, logPath: string) => {
|
export const createComposeFile = async (compose: Compose, logPath: string) => {
|
||||||
|
const { COMPOSE_PATH } = paths();
|
||||||
const { appName, composeFile } = compose;
|
const { appName, composeFile } = compose;
|
||||||
const writeStream = createWriteStream(logPath, { flags: "a" });
|
const writeStream = createWriteStream(logPath, { flags: "a" });
|
||||||
const outputPath = join(COMPOSE_PATH, appName, "code");
|
const outputPath = join(COMPOSE_PATH, appName, "code");
|
||||||
@@ -33,6 +34,7 @@ export const getCreateComposeFileCommand = (
|
|||||||
compose: Compose,
|
compose: Compose,
|
||||||
logPath: string,
|
logPath: string,
|
||||||
) => {
|
) => {
|
||||||
|
const { COMPOSE_PATH } = paths(true);
|
||||||
const { appName, composeFile } = compose;
|
const { appName, composeFile } = compose;
|
||||||
const outputPath = join(COMPOSE_PATH, appName, "code");
|
const outputPath = join(COMPOSE_PATH, appName, "code");
|
||||||
const filePath = join(outputPath, "docker-compose.yml");
|
const filePath = join(outputPath, "docker-compose.yml");
|
||||||
@@ -47,6 +49,7 @@ export const getCreateComposeFileCommand = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const createComposeFileRaw = async (compose: Compose) => {
|
export const createComposeFileRaw = async (compose: Compose) => {
|
||||||
|
const { COMPOSE_PATH } = paths();
|
||||||
const { appName, composeFile } = compose;
|
const { appName, composeFile } = compose;
|
||||||
const outputPath = join(COMPOSE_PATH, appName, "code");
|
const outputPath = join(COMPOSE_PATH, appName, "code");
|
||||||
const filePath = join(outputPath, "docker-compose.yml");
|
const filePath = join(outputPath, "docker-compose.yml");
|
||||||
@@ -59,6 +62,7 @@ export const createComposeFileRaw = async (compose: Compose) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const createComposeFileRawRemote = async (compose: Compose) => {
|
export const createComposeFileRawRemote = async (compose: Compose) => {
|
||||||
|
const { COMPOSE_PATH } = paths(true);
|
||||||
const { appName, composeFile, serverId } = compose;
|
const { appName, composeFile, serverId } = compose;
|
||||||
const outputPath = join(COMPOSE_PATH, appName, "code");
|
const outputPath = join(COMPOSE_PATH, appName, "code");
|
||||||
const filePath = join(outputPath, "docker-compose.yml");
|
const filePath = join(outputPath, "docker-compose.yml");
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
updateDeploymentStatus,
|
updateDeploymentStatus,
|
||||||
} from "@/server/api/services/deployment";
|
} from "@/server/api/services/deployment";
|
||||||
import { findServerById } from "@/server/api/services/server";
|
import { findServerById } from "@/server/api/services/server";
|
||||||
import { LOGS_PATH, SSH_PATH, getPaths } from "@/server/constants";
|
import { paths } from "@/server/constants";
|
||||||
import {
|
import {
|
||||||
getDefaultMiddlewares,
|
getDefaultMiddlewares,
|
||||||
getDefaultServerTraefikConfig,
|
getDefaultServerTraefikConfig,
|
||||||
@@ -17,6 +17,7 @@ import { readSSHKey } from "../filesystem/ssh";
|
|||||||
|
|
||||||
export const setupServer = async (serverId: string) => {
|
export const setupServer = async (serverId: string) => {
|
||||||
const server = await findServerById(serverId);
|
const server = await findServerById(serverId);
|
||||||
|
const { LOGS_PATH } = paths();
|
||||||
|
|
||||||
const slugifyName = slugify(`server ${server.name}`);
|
const slugifyName = slugify(`server ${server.name}`);
|
||||||
|
|
||||||
@@ -29,8 +30,8 @@ export const setupServer = async (serverId: string) => {
|
|||||||
title: "Setup Server",
|
title: "Setup Server",
|
||||||
description: "Setup Server",
|
description: "Setup Server",
|
||||||
});
|
});
|
||||||
const writeStream = createWriteStream(deployment.logPath, { flags: "a" });
|
|
||||||
|
|
||||||
|
const writeStream = createWriteStream(deployment.logPath, { flags: "a" });
|
||||||
try {
|
try {
|
||||||
writeStream.write("\nInstalling Server Dependencies: ✅\n");
|
writeStream.write("\nInstalling Server Dependencies: ✅\n");
|
||||||
await connectToServer(serverId, deployment.logPath);
|
await connectToServer(serverId, deployment.logPath);
|
||||||
@@ -50,8 +51,18 @@ const connectToServer = async (serverId: string, logPath: string) => {
|
|||||||
const writeStream = createWriteStream(logPath, { flags: "a" });
|
const writeStream = createWriteStream(logPath, { flags: "a" });
|
||||||
const client = new Client();
|
const client = new Client();
|
||||||
const server = await findServerById(serverId);
|
const server = await findServerById(serverId);
|
||||||
if (!server.sshKeyId) return;
|
if (!server.sshKeyId) {
|
||||||
|
writeStream.write("❌ No SSH Key found");
|
||||||
|
writeStream.close();
|
||||||
|
throw new Error("No SSH Key found");
|
||||||
|
}
|
||||||
const keys = await readSSHKey(server.sshKeyId);
|
const keys = await readSSHKey(server.sshKeyId);
|
||||||
|
|
||||||
|
if (!keys.privateKey) {
|
||||||
|
writeStream.write("❌ No SSH Key found");
|
||||||
|
writeStream.close();
|
||||||
|
throw new Error("No SSH Key found");
|
||||||
|
}
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
client
|
client
|
||||||
.once("ready", () => {
|
.once("ready", () => {
|
||||||
@@ -106,23 +117,12 @@ const connectToServer = async (serverId: string, logPath: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const setupDirectories = () => {
|
const setupDirectories = () => {
|
||||||
// const directories = [
|
const { SSH_PATH } = paths(true);
|
||||||
// BASE_PATH,
|
const directories = Object.values(paths(true));
|
||||||
// MAIN_TRAEFIK_PATH,
|
|
||||||
// DYNAMIC_TRAEFIK_PATH,
|
|
||||||
// LOGS_PATH,
|
|
||||||
// APPLICATIONS_PATH,
|
|
||||||
// SSH_PATH,
|
|
||||||
// CERTIFICATES_PATH,
|
|
||||||
// MONITORING_PATH,
|
|
||||||
// ];
|
|
||||||
|
|
||||||
const directories = getPaths("/etc/dokploy");
|
|
||||||
|
|
||||||
const createDirsCommand = directories
|
const createDirsCommand = directories
|
||||||
.map((dir) => `mkdir -p "${dir}"`)
|
.map((dir) => `mkdir -p "${dir}"`)
|
||||||
.join(" && ");
|
.join(" && ");
|
||||||
|
|
||||||
const chmodCommand = `chmod 700 "${SSH_PATH}"`;
|
const chmodCommand = `chmod 700 "${SSH_PATH}"`;
|
||||||
|
|
||||||
const command = `
|
const command = `
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import fs, { writeFileSync } from "node:fs";
|
import fs, { writeFileSync } from "node:fs";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import type { Domain } from "@/server/api/services/domain";
|
import type { Domain } from "@/server/api/services/domain";
|
||||||
import { DYNAMIC_TRAEFIK_PATH } from "@/server/constants";
|
import { paths } from "@/server/constants";
|
||||||
import { dump, load } from "js-yaml";
|
import { dump, load } from "js-yaml";
|
||||||
import { execAsyncRemote } from "../process/execAsync";
|
import { execAsyncRemote } from "../process/execAsync";
|
||||||
import type { FileConfig, HttpLoadBalancerService } from "./file-types";
|
import type { FileConfig, HttpLoadBalancerService } from "./file-types";
|
||||||
@@ -39,6 +39,7 @@ export const createTraefikConfig = (appName: string) => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
const yamlStr = dump(config);
|
const yamlStr = dump(config);
|
||||||
|
const { DYNAMIC_TRAEFIK_PATH } = paths();
|
||||||
fs.mkdirSync(DYNAMIC_TRAEFIK_PATH, { recursive: true });
|
fs.mkdirSync(DYNAMIC_TRAEFIK_PATH, { recursive: true });
|
||||||
writeFileSync(
|
writeFileSync(
|
||||||
path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`),
|
path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`),
|
||||||
@@ -52,6 +53,7 @@ export const removeTraefikConfig = async (
|
|||||||
serverId?: string | null,
|
serverId?: string | null,
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
|
const { DYNAMIC_TRAEFIK_PATH } = paths(!!serverId);
|
||||||
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
||||||
|
|
||||||
if (serverId) {
|
if (serverId) {
|
||||||
@@ -72,12 +74,14 @@ export const removeTraefikConfigRemote = async (
|
|||||||
serverId: string,
|
serverId: string,
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
|
const { DYNAMIC_TRAEFIK_PATH } = paths(true);
|
||||||
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
||||||
await execAsyncRemote(serverId, `rm ${configPath}`);
|
await execAsyncRemote(serverId, `rm ${configPath}`);
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const loadOrCreateConfig = (appName: string): FileConfig => {
|
export const loadOrCreateConfig = (appName: string): FileConfig => {
|
||||||
|
const { DYNAMIC_TRAEFIK_PATH } = paths();
|
||||||
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
||||||
if (fs.existsSync(configPath)) {
|
if (fs.existsSync(configPath)) {
|
||||||
const yamlStr = fs.readFileSync(configPath, "utf8");
|
const yamlStr = fs.readFileSync(configPath, "utf8");
|
||||||
@@ -93,6 +97,7 @@ export const loadOrCreateConfigRemote = async (
|
|||||||
serverId: string,
|
serverId: string,
|
||||||
appName: string,
|
appName: string,
|
||||||
) => {
|
) => {
|
||||||
|
const { DYNAMIC_TRAEFIK_PATH } = paths(true);
|
||||||
const fileConfig: FileConfig = { http: { routers: {}, services: {} } };
|
const fileConfig: FileConfig = { http: { routers: {}, services: {} } };
|
||||||
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
||||||
try {
|
try {
|
||||||
@@ -110,6 +115,7 @@ export const loadOrCreateConfigRemote = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const readConfig = (appName: string) => {
|
export const readConfig = (appName: string) => {
|
||||||
|
const { DYNAMIC_TRAEFIK_PATH } = paths();
|
||||||
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
||||||
if (fs.existsSync(configPath)) {
|
if (fs.existsSync(configPath)) {
|
||||||
const yamlStr = fs.readFileSync(configPath, "utf8");
|
const yamlStr = fs.readFileSync(configPath, "utf8");
|
||||||
@@ -119,6 +125,7 @@ export const readConfig = (appName: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const readRemoteConfig = async (serverId: string, appName: string) => {
|
export const readRemoteConfig = async (serverId: string, appName: string) => {
|
||||||
|
const { DYNAMIC_TRAEFIK_PATH } = paths(true);
|
||||||
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
||||||
try {
|
try {
|
||||||
const { stdout } = await execAsyncRemote(serverId, `cat ${configPath}`);
|
const { stdout } = await execAsyncRemote(serverId, `cat ${configPath}`);
|
||||||
@@ -130,6 +137,7 @@ export const readRemoteConfig = async (serverId: string, appName: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const readMonitoringConfig = () => {
|
export const readMonitoringConfig = () => {
|
||||||
|
const { DYNAMIC_TRAEFIK_PATH } = paths();
|
||||||
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, "access.log");
|
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, "access.log");
|
||||||
if (fs.existsSync(configPath)) {
|
if (fs.existsSync(configPath)) {
|
||||||
const yamlStr = fs.readFileSync(configPath, "utf8");
|
const yamlStr = fs.readFileSync(configPath, "utf8");
|
||||||
@@ -149,6 +157,7 @@ export const readConfigInPath = (pathFile: string) => {
|
|||||||
|
|
||||||
export const writeConfig = (appName: string, traefikConfig: string) => {
|
export const writeConfig = (appName: string, traefikConfig: string) => {
|
||||||
try {
|
try {
|
||||||
|
const { DYNAMIC_TRAEFIK_PATH } = paths();
|
||||||
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
||||||
fs.writeFileSync(configPath, traefikConfig, "utf8");
|
fs.writeFileSync(configPath, traefikConfig, "utf8");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -162,6 +171,7 @@ export const writeConfigRemote = async (
|
|||||||
traefikConfig: string,
|
traefikConfig: string,
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
|
const { DYNAMIC_TRAEFIK_PATH } = paths(true);
|
||||||
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
||||||
await execAsyncRemote(serverId, `echo '${traefikConfig}' > ${configPath}`);
|
await execAsyncRemote(serverId, `echo '${traefikConfig}' > ${configPath}`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -186,6 +196,7 @@ export const writeTraefikConfig = (
|
|||||||
appName: string,
|
appName: string,
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
|
const { DYNAMIC_TRAEFIK_PATH } = paths();
|
||||||
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
||||||
const yamlStr = dump(traefikConfig);
|
const yamlStr = dump(traefikConfig);
|
||||||
fs.writeFileSync(configPath, yamlStr, "utf8");
|
fs.writeFileSync(configPath, yamlStr, "utf8");
|
||||||
@@ -200,6 +211,7 @@ export const writeTraefikConfigRemote = async (
|
|||||||
serverId: string,
|
serverId: string,
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
|
const { DYNAMIC_TRAEFIK_PATH } = paths(true);
|
||||||
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
||||||
const yamlStr = dump(traefikConfig);
|
const yamlStr = dump(traefikConfig);
|
||||||
await execAsyncRemote(serverId, `echo '${yamlStr}' > ${configPath}`);
|
await execAsyncRemote(serverId, `echo '${yamlStr}' > ${configPath}`);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { DYNAMIC_TRAEFIK_PATH } from "@/server/constants";
|
import { paths } from "@/server/constants";
|
||||||
import { dump, load } from "js-yaml";
|
import { dump, load } from "js-yaml";
|
||||||
import type { ApplicationNested } from "../builders";
|
import type { ApplicationNested } from "../builders";
|
||||||
import { execAsyncRemote } from "../process/execAsync";
|
import { execAsyncRemote } from "../process/execAsync";
|
||||||
@@ -69,6 +69,7 @@ export const deleteAllMiddlewares = async (application: ApplicationNested) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const loadMiddlewares = <T>() => {
|
export const loadMiddlewares = <T>() => {
|
||||||
|
const { DYNAMIC_TRAEFIK_PATH } = paths();
|
||||||
const configPath = join(DYNAMIC_TRAEFIK_PATH, "middlewares.yml");
|
const configPath = join(DYNAMIC_TRAEFIK_PATH, "middlewares.yml");
|
||||||
if (!existsSync(configPath)) {
|
if (!existsSync(configPath)) {
|
||||||
throw new Error(`File not found: ${configPath}`);
|
throw new Error(`File not found: ${configPath}`);
|
||||||
@@ -79,6 +80,7 @@ export const loadMiddlewares = <T>() => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const loadRemoteMiddlewares = async (serverId: string) => {
|
export const loadRemoteMiddlewares = async (serverId: string) => {
|
||||||
|
const { DYNAMIC_TRAEFIK_PATH } = paths(true);
|
||||||
const configPath = join(DYNAMIC_TRAEFIK_PATH, "middlewares.yml");
|
const configPath = join(DYNAMIC_TRAEFIK_PATH, "middlewares.yml");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -98,6 +100,7 @@ export const loadRemoteMiddlewares = async (serverId: string) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
export const writeMiddleware = <T>(config: T) => {
|
export const writeMiddleware = <T>(config: T) => {
|
||||||
|
const { DYNAMIC_TRAEFIK_PATH } = paths();
|
||||||
const configPath = join(DYNAMIC_TRAEFIK_PATH, "middlewares.yml");
|
const configPath = join(DYNAMIC_TRAEFIK_PATH, "middlewares.yml");
|
||||||
const newYamlContent = dump(config);
|
const newYamlContent = dump(config);
|
||||||
writeFileSync(configPath, newYamlContent, "utf8");
|
writeFileSync(configPath, newYamlContent, "utf8");
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import type { Registry } from "@/server/api/services/registry";
|
import type { Registry } from "@/server/api/services/registry";
|
||||||
import { REGISTRY_PATH } from "@/server/constants";
|
import { paths } from "@/server/constants";
|
||||||
import { dump, load } from "js-yaml";
|
import { dump, load } from "js-yaml";
|
||||||
import { removeDirectoryIfExistsContent } from "../filesystem/directory";
|
import { removeDirectoryIfExistsContent } from "../filesystem/directory";
|
||||||
import type { FileConfig, HttpRouter } from "./file-types";
|
import type { FileConfig, HttpRouter } from "./file-types";
|
||||||
|
|
||||||
export const manageRegistry = async (registry: Registry) => {
|
export const manageRegistry = async (registry: Registry) => {
|
||||||
|
const { REGISTRY_PATH } = paths();
|
||||||
if (!existsSync(REGISTRY_PATH)) {
|
if (!existsSync(REGISTRY_PATH)) {
|
||||||
mkdirSync(REGISTRY_PATH, { recursive: true });
|
mkdirSync(REGISTRY_PATH, { recursive: true });
|
||||||
}
|
}
|
||||||
@@ -36,6 +37,7 @@ export const manageRegistry = async (registry: Registry) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const removeSelfHostedRegistry = async () => {
|
export const removeSelfHostedRegistry = async () => {
|
||||||
|
const { REGISTRY_PATH } = paths();
|
||||||
await removeDirectoryIfExistsContent(REGISTRY_PATH);
|
await removeDirectoryIfExistsContent(REGISTRY_PATH);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -60,6 +62,7 @@ const createRegistryRouterConfig = async (registry: Registry) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const loadOrCreateConfig = (): FileConfig => {
|
const loadOrCreateConfig = (): FileConfig => {
|
||||||
|
const { REGISTRY_PATH } = paths();
|
||||||
const configPath = join(REGISTRY_PATH, "registry.yml");
|
const configPath = join(REGISTRY_PATH, "registry.yml");
|
||||||
if (existsSync(configPath)) {
|
if (existsSync(configPath)) {
|
||||||
const yamlStr = readFileSync(configPath, "utf8");
|
const yamlStr = readFileSync(configPath, "utf8");
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import type { Admin } from "@/server/api/services/admin";
|
import type { Admin } from "@/server/api/services/admin";
|
||||||
import { MAIN_TRAEFIK_PATH } from "@/server/constants";
|
import { paths } from "@/server/constants";
|
||||||
import { dump, load } from "js-yaml";
|
import { dump, load } from "js-yaml";
|
||||||
import { loadOrCreateConfig, writeTraefikConfig } from "./application";
|
import { loadOrCreateConfig, writeTraefikConfig } from "./application";
|
||||||
import type { FileConfig } from "./file-types";
|
import type { FileConfig } from "./file-types";
|
||||||
@@ -42,6 +42,7 @@ export const updateServerTraefik = (
|
|||||||
export const updateLetsEncryptEmail = (newEmail: string | null) => {
|
export const updateLetsEncryptEmail = (newEmail: string | null) => {
|
||||||
try {
|
try {
|
||||||
if (!newEmail) return;
|
if (!newEmail) return;
|
||||||
|
const { MAIN_TRAEFIK_PATH } = paths();
|
||||||
const configPath = join(MAIN_TRAEFIK_PATH, "traefik.yml");
|
const configPath = join(MAIN_TRAEFIK_PATH, "traefik.yml");
|
||||||
const configContent = readFileSync(configPath, "utf8");
|
const configContent = readFileSync(configPath, "utf8");
|
||||||
const config = load(configContent) as MainTraefikConfig;
|
const config = load(configContent) as MainTraefikConfig;
|
||||||
@@ -58,6 +59,7 @@ export const updateLetsEncryptEmail = (newEmail: string | null) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const readMainConfig = () => {
|
export const readMainConfig = () => {
|
||||||
|
const { MAIN_TRAEFIK_PATH } = paths();
|
||||||
const configPath = join(MAIN_TRAEFIK_PATH, "traefik.yml");
|
const configPath = join(MAIN_TRAEFIK_PATH, "traefik.yml");
|
||||||
if (existsSync(configPath)) {
|
if (existsSync(configPath)) {
|
||||||
const yamlStr = readFileSync(configPath, "utf8");
|
const yamlStr = readFileSync(configPath, "utf8");
|
||||||
@@ -68,6 +70,7 @@ export const readMainConfig = () => {
|
|||||||
|
|
||||||
export const writeMainConfig = (traefikConfig: string) => {
|
export const writeMainConfig = (traefikConfig: string) => {
|
||||||
try {
|
try {
|
||||||
|
const { MAIN_TRAEFIK_PATH } = paths();
|
||||||
const configPath = join(MAIN_TRAEFIK_PATH, "traefik.yml");
|
const configPath = join(MAIN_TRAEFIK_PATH, "traefik.yml");
|
||||||
writeFileSync(configPath, traefikConfig, "utf8");
|
writeFileSync(configPath, traefikConfig, "utf8");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -43,8 +43,11 @@ export const setupDeploymentLogsWebSocketServer = (
|
|||||||
ws.close();
|
ws.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
console.log(serverId);
|
||||||
if (serverId) {
|
if (serverId) {
|
||||||
|
console.log("Entre aca");
|
||||||
const server = await findServerById(serverId);
|
const server = await findServerById(serverId);
|
||||||
|
|
||||||
if (!server.sshKeyId) return;
|
if (!server.sshKeyId) return;
|
||||||
@@ -87,6 +90,8 @@ export const setupDeploymentLogsWebSocketServer = (
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
console.log("Entre aca2");
|
||||||
|
console.log(logPath);
|
||||||
const tail = spawn("tail", ["-n", "+1", "-f", logPath]);
|
const tail = spawn("tail", ["-n", "+1", "-f", logPath]);
|
||||||
|
|
||||||
tail.stdout.on("data", (data) => {
|
tail.stdout.on("data", (data) => {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { publicIpv4, publicIpv6 } from "public-ip";
|
|||||||
import { WebSocketServer } from "ws";
|
import { WebSocketServer } from "ws";
|
||||||
import { findServerById } from "../api/services/server";
|
import { findServerById } from "../api/services/server";
|
||||||
import { validateWebSocketRequest } from "../auth/auth";
|
import { validateWebSocketRequest } from "../auth/auth";
|
||||||
import { SSH_PATH } from "../constants";
|
import { paths } from "../constants";
|
||||||
|
|
||||||
export const getPublicIpWithFallback = async () => {
|
export const getPublicIpWithFallback = async () => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -64,7 +64,7 @@ export const setupTerminalWebSocketServer = (
|
|||||||
ws.close();
|
ws.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const { SSH_PATH } = paths();
|
||||||
const privateKey = path.join(SSH_PATH, `${server.sshKeyId}_rsa`);
|
const privateKey = path.join(SSH_PATH, `${server.sshKeyId}_rsa`);
|
||||||
const sshCommand = [
|
const sshCommand = [
|
||||||
"ssh",
|
"ssh",
|
||||||
|
|||||||
@@ -30,8 +30,7 @@ export const generateRandomDomain = ({
|
|||||||
const hash = randomBytes(3).toString("hex");
|
const hash = randomBytes(3).toString("hex");
|
||||||
const slugIp = serverIp.replaceAll(".", "-");
|
const slugIp = serverIp.replaceAll(".", "-");
|
||||||
|
|
||||||
return "";
|
return `${projectName}-${hash}${slugIp === "" ? "" : `-${slugIp}`}.traefik.me`;
|
||||||
// return `${projectName}-${hash}${process.env.NODE_ENV === "production" || IS_CLOUD ? `-${slugIp}` : ""}.traefik.me`;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const generateHash = (projectName: string, quantity = 3): string => {
|
export const generateHash = (projectName: string, quantity = 3): string => {
|
||||||
|
|||||||
Reference in New Issue
Block a user