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