nixos/seafile: add persistent user, configurable storage path, gc service

This commit is contained in:
melvyn 2024-09-27 19:58:34 -04:00
parent 90e07d3e55
commit 073099d0f7
No known key found for this signature in database
GPG Key ID: 25A2B7280CE3AFF6
4 changed files with 284 additions and 116 deletions

View File

@ -529,6 +529,16 @@
- The kubelet configuration file can now be amended with arbitrary additional content using the `services.kubernetes.kubelet.extraConfig` option. - 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. - 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. The derivation now installs "impl" headers selectively instead of by a wildcard.
Use `imgui.src` if you just want to access the unpacked sources. Use `imgui.src` if you just want to access the unpacked sources.

View File

@ -1,19 +1,42 @@
{ config, lib, pkgs, ... }: {
with lib; config,
lib,
pkgs,
...
}:
let let
cfg = config.services.seafile; cfg = config.services.seafile;
settingsFormat = pkgs.formats.ini { }; 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" '' seahubSettings = pkgs.writeText "seahub_settings.py" ''
FILE_SERVER_ROOT = '${cfg.ccnetSettings.General.SERVICE_URL}/seafhttp' FILE_SERVER_ROOT = '${cfg.ccnetSettings.General.SERVICE_URL}/seafhttp'
DATABASES = { DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.sqlite3', 'ENGINE': 'django.db.backends.mysql',
'NAME': '${seahubDir}/seahub.db', 'NAME' : 'seahub_db',
'HOST' : '/var/run/mysqld/mysqld.sock',
} }
} }
MEDIA_ROOT = '${seahubDir}/media/' MEDIA_ROOT = '${seahubDir}/media/'
@ -21,13 +44,15 @@ let
SERVICE_URL = '${cfg.ccnetSettings.General.SERVICE_URL}' SERVICE_URL = '${cfg.ccnetSettings.General.SERVICE_URL}'
CSRF_TRUSTED_ORIGINS = ["${cfg.ccnetSettings.General.SERVICE_URL}"]
with open('${seafRoot}/.seahubSecret') as f: with open('${seafRoot}/.seahubSecret') as f:
SECRET_KEY = f.readline().rstrip() SECRET_KEY = f.readline().rstrip()
${cfg.seahubExtraConf} ${cfg.seahubExtraConf}
''; '';
seafRoot = "/var/lib/seafile"; # hardcode it due to dynamicuser seafRoot = "/var/lib/seafile";
ccnetDir = "${seafRoot}/ccnet"; ccnetDir = "${seafRoot}/ccnet";
seahubDir = "${seafRoot}/seahub"; seahubDir = "${seafRoot}/seahub";
defaultUser = "seafile"; defaultUser = "seafile";
@ -37,7 +62,7 @@ in
###### Interface ###### Interface
options.services.seafile = { options.services.seafile = with lib; {
enable = mkEnableOption "Seafile server"; enable = mkEnableOption "Seafile server";
ccnetSettings = mkOption { ccnetSettings = mkOption {
@ -47,7 +72,7 @@ in
options = { options = {
General = { General = {
SERVICE_URL = mkOption { SERVICE_URL = mkOption {
type = types.str; type = types.singleLineStr;
example = "https://www.example.com"; example = "https://www.example.com";
description = '' description = ''
Seahub public URL. Seahub public URL.
@ -78,11 +103,17 @@ in
''; '';
}; };
host = mkOption { host = mkOption {
type = types.str; type = types.singleLineStr;
default = "127.0.0.1"; default = "ipv4:127.0.0.1";
example = "0.0.0.0"; example = "unix:/run/seafile/server.sock";
description = '' 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:<ipv6addr> for binding to an IPv6 address.
- unix:<named pipe> for binding to a unix named socket
- ipv4:<ipv4addr> 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 { workers = mkOption {
type = types.int; type = types.int;
default = 4; default = 4;
@ -107,7 +151,7 @@ in
adminEmail = mkOption { adminEmail = mkOption {
example = "john@example.com"; example = "john@example.com";
type = types.str; type = types.singleLineStr;
description = '' description = ''
Seafile Seahub Admin Account Email. Seafile Seahub Admin Account Email.
''; '';
@ -115,45 +159,79 @@ in
initialAdminPassword = mkOption { initialAdminPassword = mkOption {
example = "someStrongPass"; example = "someStrongPass";
type = types.str; type = types.singleLineStr;
description = '' description = ''
Seafile Seahub Admin Account initial password. Seafile Seahub Admin Account initial password.
Should be changed via Seahub web front-end. Should be changed via Seahub web front-end.
''; '';
}; };
seafilePackage = mkPackageOption pkgs "seafile-server" { }; seahubPackage = mkPackageOption pkgs "seahub" { };
user = mkOption { user = mkOption {
type = types.str; type = types.singleLineStr;
default = defaultUser; default = defaultUser;
description = "User account under which seafile runs."; description = "User account under which seafile runs.";
}; };
group = mkOption { group = mkOption {
type = types.str; type = types.singleLineStr;
default = defaultUser; default = defaultUser;
description = "Group under which seafile runs."; description = "Group under which seafile runs.";
}; };
dataDir = mkOption { dataDir = mkOption {
type = types.nullOr types.path; type = types.path;
default = null; default = "${seafRoot}/data";
defaultText = "${seafRoot}/data";
description = "Path in which to store user data"; description = "Path in which to store user data";
}; };
gcDates = mkOption { gc = {
type = types.listOf types.str; enable = mkEnableOption "automatic garbage collection on stored data blocks";
default = [ ];
example = [ "Sun 03:00:00" ]; dates = mkOption {
description = '' type = types.listOf types.singleLineStr;
When to run garbage collection on stored data blocks. 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 { seahubExtraConf = mkOption {
default = ""; default = "";
example = ''
CSRF_TRUSTED_ORIGINS = ["https://example.com"]
'';
type = types.lines; type = types.lines;
description = '' description = ''
Extra config to append to `seahub_settings.py` file. Extra config to append to `seahub_settings.py` file.
@ -165,7 +243,26 @@ in
###### Implementation ###### 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/ccnet.conf".source = ccnetConf;
environment.etc."seafile/seafile.conf".source = seafileConf; environment.etc."seafile/seafile.conf".source = seafileConf;
@ -178,9 +275,7 @@ in
}; };
}; };
users.groups = lib.optionalAttrs (cfg.group == defaultUser) { users.groups = lib.optionalAttrs (cfg.group == defaultUser) { "${defaultUser}" = { }; };
"${defaultUser}" = {};
};
systemd.targets.seafile = { systemd.targets.seafile = {
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
@ -189,7 +284,7 @@ in
systemd.services = systemd.services =
let let
securityOptions = { serviceOptions = {
ProtectHome = true; ProtectHome = true;
PrivateUsers = true; PrivateUsers = true;
PrivateDevices = true; PrivateDevices = true;
@ -210,34 +305,42 @@ in
NoNewPrivileges = true; NoNewPrivileges = true;
MemoryDenyWriteExecute = true; MemoryDenyWriteExecute = true;
SystemCallArchitectures = "native"; 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 in
{ {
seaf-server = { seaf-server = {
description = "Seafile server"; description = "Seafile server";
partOf = [ "seafile.target" ]; partOf = [ "seafile.target" ];
after = [ "network.target" ]; unitConfig.RequiresMountsFor = lib.lists.optional (cfg.dataDir != "${seafRoot}/data") cfg.dataDir;
unitConfig.RequiresMountsFor = optionalDataDir; requires = [ "mysql.service" ];
after = [
"network.target"
"mysql.service"
];
wantedBy = [ "seafile.target" ]; wantedBy = [ "seafile.target" ];
restartTriggers = [ ccnetConf seafileConf ]; restartTriggers = [
path = [ pkgs.sqlite ]; ccnetConf
serviceConfig = securityOptions // { seafileConf
User = cfg.user; ];
Group = cfg.group; serviceConfig = serviceOptions // {
StateDirectory = "seafile";
RuntimeDirectory = "seafile";
LogsDirectory = "seafile";
ConfigurationDirectory = "seafile";
ReadWritePaths = optionalDataDir;
ExecStart = '' ExecStart = ''
${cfg.seafilePackage}/bin/seaf-server \ ${lib.getExe cfg.seahubPackage.seafile-server} \
--foreground \ --foreground \
-F /etc/seafile \ -F /etc/seafile \
-c ${ccnetDir} \ -c ${ccnetDir} \
-d ${dataDir} \ -d ${cfg.dataDir} \
-l /var/log/seafile/server.log \ -l /var/log/seafile/server.log \
-P /run/seafile/server.pid \ -P /run/seafile/server.pid \
-p /run/seafile -p /run/seafile
@ -245,118 +348,162 @@ in
}; };
preStart = '' preStart = ''
if [ ! -f "${seafRoot}/server-setup" ]; then if [ ! -f "${seafRoot}/server-setup" ]; then
mkdir -p ${dataDir}/library-template mkdir -p ${cfg.dataDir}/library-template
mkdir -p ${ccnetDir}/{GroupMgr,misc,OrgMgr,PeerMgr} # Load schema on first install
sqlite3 ${ccnetDir}/GroupMgr/groupmgr.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/groupmgr.sql" ${pkgs.mariadb.client}/bin/mysql --database=ccnet_db < ${cfg.seahubPackage.seafile-server}/share/seafile/sql/mysql/ccnet.sql
sqlite3 ${ccnetDir}/misc/config.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/config.sql" ${pkgs.mariadb.client}/bin/mysql --database=seafile_db < ${cfg.seahubPackage.seafile-server}/share/seafile/sql/mysql/seafile.sql
sqlite3 ${ccnetDir}/OrgMgr/orgmgr.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/org.sql" echo "${cfg.seahubPackage.seafile-server.version}-mysql" > "${seafRoot}"/server-setup
sqlite3 ${ccnetDir}/PeerMgr/usermgr.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/user.sql" echo Loaded MySQL schemas for first install
sqlite3 ${dataDir}/seafile.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/seafile.sql"
echo "${cfg.seafilePackage.version}-sqlite" > "${seafRoot}"/server-setup
fi fi
# checking for upgrades and handling them # checking for upgrades and handling them
installedMajor=$(cat "${seafRoot}/server-setup" | cut -d"-" -f1 | cut -d"." -f1) installedMajor=$(cat "${seafRoot}/server-setup" | cut -d"-" -f1 | cut -d"." -f1)
installedMinor=$(cat "${seafRoot}/server-setup" | cut -d"-" -f1 | cut -d"." -f2) installedMinor=$(cat "${seafRoot}/server-setup" | cut -d"-" -f1 | cut -d"." -f2)
pkgMajor=$(echo "${cfg.seafilePackage.version}" | cut -d"." -f1) pkgMajor=$(echo "${cfg.seahubPackage.seafile-server.version}" | cut -d"." -f1)
pkgMinor=$(echo "${cfg.seafilePackage.version}" | cut -d"." -f2) pkgMinor=$(echo "${cfg.seahubPackage.seafile-server.version}" | cut -d"." -f2)
if [[ $installedMajor == $pkgMajor && $installedMinor == $pkgMinor ]]; then if [[ $installedMajor == $pkgMajor && $installedMinor == $pkgMinor ]]; then
: :
elif [[ $installedMajor == 8 && $installedMinor == 0 && $pkgMajor == 9 && $pkgMinor == 0 ]]; then elif [[ $installedMajor == 10 && $installedMinor == 0 && $pkgMajor == 11 && $pkgMinor == 0 ]]; then
# Upgrade from 8.0 to 9.0 # Upgrade from 10.0 to 11.0: migrate to mysql
sqlite3 ${dataDir}/seafile.db ".read ${pkgs.seahub}/scripts/upgrade/sql/9.0.0/sqlite3/seafile.sql" echo Migrating from version 10 to 11
echo "${cfg.seafilePackage.version}-sqlite" > "${seafRoot}"/server-setup
elif [[ $installedMajor == 9 && $installedMinor == 0 && $pkgMajor == 10 && $pkgMinor == 0 ]]; then # From https://github.com/haiwen/seahub/blob/e12f941bfef7191795d8c72a7d339c01062964b2/scripts/sqlite2mysql.sh
# Upgrade from 9.0 to 10.0
sqlite3 ${dataDir}/seafile.db ".read ${pkgs.seahub}/scripts/upgrade/sql/10.0.0/sqlite3/seafile.sql" echo Migrating ccnet database to MySQL
echo "${cfg.seafilePackage.version}-sqlite" > "${seafRoot}"/server-setup ${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 else
echo "Unsupported upgrade" >&2 echo "Unsupported upgrade: $installedMajor.$installedMinor to $pkgMajor.$pkgMinor" >&2
exit 1 exit 1
fi 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 = { seahub = {
description = "Seafile Server Web Frontend"; description = "Seafile Server Web Frontend";
wantedBy = [ "seafile.target" ]; wantedBy = [ "seafile.target" ];
partOf = [ "seafile.target" ]; partOf = [ "seafile.target" ];
after = [ "network.target" "seaf-server.service" ]; unitConfig.RequiresMountsFor = lib.lists.optional (cfg.dataDir != "${seafRoot}/data") cfg.dataDir;
requires = [ "seaf-server.service" ]; requires = [
unitConfig.RequiresMountsFor = optionalDataDir; "mysql.service"
"seaf-server.service"
];
after = [
"network.target"
"mysql.service"
"seaf-server.service"
];
restartTriggers = [ seahubSettings ]; restartTriggers = [ seahubSettings ];
environment = { environment = {
PYTHONPATH = "${pkgs.seahub.pythonPath}:${pkgs.seahub}/thirdpart:${pkgs.seahub}"; PYTHONPATH = "${cfg.seahubPackage.pythonPath}:${cfg.seahubPackage}/thirdpart:${cfg.seahubPackage}";
DJANGO_SETTINGS_MODULE = "seahub.settings"; DJANGO_SETTINGS_MODULE = "seahub.settings";
CCNET_CONF_DIR = ccnetDir; CCNET_CONF_DIR = ccnetDir;
SEAFILE_CONF_DIR = dataDir; SEAFILE_CONF_DIR = cfg.dataDir;
SEAFILE_CENTRAL_CONF_DIR = "/etc/seafile"; SEAFILE_CENTRAL_CONF_DIR = "/etc/seafile";
SEAFILE_RPC_PIPE_PATH = "/run/seafile"; SEAFILE_RPC_PIPE_PATH = "/run/seafile";
SEAHUB_LOG_DIR = "/var/log/seafile"; SEAHUB_LOG_DIR = "/var/log/seafile";
}; };
serviceConfig = securityOptions // { serviceConfig = serviceOptions // {
User = cfg.user;
Group = cfg.group;
RuntimeDirectory = "seahub"; RuntimeDirectory = "seahub";
StateDirectory = "seafile";
LogsDirectory = "seafile";
ConfigurationDirectory = "seafile";
ReadWritePaths = optionalDataDir;
ExecStart = '' ExecStart = ''
${pkgs.seahub.python.pkgs.gunicorn}/bin/gunicorn seahub.wsgi:application \ ${lib.getExe cfg.seahubPackage.python3.pkgs.gunicorn} seahub.wsgi:application \
--name seahub \ --name seahub \
--workers ${toString cfg.workers} \ --workers ${toString cfg.workers} \
--log-level=info \ --log-level=info \
--preload \ --preload \
--timeout=1200 \ --timeout=1200 \
--limit-request-line=8190 \ --limit-request-line=8190 \
--bind unix:/run/seahub/gunicorn.sock --bind ${cfg.seahubAddress}
''; '';
}; };
preStart = '' preStart = ''
mkdir -p ${seahubDir}/media mkdir -p ${seahubDir}/media
# Link all media except avatars # 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/ ln -sf $m ${seahubDir}/media/
done done
if [ ! -e "${seafRoot}/.seahubSecret" ]; then 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 fi
if [ ! -f "${seafRoot}/seahub-setup" ]; then if [ ! -f "${seafRoot}/seahub-setup" ]; then
# avatars directory should be writable # avatars directory should be writable
install -D -t ${seahubDir}/media/avatars/ ${pkgs.seahub}/media/avatars/default.png install -D -t ${seahubDir}/media/avatars/ ${cfg.seahubPackage}/media/avatars/default.png
install -D -t ${seahubDir}/media/avatars/groups ${pkgs.seahub}/media/avatars/groups/default.png install -D -t ${seahubDir}/media/avatars/groups ${cfg.seahubPackage}/media/avatars/groups/default.png
# init database # init database
${pkgs.seahub}/manage.py migrate ${cfg.seahubPackage}/manage.py migrate
# create admin account # 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."' ${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 "${pkgs.seahub.version}-sqlite" > "${seafRoot}/seahub-setup" echo "${cfg.seahubPackage.version}-mysql" > "${seafRoot}/seahub-setup"
fi fi
if [ $(cat "${seafRoot}/seahub-setup" | cut -d"-" -f1) != "${pkgs.seahub.version}" ]; then if [ $(cat "${seafRoot}/seahub-setup" | cut -d"-" -f1) != "${pkgs.seahub.version}" ]; then
# update database # run django migrations
${pkgs.seahub}/manage.py migrate ${cfg.seahubPackage}/manage.py migrate
echo "${pkgs.seahub.version}-sqlite" > "${seafRoot}/seahub-setup" echo "${cfg.seahubPackage.version}-mysql" > "${seafRoot}/seahub-setup"
fi fi
''; '';
}; };
seaf-gc = { seaf-gc = {
description = "Seafile storage garbage collection"; description = "Seafile storage garbage collection";
conflicts = [ "seaf-server.service" "seahub.service" ]; conflicts = [
after = [ "seaf-server.service" "seahub.service" ]; "seaf-server.service"
unitConfig.RequiresMountsFor = optionalDataDir; "seahub.service"
onSuccess = [ "seaf-server.service" "seahub.service" ]; ];
onFailure = [ "seaf-server.service" "seahub.service" ]; after = [
startAt = cfg.gcDates; "seaf-server.service"
serviceConfig = securityOptions // { "seahub.service"
User = cfg.user; ];
Group = cfg.group; unitConfig.RequiresMountsFor = lib.lists.optional (cfg.dataDir != "${seafRoot}/data") cfg.dataDir;
StateDirectory = "seafile"; onSuccess = [
RuntimeDirectory = "seafile"; "seaf-server.service"
LogsDirectory = "seafile"; "seahub.service"
ConfigurationDirectory = "seafile"; ];
ReadWritePaths = optionalDataDir; onFailure = [
"seaf-server.service"
"seahub.service"
];
startAt = lib.lists.optionals cfg.gc.enable cfg.gc.dates;
serviceConfig = serviceOptions // {
Type = "oneshot"; Type = "oneshot";
}; };
script = '' script = ''
@ -368,8 +515,8 @@ in
# checking for pending upgrades # checking for pending upgrades
installedMajor=$(cat "${seafRoot}/server-setup" | cut -d"-" -f1 | cut -d"." -f1) installedMajor=$(cat "${seafRoot}/server-setup" | cut -d"-" -f1 | cut -d"." -f1)
installedMinor=$(cat "${seafRoot}/server-setup" | cut -d"-" -f1 | cut -d"." -f2) installedMinor=$(cat "${seafRoot}/server-setup" | cut -d"-" -f1 | cut -d"." -f2)
pkgMajor=$(echo "${cfg.seafilePackage.version}" | cut -d"." -f1) pkgMajor=$(echo "${cfg.seahubPackage.seafile-server.version}" | cut -d"." -f1)
pkgMinor=$(echo "${cfg.seafilePackage.version}" | cut -d"." -f2) pkgMinor=$(echo "${cfg.seahubPackage.seafile-server.version}" | cut -d"." -f2)
if [[ $installedMajor != $pkgMajor || $installedMinor != $pkgMinor ]]; then if [[ $installedMajor != $pkgMajor || $installedMinor != $pkgMinor ]]; then
echo "Server not upgraded yet" >&2 echo "Server not upgraded yet" >&2
@ -377,15 +524,25 @@ in
fi fi
# Clean up user-deleted blocks and libraries # Clean up user-deleted blocks and libraries
${cfg.seafilePackage}/bin/seafserv-gc \ ${cfg.seahubPackage.seafile-server}/bin/seafserv-gc \
-F /etc/seafile \ -F /etc/seafile \
-c ${ccnetDir} \ -c ${ccnetDir} \
-d ${dataDir} \ -d ${cfg.dataDir} \
--rm-fs --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
];
} }

View File

@ -14,6 +14,7 @@ import ./make-test-python.nix ({ pkgs, ... }:
services.seafile = { services.seafile = {
enable = true; enable = true;
ccnetSettings.General.SERVICE_URL = "http://server"; ccnetSettings.General.SERVICE_URL = "http://server";
seafileSettings.fileserver.host = "unix:/run/seafile/server.sock";
adminEmail = "admin@example.com"; adminEmail = "admin@example.com";
initialAdminPassword = "seafile_password"; initialAdminPassword = "seafile_password";
}; };
@ -22,7 +23,7 @@ import ./make-test-python.nix ({ pkgs, ... }:
virtualHosts."server" = { virtualHosts."server" = {
locations."/".proxyPass = "http://unix:/run/seahub/gunicorn.sock"; locations."/".proxyPass = "http://unix:/run/seahub/gunicorn.sock";
locations."/seafhttp" = { locations."/seafhttp" = {
proxyPass = "http://127.0.0.1:8082"; proxyPass = "http://unix:/run/seafile/server.sock";
extraConfig = '' extraConfig = ''
rewrite ^/seafhttp(.*)$ $1 break; rewrite ^/seafhttp(.*)$ $1 break;
client_max_body_size 0; client_max_body_size 0;

View File

@ -52,7 +52,7 @@ python3.pkgs.buildPythonApplication rec {
markdown markdown
bleach bleach
(seafile-server.override { inherit python3; }) (python3.pkgs.toPythonModule (seafile-server.override { inherit python3; }))
]; ];
postPatch = '' postPatch = ''
@ -67,7 +67,7 @@ python3.pkgs.buildPythonApplication rec {
passthru = { passthru = {
inherit python3; inherit python3;
pythonPath = "${python3.pkgs.makePythonPath propagatedBuildInputs}:${seafile-server}/${python3.sitePackages}"; pythonPath = python3.pkgs.makePythonPath propagatedBuildInputs;
tests = { tests = {
inherit (nixosTests) seafile; inherit (nixosTests) seafile;
}; };