tangled
alpha
login
or
join now
pyrox.dev
/
nixpkgs
0
fork
atom
lol
0
fork
atom
overview
issues
pulls
pipelines
nixos: init module: restya-board
Thomas Strobel
8 years ago
67ce1357
2128d8ce
+385
2 changed files
expand all
collapse all
unified
split
nixos
modules
module-list.nix
services
web-apps
restya-board.nix
+1
nixos/modules/module-list.nix
···
608
608
./services/web-apps/pgpkeyserver-lite.nix
609
609
./services/web-apps/piwik.nix
610
610
./services/web-apps/pump.io.nix
611
611
+
./services/web-apps/restya-board.nix
611
612
./services/web-apps/tt-rss.nix
612
613
./services/web-apps/selfoss.nix
613
614
./services/web-apps/quassel-webserver.nix
+384
nixos/modules/services/web-apps/restya-board.nix
···
1
1
+
{ config, lib, pkgs, ... }:
2
2
+
3
3
+
with lib;
4
4
+
5
5
+
# TODO: are these php-packages needed?
6
6
+
#imagick
7
7
+
#php-geoip -> php.ini: extension = geoip.so
8
8
+
#expat
9
9
+
10
10
+
let
11
11
+
cfg = config.services.restya-board;
12
12
+
13
13
+
runDir = "/run/restya-board";
14
14
+
15
15
+
poolName = "restya-board";
16
16
+
phpfpmSocketName = "/var/run/phpfpm/${poolName}.sock";
17
17
+
18
18
+
in
19
19
+
20
20
+
{
21
21
+
22
22
+
###### interface
23
23
+
24
24
+
options = {
25
25
+
26
26
+
services.restya-board = {
27
27
+
28
28
+
enable = mkEnableOption "restya-board";
29
29
+
30
30
+
dataDir = mkOption {
31
31
+
type = types.path;
32
32
+
default = "/var/lib/restya-board";
33
33
+
example = "/var/lib/restya-board";
34
34
+
description = ''
35
35
+
Data of the application.
36
36
+
'';
37
37
+
};
38
38
+
39
39
+
user = mkOption {
40
40
+
type = types.str;
41
41
+
default = "restya-board";
42
42
+
example = "restya-board";
43
43
+
description = ''
44
44
+
User account under which the web-application runs.
45
45
+
'';
46
46
+
};
47
47
+
48
48
+
group = mkOption {
49
49
+
type = types.str;
50
50
+
default = "nginx";
51
51
+
example = "nginx";
52
52
+
description = ''
53
53
+
Group account under which the web-application runs.
54
54
+
'';
55
55
+
};
56
56
+
57
57
+
virtualHost = {
58
58
+
serverName = mkOption {
59
59
+
type = types.str;
60
60
+
default = "restya.board";
61
61
+
description = ''
62
62
+
Name of the nginx virtualhost to use.
63
63
+
'';
64
64
+
};
65
65
+
66
66
+
listenHost = mkOption {
67
67
+
type = types.str;
68
68
+
default = "localhost";
69
69
+
description = ''
70
70
+
Listen address for the virtualhost to use.
71
71
+
'';
72
72
+
};
73
73
+
74
74
+
listenPort = mkOption {
75
75
+
type = types.int;
76
76
+
default = 3000;
77
77
+
description = ''
78
78
+
Listen port for the virtualhost to use.
79
79
+
'';
80
80
+
};
81
81
+
};
82
82
+
83
83
+
database = {
84
84
+
host = mkOption {
85
85
+
type = types.nullOr types.str;
86
86
+
default = null;
87
87
+
description = ''
88
88
+
Host of the database. Leave 'null' to use a local PostgreSQL database.
89
89
+
A local PostgreSQL database is initialized automatically.
90
90
+
'';
91
91
+
};
92
92
+
93
93
+
port = mkOption {
94
94
+
type = types.nullOr types.int;
95
95
+
default = 5432;
96
96
+
description = ''
97
97
+
The database's port.
98
98
+
'';
99
99
+
};
100
100
+
101
101
+
name = mkOption {
102
102
+
type = types.str;
103
103
+
default = "restya_board";
104
104
+
description = ''
105
105
+
Name of the database. The database must exist.
106
106
+
'';
107
107
+
};
108
108
+
109
109
+
user = mkOption {
110
110
+
type = types.str;
111
111
+
default = "restya_board";
112
112
+
description = ''
113
113
+
The database user. The user must exist and have access to
114
114
+
the specified database.
115
115
+
'';
116
116
+
};
117
117
+
118
118
+
passwordFile = mkOption {
119
119
+
type = types.nullOr types.str;
120
120
+
default = null;
121
121
+
description = ''
122
122
+
The database user's password. 'null' if no password is set.
123
123
+
'';
124
124
+
};
125
125
+
};
126
126
+
127
127
+
email = {
128
128
+
server = mkOption {
129
129
+
type = types.nullOr types.str;
130
130
+
default = null;
131
131
+
example = "localhost";
132
132
+
description = ''
133
133
+
Hostname to send outgoing mail. Null to use the system MTA.
134
134
+
'';
135
135
+
};
136
136
+
137
137
+
port = mkOption {
138
138
+
type = types.int;
139
139
+
default = 25;
140
140
+
description = ''
141
141
+
Port used to connect to SMTP server.
142
142
+
'';
143
143
+
};
144
144
+
145
145
+
login = mkOption {
146
146
+
type = types.str;
147
147
+
default = "";
148
148
+
description = ''
149
149
+
SMTP authentication login used when sending outgoing mail.
150
150
+
'';
151
151
+
};
152
152
+
153
153
+
password = mkOption {
154
154
+
type = types.str;
155
155
+
default = "";
156
156
+
description = ''
157
157
+
SMTP authentication password used when sending outgoing mail.
158
158
+
159
159
+
ATTENTION: The password is stored world-readable in the nix-store!
160
160
+
'';
161
161
+
};
162
162
+
};
163
163
+
164
164
+
timezone = mkOption {
165
165
+
type = types.lines;
166
166
+
default = "GMT";
167
167
+
description = ''
168
168
+
Timezone the web-app runs in.
169
169
+
'';
170
170
+
};
171
171
+
172
172
+
};
173
173
+
174
174
+
};
175
175
+
176
176
+
177
177
+
###### implementation
178
178
+
179
179
+
config = mkIf cfg.enable {
180
180
+
181
181
+
services.phpfpm.poolConfigs = {
182
182
+
"${poolName}" = ''
183
183
+
listen = "${phpfpmSocketName}";
184
184
+
listen.owner = nginx
185
185
+
listen.group = nginx
186
186
+
listen.mode = 0600
187
187
+
user = ${cfg.user}
188
188
+
group = ${cfg.group}
189
189
+
pm = dynamic
190
190
+
pm.max_children = 75
191
191
+
pm.start_servers = 10
192
192
+
pm.min_spare_servers = 5
193
193
+
pm.max_spare_servers = 20
194
194
+
pm.max_requests = 500
195
195
+
catch_workers_output = 1
196
196
+
'';
197
197
+
};
198
198
+
199
199
+
services.phpfpm.phpOptions = ''
200
200
+
date.timezone = "CET"
201
201
+
202
202
+
${optionalString (!isNull cfg.email.server) ''
203
203
+
SMTP = ${cfg.email.server}
204
204
+
smtp_port = ${toString cfg.email.port}
205
205
+
auth_username = ${cfg.email.login}
206
206
+
auth_password = ${cfg.email.password}
207
207
+
''}
208
208
+
'';
209
209
+
210
210
+
services.nginx.enable = true;
211
211
+
services.nginx.virtualHosts."${cfg.virtualHost.serverName}" = {
212
212
+
listen = [ { addr = cfg.virtualHost.listenHost; port = cfg.virtualHost.listenPort; } ];
213
213
+
serverName = cfg.virtualHost.serverName;
214
214
+
root = runDir;
215
215
+
extraConfig = ''
216
216
+
index index.html index.php;
217
217
+
218
218
+
gzip on;
219
219
+
gzip_disable "msie6";
220
220
+
221
221
+
gzip_comp_level 6;
222
222
+
gzip_min_length 1100;
223
223
+
gzip_buffers 16 8k;
224
224
+
gzip_proxied any;
225
225
+
gzip_types text/plain application/xml text/css text/js text/xml application/x-javascript text/javascript application/json application/xml+rss;
226
226
+
227
227
+
client_max_body_size 300M;
228
228
+
229
229
+
rewrite ^/oauth/authorize$ /server/php/authorize.php last;
230
230
+
rewrite ^/oauth_callback/([a-zA-Z0-9_\.]*)/([a-zA-Z0-9_\.]*)$ /server/php/oauth_callback.php?plugin=$1&code=$2 last;
231
231
+
rewrite ^/download/([0-9]*)/([a-zA-Z0-9_\.]*)$ /server/php/download.php?id=$1&hash=$2 last;
232
232
+
rewrite ^/ical/([0-9]*)/([0-9]*)/([a-z0-9]*).ics$ /server/php/ical.php?board_id=$1&user_id=$2&hash=$3 last;
233
233
+
rewrite ^/api/(.*)$ /server/php/R/r.php?_url=$1&$args last;
234
234
+
rewrite ^/api_explorer/api-docs/$ /client/api_explorer/api-docs/index.php last;
235
235
+
'';
236
236
+
237
237
+
locations."/".root = "${runDir}/client";
238
238
+
239
239
+
locations."~ \.php$" = {
240
240
+
tryFiles = "$uri =404";
241
241
+
extraConfig = ''
242
242
+
include ${pkgs.nginx}/conf/fastcgi_params;
243
243
+
fastcgi_pass unix:${phpfpmSocketName};
244
244
+
fastcgi_index index.php;
245
245
+
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
246
246
+
fastcgi_param PHP_VALUE "upload_max_filesize=9G \n post_max_size=9G \n max_execution_time=200 \n max_input_time=200 \n memory_limit=256M";
247
247
+
'';
248
248
+
};
249
249
+
250
250
+
locations."~* \.(css|js|less|html|ttf|woff|jpg|jpeg|gif|png|bmp|ico)" = {
251
251
+
root = "${runDir}/client";
252
252
+
extraConfig = ''
253
253
+
if (-f $request_filename) {
254
254
+
break;
255
255
+
}
256
256
+
rewrite ^/img/([a-zA-Z_]*)/([a-zA-Z_]*)/([a-zA-Z0-9_\.]*)$ /server/php/image.php?size=$1&model=$2&filename=$3 last;
257
257
+
add_header Cache-Control public;
258
258
+
add_header Cache-Control must-revalidate;
259
259
+
expires 7d;
260
260
+
'';
261
261
+
};
262
262
+
};
263
263
+
264
264
+
systemd.services.restya-board-init = {
265
265
+
description = "Restya board initialization";
266
266
+
serviceConfig.Type = "oneshot";
267
267
+
serviceConfig.RemainAfterExit = true;
268
268
+
269
269
+
wantedBy = [ "multi-user.target" ];
270
270
+
requires = [ "postgresql.service" ];
271
271
+
after = [ "network.target" "postgresql.service" ];
272
272
+
273
273
+
script = ''
274
274
+
rm -rf "${runDir}"
275
275
+
mkdir -m 750 -p "${runDir}"
276
276
+
cp -r "${pkgs.restya-board}/"* "${runDir}"
277
277
+
sed -i "s/@restya.com/@${cfg.virtualHost.serverName}/g" "${runDir}/sql/restyaboard_with_empty_data.sql"
278
278
+
rm -rf "${runDir}/media"
279
279
+
rm -rf "${runDir}/client/img"
280
280
+
chmod -R 0750 "${runDir}"
281
281
+
282
282
+
sed -i "s@^php@${config.services.phpfpm.phpPackage}/bin/php@" "${runDir}/server/php/shell/"*.sh
283
283
+
284
284
+
${if (isNull cfg.database.host) then ''
285
285
+
sed -i "s/^.*'R_DB_HOST'.*$/define('R_DB_HOST', 'localhost');/g" "${runDir}/server/php/config.inc.php"
286
286
+
sed -i "s/^.*'R_DB_PASSWORD'.*$/define('R_DB_PASSWORD', 'restya');/g" "${runDir}/server/php/config.inc.php"
287
287
+
'' else ''
288
288
+
sed -i "s/^.*'R_DB_HOST'.*$/define('R_DB_HOST', '${cfg.database.host}');/g" "${runDir}/server/php/config.inc.php"
289
289
+
sed -i "s/^.*'R_DB_PASSWORD'.*$/define('R_DB_PASSWORD', '$(<${cfg.database.dbPassFile})');/g" "${runDir}/server/php/config.inc.php"
290
290
+
''}
291
291
+
sed -i "s/^.*'R_DB_PORT'.*$/define('R_DB_PORT', '${toString cfg.database.port}');/g" "${runDir}/server/php/config.inc.php"
292
292
+
sed -i "s/^.*'R_DB_NAME'.*$/define('R_DB_NAME', '${cfg.database.name}');/g" "${runDir}/server/php/config.inc.php"
293
293
+
sed -i "s/^.*'R_DB_USER'.*$/define('R_DB_USER', '${cfg.database.user}');/g" "${runDir}/server/php/config.inc.php"
294
294
+
295
295
+
chmod 0400 "${runDir}/server/php/config.inc.php"
296
296
+
297
297
+
ln -sf "${cfg.dataDir}/media" "${runDir}/media"
298
298
+
ln -sf "${cfg.dataDir}/client/img" "${runDir}/client/img"
299
299
+
300
300
+
chmod g+w "${runDir}/tmp/cache"
301
301
+
chown -R "${cfg.user}"."${cfg.group}" "${runDir}"
302
302
+
303
303
+
304
304
+
mkdir -m 0750 -p "${cfg.dataDir}"
305
305
+
mkdir -m 0750 -p "${cfg.dataDir}/media"
306
306
+
mkdir -m 0750 -p "${cfg.dataDir}/client/img"
307
307
+
cp -r "${pkgs.restya-board}/media/"* "${cfg.dataDir}/media"
308
308
+
cp -r "${pkgs.restya-board}/client/img/"* "${cfg.dataDir}/client/img"
309
309
+
chown "${cfg.user}"."${cfg.group}" "${cfg.dataDir}"
310
310
+
chown -R "${cfg.user}"."${cfg.group}" "${cfg.dataDir}/media"
311
311
+
chown -R "${cfg.user}"."${cfg.group}" "${cfg.dataDir}/client/img"
312
312
+
313
313
+
${optionalString (isNull cfg.database.host) ''
314
314
+
if ! [ -e "${cfg.dataDir}/.db-initialized" ]; then
315
315
+
${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} \
316
316
+
${config.services.postgresql.package}/bin/psql -U ${config.services.postgresql.superUser} \
317
317
+
-c "CREATE USER ${cfg.database.user} WITH ENCRYPTED PASSWORD 'restya'"
318
318
+
319
319
+
${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} \
320
320
+
${config.services.postgresql.package}/bin/psql -U ${config.services.postgresql.superUser} \
321
321
+
-c "CREATE DATABASE ${cfg.database.name} OWNER ${cfg.database.user} ENCODING 'UTF8' TEMPLATE template0"
322
322
+
323
323
+
${pkgs.sudo}/bin/sudo -u ${cfg.user} \
324
324
+
${config.services.postgresql.package}/bin/psql -U ${cfg.database.user} \
325
325
+
-d ${cfg.database.name} -f "${runDir}/sql/restyaboard_with_empty_data.sql"
326
326
+
327
327
+
touch "${cfg.dataDir}/.db-initialized"
328
328
+
fi
329
329
+
''}
330
330
+
'';
331
331
+
};
332
332
+
333
333
+
systemd.timers.restya-board = {
334
334
+
description = "restya-board scripts for e.g. email notification";
335
335
+
wantedBy = [ "timers.target" ];
336
336
+
after = [ "restya-board-init.service" ];
337
337
+
requires = [ "restya-board-init.service" ];
338
338
+
timerConfig = {
339
339
+
OnUnitInactiveSec = "60s";
340
340
+
Unit = "restya-board-timers.service";
341
341
+
};
342
342
+
};
343
343
+
344
344
+
systemd.services.restya-board-timers = {
345
345
+
description = "restya-board scripts for e.g. email notification";
346
346
+
serviceConfig.Type = "oneshot";
347
347
+
serviceConfig.User = cfg.user;
348
348
+
349
349
+
after = [ "restya-board-init.service" ];
350
350
+
requires = [ "restya-board-init.service" ];
351
351
+
352
352
+
script = ''
353
353
+
/bin/sh ${runDir}/server/php/shell/instant_email_notification.sh 2> /dev/null || true
354
354
+
/bin/sh ${runDir}/server/php/shell/periodic_email_notification.sh 2> /dev/null || true
355
355
+
/bin/sh ${runDir}/server/php/shell/imap.sh 2> /dev/null || true
356
356
+
/bin/sh ${runDir}/server/php/shell/webhook.sh 2> /dev/null || true
357
357
+
/bin/sh ${runDir}/server/php/shell/card_due_notification.sh 2> /dev/null || true
358
358
+
'';
359
359
+
};
360
360
+
361
361
+
users.extraUsers.restya-board = {
362
362
+
isSystemUser = true;
363
363
+
createHome = false;
364
364
+
home = runDir;
365
365
+
group = "restya-board";
366
366
+
};
367
367
+
users.extraGroups.restya-board = {};
368
368
+
369
369
+
services.postgresql.enable = mkIf (isNull cfg.database.host) true;
370
370
+
371
371
+
services.postgresql.identMap = optionalString (isNull cfg.database.host)
372
372
+
''
373
373
+
restya-board-users restya-board restya_board
374
374
+
'';
375
375
+
376
376
+
services.postgresql.authentication = optionalString (isNull cfg.database.host)
377
377
+
''
378
378
+
local restya_board all ident map=restya-board-users
379
379
+
'';
380
380
+
381
381
+
};
382
382
+
383
383
+
}
384
384
+