feat(pm/wiki): factor into ingredient #155

merged
opened by a.starrysky.fyi targeting main from private/minion/push-wtxryrqunxwq

I want to have a private copy of the wiki on umber and a public copy on teal. It would be good to share configs between them, so we should use an ingredient for this

+2
packetmix/systems/default.nix
··· 90 ingredients = [ 91 "freshlybakedcake" 92 "server" 93 ]; 94 args = { 95 system = "x86_64-linux"; ··· 101 ingredients = [ 102 "freshlybakedcake" 103 "server" 104 ]; 105 args = { 106 system = "x86_64-linux";
··· 90 ingredients = [ 91 "freshlybakedcake" 92 "server" 93 + "wiki" 94 ]; 95 args = { 96 system = "x86_64-linux"; ··· 102 ingredients = [ 103 "freshlybakedcake" 104 "server" 105 + "wiki" 106 ]; 107 args = { 108 system = "x86_64-linux";
+6
packetmix/systems/teal/headscale.nix
··· 125 type = "A"; 126 value = "100.64.0.37"; 127 } 128 ]; 129 nameservers.global = [ 130 "1.1.1.1"
··· 125 type = "A"; 126 value = "100.64.0.37"; 127 } 128 + { 129 + # wiki.starrysky.fyi -> umber 130 + name = "wiki.freshly.space"; 131 + type = "A"; 132 + value = "100.64.0.48"; 133 + } 134 ]; 135 nameservers.global = [ 136 "1.1.1.1"
+6 -348
packetmix/systems/teal/wiki.nix
··· 1 # SPDX-FileCopyrightText: 2025 FreshlyBakedCake 2 # 3 # SPDX-License-Identifier: MIT 4 5 { 6 - project, 7 - system, 8 - config, 9 - pkgs, 10 - lib, 11 - ... 12 - }: 13 - { 14 - clicks.storage.impermanence.persist.directories = [ 15 - { 16 - directory = "/var/lib/mediawiki"; 17 - mode = "0700"; 18 - user = "mediawiki"; 19 - defaultPerms.mode = "0700"; 20 - } 21 - { 22 - directory = "/var/lib/private/opensearch"; 23 - mode = "0700"; 24 - user = "opensearch"; 25 - defaultPerms.mode = "0700"; 26 - } 27 - ]; 28 - 29 - services.mediawiki = { 30 - enable = true; 31 - package = project.inputs.nixos-unstable.result.${system}.mediawiki; # header auth master requires mediawiki unstable - header auth stable is broken on missing Hooks (recently removed in stable MW version) 32 - phpPackage = pkgs.php83.withExtensions ({ enabled, all }: enabled ++ [ all.luasandbox ]); 33 - database.type = "postgres"; 34 - path = [ 35 - pkgs.diffutils 36 - pkgs.imagemagick 37 - pkgs.python3Packages.pygments 38 - ]; 39 - extensions = { 40 - AdvancedSearch = project.inputs.AdvancedSearch.src; 41 - Auth_remoteuser = project.inputs.Auth_remoteuser.src; # header auth 42 - AutoCreateCategoryPages = project.inputs.AutoCreateCategoryPages.src; 43 - Cargo = project.inputs.Cargo.src; # queries and soforth 44 - CategoryTree = null; 45 - CheckUser = null; 46 - Cite = null; 47 - CiteThisPage = null; 48 - CirrusSearch = "${ 49 - pkgs.php.buildComposerProject { 50 - pname = "CirrusSearch"; 51 - version = "0.0.3665"; 52 - src = project.inputs.CirrusSearch.src; 53 - vendorHash = "sha256-MLD/3hvzX1aqR4knajJ1amb6K5SVtxlfy+UZWoSi1Bk="; 54 - composerLock = ./wiki/CirrusSearch.composer.lock; 55 - } 56 - }/share/php/CirrusSearch"; # needed for advancedsearch 57 - CodeEditor = null; 58 - DiscussionTools = null; 59 - Echo = null; 60 - EditNotify = project.inputs.EditNotify.src; 61 - Elastica = "${ 62 - pkgs.php.buildComposerProject { 63 - pname = "Elastica"; 64 - version = "0.0.3665"; 65 - src = project.inputs.Elastica.src; 66 - vendorHash = "sha256-4kp8njLTqPeFCREnGharCB/pmYBnXLJR4TdD6EH6WCI="; 67 - composerLock = ./wiki/Elastica.composer.lock; 68 - } 69 - }/share/php/Elastica"; # needed for cirrussearch 70 - Linter = null; 71 - Math = null; 72 - MobileFrontend = project.inputs.MobileFrontend.src; 73 - NamespacePreload = project.inputs.NamespacePreload.src; 74 - Network = "${ 75 - config.services.phpfpm.pools.mediawiki.phpPackage.buildComposerProject { 76 - pname = "Network"; 77 - version = "0.0.3665"; 78 - src = project.inputs.Network.src; 79 - vendorHash = "sha256-JHa6PW5xO3pcwn/2jbGXM0wGhr6UmtqFdxaGCgpaYb0="; 80 - composerLock = ./wiki/Network.composer.lock; 81 - } 82 - }/share/php/Network"; # for page connection graphs 83 - OpenIDConnect = "${ 84 - pkgs.php.buildComposerProject { 85 - pname = "OpenIDConnect"; 86 - version = "0.0.3665"; 87 - src = project.inputs.OpenIDConnect.src; 88 - vendorHash = "sha256-DjxyOK21tbBEj6hFfhVNDxeNu4a26hvMRHgD/u24ZT0="; 89 - composerLock = ./wiki/OpenIDConnect.composer.lock; 90 - 91 - postInstall = '' 92 - cat sql/postgres/ChangePrimaryKey.sql | sed 's/DROP INDEX "primary"/ALTER TABLE openid_connect DROP CONSTRAINT openid_connect_pkey/' > $out/share/php/OpenIDConnect/sql/postgres/ChangePrimaryKey.sql 93 - ''; 94 - } 95 - }/share/php/OpenIDConnect"; 96 - ParserFunctions = null; 97 - PluggableAuth = project.inputs.PluggableAuth.src; # needed for OIDC 98 - Poem = null; 99 - ReplaceText = null; 100 - Scribunto = null; 101 - SecureLinkFixer = null; 102 - SimpleTooltip = project.inputs.SimpleTooltip.src; 103 - SyntaxHighlight_GeSHi = null; 104 - TemplateData = null; 105 - TemplateStyles = null; 106 - Thanks = null; 107 - UserMerge = project.inputs.UserMerge.src; 108 - VisualEditor = null; 109 - WikiEditor = null; 110 - }; 111 - extraConfig = '' 112 - $wgMaxUploadSize = 1024*1024*1024*8; 113 - $wgGroupPermissions['autoconfirmed']['upload_by_url'] = true; 114 - $wgGroupPermissions['autoconfirmed']['interwiki'] = true; // https://wiki.freshly.space/wiki/Special:Interwiki - edit shortlink prefixes, crazy-strong permission but we trust our friends 115 - $wgAllowCopyUploads = true; 116 - $wgCopyUploadsFromSpecialUpload = true; 117 - 118 - $wgSMTP = [ 119 - 'host' => 'ssl://mail.freshly.space', 120 - 'IDHost' => 'wiki.freshly.space', 121 - 'localhost' => 'wiki.freshly.space', 122 - 'port' => 465, 123 - 'auth' => true, 124 - 'username' => 'automated@freshly.space', 125 - 'password' => trim(file_get_contents('/secrets/mediawiki/mail_password.txt')) 126 - ]; 127 - $wgLocalInterwikis = [ 128 - 'fbc' 129 - ]; 130 - 131 - $wgWhitelistReadRegexp = [ 132 - '/^Main Page$/', 133 - '/^Public:/', 134 - '/^User:/' 135 - ]; 136 - $wgGroupPermissions['*']['read'] = false; 137 - $wgGroupPermissions['*']['edit'] = false; 138 - $wgGroupPermissions['*']['createaccount'] = false; 139 - $wgGroupPermissions['*']['autocreateaccount'] = true; 140 - 141 - $wgGroupPermissions['bureaucrat']['usermerge'] = true; 142 - 143 - $wgAuthRemoteuserUserName = function () { 144 - if (!isset($_SERVER['HTTP_X_WEBAUTH_LOGIN'])) { 145 - return ""; 146 - } 147 - 148 - if ($_SERVER['HTTP_X_WEBAUTH_LOGIN'] === 'hyperneutrino') { 149 - return 'HyperNeutrino'; 150 - } 151 - 152 - return $_SERVER['HTTP_X_WEBAUTH_LOGIN']; 153 - }; 154 - $wgAuthRemoteuserPriority = MediaWiki\Session\SessionInfo::MAX_PRIORITY; 155 - 156 - $wgUseCdn = true; 157 - $wgCdnServersNoPurge = [ 158 - '127.0.0.1' 159 - ]; 160 - $wgUsePrivateIPs = true; 161 - 162 - $wgUseInstantCommons = true; 163 - $wgPingback = false; 164 - 165 - $wgPluggableAuth_Config = [ 166 - 'Freshly Baked Cake Kanidm' => [ 167 - 'plugin' => 'OpenIDConnect', 168 - 'data' => [ 169 - 'providerURL' => 'https://idm.freshly.space/oauth2/openid/mediawiki', 170 - 'clientID' => 'mediawiki', 171 - 'clientsecret' => trim(file_get_contents('/secrets/mediawiki/oidc_client_secret.txt')), 172 - 'codeChallengeMethod' => 'S256' 173 - ] 174 - ] 175 - ]; 176 - 177 - $wgOpenIDConnect_MigrateUsersByUserName = true; 178 - 179 - $wgLogos = [ 180 - 'icon' => '/icon.svg', 181 - 'svg' => '/icon.svg' 182 - ]; 183 - 184 - $wgPygmentizePath = '${pkgs.python3Packages.pygments}/bin/pygmentize'; 185 - 186 - $wgScribuntoDefaultEngine = 'luasandbox'; 187 - 188 - $wgNamespacesWithSubpages[NS_MAIN] = true; 189 - 190 - $wgNamespacePreloadDoExpansion = false; // This can't expand {{PAGENAME}} (or like) correctly, making it very nearly useless 191 - 192 - $wgCirrusSearchServers = [ 193 - [ 194 - "host" => '127.0.0.1', 195 - "port" => 1037 196 - ] 197 - ]; 198 - $wgSearchType = 'CirrusSearch'; 199 - $wgNamespacesToBeSearchedDefault[NS_CATEGORY] = true; 200 - 201 - $wgUrlProtocols[] = "rad:"; 202 - 203 - $wgSVGNativeRendering = true; 204 - 205 - $wgRCWatchCategoryMembership = true; 206 - 207 - $wgCargoPageDataColumns[] = 'creationDate'; 208 - $wgCargoPageDataColumns[] = 'modificationDate'; 209 - $wgCargoPageDataColumns[] = 'creator'; 210 - $wgCargoPageDataColumns[] = 'lastEditor'; 211 - $wgCargoPageDataColumns[] = 'displayTitle'; 212 - $wgCargoPageDataColumns[] = 'categories'; 213 - $wgCargoPageDataColumns[] = 'numRevisions'; 214 - $wgCargoPageDataColumns[] = 'outgoingLinks'; 215 - $wgCargoPageDataColumns[] = 'isRedirect'; 216 - $wgCargoPageDataColumns[] = 'pageNameOrRedirect'; 217 - $wgCargoPageDataColumns[] = 'pageIDOrRedirect'; 218 - 219 - $wgCargoFileDataColumns[] = 'mediaType'; 220 - $wgCargoFileDataColumns[] = 'path'; 221 - $wgCargoFileDataColumns[] = 'lastUploadDate'; 222 - 223 - $wgFixDoubleRedirects = true; 224 - 225 - $wgMFAutodetectMobileView = true; 226 - $wgMFEnableMobilePreferences = true; 227 - wfLoadSkin( 'MinervaNeue' ); 228 - 229 - $wgShowExceptionDetails = true; 230 - $wgDevelopmentWarnings = true; 231 - ''; 232 - webserver = "nginx"; 233 - url = "https://wiki.freshly.space"; 234 - nginx.hostName = "wiki.freshly.space"; 235 - name = "Freshly Wiki"; 236 - database.createLocally = true; 237 - 238 - passwordSender = "wiki@freshly.space"; 239 - 240 - passwordFile = "/secrets/mediawiki/initial_admin_password.txt"; 241 - }; 242 - 243 - systemd.timers.mediawiki-maintenance = { 244 - wantedBy = [ "timers.target" ]; 245 - timerConfig = { 246 - OnUnitActiveSec = "30"; 247 - OnBootSec = "30"; 248 - Persistent = false; 249 - Unit = "mediawiki-maintenance.service"; 250 - }; 251 - }; 252 - 253 - systemd.services.mediawiki-maintenance = { 254 - script = '' 255 - ${config.services.phpfpm.pools.mediawiki.phpPackage}/bin/php ${config.services.mediawiki.finalPackage}/share/mediawiki/maintenance/run.php runJobs --memory-limit 1G --maxtime 30 256 - ''; 257 - serviceConfig = { 258 - Type = "oneshot"; 259 - RemainAfterExit = false; 260 - User = "mediawiki"; 261 - Group = "nginx"; 262 - PrivateTmp = true; 263 - Environment = "MEDIAWIKI_CONFIG=${config.services.phpfpm.pools.mediawiki.phpEnv.MEDIAWIKI_CONFIG}"; 264 - }; 265 - }; 266 - 267 - services.opensearch = { 268 - # needed for cirrussearch 269 - enable = true; 270 - package = project.packages.opensearch.result.${system}; 271 - settings = { 272 - "http.port" = 1037; 273 - "path.data" = "/var/lib/private/opensearch/data"; 274 - "path.logs" = "/var/lib/private/opensearch/logs"; 275 - }; 276 - }; 277 - 278 - services.nginx.enable = true; 279 services.headscale.settings.dns.extra_records = [ 280 { 281 # wiki.freshly.space -> teal ··· 284 value = "100.64.0.5"; 285 } 286 ]; 287 - services.nginx.virtualHosts."wiki.freshly.space" = { 288 - listen = [ 289 - { 290 - addr = "127.0.0.1"; 291 - port = 1036; 292 - } 293 - ]; 294 - 295 - locations = { 296 - "= /" = lib.mkForce { 297 - extraConfig = '' 298 - return 301 https://wiki.freshly.space/wiki/; 299 - ''; # overriding nixpkgs /wiki/ redirect since as our double-proxy makes it redirect to :1036 300 - }; 301 - "= /favicon.ico".alias = ./wiki/favicon.ico; 302 - "= /icon.svg".alias = ./wiki/icon.svg; 303 - }; 304 - 305 - extraConfig = '' 306 - client_max_body_size 1024M; 307 - ''; 308 - }; 309 - services.nginx.virtualHosts."external.wiki.freshly.space" = { 310 - listenAddresses = [ 311 - "0.0.0.0" 312 - "[::0]" 313 - ]; 314 - 315 - serverName = "wiki.freshly.space"; 316 - 317 - addSSL = true; 318 - enableACME = true; 319 - acmeRoot = null; 320 - 321 - locations."/" = { 322 - proxyPass = "http://127.0.0.1:1036"; 323 - recommendedProxySettings = true; 324 - proxyWebsockets = true; 325 - 326 - extraConfig = '' 327 - proxy_set_header X-Webauth-Login ""; 328 - proxy_cache off; 329 - ''; 330 - }; 331 - 332 - extraConfig = '' 333 - client_max_body_size 1024M; 334 - ''; 335 - }; 336 - services.nginx.virtualHosts."internal.wiki.freshly.space" = { 337 - listenAddresses = [ "localhost.tailscale" ]; 338 - 339 - serverName = "wiki.freshly.space"; 340 - 341 - addSSL = true; 342 - enableACME = true; 343 - acmeRoot = null; 344 - 345 - locations."/" = { 346 - proxyPass = "http://127.0.0.1:1036"; 347 - recommendedProxySettings = true; 348 - proxyWebsockets = true; 349 - 350 - extraConfig = '' 351 - proxy_cache off; 352 - ''; 353 - }; 354 - 355 - extraConfig = '' 356 - client_max_body_size 1024M; 357 - ''; 358 - }; 359 360 - services.nginx.tailscaleAuth = { 361 - enable = true; 362 - virtualHosts = [ "internal.wiki.freshly.space" ]; 363 }; 364 }
··· 1 # SPDX-FileCopyrightText: 2025 FreshlyBakedCake 2 + # SPDX-FileCopyrightText: 2026 Collabora Productivity Limited 3 # 4 # SPDX-License-Identifier: MIT 5 6 { 7 services.headscale.settings.dns.extra_records = [ 8 { 9 # wiki.freshly.space -> teal ··· 12 value = "100.64.0.5"; 13 } 14 ]; 15 16 + ingredients.wiki.wiki = { 17 + hostname = "wiki.freshly.space"; 18 + email = "wiki@freshly.space"; 19 + enablePublicInternet = true; 20 + enableAutoRegistration = true; 21 }; 22 }
+10
packetmix/systems/umber/wiki.nix
···
··· 1 + # SPDX-FileCopyrightText: 2026 Collabora Productivity Limited 2 + # 3 + # SPDX-License-Identifier: MIT 4 + 5 + { 6 + ingredients.wiki.wiki = { 7 + hostname = "wiki.starrysky.fyi"; 8 + email = "wiki@starrysky.fyi"; 9 + }; 10 + }
+384
packetmix/systems/wiki/wiki.nix
···
··· 1 + # SPDX-FileCopyrightText: 2025 FreshlyBakedCake 2 + # SPDX-FileCopyrightText: 2026 Collabora Productivity Limited 3 + # 4 + # SPDX-License-Identifier: MIT 5 + 6 + # This ingredient has some default values (including OIDC config/etc.) that make it probably unsuitable for use outside of Freshly Baked. Sorry. 7 + { 8 + project, 9 + system, 10 + config, 11 + pkgs, 12 + lib, 13 + ... 14 + }: 15 + { 16 + options.ingredient.wiki.wiki = { 17 + hostname = lib.mkOption { 18 + type = lib.types.str; 19 + description = "Where your wiki should be hosted"; 20 + }; 21 + email = lib.mkOption { 22 + type = lib.types.str; 23 + description = "What email should notifications/password resets/etc. come from"; 24 + }; 25 + enablePublicInternet = lib.mkEnableOption "Allow access from the public internet with authentication via OIDC"; 26 + enableAutoRegistration = lib.mkEnableOption "Allow unregistered users to automatically register via OIDC or Tailscale"; 27 + }; 28 + 29 + config = { 30 + clicks.storage.impermanence.persist.directories = [ 31 + { 32 + directory = "/var/lib/mediawiki"; 33 + mode = "0700"; 34 + user = "mediawiki"; 35 + defaultPerms.mode = "0700"; 36 + } 37 + { 38 + directory = "/var/lib/private/opensearch"; 39 + mode = "0700"; 40 + user = "opensearch"; 41 + defaultPerms.mode = "0700"; 42 + } 43 + ]; 44 + 45 + services.mediawiki = { 46 + enable = true; 47 + package = project.inputs.nixos-unstable.result.${system}.mediawiki; # header auth master requires mediawiki unstable - header auth stable is broken on missing Hooks (recently removed in stable MW version) 48 + phpPackage = pkgs.php83.withExtensions ({ enabled, all }: enabled ++ [ all.luasandbox ]); 49 + database.type = "postgres"; 50 + path = [ 51 + pkgs.diffutils 52 + pkgs.imagemagick 53 + pkgs.python3Packages.pygments 54 + ]; 55 + extensions = { 56 + AdvancedSearch = project.inputs.AdvancedSearch.src; 57 + Auth_remoteuser = project.inputs.Auth_remoteuser.src; # header auth 58 + AutoCreateCategoryPages = project.inputs.AutoCreateCategoryPages.src; 59 + Cargo = project.inputs.Cargo.src; # queries and soforth 60 + CategoryTree = null; 61 + CheckUser = null; 62 + Cite = null; 63 + CiteThisPage = null; 64 + CirrusSearch = "${ 65 + pkgs.php.buildComposerProject { 66 + pname = "CirrusSearch"; 67 + version = "0.0.3665"; 68 + src = project.inputs.CirrusSearch.src; 69 + vendorHash = "sha256-MLD/3hvzX1aqR4knajJ1amb6K5SVtxlfy+UZWoSi1Bk="; 70 + composerLock = ./wiki/CirrusSearch.composer.lock; 71 + } 72 + }/share/php/CirrusSearch"; # needed for advancedsearch 73 + CodeEditor = null; 74 + DiscussionTools = null; 75 + Echo = null; 76 + EditNotify = project.inputs.EditNotify.src; 77 + Elastica = "${ 78 + pkgs.php.buildComposerProject { 79 + pname = "Elastica"; 80 + version = "0.0.3665"; 81 + src = project.inputs.Elastica.src; 82 + vendorHash = "sha256-4kp8njLTqPeFCREnGharCB/pmYBnXLJR4TdD6EH6WCI="; 83 + composerLock = ./wiki/Elastica.composer.lock; 84 + } 85 + }/share/php/Elastica"; # needed for cirrussearch 86 + Linter = null; 87 + Math = null; 88 + MobileFrontend = project.inputs.MobileFrontend.src; 89 + NamespacePreload = project.inputs.NamespacePreload.src; 90 + Network = "${ 91 + config.services.phpfpm.pools.mediawiki.phpPackage.buildComposerProject { 92 + pname = "Network"; 93 + version = "0.0.3665"; 94 + src = project.inputs.Network.src; 95 + vendorHash = "sha256-JHa6PW5xO3pcwn/2jbGXM0wGhr6UmtqFdxaGCgpaYb0="; 96 + composerLock = ./wiki/Network.composer.lock; 97 + } 98 + }/share/php/Network"; # for page connection graphs 99 + OpenIDConnect = lib.mkIf config.ingredient.wiki.wiki.enablePublicInternet "${ 100 + pkgs.php.buildComposerProject { 101 + pname = "OpenIDConnect"; 102 + version = "0.0.3665"; 103 + src = project.inputs.OpenIDConnect.src; 104 + vendorHash = "sha256-DjxyOK21tbBEj6hFfhVNDxeNu4a26hvMRHgD/u24ZT0="; 105 + composerLock = ./wiki/OpenIDConnect.composer.lock; 106 + 107 + postInstall = '' 108 + cat sql/postgres/ChangePrimaryKey.sql | sed 's/DROP INDEX "primary"/ALTER TABLE openid_connect DROP CONSTRAINT openid_connect_pkey/' > $out/share/php/OpenIDConnect/sql/postgres/ChangePrimaryKey.sql 109 + ''; 110 + } 111 + }/share/php/OpenIDConnect"; 112 + ParserFunctions = null; 113 + PluggableAuth = lib.mkIf config.ingredient.wiki.wiki.enablePublicInternet project.inputs.PluggableAuth.src; # needed for OIDC 114 + Poem = null; 115 + ReplaceText = null; 116 + Scribunto = null; 117 + SecureLinkFixer = null; 118 + SimpleTooltip = project.inputs.SimpleTooltip.src; 119 + SyntaxHighlight_GeSHi = null; 120 + TemplateData = null; 121 + TemplateStyles = null; 122 + Thanks = null; 123 + UserMerge = project.inputs.UserMerge.src; 124 + VisualEditor = null; 125 + WikiEditor = null; 126 + }; 127 + extraConfig = '' 128 + $wgMaxUploadSize = 1024*1024*1024*8; 129 + $wgGroupPermissions['autoconfirmed']['upload_by_url'] = true; 130 + $wgGroupPermissions['autoconfirmed']['interwiki'] = true; // Special:Interwiki - edit shortlink prefixes, crazy-strong permission but we trust our friends 131 + $wgAllowCopyUploads = true; 132 + $wgCopyUploadsFromSpecialUpload = true; 133 + 134 + $wgSMTP = [ 135 + 'host' => 'ssl://mail.freshly.space', 136 + 'IDHost' => '${config.ingredient.wiki.wiki.hostname}', 137 + 'localhost' => '${config.ingredient.wiki.wiki.hostname}', 138 + 'port' => 465, 139 + 'auth' => true, 140 + 'username' => 'automated@freshly.space', 141 + 'password' => trim(file_get_contents('/secrets/mediawiki/mail_password.txt')) 142 + ]; 143 + $wgLocalInterwikis = [ 144 + 'fbc' 145 + ]; 146 + 147 + $wgWhitelistReadRegexp = [ 148 + '/^Main Page$/', 149 + '/^Public:/', 150 + '/^User:/' 151 + ]; 152 + $wgGroupPermissions['*']['read'] = false; 153 + $wgGroupPermissions['*']['edit'] = false; 154 + $wgGroupPermissions['*']['createaccount'] = false; 155 + $wgGroupPermissions['*']['autocreateaccount'] = ${ 156 + if config.ingredient.wiki.wiki.enableAutoRegistration then "true" else "false" 157 + }; 158 + 159 + $wgGroupPermissions['bureaucrat']['usermerge'] = true; 160 + 161 + $wgAuthRemoteuserUserName = function () { 162 + if (!isset($_SERVER['HTTP_X_WEBAUTH_LOGIN'])) { 163 + return ""; 164 + } 165 + 166 + if ($_SERVER['HTTP_X_WEBAUTH_LOGIN'] === 'hyperneutrino') { 167 + return 'HyperNeutrino'; 168 + } 169 + 170 + return $_SERVER['HTTP_X_WEBAUTH_LOGIN']; 171 + }; 172 + $wgAuthRemoteuserPriority = MediaWiki\Session\SessionInfo::MAX_PRIORITY; 173 + 174 + $wgUseCdn = true; 175 + $wgCdnServersNoPurge = [ 176 + '127.0.0.1' 177 + ]; 178 + $wgUsePrivateIPs = true; 179 + 180 + $wgUseInstantCommons = true; 181 + $wgPingback = false; 182 + 183 + ${ 184 + if config.ingredient.wiki.wiki.enablePublicInternet then 185 + '' 186 + $wgPluggableAuth_Config = [ 187 + 'Freshly Baked Cake Kanidm' => [ 188 + 'plugin' => 'OpenIDConnect', 189 + 'data' => [ 190 + 'providerURL' => 'https://idm.freshly.space/oauth2/openid/mediawiki', 191 + 'clientID' => 'mediawiki', 192 + 'clientsecret' => trim(file_get_contents('/secrets/mediawiki/oidc_client_secret.txt')), 193 + 'codeChallengeMethod' => 'S256' 194 + ] 195 + ] 196 + ]; 197 + '' 198 + else 199 + "" 200 + } 201 + 202 + $wgOpenIDConnect_MigrateUsersByUserName = true; 203 + 204 + $wgLogos = [ 205 + 'icon' => '/icon.svg', 206 + 'svg' => '/icon.svg' 207 + ]; 208 + 209 + $wgPygmentizePath = '${pkgs.python3Packages.pygments}/bin/pygmentize'; 210 + 211 + $wgScribuntoDefaultEngine = 'luasandbox'; 212 + 213 + $wgNamespacesWithSubpages[NS_MAIN] = true; 214 + 215 + $wgNamespacePreloadDoExpansion = false; // This can't expand {{PAGENAME}} (or like) correctly, making it very nearly useless 216 + 217 + $wgCirrusSearchServers = [ 218 + [ 219 + "host" => '127.0.0.1', 220 + "port" => 1037 221 + ] 222 + ]; 223 + $wgSearchType = 'CirrusSearch'; 224 + $wgNamespacesToBeSearchedDefault[NS_CATEGORY] = true; 225 + 226 + $wgUrlProtocols[] = "rad:"; 227 + 228 + $wgSVGNativeRendering = true; 229 + 230 + $wgRCWatchCategoryMembership = true; 231 + 232 + $wgCargoPageDataColumns[] = 'creationDate'; 233 + $wgCargoPageDataColumns[] = 'modificationDate'; 234 + $wgCargoPageDataColumns[] = 'creator'; 235 + $wgCargoPageDataColumns[] = 'lastEditor'; 236 + $wgCargoPageDataColumns[] = 'displayTitle'; 237 + $wgCargoPageDataColumns[] = 'categories'; 238 + $wgCargoPageDataColumns[] = 'numRevisions'; 239 + $wgCargoPageDataColumns[] = 'outgoingLinks'; 240 + $wgCargoPageDataColumns[] = 'isRedirect'; 241 + $wgCargoPageDataColumns[] = 'pageNameOrRedirect'; 242 + $wgCargoPageDataColumns[] = 'pageIDOrRedirect'; 243 + 244 + $wgCargoFileDataColumns[] = 'mediaType'; 245 + $wgCargoFileDataColumns[] = 'path'; 246 + $wgCargoFileDataColumns[] = 'lastUploadDate'; 247 + 248 + $wgFixDoubleRedirects = true; 249 + 250 + $wgMFAutodetectMobileView = true; 251 + $wgMFEnableMobilePreferences = true; 252 + wfLoadSkin( 'MinervaNeue' ); 253 + 254 + $wgShowExceptionDetails = true; 255 + $wgDevelopmentWarnings = true; 256 + ''; 257 + webserver = "nginx"; 258 + url = "https://${config.ingredient.wiki.wiki.hostname}"; 259 + nginx.hostName = config.ingredient.wiki.wiki.hostname; 260 + name = "Freshly Wiki"; 261 + database.createLocally = true; 262 + 263 + passwordSender = config.ingredient.wiki.wiki.email; 264 + 265 + passwordFile = "/secrets/mediawiki/initial_admin_password.txt"; 266 + }; 267 + 268 + systemd.timers.mediawiki-maintenance = { 269 + wantedBy = [ "timers.target" ]; 270 + timerConfig = { 271 + OnUnitActiveSec = "30"; 272 + OnBootSec = "30"; 273 + Persistent = false; 274 + Unit = "mediawiki-maintenance.service"; 275 + }; 276 + }; 277 + 278 + systemd.services.mediawiki-maintenance = { 279 + script = '' 280 + ${config.services.phpfpm.pools.mediawiki.phpPackage}/bin/php ${config.services.mediawiki.finalPackage}/share/mediawiki/maintenance/run.php runJobs --memory-limit 1G --maxtime 30 281 + ''; 282 + serviceConfig = { 283 + Type = "oneshot"; 284 + RemainAfterExit = false; 285 + User = "mediawiki"; 286 + Group = "nginx"; 287 + PrivateTmp = true; 288 + Environment = "MEDIAWIKI_CONFIG=${config.services.phpfpm.pools.mediawiki.phpEnv.MEDIAWIKI_CONFIG}"; 289 + }; 290 + }; 291 + 292 + services.opensearch = { 293 + # needed for cirrussearch 294 + enable = true; 295 + package = project.packages.opensearch.result.${system}; 296 + settings = { 297 + "http.port" = 1037; 298 + "path.data" = "/var/lib/private/opensearch/data"; 299 + "path.logs" = "/var/lib/private/opensearch/logs"; 300 + }; 301 + }; 302 + 303 + services.nginx.enable = true; 304 + services.nginx.virtualHosts.${config.ingredient.wiki.wiki.hostname} = { 305 + listen = [ 306 + { 307 + addr = "127.0.0.1"; 308 + port = 1036; 309 + } 310 + ]; 311 + 312 + locations = { 313 + "= /" = lib.mkForce { 314 + extraConfig = '' 315 + return 301 https://${config.ingredient.wiki.wiki.hostname}/wiki/; 316 + ''; # overriding nixpkgs /wiki/ redirect since as our double-proxy makes it redirect to :1036 317 + }; 318 + "= /favicon.ico".alias = ./wiki/favicon.ico; 319 + "= /icon.svg".alias = ./wiki/icon.svg; 320 + }; 321 + 322 + extraConfig = '' 323 + client_max_body_size 1024M; 324 + ''; 325 + }; 326 + services.nginx.virtualHosts."external.${config.ingredient.wiki.wiki.hostname}" = 327 + lib.mkIf config.ingredient.wiki.wiki.enablePublicInternet 328 + { 329 + listenAddresses = [ 330 + "0.0.0.0" 331 + "[::0]" 332 + ]; 333 + 334 + serverName = config.ingredient.wiki.wiki.hostname; 335 + 336 + addSSL = true; 337 + enableACME = true; 338 + acmeRoot = null; 339 + 340 + locations."/" = { 341 + proxyPass = "http://127.0.0.1:1036"; 342 + recommendedProxySettings = true; 343 + proxyWebsockets = true; 344 + 345 + extraConfig = '' 346 + proxy_set_header X-Webauth-Login ""; 347 + proxy_cache off; 348 + ''; 349 + }; 350 + 351 + extraConfig = '' 352 + client_max_body_size 1024M; 353 + ''; 354 + }; 355 + services.nginx.virtualHosts."internal.${config.ingredient.wiki.wiki.hostname}" = { 356 + listenAddresses = [ "localhost.tailscale" ]; 357 + 358 + serverName = config.ingredient.wiki.wiki.hostname; 359 + 360 + addSSL = true; 361 + enableACME = true; 362 + acmeRoot = null; 363 + 364 + locations."/" = { 365 + proxyPass = "http://127.0.0.1:1036"; 366 + recommendedProxySettings = true; 367 + proxyWebsockets = true; 368 + 369 + extraConfig = '' 370 + proxy_cache off; 371 + ''; 372 + }; 373 + 374 + extraConfig = '' 375 + client_max_body_size 1024M; 376 + ''; 377 + }; 378 + 379 + services.nginx.tailscaleAuth = { 380 + enable = true; 381 + virtualHosts = [ "internal.${config.ingredient.wiki.wiki.hostname}" ]; 382 + }; 383 + }; 384 + }
packetmix/systems/teal/wiki/CirrusSearch.composer.lock packetmix/systems/wiki/wiki/CirrusSearch.composer.lock
packetmix/systems/teal/wiki/CirrusSearch.composer.lock.license packetmix/systems/wiki/wiki/CirrusSearch.composer.lock.license
packetmix/systems/teal/wiki/Elastica.composer.lock packetmix/systems/wiki/wiki/Elastica.composer.lock
packetmix/systems/teal/wiki/Elastica.composer.lock.license packetmix/systems/wiki/wiki/Elastica.composer.lock.license
packetmix/systems/teal/wiki/Network.composer.lock packetmix/systems/wiki/wiki/Network.composer.lock
packetmix/systems/teal/wiki/Network.composer.lock.license packetmix/systems/wiki/wiki/Network.composer.lock.license
packetmix/systems/teal/wiki/OpenIDConnect.composer.lock packetmix/systems/wiki/wiki/OpenIDConnect.composer.lock
packetmix/systems/teal/wiki/OpenIDConnect.composer.lock.license packetmix/systems/wiki/wiki/OpenIDConnect.composer.lock.license
packetmix/systems/teal/wiki/favicon.ico packetmix/systems/wiki/wiki/favicon.ico
packetmix/systems/teal/wiki/favicon.ico.license packetmix/systems/wiki/wiki/favicon.ico.license
packetmix/systems/teal/wiki/icon.svg packetmix/systems/wiki/wiki/icon.svg
packetmix/systems/teal/wiki/icon.svg.license packetmix/systems/wiki/wiki/icon.svg.license