python3Packages.orjson: Disable failing tests on 32 bit
[NixPkgs.git] / nixos / modules / services / networking / seafile.nix
blobb07d51b9b49a0d6a275821b8bc5b6b104bb28992
1 { config, lib, pkgs, ... }:
2 with lib;
3 let
4   cfg = config.services.seafile;
5   settingsFormat = pkgs.formats.ini { };
7   ccnetConf = settingsFormat.generate "ccnet.conf" cfg.ccnetSettings;
9   seafileConf = settingsFormat.generate "seafile.conf" cfg.seafileSettings;
11   seahubSettings = pkgs.writeText "seahub_settings.py" ''
12     FILE_SERVER_ROOT = '${cfg.ccnetSettings.General.SERVICE_URL}/seafhttp'
13     DATABASES = {
14         'default': {
15             'ENGINE': 'django.db.backends.sqlite3',
16             'NAME': '${seahubDir}/seahub.db',
17         }
18     }
19     MEDIA_ROOT = '${seahubDir}/media/'
20     THUMBNAIL_ROOT = '${seahubDir}/thumbnail/'
22     SERVICE_URL = '${cfg.ccnetSettings.General.SERVICE_URL}'
24     with open('${seafRoot}/.seahubSecret') as f:
25         SECRET_KEY = f.readline().rstrip()
27     ${cfg.seahubExtraConf}
28   '';
30   seafRoot = "/var/lib/seafile"; # hardcode it due to dynamicuser
31   ccnetDir = "${seafRoot}/ccnet";
32   dataDir = "${seafRoot}/data";
33   seahubDir = "${seafRoot}/seahub";
35 in {
37   ###### Interface
39   options.services.seafile = {
40     enable = mkEnableOption (lib.mdDoc "Seafile server");
42     ccnetSettings = mkOption {
43       type = types.submodule {
44         freeformType = settingsFormat.type;
46         options = {
47           General = {
48             SERVICE_URL = mkOption {
49               type = types.str;
50               example = "https://www.example.com";
51               description = lib.mdDoc ''
52                 Seahub public URL.
53               '';
54             };
55           };
56         };
57       };
58       default = { };
59       description = lib.mdDoc ''
60         Configuration for ccnet, see
61         <https://manual.seafile.com/config/ccnet-conf/>
62         for supported values.
63       '';
64     };
66     seafileSettings = mkOption {
67       type = types.submodule {
68         freeformType = settingsFormat.type;
70         options = {
71           fileserver = {
72             port = mkOption {
73               type = types.port;
74               default = 8082;
75               description = lib.mdDoc ''
76                 The tcp port used by seafile fileserver.
77               '';
78             };
79             host = mkOption {
80               type = types.str;
81               default = "127.0.0.1";
82               example = "0.0.0.0";
83               description = lib.mdDoc ''
84                 The binding address used by seafile fileserver.
85               '';
86             };
87           };
88         };
89       };
90       default = { };
91       description = lib.mdDoc ''
92         Configuration for seafile-server, see
93         <https://manual.seafile.com/config/seafile-conf/>
94         for supported values.
95       '';
96     };
98     workers = mkOption {
99       type = types.int;
100       default = 4;
101       example = 10;
102       description = lib.mdDoc ''
103         The number of gunicorn worker processes for handling requests.
104       '';
105     };
107     adminEmail = mkOption {
108       example = "john@example.com";
109       type = types.str;
110       description = lib.mdDoc ''
111         Seafile Seahub Admin Account Email.
112       '';
113     };
115     initialAdminPassword = mkOption {
116       example = "someStrongPass";
117       type = types.str;
118       description = lib.mdDoc ''
119         Seafile Seahub Admin Account initial password.
120         Should be change via Seahub web front-end.
121       '';
122     };
124     seafilePackage = mkOption {
125       type = types.package;
126       description = lib.mdDoc "Which package to use for the seafile server.";
127       default = pkgs.seafile-server;
128       defaultText = literalExpression "pkgs.seafile-server";
129     };
131     seahubExtraConf = mkOption {
132       default = "";
133       type = types.lines;
134       description = lib.mdDoc ''
135         Extra config to append to `seahub_settings.py` file.
136         Refer to <https://manual.seafile.com/config/seahub_settings_py/>
137         for all available options.
138       '';
139     };
140   };
142   ###### Implementation
144   config = mkIf cfg.enable {
146     environment.etc."seafile/ccnet.conf".source = ccnetConf;
147     environment.etc."seafile/seafile.conf".source = seafileConf;
148     environment.etc."seafile/seahub_settings.py".source = seahubSettings;
150     systemd.targets.seafile = {
151       wantedBy = [ "multi-user.target" ];
152       description = "Seafile components";
153     };
155     systemd.services = let
156       securityOptions = {
157         ProtectHome = true;
158         PrivateUsers = true;
159         PrivateDevices = true;
160         ProtectClock = true;
161         ProtectHostname = true;
162         ProtectProc = "invisible";
163         ProtectKernelModules = true;
164         ProtectKernelTunables = true;
165         ProtectKernelLogs = true;
166         ProtectControlGroups = true;
167         RestrictNamespaces = true;
168         LockPersonality = true;
169         RestrictRealtime = true;
170         RestrictSUIDSGID = true;
171         MemoryDenyWriteExecute = true;
172         SystemCallArchitectures = "native";
173         RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" ];
174       };
175     in {
176       seaf-server = {
177         description = "Seafile server";
178         partOf = [ "seafile.target" ];
179         after = [ "network.target" ];
180         wantedBy = [ "seafile.target" ];
181         restartTriggers = [ ccnetConf seafileConf ];
182         path = [ pkgs.sqlite ];
183         serviceConfig = securityOptions // {
184           User = "seafile";
185           Group = "seafile";
186           DynamicUser = true;
187           StateDirectory = "seafile";
188           RuntimeDirectory = "seafile";
189           LogsDirectory = "seafile";
190           ConfigurationDirectory = "seafile";
191           ExecStart = ''
192             ${cfg.seafilePackage}/bin/seaf-server \
193             --foreground \
194             -F /etc/seafile \
195             -c ${ccnetDir} \
196             -d ${dataDir} \
197             -l /var/log/seafile/server.log \
198             -P /run/seafile/server.pid \
199             -p /run/seafile
200           '';
201         };
202         preStart = ''
203           if [ ! -f "${seafRoot}/server-setup" ]; then
204               mkdir -p ${dataDir}/library-template
205               mkdir -p ${ccnetDir}/{GroupMgr,misc,OrgMgr,PeerMgr}
206               sqlite3 ${ccnetDir}/GroupMgr/groupmgr.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/groupmgr.sql"
207               sqlite3 ${ccnetDir}/misc/config.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/config.sql"
208               sqlite3 ${ccnetDir}/OrgMgr/orgmgr.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/org.sql"
209               sqlite3 ${ccnetDir}/PeerMgr/usermgr.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/user.sql"
210               sqlite3 ${dataDir}/seafile.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/seafile.sql"
211               echo "${cfg.seafilePackage.version}-sqlite" > "${seafRoot}"/server-setup
212           fi
213           # checking for upgrades and handling them
214           # WARNING: needs to be extended to actually handle major version migrations
215           installedMajor=$(cat "${seafRoot}/server-setup" | cut -d"-" -f1 | cut -d"." -f1)
216           installedMinor=$(cat "${seafRoot}/server-setup" | cut -d"-" -f1 | cut -d"." -f2)
217           pkgMajor=$(echo "${cfg.seafilePackage.version}" | cut -d"." -f1)
218           pkgMinor=$(echo "${cfg.seafilePackage.version}" | cut -d"." -f2)
220           if [[ $installedMajor == $pkgMajor && $installedMinor == $pkgMinor ]]; then
221              :
222           elif [[ $installedMajor == 8 && $installedMinor == 0 && $pkgMajor == 9 && $pkgMinor == 0 ]]; then
223               # Upgrade from 8.0 to 9.0
224               sqlite3 ${dataDir}/seafile.db ".read ${pkgs.seahub}/scripts/upgrade/sql/9.0.0/sqlite3/seafile.sql"
225               echo "${cfg.seafilePackage.version}-sqlite" > "${seafRoot}"/server-setup
226           else
227               echo "Unsupported upgrade" >&2
228               exit 1
229           fi
230         '';
231       };
233       seahub = {
234         description = "Seafile Server Web Frontend";
235         wantedBy = [ "seafile.target" ];
236         partOf = [ "seafile.target" ];
237         after = [ "network.target" "seaf-server.service" ];
238         requires = [ "seaf-server.service" ];
239         restartTriggers = [ seahubSettings ];
240         environment = {
241           PYTHONPATH = "${pkgs.seahub.pythonPath}:${pkgs.seahub}/thirdpart:${pkgs.seahub}";
242           DJANGO_SETTINGS_MODULE = "seahub.settings";
243           CCNET_CONF_DIR = ccnetDir;
244           SEAFILE_CONF_DIR = dataDir;
245           SEAFILE_CENTRAL_CONF_DIR = "/etc/seafile";
246           SEAFILE_RPC_PIPE_PATH = "/run/seafile";
247           SEAHUB_LOG_DIR = "/var/log/seafile";
248         };
249         serviceConfig = securityOptions // {
250           User = "seafile";
251           Group = "seafile";
252           DynamicUser = true;
253           RuntimeDirectory = "seahub";
254           StateDirectory = "seafile";
255           LogsDirectory = "seafile";
256           ConfigurationDirectory = "seafile";
257           ExecStart = ''
258             ${pkgs.seahub.python.pkgs.gunicorn}/bin/gunicorn seahub.wsgi:application \
259             --name seahub \
260             --workers ${toString cfg.workers} \
261             --log-level=info \
262             --preload \
263             --timeout=1200 \
264             --limit-request-line=8190 \
265             --bind unix:/run/seahub/gunicorn.sock
266           '';
267         };
268         preStart = ''
269           mkdir -p ${seahubDir}/media
270           # Link all media except avatars
271           for m in `find ${pkgs.seahub}/media/ -maxdepth 1 -not -name "avatars"`; do
272             ln -sf $m ${seahubDir}/media/
273           done
274           if [ ! -e "${seafRoot}/.seahubSecret" ]; then
275               ${pkgs.seahub.python}/bin/python ${pkgs.seahub}/tools/secret_key_generator.py > ${seafRoot}/.seahubSecret
276               chmod 400 ${seafRoot}/.seahubSecret
277           fi
278           if [ ! -f "${seafRoot}/seahub-setup" ]; then
279               # avatars directory should be writable
280               install -D -t ${seahubDir}/media/avatars/ ${pkgs.seahub}/media/avatars/default.png
281               install -D -t ${seahubDir}/media/avatars/groups ${pkgs.seahub}/media/avatars/groups/default.png
282               # init database
283               ${pkgs.seahub}/manage.py migrate
284               # create admin account
285               ${pkgs.expect}/bin/expect -c 'spawn ${pkgs.seahub}/manage.py createsuperuser --email=${cfg.adminEmail}; expect "Password: "; send "${cfg.initialAdminPassword}\r"; expect "Password (again): "; send "${cfg.initialAdminPassword}\r"; expect "Superuser created successfully."'
286               echo "${pkgs.seahub.version}-sqlite" > "${seafRoot}/seahub-setup"
287           fi
288           if [ $(cat "${seafRoot}/seahub-setup" | cut -d"-" -f1) != "${pkgs.seahub.version}" ]; then
289               # update database
290               ${pkgs.seahub}/manage.py migrate
291               echo "${pkgs.seahub.version}-sqlite" > "${seafRoot}/seahub-setup"
292           fi
293         '';
294       };
295     };
296   };