2024-09-28 22:50:10 -04:00
|
|
|
import { Request, Response } from "express";
|
|
|
|
import db from "@server/db";
|
|
|
|
import * as schema from "@server/db/schema";
|
2024-10-26 19:57:47 -04:00
|
|
|
import { and, eq, isNotNull } from "drizzle-orm";
|
2024-09-28 22:50:10 -04:00
|
|
|
import logger from "@server/logger";
|
2024-10-01 20:48:03 -04:00
|
|
|
import HttpCode from "@server/types/HttpCode";
|
2024-10-12 18:21:31 -04:00
|
|
|
import config from "@server/config";
|
2024-09-28 22:50:10 -04:00
|
|
|
|
2024-10-26 19:57:47 -04:00
|
|
|
export async function traefikConfigProvider(
|
|
|
|
_: Request,
|
2024-11-23 23:31:22 -05:00
|
|
|
res: Response,
|
2024-10-26 19:57:47 -04:00
|
|
|
): Promise<any> {
|
2024-09-28 22:50:10 -04:00
|
|
|
try {
|
2024-10-26 19:57:47 -04:00
|
|
|
const all = await db
|
|
|
|
.select()
|
|
|
|
.from(schema.targets)
|
|
|
|
.innerJoin(
|
|
|
|
schema.resources,
|
2024-11-23 23:31:22 -05:00
|
|
|
eq(schema.targets.resourceId, schema.resources.resourceId),
|
2024-10-26 19:57:47 -04:00
|
|
|
)
|
2024-11-15 18:25:27 -05:00
|
|
|
.innerJoin(
|
|
|
|
schema.orgs,
|
2024-11-23 23:31:22 -05:00
|
|
|
eq(schema.resources.orgId, schema.orgs.orgId),
|
2024-11-15 18:25:27 -05:00
|
|
|
)
|
2024-11-24 15:05:15 -05:00
|
|
|
.innerJoin(
|
|
|
|
schema.sites,
|
|
|
|
eq(schema.sites.siteId, schema.resources.siteId),
|
|
|
|
)
|
2024-10-26 19:57:47 -04:00
|
|
|
.where(
|
|
|
|
and(
|
|
|
|
eq(schema.targets.enabled, true),
|
2024-11-15 18:25:27 -05:00
|
|
|
isNotNull(schema.resources.subdomain),
|
2024-11-23 23:31:22 -05:00
|
|
|
isNotNull(schema.orgs.domain),
|
|
|
|
),
|
2024-10-26 19:57:47 -04:00
|
|
|
);
|
2024-09-28 22:50:10 -04:00
|
|
|
|
2024-10-26 19:57:47 -04:00
|
|
|
if (!all.length) {
|
2024-10-27 23:36:04 -04:00
|
|
|
return res.status(HttpCode.OK).json({});
|
2024-10-26 19:57:47 -04:00
|
|
|
}
|
2024-10-22 00:09:27 -04:00
|
|
|
|
2024-10-27 23:36:04 -04:00
|
|
|
const badgerMiddlewareName = "badger";
|
|
|
|
const redirectMiddlewareName = "redirect-to-https";
|
2024-09-28 22:50:10 -04:00
|
|
|
|
2024-10-27 23:36:04 -04:00
|
|
|
// const baseDomain = new URL(config.app.base_url).hostname;
|
2024-10-22 00:09:27 -04:00
|
|
|
|
2024-10-26 19:57:47 -04:00
|
|
|
const http: any = {
|
|
|
|
routers: {},
|
|
|
|
services: {},
|
|
|
|
middlewares: {
|
2024-10-27 23:36:04 -04:00
|
|
|
[badgerMiddlewareName]: {
|
2024-10-26 19:57:47 -04:00
|
|
|
plugin: {
|
2024-10-27 23:36:04 -04:00
|
|
|
[badgerMiddlewareName]: {
|
2024-10-26 19:57:47 -04:00
|
|
|
apiBaseUrl: new URL(
|
|
|
|
"/api/v1",
|
2024-11-23 23:31:22 -05:00
|
|
|
`http://${config.server.internal_hostname}:${config.server.internal_port}`,
|
2024-10-26 19:57:47 -04:00
|
|
|
).href,
|
2024-11-23 23:31:22 -05:00
|
|
|
resourceSessionCookieName:
|
|
|
|
config.badger.resource_session_cookie_name,
|
|
|
|
userSessionCookieName:
|
|
|
|
config.server.session_cookie_name,
|
|
|
|
sessionQueryParameter:
|
|
|
|
config.badger.session_query_parameter,
|
2024-10-26 19:57:47 -04:00
|
|
|
},
|
2024-09-29 14:37:26 -04:00
|
|
|
},
|
|
|
|
},
|
2024-10-27 23:36:04 -04:00
|
|
|
[redirectMiddlewareName]: {
|
|
|
|
redirectScheme: {
|
|
|
|
scheme: "https",
|
|
|
|
permanent: true,
|
|
|
|
},
|
|
|
|
},
|
2024-09-29 14:37:26 -04:00
|
|
|
},
|
2024-09-28 22:50:10 -04:00
|
|
|
};
|
2024-10-26 19:57:47 -04:00
|
|
|
for (const item of all) {
|
|
|
|
const target = item.targets;
|
|
|
|
const resource = item.resources;
|
2024-11-24 15:05:15 -05:00
|
|
|
const site = item.sites;
|
2024-11-15 18:25:27 -05:00
|
|
|
const org = item.orgs;
|
2024-10-26 19:57:47 -04:00
|
|
|
|
|
|
|
const routerName = `${target.targetId}-router`;
|
|
|
|
const serviceName = `${target.targetId}-service`;
|
2024-09-28 22:50:10 -04:00
|
|
|
|
2024-11-15 18:25:27 -05:00
|
|
|
if (!resource || !resource.subdomain) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!org || !org.domain) {
|
2024-10-28 00:07:43 -04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2024-11-15 18:25:27 -05:00
|
|
|
const fullDomain = `${resource.subdomain}.${org.domain}`;
|
|
|
|
|
|
|
|
const domainParts = fullDomain.split(".");
|
2024-10-28 00:07:43 -04:00
|
|
|
let wildCard;
|
|
|
|
if (domainParts.length <= 2) {
|
|
|
|
wildCard = `*.${domainParts.join(".")}`;
|
|
|
|
} else {
|
|
|
|
wildCard = `*.${domainParts.slice(1).join(".")}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
const tls = {
|
|
|
|
certResolver: config.traefik.cert_resolver,
|
|
|
|
...(config.traefik.prefer_wildcard_cert
|
|
|
|
? {
|
|
|
|
domains: [
|
|
|
|
{
|
2024-10-28 23:34:04 -04:00
|
|
|
main: wildCard,
|
2024-10-28 00:07:43 -04:00
|
|
|
},
|
|
|
|
],
|
|
|
|
}
|
|
|
|
: {}),
|
|
|
|
};
|
|
|
|
|
2024-10-26 19:57:47 -04:00
|
|
|
http.routers![routerName] = {
|
|
|
|
entryPoints: [
|
2024-10-28 23:34:04 -04:00
|
|
|
resource.ssl
|
2024-10-26 19:57:47 -04:00
|
|
|
? config.traefik.https_entrypoint
|
|
|
|
: config.traefik.http_entrypoint,
|
2024-09-28 22:50:10 -04:00
|
|
|
],
|
2024-10-28 23:34:04 -04:00
|
|
|
middlewares: resource.ssl ? [badgerMiddlewareName] : [],
|
2024-10-26 19:57:47 -04:00
|
|
|
service: serviceName,
|
2024-11-15 18:25:27 -05:00
|
|
|
rule: `Host(\`${fullDomain}\`)`,
|
2024-10-28 23:34:04 -04:00
|
|
|
...(resource.ssl ? { tls } : {}),
|
2024-10-26 19:57:47 -04:00
|
|
|
};
|
2024-09-28 22:50:10 -04:00
|
|
|
|
2024-10-28 23:34:04 -04:00
|
|
|
if (resource.ssl) {
|
2024-10-27 23:36:04 -04:00
|
|
|
// this is a redirect router; all it does is redirect to the https version if tls is enabled
|
|
|
|
http.routers![routerName + "-redirect"] = {
|
|
|
|
entryPoints: [config.traefik.http_entrypoint],
|
|
|
|
middlewares: [redirectMiddlewareName],
|
|
|
|
service: serviceName,
|
2024-11-15 18:25:27 -05:00
|
|
|
rule: `Host(\`${fullDomain}\`)`,
|
2024-10-27 23:36:04 -04:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2024-11-24 15:05:15 -05:00
|
|
|
if (site.type === "newt") {
|
|
|
|
const ip = site.subnet.split("/")[0];
|
|
|
|
http.services![serviceName] = {
|
|
|
|
loadBalancer: {
|
|
|
|
servers: [
|
|
|
|
{
|
|
|
|
url: `${target.method}://${ip}:${target.internalPort}`,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
};
|
|
|
|
} else if (site.type === "wireguard") {
|
|
|
|
http.services![serviceName] = {
|
|
|
|
loadBalancer: {
|
|
|
|
servers: [
|
|
|
|
{
|
|
|
|
url: `${target.method}://${target.ip}:${target.port}`,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
2024-10-26 19:57:47 -04:00
|
|
|
}
|
2024-09-28 22:50:10 -04:00
|
|
|
|
2024-10-26 19:57:47 -04:00
|
|
|
return res.status(HttpCode.OK).json({ http });
|
|
|
|
} catch (e) {
|
|
|
|
logger.error(`Failed to build traefik config: ${e}`);
|
|
|
|
return res.status(HttpCode.INTERNAL_SERVER_ERROR).json({
|
|
|
|
error: "Failed to build traefik config",
|
|
|
|
});
|
|
|
|
}
|
2024-09-28 22:50:10 -04:00
|
|
|
}
|