mirror of
https://github.com/LukeHagar/dokploy.git
synced 2025-12-10 04:19:48 +00:00
284 lines
7.5 KiB
TypeScript
284 lines
7.5 KiB
TypeScript
import fs, { createReadStream, writeFileSync } from "node:fs";
|
|
import path from "node:path";
|
|
import { createInterface } from "node:readline";
|
|
import { paths } from "@dokploy/server/constants";
|
|
import type { Domain } from "@dokploy/server/services/domain";
|
|
import { parse, stringify } from "yaml";
|
|
import { encodeBase64 } from "../docker/utils";
|
|
import { execAsyncRemote } from "../process/execAsync";
|
|
import type { FileConfig, HttpLoadBalancerService } from "./file-types";
|
|
|
|
export const createTraefikConfig = (appName: string) => {
|
|
const defaultPort = 3000;
|
|
const serviceURLDefault = `http://${appName}:${defaultPort}`;
|
|
const domainDefault = `Host(\`${appName}.docker.localhost\`)`;
|
|
const config: FileConfig = {
|
|
http: {
|
|
routers: {
|
|
...(process.env.NODE_ENV === "production"
|
|
? {}
|
|
: {
|
|
[`${appName}-router-1`]: {
|
|
rule: domainDefault,
|
|
service: `${appName}-service-1`,
|
|
entryPoints: ["web"],
|
|
},
|
|
}),
|
|
},
|
|
|
|
services: {
|
|
...(process.env.NODE_ENV === "production"
|
|
? {}
|
|
: {
|
|
[`${appName}-service-1`]: {
|
|
loadBalancer: {
|
|
servers: [{ url: serviceURLDefault }],
|
|
passHostHeader: true,
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
};
|
|
const yamlStr = stringify(config);
|
|
const { DYNAMIC_TRAEFIK_PATH } = paths();
|
|
fs.mkdirSync(DYNAMIC_TRAEFIK_PATH, { recursive: true });
|
|
writeFileSync(
|
|
path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`),
|
|
yamlStr,
|
|
"utf8",
|
|
);
|
|
};
|
|
|
|
export const removeTraefikConfig = async (
|
|
appName: string,
|
|
serverId?: string | null,
|
|
) => {
|
|
try {
|
|
const { DYNAMIC_TRAEFIK_PATH } = paths(!!serverId);
|
|
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
|
|
|
if (serverId) {
|
|
await execAsyncRemote(serverId, `rm ${configPath}`);
|
|
} else {
|
|
if (fs.existsSync(configPath)) {
|
|
await fs.promises.unlink(configPath);
|
|
}
|
|
}
|
|
if (fs.existsSync(configPath)) {
|
|
await fs.promises.unlink(configPath);
|
|
}
|
|
} catch {}
|
|
};
|
|
|
|
export const removeTraefikConfigRemote = async (
|
|
appName: string,
|
|
serverId: string,
|
|
) => {
|
|
try {
|
|
const { DYNAMIC_TRAEFIK_PATH } = paths(true);
|
|
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
|
await execAsyncRemote(serverId, `rm ${configPath}`);
|
|
} catch {}
|
|
};
|
|
|
|
export const loadOrCreateConfig = (appName: string): FileConfig => {
|
|
const { DYNAMIC_TRAEFIK_PATH } = paths();
|
|
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
|
if (fs.existsSync(configPath)) {
|
|
const yamlStr = fs.readFileSync(configPath, "utf8");
|
|
const parsedConfig = (parse(yamlStr) as FileConfig) || {
|
|
http: { routers: {}, services: {} },
|
|
};
|
|
return parsedConfig;
|
|
}
|
|
return { http: { routers: {}, services: {} } };
|
|
};
|
|
|
|
export const loadOrCreateConfigRemote = async (
|
|
serverId: string,
|
|
appName: string,
|
|
) => {
|
|
const { DYNAMIC_TRAEFIK_PATH } = paths(true);
|
|
const fileConfig: FileConfig = { http: { routers: {}, services: {} } };
|
|
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
|
try {
|
|
const { stdout } = await execAsyncRemote(serverId, `cat ${configPath}`);
|
|
|
|
if (!stdout) return fileConfig;
|
|
|
|
const parsedConfig = (parse(stdout) as FileConfig) || {
|
|
http: { routers: {}, services: {} },
|
|
};
|
|
return parsedConfig;
|
|
} catch {
|
|
return fileConfig;
|
|
}
|
|
};
|
|
|
|
export const readConfig = (appName: string) => {
|
|
const { DYNAMIC_TRAEFIK_PATH } = paths();
|
|
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
|
if (fs.existsSync(configPath)) {
|
|
const yamlStr = fs.readFileSync(configPath, "utf8");
|
|
return yamlStr;
|
|
}
|
|
return null;
|
|
};
|
|
|
|
export const readRemoteConfig = async (serverId: string, appName: string) => {
|
|
const { DYNAMIC_TRAEFIK_PATH } = paths(true);
|
|
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
|
try {
|
|
const { stdout } = await execAsyncRemote(serverId, `cat ${configPath}`);
|
|
if (!stdout) return null;
|
|
return stdout;
|
|
} catch {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
export const readMonitoringConfig = async (readAll = false) => {
|
|
const { DYNAMIC_TRAEFIK_PATH } = paths();
|
|
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, "access.log");
|
|
if (fs.existsSync(configPath)) {
|
|
if (!readAll) {
|
|
// Read first 500 lines using streams
|
|
let content = "";
|
|
let validCount = 0;
|
|
|
|
const fileStream = createReadStream(configPath, { encoding: "utf8" });
|
|
const readline = createInterface({
|
|
input: fileStream,
|
|
crlfDelay: Number.POSITIVE_INFINITY,
|
|
});
|
|
|
|
for await (const line of readline) {
|
|
try {
|
|
const trimmed = line.trim();
|
|
if (
|
|
trimmed !== "" &&
|
|
trimmed.startsWith("{") &&
|
|
trimmed.endsWith("}")
|
|
) {
|
|
const log = JSON.parse(trimmed);
|
|
if (log.ServiceName !== "dokploy-service-app@file") {
|
|
content += `${line}\n`;
|
|
validCount++;
|
|
if (validCount >= 500) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} catch {
|
|
// Ignore invalid JSON
|
|
}
|
|
}
|
|
return content;
|
|
}
|
|
return fs.readFileSync(configPath, "utf8");
|
|
}
|
|
return null;
|
|
};
|
|
|
|
export const readConfigInPath = async (pathFile: string, serverId?: string) => {
|
|
const configPath = path.join(pathFile);
|
|
|
|
if (serverId) {
|
|
const { stdout } = await execAsyncRemote(serverId, `cat ${configPath}`);
|
|
if (!stdout) return null;
|
|
return stdout;
|
|
}
|
|
if (fs.existsSync(configPath)) {
|
|
const yamlStr = fs.readFileSync(configPath, "utf8");
|
|
return yamlStr;
|
|
}
|
|
return null;
|
|
};
|
|
|
|
export const writeConfig = (appName: string, traefikConfig: string) => {
|
|
try {
|
|
const { DYNAMIC_TRAEFIK_PATH } = paths();
|
|
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
|
fs.writeFileSync(configPath, traefikConfig, "utf8");
|
|
} catch (e) {
|
|
console.error("Error saving the YAML config file:", e);
|
|
}
|
|
};
|
|
|
|
export const writeConfigRemote = async (
|
|
serverId: string,
|
|
appName: string,
|
|
traefikConfig: string,
|
|
) => {
|
|
try {
|
|
const { DYNAMIC_TRAEFIK_PATH } = paths(true);
|
|
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
|
await execAsyncRemote(serverId, `echo '${traefikConfig}' > ${configPath}`);
|
|
} catch (e) {
|
|
console.error("Error saving the YAML config file:", e);
|
|
}
|
|
};
|
|
|
|
export const writeTraefikConfigInPath = async (
|
|
pathFile: string,
|
|
traefikConfig: string,
|
|
serverId?: string,
|
|
) => {
|
|
try {
|
|
const configPath = path.join(pathFile);
|
|
if (serverId) {
|
|
const encoded = encodeBase64(traefikConfig);
|
|
await execAsyncRemote(
|
|
serverId,
|
|
`echo "${encoded}" | base64 -d > "${configPath}"`,
|
|
);
|
|
} else {
|
|
fs.writeFileSync(configPath, traefikConfig, "utf8");
|
|
}
|
|
} catch (e) {
|
|
console.error("Error saving the YAML config file:", e);
|
|
}
|
|
};
|
|
|
|
export const writeTraefikConfig = (
|
|
traefikConfig: FileConfig,
|
|
appName: string,
|
|
) => {
|
|
try {
|
|
const { DYNAMIC_TRAEFIK_PATH } = paths();
|
|
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
|
const yamlStr = stringify(traefikConfig);
|
|
fs.writeFileSync(configPath, yamlStr, "utf8");
|
|
} catch (e) {
|
|
console.error("Error saving the YAML config file:", e);
|
|
}
|
|
};
|
|
|
|
export const writeTraefikConfigRemote = async (
|
|
traefikConfig: FileConfig,
|
|
appName: string,
|
|
serverId: string,
|
|
) => {
|
|
try {
|
|
const { DYNAMIC_TRAEFIK_PATH } = paths(true);
|
|
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
|
const yamlStr = stringify(traefikConfig);
|
|
await execAsyncRemote(serverId, `echo '${yamlStr}' > ${configPath}`);
|
|
} catch (e) {
|
|
console.error("Error saving the YAML config file:", e);
|
|
}
|
|
};
|
|
|
|
export const createServiceConfig = (
|
|
appName: string,
|
|
domain: Domain,
|
|
): {
|
|
loadBalancer: HttpLoadBalancerService;
|
|
} => ({
|
|
loadBalancer: {
|
|
servers: [{ url: `http://${appName}:${domain.port || 80}` }],
|
|
passHostHeader: true,
|
|
},
|
|
});
|