{ config, lib, pkgs, ... }: let cfg = config.services.fragdenrat; dataDir = "/var/lib/fragdenrat"; manageCmd = "${pkgs.fragdenrat}/bin/fragdenrat-manage"; pythonPath = "${pkgs.fragdenrat}/share:${pkgs.fragdenrat.pythonPath}"; envVars = [ "PYTHONPATH=${pythonPath}" "DJANGO_SETTINGS_MODULE=fragdenrat.settings" "FRAGDENRAT_DATA_DIR=${dataDir}" ]; in { options = { services.fragdenrat = { enable = lib.mkEnableOption "FragDenRat web app"; }; }; config = lib.mkIf cfg.enable { # Ensure data dir exists with proper perms systemd.tmpfiles.rules = [ "d ${dataDir} 0750 fragdenrat fragdenrat -" "d ${dataDir}/staticfiles 0750 fragdenrat fragdenrat -" ]; # One-shot setup: migrate DB and collect static into dataDir systemd.services.fragdenrat-setup = { description = "Initialize FragDenRat database and static files"; wantedBy = [ "multi-user.target" ]; after = [ "network-online.target" ]; serviceConfig = { Type = "oneshot"; User = "fragdenrat"; Group = "fragdenrat"; WorkingDirectory = dataDir; Environment = envVars; }; script = '' set -euo pipefail ${manageCmd} migrate --noinput ${manageCmd} collectstatic --noinput ''; }; # uWSGI app services.uwsgi = { enable = true; plugins = [ "python3" ]; postActivation = '' # Ensure setup runs before uwsgi serves ${config.systemd.package}/bin/systemctl start fragdenrat-setup.service ''; instance = { type = "emperor"; vassals = { fragdenrat = { type = "normal"; # Use data dir as working directory chdir = dataDir; # Absolute WSGI entrypoint in Nix store wsgi-file = "${pkgs.fragdenrat}/share/fragdenrat/wsgi.py"; callable = "application"; socket = "${config.services.uwsgi.runDir}/fragdenrat.sock"; "chmod-socket" = "660"; umask = "0077"; vacuum = true; master = true; processes = 2; threads = 2; harakiri = 60; "buffer-size" = 65535; need-app = true; "no-orphans" = true; env = envVars; settings = { # Serve collected static files from dataDir "static-map" = "/static=${dataDir}/staticfiles"; # Python import path for the app and its deps pythonpath = "${pkgs.fragdenrat}/share"; }; }; }; }; }; # Ensure fragdenrat user and group exist users.users.fragdenrat = { isSystemUser = true; group = "fragdenrat"; description = "FragDenRat web application user"; }; users.groups.fragdenrat = { }; }; meta = { maintainers = with lib.maintainers; [ onny ]; }; }