···
9
+
cfg = config.services.froide-govplan;
10
+
pythonFmt = pkgs.formats.pythonVars { };
11
+
settingsFile = pythonFmt.generate "extra_settings.py" cfg.settings;
13
+
pkg = cfg.package.overridePythonAttrs (old: {
17
+
ln -s ${settingsFile} $out/${pkg.python.sitePackages}/froide_govplan/project/extra_settings.py
21
+
froide-govplan = pkgs.writeShellApplication {
22
+
name = "froide-govplan";
23
+
runtimeInputs = [ pkgs.coreutils ];
26
+
if [[ "$USER" != govplan ]]; then
27
+
SUDO="exec /run/wrappers/bin/sudo -u govplan"
29
+
$SUDO env ${lib.getExe pkg} "$@"
34
+
defaultServiceConfig = {
35
+
# Secure the services
36
+
ReadWritePaths = [ cfg.dataDir ];
37
+
CacheDirectory = "froide-govplan";
38
+
CapabilityBoundingSet = "";
39
+
# ProtectClock adds DeviceAllow=char-rtc r
41
+
LockPersonality = true;
42
+
MemoryDenyWriteExecute = true;
43
+
NoNewPrivileges = true;
44
+
PrivateDevices = true;
45
+
PrivateMounts = true;
47
+
PrivateUsers = true;
48
+
ProtectClock = true;
50
+
ProtectHostname = true;
51
+
ProtectSystem = "strict";
52
+
ProtectControlGroups = true;
53
+
ProtectKernelLogs = true;
54
+
ProtectKernelModules = true;
55
+
ProtectKernelTunables = true;
56
+
ProtectProc = "invisible";
58
+
RestrictAddressFamilies = [
63
+
RestrictNamespaces = true;
64
+
RestrictRealtime = true;
65
+
RestrictSUIDSGID = true;
66
+
SystemCallArchitectures = "native";
67
+
SystemCallFilter = [
69
+
"~@privileged @setuid @keyring"
76
+
options.services.froide-govplan = {
78
+
enable = lib.mkEnableOption "Gouvernment planer web app Govplan";
80
+
package = lib.mkPackageOption pkgs "froide-govplan" { };
82
+
hostName = lib.mkOption {
83
+
type = lib.types.str;
84
+
default = "localhost";
85
+
description = "FQDN for the froide-govplan instance.";
88
+
dataDir = lib.mkOption {
89
+
type = lib.types.str;
90
+
default = "/var/lib/froide-govplan";
91
+
description = "Directory to store the Froide-Govplan server data.";
94
+
secretKeyFile = lib.mkOption {
95
+
type = lib.types.nullOr lib.types.path;
98
+
Path to a file containing the secret key.
102
+
settings = lib.mkOption {
104
+
Configuration options to set in `extra_settings.py`.
109
+
type = lib.types.submodule {
110
+
freeformType = pythonFmt.type;
113
+
ALLOWED_HOSTS = lib.mkOption {
114
+
type = with lib.types; listOf str;
117
+
A list of valid fully-qualified domain names (FQDNs) and/or IP
118
+
addresses that can be used to reach the Froide-Govplan service.
127
+
config = lib.mkIf cfg.enable {
129
+
services.froide-govplan = {
131
+
STATIC_ROOT = "${cfg.dataDir}/static";
133
+
DATABASES.default = {
134
+
ENGINE = "django.contrib.gis.db.backends.postgis";
137
+
HOST = "/run/postgresql";
142
+
services.postgresql = {
144
+
ensureDatabases = [ "govplan" ];
148
+
ensureDBOwnership = true;
151
+
extensions = ps: with ps; [ postgis ];
155
+
enable = lib.mkDefault true;
156
+
virtualHosts."${cfg.hostName}".locations = {
157
+
"/".extraConfig = "proxy_pass http://unix:/run/froide-govplan/froide-govplan.socket;";
158
+
"/static/".alias = "${cfg.dataDir}/static/";
160
+
proxyTimeout = lib.mkDefault "120s";
166
+
postgresql.serviceConfig.ExecStartPost =
168
+
sqlFile = pkgs.writeText "immich-pgvectors-setup.sql" ''
169
+
CREATE EXTENSION IF NOT EXISTS postgis;
174
+
${lib.getExe' config.services.postgresql.package "psql"} -d govplan -f "${sqlFile}"
179
+
description = "Gouvernment planer Govplan";
180
+
serviceConfig = defaultServiceConfig // {
181
+
WorkingDirectory = cfg.dataDir;
182
+
StateDirectory = lib.mkIf (cfg.dataDir == "/var/lib/froide-govplan") "froide-govplan";
187
+
"postgresql.service"
189
+
"systemd-tmpfiles-setup.service"
191
+
wantedBy = [ "multi-user.target" ];
194
+
PYTHONPATH = pkg.pythonPath;
195
+
GDAL_LIBRARY_PATH = "${pkgs.gdal}/lib/libgdal.so";
196
+
GEOS_LIBRARY_PATH = "${pkgs.geos}/lib/libgeos_c.so";
198
+
// lib.optionalAttrs (cfg.secretKeyFile != null) {
199
+
SECRET_KEY_FILE = cfg.secretKeyFile;
202
+
# Auto-migrate on first run or if the package has changed
203
+
versionFile="${cfg.dataDir}/src-version"
204
+
version=$(cat "$versionFile" 2>/dev/null || echo 0)
206
+
if [[ $version != ${pkg.version} ]]; then
207
+
${lib.getExe pkg} migrate --no-input
208
+
${lib.getExe pkg} collectstatic --no-input --clear
209
+
echo ${pkg.version} > "$versionFile"
213
+
${pkg.python.pkgs.uvicorn}/bin/uvicorn --uds /run/froide-govplan/froide-govplan.socket \
214
+
--app-dir ${pkg}/${pkg.python.sitePackages}/froide_govplan \
215
+
project.asgi:application
222
+
systemd.tmpfiles.rules = [ "d /run/froide-govplan - govplan govplan - -" ];
224
+
environment.systemPackages = [ froide-govplan ];
226
+
users.users.govplan = {
227
+
home = "${cfg.dataDir}";
228
+
isSystemUser = true;
231
+
users.groups.govplan = { };
235
+
meta.maintainers = with lib.maintainers; [ onny ];