# Web services configuration
{ config, pkgs, lib, ... }: {

  # Reverse proxy configuration
  services.nginx = {
    enable = true;
    recommendedTlsSettings = true;
    recommendedGzipSettings = true;
    recommendedProxySettings = true;
    recommendedOptimisation = true;
    recommendedBrotliSettings = true;
    clientMaxBodySize = "0";
    sslCiphers =
      "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK:!AES128";
    sslProtocols = "TLSv1.2 TLSv1.3";
    sslDhparam = "/var/lib/dhparams/nginx.pem";
    commonHttpConfig = ''
      # Add HSTS header with preloading to HTTPS requests.
      add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";

      # Minimize information leaked to other domains
      add_header 'Referrer-Policy' 'strict-origin-when-cross-origin';

      # Prevent injection of code in other mime types (XSS Attacks)
      add_header X-Content-Type-Options nosniff;
      add_header X-XSS-Protection "1; mode=block";
      add_header X-Frame-Options SAMEORIGIN;

      # This might create errors
      proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict";
    '';
    virtualHosts = {
      "coolneng.duckdns.org" = {
        useACMEHost = "coolneng.duckdns.org";
        forceSSL = true;
        # Redirect from legacy subdirectory URL to subdomain
        locations = {
          "/radicale/".return = "301 https://radicale.coolneng.duckdns.org";
          "/syncthing/".return = "301 https://sync.coolneng.duckdns.org";
          "/gitea/".extraConfig =
            "rewrite ^/gitea/(.*)$ https://git.coolneng.duckdns.org/$1 last;";
          "/miniflux/".extraConfig =
            "rewrite ^/miniflux/(.*)$ https://rss.coolneng.duckdns.org/$1 last;";
          # Delegation for Matrix
          "/.well-known/" = {
            alias = "${../well-known}" + "/";
            extraConfig = ''
              ${config.services.nginx.commonHttpConfig}
              default_type application/json;
              add_header Access-Control-Allow-Origin * always;
            '';
          };
        };
      };
      "radicale.coolneng.duckdns.org" = {
        useACMEHost = "coolneng.duckdns.org";
        forceSSL = true;
        locations."/" = {
          proxyPass = "http://localhost:5232/";
          extraConfig = ''
            proxy_set_header     X-Script-Name /;
            proxy_pass_header Authorization;
          '';
        };
      };
      "sync.coolneng.duckdns.org" = {
        useACMEHost = "coolneng.duckdns.org";
        forceSSL = true;
        locations."/".proxyPass = "http://localhost:8384/";
      };
      "git.coolneng.duckdns.org" = {
        useACMEHost = "coolneng.duckdns.org";
        forceSSL = true;
        locations."/" = {
          proxyPass = "http://localhost:3000/";
          extraConfig = ''
            ${config.services.nginx.commonHttpConfig}
            # Disable embedding as a frame, except from the same origin
            add_header Content-Security-Policy "frame-src git.coolneng.duckdns.org; frame-ancestors git.coolneng.duckdns.org";
          '';
        };
      };
      "rss.coolneng.duckdns.org" = {
        useACMEHost = "coolneng.duckdns.org";
        forceSSL = true;
        locations."/".proxyPass = "http://localhost:8080/";
      };
      "matrix.coolneng.duckdns.org" = {
        useACMEHost = "coolneng.duckdns.org";
        forceSSL = true;
        listen = [
          {
            addr = "0.0.0.0";
            port = 8448;
            ssl = true;
          }
          {
            addr = "0.0.0.0";
            port = 443;
            ssl = true;
          }
        ];
        locations."~ ^(/_matrix|/_synapse/client)" = {
          proxyPass = "http://localhost:8008";
          extraConfig = ''
            proxy_set_header X-Forwarded-For $remote_addr;
            proxy_set_header X-Forwarded-Proto $scheme;
          '';
        };
      };
      "element.coolneng.duckdns.org" = {
        useACMEHost = "coolneng.duckdns.org";
        forceSSL = true;
        locations."/".root = pkgs.element-web.override {
          conf.default_server_config = {
            "m.homeserver"."base_url" = "https://matrix.coolneng.duckdns.org";
            "m.identity_server"."base_url" = "https://vector.im";
          };
        };
      };
      "wallabag.coolneng.duckdns.org" = {
        useACMEHost = "coolneng.duckdns.org";
        forceSSL = true;
        root = "${pkgs.wallabag}/web";
        locations = {
          "/".tryFiles = "$uri /app.php$is_args$args";
          "/assets".root = "${config.environment.variables.WALLABAG_DATA}/web";
          "~ ^/app.php(/|$)" = {
            fastcgiParams = {
              SCRIPT_FILENAME = "${pkgs.wallabag}/web/$fastcgi_script_name";
              DOCUMENT_ROOT = "${pkgs.wallabag}/web";
            };
            extraConfig = ''
              fastcgi_pass unix:${config.services.phpfpm.pools.wallabag.socket};
              fastcgi_split_path_info ^(.+\.php)(/.*)$;
              include ${pkgs.nginx}/conf/fastcgi_params;
              internal;
            '';
          };
        };
      };
      "books.coolneng.duckdns.org" = {
        useACMEHost = "coolneng.duckdns.org";
        forceSSL = true;
        locations."/" = {
          proxyPass = "http://localhost:9000/";
          proxyWebsockets = true;
          extraConfig = ''
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";
          '';
        };
      };
      "grafana.coolneng.duckdns.org" = {
        useACMEHost = "coolneng.duckdns.org";
        forceSSL = true;
        locations."/" = {
          proxyPass = "http://localhost:9009/";
          proxyWebsockets = true;
        };
      };
      "nightscout.coolneng.duckdns.org" = {
        useACMEHost = "coolneng.duckdns.org";
        forceSSL = true;
        locations."/" = {
          proxyPass = "http://localhost:1337";
          extraConfig = ''
            proxy_set_header X-Forwarded-For $remote_addr;
          '';
        };
      };
    };
  };

  # ACME certs configuration
  security.acme = {
    acceptTerms = true;
    defaults = {
      email = "akasroua@disroot.org";
      dnsResolver = "127.0.0.1:53";
      group = "nginx";
      webroot = "/var/lib/acme/acme-challenge";
      ocspMustStaple = true;
    };
    certs."coolneng.duckdns.org".extraDomainNames =
      lib.attrsets.mapAttrsToList (name: value: "${name}")
      config.services.nginx.virtualHosts;
  };

  # Generate dhparams
  security.dhparams = {
    enable = true;
    params.nginx.bits = 2048;
  };

  # PostgreSQL databases configuration
  services.postgresql = {
    enable = true;
    package = pkgs.postgresql_15;
    authentication = lib.mkForce ''
      # Generated file; do not edit!
      # TYPE  DATABASE        USER            ADDRESS                 METHOD
      local   all             all                                     trust
      host    all             all             127.0.0.1/32            trust
      host    all             all             ::1/128                 trust
    '';
    settings = {
      max_connections = "300";
      shared_buffers = "1024MB";
    };
  };

  # Restart reverse proxy after services startup
  systemd.services.nginx.after = [
    "gitea.service"
    "syncthing.service"
    "miniflux.service"
    "radicale.service"
    "dendrite.service"
    "phpfpm-wallabag.service"
    "systemd-tmpfiles-setup.service"
    "podman-openbooks.service"
    "podman-mqtt2prometheus.service"
    "podman-nightscout.service"
  ];
}