summaryrefslogtreecommitdiffstats
path: root/makefu/2configs/deployment/nixos.wiki/mediawiki.module.nix
diff options
context:
space:
mode:
authorlassulus <git@lassul.us>2023-06-19 03:25:39 +0200
committerlassulus <git@lassul.us>2023-06-19 03:25:39 +0200
commit139799c53cdaf55c362109e01be9dd96cc8700ed (patch)
treeb1ce719ec8f62458bce2d9fe2191b8d004630f2a /makefu/2configs/deployment/nixos.wiki/mediawiki.module.nix
parentcb8fbb09127392a17d698d91f78ede7ae46accb8 (diff)
parenta766e88e7c8d87aa6bdbde796d3a454f7b5e546e (diff)
Merge remote-tracking branch 'gum/master'
Diffstat (limited to 'makefu/2configs/deployment/nixos.wiki/mediawiki.module.nix')
-rw-r--r--makefu/2configs/deployment/nixos.wiki/mediawiki.module.nix481
1 files changed, 481 insertions, 0 deletions
diff --git a/makefu/2configs/deployment/nixos.wiki/mediawiki.module.nix b/makefu/2configs/deployment/nixos.wiki/mediawiki.module.nix
new file mode 100644
index 000000000..24715f81e
--- /dev/null
+++ b/makefu/2configs/deployment/nixos.wiki/mediawiki.module.nix
@@ -0,0 +1,481 @@
+{ config, pkgs, lib, ... }:
+
+let
+
+ inherit (lib) mkDefault mkEnableOption mkForce mkIf mkMerge mkOption;
+ inherit (lib) concatStringsSep literalExample mapAttrsToList optional optionals optionalString types;
+
+ cfg = config.services.mediawiki;
+ fpm = config.services.phpfpm.pools.mediawiki;
+ user = "mediawiki";
+ group = config.services.httpd.group;
+ cacheDir = "/var/cache/mediawiki";
+ stateDir = "/var/lib/mediawiki";
+
+ pkg = pkgs.stdenv.mkDerivation rec {
+ pname = "mediawiki-full";
+ version = src.version;
+ src = cfg.package;
+
+ installPhase = ''
+ mkdir -p $out
+ cp -r * $out/
+
+ rm -rf $out/share/mediawiki/skins/*
+ rm -rf $out/share/mediawiki/extensions/*
+
+ ${concatStringsSep "\n" (mapAttrsToList (k: v: ''
+ ln -s ${v} $out/share/mediawiki/skins/${k}
+ '') cfg.skins)}
+
+ ${concatStringsSep "\n" (mapAttrsToList (k: v: ''
+ ln -s ${if v != null then v else "$src/share/mediawiki/extensions/${k}"} $out/share/mediawiki/extensions/${k}
+ '') cfg.extensions)}
+ '';
+ };
+
+ mediawikiScripts = pkgs.runCommand "mediawiki-scripts" {
+ buildInputs = [ pkgs.makeWrapper ];
+ preferLocalBuild = true;
+ } ''
+ mkdir -p $out/bin
+ for i in changePassword.php createAndPromote.php userOptions.php edit.php nukePage.php update.php; do
+ makeWrapper ${pkgs.php}/bin/php $out/bin/mediawiki-$(basename $i .php) \
+ --set MEDIAWIKI_CONFIG ${mediawikiConfig} \
+ --add-flags ${pkg}/share/mediawiki/maintenance/$i
+ done
+ '';
+
+ mediawikiConfig = pkgs.writeText "LocalSettings.php" ''
+ <?php
+ # Protect against web entry
+ if ( !defined( 'MEDIAWIKI' ) ) {
+ exit;
+ }
+
+ $wgSitename = "${cfg.name}";
+ $wgMetaNamespace = false;
+
+ ## The URL base path to the directory containing the wiki;
+ ## defaults for all runtime URL paths are based off of this.
+ ## For more information on customizing the URLs
+ ## (like /w/index.php/Page_title to /wiki/Page_title) please see:
+ ## https://www.mediawiki.org/wiki/Manual:Short_URL
+ $wgScriptPath = "${cfg.basePath}";
+
+ ## The protocol and server name to use in fully-qualified URLs
+ #$wgServer = "${if cfg.virtualHost.addSSL || cfg.virtualHost.forceSSL || cfg.virtualHost.onlySSL then "https" else "http"}://${cfg.virtualHost.hostName}";
+ #$wgServer = "";
+ $wgServer = "http://localhost";
+
+ ## The URL path to static resources (images, scripts, etc.)
+ $wgResourceBasePath = $wgScriptPath;
+
+ ## The URL path to the logo. Make sure you change this from the default,
+ ## or else you'll overwrite your logo when you upgrade!
+ $wgLogo = "$wgResourceBasePath/resources/assets/wiki.png";
+
+ ## UPO means: this is also a user preference option
+
+ $wgEnableEmail = true;
+ $wgEnableUserEmail = true; # UPO
+
+ $wgEmergencyContact = "${if cfg.virtualHost.adminAddr != null then cfg.virtualHost.adminAddr else config.services.httpd.adminAddr}";
+ $wgPasswordSender = $wgEmergencyContact;
+
+ $wgEnotifUserTalk = false; # UPO
+ $wgEnotifWatchlist = false; # UPO
+ $wgEmailAuthentication = true;
+
+ ## Database settings
+ $wgDBtype = "${cfg.database.type}";
+ $wgDBserver = "${cfg.database.host}:${if cfg.database.socket != null then cfg.database.socket else toString cfg.database.port}";
+ $wgDBname = "${cfg.database.name}";
+ $wgDBuser = "${cfg.database.user}";
+ ${optionalString (cfg.database.passwordFile != null) "$wgDBpassword = file_get_contents(\"${cfg.database.passwordFile}\");"}
+
+ ${optionalString (cfg.database.type == "mysql" && cfg.database.tablePrefix != null) ''
+ # MySQL specific settings
+ $wgDBprefix = "${cfg.database.tablePrefix}";
+ ''}
+
+ ${optionalString (cfg.database.type == "mysql") ''
+ # MySQL table options to use during installation or update
+ $wgDBTableOptions = "ENGINE=InnoDB, DEFAULT CHARSET=binary";
+ ''}
+
+ ## Shared memory settings
+ $wgMainCacheType = CACHE_NONE;
+ $wgMemCachedServers = [];
+
+ ${optionalString (cfg.uploadsDir != null) ''
+ $wgEnableUploads = true;
+ $wgUploadDirectory = "${cfg.uploadsDir}";
+ ''}
+
+ $wgUseImageMagick = true;
+ $wgImageMagickConvertCommand = "${pkgs.imagemagick}/bin/convert";
+
+ # InstantCommons allows wiki to use images from https://commons.wikimedia.org
+ $wgUseInstantCommons = false;
+
+ # Periodically send a pingback to https://www.mediawiki.org/ with basic data
+ # about this MediaWiki instance. The Wikimedia Foundation shares this data
+ # with MediaWiki developers to help guide future development efforts.
+ $wgPingback = true;
+
+ ## If you use ImageMagick (or any other shell command) on a
+ ## Linux server, this will need to be set to the name of an
+ ## available UTF-8 locale
+ $wgShellLocale = "C.UTF-8";
+
+ ## Set $wgCacheDirectory to a writable directory on the web server
+ ## to make your wiki go slightly faster. The directory should not
+ ## be publically accessible from the web.
+ $wgCacheDirectory = "${cacheDir}";
+
+ # Site language code, should be one of the list in ./languages/data/Names.php
+ $wgLanguageCode = "en";
+
+ $wgSecretKey = file_get_contents("${stateDir}/secret.key");
+
+ # Changing this will log out all existing sessions.
+ $wgAuthenticationTokenVersion = "";
+
+ ## For attaching licensing metadata to pages, and displaying an
+ ## appropriate copyright notice / icon. GNU Free Documentation
+ ## License and Creative Commons licenses are supported so far.
+ $wgRightsPage = ""; # Set to the title of a wiki page that describes your license/copyright
+ $wgRightsUrl = "";
+ $wgRightsText = "";
+ $wgRightsIcon = "";
+
+ # Path to the GNU diff3 utility. Used for conflict resolution.
+ $wgDiff = "${pkgs.diffutils}/bin/diff";
+ $wgDiff3 = "${pkgs.diffutils}/bin/diff3";
+
+ # Enabled skins.
+ ${concatStringsSep "\n" (mapAttrsToList (k: v: "wfLoadSkin('${k}');") cfg.skins)}
+
+ # Enabled extensions.
+ ${concatStringsSep "\n" (mapAttrsToList (k: v: "wfLoadExtension('${k}');") cfg.extensions)}
+
+
+ # End of automatically generated settings.
+ # Add more configuration options below.
+
+ ${cfg.extraConfig}
+ '';
+
+in
+{
+ # interface
+ options = {
+ services.mediawiki = {
+
+ enable = mkEnableOption "MediaWiki";
+
+ package = mkOption {
+ type = types.package;
+ default = pkgs.mediawiki;
+ description = "Which MediaWiki package to use.";
+ };
+
+ basePath = mkOption {
+ type = types.str;
+ default = "/";
+ description = "Base path to Wiki";
+ };
+
+ name = mkOption {
+ default = "MediaWiki";
+ example = "Foobar Wiki";
+ description = "Name of the wiki.";
+ };
+
+ uploadsDir = mkOption {
+ type = types.nullOr types.path;
+ default = "${stateDir}/uploads";
+ description = ''
+ This directory is used for uploads of pictures. The directory passed here is automatically
+ created and permissions adjusted as required.
+ '';
+ };
+
+ passwordFile = mkOption {
+ type = types.path;
+ description = "A file containing the initial password for the admin user.";
+ example = "/run/keys/mediawiki-password";
+ };
+
+ skins = mkOption {
+ default = {};
+ type = types.attrsOf types.path;
+ description = ''
+ Attribute set of paths whose content is copied to the <filename>skins</filename>
+ subdirectory of the MediaWiki installation in addition to the default skins.
+ '';
+ };
+
+ extensions = mkOption {
+ default = {};
+ type = types.attrsOf (types.nullOr types.path);
+ description = ''
+ Attribute set of paths whose content is copied to the <filename>extensions</filename>
+ subdirectory of the MediaWiki installation and enabled in configuration.
+
+ Use <literal>null</literal> instead of path to enable extensions that are part of MediaWiki.
+ '';
+ example = literalExample ''
+ {
+ Matomo = pkgs.fetchzip {
+ url = "https://github.com/DaSchTour/matomo-mediawiki-extension/archive/v4.0.1.tar.gz";
+ sha256 = "0g5rd3zp0avwlmqagc59cg9bbkn3r7wx7p6yr80s644mj6dlvs1b";
+ };
+ ParserFunctions = null;
+ }
+ '';
+ };
+
+ database = {
+ type = mkOption {
+ type = types.enum [ "mysql" "postgres" "sqlite" "mssql" "oracle" ];
+ default = "mysql";
+ description = "Database engine to use. MySQL/MariaDB is the database of choice by MediaWiki developers.";
+ };
+
+ host = mkOption {
+ type = types.str;
+ default = "localhost";
+ description = "Database host address.";
+ };
+
+ port = mkOption {
+ type = types.port;
+ default = 3306;
+ description = "Database host port.";
+ };
+
+ name = mkOption {
+ type = types.str;
+ default = "mediawiki";
+ description = "Database name.";
+ };
+
+ user = mkOption {
+ type = types.str;
+ default = "mediawiki";
+ description = "Database user.";
+ };
+
+ passwordFile = mkOption {
+ type = types.nullOr types.path;
+ default = null;
+ example = "/run/keys/mediawiki-dbpassword";
+ description = ''
+ A file containing the password corresponding to
+ <option>database.user</option>.
+ '';
+ };
+
+ tablePrefix = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = ''
+ If you only have access to a single database and wish to install more than
+ one version of MediaWiki, or have other applications that also use the
+ database, you can give the table names a unique prefix to stop any naming
+ conflicts or confusion.
+ See <link xlink:href='https://www.mediawiki.org/wiki/Manual:$wgDBprefix'/>.
+ '';
+ };
+
+ socket = mkOption {
+ type = types.nullOr types.path;
+ default = if cfg.database.createLocally then "/run/mysqld/mysqld.sock" else null;
+ defaultText = "/run/mysqld/mysqld.sock";
+ description = "Path to the unix socket file to use for authentication.";
+ };
+
+ createLocally = mkOption {
+ type = types.bool;
+ default = cfg.database.type == "mysql";
+ defaultText = "true";
+ description = ''
+ Create the database and database user locally.
+ This currently only applies if database type "mysql" is selected.
+ '';
+ };
+ };
+
+ virtualHost = mkOption {
+ type = types.submodule (import <nixpkgs/nixos/modules/services/web-servers/apache-httpd/vhost-options.nix>);
+ example = literalExample ''
+ {
+ hostName = "mediawiki.example.org";
+ adminAddr = "webmaster@example.org";
+ forceSSL = true;
+ enableACME = true;
+ }
+ '';
+ description = ''
+ Apache configuration can be done by adapting <option>services.httpd.virtualHosts</option>.
+ See <xref linkend="opt-services.httpd.virtualHosts"/> for further information.
+ '';
+ };
+
+ poolConfig = mkOption {
+ type = with types; attrsOf (oneOf [ str int bool ]);
+ default = {
+ "pm" = "dynamic";
+ "pm.max_children" = 32;
+ "pm.start_servers" = 2;
+ "pm.min_spare_servers" = 2;
+ "pm.max_spare_servers" = 4;
+ "pm.max_requests" = 500;
+ };
+ description = ''
+ Options for the MediaWiki PHP pool. See the documentation on <literal>php-fpm.conf</literal>
+ for details on configuration directives.
+ '';
+ };
+
+ extraConfig = mkOption {
+ type = types.lines;
+ description = ''
+ Any additional text to be appended to MediaWiki's
+ LocalSettings.php configuration file. For configuration
+ settings, see <link xlink:href="https://www.mediawiki.org/wiki/Manual:Configuration_settings"/>.
+ '';
+ default = "";
+ example = ''
+ $wgEnableEmail = false;
+ '';
+ };
+
+ };
+ };
+
+ # implementation
+ config = mkIf cfg.enable {
+
+ assertions = [
+ { assertion = cfg.database.createLocally -> cfg.database.type == "mysql";
+ message = "services.mediawiki.createLocally is currently only supported for database type 'mysql'";
+ }
+ { assertion = cfg.database.createLocally -> cfg.database.user == user;
+ message = "services.mediawiki.database.user must be set to ${user} if services.mediawiki.database.createLocally is set true";
+ }
+ { assertion = cfg.database.createLocally -> cfg.database.socket != null;
+ message = "services.mediawiki.database.socket must be set if services.mediawiki.database.createLocally is set to true";
+ }
+ { assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
+ message = "a password cannot be specified if services.mediawiki.database.createLocally is set to true";
+ }
+ ];
+
+ services.mediawiki.skins = {
+ MonoBook = "${cfg.package}/share/mediawiki/skins/MonoBook";
+ Timeless = "${cfg.package}/share/mediawiki/skins/Timeless";
+ Vector = "${cfg.package}/share/mediawiki/skins/Vector";
+ };
+
+ services.mysql = mkIf cfg.database.createLocally {
+ enable = true;
+ package = mkDefault pkgs.mariadb;
+ ensureDatabases = [ cfg.database.name ];
+ ensureUsers = [
+ { name = cfg.database.user;
+ ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; };
+ }
+ ];
+ };
+
+ services.phpfpm.pools.mediawiki = {
+ inherit user group;
+ phpEnv.MEDIAWIKI_CONFIG = "${mediawikiConfig}";
+ settings = {
+ "listen.owner" = config.services.httpd.user;
+ "listen.group" = config.services.httpd.group;
+ } // cfg.poolConfig;
+ };
+
+ services.httpd = {
+ enable = true;
+ extraModules = [ "proxy_fcgi" ];
+ virtualHosts.${cfg.virtualHost.hostName} = mkMerge [ cfg.virtualHost {
+ documentRoot = mkForce "${pkg}/share/mediawiki";
+ extraConfig = ''
+ <Directory "${pkg}/share/mediawiki">
+ <FilesMatch "\.php$">
+ <If "-f %{REQUEST_FILENAME}">
+ SetHandler "proxy:unix:${fpm.socket}|fcgi://localhost/"
+ </If>
+ </FilesMatch>
+
+ Require all granted
+ DirectoryIndex index.php
+ AllowOverride All
+ </Directory>
+ '' + optionalString (cfg.uploadsDir != null) ''
+ Alias "/images" "${cfg.uploadsDir}"
+ <Directory "${cfg.uploadsDir}">
+ Require all granted
+ </Directory>
+ '';
+ } ];
+ };
+
+ systemd.tmpfiles.rules = [
+ "d '${stateDir}' 0750 ${user} ${group} - -"
+ "d '${cacheDir}' 0750 ${user} ${group} - -"
+ ] ++ optionals (cfg.uploadsDir != null) [
+ "d '${cfg.uploadsDir}' 0750 ${user} ${group} - -"
+ "Z '${cfg.uploadsDir}' 0750 ${user} ${group} - -"
+ ];
+
+ systemd.services.mediawiki-init = {
+ wantedBy = [ "multi-user.target" ];
+ before = [ "phpfpm-mediawiki.service" ];
+ after = optional cfg.database.createLocally "mysql.service";
+ script = ''
+ if ! test -e "${stateDir}/secret.key"; then
+ tr -dc A-Za-z0-9 </dev/urandom 2>/dev/null | head -c 64 > ${stateDir}/secret.key
+ fi
+
+ echo "exit( wfGetDB( DB_MASTER )->tableExists( 'user' ) ? 1 : 0 );" | \
+ ${pkgs.php}/bin/php ${pkg}/share/mediawiki/maintenance/eval.php --conf ${mediawikiConfig} && \
+ ${pkgs.php}/bin/php ${pkg}/share/mediawiki/maintenance/install.php \
+ --confpath /tmp \
+ --scriptpath ${cfg.basePath} \
+ --dbserver ${cfg.database.host}${optionalString (cfg.database.socket != null) ":${cfg.database.socket}"} \
+ --dbport ${toString cfg.database.port} \
+ --dbname ${cfg.database.name} \
+ ${optionalString (cfg.database.tablePrefix != null) "--dbprefix ${cfg.database.tablePrefix}"} \
+ --dbuser ${cfg.database.user} \
+ ${optionalString (cfg.database.passwordFile != null) "--dbpassfile ${cfg.database.passwordFile}"} \
+ --passfile ${cfg.passwordFile} \
+ "${cfg.name}" \
+ admin
+
+ ${pkgs.php}/bin/php ${pkg}/share/mediawiki/maintenance/update.php --conf ${mediawikiConfig} --quick
+ '';
+
+ serviceConfig = {
+ Type = "oneshot";
+ User = user;
+ Group = group;
+ PrivateTmp = true;
+ };
+ };
+
+ systemd.services.httpd.after = optional (cfg.database.createLocally && cfg.database.type == "mysql") "mysql.service";
+
+ users.users.${user} = {
+ group = group;
+ isSystemUser = true;
+ };
+
+ environment.systemPackages = [ mediawikiScripts ];
+ };
+}