{ lib, pkgs, config, ... }: let cfg = config.services.kukkee; in with builtins; { options.services.kukkee = with lib; { enable = mkEnableOption "kukkee service"; package = mkPackageOption pkgs "kukkee" { }; user = mkOption { type = types.str; default = "kukkee"; description = "User under which Kukkee runs."; }; group = mkOption { type = types.str; default = "kukkee"; description = "Group under which Kukkee runs."; }; listen = mkOption { type = types.str; default = "127.0.0.1"; description = "Which address to listen on."; }; port = mkOption { type = types.port; default = 3000; description = "Which port Kukkee should listen to for HTTP."; }; extraArgs = mkOption { type = types.listOf types.str; default = []; description = "Extra command-line arguments for the next.js runtime."; }; baseUrl = mkOption { type = types.str; default = "http://localhost:${cfg.port}"; description = "The base URL for the site"; }; openFirewall = mkOption { type = types.bool; default = false; description = '' Open the configured port in the firewall for the Kukkee server. Preferably the Kukkee server is instead put behind a reverse proxy. ''; }; mongodb.enable = mkOption { type = types.bool; default = true; description = "Whether to configure a local MongoDB instance."; }; mongodb.uri = mkOption { type = types.str; default = "mongodb://127.0.0.1:27017/kukkeePolls"; example = "mongodb+srv://:@.aewjs.mongodb.net/?retryWrites=true&w=majority"; description = '' Mongodb connection string. MongoDB databases are normally created automatically upon first write. ''; }; }; config = lib.mkMerge [ (lib.mkIf (cfg.enable && cfg.mongodb.enable) { services.mongodb.enable = true; systemd.services.kukkee.after = [ "mongodb.service" ]; systemd.services.kukkee.requires = [ "mongodb.service" ]; }) (lib.mkIf cfg.enable { systemd.services.kukkee = { description = "Kukkee Server"; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; environment = { # https://github.com/AnandBaburajan/Kukkee/blob/270c8ed421c8f1100a845958430e1ebe61d86d5a/.env.example NEXT_MONGODB_URI = cfg.mongodb.uri; NEXT_PUBLIC_BASE_URL = cfg.baseUrl; NEXT_PUBLIC_ENCRYPTION_KEY = "2a148b84dcec756c59ab96d450a79372"; # TODO NEXT_PUBLIC_ENCRYPTION_IV = "0d88ec0887f614b6"; # TODO }; serviceConfig = let args = map lib.strings.escapeShellArg [ "--hostname" cfg.listen "--port" cfg.port ] ++ cfg.extraArgs; in rec { User = cfg.user; Group = cfg.group; ExecStart = "${cfg.package}/bin/kukkee " + (lib.strings.concatStringsSep " " args); Restart = "on-failure"; # Security options: NoNewPrivileges = true; AmbientCapabilities = ""; CapabilityBoundingSet = ""; DeviceAllow = ""; LockPersonality = true; PrivateTmp = true; PrivateDevices = true; PrivateUsers = true; ProtectClock = true; ProtectControlGroups = true; ProtectHostname = true; ProtectKernelLogs = true; ProtectKernelModules = true; ProtectKernelTunables = true; RemoveIPC = true; RestrictNamespaces = true; RestrictAddressFamilies = [ "AF_NETLINK" "AF_INET" "AF_INET6" "AF_UNIX" ]; RestrictRealtime = true; RestrictSUIDSGID = true; SystemCallArchitectures = "native"; SystemCallErrorNumber = "EPERM"; SystemCallFilter = [ "@system-service" "~@cpu-emulation" "~@debug" "~@keyring" "~@memlock" "~@obsolete" "~@privileged" "~@setuid" ]; }; }; users.users = lib.mkIf (cfg.user == "kukkee") { kukkee = { group = cfg.group; isSystemUser = true; }; }; users.groups = lib.mkIf (cfg.group == "kukkee") { kukkee = {}; }; networking.firewall = lib.mkIf cfg.openFirewall { allowedTCPPorts = [ cfg.port ]; }; }) ]; meta.maintainers = with lib.maintainers; [ pbsds ]; }