feat: initial commit

This commit is contained in:
Mauricio Siu
2024-04-28 23:57:52 -06:00
parent 8857a20344
commit be56ba046c
412 changed files with 60777 additions and 1 deletions

View File

@@ -0,0 +1,132 @@
import fs, { writeFileSync } from "node:fs";
import path from "node:path";
import type { Domain } from "@/server/api/services/domain";
import { DYNAMIC_TRAEFIK_PATH } from "@/server/constants";
import { dump, load } from "js-yaml";
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 = dump(config);
fs.mkdirSync(DYNAMIC_TRAEFIK_PATH, { recursive: true });
writeFileSync(
path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`),
yamlStr,
"utf8",
);
};
export const removeTraefikConfig = async (appName: string) => {
try {
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
if (fs.existsSync(configPath)) {
await fs.promises.unlink(configPath);
}
} catch (error) {}
};
export const loadOrCreateConfig = (appName: string): FileConfig => {
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
if (fs.existsSync(configPath)) {
const yamlStr = fs.readFileSync(configPath, "utf8");
const parsedConfig = (load(yamlStr) as FileConfig) || {
http: { routers: {}, services: {} },
};
return parsedConfig;
}
return { http: { routers: {}, services: {} } };
};
export const readConfig = (appName: string) => {
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 readConfigInPath = (pathFile: string) => {
const configPath = path.join(pathFile);
if (fs.existsSync(configPath)) {
const yamlStr = fs.readFileSync(configPath, "utf8");
return yamlStr;
}
return null;
};
export const writeConfig = (appName: string, traefikConfig: string) => {
try {
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 writeTraefikConfigInPath = (
pathFile: string,
traefikConfig: string,
) => {
try {
const configPath = path.join(pathFile);
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 configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
const yamlStr = dump(traefikConfig);
fs.writeFileSync(configPath, yamlStr, "utf8");
} 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,
},
});

View File

@@ -0,0 +1,82 @@
import {
createServiceConfig,
loadOrCreateConfig,
writeTraefikConfig,
} from "./application";
import type { ApplicationNested } from "../builders";
import type { Domain } from "@/server/api/services/domain";
import type { FileConfig, HttpRouter } from "./file-types";
export const manageDomain = async (app: ApplicationNested, domain: Domain) => {
const { appName } = app;
const config: FileConfig = loadOrCreateConfig(appName);
const serviceName = `${appName}-service-${domain.uniqueConfigKey}`;
const routerName = `${appName}-router-${domain.uniqueConfigKey}`;
config.http = config.http || { routers: {}, services: {} };
config.http.routers = config.http.routers || {};
config.http.services = config.http.services || {};
config.http.routers[routerName] = await createRouterConfig(app, domain);
config.http.services[serviceName] = createServiceConfig(appName, domain);
writeTraefikConfig(config, appName);
};
export const removeDomain = (appName: string, uniqueKey: number) => {
const config: FileConfig = loadOrCreateConfig(appName);
const routerKey = `${appName}-router-${uniqueKey}`;
const serviceKey = `${appName}-service-${uniqueKey}`;
if (config.http?.routers?.[routerKey]) {
delete config.http.routers[routerKey];
}
if (config.http?.services?.[serviceKey]) {
delete config.http.services[serviceKey];
}
writeTraefikConfig(config, appName);
};
export const createRouterConfig = async (
app: ApplicationNested,
domain: Domain,
) => {
const { appName, redirects, security } = app;
const { certificateType } = domain;
const { host, path, https, uniqueConfigKey } = domain;
const routerConfig: HttpRouter = {
rule: `Host(\`${host}\`)${path ? ` && PathPrefix(\`${path}\`)` : ""}`,
service: `${appName}-service-${uniqueConfigKey}`,
middlewares: [],
entryPoints: https
? ["web", ...(process.env.NODE_ENV === "production" ? ["websecure"] : [])]
: ["web"],
tls: {},
};
if (https) {
routerConfig.middlewares = ["redirect-to-https"];
}
// redirects
for (const redirect of redirects) {
const middlewareName = `redirect-${appName}-${redirect.uniqueConfigKey}`;
routerConfig.middlewares?.push(middlewareName);
}
// security
if (security.length > 0) {
const middlewareName = `auth-${appName}`;
routerConfig.middlewares?.push(middlewareName);
}
if (certificateType === "letsencrypt") {
routerConfig.tls = { certResolver: "letsencrypt" };
} else if (certificateType === "none") {
routerConfig.tls = undefined;
}
return routerConfig;
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,79 @@
import { dump, load } from "js-yaml";
import { join } from "node:path";
import { DYNAMIC_TRAEFIK_PATH } from "@/server/constants";
import { existsSync, readFileSync, writeFileSync } from "node:fs";
import type { ApplicationNested } from "../builders";
import type { FileConfig } from "./file-types";
export const addMiddleware = (config: FileConfig, middlewareName: string) => {
if (config.http?.routers) {
const values = Object.keys(config.http.routers);
for (const routerName of values) {
const router = config.http.routers[routerName];
if (router) {
if (!router.middlewares) {
router.middlewares = [];
}
if (!router.middlewares.includes(middlewareName)) {
router.middlewares.push(middlewareName);
}
}
}
}
};
export const deleteMiddleware = (
config: FileConfig,
middlewareName: string,
) => {
if (config.http?.routers) {
const values = Object.keys(config?.http?.routers);
for (const routerName of values) {
const router = config.http.routers[routerName];
if (router?.middlewares) {
router.middlewares = router.middlewares.filter(
(m) => m !== middlewareName,
);
}
}
}
};
export const deleteAllMiddlewares = (application: ApplicationNested) => {
const config = loadMiddlewares<FileConfig>();
const { security, appName, redirects } = application;
if (config.http?.middlewares) {
if (security.length > 0) {
const middlewareName = `auth-${appName}`;
delete config.http.middlewares[middlewareName];
}
for (const redirect of redirects) {
const middlewareName = `redirect-${appName}-${redirect.uniqueConfigKey}`;
delete config.http.middlewares[middlewareName];
}
}
writeMiddleware(config);
};
export const loadMiddlewares = <T>() => {
const configPath = join(DYNAMIC_TRAEFIK_PATH, "middlewares.yml");
if (!existsSync(configPath)) {
throw new Error(`File not found: ${configPath}`);
}
const yamlStr = readFileSync(configPath, "utf8");
const config = load(yamlStr) as T;
return config;
};
export const writeMiddleware = <T>(config: T) => {
const configPath = join(DYNAMIC_TRAEFIK_PATH, "middlewares.yml");
const newYamlContent = dump(config);
writeFileSync(configPath, newYamlContent, "utf8");
};

View File

@@ -0,0 +1,69 @@
import type { Redirect } from "@/server/api/services/redirect";
import { loadOrCreateConfig, writeTraefikConfig } from "./application";
import {
addMiddleware,
deleteMiddleware,
loadMiddlewares,
writeMiddleware,
} from "./middleware";
import type { FileConfig } from "./file-types";
export const updateRedirectMiddleware = (appName: string, data: Redirect) => {
const config = loadMiddlewares<FileConfig>();
if (config?.http?.middlewares?.[appName]) {
const middlewareName = `${appName}-${data.uniqueConfigKey}`;
config.http.middlewares[middlewareName] = {
redirectRegex: {
regex: data.regex,
replacement: data.replacement,
permanent: data.permanent,
},
};
}
writeMiddleware(config);
};
export const createRedirectMiddleware = (appName: string, data: Redirect) => {
const config = loadMiddlewares<FileConfig>();
const middlewareName = `redirect-${appName}-${data.uniqueConfigKey}`;
const newMiddleware = {
[middlewareName]: {
redirectRegex: {
regex: data.regex,
replacement: data.replacement,
permanent: data.permanent,
},
},
};
if (config?.http) {
config.http.middlewares = {
...config.http.middlewares,
...newMiddleware,
};
}
const appConfig = loadOrCreateConfig(appName);
addMiddleware(appConfig, middlewareName);
writeTraefikConfig(appConfig, appName);
writeMiddleware(config);
};
export const removeRedirectMiddleware = (appName: string, data: Redirect) => {
const config = loadMiddlewares<FileConfig>();
const middlewareName = `redirect-${appName}-${data.uniqueConfigKey}`;
if (config?.http?.middlewares?.[middlewareName]) {
delete config.http.middlewares[middlewareName];
}
const appConfig = loadOrCreateConfig(appName);
deleteMiddleware(appConfig, middlewareName);
writeTraefikConfig(appConfig, appName);
writeMiddleware(config);
};

View File

@@ -0,0 +1,82 @@
import { loadOrCreateConfig, writeTraefikConfig } from "./application";
import type { Security } from "@/server/api/services/security";
import * as bcrypt from "bcrypt";
import {
addMiddleware,
deleteMiddleware,
loadMiddlewares,
writeMiddleware,
} from "./middleware";
import type {
BasicAuthMiddleware,
FileConfig,
HttpMiddleware,
} from "./file-types";
export const createSecurityMiddleware = async (
appName: string,
data: Security,
) => {
const config = loadMiddlewares<FileConfig>();
const middlewareName = `auth-${appName}`;
const user = `${data.username}:${await bcrypt.hash(data.password, 10)}`;
if (config.http?.middlewares) {
const currentMiddleware = config.http.middlewares[middlewareName];
if (isBasicAuthMiddleware(currentMiddleware)) {
currentMiddleware.basicAuth.users = [
...(currentMiddleware.basicAuth.users || []),
user,
];
} else {
config.http.middlewares[middlewareName] = {
basicAuth: {
removeHeader: true,
users: [user],
},
};
}
}
const appConfig = loadOrCreateConfig(appName);
addMiddleware(appConfig, middlewareName);
writeTraefikConfig(appConfig, appName);
writeMiddleware(config);
};
export const removeSecurityMiddleware = (appName: string, data: Security) => {
const config = loadMiddlewares<FileConfig>();
const appConfig = loadOrCreateConfig(appName);
const middlewareName = `auth-${appName}`;
if (config.http?.middlewares) {
const currentMiddleware = config.http.middlewares[middlewareName];
if (isBasicAuthMiddleware(currentMiddleware)) {
const users = currentMiddleware.basicAuth.users;
const filteredUsers =
users?.filter((user) => {
const [username] = user.split(":");
return username !== data.username;
}) || [];
currentMiddleware.basicAuth.users = filteredUsers;
if (filteredUsers.length === 0) {
if (config?.http?.middlewares?.[middlewareName]) {
delete config.http.middlewares[middlewareName];
}
deleteMiddleware(appConfig, middlewareName);
writeTraefikConfig(appConfig, appName);
}
}
}
writeMiddleware(config);
};
const isBasicAuthMiddleware = (
middleware: HttpMiddleware | undefined,
): middleware is { basicAuth: BasicAuthMiddleware } => {
return !!middleware && "basicAuth" in middleware;
};

View File

@@ -0,0 +1,574 @@
/* eslint-disable */
export interface MainTraefikConfig {
accessLog?: {
filePath?: string;
format?: string;
filters?: {
statusCodes?: string[];
retryAttempts?: boolean;
minDuration?: string;
[k: string]: unknown;
};
fields?: {
defaultMode?: string;
names?: {
/**
* This interface was referenced by `undefined`'s JSON-Schema definition
* via the `patternProperty` "[a-zA-Z0-9-_]+".
*/
[k: string]: string;
};
headers?: {
defaultMode?: string;
names?: {
/**
* This interface was referenced by `undefined`'s JSON-Schema definition
* via the `patternProperty` "[a-zA-Z0-9-_]+".
*/
[k: string]: string;
};
[k: string]: unknown;
};
[k: string]: unknown;
};
bufferingSize?: number;
};
api?: {
insecure?: boolean;
dashboard?: boolean;
debug?: boolean;
};
certificatesResolvers?: {
/**
* This interface was referenced by `undefined`'s JSON-Schema definition
* via the `patternProperty` "[a-zA-Z0-9-_]+".
*/
[k: string]: {
acme?: {
email?: string;
caServer?: string;
certificatesDuration?: number;
preferredChain?: string;
storage?: string;
keyType?: string;
eab?: {
kid?: string;
hmacEncoded?: string;
[k: string]: unknown;
};
dnsChallenge?: {
provider?: string;
delayBeforeCheck?: string;
resolvers?: string[];
disablePropagationCheck?: boolean;
[k: string]: unknown;
};
httpChallenge?: {
entryPoint?: string;
[k: string]: unknown;
};
tlsChallenge?: {
[k: string]: unknown;
};
[k: string]: unknown;
};
};
};
entryPoints?: {
/**
* This interface was referenced by `undefined`'s JSON-Schema definition
* via the `patternProperty` "[a-zA-Z0-9-_]+".
*/
[k: string]: {
address?: string;
transport?: {
lifeCycle?: {
requestAcceptGraceTimeout?: string;
graceTimeOut?: string;
[k: string]: unknown;
};
respondingTimeouts?: {
readTimeout?: string;
writeTimeout?: string;
idleTimeout?: string;
[k: string]: unknown;
};
[k: string]: unknown;
};
proxyProtocol?: {
insecure?: boolean;
trustedIPs?: string[];
[k: string]: unknown;
};
forwardedHeaders?: {
insecure?: boolean;
trustedIPs?: string[];
[k: string]: unknown;
};
http?: {
redirections?: {
entryPoint?: {
to?: string;
scheme?: string;
permanent?: boolean;
priority?: number;
[k: string]: unknown;
};
[k: string]: unknown;
};
middlewares?: string[];
tls?: {
options?: string;
certResolver?: string;
domains?: {
main?: string;
sans?: string[];
[k: string]: unknown;
}[];
[k: string]: unknown;
};
[k: string]: unknown;
};
http2?: {
maxConcurrentStreams?: number;
[k: string]: unknown;
};
http3?: {
advertisedPort?: number;
[k: string]: unknown;
};
udp?: {
timeout?: string;
[k: string]: unknown;
};
};
};
experimental?: {
kubernetesGateway?: boolean;
http3?: boolean;
hub?: boolean;
plugins?: {
/**
* This interface was referenced by `undefined`'s JSON-Schema definition
* via the `patternProperty` "[a-zA-Z0-9-_]+".
*/
[k: string]: {
moduleName?: string;
version?: string;
};
};
localPlugins?: {
/**
* This interface was referenced by `undefined`'s JSON-Schema definition
* via the `patternProperty` "[a-zA-Z0-9-_]+".
*/
[k: string]: {
moduleName?: string;
};
};
};
global?: {
checkNewVersion?: boolean;
sendAnonymousUsage?: boolean;
};
hostResolver?: {
cnameFlattening?: boolean;
resolvConfig?: string;
resolvDepth?: number;
};
hub?: {
tls?: {
insecure?: boolean;
ca?: string;
cert?: string;
key?: string;
[k: string]: unknown;
};
};
log?: {
level?: string;
filePath?: string;
format?: string;
};
metrics?: {
prometheus?: {
buckets?: number[];
addEntryPointsLabels?: boolean;
addRoutersLabels?: boolean;
addServicesLabels?: boolean;
entryPoint?: string;
manualRouting?: boolean;
};
datadog?: {
address?: string;
pushInterval?: string;
addEntryPointsLabels?: boolean;
addRoutersLabels?: boolean;
addServicesLabels?: boolean;
prefix?: string;
};
statsD?: {
address?: string;
pushInterval?: string;
addEntryPointsLabels?: boolean;
addRoutersLabels?: boolean;
addServicesLabels?: boolean;
prefix?: string;
};
influxDB?: {
address?: string;
protocol?: string;
pushInterval?: string;
database?: string;
retentionPolicy?: string;
username?: string;
password?: string;
addEntryPointsLabels?: boolean;
addRoutersLabels?: boolean;
addServicesLabels?: boolean;
additionalLabels?: {
[k: string]: unknown;
};
};
influxDB2?: {
address?: string;
token?: string;
pushInterval?: string;
org?: string;
bucket?: string;
addEntryPointsLabels?: boolean;
addRoutersLabels?: boolean;
addServicesLabels?: boolean;
additionalLabels?: {
[k: string]: unknown;
};
};
};
pilot?: {
token?: string;
dashboard?: boolean;
};
ping?: {
entryPoint?: string;
manualRouting?: boolean;
terminatingStatusCode?: number;
};
providers?: {
providersThrottleDuration?: string;
docker?: {
allowEmptyServices?: boolean;
constraints?: string;
defaultRule?: string;
endpoint?: string;
exposedByDefault?: boolean;
httpClientTimeout?: number;
network?: string;
swarmMode?: boolean;
swarmModeRefreshSeconds?: string;
tls?: {
ca?: string;
caOptional?: boolean;
cert?: string;
key?: string;
insecureSkipVerify?: boolean;
};
useBindPortIP?: boolean;
watch?: boolean;
};
file?: {
directory?: string;
watch?: boolean;
filename?: string;
debugLogGeneratedTemplate?: boolean;
};
marathon?: {
constraints?: string;
trace?: boolean;
watch?: boolean;
endpoint?: string;
defaultRule?: string;
exposedByDefault?: boolean;
dcosToken?: string;
tls?: {
ca?: string;
caOptional?: boolean;
cert?: string;
key?: string;
insecureSkipVerify?: boolean;
};
dialerTimeout?: string;
responseHeaderTimeout?: string;
tlsHandshakeTimeout?: string;
keepAlive?: string;
forceTaskHostname?: boolean;
basic?: {
httpBasicAuthUser?: string;
httpBasicPassword?: string;
};
respectReadinessChecks?: boolean;
};
kubernetesIngress?: {
endpoint?: string;
token?: string;
certAuthFilePath?: string;
namespaces?: string[];
labelSelector?: string;
ingressClass?: string;
throttleDuration?: string;
allowEmptyServices?: boolean;
allowExternalNameServices?: boolean;
ingressEndpoint?: {
ip?: string;
hostname?: string;
publishedService?: string;
};
};
kubernetesCRD?: {
endpoint?: string;
token?: string;
certAuthFilePath?: string;
namespaces?: string[];
allowCrossNamespace?: boolean;
allowExternalNameServices?: boolean;
labelSelector?: string;
ingressClass?: string;
throttleDuration?: string;
allowEmptyServices?: boolean;
};
kubernetesGateway?: {
endpoint?: string;
token?: string;
certAuthFilePath?: string;
namespaces?: string[];
labelSelector?: string;
throttleDuration?: string;
};
rest?: {
insecure?: boolean;
};
rancher?: {
constraints?: string;
watch?: boolean;
defaultRule?: string;
exposedByDefault?: boolean;
enableServiceHealthFilter?: boolean;
refreshSeconds?: number;
intervalPoll?: boolean;
prefix?: string;
};
consulCatalog?: {
constraints?: string;
prefix?: string;
refreshInterval?: string;
requireConsistent?: boolean;
stale?: boolean;
cache?: boolean;
exposedByDefault?: boolean;
defaultRule?: string;
connectAware?: boolean;
connectByDefault?: boolean;
serviceName?: string;
namespace?: string;
namespaces?: string[];
watch?: boolean;
endpoint?: {
address?: string;
scheme?: string;
datacenter?: string;
token?: string;
endpointWaitTime?: string;
tls?: {
ca?: string;
caOptional?: boolean;
cert?: string;
key?: string;
insecureSkipVerify?: boolean;
};
httpAuth?: {
username?: string;
password?: string;
};
};
[k: string]: unknown;
};
nomad?: {
constraints?: string;
prefix?: string;
refreshInterval?: string;
stale?: boolean;
exposedByDefault?: boolean;
defaultRule?: string;
namespace?: string;
endpoint?: {
address?: string;
region?: string;
token?: string;
endpointWaitTime?: string;
tls?: {
ca?: string;
caOptional?: boolean;
cert?: string;
key?: string;
insecureSkipVerify?: boolean;
};
};
};
ecs?: {
constraints?: string;
exposedByDefault?: boolean;
ecsAnywhere?: boolean;
refreshSeconds?: number;
defaultRule?: string;
clusters?: string[];
autoDiscoverClusters?: boolean;
region?: string;
accessKeyID?: string;
secretAccessKey?: string;
};
consul?: {
rootKey?: string;
endpoints?: string[];
token?: string;
namespace?: string;
namespaces?: string[];
tls?: {
ca?: string;
caOptional?: boolean;
cert?: string;
key?: string;
insecureSkipVerify?: boolean;
};
};
etcd?: {
rootKey?: string;
endpoints?: string[];
username?: string;
password?: string;
tls?: {
ca?: string;
caOptional?: boolean;
cert?: string;
key?: string;
insecureSkipVerify?: boolean;
};
};
zooKeeper?: {
rootKey?: string;
endpoints?: string[];
username?: string;
password?: string;
};
redis?: {
rootKey?: string;
endpoints?: string[];
username?: string;
password?: string;
db?: number;
tls?: {
ca?: string;
caOptional?: boolean;
cert?: string;
key?: string;
insecureSkipVerify?: boolean;
};
};
http?: {
endpoint?: string;
pollInterval?: string;
pollTimeout?: string;
tls?: {
ca?: string;
caOptional?: boolean;
cert?: string;
key?: string;
insecureSkipVerify?: boolean;
};
};
plugin?: {
/**
* This interface was referenced by `undefined`'s JSON-Schema definition
* via the `patternProperty` "[a-zA-Z0-9-_]+".
*/
[k: string]: {
[k: string]: unknown;
};
};
[k: string]: unknown;
};
serversTransport?: {
insecureSkipVerify?: boolean;
rootCAs?: string[];
maxIdleConnsPerHost?: number;
forwardingTimeouts?: {
dialTimeout?: string;
responseHeaderTimeout?: string;
idleConnTimeout?: string;
};
};
tracing?: {
serviceName?: string;
spanNameLimit?: number;
jaeger?: {
samplingServerURL?: string;
samplingType?: string;
samplingParam?: number;
localAgentHostPort?: string;
gen128Bit?: boolean;
propagation?: string;
traceContextHeaderName?: string;
disableAttemptReconnecting?: boolean;
collector?: {
endpoint?: string;
user?: string;
password?: string;
};
};
zipkin?: {
httpEndpoint?: string;
sameSpan?: boolean;
id128Bit?: boolean;
sampleRate?: number;
};
datadog?: {
localAgentHostPort?: string;
globalTag?: string;
/**
* Sets a list of key:value tags on all spans.
*/
globalTags?: {
/**
* This interface was referenced by `undefined`'s JSON-Schema definition
* via the `patternProperty` "[a-zA-Z0-9-_]+".
*/
[k: string]: string;
};
debug?: boolean;
prioritySampling?: boolean;
traceIDHeaderName?: string;
parentIDHeaderName?: string;
samplingPriorityHeaderName?: string;
bagagePrefixHeaderName?: string;
};
instana?: {
localAgentHost?: string;
localAgentPort?: number;
logLevel?: string;
enableAutoProfile?: boolean;
};
haystack?: {
localAgentHost?: string;
localAgentPort?: number;
globalTag?: string;
traceIDHeaderName?: string;
parentIDHeaderName?: string;
spanIDHeaderName?: string;
baggagePrefixHeaderName?: string;
};
elastic?: {
serverURL?: string;
secretToken?: string;
serviceEnvironment?: string;
};
};
}

View File

@@ -0,0 +1,70 @@
import { join } from "node:path";
import type { MainTraefikConfig } from "./types";
import { loadOrCreateConfig, writeTraefikConfig } from "./application";
import { MAIN_TRAEFIK_PATH } from "@/server/constants";
import { existsSync, readFileSync, writeFileSync } from "node:fs";
import { dump, load } from "js-yaml";
import type { FileConfig } from "./file-types";
import type { Admin } from "@/server/api/services/admin";
export const updateServerTraefik = (
admin: Admin | null,
newHost: string | null,
) => {
const appName = "dokploy";
const config: FileConfig = loadOrCreateConfig(appName);
config.http = config.http || { routers: {}, services: {} };
config.http.routers = config.http.routers || {};
const currentRouterConfig = config.http.routers[`${appName}-router-app`];
if (currentRouterConfig) {
if (newHost) {
currentRouterConfig.rule = `Host(\`${newHost}\`)`;
}
if (admin?.certificateType === "letsencrypt") {
currentRouterConfig.tls = { certResolver: "letsencrypt" };
} else if (admin?.certificateType === "none") {
currentRouterConfig.tls = undefined;
}
}
writeTraefikConfig(config, appName);
};
export const updateLetsEncryptEmail = (newEmail: string | null) => {
try {
if (!newEmail) return;
const configPath = join(MAIN_TRAEFIK_PATH, "traefik.yml");
const configContent = readFileSync(configPath, "utf8");
const config = load(configContent) as MainTraefikConfig;
if (config?.certificatesResolvers?.letsencrypt?.acme) {
config.certificatesResolvers.letsencrypt.acme.email = newEmail;
} else {
throw new Error("Invalid Let's Encrypt configuration structure.");
}
const newYamlContent = dump(config);
writeFileSync(configPath, newYamlContent, "utf8");
} catch (error) {
throw error;
}
};
export const readMainConfig = () => {
const configPath = join(MAIN_TRAEFIK_PATH, "traefik.yml");
if (existsSync(configPath)) {
const yamlStr = readFileSync(configPath, "utf8");
return yamlStr;
}
return null;
};
export const writeMainConfig = (traefikConfig: string) => {
try {
const configPath = join(MAIN_TRAEFIK_PATH, "traefik.yml");
writeFileSync(configPath, traefikConfig, "utf8");
} catch (e) {
console.error("Error saving the YAML config file:", e);
}
};