{ config, lib, pkgs, utils, sources, ... }: let inherit (lib) mapAttrsToList; python = let python3 = pkgs.python312; nix-pkgs = import sources.nix-pkgs { inherit pkgs python3; }; in python3.override { packageOverrides = _: _: { inherit (nix-pkgs) django-allauth django-allauth-cas django-browser-reload django-bulma-forms django-sass-processor django-sass-processor-dart-sass django-unfold pykanidm python-cas loadcredential ; }; }; pythonEnv = python.withPackages (ps: [ ps.django ps.gunicorn ps.psycopg ps.django-compressor # Local packages ps.django-allauth ps.django-allauth-cas ps.django-browser-reload ps.django-bulma-forms ps.django-sass-processor ps.django-sass-processor-dart-sass ps.django-unfold ps.loadcredential ps.pykanidm ps.python-cas ]); staticDrv = pkgs.stdenv.mkDerivation { name = "dgsi-static"; src = sources.dgsi; sourceRoot = "source/src"; nativeBuildInputs = [ pkgs.dart-sass pythonEnv ]; configurePhase = '' export DGSI_STATIC_ROOT=$out/static export CREDENTIALS_DIRECTORY=$(pwd)/../.credentials export DGSI_KANIDM_CLIENT="dgsi_test"; export DGSI_KANIDM_AUTH_TOKEN="fake.token"; ''; doBuild = false; installPhase = '' mkdir -p $out/static python3 manage.py compilescss python3 manage.py collectstatic ''; }; in { users = { users.nginx.extraGroups = [ "django-apps" ]; groups.django-apps = { }; }; systemd = { services = { dj-dgsi = { description = "DGSI web app"; requires = [ "dj-dgsi.socket" ]; wantedBy = [ "multi-user.target" ]; after = [ "network.target" "postgresql.service" ]; serviceConfig = { DynamicUser = true; LoadCredential = mapAttrsToList (name: value: "${name}:${value}") { SECRET_KEY = config.age.secrets."dgsi-secret_key_file".path; KANIDM_AUTH_TOKEN = config.age.secrets."dgsi-kanidm_auth_token_file".path; KANIDM_SECRET = config.age.secrets."dgsi-kanidm_secret_file".path; EMAIL_HOST_PASSWORD = config.age.secrets."dgsi-email_host_password_file".path; }; RuntimeDirectory = "django-apps/dgsi"; StateDirectory = "django-apps/dgsi"; UMask = "0027"; User = "dj-dgsi"; Group = "django-apps"; WorkingDirectory = sources.dgsi; ExecReload = "${lib.getExe' pkgs.coreutils "kill"} -s HUP $MAINPID"; KillMode = "mixed"; Type = "notify"; ExecStart = utils.escapeSystemdExecArgs [ (lib.getExe' pythonEnv "gunicorn") "--workers" 4 "--bind" "unix:/run/django-apps/dgsi.sock" "--pythonpath" "src" "app.wsgi" ]; }; environment = { DGSI_ALLOWED_HOSTS = builtins.toJSON [ "profil.dgnum.eu" "dgsi.dgnum.eu" ]; DGSI_EMAIL_HOST = "kurisu.lahfa.xyz"; DGSI_EMAIL_HOST_USER = "web-services@infra.dgnum.eu"; DGSI_EMAIL_USE_SSL = builtins.toJSON true; DGSI_FROM_EMAIL = "La Délégation Générale Numérique "; DGSI_SERVER_EMAIL = "dgsi@infra.dgnum.eu"; DGSI_KANIDM_CLIENT = "dgsi"; DGSI_KANIDM_URI = "https://sso.dgnum.eu"; DGSI_MEDIA_ROOT = "/var/lib/django-apps/dgsi/media"; DGSI_STATIC_ROOT = "${staticDrv}/static"; DGSI_DATABASES = builtins.toJSON { default = { ENGINE = "django.db.backends.postgresql"; NAME = "dj-dgsi"; }; }; DJANGO_SETTINGS_MODULE = "app.settings"; }; path = [ pythonEnv ]; preStart = '' python3 src/manage.py migrate --no-input ''; }; }; sockets."dj-dgsi" = { description = "Socket for the DGSI Django Application"; wantedBy = [ "sockets.target" ]; socketConfig = { ListenStream = "/run/django-apps/dgsi.sock"; SocketMode = "600"; SocketUser = config.services.nginx.user; }; }; mounts = [ { where = "/run/django-apps/dgsi/media"; what = "/var/lib/django-apps/dgsi/media"; options = "bind"; after = [ "dj-dgsi.service" ]; partOf = [ "dj-dgsi.service" ]; upheldBy = [ "dj-dgsi.service" ]; } ]; }; dgn-redirections.redirections."dgsi.dgnum.eu" = "profil.dgnum.eu"; services = { postgresql = { ensureDatabases = [ "dj-dgsi" ]; ensureUsers = [ { name = "dj-dgsi"; ensureDBOwnership = true; } ]; }; nginx.virtualHosts."profil.dgnum.eu" = { enableACME = true; forceSSL = true; locations = { "/".proxyPass = "http://unix:/run/django-apps/dgsi.sock"; "/static/".root = staticDrv; "/media/".root = "/run/django-apps/dgsi"; }; }; }; }