From 073099d0f7a1fe42490ba95f7295f78434004197 Mon Sep 17 00:00:00 2001 From: melvyn Date: Fri, 27 Sep 2024 19:58:34 -0400 Subject: [PATCH] nixos/seafile: add persistent user, configurable storage path, gc service --- .../manual/release-notes/rl-2411.section.md | 10 + nixos/modules/services/networking/seafile.nix | 383 ++++++++++++------ nixos/tests/seafile.nix | 3 +- pkgs/by-name/se/seahub/package.nix | 4 +- 4 files changed, 284 insertions(+), 116 deletions(-) diff --git a/nixos/doc/manual/release-notes/rl-2411.section.md b/nixos/doc/manual/release-notes/rl-2411.section.md index f9a5846c1f38..a62387cb6e56 100644 --- a/nixos/doc/manual/release-notes/rl-2411.section.md +++ b/nixos/doc/manual/release-notes/rl-2411.section.md @@ -529,6 +529,16 @@ - The kubelet configuration file can now be amended with arbitrary additional content using the `services.kubernetes.kubelet.extraConfig` option. +- The `services.seafile` module was updated to major version 11. + - As part of this upgrade, the database backend will be migrated to MySQL. + This process should be automatic, but in case of a botched migration, + old sqlite files are not removed and can be used to manually migrate the database. + - Additionally, the updated CSRF protection may prevent some users from logging in. + Specific origin addresses can be whitelisted using the `services.seafile.seahubExtraConf` option + (e.g. `services.seafile.seahubExtraConf = ''CSRF_TRUSTED_ORIGINS = ["https://example.com"]'';`). + Note that first solution of the [official FAQ answer](https://cloud.seatable.io/dtable/external-links/7b976c85f504491cbe8e/?tid=0000&vid=0000&row-id=BQhH-2HSQs68Nq2EW91DBA) + is not allowed by the `services.nginx` module's config-checker. + - To facilitate dependency injection, the `imgui` package now builds a static archive using vcpkg' CMake rules. The derivation now installs "impl" headers selectively instead of by a wildcard. Use `imgui.src` if you just want to access the unpacked sources. diff --git a/nixos/modules/services/networking/seafile.nix b/nixos/modules/services/networking/seafile.nix index e50829634d19..356bcfe04419 100644 --- a/nixos/modules/services/networking/seafile.nix +++ b/nixos/modules/services/networking/seafile.nix @@ -1,19 +1,42 @@ -{ config, lib, pkgs, ... }: -with lib; +{ + config, + lib, + pkgs, + ... +}: let cfg = config.services.seafile; settingsFormat = pkgs.formats.ini { }; - ccnetConf = settingsFormat.generate "ccnet.conf" cfg.ccnetSettings; + ccnetConf = settingsFormat.generate "ccnet.conf" ( + lib.attrsets.recursiveUpdate { + Database = { + ENGINE = "mysql"; + UNIX_SOCKET = "/var/run/mysqld/mysqld.sock"; + DB = "ccnet_db"; + CONNECTION_CHARSET = "utf8"; + }; + } cfg.ccnetSettings + ); - seafileConf = settingsFormat.generate "seafile.conf" cfg.seafileSettings; + seafileConf = settingsFormat.generate "seafile.conf" ( + lib.attrsets.recursiveUpdate { + database = { + type = "mysql"; + unix_socket = "/var/run/mysqld/mysqld.sock"; + db_name = "seafile_db"; + connection_charset = "utf8"; + }; + } cfg.seafileSettings + ); seahubSettings = pkgs.writeText "seahub_settings.py" '' FILE_SERVER_ROOT = '${cfg.ccnetSettings.General.SERVICE_URL}/seafhttp' DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': '${seahubDir}/seahub.db', + 'ENGINE': 'django.db.backends.mysql', + 'NAME' : 'seahub_db', + 'HOST' : '/var/run/mysqld/mysqld.sock', } } MEDIA_ROOT = '${seahubDir}/media/' @@ -21,13 +44,15 @@ let SERVICE_URL = '${cfg.ccnetSettings.General.SERVICE_URL}' + CSRF_TRUSTED_ORIGINS = ["${cfg.ccnetSettings.General.SERVICE_URL}"] + with open('${seafRoot}/.seahubSecret') as f: SECRET_KEY = f.readline().rstrip() ${cfg.seahubExtraConf} ''; - seafRoot = "/var/lib/seafile"; # hardcode it due to dynamicuser + seafRoot = "/var/lib/seafile"; ccnetDir = "${seafRoot}/ccnet"; seahubDir = "${seafRoot}/seahub"; defaultUser = "seafile"; @@ -37,7 +62,7 @@ in ###### Interface - options.services.seafile = { + options.services.seafile = with lib; { enable = mkEnableOption "Seafile server"; ccnetSettings = mkOption { @@ -47,7 +72,7 @@ in options = { General = { SERVICE_URL = mkOption { - type = types.str; + type = types.singleLineStr; example = "https://www.example.com"; description = '' Seahub public URL. @@ -78,11 +103,17 @@ in ''; }; host = mkOption { - type = types.str; - default = "127.0.0.1"; - example = "0.0.0.0"; + type = types.singleLineStr; + default = "ipv4:127.0.0.1"; + example = "unix:/run/seafile/server.sock"; description = '' - The binding address used by seafile fileserver. + The bind address used by seafile fileserver. + + The addr can be defined as one of the following: + - ipv6: for binding to an IPv6 address. + - unix: for binding to a unix named socket + - ipv4: for binding to an ipv4 address + Otherwise the addr is assumed to be ipv4. ''; }; }; @@ -96,6 +127,19 @@ in ''; }; + seahubAddress = mkOption { + type = types.singleLineStr; + default = "unix:/run/seahub/gunicorn.sock"; + example = "[::1]:8083"; + description = '' + Which address to bind the seahub server to, of the form: + - HOST + - HOST:PORT + - unix:PATH. + IPv6 HOSTs must be wrapped in brackets. + ''; + }; + workers = mkOption { type = types.int; default = 4; @@ -107,7 +151,7 @@ in adminEmail = mkOption { example = "john@example.com"; - type = types.str; + type = types.singleLineStr; description = '' Seafile Seahub Admin Account Email. ''; @@ -115,45 +159,79 @@ in initialAdminPassword = mkOption { example = "someStrongPass"; - type = types.str; + type = types.singleLineStr; description = '' Seafile Seahub Admin Account initial password. Should be changed via Seahub web front-end. ''; }; - seafilePackage = mkPackageOption pkgs "seafile-server" { }; + seahubPackage = mkPackageOption pkgs "seahub" { }; user = mkOption { - type = types.str; + type = types.singleLineStr; default = defaultUser; description = "User account under which seafile runs."; }; group = mkOption { - type = types.str; + type = types.singleLineStr; default = defaultUser; description = "Group under which seafile runs."; }; dataDir = mkOption { - type = types.nullOr types.path; - default = null; - defaultText = "${seafRoot}/data"; + type = types.path; + default = "${seafRoot}/data"; description = "Path in which to store user data"; }; - gcDates = mkOption { - type = types.listOf types.str; - default = [ ]; - example = [ "Sun 03:00:00" ]; - description = '' - When to run garbage collection on stored data blocks. - ''; + gc = { + enable = mkEnableOption "automatic garbage collection on stored data blocks"; + + dates = mkOption { + type = types.listOf types.singleLineStr; + default = [ "Sun 03:00:00" ]; + description = '' + When to run garbage collection on stored data blocks. + The time format is described in {manpage}`systemd.time(7)`. + ''; + }; + + randomizedDelaySec = mkOption { + default = "0"; + type = types.singleLineStr; + example = "45min"; + description = '' + Add a randomized delay before each garbage collection. + The delay will be chosen between zero and this value. + This value must be a time span in the format specified by + {manpage}`systemd.time(7)` + ''; + }; + + persistent = mkOption { + default = true; + type = types.bool; + example = false; + description = '' + Takes a boolean argument. If true, the time when the service + unit was last triggered is stored on disk. When the timer is + activated, the service unit is triggered immediately if it + would have been triggered at least once during the time when + the timer was inactive. Such triggering is nonetheless + subject to the delay imposed by RandomizedDelaySec=. This is + useful to catch up on missed runs of the service when the + system was powered down. + ''; + }; }; seahubExtraConf = mkOption { default = ""; + example = '' + CSRF_TRUSTED_ORIGINS = ["https://example.com"] + ''; type = types.lines; description = '' Extra config to append to `seahub_settings.py` file. @@ -165,7 +243,26 @@ in ###### Implementation - config = mkIf cfg.enable { + config = lib.mkIf cfg.enable { + services.mysql = { + enable = true; + package = lib.mkDefault pkgs.mariadb; + ensureDatabases = [ + "ccnet_db" + "seafile_db" + "seahub_db" + ]; + ensureUsers = [ + { + name = cfg.user; + ensurePermissions = { + "ccnet_db.*" = "ALL PRIVILEGES"; + "seafile_db.*" = "ALL PRIVILEGES"; + "seahub_db.*" = "ALL PRIVILEGES"; + }; + } + ]; + }; environment.etc."seafile/ccnet.conf".source = ccnetConf; environment.etc."seafile/seafile.conf".source = seafileConf; @@ -178,9 +275,7 @@ in }; }; - users.groups = lib.optionalAttrs (cfg.group == defaultUser) { - "${defaultUser}" = {}; - }; + users.groups = lib.optionalAttrs (cfg.group == defaultUser) { "${defaultUser}" = { }; }; systemd.targets.seafile = { wantedBy = [ "multi-user.target" ]; @@ -189,7 +284,7 @@ in systemd.services = let - securityOptions = { + serviceOptions = { ProtectHome = true; PrivateUsers = true; PrivateDevices = true; @@ -210,34 +305,42 @@ in NoNewPrivileges = true; MemoryDenyWriteExecute = true; SystemCallArchitectures = "native"; - RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" ]; + RestrictAddressFamilies = [ + "AF_UNIX" + "AF_INET" + ]; + + User = cfg.user; + Group = cfg.group; + StateDirectory = "seafile"; + RuntimeDirectory = "seafile"; + LogsDirectory = "seafile"; + ConfigurationDirectory = "seafile"; + ReadWritePaths = lib.lists.optional (cfg.dataDir != "${seafRoot}/data") cfg.dataDir; }; - dataDir = if (!builtins.isNull cfg.dataDir) then cfg.dataDir else "${seafRoot}/data"; - optionalDataDir = lib.lists.optional (!builtins.isNull cfg.dataDir) cfg.dataDir; in { seaf-server = { description = "Seafile server"; partOf = [ "seafile.target" ]; - after = [ "network.target" ]; - unitConfig.RequiresMountsFor = optionalDataDir; + unitConfig.RequiresMountsFor = lib.lists.optional (cfg.dataDir != "${seafRoot}/data") cfg.dataDir; + requires = [ "mysql.service" ]; + after = [ + "network.target" + "mysql.service" + ]; wantedBy = [ "seafile.target" ]; - restartTriggers = [ ccnetConf seafileConf ]; - path = [ pkgs.sqlite ]; - serviceConfig = securityOptions // { - User = cfg.user; - Group = cfg.group; - StateDirectory = "seafile"; - RuntimeDirectory = "seafile"; - LogsDirectory = "seafile"; - ConfigurationDirectory = "seafile"; - ReadWritePaths = optionalDataDir; + restartTriggers = [ + ccnetConf + seafileConf + ]; + serviceConfig = serviceOptions // { ExecStart = '' - ${cfg.seafilePackage}/bin/seaf-server \ + ${lib.getExe cfg.seahubPackage.seafile-server} \ --foreground \ -F /etc/seafile \ -c ${ccnetDir} \ - -d ${dataDir} \ + -d ${cfg.dataDir} \ -l /var/log/seafile/server.log \ -P /run/seafile/server.pid \ -p /run/seafile @@ -245,118 +348,162 @@ in }; preStart = '' if [ ! -f "${seafRoot}/server-setup" ]; then - mkdir -p ${dataDir}/library-template - mkdir -p ${ccnetDir}/{GroupMgr,misc,OrgMgr,PeerMgr} - sqlite3 ${ccnetDir}/GroupMgr/groupmgr.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/groupmgr.sql" - sqlite3 ${ccnetDir}/misc/config.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/config.sql" - sqlite3 ${ccnetDir}/OrgMgr/orgmgr.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/org.sql" - sqlite3 ${ccnetDir}/PeerMgr/usermgr.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/user.sql" - sqlite3 ${dataDir}/seafile.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/seafile.sql" - echo "${cfg.seafilePackage.version}-sqlite" > "${seafRoot}"/server-setup + mkdir -p ${cfg.dataDir}/library-template + # Load schema on first install + ${pkgs.mariadb.client}/bin/mysql --database=ccnet_db < ${cfg.seahubPackage.seafile-server}/share/seafile/sql/mysql/ccnet.sql + ${pkgs.mariadb.client}/bin/mysql --database=seafile_db < ${cfg.seahubPackage.seafile-server}/share/seafile/sql/mysql/seafile.sql + echo "${cfg.seahubPackage.seafile-server.version}-mysql" > "${seafRoot}"/server-setup + echo Loaded MySQL schemas for first install fi # checking for upgrades and handling them installedMajor=$(cat "${seafRoot}/server-setup" | cut -d"-" -f1 | cut -d"." -f1) installedMinor=$(cat "${seafRoot}/server-setup" | cut -d"-" -f1 | cut -d"." -f2) - pkgMajor=$(echo "${cfg.seafilePackage.version}" | cut -d"." -f1) - pkgMinor=$(echo "${cfg.seafilePackage.version}" | cut -d"." -f2) + pkgMajor=$(echo "${cfg.seahubPackage.seafile-server.version}" | cut -d"." -f1) + pkgMinor=$(echo "${cfg.seahubPackage.seafile-server.version}" | cut -d"." -f2) if [[ $installedMajor == $pkgMajor && $installedMinor == $pkgMinor ]]; then : - elif [[ $installedMajor == 8 && $installedMinor == 0 && $pkgMajor == 9 && $pkgMinor == 0 ]]; then - # Upgrade from 8.0 to 9.0 - sqlite3 ${dataDir}/seafile.db ".read ${pkgs.seahub}/scripts/upgrade/sql/9.0.0/sqlite3/seafile.sql" - echo "${cfg.seafilePackage.version}-sqlite" > "${seafRoot}"/server-setup - elif [[ $installedMajor == 9 && $installedMinor == 0 && $pkgMajor == 10 && $pkgMinor == 0 ]]; then - # Upgrade from 9.0 to 10.0 - sqlite3 ${dataDir}/seafile.db ".read ${pkgs.seahub}/scripts/upgrade/sql/10.0.0/sqlite3/seafile.sql" - echo "${cfg.seafilePackage.version}-sqlite" > "${seafRoot}"/server-setup + elif [[ $installedMajor == 10 && $installedMinor == 0 && $pkgMajor == 11 && $pkgMinor == 0 ]]; then + # Upgrade from 10.0 to 11.0: migrate to mysql + echo Migrating from version 10 to 11 + + # From https://github.com/haiwen/seahub/blob/e12f941bfef7191795d8c72a7d339c01062964b2/scripts/sqlite2mysql.sh + + echo Migrating ccnet database to MySQL + ${lib.getExe pkgs.sqlite} ${ccnetDir}/PeerMgr/usermgr.db ".dump" | \ + ${lib.getExe cfg.seahubPackage.python3} ${cfg.seahubPackage}/scripts/sqlite2mysql.py > ${ccnetDir}/ccnet.sql + ${lib.getExe pkgs.sqlite} ${ccnetDir}/GroupMgr/groupmgr.db ".dump" | \ + ${lib.getExe cfg.seahubPackage.python3} ${cfg.seahubPackage}/scripts/sqlite2mysql.py >> ${ccnetDir}/ccnet.sql + sed 's/ctime INTEGER/ctime BIGINT/g' -i ${ccnetDir}/ccnet.sql + sed 's/email TEXT, role TEXT/email VARCHAR(255), role TEXT/g' -i ${ccnetDir}/ccnet.sql + ${pkgs.mariadb.client}/bin/mysql --database=ccnet_db < ${ccnetDir}/ccnet.sql + + echo Migrating seafile database to MySQL + ${lib.getExe pkgs.sqlite} ${cfg.dataDir}/seafile.db ".dump" | \ + ${lib.getExe cfg.seahubPackage.python3} ${cfg.seahubPackage}/scripts/sqlite2mysql.py > ${cfg.dataDir}/seafile.sql + sed 's/owner_id TEXT/owner_id VARCHAR(255)/g' -i ${cfg.dataDir}/seafile.sql + sed 's/user_name TEXT/user_name VARCHAR(255)/g' -i ${cfg.dataDir}/seafile.sql + ${pkgs.mariadb.client}/bin/mysql --database=seafile_db < ${cfg.dataDir}/seafile.sql + + echo Migrating seahub database to MySQL + echo 'SET FOREIGN_KEY_CHECKS=0;' > ${seahubDir}/seahub.sql + ${lib.getExe pkgs.sqlite} ${seahubDir}/seahub.db ".dump" | \ + ${lib.getExe cfg.seahubPackage.python3} ${cfg.seahubPackage}/scripts/sqlite2mysql.py >> ${seahubDir}/seahub.sql + sed 's/`permission` , `reporter` text NOT NULL/`permission` longtext NOT NULL/g' -i ${seahubDir}/seahub.sql + sed 's/varchar(256) NOT NULL UNIQUE/varchar(255) NOT NULL UNIQUE/g' -i ${seahubDir}/seahub.sql + sed 's/, UNIQUE (`user_email`, `contact_email`)//g' -i ${seahubDir}/seahub.sql + sed '/INSERT INTO `base_dirfileslastmodifiedinfo`/d' -i ${seahubDir}/seahub.sql + sed '/INSERT INTO `notifications_usernotification`/d' -i ${seahubDir}/seahub.sql + sed 's/DEFERRABLE INITIALLY DEFERRED//g' -i ${seahubDir}/seahub.sql + ${pkgs.mariadb.client}/bin/mysql --database=seahub_db < ${seahubDir}/seahub.sql + + echo "${cfg.seahubPackage.seafile-server.version}-mysql" > "${seafRoot}"/server-setup + echo Migration complete else - echo "Unsupported upgrade" >&2 + echo "Unsupported upgrade: $installedMajor.$installedMinor to $pkgMajor.$pkgMinor" >&2 exit 1 fi ''; + + # Fix unix socket permissions + postStart = ( + lib.strings.optionalString (lib.strings.hasPrefix "unix:" cfg.seafileSettings.fileserver.host) '' + while [[ ! -S "${lib.strings.removePrefix "unix:" cfg.seafileSettings.fileserver.host}" ]]; do + sleep 1 + done + chmod 666 "${lib.strings.removePrefix "unix:" cfg.seafileSettings.fileserver.host}" + '' + ); }; seahub = { description = "Seafile Server Web Frontend"; wantedBy = [ "seafile.target" ]; partOf = [ "seafile.target" ]; - after = [ "network.target" "seaf-server.service" ]; - requires = [ "seaf-server.service" ]; - unitConfig.RequiresMountsFor = optionalDataDir; + unitConfig.RequiresMountsFor = lib.lists.optional (cfg.dataDir != "${seafRoot}/data") cfg.dataDir; + requires = [ + "mysql.service" + "seaf-server.service" + ]; + after = [ + "network.target" + "mysql.service" + "seaf-server.service" + ]; restartTriggers = [ seahubSettings ]; environment = { - PYTHONPATH = "${pkgs.seahub.pythonPath}:${pkgs.seahub}/thirdpart:${pkgs.seahub}"; + PYTHONPATH = "${cfg.seahubPackage.pythonPath}:${cfg.seahubPackage}/thirdpart:${cfg.seahubPackage}"; DJANGO_SETTINGS_MODULE = "seahub.settings"; CCNET_CONF_DIR = ccnetDir; - SEAFILE_CONF_DIR = dataDir; + SEAFILE_CONF_DIR = cfg.dataDir; SEAFILE_CENTRAL_CONF_DIR = "/etc/seafile"; SEAFILE_RPC_PIPE_PATH = "/run/seafile"; SEAHUB_LOG_DIR = "/var/log/seafile"; }; - serviceConfig = securityOptions // { - User = cfg.user; - Group = cfg.group; + serviceConfig = serviceOptions // { RuntimeDirectory = "seahub"; - StateDirectory = "seafile"; - LogsDirectory = "seafile"; - ConfigurationDirectory = "seafile"; - ReadWritePaths = optionalDataDir; ExecStart = '' - ${pkgs.seahub.python.pkgs.gunicorn}/bin/gunicorn seahub.wsgi:application \ + ${lib.getExe cfg.seahubPackage.python3.pkgs.gunicorn} seahub.wsgi:application \ --name seahub \ --workers ${toString cfg.workers} \ --log-level=info \ --preload \ --timeout=1200 \ --limit-request-line=8190 \ - --bind unix:/run/seahub/gunicorn.sock + --bind ${cfg.seahubAddress} ''; }; preStart = '' mkdir -p ${seahubDir}/media # Link all media except avatars - for m in `find ${pkgs.seahub}/media/ -maxdepth 1 -not -name "avatars"`; do + for m in `find ${cfg.seahubPackage}/media/ -maxdepth 1 -not -name "avatars"`; do ln -sf $m ${seahubDir}/media/ done if [ ! -e "${seafRoot}/.seahubSecret" ]; then - ${pkgs.seahub.python}/bin/python ${pkgs.seahub}/tools/secret_key_generator.py > ${seafRoot}/.seahubSecret - chmod 400 ${seafRoot}/.seahubSecret + ( + umask 377 && + ${lib.getExe cfg.seahubPackage.python3} ${cfg.seahubPackage}/tools/secret_key_generator.py > ${seafRoot}/.seahubSecret + ) fi if [ ! -f "${seafRoot}/seahub-setup" ]; then # avatars directory should be writable - install -D -t ${seahubDir}/media/avatars/ ${pkgs.seahub}/media/avatars/default.png - install -D -t ${seahubDir}/media/avatars/groups ${pkgs.seahub}/media/avatars/groups/default.png + install -D -t ${seahubDir}/media/avatars/ ${cfg.seahubPackage}/media/avatars/default.png + install -D -t ${seahubDir}/media/avatars/groups ${cfg.seahubPackage}/media/avatars/groups/default.png # init database - ${pkgs.seahub}/manage.py migrate + ${cfg.seahubPackage}/manage.py migrate # create admin account - ${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."' - echo "${pkgs.seahub.version}-sqlite" > "${seafRoot}/seahub-setup" + ${lib.getExe pkgs.expect} -c 'spawn ${cfg.seahubPackage}/manage.py createsuperuser --email=${cfg.adminEmail}; expect "Password: "; send "${cfg.initialAdminPassword}\r"; expect "Password (again): "; send "${cfg.initialAdminPassword}\r"; expect "Superuser created successfully."' + echo "${cfg.seahubPackage.version}-mysql" > "${seafRoot}/seahub-setup" fi if [ $(cat "${seafRoot}/seahub-setup" | cut -d"-" -f1) != "${pkgs.seahub.version}" ]; then - # update database - ${pkgs.seahub}/manage.py migrate - echo "${pkgs.seahub.version}-sqlite" > "${seafRoot}/seahub-setup" + # run django migrations + ${cfg.seahubPackage}/manage.py migrate + echo "${cfg.seahubPackage.version}-mysql" > "${seafRoot}/seahub-setup" fi ''; }; seaf-gc = { description = "Seafile storage garbage collection"; - conflicts = [ "seaf-server.service" "seahub.service" ]; - after = [ "seaf-server.service" "seahub.service" ]; - unitConfig.RequiresMountsFor = optionalDataDir; - onSuccess = [ "seaf-server.service" "seahub.service" ]; - onFailure = [ "seaf-server.service" "seahub.service" ]; - startAt = cfg.gcDates; - serviceConfig = securityOptions // { - User = cfg.user; - Group = cfg.group; - StateDirectory = "seafile"; - RuntimeDirectory = "seafile"; - LogsDirectory = "seafile"; - ConfigurationDirectory = "seafile"; - ReadWritePaths = optionalDataDir; + conflicts = [ + "seaf-server.service" + "seahub.service" + ]; + after = [ + "seaf-server.service" + "seahub.service" + ]; + unitConfig.RequiresMountsFor = lib.lists.optional (cfg.dataDir != "${seafRoot}/data") cfg.dataDir; + onSuccess = [ + "seaf-server.service" + "seahub.service" + ]; + onFailure = [ + "seaf-server.service" + "seahub.service" + ]; + startAt = lib.lists.optionals cfg.gc.enable cfg.gc.dates; + serviceConfig = serviceOptions // { Type = "oneshot"; }; script = '' @@ -368,8 +515,8 @@ in # checking for pending upgrades installedMajor=$(cat "${seafRoot}/server-setup" | cut -d"-" -f1 | cut -d"." -f1) installedMinor=$(cat "${seafRoot}/server-setup" | cut -d"-" -f1 | cut -d"." -f2) - pkgMajor=$(echo "${cfg.seafilePackage.version}" | cut -d"." -f1) - pkgMinor=$(echo "${cfg.seafilePackage.version}" | cut -d"." -f2) + pkgMajor=$(echo "${cfg.seahubPackage.seafile-server.version}" | cut -d"." -f1) + pkgMinor=$(echo "${cfg.seahubPackage.seafile-server.version}" | cut -d"." -f2) if [[ $installedMajor != $pkgMajor || $installedMinor != $pkgMinor ]]; then echo "Server not upgraded yet" >&2 @@ -377,15 +524,25 @@ in fi # Clean up user-deleted blocks and libraries - ${cfg.seafilePackage}/bin/seafserv-gc \ + ${cfg.seahubPackage.seafile-server}/bin/seafserv-gc \ -F /etc/seafile \ -c ${ccnetDir} \ - -d ${dataDir} \ + -d ${cfg.dataDir} \ --rm-fs ''; }; }; + + systemd.timers.seaf-gc = lib.mkIf cfg.gc.enable { + timerConfig = { + randomizedDelaySec = cfg.gc.randomizedDelaySec; + Persistent = cfg.gc.persistent; + }; + }; }; - meta.maintainers = with maintainers; [ greizgh schmittlauch ]; + meta.maintainers = with lib.maintainers; [ + greizgh + schmittlauch + ]; } diff --git a/nixos/tests/seafile.nix b/nixos/tests/seafile.nix index 78e735f4fed7..7784d5fddaed 100644 --- a/nixos/tests/seafile.nix +++ b/nixos/tests/seafile.nix @@ -14,6 +14,7 @@ import ./make-test-python.nix ({ pkgs, ... }: services.seafile = { enable = true; ccnetSettings.General.SERVICE_URL = "http://server"; + seafileSettings.fileserver.host = "unix:/run/seafile/server.sock"; adminEmail = "admin@example.com"; initialAdminPassword = "seafile_password"; }; @@ -22,7 +23,7 @@ import ./make-test-python.nix ({ pkgs, ... }: virtualHosts."server" = { locations."/".proxyPass = "http://unix:/run/seahub/gunicorn.sock"; locations."/seafhttp" = { - proxyPass = "http://127.0.0.1:8082"; + proxyPass = "http://unix:/run/seafile/server.sock"; extraConfig = '' rewrite ^/seafhttp(.*)$ $1 break; client_max_body_size 0; diff --git a/pkgs/by-name/se/seahub/package.nix b/pkgs/by-name/se/seahub/package.nix index be69d643abb1..b1df123cd814 100644 --- a/pkgs/by-name/se/seahub/package.nix +++ b/pkgs/by-name/se/seahub/package.nix @@ -52,7 +52,7 @@ python3.pkgs.buildPythonApplication rec { markdown bleach - (seafile-server.override { inherit python3; }) + (python3.pkgs.toPythonModule (seafile-server.override { inherit python3; })) ]; postPatch = '' @@ -67,7 +67,7 @@ python3.pkgs.buildPythonApplication rec { passthru = { inherit python3; - pythonPath = "${python3.pkgs.makePythonPath propagatedBuildInputs}:${seafile-server}/${python3.sitePackages}"; + pythonPath = python3.pkgs.makePythonPath propagatedBuildInputs; tests = { inherit (nixosTests) seafile; };