diff --git a/mod-seafile-server.nix b/mod-seafile-server.nix
index f5ae079..68697ae 100644
--- a/mod-seafile-server.nix
+++ b/mod-seafile-server.nix
@@ -2,40 +2,6 @@
with lib;
let
cfg = config.services.seafile-server;
- seafileConfigFile = pkgs.writeText "seafile.conf"
- (generators.toINI {} cfg.seafileSettings);
- ccnetConfigFile = pkgs.writeText "ccnet.conf"
- (generators.toINI {} cfg.ccnetSettings);
- gunicornConfigFile = pkgs.writeText "gunicorn.conf.py"
- ''
- import os
- daemon = True
- workers = 5
- # default localhost:8000
- bind = "127.0.0.1:8000"
- # Pid
- pids_dir = '${cfg.storagePath}/pids'
- pidfile = os.path.join(pids_dir, 'seahub.pid')
- # for file upload, we need a longer timeout value (default is only 30s, too short)
- timeout = 1200
- limit_request_line = 8190
- '';
- seahubConfigFile = pkgs.writeText "seahub_settings.py"
- ''
- SECRET_KEY = #seckey#
-
- DATABASES = {
- 'default': {
- 'ENGINE': 'django.db.backends.${if cfg.db.type == "mysql" then
- "mysql" else abort "invalid db type"}',
- 'NAME': '${cfg.db.dbnameSeahub}',
- 'USER': '${cfg.db.user}',
- 'PASSWORD': '#dbpass#',
- 'HOST': '${cfg.db.host}',
- 'PORT': '${toString cfg.db.port}'
- }
- }
- '';
# fix permissions at start
in
{
@@ -46,20 +12,6 @@ in
default = "/srv/seafile";
description = "where to store uploaded file data";
};
- ccnetSettings = mkOption {
- type = with types; attrsOf (attrsOf (oneOf [ bool int str ]));
- default = {};
- description = ''
- all possible ccnet.conf settings
- '';
- };
- seafileSettings = mkOption {
- type = with types; attrsOf (attrsOf (oneOf [ bool int str ]));
- default = {};
- description = ''
- all possible seafile.conf settings
- '';
- };
autorun = mkOption {
type = types.bool;
default = true;
@@ -76,40 +28,38 @@ in
default = "seafile";
description = "Database user name. Not required for sqlite.";
};
- dbnameSeafile = mkOption {
+ dbname = mkOption {
type = types.nullOr types.str;
default = "seafile";
- description = "Database name for Seafile server. Not required for sqlite.";
+ description = "Database name. Not required for sqlite.";
};
- dbnameCcnet = mkOption {
- type = types.nullOr types.str;
- default = "seafile";
- description = "Database name for Ccnet server. Not required for sqlite.";
- };
- dbnameSeahub = mkOption {
- type = types.nullOr types.str;
- default = "seafile";
- description = "Database name for Seahub web interface. Not required for sqlite.";
- };
- passwordFile = mkOption {
+ password = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
- The full path to a file that contains the database password.
- Not required for sqlite.
- '';
+ Database password. Use passwordFile to avoid this
+ being world-readable in the /nix/store.
+
+ Not required for sqlite.'';
+ };
+ passwordFile = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = ''
+ The full path to a file that contains the database password.
+ '';
+ };
+ host = mkOption {
+ type = types.nullOr types.str;
+ default = "localhost";
+ description = "Database host.";
+ };
+ dbport = mkOption {
+ type = with types; nullOr (either int str);
+ default = 3306;
+ description = "Database port. Not required for sqlite.";
+ };
};
- host = mkOption {
- type = types.nullOr types.str;
- default = "localhost";
- description = "Database host.";
- };
- port = mkOption {
- type = with types; nullOr (either int str);
- default = 3306;
- description = "Database port. Not required for sqlite.";
- };
- };
user = mkOption {
type = types.str;
@@ -123,11 +73,11 @@ in
description = "Group account under which the Seafile server runs.";
};
- name = mkOption {
- type = types.str;
- default = "Seafile";
- description = "name of the Seafile instance, will show up in client and web interface";
- };
+# name = mkOption {
+# type = types.str;
+# default = "Seafile";
+# description = "name of the Seafile instance, will show up in client and web interface";
+# };
domainName = mkOption {
type = types.str;
@@ -222,68 +172,16 @@ in
directoriesToManage = [ cfg.storagePath ];
in
mkIf cfg.enable {
- services.seafile-server.ccnetSettings = {
- # TODO: ID and NAME might be required
- General.SERVICE_URL="http${if cfg.enableTLS then "s" else ""}://${cfg.domainName}:${toString cfg.externalPort}/";
- Database = mkMerge [
- {
- ENGINE = cfg.db.type;
- }
- (mkIf (cfg.db.type == "mysql") {
- HOST = cfg.db.host;
- PORT = cfg.db.port;
- USER = cfg.db.user;
- CONNECTION_CHARSET = "utf8";
- DB = cfg.db.dbnameCcnet;
- password = "#dbpass#";
- })
- ];
-
- };
-
- services.seafile-server.seafileSettings = {
- library_trash.expire_days = cfg.trashExpirationTime;
- fileserver = {
- host = cfg.fileserverBindAddress;
- port = cfg.fileserverPort;
- worker_threads = cfg.fileserverWorkers;
- max_indexing_threads = cfg.fileserverIndexers;
- fixed_block_size = cfg.fileserverBlockSize;
- };
- quota = mkIf (! isNull cfg.defaultQuota) {
- default = cfg.defaultQuota;
- };
- history = mkIf (! isNull cfg.fileRevisionHistoryDays) {
- keep_days = cfg.fileRevisionHistoryDays;
- };
- database = mkMerge [
- {
- type = cfg.db.type;
- }
- # while just using the cfg.db set directly might be possible and
- # save lines of code, I prefer hand-picking options
- (mkIf (cfg.db.type == "mysql") {
- host = cfg.db.host;
- port = cfg.db.port;
- user = cfg.db.user;
- connection_charset = "utf8";
- db_name = cfg.db.dbnameSeafile;
- max_connections = 100;
- password = "#dbpass#";
- })
- ];
- };
-
systemd = {
# state directory permissions managed by systemd
tmpfiles.rules = [
"d ${cfg.storagePath} 0750 ${cfg.user} ${cfg.group} -"
- "d ${cfg.storagePath}/conf 0700 ${cfg.user} ${cfg.group} -"
- "d ${cfg.storagePath}/pids 0710 ${cfg.user} ${cfg.group} -"
+ "d ${cfg.storagePath}/conf 0750 ${cfg.user} ${cfg.group} -"
+ "d ${cfg.storagePath}/home 0710 ${cfg.user} ${cfg.group} -"
];
services.seafile-server = {
- path = with pkgs; [ seafile-server.seafile-server-core ];
+ path = with pkgs; [ seafile-server.ccnet-server seafile-server.seafile-server-core ];
script = ''
./seafile-server/seafile-server-latest/bin/seafile-admin start
'';
@@ -295,46 +193,66 @@ in
''}")
("${pkgs.writeShellScript "seafile-server-preStart-unprivileged" ''
# stuff run as seafile user
- set -ex
+
+ # ccnet-init must only be run once per installation, as it also generates stateful key and ID
+ # solution: invoke it once, use result as template
+ if [ ! -e ./ccnet/mykey.peer ]; then
+ ${pkgs.seafile-server.ccnet-server}/bin/ccnet-init -c ./ccnet -H 'TEMPLATEHOST'
+ mv ./ccnet/ccnet.conf{,.template}
+ fi
+
+ # generate actual ccnet config file
+ echo "[General]" > ./conf/ccnet.conf
+ grep "^ID =" ./ccnet/ccnet.conf.template >> ./conf/ccnet.conf
+ # outside URL
+ SERVICE_URL = http${if cfg.enableTLS then "s" else ""}://${cfg.domainName}:${toString cfg.externalPort}
# seafile.conf generation
- # move config templates from nix store
- ${pkgs.coreutils}/bin/install ${ccnetConfigFile} ./conf/ccnet.conf
- ${pkgs.coreutils}/bin/install ${seafileConfigFile} ./conf/seafile.conf
- ${pkgs.coreutils}/bin/install ${gunicornConfigFile} ./conf/gunicorn.conf.py
- ${pkgs.coreutils}/bin/install ${seahubConfigFile} ./conf/seahub_settings.py
+ echo '[library_trash]
+ expire_days ${toString cfg.trashExpirationTime}
- # seahub secret key
- if [ ! -e .seahubSecret ]; then
- ${pkgs.seafile-server.pythonEnv}/bin/python ${pkgs.seafile-server}/seahub/tools/secret_key_generator.py > .seahubSecret
- chmod 400 .seahubSecret
- fi
- SEAHUB_SECRET="$(head -n1 .seahubSecret)"
- # TODO: check for special characters needing to be escaped
- sed -e "s,#seckey#,$SEAHUB_SECRET,g" -i ./conf/seahub_settings.py
+ [fileserver]
+ host = ${cfg.fileserverBindAddress}
+ port = ${toString cfg.fileserverPort}
+ worker_threads = ${toString cfg.fileserverWorkers}
+ max_indexing_threads = ${toString cfg.fileserverIndexers}
+ fixed_block_size = ${toString cfg.fileserverIndexers}' > ./conf/seafile.conf
- # replace placeholder secrets with real secret read from file
- #TODO: unset -x to prevent DBPASS from being leaked in journal
- ${if !(isNull cfg.db.passwordFile) then ''
- DBPASS="$(head -n1 ${toString cfg.db.passwordFile})"
- sed -e "s,#dbpass#,$DBPASS,g" -i ./conf/seafile.conf ./conf/ccnet.conf ./conf/seahub_settings.py
- ''
- else ""
- }
-
-
- # initialise db and other things needed at first run
- if [ -e .initialised ]; then
- #TODO: db initialisation
-
- touch .initialised
+ if [ ${toString (! isNull cfg.defaultQuota)} ]; then
+ echo '[quota]' >> ./conf/seafile.conf
+ echo 'default = ${toString cfg.defaultQuota}' >> ./conf/seafile.conf
fi
- ln -nsf ${pkgs.seafile-server} seafile-server
-
- # for determining update version mismatches
- ${pkgs.coreutils}/bin/install ${pkgs.seafile-server}/installed_version .
+ if [ ${toString (! isNull cfg.fileRevisionHistoryDays)} ]; then
+ echo '[history]' >> ./conf/seafile.conf
+ echo 'keep_days = ${toString cfg.defaultQuota}' >> ./conf/seafile.conf
+ fi
+
+ # seafile database settings
+
+ if [ ${cfg.db.type} = "mysql" ]; then
+ echo '[database]
+ type = mysql
+ host = ${cfg.db.host}
+ port = ${toString cfg.db.dbport}
+ user = ${cfg.db.user}
+ connection_charset = utf8
+ db_name = ${cfg.db.dbname}
+ max_connections = 100' >> ./conf/seafile.conf
+
+ if [ ${toString (! isNull cfg.db.password)}; then
+ echo 'password = ${toString cfg.db.password}' >> ./conf/seafile.conf
+ else
+ echo "password = $(cat ${toString cfg.db.passwordFile})" >> ./conf/seafile.conf
+ fi
+ else
+ echo '[database]
+ type = sqlite' >> ./conf/seafile.conf
+ fi
+
+ ln -s ${pkgs.seafile-server} seafile-server
+ ./seafile-server/seafile-server-latest/bin/seafile-admin setup
''}")
];
User = cfg.user;
diff --git a/seafile-nixos-tests.nix b/seafile-nixos-tests.nix
index ed1122c..d1e404a 100644
--- a/seafile-nixos-tests.nix
+++ b/seafile-nixos-tests.nix
@@ -15,7 +15,7 @@ import () {
(import ./default.nix)
];
- console.keyMap = "de";
+ i18n.consoleKeyMap = "de";
users.mutableUsers = false;
users.users.test = {
isNormalUser = true;
diff --git a/seafile-server/default.nix b/seafile-server/default.nix
index 2f4d32b..02868fb 100644
--- a/seafile-server/default.nix
+++ b/seafile-server/default.nix
@@ -28,7 +28,7 @@
, python3Packages
}:
let
- version = "8.0.3";
+ version = "8.0.0";
python = python3;
pythonPackages = python3Packages;
django = pythonPackages.django;
@@ -49,8 +49,6 @@ let
pycryptodome
]
++ map (p: p.override { inherit django; }) djangoModules; # build django modules with required version
- # defining them here to be able to expose them in a python environment as well
- pythonEnvDeps = seahubPythonDependencies ++ [ libsearpc ];
seafile-server-core = stdenv.mkDerivation rec {
name = "seafile-server-core";
inherit version;
@@ -58,7 +56,7 @@ let
owner = "haiwen";
repo = "seafile-server";
rev = "v${version}-server";
- sha256 = "1wmbx4smf342b5pars1zm9af2i0yaq7kjj7ry0gr337gdpa4qn3b";
+ sha256 = "0pd1zjsw6lkpxd54ln0dz5r9zx9585nib10kvpl1vgzp61g4d223";
};
# patch to work with latest, non-vulnerable libevhtp
patches = [
@@ -68,14 +66,7 @@ let
# `which` is called directly from python during buildPhase, so we need the binary
nativeBuildInputs = [ autoconf automake libtool pkgconfig vala autoreconfHook which pythonPackages.wrapPython ];
buildInputs = [ sqlite glib python libuuid openssl oniguruma fuse libarchive libevent libevhtp ];
- propagatedBuildInputs = pythonEnvDeps;
- # copy manual to required location
- postInstall = ''
- mkdir $out/doc
- cp ${src}/doc/*.doc $out/doc/
- '';
- # prevent doc directory from being moved to share in fixupPhase
- forceShare = [ "man" "info" ];
+ propagatedBuildInputs = [ libsearpc ] ++ seahubPythonDependencies;
postFixup = ''
buildPythonPath $propagatedBuildInputs
wrapPythonProgramsIn "$out/bin" "$out $pythonPath"
@@ -94,7 +85,7 @@ let
owner = "haiwen";
repo = "seahub";
rev = "v${version}-server";
- sha256 = "0vfkiavsmpjm6wjr5rcnmnpnb3rxr3svwk8fsh5c76zg87ckdz4d";
+ sha256 = "0j7g43j7w1zb00pg4aaacdv5ycva3qf561hj9pbwh4709mbiykip";
};
phases = [ "unpackPhase" "installPhase" "fixupPhase" "distPhase" ];
buildInputs = [ python pythonPackages.wrapPython ];
@@ -129,40 +120,19 @@ stdenv.mkDerivation {
name = "seafile-server";
inherit version;
- nativeBuildInputs = [ python3Packages.wrapPython ];
buildInputs = [ seahub seafile-server-core libsearpc ]
++ lib.optional withMysql libmysqlclient;
phases = [ "installPhase" "fixupPhase" "distPhase" ];
- # create required directory structure
- # Which files need to be copied is specified in the function `copy_scripts_and_libs`
- # of ${seafile-server-core.src}/scripts/build/build-server.py
- # The install script below has been hand crafted from that list of files and needs to be updated on new releases.
+ # todo: create data directory in /srv in activation script
installPhase = ''
mkdir "$out"
cd "$out"
ln -s ${seahub} seahub
- ln -s ${seafile-server-core} seafile-server
- # copy general scripts
- cp ${seafile-server-core.src}/scripts/{setup-seafile.sh,setup-seafile-mysql.sh,setup-seafile-mysql.py,seafile.sh,seahub.sh,reset-admin.sh,seaf-fuse.sh,check_init_admin.py,seaf-gc.sh,seaf-fsck.sh} .
- # copy update scripts (and their sql)
- cp -r ${seafile-server-core.src}/scripts/upgrade .
- cp -r ${seafile-server-core.src}/scripts/sql .
- # copy_user_manual is already done in the postInstall hook of seafile-server-core
- # python admin scripts need to be made executable and patched with python path
- chmod ugo+x *.py
- buildPythonPath $propagatedBuildInputs
- wrapPythonProgramsIn "$out/*.py" "$out $pythonPath"
-
- echo -n "${version}" > installed_version
+ ln -s ${seafile-server-core} seafile-server-latest
'';
meta = with lib; {
maintainers = with maintainers; [ schmittlauch ];
license = licenses.free; # components with different free software licenses are combined
};
inherit seafile-server-core seahub;# for using the path in the NixOS module
-
- pythonEnv = python3.buildEnv.override {
- extraLibs = pythonEnvDeps;
- ignoreCollisions = true;
- };
}
diff --git a/seafile-test.nix b/seafile-test.nix
index 62aeded..cf624c2 100644
--- a/seafile-test.nix
+++ b/seafile-test.nix
@@ -10,13 +10,13 @@
(import ./default.nix)
];
- console.keyMap = "de";
+ i18n.consoleKeyMap = "de";
users.mutableUsers = false;
users.users.test = {
isNormalUser = true;
extraGroups = [ "wheel" ];
#hashedPassword = "$6$SZCzE/xB$Hr9sfsJ7xAcBCoptG39cxxQk8RZfldDjjGpSngOvn9Ufex5dHBEbdncXRZnfrGATsGcYPvLi7m4wIu.f8tY9B.";
- password = "test";
+ password = "";
home = "/home/test";
createHome = true;
};
@@ -25,33 +25,7 @@
services.seafile-server = {
enable = true;
#autorun = false;
- domainName = "seaf.local";
- db = {
- type = "mysql";
- passwordFile = toString (pkgs.writeText "testPW" "test");
- };
+ domainName = "localhost";
};
- # db backend
- services.mysql =
- {
- enable = true;
- package = pkgs.mariadb;
- ensureDatabases = [ "ccnet" "seafile" "seahub" ];
- ensureUsers = [
- rec {
- name = config.services.seafile-server.db.user;
- ensurePermissions = {
- "ccnet.*" = "ALL PRIVILEGES";
- "seafile.*" = "ALL PRIVILEGES";
- "seahub.*" = "ALL PRIVILEGES";
- };
- }
- ];
- # set a password for the seafile user
- initialScript = pkgs.writeText "mariadb-init.sql" ''
- CREATE USER ${config.services.seafile-server.db.user}@localhost IDENTIFIED VIA mysql_native_password USING PASSWORD("test");
- '';
- };
-
}