mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-07-09 16:05:23 +02:00
feat: add domains in external server
This commit is contained in:
@@ -11,7 +11,6 @@ export const executeCommand = async (serverId: string, command: string) => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
client
|
||||
.on("ready", () => {
|
||||
console.log("Client :: ready", command);
|
||||
client.exec(command, (err, stream) => {
|
||||
if (err) {
|
||||
console.error("Execution error:", err);
|
||||
|
||||
@@ -22,6 +22,7 @@ import { Client } from "ssh2";
|
||||
import { readSSHKey } from "../filesystem/ssh";
|
||||
import {
|
||||
getDefaultMiddlewares,
|
||||
getDefaultServerTraefikConfig,
|
||||
getDefaultTraefikConfig,
|
||||
} from "@/server/setup/traefik-setup";
|
||||
|
||||
@@ -220,7 +221,7 @@ const validatePorts = () => `
|
||||
`;
|
||||
|
||||
const createTraefikConfig = () => {
|
||||
const config = getDefaultTraefikConfig();
|
||||
const config = getDefaultServerTraefikConfig();
|
||||
|
||||
const command = `
|
||||
if [ -f "/etc/dokploy/traefik/dynamic/acme.json" ]; then
|
||||
|
||||
@@ -4,6 +4,9 @@ import type { Domain } from "@/server/api/services/domain";
|
||||
import { DYNAMIC_TRAEFIK_PATH, MAIN_TRAEFIK_PATH } from "@/server/constants";
|
||||
import { dump, load } from "js-yaml";
|
||||
import type { FileConfig, HttpLoadBalancerService } from "./file-types";
|
||||
import { findServerById } from "@/server/api/services/server";
|
||||
import { Client } from "ssh2";
|
||||
import { readSSHKey } from "../filesystem/ssh";
|
||||
|
||||
export const createTraefikConfig = (appName: string) => {
|
||||
const defaultPort = 3000;
|
||||
@@ -67,6 +70,62 @@ export const loadOrCreateConfig = (appName: string): FileConfig => {
|
||||
return { http: { routers: {}, services: {} } };
|
||||
};
|
||||
|
||||
export const loadOrCreateConfigRemote = async (
|
||||
serverId: string,
|
||||
appName: string,
|
||||
) => {
|
||||
const server = await findServerById(serverId);
|
||||
if (!server.sshKeyId) return { http: { routers: {}, services: {} } };
|
||||
|
||||
const keys = await readSSHKey(server.sshKeyId);
|
||||
const client = new Client();
|
||||
let fileConfig: FileConfig;
|
||||
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
||||
return new Promise<FileConfig>((resolve, reject) => {
|
||||
client
|
||||
.on("ready", () => {
|
||||
client.exec(`cat ${configPath}`, (err, stream) => {
|
||||
if (err) {
|
||||
console.error("Execution error:", err);
|
||||
return { http: { routers: {}, services: {} } };
|
||||
}
|
||||
stream
|
||||
.on("close", (code, signal) => {
|
||||
client.end();
|
||||
if (code === 0) {
|
||||
if (!fileConfig) {
|
||||
fileConfig = { http: { routers: {}, services: {} } };
|
||||
}
|
||||
resolve(
|
||||
(load(fileConfig) as FileConfig) || {
|
||||
http: { routers: {}, services: {} },
|
||||
},
|
||||
);
|
||||
} else {
|
||||
console.log(fileConfig);
|
||||
|
||||
resolve({ http: { routers: {}, services: {} } });
|
||||
|
||||
// reject(new Error(`Command exited with code ${code}`));
|
||||
}
|
||||
})
|
||||
.on("data", (data: string) => {
|
||||
console.log(data.toString());
|
||||
fileConfig = data.toString() as unknown as FileConfig;
|
||||
})
|
||||
.stderr.on("data", (data) => {});
|
||||
});
|
||||
})
|
||||
.connect({
|
||||
host: server.ipAddress,
|
||||
port: server.port,
|
||||
username: server.username,
|
||||
privateKey: keys.privateKey,
|
||||
timeout: 99999,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const readConfig = (appName: string) => {
|
||||
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
||||
if (fs.existsSync(configPath)) {
|
||||
@@ -76,6 +135,53 @@ export const readConfig = (appName: string) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
export const readConfigInServer = async (serverId: string, appName: string) => {
|
||||
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
||||
let content = "";
|
||||
// if (fs.existsSync(configPath)) {
|
||||
// const yamlStr = fs.readFileSync(configPath, "utf8");
|
||||
// return yamlStr;
|
||||
// }
|
||||
|
||||
const client = new Client();
|
||||
const server = await findServerById(serverId);
|
||||
if (!server.sshKeyId) return;
|
||||
const keys = await readSSHKey(server.sshKeyId);
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
client
|
||||
.on("ready", () => {
|
||||
const bashCommand = `
|
||||
cat ${configPath}
|
||||
`;
|
||||
|
||||
client.exec(bashCommand, (err, stream) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
stream
|
||||
.on("close", () => {
|
||||
client.end();
|
||||
resolve(content);
|
||||
})
|
||||
.on("data", (data: string) => {
|
||||
content = data.toString();
|
||||
})
|
||||
.stderr.on("data", (data) => {
|
||||
reject(new Error(`stderr: ${data.toString()}`));
|
||||
});
|
||||
});
|
||||
})
|
||||
.connect({
|
||||
host: server.ipAddress,
|
||||
port: server.port,
|
||||
username: server.username,
|
||||
privateKey: keys.privateKey,
|
||||
timeout: 99999,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const readMonitoringConfig = () => {
|
||||
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, "access.log");
|
||||
if (fs.existsSync(configPath)) {
|
||||
@@ -122,6 +228,7 @@ export const writeTraefikConfig = (
|
||||
try {
|
||||
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
||||
const yamlStr = dump(traefikConfig);
|
||||
console.log(yamlStr);
|
||||
fs.writeFileSync(configPath, yamlStr, "utf8");
|
||||
} catch (e) {
|
||||
console.error("Error saving the YAML config file:", e);
|
||||
|
||||
@@ -3,14 +3,25 @@ import type { ApplicationNested } from "../builders";
|
||||
import {
|
||||
createServiceConfig,
|
||||
loadOrCreateConfig,
|
||||
loadOrCreateConfigRemote,
|
||||
removeTraefikConfig,
|
||||
writeTraefikConfig,
|
||||
} from "./application";
|
||||
import type { FileConfig, HttpRouter } from "./file-types";
|
||||
import { DYNAMIC_TRAEFIK_PATH } from "@/server/constants";
|
||||
import path from "node:path";
|
||||
import { dump } from "js-yaml";
|
||||
import { executeCommand } from "../servers/command";
|
||||
|
||||
export const manageDomain = async (app: ApplicationNested, domain: Domain) => {
|
||||
const { appName } = app;
|
||||
const config: FileConfig = loadOrCreateConfig(appName);
|
||||
let config: FileConfig;
|
||||
|
||||
if (app.serverId) {
|
||||
config = await loadOrCreateConfigRemote(app.serverId, appName);
|
||||
} else {
|
||||
config = loadOrCreateConfig(appName);
|
||||
}
|
||||
const serviceName = `${appName}-service-${domain.uniqueConfigKey}`;
|
||||
const routerName = `${appName}-router-${domain.uniqueConfigKey}`;
|
||||
const routerNameSecure = `${appName}-router-websecure-${domain.uniqueConfigKey}`;
|
||||
@@ -36,7 +47,21 @@ export const manageDomain = async (app: ApplicationNested, domain: Domain) => {
|
||||
}
|
||||
|
||||
config.http.services[serviceName] = createServiceConfig(appName, domain);
|
||||
writeTraefikConfig(config, appName);
|
||||
|
||||
if (app.serverId) {
|
||||
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
||||
const yamlStr = dump(config);
|
||||
|
||||
console.log(yamlStr);
|
||||
|
||||
const command = `
|
||||
echo '${yamlStr}' > ${configPath}
|
||||
`;
|
||||
|
||||
await executeCommand(app.serverId, command);
|
||||
} else {
|
||||
writeTraefikConfig(config, appName);
|
||||
}
|
||||
};
|
||||
|
||||
export const removeDomain = async (appName: string, uniqueKey: number) => {
|
||||
|
||||
Reference in New Issue
Block a user