{ config, lib, pkgs, ... }: let cfg = config.services.tide; landingPage = import ./site.nix { inherit pkgs; }; contentSecurityPolicy = lib.concatStringsSep " " [ "default-src 'self';" "base-uri 'self';" "font-src 'self';" "form-action 'self';" "frame-ancestors 'none';" "img-src 'self' data:;" "object-src 'none';" "style-src 'self';" ]; dumpCommand = lib.concatStringsSep " " [ "${config.services.postgresql.package}/bin/pg_dump" "--format=custom" "--dbname=${config.services.discourse.database.name}" ]; secretFileOption = description: lib.mkOption { type = lib.types.path; inherit description; }; in { options.services.tide = { enable = lib.mkEnableOption "Torah Im Derech Eretz forum"; secretFiles = { adminPassword = secretFileOption "Discourse administrator password file."; mailPassword = secretFileOption "Mailjet SMTP password file."; secretKeyBase = secretFileOption "Discourse secret key base file."; borgPassphrase = secretFileOption "Borg repository passphrase file."; borgPrivateKey = secretFileOption "Borg repository SSH private key file."; }; }; config = lib.mkIf cfg.enable { services.nginx = { enable = true; virtualHosts."torahimderecheretz.com" = { enableACME = true; forceSSL = true; root = landingPage; locations."/" = { tryFiles = "$uri $uri/ =404"; extraConfig = '' add_header Content-Security-Policy "${contentSecurityPolicy}" always; add_header Permissions-Policy "camera=(), geolocation=(), microphone=()" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header X-Content-Type-Options "nosniff" always; ''; }; }; }; services.discourse = { enable = true; admin = { email = "yisroel.d.baum@gmail.com"; fullName = "Yisroel Baum"; passwordFile = cfg.secretFiles.adminPassword; username = "yisroeldbaum"; }; database.ignorePostgresqlVersion = true; hostname = "discourse.torahimderecheretz.com"; mail = { notificationEmailAddress = "system@torahimderecheretz.com"; contactEmailAddress = "system@torahimderecheretz.com"; outgoing = { serverAddress = "in-v3.mailjet.com"; port = 587; username = "1ebdd73d944240c1a9c3201e52a1e8c6"; passwordFile = cfg.secretFiles.mailPassword; authentication = "login"; }; }; secretKeyBaseFile = cfg.secretFiles.secretKeyBase; siteSettings.required = { title = "Torah Im Derech Eretz"; site_description = "A forum to discuss the ideas of Rav Shamshon Refael Hirsch"; }; }; services.borgbackup.jobs.discourse = { paths = [ "/var/lib/discourse" "/var/backup/discourse/discourse.dump" ]; repo = "ssh://oas17j8p@oas17j8p.repo.borgbase.com/./repo"; user = "root"; group = "root"; encryption = { mode = "repokey-blake2"; passCommand = "${pkgs.coreutils}/bin/cat ${cfg.secretFiles.borgPassphrase}"; }; doInit = true; compression = "auto,zstd"; startAt = "*-*-* 07:15:00"; persistentTimer = true; prune.keep = { daily = 7; weekly = 4; monthly = 6; }; readWritePaths = [ "/var/backup/discourse" ]; environment.BORG_RSH = lib.concatStringsSep " " [ "ssh" "-i ${cfg.secretFiles.borgPrivateKey}" "-o IdentitiesOnly=yes" "-o BatchMode=yes" "-o StrictHostKeyChecking=accept-new" "-o UserKnownHostsFile=/root/.config/borg/known_hosts" ]; preHook = '' discourseWasActive=/root/.cache/borg/discourse-was-active dumpFile=/var/backup/discourse/discourse.dump if ${pkgs.systemd}/bin/systemctl is-active --quiet discourse.service; then touch "$discourseWasActive" ${pkgs.systemd}/bin/systemctl stop discourse.service else rm -f "$discourseWasActive" fi rm -f "$dumpFile" umask 077 ${lib.getExe' pkgs.su "su"} \ -s ${pkgs.runtimeShell} \ ${config.services.postgresql.superUser} \ -c '${dumpCommand}' \ > "$dumpFile" ''; postHook = '' discourseWasActive=/root/.cache/borg/discourse-was-active dumpFile=/var/backup/discourse/discourse.dump rm -f "$dumpFile" if [ -e "$discourseWasActive" ]; then ${pkgs.systemd}/bin/systemctl start discourse.service rm -f "$discourseWasActive" fi ''; }; systemd.tmpfiles.rules = [ "d /var/backup/discourse 0700 root root - -" ]; systemd.services.borgbackup-job-discourse = { after = [ "sops-install-secrets.service" "network-online.target" "postgresql.target" "discourse-postgresql.service" ]; wants = [ "network-online.target" "postgresql.target" "discourse-postgresql.service" ]; }; }; }