at 22.05-pre 9.2 kB view raw
1{ config, lib, pkgs, ... }: 2 3let 4 5 inherit (lib) mkDefault mkEnableOption mkForce mkIf mkMerge mkOption; 6 inherit (lib) literalExpression mapAttrs optional optionalString types; 7 8 cfg = config.services.limesurvey; 9 fpm = config.services.phpfpm.pools.limesurvey; 10 11 user = "limesurvey"; 12 group = config.services.httpd.group; 13 stateDir = "/var/lib/limesurvey"; 14 15 pkg = pkgs.limesurvey; 16 17 configType = with types; oneOf [ (attrsOf configType) str int bool ] // { 18 description = "limesurvey config type (str, int, bool or attribute set thereof)"; 19 }; 20 21 limesurveyConfig = pkgs.writeText "config.php" '' 22 <?php 23 return json_decode('${builtins.toJSON cfg.config}', true); 24 ?> 25 ''; 26 27 mysqlLocal = cfg.database.createLocally && cfg.database.type == "mysql"; 28 pgsqlLocal = cfg.database.createLocally && cfg.database.type == "pgsql"; 29 30in 31{ 32 # interface 33 34 options.services.limesurvey = { 35 enable = mkEnableOption "Limesurvey web application."; 36 37 database = { 38 type = mkOption { 39 type = types.enum [ "mysql" "pgsql" "odbc" "mssql" ]; 40 example = "pgsql"; 41 default = "mysql"; 42 description = "Database engine to use."; 43 }; 44 45 host = mkOption { 46 type = types.str; 47 default = "localhost"; 48 description = "Database host address."; 49 }; 50 51 port = mkOption { 52 type = types.int; 53 default = if cfg.database.type == "pgsql" then 5442 else 3306; 54 defaultText = literalExpression "3306"; 55 description = "Database host port."; 56 }; 57 58 name = mkOption { 59 type = types.str; 60 default = "limesurvey"; 61 description = "Database name."; 62 }; 63 64 user = mkOption { 65 type = types.str; 66 default = "limesurvey"; 67 description = "Database user."; 68 }; 69 70 passwordFile = mkOption { 71 type = types.nullOr types.path; 72 default = null; 73 example = "/run/keys/limesurvey-dbpassword"; 74 description = '' 75 A file containing the password corresponding to 76 <option>database.user</option>. 77 ''; 78 }; 79 80 socket = mkOption { 81 type = types.nullOr types.path; 82 default = 83 if mysqlLocal then "/run/mysqld/mysqld.sock" 84 else if pgsqlLocal then "/run/postgresql" 85 else null 86 ; 87 defaultText = literalExpression "/run/mysqld/mysqld.sock"; 88 description = "Path to the unix socket file to use for authentication."; 89 }; 90 91 createLocally = mkOption { 92 type = types.bool; 93 default = cfg.database.type == "mysql"; 94 defaultText = literalExpression "true"; 95 description = '' 96 Create the database and database user locally. 97 This currently only applies if database type "mysql" is selected. 98 ''; 99 }; 100 }; 101 102 virtualHost = mkOption { 103 type = types.submodule (import ../web-servers/apache-httpd/vhost-options.nix); 104 example = literalExpression '' 105 { 106 hostName = "survey.example.org"; 107 adminAddr = "webmaster@example.org"; 108 forceSSL = true; 109 enableACME = true; 110 } 111 ''; 112 description = '' 113 Apache configuration can be done by adapting <literal>services.httpd.virtualHosts.&lt;name&gt;</literal>. 114 See <xref linkend="opt-services.httpd.virtualHosts"/> for further information. 115 ''; 116 }; 117 118 poolConfig = mkOption { 119 type = with types; attrsOf (oneOf [ str int bool ]); 120 default = { 121 "pm" = "dynamic"; 122 "pm.max_children" = 32; 123 "pm.start_servers" = 2; 124 "pm.min_spare_servers" = 2; 125 "pm.max_spare_servers" = 4; 126 "pm.max_requests" = 500; 127 }; 128 description = '' 129 Options for the LimeSurvey PHP pool. See the documentation on <literal>php-fpm.conf</literal> 130 for details on configuration directives. 131 ''; 132 }; 133 134 config = mkOption { 135 type = configType; 136 default = {}; 137 description = '' 138 LimeSurvey configuration. Refer to 139 <link xlink:href="https://manual.limesurvey.org/Optional_settings"/> 140 for details on supported values. 141 ''; 142 }; 143 }; 144 145 # implementation 146 147 config = mkIf cfg.enable { 148 149 assertions = [ 150 { assertion = cfg.database.createLocally -> cfg.database.type == "mysql"; 151 message = "services.limesurvey.createLocally is currently only supported for database type 'mysql'"; 152 } 153 { assertion = cfg.database.createLocally -> cfg.database.user == user; 154 message = "services.limesurvey.database.user must be set to ${user} if services.limesurvey.database.createLocally is set true"; 155 } 156 { assertion = cfg.database.createLocally -> cfg.database.socket != null; 157 message = "services.limesurvey.database.socket must be set if services.limesurvey.database.createLocally is set to true"; 158 } 159 { assertion = cfg.database.createLocally -> cfg.database.passwordFile == null; 160 message = "a password cannot be specified if services.limesurvey.database.createLocally is set to true"; 161 } 162 ]; 163 164 services.limesurvey.config = mapAttrs (name: mkDefault) { 165 runtimePath = "${stateDir}/tmp/runtime"; 166 components = { 167 db = { 168 connectionString = "${cfg.database.type}:dbname=${cfg.database.name};host=${if pgsqlLocal then cfg.database.socket else cfg.database.host};port=${toString cfg.database.port}" + 169 optionalString mysqlLocal ";socket=${cfg.database.socket}"; 170 username = cfg.database.user; 171 password = mkIf (cfg.database.passwordFile != null) "file_get_contents(\"${toString cfg.database.passwordFile}\");"; 172 tablePrefix = "limesurvey_"; 173 }; 174 assetManager.basePath = "${stateDir}/tmp/assets"; 175 urlManager = { 176 urlFormat = "path"; 177 showScriptName = false; 178 }; 179 }; 180 config = { 181 tempdir = "${stateDir}/tmp"; 182 uploaddir = "${stateDir}/upload"; 183 force_ssl = mkIf (cfg.virtualHost.addSSL || cfg.virtualHost.forceSSL || cfg.virtualHost.onlySSL) "on"; 184 config.defaultlang = "en"; 185 }; 186 }; 187 188 services.mysql = mkIf mysqlLocal { 189 enable = true; 190 package = mkDefault pkgs.mariadb; 191 ensureDatabases = [ cfg.database.name ]; 192 ensureUsers = [ 193 { name = cfg.database.user; 194 ensurePermissions = { 195 "${cfg.database.name}.*" = "SELECT, CREATE, INSERT, UPDATE, DELETE, ALTER, DROP, INDEX"; 196 }; 197 } 198 ]; 199 }; 200 201 services.phpfpm.pools.limesurvey = { 202 inherit user group; 203 phpEnv.LIMESURVEY_CONFIG = "${limesurveyConfig}"; 204 settings = { 205 "listen.owner" = config.services.httpd.user; 206 "listen.group" = config.services.httpd.group; 207 } // cfg.poolConfig; 208 }; 209 210 services.httpd = { 211 enable = true; 212 adminAddr = mkDefault cfg.virtualHost.adminAddr; 213 extraModules = [ "proxy_fcgi" ]; 214 virtualHosts.${cfg.virtualHost.hostName} = mkMerge [ cfg.virtualHost { 215 documentRoot = mkForce "${pkg}/share/limesurvey"; 216 extraConfig = '' 217 Alias "/tmp" "${stateDir}/tmp" 218 <Directory "${stateDir}"> 219 AllowOverride all 220 Require all granted 221 Options -Indexes +FollowSymlinks 222 </Directory> 223 224 Alias "/upload" "${stateDir}/upload" 225 <Directory "${stateDir}/upload"> 226 AllowOverride all 227 Require all granted 228 Options -Indexes 229 </Directory> 230 231 <Directory "${pkg}/share/limesurvey"> 232 <FilesMatch "\.php$"> 233 <If "-f %{REQUEST_FILENAME}"> 234 SetHandler "proxy:unix:${fpm.socket}|fcgi://localhost/" 235 </If> 236 </FilesMatch> 237 238 AllowOverride all 239 Options -Indexes 240 DirectoryIndex index.php 241 </Directory> 242 ''; 243 } ]; 244 }; 245 246 systemd.tmpfiles.rules = [ 247 "d ${stateDir} 0750 ${user} ${group} - -" 248 "d ${stateDir}/tmp 0750 ${user} ${group} - -" 249 "d ${stateDir}/tmp/assets 0750 ${user} ${group} - -" 250 "d ${stateDir}/tmp/runtime 0750 ${user} ${group} - -" 251 "d ${stateDir}/tmp/upload 0750 ${user} ${group} - -" 252 "C ${stateDir}/upload 0750 ${user} ${group} - ${pkg}/share/limesurvey/upload" 253 ]; 254 255 systemd.services.limesurvey-init = { 256 wantedBy = [ "multi-user.target" ]; 257 before = [ "phpfpm-limesurvey.service" ]; 258 after = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service"; 259 environment.LIMESURVEY_CONFIG = limesurveyConfig; 260 script = '' 261 # update or install the database as required 262 ${pkgs.php}/bin/php ${pkg}/share/limesurvey/application/commands/console.php updatedb || \ 263 ${pkgs.php}/bin/php ${pkg}/share/limesurvey/application/commands/console.php install admin password admin admin@example.com verbose 264 ''; 265 serviceConfig = { 266 User = user; 267 Group = group; 268 Type = "oneshot"; 269 }; 270 }; 271 272 systemd.services.httpd.after = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service"; 273 274 users.users.${user} = { 275 group = group; 276 isSystemUser = true; 277 }; 278 279 }; 280}