]> git.immae.eu Git - perso/Immae/Config/Nix.git/blob - modules/websites/php-application.nix
Upgrade nixos
[perso/Immae/Config/Nix.git] / modules / websites / php-application.nix
1 { lib, config, ... }:
2 with lib;
3 let
4 cfg = config.services.phpApplication;
5 cfgByEnv = lists.groupBy (x: x.websiteEnv) (builtins.attrValues cfg.apps);
6 in
7 {
8 options = with types; {
9 services.phpApplication.apps = mkOption {
10 default = {};
11 description = ''
12 php applications to define
13 '';
14 type = attrsOf (submodule {
15 options = {
16 varDir = mkOption {
17 type = nullOr path;
18 description = ''
19 Path to application’s vardir.
20 '';
21 };
22 varDirPaths = mkOption {
23 type = attrsOf str;
24 default = {};
25 description = ''
26 Map of additional folders => mode to create under varDir
27 '';
28 };
29 mode = mkOption {
30 type = str;
31 default = "0700";
32 description = ''
33 Mode to apply to the vardir
34 '';
35 };
36 phpSession = mkOption {
37 type = bool;
38 default = true;
39 description = "Handle phpsession files separately in vardir";
40 };
41 phpListen = mkOption {
42 type = nullOr str;
43 default = null;
44 description = "Name of the socket to listen to. Defaults to app name if null";
45 };
46 phpPool = mkOption {
47 type = attrsOf str;
48 default = {};
49 description = "Pool configuration to append";
50 };
51 phpEnv = mkOption {
52 type = attrsOf str;
53 default = {};
54 description = "Pool environment to append";
55 };
56 phpOptions = mkOption {
57 type = lines;
58 default = "";
59 description = "php configuration to append";
60 };
61 phpOpenbasedir = mkOption {
62 type = listOf path;
63 default = [];
64 description = ''
65 paths to add to php open_basedir configuration in addition to app and vardir
66 '';
67 };
68 phpWatchFiles = mkOption {
69 type = listOf path;
70 default = [];
71 description = ''
72 Path to other files to watch to trigger preStart scripts
73 '';
74 };
75 websiteEnv = mkOption {
76 type = str;
77 description = ''
78 website instance name to use
79 '';
80 };
81 httpdUser = mkOption {
82 type = str;
83 default = config.services.httpd.user;
84 description = ''
85 httpd user to run the prestart scripts as.
86 '';
87 };
88 httpdGroup = mkOption {
89 type = str;
90 default = config.services.httpd.group;
91 description = ''
92 httpd group to run the prestart scripts as.
93 '';
94 };
95 httpdWatchFiles = mkOption {
96 type = listOf path;
97 default = [];
98 description = ''
99 Path to other files to watch to trigger httpd reload
100 '';
101 };
102 app = mkOption {
103 type = path;
104 description = ''
105 Path to application root
106 '';
107 };
108 webappName = mkOption {
109 type = nullOr str;
110 default = null;
111 description = ''
112 Alias name for the app, to be used in services.websites.webappDirs
113 '';
114 };
115 webRoot = mkOption {
116 type = nullOr path;
117 description = ''
118 Path to the web root path of the application. May differ from the application itself (usually a subdirectory)
119 '';
120 };
121 preStartActions = mkOption {
122 type = listOf str;
123 default = [];
124 description = ''
125 List of actions to run as apache user at preStart when
126 whatchFiles or app dir changed.
127 '';
128 };
129 serviceDeps = mkOption {
130 type = listOf str;
131 default = [];
132 description = ''
133 List of systemd services this application depends on
134 '';
135 };
136 };
137 });
138 };
139 # Read-only variables
140 services.phpApplication.phpListenPaths = mkOption {
141 type = attrsOf path;
142 default = attrsets.mapAttrs' (name: icfg: attrsets.nameValuePair
143 name config.services.phpfpm.pools."${name}".socket
144 ) cfg.apps;
145 readOnly = true;
146 description = ''
147 Full paths to listen for php
148 '';
149 };
150 services.phpApplication.webappDirs = mkOption {
151 type = attrsOf path;
152 default = attrsets.filterAttrs (n: v: builtins.hasAttr n cfg.apps) config.services.websites.webappDirsPaths;
153 readOnly = true;
154 description = ''
155 Stable name webapp dirs for httpd
156 '';
157 };
158 };
159
160 config = {
161 services.websites.env = attrsets.mapAttrs' (name: cfgs: attrsets.nameValuePair
162 name {
163 modules = [ "proxy_fcgi" ];
164 watchPaths = builtins.concatLists (map (c: c.httpdWatchFiles) cfgs);
165 }
166 ) cfgByEnv;
167
168 services.phpfpm.pools = attrsets.mapAttrs' (name: icfg: attrsets.nameValuePair
169 name {
170 user = icfg.httpdUser;
171 group = icfg.httpdUser;
172 settings = {
173 "listen.owner" = icfg.httpdUser;
174 "listen.group" = icfg.httpdGroup;
175 "php_admin_value[open_basedir]" = builtins.concatStringsSep ":" ([icfg.app icfg.varDir] ++ icfg.phpWatchFiles ++ icfg.phpOpenbasedir);
176 }
177 // optionalAttrs (icfg.phpSession) { "php_admin_value[session.save_path]" = "${icfg.varDir}/phpSessions"; }
178 // icfg.phpPool;
179 phpOptions = config.services.phpfpm.phpOptions + icfg.phpOptions;
180 inherit (icfg) phpEnv;
181 }
182 ) cfg.apps;
183
184 services.websites.webappDirs = attrsets.mapAttrs' (name: icfg: attrsets.nameValuePair
185 (if icfg.webappName == null then name else icfg.webappName) icfg.webRoot
186 ) (attrsets.filterAttrs (n: v: !isNull v.webRoot) cfg.apps);
187
188 services.filesWatcher = attrsets.mapAttrs' (name: icfg: attrsets.nameValuePair
189 "phpfpm-${name}" {
190 restart = true;
191 paths = icfg.phpWatchFiles;
192 }
193 ) (attrsets.filterAttrs (n: v: builtins.length v.phpWatchFiles > 0) cfg.apps);
194
195 systemd.services = attrsets.mapAttrs' (name: icfg: attrsets.nameValuePair
196 "phpfpm-${name}" {
197 after = lib.mkAfter icfg.serviceDeps;
198 wants = icfg.serviceDeps;
199 preStart = lib.mkAfter (optionalString (!isNull icfg.varDir) ''
200 watchFilesChanged() {
201 ${optionalString (builtins.length icfg.phpWatchFiles == 0) "return 1"}
202 [ ! -f "${icfg.varDir}"/watchedFiles ] \
203 || ! sha512sum -c --status ${icfg.varDir}/watchedFiles
204 }
205 appDirChanged() {
206 [ ! -f "${icfg.varDir}/currentWebappDir" -o \
207 "${icfg.app}" != "$(cat ${icfg.varDir}/currentWebappDir 2>/dev/null)" ]
208 }
209 updateWatchFiles() {
210 ${optionalString (builtins.length icfg.phpWatchFiles == 0) "return 0"}
211 sha512sum ${builtins.concatStringsSep " " icfg.phpWatchFiles} > ${icfg.varDir}/watchedFiles
212 }
213
214 if watchFilesChanged || appDirChanged; then
215 pushd ${icfg.app} > /dev/null
216 ${builtins.concatStringsSep "\n " (map (c: "/run/wrappers/bin/sudo -u ${icfg.httpdUser} ${c}") icfg.preStartActions) }
217 popd > /dev/null
218 echo -n "${icfg.app}" > ${icfg.varDir}/currentWebappDir
219 updateWatchFiles
220 fi
221 '');
222 }
223 ) cfg.apps;
224
225 system.activationScripts = attrsets.mapAttrs' (name: icfg: attrsets.nameValuePair
226 name {
227 deps = [];
228 text = optionalString (!isNull icfg.varDir) ''
229 install -m ${icfg.mode} -o ${icfg.httpdUser} -g ${icfg.httpdGroup} -d ${icfg.varDir}
230 '' + optionalString (icfg.phpSession) ''
231 install -m 0700 -o ${icfg.httpdUser} -g ${icfg.httpdGroup} -d ${icfg.varDir}/phpSessions
232 '' + builtins.concatStringsSep "\n" (attrsets.mapAttrsToList (n: v: ''
233 install -m ${v} -o ${icfg.httpdUser} -g ${icfg.httpdGroup} -d ${icfg.varDir}/${n}
234 '') icfg.varDirPaths);
235 }
236 ) cfg.apps;
237 };
238 }