{ config, lib, ... }: let cfg = config.hive.gotify-instance; server-config = { server = { listenaddr = "localhost"; port = 54545; ssl.enabled = false; ssl.redirecttohttps = false; cors.alloworigins = ["${cfg.instanceFQDN}"]; stream.allowedorigins = ["${cfg.instanceFQDN}"]; }; database = { dialect = "postgres"; connection = "host=/run/postgresql dbname=${cfg.user} user=${cfg.user}"; }; defaultuser = { name = "admin"; pass = config.sops.placeholder.${cfg.adminPasswordSopsKey}; }; registration = false; }; server-config-yaml = lib.generators.toYAML {} server-config; in { options.hive.gotify-instance = { enable = lib.mkEnableOption "Enable the Gotify instance"; instanceFQDN = lib.mkOption { type = lib.types.singleLineStr; example = "gotify.example.com"; description = "Fully qualified domain name of the Gotify instance"; }; user = lib.mkOption { type = lib.types.singleLineStr; default = "gotify"; description = "The user to run the service as"; }; group = lib.mkOption { type = lib.types.singleLineStr; default = "gotify"; description = "The group to run the service as"; }; adminPasswordSopsKey = lib.mkOption { type = lib.types.singleLineStr; description = "The SOPS key for the default admin user"; }; }; config = lib.mkIf cfg.enable { services.gotify.enable = true; # Config setup sops.templates."gotify-server-config.yml" = { owner = cfg.user; content = server-config-yaml; }; environment.etc."gotify/config.yml".source = config.sops.templates."gotify-server-config.yml".path; # User setup users.users = lib.mkIf (cfg.user == "gotify") { gotify = { description = "Gotify service"; useDefaultShell = true; group = cfg.group; isSystemUser = true; }; }; users.groups = lib.mkIf (cfg.group == "gotify") { gotify = {}; }; # Configure gotify to run as the specified user (for postgres authentication) systemd.services.gotify-server = { serviceConfig = { DynamicUser = lib.mkForce false; User = cfg.user; RuntimeDirectory = "gotify"; }; }; # Fallback server with only 403 services.nginx.virtualHosts.${config.networking.domain} = lib.mkDefault { default = true; locations."/".return = 403; forceSSL = true; enableACME = true; }; # Virtual host for gotify services.nginx.virtualHosts."${cfg.instanceFQDN}" = { forceSSL = true; enableACME = true; locations."/" = { proxyPass = "http://${server-config.server.listenaddr}:${toString server-config.server.port}"; extraConfig = '' # Ensuring it can use websockets proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto http; proxy_redirect http:// $scheme://; # The proxy must preserve the host because gotify verifies the host with the origin # for WebSocket connections proxy_set_header Host $host; # These sets the timeout so that the websocket can stay alive proxy_connect_timeout 1m; proxy_send_timeout 1m; proxy_read_timeout 1m; ''; }; }; # Database setup services.postgresql = { enable = true; ensureDatabases = [cfg.user]; ensureUsers = [ { name = cfg.user; ensureDBOwnership = true; } ]; }; }; }