summaryrefslogtreecommitdiff
path: root/modules/webapps
diff options
context:
space:
mode:
Diffstat (limited to 'modules/webapps')
-rw-r--r--modules/webapps/diaspora.nix171
-rw-r--r--modules/webapps/etherpad-lite.nix158
-rw-r--r--modules/webapps/mastodon.nix223
-rw-r--r--modules/webapps/mediagoblin.nix237
-rw-r--r--modules/webapps/peertube.nix105
-rw-r--r--modules/webapps/webstats/default.nix81
-rw-r--r--modules/webapps/webstats/goaccess.conf99
7 files changed, 1074 insertions, 0 deletions
diff --git a/modules/webapps/diaspora.nix b/modules/webapps/diaspora.nix
new file mode 100644
index 00000000..65599b73
--- /dev/null
+++ b/modules/webapps/diaspora.nix
@@ -0,0 +1,171 @@
1{ lib, pkgs, config, ... }:
2let
3 name = "diaspora";
4 cfg = config.services.diaspora;
5
6 uid = config.ids.uids.diaspora;
7 gid = config.ids.gids.diaspora;
8in
9{
10 options.services.diaspora = {
11 enable = lib.mkEnableOption "Enable Diaspora’s service";
12 user = lib.mkOption {
13 type = lib.types.str;
14 default = name;
15 description = "User account under which Diaspora runs";
16 };
17 group = lib.mkOption {
18 type = lib.types.str;
19 default = name;
20 description = "Group under which Diaspora runs";
21 };
22 adminEmail = lib.mkOption {
23 type = lib.types.str;
24 example = "admin@example.com";
25 description = "Admin e-mail for Diaspora";
26 };
27 dataDir = lib.mkOption {
28 type = lib.types.path;
29 default = "/var/lib/${name}";
30 description = ''
31 The directory where Diaspora stores its data.
32 '';
33 };
34 socketsDir = lib.mkOption {
35 type = lib.types.path;
36 default = "/run/${name}";
37 description = ''
38 The directory where Diaspora puts runtime files and sockets.
39 '';
40 };
41 configDir = lib.mkOption {
42 type = lib.types.path;
43 description = ''
44 The configuration path for Diaspora.
45 '';
46 };
47 package = lib.mkOption {
48 type = lib.types.package;
49 default = pkgs.webapps.diaspora;
50 description = ''
51 Diaspora package to use.
52 '';
53 };
54 # Output variables
55 systemdStateDirectory = lib.mkOption {
56 type = lib.types.str;
57 # Use ReadWritePaths= instead if varDir is outside of /var/lib
58 default = assert lib.strings.hasPrefix "/var/lib/" cfg.dataDir;
59 lib.strings.removePrefix "/var/lib/" cfg.dataDir;
60 description = ''
61 Adjusted Diaspora data directory for systemd
62 '';
63 readOnly = true;
64 };
65 systemdRuntimeDirectory = lib.mkOption {
66 type = lib.types.str;
67 # Use ReadWritePaths= instead if socketsDir is outside of /run
68 default = assert lib.strings.hasPrefix "/run/" cfg.socketsDir;
69 lib.strings.removePrefix "/run/" cfg.socketsDir;
70 description = ''
71 Adjusted Diaspora sockets directory for systemd
72 '';
73 readOnly = true;
74 };
75 workdir = lib.mkOption {
76 type = lib.types.package;
77 default = cfg.package.override {
78 varDir = cfg.dataDir;
79 podmin_email = cfg.adminEmail;
80 config_dir = cfg.configDir;
81 };
82 description = ''
83 Adjusted diaspora package with overriden values
84 '';
85 readOnly = true;
86 };
87 sockets = lib.mkOption {
88 type = lib.types.attrsOf lib.types.path;
89 default = {
90 rails = "${cfg.socketsDir}/diaspora.sock";
91 eye = "${cfg.socketsDir}/eye.sock";
92 };
93 readOnly = true;
94 description = ''
95 Diaspora sockets
96 '';
97 };
98 pids = lib.mkOption {
99 type = lib.types.attrsOf lib.types.path;
100 default = {
101 eye = "${cfg.socketsDir}/eye.pid";
102 };
103 readOnly = true;
104 description = ''
105 Diaspora pids
106 '';
107 };
108 };
109
110 config = lib.mkIf cfg.enable {
111 users.users = lib.optionalAttrs (cfg.user == name) (lib.singleton {
112 inherit name;
113 inherit uid;
114 group = cfg.group;
115 description = "Diaspora user";
116 home = cfg.dataDir;
117 packages = [ cfg.workdir.gems pkgs.nodejs cfg.workdir.gems.ruby ];
118 useDefaultShell = true;
119 });
120 users.groups = lib.optionalAttrs (cfg.group == name) (lib.singleton {
121 inherit name;
122 inherit gid;
123 });
124
125 systemd.services.diaspora = {
126 description = "Diaspora";
127 wantedBy = [ "multi-user.target" ];
128 after = [
129 "network.target" "redis.service" "postgresql.service"
130 ];
131 wants = [
132 "redis.service" "postgresql.service"
133 ];
134
135 environment.RAILS_ENV = "production";
136 environment.BUNDLE_PATH = "${cfg.workdir.gems}/${cfg.workdir.gems.ruby.gemPath}";
137 environment.BUNDLE_GEMFILE = "${cfg.workdir.gems.confFiles}/Gemfile";
138 environment.EYE_SOCK = cfg.sockets.eye;
139 environment.EYE_PID = cfg.pids.eye;
140
141 path = [ cfg.workdir.gems pkgs.nodejs cfg.workdir.gems.ruby pkgs.curl pkgs.which pkgs.gawk ];
142
143 preStart = ''
144 install -m 0755 -d ${cfg.dataDir}/uploads ${cfg.dataDir}/tmp ${cfg.dataDir}/log
145 install -m 0700 -d ${cfg.dataDir}/tmp/pids
146 if [ ! -f ${cfg.dataDir}/schedule.yml ]; then
147 echo "{}" > ${cfg.dataDir}/schedule.yml
148 fi
149 ./bin/bundle exec rails db:migrate
150 '';
151
152 script = ''
153 exec ${cfg.workdir}/script/server
154 '';
155
156 serviceConfig = {
157 User = cfg.user;
158 PrivateTmp = true;
159 Restart = "always";
160 Type = "simple";
161 WorkingDirectory = cfg.workdir;
162 StateDirectory = cfg.systemdStateDirectory;
163 RuntimeDirectory = cfg.systemdRuntimeDirectory;
164 StandardInput = "null";
165 KillMode = "control-group";
166 };
167
168 unitConfig.RequiresMountsFor = cfg.dataDir;
169 };
170 };
171}
diff --git a/modules/webapps/etherpad-lite.nix b/modules/webapps/etherpad-lite.nix
new file mode 100644
index 00000000..7f0e2ed4
--- /dev/null
+++ b/modules/webapps/etherpad-lite.nix
@@ -0,0 +1,158 @@
1{ lib, pkgs, config, ... }:
2let
3 name = "etherpad-lite";
4 cfg = config.services.etherpad-lite;
5
6 uid = config.ids.uids.etherpad-lite;
7 gid = config.ids.gids.etherpad-lite;
8in
9{
10 options.services.etherpad-lite = {
11 enable = lib.mkEnableOption "Enable Etherpad lite’s service";
12 user = lib.mkOption {
13 type = lib.types.str;
14 default = name;
15 description = "User account under which Etherpad lite runs";
16 };
17 group = lib.mkOption {
18 type = lib.types.str;
19 default = name;
20 description = "Group under which Etherpad lite runs";
21 };
22 dataDir = lib.mkOption {
23 type = lib.types.path;
24 default = "/var/lib/${name}";
25 description = ''
26 The directory where Etherpad lite stores its data.
27 '';
28 };
29 socketsDir = lib.mkOption {
30 type = lib.types.path;
31 default = "/run/${name}";
32 description = ''
33 The directory where Etherpad lite stores its sockets.
34 '';
35 };
36 configFile = lib.mkOption {
37 type = lib.types.path;
38 description = ''
39 The config file path for Etherpad lite.
40 '';
41 };
42 sessionKeyFile = lib.mkOption {
43 type = lib.types.path;
44 description = ''
45 The Session key file path for Etherpad lite.
46 '';
47 };
48 apiKeyFile = lib.mkOption {
49 type = lib.types.path;
50 description = ''
51 The API key file path for Etherpad lite.
52 '';
53 };
54 package = lib.mkOption {
55 type = lib.types.package;
56 default = pkgs.webapps.etherpad-lite;
57 description = ''
58 Etherpad lite package to use.
59 '';
60 };
61 modules = lib.mkOption {
62 type = lib.types.listOf lib.types.package;
63 default = [];
64 description = ''
65 Etherpad lite modules to use.
66 '';
67 };
68 # Output variables
69 workdir = lib.mkOption {
70 type = lib.types.package;
71 default = cfg.package.withModules cfg.modules;
72 description = ''
73 Adjusted Etherpad lite package with plugins
74 '';
75 readOnly = true;
76 };
77 systemdStateDirectory = lib.mkOption {
78 type = lib.types.str;
79 # Use ReadWritePaths= instead if varDir is outside of /var/lib
80 default = assert lib.strings.hasPrefix "/var/lib/" cfg.dataDir;
81 lib.strings.removePrefix "/var/lib/" cfg.dataDir;
82 description = ''
83 Adjusted Etherpad lite data directory for systemd
84 '';
85 readOnly = true;
86 };
87 systemdRuntimeDirectory = lib.mkOption {
88 type = lib.types.str;
89 # Use ReadWritePaths= instead if socketsDir is outside of /run
90 default = assert lib.strings.hasPrefix "/run/" cfg.socketsDir;
91 lib.strings.removePrefix "/run/" cfg.socketsDir;
92 description = ''
93 Adjusted Etherpad lite sockets directory for systemd
94 '';
95 readOnly = true;
96 };
97 sockets = lib.mkOption {
98 type = lib.types.attrsOf lib.types.path;
99 default = {
100 node = "${cfg.socketsDir}/etherpad-lite.sock";
101 };
102 readOnly = true;
103 description = ''
104 Etherpad lite sockets
105 '';
106 };
107 };
108
109 config = lib.mkIf cfg.enable {
110 systemd.services.etherpad-lite = {
111 description = "Etherpad-lite";
112 wantedBy = [ "multi-user.target" ];
113 after = [ "network.target" "postgresql.service" ];
114 wants = [ "postgresql.service" ];
115
116 environment.NODE_ENV = "production";
117 environment.HOME = cfg.workdir;
118
119 path = [ pkgs.nodejs ];
120
121 script = ''
122 exec ${pkgs.nodejs}/bin/node ${cfg.workdir}/src/node/server.js \
123 --sessionkey ${cfg.sessionKeyFile} \
124 --apikey ${cfg.apiKeyFile} \
125 --settings ${cfg.configFile}
126 '';
127
128 postStart = ''
129 while [ ! -S ${cfg.sockets.node} ]; do
130 sleep 0.5
131 done
132 chmod a+w ${cfg.sockets.node}
133 '';
134 serviceConfig = {
135 DynamicUser = true;
136 User = cfg.user;
137 Group = cfg.group;
138 WorkingDirectory = cfg.workdir;
139 PrivateTmp = true;
140 NoNewPrivileges = true;
141 PrivateDevices = true;
142 ProtectHome = true;
143 ProtectControlGroups = true;
144 ProtectKernelModules = true;
145 Restart = "always";
146 Type = "simple";
147 TimeoutSec = 60;
148 RuntimeDirectory = cfg.systemdRuntimeDirectory;
149 StateDirectory= cfg.systemdStateDirectory;
150 ExecStartPre = [
151 "+${pkgs.coreutils}/bin/install -d -m 0755 -o ${cfg.user} -g ${cfg.group} ${cfg.dataDir}/ep_initialized"
152 "+${pkgs.coreutils}/bin/chown -R ${cfg.user}:${cfg.group} ${cfg.dataDir} ${cfg.configFile} ${cfg.sessionKeyFile} ${cfg.apiKeyFile}"
153 ];
154 };
155 };
156
157 };
158}
diff --git a/modules/webapps/mastodon.nix b/modules/webapps/mastodon.nix
new file mode 100644
index 00000000..6255de91
--- /dev/null
+++ b/modules/webapps/mastodon.nix
@@ -0,0 +1,223 @@
1{ lib, pkgs, config, ... }:
2let
3 name = "mastodon";
4 cfg = config.services.mastodon;
5
6 uid = config.ids.uids.mastodon;
7 gid = config.ids.gids.mastodon;
8in
9{
10 options.services.mastodon = {
11 enable = lib.mkEnableOption "Enable Mastodon’s service";
12 user = lib.mkOption {
13 type = lib.types.str;
14 default = name;
15 description = "User account under which Mastodon runs";
16 };
17 group = lib.mkOption {
18 type = lib.types.str;
19 default = name;
20 description = "Group under which Mastodon runs";
21 };
22 dataDir = lib.mkOption {
23 type = lib.types.path;
24 default = "/var/lib/${name}";
25 description = ''
26 The directory where Mastodon stores its data.
27 '';
28 };
29 socketsPrefix = lib.mkOption {
30 type = lib.types.string;
31 default = "live";
32 description = ''
33 The prefix to use for Mastodon sockets.
34 '';
35 };
36 socketsDir = lib.mkOption {
37 type = lib.types.path;
38 default = "/run/${name}";
39 description = ''
40 The directory where Mastodon puts runtime files and sockets.
41 '';
42 };
43 configFile = lib.mkOption {
44 type = lib.types.path;
45 description = ''
46 The configuration file path for Mastodon.
47 '';
48 };
49 package = lib.mkOption {
50 type = lib.types.package;
51 default = pkgs.webapps.mastodon;
52 description = ''
53 Mastodon package to use.
54 '';
55 };
56 # Output variables
57 workdir = lib.mkOption {
58 type = lib.types.package;
59 default = cfg.package.override { varDir = cfg.dataDir; };
60 description = ''
61 Adjusted mastodon package with overriden varDir
62 '';
63 readOnly = true;
64 };
65 systemdStateDirectory = lib.mkOption {
66 type = lib.types.str;
67 # Use ReadWritePaths= instead if varDir is outside of /var/lib
68 default = assert lib.strings.hasPrefix "/var/lib/" cfg.dataDir;
69 lib.strings.removePrefix "/var/lib/" cfg.dataDir;
70 description = ''
71 Adjusted Mastodon data directory for systemd
72 '';
73 readOnly = true;
74 };
75 systemdRuntimeDirectory = lib.mkOption {
76 type = lib.types.str;
77 # Use ReadWritePaths= instead if socketsDir is outside of /run
78 default = assert lib.strings.hasPrefix "/run/" cfg.socketsDir;
79 lib.strings.removePrefix "/run/" cfg.socketsDir;
80 description = ''
81 Adjusted Mastodon sockets directory for systemd
82 '';
83 readOnly = true;
84 };
85 sockets = lib.mkOption {
86 type = lib.types.attrsOf lib.types.path;
87 default = {
88 node = "${cfg.socketsDir}/${cfg.socketsPrefix}_node.sock";
89 rails = "${cfg.socketsDir}/${cfg.socketsPrefix}_puma.sock";
90 };
91 readOnly = true;
92 description = ''
93 Mastodon sockets
94 '';
95 };
96 };
97
98 config = lib.mkIf cfg.enable {
99 users.users = lib.optionalAttrs (cfg.user == name) (lib.singleton {
100 inherit name;
101 inherit uid;
102 group = cfg.group;
103 description = "Mastodon user";
104 home = cfg.dataDir;
105 useDefaultShell = true;
106 });
107 users.groups = lib.optionalAttrs (cfg.group == name) (lib.singleton {
108 inherit name;
109 inherit gid;
110 });
111
112 systemd.services.mastodon-streaming = {
113 description = "Mastodon Streaming";
114 wantedBy = [ "multi-user.target" ];
115 after = [ "network.target" "mastodon-web.service" ];
116
117 environment.NODE_ENV = "production";
118 environment.SOCKET = cfg.sockets.node;
119
120 path = [ pkgs.nodejs pkgs.bashInteractive ];
121
122 script = ''
123 exec npm run start
124 '';
125
126 postStart = ''
127 while [ ! -S $SOCKET ]; do
128 sleep 0.5
129 done
130 chmod a+w $SOCKET
131 '';
132
133 postStop = ''
134 rm $SOCKET
135 '';
136
137 serviceConfig = {
138 User = cfg.user;
139 EnvironmentFile = cfg.configFile;
140 PrivateTmp = true;
141 Restart = "always";
142 TimeoutSec = 15;
143 Type = "simple";
144 WorkingDirectory = cfg.workdir;
145 StateDirectory = cfg.systemdStateDirectory;
146 RuntimeDirectory = cfg.systemdRuntimeDirectory;
147 RuntimeDirectoryPreserve = "yes";
148 };
149
150 unitConfig.RequiresMountsFor = cfg.dataDir;
151 };
152
153 systemd.services.mastodon-web = {
154 description = "Mastodon Web app";
155 wantedBy = [ "multi-user.target" ];
156 after = [ "network.target" ];
157
158 environment.RAILS_ENV = "production";
159 environment.BUNDLE_PATH = "${cfg.workdir.gems}/${cfg.workdir.gems.ruby.gemPath}";
160 environment.BUNDLE_GEMFILE = "${cfg.workdir.gems.confFiles}/Gemfile";
161 environment.SOCKET = cfg.sockets.rails;
162
163 path = [ cfg.workdir.gems cfg.workdir.gems.ruby pkgs.file ];
164
165 preStart = ''
166 install -m 0755 -d ${cfg.dataDir}/tmp/cache
167 ./bin/bundle exec rails db:migrate
168 '';
169
170 script = ''
171 exec ./bin/bundle exec puma -C config/puma.rb
172 '';
173
174 serviceConfig = {
175 User = cfg.user;
176 EnvironmentFile = cfg.configFile;
177 PrivateTmp = true;
178 Restart = "always";
179 TimeoutSec = 60;
180 Type = "simple";
181 WorkingDirectory = cfg.workdir;
182 StateDirectory = cfg.systemdStateDirectory;
183 RuntimeDirectory = cfg.systemdRuntimeDirectory;
184 RuntimeDirectoryPreserve = "yes";
185 };
186
187 unitConfig.RequiresMountsFor = cfg.dataDir;
188 };
189
190 systemd.services.mastodon-sidekiq = {
191 description = "Mastodon Sidekiq";
192 wantedBy = [ "multi-user.target" ];
193 after = [ "network.target" "mastodon-web.service" ];
194
195 environment.RAILS_ENV="production";
196 environment.BUNDLE_PATH = "${cfg.workdir.gems}/${cfg.workdir.gems.ruby.gemPath}";
197 environment.BUNDLE_GEMFILE = "${cfg.workdir.gems.confFiles}/Gemfile";
198 environment.DB_POOL="5";
199
200 path = [ cfg.workdir.gems cfg.workdir.gems.ruby pkgs.imagemagick pkgs.ffmpeg pkgs.file ];
201
202 script = ''
203 exec ./bin/bundle exec sidekiq -c 5 -q default -q mailers -q pull -q push
204 '';
205
206 serviceConfig = {
207 User = cfg.user;
208 EnvironmentFile = cfg.configFile;
209 PrivateTmp = true;
210 Restart = "always";
211 TimeoutSec = 15;
212 Type = "simple";
213 WorkingDirectory = cfg.workdir;
214 StateDirectory = cfg.systemdStateDirectory;
215 RuntimeDirectory = cfg.systemdRuntimeDirectory;
216 RuntimeDirectoryPreserve = "yes";
217 };
218
219 unitConfig.RequiresMountsFor = cfg.dataDir;
220 };
221
222 };
223}
diff --git a/modules/webapps/mediagoblin.nix b/modules/webapps/mediagoblin.nix
new file mode 100644
index 00000000..78bbef6f
--- /dev/null
+++ b/modules/webapps/mediagoblin.nix
@@ -0,0 +1,237 @@
1{ lib, pkgs, config, ... }:
2let
3 name = "mediagoblin";
4 cfg = config.services.mediagoblin;
5
6 uid = config.ids.uids.mediagoblin;
7 gid = config.ids.gids.mediagoblin;
8
9 paste_local = pkgs.writeText "paste_local.ini" ''
10 [DEFAULT]
11 debug = false
12
13 [pipeline:main]
14 pipeline = mediagoblin
15
16 [app:mediagoblin]
17 use = egg:mediagoblin#app
18 config = ${cfg.configFile} ${cfg.workdir}/mediagoblin.ini
19 /mgoblin_static = ${cfg.workdir}/mediagoblin/static
20
21 [loggers]
22 keys = root
23
24 [handlers]
25 keys = console
26
27 [formatters]
28 keys = generic
29
30 [logger_root]
31 level = INFO
32 handlers = console
33
34 [handler_console]
35 class = StreamHandler
36 args = (sys.stderr,)
37 level = NOTSET
38 formatter = generic
39
40 [formatter_generic]
41 format = %(levelname)-7.7s [%(name)s] %(message)s
42
43 [filter:errors]
44 use = egg:mediagoblin#errors
45 debug = false
46
47 [server:main]
48 use = egg:waitress#main
49 unix_socket = ${cfg.sockets.paster}
50 unix_socket_perms = 777
51 url_scheme = https
52 '';
53in
54{
55 options.services.mediagoblin = {
56 enable = lib.mkEnableOption "Enable Mediagoblin’s service";
57 user = lib.mkOption {
58 type = lib.types.str;
59 default = name;
60 description = "User account under which Mediagoblin runs";
61 };
62 group = lib.mkOption {
63 type = lib.types.str;
64 default = name;
65 description = "Group under which Mediagoblin runs";
66 };
67 dataDir = lib.mkOption {
68 type = lib.types.path;
69 default = "/var/lib/${name}";
70 description = ''
71 The directory where Mediagoblin stores its data.
72 '';
73 };
74 socketsDir = lib.mkOption {
75 type = lib.types.path;
76 default = "/run/${name}";
77 description = ''
78 The directory where Mediagoblin puts runtime files and sockets.
79 '';
80 };
81 configFile = lib.mkOption {
82 type = lib.types.path;
83 description = ''
84 The configuration file path for Mediagoblin.
85 '';
86 };
87 package = lib.mkOption {
88 type = lib.types.package;
89 default = pkgs.webapps.mediagoblin;
90 description = ''
91 Mediagoblin package to use.
92 '';
93 };
94 plugins = lib.mkOption {
95 type = lib.types.listOf lib.types.package;
96 default = [];
97 description = ''
98 Mediagoblin plugins to use.
99 '';
100 };
101 # Output variables
102 workdir = lib.mkOption {
103 type = lib.types.package;
104 default = cfg.package.withPlugins cfg.plugins;
105 description = ''
106 Adjusted Mediagoblin package with plugins
107 '';
108 readOnly = true;
109 };
110 systemdStateDirectory = lib.mkOption {
111 type = lib.types.str;
112 # Use ReadWritePaths= instead if varDir is outside of /var/lib
113 default = assert lib.strings.hasPrefix "/var/lib/" cfg.dataDir;
114 lib.strings.removePrefix "/var/lib/" cfg.dataDir;
115 description = ''
116 Adjusted Mediagoblin data directory for systemd
117 '';
118 readOnly = true;
119 };
120 systemdRuntimeDirectory = lib.mkOption {
121 type = lib.types.str;
122 # Use ReadWritePaths= instead if socketsDir is outside of /run
123 default = assert lib.strings.hasPrefix "/run/" cfg.socketsDir;
124 lib.strings.removePrefix "/run/" cfg.socketsDir;
125 description = ''
126 Adjusted Mediagoblin sockets directory for systemd
127 '';
128 readOnly = true;
129 };
130 sockets = lib.mkOption {
131 type = lib.types.attrsOf lib.types.path;
132 default = {
133 paster = "${cfg.socketsDir}/mediagoblin.sock";
134 };
135 readOnly = true;
136 description = ''
137 Mediagoblin sockets
138 '';
139 };
140 pids = lib.mkOption {
141 type = lib.types.attrsOf lib.types.path;
142 default = {
143 paster = "${cfg.socketsDir}/mediagoblin.pid";
144 celery = "${cfg.socketsDir}/mediagoblin-celeryd.pid";
145 };
146 readOnly = true;
147 description = ''
148 Mediagoblin pid files
149 '';
150 };
151 };
152
153 config = lib.mkIf cfg.enable {
154 users.users = lib.optionalAttrs (cfg.user == name) (lib.singleton {
155 inherit name;
156 inherit uid;
157 group = cfg.group;
158 description = "Mediagoblin user";
159 home = cfg.dataDir;
160 useDefaultShell = true;
161 });
162 users.groups = lib.optionalAttrs (cfg.group == name) (lib.singleton {
163 inherit name;
164 inherit gid;
165 });
166
167 systemd.services.mediagoblin-web = {
168 description = "Mediagoblin service";
169 wantedBy = [ "multi-user.target" ];
170 after = [ "network.target" ];
171 wants = [ "postgresql.service" "redis.service" ];
172
173 environment.SCRIPT_NAME = "/mediagoblin/";
174
175 script = ''
176 exec ./bin/paster serve \
177 ${paste_local} \
178 --pid-file=${cfg.pids.paster}
179 '';
180 preStop = ''
181 exec ./bin/paster serve \
182 --pid-file=${cfg.pids.paster} \
183 ${paste_local} stop
184 '';
185 preStart = ''
186 if [ -d ${cfg.dataDir}/plugin_static/ ]; then
187 rm ${cfg.dataDir}/plugin_static/coreplugin_basic_auth
188 ln -sf ${cfg.workdir}/mediagoblin/plugins/basic_auth/static ${cfg.dataDir}/plugin_static/coreplugin_basic_auth
189 fi
190 ./bin/gmg -cf ${cfg.configFile} dbupdate
191 '';
192
193 serviceConfig = {
194 User = cfg.user;
195 PrivateTmp = true;
196 Restart = "always";
197 TimeoutSec = 15;
198 Type = "simple";
199 WorkingDirectory = cfg.workdir;
200 RuntimeDirectory = cfg.systemdRuntimeDirectory;
201 StateDirectory= cfg.systemdStateDirectory;
202 PIDFile = cfg.pids.paster;
203 };
204
205 unitConfig.RequiresMountsFor = cfg.dataDir;
206 };
207
208 systemd.services.mediagoblin-celeryd = {
209 description = "Mediagoblin service";
210 wantedBy = [ "multi-user.target" ];
211 after = [ "network.target" "mediagoblin-web.service" ];
212
213 environment.MEDIAGOBLIN_CONFIG = cfg.configFile;
214 environment.CELERY_CONFIG_MODULE = "mediagoblin.init.celery.from_celery";
215
216 script = ''
217 exec ./bin/celery worker \
218 --logfile=${cfg.dataDir}/celery.log \
219 --loglevel=INFO
220 '';
221
222 serviceConfig = {
223 User = cfg.user;
224 PrivateTmp = true;
225 Restart = "always";
226 TimeoutSec = 60;
227 Type = "simple";
228 WorkingDirectory = cfg.workdir;
229 RuntimeDirectory = cfg.systemdRuntimeDirectory;
230 StateDirectory= cfg.systemdStateDirectory;
231 PIDFile = cfg.pids.celery;
232 };
233
234 unitConfig.RequiresMountsFor = cfg.dataDir;
235 };
236 };
237}
diff --git a/modules/webapps/peertube.nix b/modules/webapps/peertube.nix
new file mode 100644
index 00000000..89dcc67a
--- /dev/null
+++ b/modules/webapps/peertube.nix
@@ -0,0 +1,105 @@
1{ lib, pkgs, config, ... }:
2let
3 name = "peertube";
4 cfg = config.services.peertube;
5
6 uid = config.ids.uids.peertube;
7 gid = config.ids.gids.peertube;
8in
9{
10 options.services.peertube = {
11 enable = lib.mkEnableOption "Enable Peertube’s service";
12 user = lib.mkOption {
13 type = lib.types.str;
14 default = name;
15 description = "User account under which Peertube runs";
16 };
17 group = lib.mkOption {
18 type = lib.types.str;
19 default = name;
20 description = "Group under which Peertube runs";
21 };
22 dataDir = lib.mkOption {
23 type = lib.types.path;
24 default = "/var/lib/${name}";
25 description = ''
26 The directory where Peertube stores its data.
27 '';
28 };
29 configFile = lib.mkOption {
30 type = lib.types.path;
31 description = ''
32 The configuration file path for Peertube.
33 '';
34 };
35 package = lib.mkOption {
36 type = lib.types.package;
37 default = pkgs.webapps.peertube;
38 description = ''
39 Peertube package to use.
40 '';
41 };
42 # Output variables
43 systemdStateDirectory = lib.mkOption {
44 type = lib.types.str;
45 # Use ReadWritePaths= instead if varDir is outside of /var/lib
46 default = assert lib.strings.hasPrefix "/var/lib/" cfg.dataDir;
47 lib.strings.removePrefix "/var/lib/" cfg.dataDir;
48 description = ''
49 Adjusted Peertube data directory for systemd
50 '';
51 readOnly = true;
52 };
53 };
54
55 config = lib.mkIf cfg.enable {
56 users.users = lib.optionalAttrs (cfg.user == name) (lib.singleton {
57 inherit name;
58 inherit uid;
59 group = cfg.group;
60 description = "Peertube user";
61 home = cfg.dataDir;
62 useDefaultShell = true;
63 });
64 users.groups = lib.optionalAttrs (cfg.group == name) (lib.singleton {
65 inherit name;
66 inherit gid;
67 });
68
69 systemd.services.peertube = {
70 description = "Peertube";
71 wantedBy = [ "multi-user.target" ];
72 after = [ "network.target" "postgresql.service" ];
73 wants = [ "postgresql.service" ];
74
75 environment.NODE_CONFIG_DIR = "${cfg.dataDir}/config";
76 environment.NODE_ENV = "production";
77 environment.HOME = cfg.package;
78
79 path = [ pkgs.nodejs pkgs.bashInteractive pkgs.ffmpeg pkgs.openssl ];
80
81 script = ''
82 install -m 0750 -d ${cfg.dataDir}/config
83 ln -sf ${cfg.configFile} ${cfg.dataDir}/config/production.yaml
84 exec npm run start
85 '';
86
87 serviceConfig = {
88 User = cfg.user;
89 Group = cfg.group;
90 WorkingDirectory = cfg.package;
91 StateDirectory = cfg.systemdStateDirectory;
92 StateDirectoryMode = 0750;
93 PrivateTmp = true;
94 ProtectHome = true;
95 ProtectControlGroups = true;
96 Restart = "always";
97 Type = "simple";
98 TimeoutSec = 60;
99 };
100
101 unitConfig.RequiresMountsFor = cfg.dataDir;
102 };
103 };
104}
105
diff --git a/modules/webapps/webstats/default.nix b/modules/webapps/webstats/default.nix
new file mode 100644
index 00000000..924d72de
--- /dev/null
+++ b/modules/webapps/webstats/default.nix
@@ -0,0 +1,81 @@
1{ lib, pkgs, config, ... }:
2let
3 name = "goaccess";
4 cfg = config.services.webstats;
5in {
6 options.services.webstats = {
7 dataDir = lib.mkOption {
8 type = lib.types.path;
9 default = "/var/lib/${name}";
10 description = ''
11 The directory where Goaccess stores its data.
12 '';
13 };
14 sites = lib.mkOption {
15 type = lib.types.listOf (lib.types.submodule {
16 options = {
17 conf = lib.mkOption {
18 type = lib.types.nullOr lib.types.path;
19 default = null;
20 description = ''
21 use custom goaccess configuration file instead of the
22 default one.
23 '';
24 };
25 name = lib.mkOption {
26 type = lib.types.string;
27 description = ''
28 Domain name. Corresponds to the Apache file name and the
29 folder name in which the state will be saved.
30 '';
31 };
32 };
33 });
34 default = [];
35 description = "Sites to generate stats";
36 };
37 };
38
39 config = lib.mkIf (builtins.length cfg.sites > 0) {
40 users.users.root.packages = [
41 pkgs.goaccess
42 ];
43
44 services.cron = {
45 enable = true;
46 systemCronJobs = let
47 stats = domain: conf: let
48 config = if builtins.isNull conf
49 then pkgs.runCommand "goaccess.conf" {
50 dbPath = "${cfg.dataDir}/${domain}";
51 } "substituteAll ${./goaccess.conf} $out"
52 else conf;
53 d = pkgs.writeScriptBin "stats-${domain}" ''
54 #!${pkgs.stdenv.shell}
55 set -e
56 shopt -s nullglob
57 date_regex=$(LC_ALL=C date -d yesterday +'%d\/%b\/%Y')
58 TMPFILE=$(mktemp)
59 trap "rm -f $TMPFILE" EXIT
60
61 mkdir -p ${cfg.dataDir}/${domain}
62 cat /var/log/httpd/access-${domain}.log | sed -n "/\\[$date_regex/ p" > $TMPFILE
63 for i in /var/log/httpd/access-${domain}*.gz; do
64 zcat "$i" | sed -n "/\\[$date_regex/ p" >> $TMPFILE
65 done
66 ${pkgs.goaccess}/bin/goaccess $TMPFILE --no-progress -o ${cfg.dataDir}/${domain}/index.html -p ${config}
67 '';
68 in "${d}/bin/stats-${domain}";
69 allStats = sites: pkgs.writeScript "stats" ''
70 #!${pkgs.stdenv.shell}
71
72 mkdir -p ${cfg.dataDir}
73 ${builtins.concatStringsSep "\n" (map (v: stats v.name v.conf) sites)}
74 '';
75 in
76 [
77 "5 0 * * * root ${allStats cfg.sites}"
78 ];
79 };
80 };
81}
diff --git a/modules/webapps/webstats/goaccess.conf b/modules/webapps/webstats/goaccess.conf
new file mode 100644
index 00000000..49189883
--- /dev/null
+++ b/modules/webapps/webstats/goaccess.conf
@@ -0,0 +1,99 @@
1time-format %H:%M:%S
2date-format %d/%b/%Y
3
4#sur immae.eu
5#log-format %v %h %^[%d:%t %^] "%r" %s %b "%R" "%u" $^
6
7log-format VCOMBINED
8#= %v:%^ %h %^[%d:%t %^] "%r" %s %b "%R" "%u"
9
10html-prefs {"theme":"bright","layout":"vertical"}
11
12exclude-ip 188.165.209.148
13exclude-ip 178.33.252.96
14exclude-ip 2001:41d0:2:9c94::1
15exclude-ip 2001:41d0:2:9c94::
16exclude-ip 176.9.151.89
17exclude-ip 2a01:4f8:160:3445::
18exclude-ip 82.255.56.72
19
20no-query-string true
21
22keep-db-files true
23load-from-disk true
24db-path @dbPath@
25
26ignore-panel REFERRERS
27ignore-panel KEYPHRASES
28
29static-file .css
30static-file .js
31static-file .jpg
32static-file .png
33static-file .gif
34static-file .ico
35static-file .jpeg
36static-file .pdf
37static-file .csv
38static-file .mpeg
39static-file .mpg
40static-file .swf
41static-file .woff
42static-file .woff2
43static-file .xls
44static-file .xlsx
45static-file .doc
46static-file .docx
47static-file .ppt
48static-file .pptx
49static-file .txt
50static-file .zip
51static-file .ogg
52static-file .mp3
53static-file .mp4
54static-file .exe
55static-file .iso
56static-file .gz
57static-file .rar
58static-file .svg
59static-file .bmp
60static-file .tar
61static-file .tgz
62static-file .tiff
63static-file .tif
64static-file .ttf
65static-file .flv
66#static-file .less
67#static-file .ac3
68#static-file .avi
69#static-file .bz2
70#static-file .class
71#static-file .cue
72#static-file .dae
73#static-file .dat
74#static-file .dts
75#static-file .ejs
76#static-file .eot
77#static-file .eps
78#static-file .img
79#static-file .jar
80#static-file .map
81#static-file .mid
82#static-file .midi
83#static-file .ogv
84#static-file .webm
85#static-file .mkv
86#static-file .odp
87#static-file .ods
88#static-file .odt
89#static-file .otf
90#static-file .pict
91#static-file .pls
92#static-file .ps
93#static-file .qt
94#static-file .rm
95#static-file .svgz
96#static-file .wav
97#static-file .webp
98
99