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