refactor: split server modules into smaller more manageable files

This commit is contained in:
Leyla Becker 2025-09-16 10:14:33 -05:00
parent b2e5ae1f98
commit cdeb4e108b
49 changed files with 1519 additions and 1270 deletions

View file

@ -0,0 +1,340 @@
{
config,
lib,
pkgs,
...
}:
with lib; let
# Database configuration assertions
dbUrlConfigured = config.services.panoramax.database.url != null;
individualDbConfigured = all (x: x != null) [
config.services.panoramax.database.host
config.services.panoramax.database.port
config.services.panoramax.database.username
config.services.panoramax.database.password
config.services.panoramax.database.name
];
envContent = ''
# Panoramax Configuration
FLASK_APP=geovisio
${
if dbUrlConfigured
then "DB_URL=${config.services.panoramax.database.url}"
else ''
DB_HOST=${config.services.panoramax.database.host}
DB_PORT=${toString config.services.panoramax.database.port}
DB_USERNAME=${config.services.panoramax.database.username}
DB_PASSWORD=${config.services.panoramax.database.password}
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}"}
${optionalString (config.services.panoramax.sgblur.enable) "SGBLUR_API_URL=${config.services.panoramax.sgblur.url}"}
${concatStringsSep "\n" (mapAttrsToList (name: value: "${name}=${value}") config.services.panoramax.extraEnvironment)}
'';
envFile = pkgs.writeText "panoramax.env" envContent;
in {
imports = [
./proxy.nix
./fail2ban.nix
./impermanence.nix
];
options.services.panoramax = {
enable = lib.mkEnableOption "panoramax";
package = lib.mkOption {
type = lib.types.package;
default = pkgs.panoramax;
description = "The panoramax package to use";
};
subdomain = lib.mkOption {
type = lib.types.str;
description = "subdomain of base domain that panoramax will be hosted at";
default = "panoramax";
};
database = {
createDB = mkOption {
type = types.bool;
default = true;
description = "Whether to automatically create the database and user";
};
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.str;
default = "panoramax";
description = "Database name (ignored if database.url is set)";
};
};
sgblur = {
enable = mkOption {
type = types.bool;
default = false;
description = "Whether to enable sgblur integration for face and license plate blurring";
};
package = mkOption {
type = types.package;
default = pkgs.sgblur;
description = "The sgblur package to use";
};
port = mkOption {
type = types.port;
default = 8080;
description = "Port for the sgblur service";
};
host = mkOption {
type = types.str;
default = "127.0.0.1";
description = "Host to bind the sgblur service to";
};
url = mkOption {
type = types.str;
default = "http://127.0.0.1:8080";
description = "URL where sgblur service is accessible";
};
};
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";
};
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
]
++ optionals config.services.panoramax.sgblur.enable [
config.services.panoramax.sgblur.package
];
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"
}
'';
}
{
assertion = !config.services.panoramax.database.createDB || config.services.panoramax.database.url == null || (lib.hasPrefix "/run/" config.services.panoramax.database.url || lib.hasPrefix "unix:" config.services.panoramax.database.url || lib.hasPrefix "/" config.services.panoramax.database.host);
message = ''
Panoramax createDB option can only be used with socket connections when a database URL is provided.
Socket connections are identified by:
- URLs starting with "unix:"
- URLs starting with "/run/"
- Host paths starting with "/"
Current configuration:
- createDB: ${lib.boolToString config.services.panoramax.database.createDB}
- database.url: ${
if config.services.panoramax.database.url != null
then config.services.panoramax.database.url
else "not set"
}
- database.host: ${config.services.panoramax.database.host}
'';
}
];
}
(lib.mkIf config.services.panoramax.database.createDB {
systemd.services.panoramax = {
after = ["postgresql.service"];
requires = ["postgresql.service"];
};
services.postgresql = {
enable = true;
ensureDatabases = [config.services.panoramax.database.name];
ensureUsers = [
{
name = config.services.panoramax.database.username;
ensureDBOwnership = true;
ensureClauses.login = true;
}
];
extensions = ps: with ps; [postgis];
settings = {
shared_preload_libraries = ["postgis"];
};
};
systemd.services.postgresql.serviceConfig.ExecStartPost = let
sqlFile = pkgs.writeText "panoramax-postgis-setup.sql" ''
CREATE EXTENSION IF NOT EXISTS postgis;
CREATE EXTENSION IF NOT EXISTS postgis_topology;
CREATE EXTENSION IF NOT EXISTS fuzzystrmatch;
CREATE EXTENSION IF NOT EXISTS postgis_tiger_geocoder;
ALTER SCHEMA public OWNER TO ${config.services.panoramax.database.username};
GRANT ALL ON SCHEMA public TO ${config.services.panoramax.database.username};
'';
in [
''
${lib.getExe' config.services.postgresql.package "psql"} -d "${config.services.panoramax.database.name}" -f "${sqlFile}"
''
];
})
]);
}