refactor(reverse-proxy): use route registry
This commit was merged in pull request #61.
This commit is contained in:
@@ -1,73 +1,71 @@
|
||||
{ config, lib, my, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
registry,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.modules.reverse-proxy;
|
||||
inherit (my) reverseProxyRoutes;
|
||||
|
||||
enabledRoutes = lib.filterAttrs (_: route: route.enable) cfg.routes;
|
||||
routesByHost = lib.groupBy (route: route.host) (lib.attrValues enabledRoutes);
|
||||
mkRegistryPath = registryName: host: entry: "registry.${registryName}.${host}.${entry}";
|
||||
|
||||
rootRoutesByHost = lib.mapAttrs (
|
||||
host: routes:
|
||||
mkBackend = address: route: "http://${address}:${toString route.port}";
|
||||
|
||||
mkVirtualHost = host: entry:
|
||||
let
|
||||
roots = builtins.filter (route: route.path == "/") routes;
|
||||
compositeEntry = registry.join host entry;
|
||||
route = compositeEntry.route;
|
||||
address = compositeEntry.address or (throw "no matching entry in registry.address for ${mkRegistryPath "route" host entry}");
|
||||
in
|
||||
if roots == [ ] then null else builtins.head roots
|
||||
) routesByHost;
|
||||
|
||||
hasDuplicatePaths = routes:
|
||||
let
|
||||
paths = map (route: route.path) routes;
|
||||
in
|
||||
lib.length paths != lib.length (lib.unique paths);
|
||||
|
||||
hasUnresolvedBackend = route: route.backend == null;
|
||||
|
||||
mkLocation = route: {
|
||||
proxyPass = route.backend;
|
||||
proxyWebsockets = route.websockets;
|
||||
};
|
||||
|
||||
mkVirtualHost = host: routes:
|
||||
let
|
||||
rootRoute = rootRoutesByHost.${host};
|
||||
in
|
||||
lib.mkIf (rootRoute != null) {
|
||||
forceSSL = rootRoute.tls;
|
||||
enableACME = rootRoute.tls;
|
||||
serverAliases = rootRoute.aliases;
|
||||
locations = lib.listToAttrs (
|
||||
map (route: lib.nameValuePair route.path (mkLocation route)) (
|
||||
builtins.filter (route: route.backend != null) routes
|
||||
)
|
||||
);
|
||||
{
|
||||
inherit host entry;
|
||||
name = route.domain;
|
||||
value = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
locations."/".proxyPass = mkBackend address route;
|
||||
};
|
||||
};
|
||||
|
||||
generatedVirtualHostList = lib.concatLists (
|
||||
lib.mapAttrsToList (
|
||||
host: routes:
|
||||
lib.mapAttrsToList (entry: _route: mkVirtualHost host entry) routes
|
||||
) registry.route
|
||||
);
|
||||
|
||||
virtualHostsByDomain = lib.foldl' (
|
||||
acc: virtualHost:
|
||||
acc
|
||||
// {
|
||||
${virtualHost.name} = (acc.${virtualHost.name} or [ ]) ++ [ virtualHost ];
|
||||
}
|
||||
) { } generatedVirtualHostList;
|
||||
|
||||
duplicateDomainMessages = lib.concatLists (
|
||||
lib.mapAttrsToList (
|
||||
domain: virtualHosts:
|
||||
if lib.length virtualHosts > 1 then
|
||||
[
|
||||
"duplicate route domain ${domain} in ${lib.concatMapStringsSep " and " (virtualHost: mkRegistryPath "route" virtualHost.host virtualHost.entry) virtualHosts}"
|
||||
]
|
||||
else
|
||||
[ ]
|
||||
) virtualHostsByDomain
|
||||
);
|
||||
|
||||
generatedVirtualHosts =
|
||||
if duplicateDomainMessages != [ ] then
|
||||
throw (builtins.head duplicateDomainMessages)
|
||||
else
|
||||
lib.listToAttrs generatedVirtualHostList;
|
||||
in
|
||||
{
|
||||
options.modules.reverse-proxy = {
|
||||
enable = lib.mkEnableOption "the Nginx reverse proxy route realizer";
|
||||
|
||||
routes = lib.mkOption {
|
||||
type = lib.types.attrsOf reverseProxyRoutes.routeType;
|
||||
default = { };
|
||||
description = "Reverse-proxy route declarations keyed by stable route name.";
|
||||
};
|
||||
enable = lib.mkEnableOption "the public edge reverse proxy";
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
assertions =
|
||||
lib.mapAttrsToList (host: rootRoute: {
|
||||
assertion = rootRoute != null;
|
||||
message = "modules.reverse-proxy.routes for ${host} must include a primary / route.";
|
||||
}) rootRoutesByHost
|
||||
++ lib.mapAttrsToList (host: routes: {
|
||||
assertion = !hasDuplicatePaths routes;
|
||||
message = "modules.reverse-proxy.routes for ${host} must not declare the same path more than once.";
|
||||
}) routesByHost
|
||||
++ map (route: {
|
||||
assertion = !hasUnresolvedBackend route;
|
||||
message = "modules.reverse-proxy.routes for ${route.host}${route.path} must resolve to a backend URL before actualization.";
|
||||
}) (builtins.filter hasUnresolvedBackend (lib.attrValues enabledRoutes));
|
||||
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
80
|
||||
443
|
||||
@@ -91,7 +89,7 @@ in
|
||||
recommendedOptimisation = true;
|
||||
recommendedProxySettings = true;
|
||||
recommendedTlsSettings = true;
|
||||
virtualHosts = lib.mapAttrs mkVirtualHost routesByHost;
|
||||
virtualHosts = generatedVirtualHosts;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user