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