diff --git a/modules/nixos-modules/server/panoramax.nix b/modules/nixos-modules/server/panoramax.nix index f7d305f..5196a2d 100644 --- a/modules/nixos-modules/server/panoramax.nix +++ b/modules/nixos-modules/server/panoramax.nix @@ -4,8 +4,28 @@ pkgs, osConfig, ... -}: let - cfg = config.services.panoramax; +}: +with lib; let + envContent = '' + # Panoramax Configuration + FLASK_APP=geovisio + ${optionalString (config.services.panoramax.database.url != null) "DB_URL=${config.services.panoramax.database.url}"} + ${optionalString (config.services.panoramax.database.url == null && config.services.panoramax.database.port != null) "DB_PORT=${toString config.services.panoramax.database.port}"} + ${optionalString (config.services.panoramax.database.url == null && config.services.panoramax.database.host != null) "DB_HOST=${config.services.panoramax.database.host}"} + ${optionalString (config.services.panoramax.database.url == null && config.services.panoramax.database.username != null) "DB_USERNAME=${config.services.panoramax.database.username}"} + ${optionalString (config.services.panoramax.database.url == null && config.services.panoramax.database.password != null) "DB_PASSWORD=${config.services.panoramax.database.password}"} + ${optionalString (config.services.panoramax.database.url == null && config.services.panoramax.database.name != null) "DB_NAME=${config.services.panoramax.database.name}"} + ${optionalString (config.services.panoramax.storage.fsUrl != null) "FS_URL=${config.services.panoramax.storage.fsUrl}"} + ${optionalString (config.services.panoramax.infrastructure.nbProxies != null) "INFRA_NB_PROXIES=${toString config.services.panoramax.infrastructure.nbProxies}"} + ${optionalString (config.services.panoramax.flask.secretKey != null) "FLASK_SECRET_KEY=${config.services.panoramax.flask.secretKey}"} + ${optionalString (config.services.panoramax.flask.sessionCookieDomain != null) "FLASK_SESSION_COOKIE_DOMAIN=${config.services.panoramax.flask.sessionCookieDomain}"} + ${optionalString (config.services.panoramax.api.pictures.licenseSpdxId != null) "API_PICTURES_LICENSE_SPDX_ID=${config.services.panoramax.api.pictures.licenseSpdxId}"} + ${optionalString (config.services.panoramax.api.pictures.licenseUrl != null) "API_PICTURES_LICENSE_URL=${config.services.panoramax.api.pictures.licenseUrl}"} + ${optionalString (config.services.panoramax.port != null) "PORT=${toString config.services.panoramax.port}"} + ${concatStringsSep "\n" (mapAttrsToList (name: value: "${name}=${value}") config.services.panoramax.extraEnvironment)} + ''; + + envFile = pkgs.writeText "panoramax.env" envContent; in { options.services.panoramax = { enable = lib.mkEnableOption "panoramax"; @@ -16,23 +36,166 @@ in { description = "The panoramax package to use"; }; - # TODO: create configs - # TODO: auto config db - # config = { - # DB_PORT = lib.mkOption {}; - # DB_HOST = lib.mkOption {}; - # DB_USERNAME = lib.mkOption {}; - # DB_PASSWORD = lib.mkOption {}; - # DB_NAME = lib.mkOption {}; - # FS_URL = lib.mkOption {}; - # }; + # TODO: sgblur config + port = mkOption { + type = types.nullOr types.port; + default = 5000; + description = "Port for the Panoramax service"; + }; + + host = mkOption { + type = types.str; + default = "127.0.0.1"; + description = "Host to bind the Panoramax service to"; + }; + + urlScheme = mkOption { + type = types.enum ["http" "https"]; + default = "https"; + description = "URL scheme for the application"; + }; + + database = { + url = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Complete database URL connection string (e.g., "postgresql://user:password@host:port/dbname"). + If provided, individual database options (host, port, username, password, name) are ignored. + ''; + }; + + port = mkOption { + type = types.nullOr types.port; + default = 5432; + description = "Database port (ignored if database.url is set)"; + }; + + host = mkOption { + type = types.nullOr types.str; + default = "localhost"; + description = "Database host (ignored if database.url is set)"; + }; + + username = mkOption { + type = types.nullOr types.str; + default = "panoramax"; + description = "Database username (ignored if database.url is set)"; + }; + + password = mkOption { + type = types.nullOr types.str; + default = null; + description = "Database password (ignored if database.url is set)"; + }; + + name = mkOption { + type = types.nullOr types.str; + default = "panoramax"; + description = "Database name (ignored if database.url is set)"; + }; + }; + + storage = { + fsUrl = mkOption { + type = types.nullOr types.str; + default = "/var/lib/panoramax/storage"; + description = "File system URL for storage"; + }; + }; + + infrastructure = { + nbProxies = mkOption { + type = types.nullOr types.int; + default = 1; + description = "Number of proxies in front of the application"; + }; + }; + + flask = { + secretKey = mkOption { + type = types.nullOr types.str; + default = null; + description = "Flask secret key for session security"; + }; + + sessionCookieDomain = mkOption { + type = types.nullOr types.str; + default = null; + description = "Flask session cookie domain"; + }; + }; + + api = { + pictures = { + licenseSpdxId = mkOption { + type = types.nullOr types.str; + default = null; + description = "SPDX license identifier for API pictures"; + }; + + licenseUrl = mkOption { + type = types.nullOr types.str; + default = null; + description = "License URL for API pictures"; + }; + }; + }; + + extraEnvironment = mkOption { + type = types.attrsOf types.str; + default = {}; + description = "Additional environment variables"; + example = { + CUSTOM_SETTING = "value"; + DEBUG = "true"; + }; + }; }; - config = lib.mkIf cfg.enable ( + config = lib.mkIf config.services.panoramax.enable ( lib.mkMerge [ { - # TODO: configure options for the package + environment.systemPackages = with pkgs; [ + config.services.panoramax.package + python3Packages.waitress + ]; + + systemd.services.panoramax = { + description = "Panoramax Service"; + after = ["network.target"]; + wantedBy = ["multi-user.target"]; + serviceConfig = { + ExecStart = "${pkgs.python3Packages.waitress}/bin/waitress-serve --env-file=${envFile} --host=${config.services.panoramax.host} --port=${toString config.services.panoramax.port} --url-scheme=${config.services.panoramax.urlScheme} --call geovisio:create_app"; + Restart = "always"; + User = "panoramax"; + Group = "panoramax"; + WorkingDirectory = "/var/lib/panoramax"; + Environment = "PYTHONPATH=${config.services.panoramax.package}/lib/python3.11/site-packages"; + }; + }; + + users.users.panoramax = { + isSystemUser = true; + group = "panoramax"; + home = "/var/lib/panoramax"; + createHome = true; + }; + + users.groups.panoramax = {}; + + systemd.tmpfiles.rules = [ + "d /var/lib/panoramax 0755 panoramax panoramax -" + "d ${config.services.panoramax.storage.fsUrl} 0755 panoramax panoramax -" + ]; + + # TODO: auto config db } + ( + lib.mkIf config.host.reverse_proxy.enable { + # TODO: configure reverse proxy here + } + ) ( lib.mkIf config.services.fail2ban { # TODO: configure options for fail2ban