123 lines
4.2 KiB
Nix
123 lines
4.2 KiB
Nix
{ config, pkgs, lib, ... }:
|
|
{
|
|
services.mediawiki.extraConfig = lib.mkAfter ''
|
|
# ----- Invite-only accounts -----
|
|
$wgGroupPermissions['*']['createaccount'] = false;
|
|
$wgGroupPermissions['bureaucrat']['createaccount'] = true;
|
|
|
|
# ----- File cache for anonymous readers -----
|
|
$wgUseFileCache = true;
|
|
$wgFileCacheDirectory = "/var/cache/mediawiki";
|
|
$wgShowIPinHeader = false;
|
|
|
|
# ----- Rate limit exemption for logged-in users -----
|
|
$wgGroupPermissions['user']['noratelimit'] = true;
|
|
|
|
# ----- Email -----
|
|
$wgEnableEmail = true;
|
|
$wgEnableUserEmail = true;
|
|
$wgEmergencyContact = "wiki@noisebridge.net";
|
|
$wgPasswordSender = "wiki@noisebridge.net";
|
|
# Mail sent via local Postfix, which relays through m3.noisebridge.net
|
|
|
|
# ----- ReCaptcha (login brute-force only) -----
|
|
wfLoadExtension( 'ConfirmEdit/ReCaptchaNoCaptcha' );
|
|
$wgReCaptchaSiteKey = '6Le_REPLACE_SITE_KEY';
|
|
$wgReCaptchaSecretKey = trim(file_get_contents('${config.age.secrets.mediawiki-recaptcha.path}'));
|
|
$wgCaptchaTriggers['badlogin'] = true;
|
|
$wgCaptchaTriggers['createaccount'] = false;
|
|
$wgCaptchaTriggers['edit'] = false;
|
|
$wgCaptchaTriggers['create'] = false;
|
|
'';
|
|
|
|
# PHP-FPM: static pool for maximum performance
|
|
# Use individual mkForce to override defaults without clobbering
|
|
# required settings (listen, user, group) set by the mediawiki module
|
|
services.phpfpm.pools.mediawiki.settings = {
|
|
"pm" = lib.mkForce "static";
|
|
"pm.max_children" = lib.mkForce 30;
|
|
"pm.max_requests" = lib.mkForce 500;
|
|
"request_terminate_timeout" = lib.mkForce "30s";
|
|
"catch_workers_output" = lib.mkForce true;
|
|
"pm.status_path" = "/fpm-status";
|
|
|
|
# OPcache
|
|
"php_admin_value[opcache.enable]" = 1;
|
|
"php_admin_value[opcache.memory_consumption]" = 256;
|
|
"php_admin_value[opcache.max_accelerated_files]" = 10000;
|
|
"php_admin_value[opcache.revalidate_freq]" = 60;
|
|
"php_admin_value[opcache.jit]" = 1255;
|
|
"php_admin_value[opcache.jit_buffer_size]" = "64M";
|
|
|
|
# Memory & execution
|
|
"php_admin_value[memory_limit]" = "256M";
|
|
"php_admin_value[max_execution_time]" = 30;
|
|
"php_admin_value[upload_max_filesize]" = "10M";
|
|
"php_admin_value[post_max_size]" = "12M";
|
|
};
|
|
|
|
# File cache directory
|
|
systemd.tmpfiles.rules = [
|
|
"d /var/cache/mediawiki 0755 mediawiki mediawiki -"
|
|
];
|
|
|
|
# MediaWiki job runner (since wgJobRunRate=0)
|
|
systemd.services.mediawiki-jobrunner = {
|
|
description = "MediaWiki job runner";
|
|
after = [ "mysql.service" "phpfpm-mediawiki.service" ];
|
|
wantedBy = [ "multi-user.target" ];
|
|
serviceConfig = {
|
|
Type = "simple";
|
|
User = "mediawiki";
|
|
Group = "mediawiki";
|
|
ExecStart = "${pkgs.php}/bin/php ${config.services.mediawiki.finalPackage}/share/mediawiki/maintenance/runJobs.php --wait --maxjobs=10";
|
|
Restart = "always";
|
|
RestartSec = "30s";
|
|
};
|
|
};
|
|
|
|
# Sync uploaded images to replica over Tailscale (hourly)
|
|
# Writes textfile metrics so Prometheus can alert on stale syncs
|
|
systemd.services.wiki-image-sync = {
|
|
description = "Sync wiki images to replica";
|
|
after = [ "tailscale-autoconnect.service" ];
|
|
path = [ pkgs.rsync ];
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
User = "root";
|
|
};
|
|
script = ''
|
|
TEXTFILE_DIR="/var/lib/prometheus-node-exporter/textfile"
|
|
|
|
if rsync -az --delete /var/lib/mediawiki/images/ wiki-replica:/var/lib/mediawiki/images/; then
|
|
SYNC_OK=1
|
|
else
|
|
SYNC_OK=0
|
|
fi
|
|
|
|
cat > "$TEXTFILE_DIR/imagesync.prom" <<EOF
|
|
# HELP imagesync_latest_timestamp_seconds Unix timestamp of latest image sync attempt
|
|
# TYPE imagesync_latest_timestamp_seconds gauge
|
|
imagesync_latest_timestamp_seconds $(date +%s)
|
|
# HELP imagesync_success Whether the last image sync succeeded (1=success, 0=failure)
|
|
# TYPE imagesync_success gauge
|
|
imagesync_success $SYNC_OK
|
|
EOF
|
|
'';
|
|
};
|
|
|
|
systemd.timers.wiki-image-sync = {
|
|
description = "Hourly wiki image sync to replica";
|
|
wantedBy = [ "timers.target" ];
|
|
timerConfig = {
|
|
OnCalendar = "hourly";
|
|
Persistent = true;
|
|
};
|
|
};
|
|
|
|
age.secrets.mediawiki-recaptcha = {
|
|
file = ../../secrets/mediawiki-recaptcha.age;
|
|
owner = "mediawiki";
|
|
group = "mediawiki";
|
|
};
|
|
}
|