nixos/seafile: add persistent user, configurable storage path, gc service
This commit is contained in:
parent
90e07d3e55
commit
073099d0f7
@ -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.
|
||||||
|
@ -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
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user