1{ config, lib, pkgs, utils, ... }:
2
3let
4 inherit (lib) mkDefault mkEnableOption mkIf mkOption types mkPackageOption;
5 cfg = config.services.engelsystem;
6in {
7 options = {
8 services.engelsystem = {
9 enable = mkOption {
10 default = false;
11 example = true;
12 description = ''
13 Whether to enable engelsystem, an online tool for coordinating volunteers
14 and shifts on large events.
15 '';
16 type = lib.types.bool;
17 };
18
19 domain = mkOption {
20 type = types.str;
21 example = "engelsystem.example.com";
22 description = "Domain to serve on.";
23 };
24
25 package = mkPackageOption pkgs "engelsystem" { };
26
27 createDatabase = mkOption {
28 type = types.bool;
29 default = true;
30 description = ''
31 Whether to create a local database automatically.
32 This will override every database setting in {option}`services.engelsystem.config`.
33 '';
34 };
35 };
36
37 services.engelsystem.config = mkOption {
38 type = types.attrs;
39 default = {
40 database = {
41 host = "localhost";
42 database = "engelsystem";
43 username = "engelsystem";
44 };
45 };
46 example = {
47 maintenance = false;
48 database = {
49 host = "database.example.com";
50 database = "engelsystem";
51 username = "engelsystem";
52 password._secret = "/var/keys/engelsystem/database";
53 };
54 email = {
55 driver = "smtp";
56 host = "smtp.example.com";
57 port = 587;
58 from.address = "engelsystem@example.com";
59 from.name = "example engelsystem";
60 encryption = "tls";
61 username = "engelsystem@example.com";
62 password._secret = "/var/keys/engelsystem/mail";
63 };
64 autoarrive = true;
65 min_password_length = 6;
66 default_locale = "de_DE";
67 };
68 description = ''
69 Options to be added to config.php, as a nix attribute set. Options containing secret data
70 should be set to an attribute set containing the attribute _secret - a string pointing to a
71 file containing the value the option should be set to. See the example to get a better
72 picture of this: in the resulting config.php file, the email.password key will be set to
73 the contents of the /var/keys/engelsystem/mail file.
74
75 See https://engelsystem.de/doc/admin/configuration/ for available options.
76
77 Note that the admin user login credentials cannot be set here - they always default to
78 admin:asdfasdf. Log in and change them immediately.
79 '';
80 };
81 };
82
83 config = mkIf cfg.enable {
84 # create database
85 services.mysql = mkIf cfg.createDatabase {
86 enable = true;
87 package = mkDefault pkgs.mariadb;
88 ensureUsers = [{
89 name = "engelsystem";
90 ensurePermissions = { "engelsystem.*" = "ALL PRIVILEGES"; };
91 }];
92 ensureDatabases = [ "engelsystem" ];
93 };
94
95 environment.etc."engelsystem/config.php".source =
96 pkgs.writeText "config.php" ''
97 <?php
98 return json_decode(file_get_contents("/var/lib/engelsystem/config.json"), true);
99 '';
100
101 services.phpfpm.pools.engelsystem = {
102 user = "engelsystem";
103 settings = {
104 "listen.owner" = config.services.nginx.user;
105 "pm" = "dynamic";
106 "pm.max_children" = 32;
107 "pm.max_requests" = 500;
108 "pm.start_servers" = 2;
109 "pm.min_spare_servers" = 2;
110 "pm.max_spare_servers" = 5;
111 "php_admin_value[error_log]" = "stderr";
112 "php_admin_flag[log_errors]" = true;
113 "catch_workers_output" = true;
114 };
115 };
116
117 services.nginx = {
118 enable = true;
119 virtualHosts."${cfg.domain}".locations = {
120 "/" = {
121 root = "${cfg.package}/share/engelsystem/public";
122 extraConfig = ''
123 index index.php;
124 try_files $uri $uri/ /index.php?$args;
125 autoindex off;
126 '';
127 };
128 "~ \\.php$" = {
129 root = "${cfg.package}/share/engelsystem/public";
130 extraConfig = ''
131 fastcgi_pass unix:${config.services.phpfpm.pools.engelsystem.socket};
132 fastcgi_index index.php;
133 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
134 include ${config.services.nginx.package}/conf/fastcgi_params;
135 include ${config.services.nginx.package}/conf/fastcgi.conf;
136 '';
137 };
138 };
139 };
140
141 systemd.services."engelsystem-init" = {
142 wantedBy = [ "multi-user.target" ];
143 serviceConfig = { Type = "oneshot"; };
144 script =
145 let
146 genConfigScript = pkgs.writeScript "engelsystem-gen-config.sh"
147 (utils.genJqSecretsReplacementSnippet cfg.config "config.json");
148 in ''
149 umask 077
150 mkdir -p /var/lib/engelsystem/storage/app
151 mkdir -p /var/lib/engelsystem/storage/cache/views
152 cd /var/lib/engelsystem
153 ${genConfigScript}
154 chmod 400 config.json
155 chown -R engelsystem .
156 '';
157 };
158 systemd.services."engelsystem-migrate" = {
159 wantedBy = [ "multi-user.target" ];
160 serviceConfig = {
161 Type = "oneshot";
162 User = "engelsystem";
163 Group = "engelsystem";
164 };
165 script = ''
166 ${cfg.package}/bin/migrate
167 '';
168 after = [ "engelsystem-init.service" "mysql.service" ];
169 };
170 systemd.services."phpfpm-engelsystem".after =
171 [ "engelsystem-migrate.service" ];
172
173 users.users.engelsystem = {
174 isSystemUser = true;
175 createHome = true;
176 home = "/var/lib/engelsystem/storage";
177 group = "engelsystem";
178 };
179 users.groups.engelsystem = { };
180 };
181}