at 22.05-pre 17 kB view raw
1{ config, pkgs, lib, ... }: 2 3let 4 inherit (lib) mkDefault mkEnableOption mkForce mkIf mkMerge mkOption types maintainers recursiveUpdate; 5 inherit (lib) any attrValues concatMapStrings concatMapStringsSep flatten literalExpression; 6 inherit (lib) filterAttrs mapAttrs mapAttrs' mapAttrsToList nameValuePair optional optionalAttrs optionalString; 7 8 cfg = migrateOldAttrs config.services.dokuwiki; 9 eachSite = cfg.sites; 10 user = "dokuwiki"; 11 webserver = config.services.${cfg.webserver}; 12 stateDir = hostName: "/var/lib/dokuwiki/${hostName}/data"; 13 14 # Migrate config.services.dokuwiki.<hostName> to config.services.dokuwiki.sites.<hostName> 15 oldSites = filterAttrs (o: _: o != "sites" && o != "webserver"); 16 migrateOldAttrs = cfg: cfg // { sites = cfg.sites // oldSites cfg; }; 17 18 dokuwikiAclAuthConfig = hostName: cfg: pkgs.writeText "acl.auth-${hostName}.php" '' 19 # acl.auth.php 20 # <?php exit()?> 21 # 22 # Access Control Lists 23 # 24 ${toString cfg.acl} 25 ''; 26 27 dokuwikiLocalConfig = hostName: cfg: pkgs.writeText "local-${hostName}.php" '' 28 <?php 29 $conf['savedir'] = '${cfg.stateDir}'; 30 $conf['superuser'] = '${toString cfg.superUser}'; 31 $conf['useacl'] = '${toString cfg.aclUse}'; 32 $conf['disableactions'] = '${cfg.disableActions}'; 33 ${toString cfg.extraConfig} 34 ''; 35 36 dokuwikiPluginsLocalConfig = hostName: cfg: pkgs.writeText "plugins.local-${hostName}.php" '' 37 <?php 38 ${cfg.pluginsConfig} 39 ''; 40 41 42 pkg = hostName: cfg: pkgs.stdenv.mkDerivation rec { 43 pname = "dokuwiki-${hostName}"; 44 version = src.version; 45 src = cfg.package; 46 47 installPhase = '' 48 mkdir -p $out 49 cp -r * $out/ 50 51 # symlink the dokuwiki config 52 ln -s ${dokuwikiLocalConfig hostName cfg} $out/share/dokuwiki/local.php 53 54 # symlink plugins config 55 ln -s ${dokuwikiPluginsLocalConfig hostName cfg} $out/share/dokuwiki/plugins.local.php 56 57 # symlink acl 58 ln -s ${dokuwikiAclAuthConfig hostName cfg} $out/share/dokuwiki/acl.auth.php 59 60 # symlink additional plugin(s) and templates(s) 61 ${concatMapStringsSep "\n" (template: "ln -s ${template} $out/share/dokuwiki/lib/tpl/${template.name}") cfg.templates} 62 ${concatMapStringsSep "\n" (plugin: "ln -s ${plugin} $out/share/dokuwiki/lib/plugins/${plugin.name}") cfg.plugins} 63 ''; 64 }; 65 66 siteOpts = { config, lib, name, ... }: 67 { 68 options = { 69 enable = mkEnableOption "DokuWiki web application."; 70 71 package = mkOption { 72 type = types.package; 73 default = pkgs.dokuwiki; 74 defaultText = literalExpression "pkgs.dokuwiki"; 75 description = "Which DokuWiki package to use."; 76 }; 77 78 stateDir = mkOption { 79 type = types.path; 80 default = "/var/lib/dokuwiki/${name}/data"; 81 description = "Location of the DokuWiki state directory."; 82 }; 83 84 acl = mkOption { 85 type = types.nullOr types.lines; 86 default = null; 87 example = "* @ALL 8"; 88 description = '' 89 Access Control Lists: see <link xlink:href="https://www.dokuwiki.org/acl"/> 90 Mutually exclusive with services.dokuwiki.aclFile 91 Set this to a value other than null to take precedence over aclFile option. 92 93 Warning: Consider using aclFile instead if you do not 94 want to store the ACL in the world-readable Nix store. 95 ''; 96 }; 97 98 aclFile = mkOption { 99 type = with types; nullOr str; 100 default = if (config.aclUse && config.acl == null) then "/var/lib/dokuwiki/${name}/acl.auth.php" else null; 101 description = '' 102 Location of the dokuwiki acl rules. Mutually exclusive with services.dokuwiki.acl 103 Mutually exclusive with services.dokuwiki.acl which is preferred. 104 Consult documentation <link xlink:href="https://www.dokuwiki.org/acl"/> for further instructions. 105 Example: <link xlink:href="https://github.com/splitbrain/dokuwiki/blob/master/conf/acl.auth.php.dist"/> 106 ''; 107 example = "/var/lib/dokuwiki/${name}/acl.auth.php"; 108 }; 109 110 aclUse = mkOption { 111 type = types.bool; 112 default = true; 113 description = '' 114 Necessary for users to log in into the system. 115 Also limits anonymous users. When disabled, 116 everyone is able to create and edit content. 117 ''; 118 }; 119 120 pluginsConfig = mkOption { 121 type = types.lines; 122 default = '' 123 $plugins['authad'] = 0; 124 $plugins['authldap'] = 0; 125 $plugins['authmysql'] = 0; 126 $plugins['authpgsql'] = 0; 127 ''; 128 description = '' 129 List of the dokuwiki (un)loaded plugins. 130 ''; 131 }; 132 133 superUser = mkOption { 134 type = types.nullOr types.str; 135 default = "@admin"; 136 description = '' 137 You can set either a username, a list of usernames (admin1,admin2), 138 or the name of a group by prepending an @ char to the groupname 139 Consult documentation <link xlink:href="https://www.dokuwiki.org/config:superuser"/> for further instructions. 140 ''; 141 }; 142 143 usersFile = mkOption { 144 type = with types; nullOr str; 145 default = if config.aclUse then "/var/lib/dokuwiki/${name}/users.auth.php" else null; 146 description = '' 147 Location of the dokuwiki users file. List of users. Format: 148 login:passwordhash:Real Name:email:groups,comma,separated 149 Create passwordHash easily by using:$ mkpasswd -5 password `pwgen 8 1` 150 Example: <link xlink:href="https://github.com/splitbrain/dokuwiki/blob/master/conf/users.auth.php.dist"/> 151 ''; 152 example = "/var/lib/dokuwiki/${name}/users.auth.php"; 153 }; 154 155 disableActions = mkOption { 156 type = types.nullOr types.str; 157 default = ""; 158 example = "search,register"; 159 description = '' 160 Disable individual action modes. Refer to 161 <link xlink:href="https://www.dokuwiki.org/config:action_modes"/> 162 for details on supported values. 163 ''; 164 }; 165 166 plugins = mkOption { 167 type = types.listOf types.path; 168 default = []; 169 description = '' 170 List of path(s) to respective plugin(s) which are copied from the 'plugin' directory. 171 <note><para>These plugins need to be packaged before use, see example.</para></note> 172 ''; 173 example = literalExpression '' 174 let 175 # Let's package the icalevents plugin 176 plugin-icalevents = pkgs.stdenv.mkDerivation { 177 name = "icalevents"; 178 # Download the plugin from the dokuwiki site 179 src = pkgs.fetchurl { 180 url = "https://github.com/real-or-random/dokuwiki-plugin-icalevents/releases/download/2017-06-16/dokuwiki-plugin-icalevents-2017-06-16.zip"; 181 sha256 = "e40ed7dd6bbe7fe3363bbbecb4de481d5e42385b5a0f62f6a6ce6bf3a1f9dfa8"; 182 }; 183 sourceRoot = "."; 184 # We need unzip to build this package 185 buildInputs = [ pkgs.unzip ]; 186 # Installing simply means copying all files to the output directory 187 installPhase = "mkdir -p $out; cp -R * $out/"; 188 }; 189 # And then pass this theme to the plugin list like this: 190 in [ plugin-icalevents ] 191 ''; 192 }; 193 194 templates = mkOption { 195 type = types.listOf types.path; 196 default = []; 197 description = '' 198 List of path(s) to respective template(s) which are copied from the 'tpl' directory. 199 <note><para>These templates need to be packaged before use, see example.</para></note> 200 ''; 201 example = literalExpression '' 202 let 203 # Let's package the bootstrap3 theme 204 template-bootstrap3 = pkgs.stdenv.mkDerivation { 205 name = "bootstrap3"; 206 # Download the theme from the dokuwiki site 207 src = pkgs.fetchurl { 208 url = "https://github.com/giterlizzi/dokuwiki-template-bootstrap3/archive/v2019-05-22.zip"; 209 sha256 = "4de5ff31d54dd61bbccaf092c9e74c1af3a4c53e07aa59f60457a8f00cfb23a6"; 210 }; 211 # We need unzip to build this package 212 buildInputs = [ pkgs.unzip ]; 213 # Installing simply means copying all files to the output directory 214 installPhase = "mkdir -p $out; cp -R * $out/"; 215 }; 216 # And then pass this theme to the template list like this: 217 in [ template-bootstrap3 ] 218 ''; 219 }; 220 221 poolConfig = mkOption { 222 type = with types; attrsOf (oneOf [ str int bool ]); 223 default = { 224 "pm" = "dynamic"; 225 "pm.max_children" = 32; 226 "pm.start_servers" = 2; 227 "pm.min_spare_servers" = 2; 228 "pm.max_spare_servers" = 4; 229 "pm.max_requests" = 500; 230 }; 231 description = '' 232 Options for the DokuWiki PHP pool. See the documentation on <literal>php-fpm.conf</literal> 233 for details on configuration directives. 234 ''; 235 }; 236 237 extraConfig = mkOption { 238 type = types.nullOr types.lines; 239 default = null; 240 example = '' 241 $conf['title'] = 'My Wiki'; 242 $conf['userewrite'] = 1; 243 ''; 244 description = '' 245 DokuWiki configuration. Refer to 246 <link xlink:href="https://www.dokuwiki.org/config"/> 247 for details on supported values. 248 ''; 249 }; 250 251 }; 252 253 }; 254in 255{ 256 # interface 257 options = { 258 services.dokuwiki = mkOption { 259 type = types.submodule { 260 # Used to support old interface 261 freeformType = types.attrsOf (types.submodule siteOpts); 262 263 # New interface 264 options.sites = mkOption { 265 type = types.attrsOf (types.submodule siteOpts); 266 default = {}; 267 description = "Specification of one or more DokuWiki sites to serve"; 268 }; 269 270 options.webserver = mkOption { 271 type = types.enum [ "nginx" "caddy" ]; 272 default = "nginx"; 273 description = '' 274 Whether to use nginx or caddy for virtual host management. 275 276 Further nginx configuration can be done by adapting <literal>services.nginx.virtualHosts.&lt;name&gt;</literal>. 277 See <xref linkend="opt-services.nginx.virtualHosts"/> for further information. 278 279 Further apache2 configuration can be done by adapting <literal>services.httpd.virtualHosts.&lt;name&gt;</literal>. 280 See <xref linkend="opt-services.httpd.virtualHosts"/> for further information. 281 ''; 282 }; 283 }; 284 default = {}; 285 description = "DokuWiki configuration"; 286 }; 287 288 }; 289 290 # implementation 291 config = mkIf (eachSite != {}) (mkMerge [{ 292 293 assertions = flatten (mapAttrsToList (hostName: cfg: 294 [{ 295 assertion = cfg.aclUse -> (cfg.acl != null || cfg.aclFile != null); 296 message = "Either services.dokuwiki.sites.${hostName}.acl or services.dokuwiki.sites.${hostName}.aclFile is mandatory if aclUse true"; 297 } 298 { 299 assertion = cfg.usersFile != null -> cfg.aclUse != false; 300 message = "services.dokuwiki.sites.${hostName}.aclUse must must be true if usersFile is not null"; 301 } 302 ]) eachSite); 303 304 warnings = mapAttrsToList (hostName: _: ''services.dokuwiki."${hostName}" is deprecated use services.dokuwiki.sites."${hostName}"'') (oldSites cfg); 305 306 services.phpfpm.pools = mapAttrs' (hostName: cfg: ( 307 nameValuePair "dokuwiki-${hostName}" { 308 inherit user; 309 group = webserver.group; 310 311 phpEnv = { 312 DOKUWIKI_LOCAL_CONFIG = "${dokuwikiLocalConfig hostName cfg}"; 313 DOKUWIKI_PLUGINS_LOCAL_CONFIG = "${dokuwikiPluginsLocalConfig hostName cfg}"; 314 } // optionalAttrs (cfg.usersFile != null) { 315 DOKUWIKI_USERS_AUTH_CONFIG = "${cfg.usersFile}"; 316 } //optionalAttrs (cfg.aclUse) { 317 DOKUWIKI_ACL_AUTH_CONFIG = if (cfg.acl != null) then "${dokuwikiAclAuthConfig hostName cfg}" else "${toString cfg.aclFile}"; 318 }; 319 320 settings = { 321 "listen.owner" = webserver.user; 322 "listen.group" = webserver.group; 323 } // cfg.poolConfig; 324 } 325 )) eachSite; 326 327 } 328 329 { 330 systemd.tmpfiles.rules = flatten (mapAttrsToList (hostName: cfg: [ 331 "d ${stateDir hostName}/attic 0750 ${user} ${webserver.group} - -" 332 "d ${stateDir hostName}/cache 0750 ${user} ${webserver.group} - -" 333 "d ${stateDir hostName}/index 0750 ${user} ${webserver.group} - -" 334 "d ${stateDir hostName}/locks 0750 ${user} ${webserver.group} - -" 335 "d ${stateDir hostName}/media 0750 ${user} ${webserver.group} - -" 336 "d ${stateDir hostName}/media_attic 0750 ${user} ${webserver.group} - -" 337 "d ${stateDir hostName}/media_meta 0750 ${user} ${webserver.group} - -" 338 "d ${stateDir hostName}/meta 0750 ${user} ${webserver.group} - -" 339 "d ${stateDir hostName}/pages 0750 ${user} ${webserver.group} - -" 340 "d ${stateDir hostName}/tmp 0750 ${user} ${webserver.group} - -" 341 ] ++ lib.optional (cfg.aclFile != null) "C ${cfg.aclFile} 0640 ${user} ${webserver.group} - ${pkg hostName cfg}/share/dokuwiki/conf/acl.auth.php.dist" 342 ++ lib.optional (cfg.usersFile != null) "C ${cfg.usersFile} 0640 ${user} ${webserver.group} - ${pkg hostName cfg}/share/dokuwiki/conf/users.auth.php.dist" 343 ) eachSite); 344 345 users.users.${user} = { 346 group = webserver.group; 347 isSystemUser = true; 348 }; 349 } 350 351 (mkIf (cfg.webserver == "nginx") { 352 services.nginx = { 353 enable = true; 354 virtualHosts = mapAttrs (hostName: cfg: { 355 serverName = mkDefault hostName; 356 root = "${pkg hostName cfg}/share/dokuwiki"; 357 358 locations = { 359 "~ /(conf/|bin/|inc/|install.php)" = { 360 extraConfig = "deny all;"; 361 }; 362 363 "~ ^/data/" = { 364 root = "${stateDir hostName}"; 365 extraConfig = "internal;"; 366 }; 367 368 "~ ^/lib.*\.(js|css|gif|png|ico|jpg|jpeg)$" = { 369 extraConfig = "expires 365d;"; 370 }; 371 372 "/" = { 373 priority = 1; 374 index = "doku.php"; 375 extraConfig = ''try_files $uri $uri/ @dokuwiki;''; 376 }; 377 378 "@dokuwiki" = { 379 extraConfig = '' 380 # rewrites "doku.php/" out of the URLs if you set the userwrite setting to .htaccess in dokuwiki config page 381 rewrite ^/_media/(.*) /lib/exe/fetch.php?media=$1 last; 382 rewrite ^/_detail/(.*) /lib/exe/detail.php?media=$1 last; 383 rewrite ^/_export/([^/]+)/(.*) /doku.php?do=export_$1&id=$2 last; 384 rewrite ^/(.*) /doku.php?id=$1&$args last; 385 ''; 386 }; 387 388 "~ \\.php$" = { 389 extraConfig = '' 390 try_files $uri $uri/ /doku.php; 391 include ${pkgs.nginx}/conf/fastcgi_params; 392 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 393 fastcgi_param REDIRECT_STATUS 200; 394 fastcgi_pass unix:${config.services.phpfpm.pools."dokuwiki-${hostName}".socket}; 395 ''; 396 }; 397 398 }; 399 }) eachSite; 400 }; 401 }) 402 403 (mkIf (cfg.webserver == "caddy") { 404 services.caddy = { 405 enable = true; 406 virtualHosts = mapAttrs' (hostName: cfg: ( 407 nameValuePair "http://${hostName}" { 408 extraConfig = '' 409 root * ${pkg hostName cfg}/share/dokuwiki 410 file_server 411 412 encode zstd gzip 413 php_fastcgi unix/${config.services.phpfpm.pools."dokuwiki-${hostName}".socket} 414 415 @restrict_files { 416 path /data/* /conf/* /bin/* /inc/* /vendor/* /install.php 417 } 418 419 respond @restrict_files 404 420 421 @allow_media { 422 path_regexp path ^/_media/(.*)$ 423 } 424 rewrite @allow_media /lib/exe/fetch.php?media=/{http.regexp.path.1} 425 426 @allow_detail { 427 path /_detail* 428 } 429 rewrite @allow_detail /lib/exe/detail.php?media={path} 430 431 @allow_export { 432 path /_export* 433 path_regexp export /([^/]+)/(.*) 434 } 435 rewrite @allow_export /doku.php?do=export_{http.regexp.export.1}&id={http.regexp.export.2} 436 437 try_files {path} {path}/ /doku.php?id={path}&{query} 438 ''; 439 } 440 )) eachSite; 441 }; 442 }) 443 444 ]); 445 446 meta.maintainers = with maintainers; [ 447 _1000101 448 onny 449 ]; 450}