diff options
Diffstat (limited to 'modules/webapps')
-rw-r--r-- | modules/webapps/diaspora.nix | 171 | ||||
-rw-r--r-- | modules/webapps/etherpad-lite.nix | 158 | ||||
-rw-r--r-- | modules/webapps/mastodon.nix | 223 | ||||
-rw-r--r-- | modules/webapps/mediagoblin.nix | 237 | ||||
-rw-r--r-- | modules/webapps/peertube.nix | 105 | ||||
-rw-r--r-- | modules/webapps/webstats/default.nix | 81 | ||||
-rw-r--r-- | modules/webapps/webstats/goaccess.conf | 99 |
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, ... }: | ||
2 | let | ||
3 | name = "diaspora"; | ||
4 | cfg = config.services.diaspora; | ||
5 | |||
6 | uid = config.ids.uids.diaspora; | ||
7 | gid = config.ids.gids.diaspora; | ||
8 | in | ||
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, ... }: | ||
2 | let | ||
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; | ||
8 | in | ||
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, ... }: | ||
2 | let | ||
3 | name = "mastodon"; | ||
4 | cfg = config.services.mastodon; | ||
5 | |||
6 | uid = config.ids.uids.mastodon; | ||
7 | gid = config.ids.gids.mastodon; | ||
8 | in | ||
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, ... }: | ||
2 | let | ||
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 | ''; | ||
53 | in | ||
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, ... }: | ||
2 | let | ||
3 | name = "peertube"; | ||
4 | cfg = config.services.peertube; | ||
5 | |||
6 | uid = config.ids.uids.peertube; | ||
7 | gid = config.ids.gids.peertube; | ||
8 | in | ||
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, ... }: | ||
2 | let | ||
3 | name = "goaccess"; | ||
4 | cfg = config.services.webstats; | ||
5 | in { | ||
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 @@ | |||
1 | time-format %H:%M:%S | ||
2 | date-format %d/%b/%Y | ||
3 | |||
4 | #sur immae.eu | ||
5 | #log-format %v %h %^[%d:%t %^] "%r" %s %b "%R" "%u" $^ | ||
6 | |||
7 | log-format VCOMBINED | ||
8 | #= %v:%^ %h %^[%d:%t %^] "%r" %s %b "%R" "%u" | ||
9 | |||
10 | html-prefs {"theme":"bright","layout":"vertical"} | ||
11 | |||
12 | exclude-ip 188.165.209.148 | ||
13 | exclude-ip 178.33.252.96 | ||
14 | exclude-ip 2001:41d0:2:9c94::1 | ||
15 | exclude-ip 2001:41d0:2:9c94:: | ||
16 | exclude-ip 176.9.151.89 | ||
17 | exclude-ip 2a01:4f8:160:3445:: | ||
18 | exclude-ip 82.255.56.72 | ||
19 | |||
20 | no-query-string true | ||
21 | |||
22 | keep-db-files true | ||
23 | load-from-disk true | ||
24 | db-path @dbPath@ | ||
25 | |||
26 | ignore-panel REFERRERS | ||
27 | ignore-panel KEYPHRASES | ||
28 | |||
29 | static-file .css | ||
30 | static-file .js | ||
31 | static-file .jpg | ||
32 | static-file .png | ||
33 | static-file .gif | ||
34 | static-file .ico | ||
35 | static-file .jpeg | ||
36 | static-file .pdf | ||
37 | static-file .csv | ||
38 | static-file .mpeg | ||
39 | static-file .mpg | ||
40 | static-file .swf | ||
41 | static-file .woff | ||
42 | static-file .woff2 | ||
43 | static-file .xls | ||
44 | static-file .xlsx | ||
45 | static-file .doc | ||
46 | static-file .docx | ||
47 | static-file .ppt | ||
48 | static-file .pptx | ||
49 | static-file .txt | ||
50 | static-file .zip | ||
51 | static-file .ogg | ||
52 | static-file .mp3 | ||
53 | static-file .mp4 | ||
54 | static-file .exe | ||
55 | static-file .iso | ||
56 | static-file .gz | ||
57 | static-file .rar | ||
58 | static-file .svg | ||
59 | static-file .bmp | ||
60 | static-file .tar | ||
61 | static-file .tgz | ||
62 | static-file .tiff | ||
63 | static-file .tif | ||
64 | static-file .ttf | ||
65 | static-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 | |||