mirror of
https://github.com/fosrl/pangolin.git
synced 2026-09-17 08:09:47 +02:00
122 lines
4.0 KiB
TypeScript
122 lines
4.0 KiB
TypeScript
import logger from "@server/logger";
|
|
import config from "@server/lib/config";
|
|
import {
|
|
buildHostRule,
|
|
appendPathMatch,
|
|
computeRoutePriority
|
|
} from "@server/lib/traefik/rule";
|
|
|
|
export type RedirectRouteRow = {
|
|
redirectId: number;
|
|
/** Host the redirect listens on (resource fullDomain or subdomain.baseDomain). */
|
|
fullDomain: string;
|
|
hasSubdomain: boolean;
|
|
wildcard: boolean | null;
|
|
ssl: boolean;
|
|
matchPath: string;
|
|
pathMatchType: string;
|
|
priority: number | null;
|
|
domainCertResolver?: string | null;
|
|
preferWildcardCert?: boolean | null;
|
|
};
|
|
|
|
/**
|
|
* Add Traefik routers for redirects. Like resources, every request is sent
|
|
* through badger, which looks up the redirect by host/path, applies any
|
|
* path rewrite and answers with the redirect itself - Traefik only has to
|
|
* match the host (+ path) and terminate TLS. Redirects have no backend, so
|
|
* the routers point at Traefik's built-in noop@internal service.
|
|
* TLS/cert-resolver handling differs between the OSS and private
|
|
* (pangolin-dns aware) config generators, so callers resolve that via
|
|
* resolveTls - returning null skips the redirect (no valid cert yet).
|
|
*/
|
|
export function buildRedirectConfig(params: {
|
|
config_output: any;
|
|
redirects: RedirectRouteRow[];
|
|
badgerMiddlewareName: string;
|
|
redirectHttpsMiddlewareName: string;
|
|
resolveTls: (row: RedirectRouteRow) => any | null;
|
|
}): void {
|
|
const {
|
|
config_output,
|
|
redirects,
|
|
badgerMiddlewareName,
|
|
redirectHttpsMiddlewareName,
|
|
resolveTls
|
|
} = params;
|
|
|
|
if (redirects.length === 0) {
|
|
return;
|
|
}
|
|
|
|
const httpEntrypoint = config.getRawConfig().traefik.http_entrypoint;
|
|
const httpsEntrypoint = config.getRawConfig().traefik.https_entrypoint;
|
|
const additionalMiddlewares =
|
|
config.getRawConfig().traefik.additional_middlewares || [];
|
|
const routerMiddlewares = [badgerMiddlewareName, ...additionalMiddlewares];
|
|
|
|
for (const redirect of redirects) {
|
|
const routerName = `redirect-${redirect.redirectId}-router`;
|
|
|
|
let tls: any = {};
|
|
if (redirect.ssl) {
|
|
tls = resolveTls(redirect);
|
|
if (tls === null) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (!config_output.http.routers) {
|
|
config_output.http.routers = {};
|
|
}
|
|
|
|
if (redirect.pathMatchType === "regex") {
|
|
try {
|
|
new RegExp(redirect.matchPath);
|
|
} catch {
|
|
logger.debug(
|
|
`Invalid regex pattern in redirect ${redirect.redirectId} match path: ${redirect.matchPath}`
|
|
);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
const rule = appendPathMatch(
|
|
buildHostRule(redirect.fullDomain, redirect.wildcard),
|
|
redirect.matchPath,
|
|
redirect.pathMatchType
|
|
);
|
|
|
|
// A redirect attached to a resource must win over that resource's
|
|
// router at the same host/path specificity, so nudge derived
|
|
// priorities up by one. Explicit priorities are used as-is.
|
|
const hasExplicitPriority =
|
|
!!redirect.priority && redirect.priority !== 100;
|
|
const priority =
|
|
computeRoutePriority(
|
|
redirect.priority,
|
|
redirect.matchPath,
|
|
redirect.pathMatchType
|
|
) + (hasExplicitPriority ? 0 : 1);
|
|
|
|
if (redirect.ssl) {
|
|
config_output.http.routers[`${routerName}-redirect`] = {
|
|
entryPoints: [httpEntrypoint],
|
|
middlewares: [redirectHttpsMiddlewareName],
|
|
service: "noop@internal",
|
|
rule,
|
|
priority
|
|
};
|
|
}
|
|
|
|
config_output.http.routers[routerName] = {
|
|
entryPoints: [redirect.ssl ? httpsEntrypoint : httpEntrypoint],
|
|
middlewares: routerMiddlewares,
|
|
service: "noop@internal",
|
|
rule,
|
|
priority,
|
|
...(redirect.ssl ? { tls } : {})
|
|
};
|
|
}
|
|
}
|