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