251 lines
7.4 KiB
Nix
251 lines
7.4 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
osConfig,
|
|
...
|
|
}:
|
|
with lib; let
|
|
cfg = config.services.panoramax;
|
|
|
|
# Database configuration assertions
|
|
dbUrlConfigured = cfg.database.url != null;
|
|
individualDbConfigured = all (x: x != null) [
|
|
cfg.database.host
|
|
cfg.database.port
|
|
cfg.database.username
|
|
cfg.database.password
|
|
cfg.database.name
|
|
];
|
|
|
|
envContent = ''
|
|
# Panoramax Configuration
|
|
FLASK_APP=geovisio
|
|
${
|
|
if dbUrlConfigured
|
|
then "DB_URL=${cfg.database.url}"
|
|
else ''
|
|
DB_HOST=${cfg.database.host}
|
|
DB_PORT=${toString cfg.database.port}
|
|
DB_USERNAME=${cfg.database.username}
|
|
DB_PASSWORD=${cfg.database.password}
|
|
DB_NAME=${cfg.database.name}
|
|
''
|
|
}
|
|
${optionalString (cfg.storage.fsUrl != null) "FS_URL=${cfg.storage.fsUrl}"}
|
|
${optionalString (cfg.infrastructure.nbProxies != null) "INFRA_NB_PROXIES=${toString cfg.infrastructure.nbProxies}"}
|
|
${optionalString (cfg.flask.secretKey != null) "FLASK_SECRET_KEY=${cfg.flask.secretKey}"}
|
|
${optionalString (cfg.flask.sessionCookieDomain != null) "FLASK_SESSION_COOKIE_DOMAIN=${cfg.flask.sessionCookieDomain}"}
|
|
${optionalString (cfg.api.pictures.licenseSpdxId != null) "API_PICTURES_LICENSE_SPDX_ID=${cfg.api.pictures.licenseSpdxId}"}
|
|
${optionalString (cfg.api.pictures.licenseUrl != null) "API_PICTURES_LICENSE_URL=${cfg.api.pictures.licenseUrl}"}
|
|
${optionalString (cfg.port != null) "PORT=${toString cfg.port}"}
|
|
${concatStringsSep "\n" (mapAttrsToList (name: value: "${name}=${value}") cfg.extraEnvironment)}
|
|
'';
|
|
|
|
envFile = pkgs.writeText "panoramax.env" envContent;
|
|
in {
|
|
options.services.panoramax = {
|
|
enable = lib.mkEnableOption "panoramax";
|
|
|
|
package = lib.mkOption {
|
|
type = lib.types.package;
|
|
default = pkgs.panoramax;
|
|
description = "The panoramax package to use";
|
|
};
|
|
|
|
# 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 config.services.panoramax.enable (
|
|
lib.mkMerge [
|
|
{
|
|
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 -"
|
|
];
|
|
|
|
assertions = [
|
|
{
|
|
assertion = dbUrlConfigured || individualDbConfigured;
|
|
message = ''
|
|
Panoramax database configuration requires either:
|
|
- A complete database URL (services.panoramax.database.url), OR
|
|
- All individual database options (host, port, username, password, name)
|
|
|
|
Currently configured:
|
|
- database.url: ${
|
|
if dbUrlConfigured
|
|
then "✓ configured"
|
|
else "✗ not configured"
|
|
}
|
|
- individual options: ${
|
|
if individualDbConfigured
|
|
then "✓ all configured"
|
|
else "✗ some missing"
|
|
}
|
|
'';
|
|
}
|
|
];
|
|
|
|
# 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
|
|
}
|
|
)
|
|
(
|
|
lib.mkIf osConfig.host.impermanence.enable {
|
|
# TODO: configure impermanence for panoramax data
|
|
}
|
|
)
|
|
]
|
|
);
|
|
}
|