blob: 376ed02e6c4d7cfa4d57e4586119fe5c5817d8ec [file] [log] [blame]
Skyler Greyc0a62472023-10-18 23:29:13 +00001{ pkgs, ... }: let
2 lib = pkgs.lib;
3in {
4 Host = host: service: {
5 inherit host service;
6 extraHosts = [];
7 secure = true;
8 type = "hosts";
9 };
10 Hosts = hosts: service: {
11 inherit service;
Skyler Grey7de65802023-10-19 22:23:47 +000012 host = builtins.elemAt hosts 0;
13 extraHosts = builtins.tail hosts;
Skyler Greyc0a62472023-10-18 23:29:13 +000014 secure = true;
15 type = "hosts";
16 };
17 InsecureHost = host: service: {
18 inherit host service;
19 extraHosts = [];
20 secure = false;
21 type = "hosts";
22 };
23 InsecureHosts = hosts: service: {
24 inherit service;
Skyler Grey7de65802023-10-19 22:23:47 +000025 host = builtins.elemAt hosts 0;
26 extraHosts = builtins.tail hosts;
Skyler Greyc0a62472023-10-18 23:29:13 +000027 secure = false;
28 type = "hosts";
29 };
30 ReverseProxy = to: {
31 inherit to;
32 type = "reverseproxy";
33 };
34 PHP = root: socket: {
35 inherit root socket;
36 type = "php";
37 };
Skyler Grey2a7dd0e2023-10-20 00:07:45 +000038 Redirect = to: {
39 inherit to;
40 permanent = false;
41 type = "redirect";
42 };
43 RedirectPermanent = to: {
44 inherit to;
45 permanent = true;
Skyler Greyc0a62472023-10-18 23:29:13 +000046 type = "redirect";
47 };
48 Directory = root: {
49 inherit root;
50 private = false;
51 type = "directory";
52 };
53 PrivateDirectory = root: {
54 inherit root;
55 private = true;
56 type = "directory";
57 };
58 File = path: {
59 inherit path;
60 type = "file";
61 };
62 Compose = services: {
63 inherit services;
64 type = "compose";
65 };
66 Path = path: service: {
67 inherit path service;
68 type = "path";
69 };
TheCodedProf5c7ee822023-10-20 19:01:28 -040070 Status = statusCode: {
71 inherit statusCode;
72 type = "status";
73 };
Skyler Greyc0a62472023-10-18 23:29:13 +000074
75 Merge = let
76 # builtins.length and count up
77 _iterateCompose = services: currentConfig: currentPath: secure: priority: i:
Skyler Grey00495a82023-10-20 22:23:25 +000078 if i < builtins.length services
TheCodedProf5c7ee822023-10-20 19:01:28 -040079 then _iterateCompose services (_merge (builtins.elemAt services i) currentConfig currentPath secure (priority+i)) currentPath secure priority (i+1)
Skyler Grey00495a82023-10-20 22:23:25 +000080 else currentConfig;
Skyler Greyc0a62472023-10-18 23:29:13 +000081
82 _iterateMerge = i: current: services:
Skyler Grey00495a82023-10-20 22:23:25 +000083 if i < builtins.length services
84 then _iterateMerge (i+1) (current++[(_merge (builtins.elemAt services i) {} "/" true 1000)]) services
85 else current;
Skyler Greyc0a62472023-10-18 23:29:13 +000086
87 _merge = service: currentConfig: currentPath: secure: priority:
88 if service.type == "hosts"
Skyler Greye5a56792023-10-19 23:44:33 +000089 then _merge service.service (lib.recursiveUpdate currentConfig {
Skyler Greyc0a62472023-10-18 23:29:13 +000090 name = service.host;
91 value = {
92 serverAliases = service.extraHosts;
93
Skyler Grey2efad742023-10-22 10:58:12 +000094 enableACME = true;
Skyler Greyc0a62472023-10-18 23:29:13 +000095 forceSSL = service.secure;
Skyler Grey2efad742023-10-22 10:58:12 +000096 addSSL = !service.secure;
97 listenAddresses = [ "0.0.0.0" ];
Skyler Greyc0a62472023-10-18 23:29:13 +000098 };
99 }) currentPath service.secure priority
100 else if service.type == "reverseproxy"
Skyler Greye5a56792023-10-19 23:44:33 +0000101 then (lib.recursiveUpdate currentConfig {
Skyler Greyc0a62472023-10-18 23:29:13 +0000102 value.locations.${currentPath} = {
TheCodedProf5c7ee822023-10-20 19:01:28 -0400103 proxyPass = "http://${service.to}";
Skyler Greyc0a62472023-10-18 23:29:13 +0000104 proxyWebsockets = true;
105 recommendedProxySettings = true;
106 };
107 })
108 else if service.type == "php"
Skyler Greye5a56792023-10-19 23:44:33 +0000109 then (lib.recursiveUpdate currentConfig {
Skyler Greyc0a62472023-10-18 23:29:13 +0000110 value.locations.${currentPath} = {
111 root = service.root;
112 index = "index.php index.html index.htm";
113 tryFiles = "$uri $uri/ ${currentPath}index.php?$query_string =404";
114 };
115 value.locations."~ ^${currentPath}.*\.php$" = {
116 tryFiles = "$uri $uri/ ${currentPath}index.php?$query_string =404";
117 extraConfig = ''
118 include ${pkgs.nginx}/conf/fastcgi_params;
119 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
120 fastcgi_param REDIRECT_STATUS 200;
121 fastcgi_pass unix:${service.socket};
122 fastcgi_intercept_errors on;
123 ${lib.optionalString secure "fastcgi_param HTTPS on;"}
124 '';
125 };
126 })
127 else if service.type == "redirect"
Skyler Greye5a56792023-10-19 23:44:33 +0000128 then (lib.recursiveUpdate currentConfig {
Skyler Greyc0a62472023-10-18 23:29:13 +0000129 value.locations.${currentPath}.return =
130 if service.permanent
131 then "308 ${service.to}"
132 else "307 ${service.to}";
133 })
134 else if service.type == "directory"
Skyler Greye5a56792023-10-19 23:44:33 +0000135 then (lib.recursiveUpdate currentConfig {
Skyler Greyc0a62472023-10-18 23:29:13 +0000136 value.locations.${currentPath} = {
137 root = service.root;
138 index = "index.html index.htm";
139 tryFiles = "$uri $uri/ =404";
Skyler Grey7de65802023-10-19 22:23:47 +0000140 extraConfig = lib.optionalString (!service.private) "autoindex on;";
Skyler Greyc0a62472023-10-18 23:29:13 +0000141 };
142 })
143 else if service.type == "file"
Skyler Greye5a56792023-10-19 23:44:33 +0000144 then (lib.recursiveUpdate currentConfig {
Skyler Greyc0a62472023-10-18 23:29:13 +0000145 value.locations.${currentPath} = {
146 tryFiles = "${service.path} =404";
147 };
148 })
149 else if service.type == "path"
Skyler Grey7ecfdba2023-10-19 23:19:35 +0000150 then _merge service.service currentConfig service.path service.secure priority
Skyler Greyc0a62472023-10-18 23:29:13 +0000151 else if service.type == "compose"
152 then (_iterateCompose service.services currentConfig currentPath secure priority 0)
TheCodedProf5c7ee822023-10-20 19:01:28 -0400153 else if service.type == "status"
154 then (lib.recursiveUpdate currentConfig {
155 value.locations.${currentPath} = {
156 return = "${builtins.toString service.statusCode}";
157 };
158 })
Skyler Greyc0a62472023-10-18 23:29:13 +0000159 else throw "Unknown service type: ${service.type}";
160 in (services: lib.pipe services [
161 (_iterateMerge 0 [])
162 builtins.listToAttrs
163 ]);
164
165 # https://www.nginx.com/resources/wiki/start/topics/examples/full/
166
167 /**
168 Internal needs to be a string that is both a host and a port, e.g. generic:1000
169 External should only be a port
170 Protocol should be TCP or UDP
171 */
Skyler Grey00495a82023-10-20 22:23:25 +0000172 Stream = external: internal: protocol: { inherit external internal protocol; };
Skyler Greyc0a62472023-10-18 23:29:13 +0000173
174 Alias = host: alias: {
175 inherit host;
176 aliases = [ alias ];
177 type = "aliases";
178 };
179
180 Aliases = host: aliases: {
181 inherit host aliases;
182 type = "aliases";
183 };
184}
185