blob: 08dea0771c2b057d24aff3af64e867a3586de088 [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
94 enableACME = service.secure;
95 forceSSL = service.secure;
96 listen = [
97 {
98 addr = "0.0.0.0";
99 port = if service.secure then 443 else 80;
100 ssl = service.secure;
101 }
102 ];
103 };
104 }) currentPath service.secure priority
105 else if service.type == "reverseproxy"
Skyler Greye5a56792023-10-19 23:44:33 +0000106 then (lib.recursiveUpdate currentConfig {
Skyler Greyc0a62472023-10-18 23:29:13 +0000107 value.locations.${currentPath} = {
TheCodedProf5c7ee822023-10-20 19:01:28 -0400108 proxyPass = "http://${service.to}";
Skyler Greyc0a62472023-10-18 23:29:13 +0000109 proxyWebsockets = true;
110 recommendedProxySettings = true;
111 };
112 })
113 else if service.type == "php"
Skyler Greye5a56792023-10-19 23:44:33 +0000114 then (lib.recursiveUpdate currentConfig {
Skyler Greyc0a62472023-10-18 23:29:13 +0000115 value.locations.${currentPath} = {
116 root = service.root;
117 index = "index.php index.html index.htm";
118 tryFiles = "$uri $uri/ ${currentPath}index.php?$query_string =404";
119 };
120 value.locations."~ ^${currentPath}.*\.php$" = {
121 tryFiles = "$uri $uri/ ${currentPath}index.php?$query_string =404";
122 extraConfig = ''
123 include ${pkgs.nginx}/conf/fastcgi_params;
124 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
125 fastcgi_param REDIRECT_STATUS 200;
126 fastcgi_pass unix:${service.socket};
127 fastcgi_intercept_errors on;
128 ${lib.optionalString secure "fastcgi_param HTTPS on;"}
129 '';
130 };
131 })
132 else if service.type == "redirect"
Skyler Greye5a56792023-10-19 23:44:33 +0000133 then (lib.recursiveUpdate currentConfig {
Skyler Greyc0a62472023-10-18 23:29:13 +0000134 value.locations.${currentPath}.return =
135 if service.permanent
136 then "308 ${service.to}"
137 else "307 ${service.to}";
138 })
139 else if service.type == "directory"
Skyler Greye5a56792023-10-19 23:44:33 +0000140 then (lib.recursiveUpdate currentConfig {
Skyler Greyc0a62472023-10-18 23:29:13 +0000141 value.locations.${currentPath} = {
142 root = service.root;
143 index = "index.html index.htm";
144 tryFiles = "$uri $uri/ =404";
Skyler Grey7de65802023-10-19 22:23:47 +0000145 extraConfig = lib.optionalString (!service.private) "autoindex on;";
Skyler Greyc0a62472023-10-18 23:29:13 +0000146 };
147 })
148 else if service.type == "file"
Skyler Greye5a56792023-10-19 23:44:33 +0000149 then (lib.recursiveUpdate currentConfig {
Skyler Greyc0a62472023-10-18 23:29:13 +0000150 value.locations.${currentPath} = {
151 tryFiles = "${service.path} =404";
152 };
153 })
154 else if service.type == "path"
Skyler Grey7ecfdba2023-10-19 23:19:35 +0000155 then _merge service.service currentConfig service.path service.secure priority
Skyler Greyc0a62472023-10-18 23:29:13 +0000156 else if service.type == "compose"
157 then (_iterateCompose service.services currentConfig currentPath secure priority 0)
TheCodedProf5c7ee822023-10-20 19:01:28 -0400158 else if service.type == "status"
159 then (lib.recursiveUpdate currentConfig {
160 value.locations.${currentPath} = {
161 return = "${builtins.toString service.statusCode}";
162 };
163 })
Skyler Greyc0a62472023-10-18 23:29:13 +0000164 else throw "Unknown service type: ${service.type}";
165 in (services: lib.pipe services [
166 (_iterateMerge 0 [])
167 builtins.listToAttrs
168 ]);
169
170 # https://www.nginx.com/resources/wiki/start/topics/examples/full/
171
172 /**
173 Internal needs to be a string that is both a host and a port, e.g. generic:1000
174 External should only be a port
175 Protocol should be TCP or UDP
176 */
Skyler Grey00495a82023-10-20 22:23:25 +0000177 Stream = external: internal: protocol: { inherit external internal protocol; };
Skyler Greyc0a62472023-10-18 23:29:13 +0000178
179 Alias = host: alias: {
180 inherit host;
181 aliases = [ alias ];
182 type = "aliases";
183 };
184
185 Aliases = host: aliases: {
186 inherit host aliases;
187 type = "aliases";
188 };
189}
190