From b15113cbcec484dd1037860d01baf42363e8fc6d Mon Sep 17 00:00:00 2001 From: networkException Date: Sun, 22 Dec 2024 20:10:14 +0100 Subject: [PATCH] nixos/nextcloud: format with nixfmt-rfc-style --- nixos/modules/services/web-apps/nextcloud.nix | 876 ++++++++++-------- 1 file changed, 511 insertions(+), 365 deletions(-) diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix index b6c1aa456f162..db0e6ba831481 100644 --- a/nixos/modules/services/web-apps/nextcloud.nix +++ b/nixos/modules/services/web-apps/nextcloud.nix @@ -1,4 +1,9 @@ -{ config, lib, pkgs, ... }: +{ + config, + lib, + pkgs, + ... +}: with lib; @@ -6,7 +11,7 @@ let cfg = config.services.nextcloud; fpm = config.services.phpfpm.pools.nextcloud; - jsonFormat = pkgs.formats.json {}; + jsonFormat = pkgs.formats.json { }; defaultPHPSettings = { output_buffering = "0"; @@ -32,8 +37,9 @@ let # apps installed via cfg.extraApps nix-apps = { enabled = cfg.extraApps != { }; - linkTarget = pkgs.linkFarm "nix-apps" - (mapAttrsToList (name: path: { inherit name path; }) cfg.extraApps); + linkTarget = pkgs.linkFarm "nix-apps" ( + mapAttrsToList (name: path: { inherit name path; }) cfg.extraApps + ); writable = false; }; # apps installed via the app store. @@ -44,27 +50,41 @@ let }; }; - webroot = pkgs.runCommand "${cfg.package.name or "nextcloud"}-with-apps" { - preferLocalBuild = true; - } '' - mkdir $out - ln -sfv "${cfg.package}"/* "$out" - ${concatStrings - (mapAttrsToList (name: store: optionalString (store.enabled && store?linkTarget) '' - if [ -e "$out"/${name} ]; then - echo "Didn't expect ${name} already in $out!" - exit 1 - fi - ln -sfTv ${store.linkTarget} "$out"/${name} - '') appStores)} - ''; + webroot = + pkgs.runCommand "${cfg.package.name or "nextcloud"}-with-apps" + { + preferLocalBuild = true; + } + '' + mkdir $out + ln -sfv "${cfg.package}"/* "$out" + ${concatStrings ( + mapAttrsToList ( + name: store: + optionalString (store.enabled && store ? linkTarget) '' + if [ -e "$out"/${name} ]; then + echo "Didn't expect ${name} already in $out!" + exit 1 + fi + ln -sfTv ${store.linkTarget} "$out"/${name} + '' + ) appStores + )} + ''; inherit (cfg) datadir; phpPackage = cfg.phpPackage.buildEnv { - extensions = { enabled, all }: - (with all; enabled - ++ [ bz2 intl sodium ] # recommended + extensions = + { enabled, all }: + ( + with all; + enabled + ++ [ + bz2 + intl + sodium + ] # recommended ++ optional cfg.enableImagemagick imagick # Optionally enabled depending on caching settings ++ optional cfg.caching.apcu apcu @@ -76,64 +96,76 @@ let }; toKeyValue = generators.toKeyValue { - mkKeyValue = generators.mkKeyValueDefault {} " = "; + mkKeyValue = generators.mkKeyValueDefault { } " = "; }; - phpCli = concatStringsSep " " ([ - "${getExe phpPackage}" - ] ++ optionals (cfg.cli.memoryLimit != null) [ - "-dmemory_limit=${cfg.cli.memoryLimit}" - ]); + phpCli = concatStringsSep " " ( + [ + "${getExe phpPackage}" + ] + ++ optionals (cfg.cli.memoryLimit != null) [ + "-dmemory_limit=${cfg.cli.memoryLimit}" + ] + ); # NOTE: The credentials required by all services at runtime, not including things like the # admin password which is only needed by the setup service. - runtimeSystemdCredentials = [] + runtimeSystemdCredentials = + [ ] ++ (lib.optional (cfg.config.dbpassFile != null) "dbpass:${cfg.config.dbpassFile}") ++ (lib.optional (cfg.config.objectstore.s3.enable) "s3_secret:${cfg.config.objectstore.s3.secretFile}") - ++ (lib.optional (cfg.config.objectstore.s3.sseCKeyFile != null) "s3_sse_c_key:${cfg.config.objectstore.s3.sseCKeyFile}"); + ++ (lib.optional ( + cfg.config.objectstore.s3.sseCKeyFile != null + ) "s3_sse_c_key:${cfg.config.objectstore.s3.sseCKeyFile}"); requiresRuntimeSystemdCredentials = (lib.length runtimeSystemdCredentials) != 0; occ = pkgs.writeShellApplication { name = "nextcloud-occ"; - text = let - command = '' - ${lib.getExe' pkgs.coreutils "env"} \ - NEXTCLOUD_CONFIG_DIR="${datadir}/config" \ - ${phpCli} \ - occ "$@" + text = + let + command = '' + ${lib.getExe' pkgs.coreutils "env"} \ + NEXTCLOUD_CONFIG_DIR="${datadir}/config" \ + ${phpCli} \ + occ "$@" + ''; + in + '' + cd ${webroot} + + # NOTE: This is templated at eval time + requiresRuntimeSystemdCredentials=${lib.boolToString requiresRuntimeSystemdCredentials} + + # NOTE: This wrapper is both used in the internal nextcloud service units + # and by users outside a service context for administration. As such, + # when there's an existing CREDENTIALS_DIRECTORY, we inherit it for use + # in the nix_read_secret() php function. + # When there's no CREDENTIALS_DIRECTORY we try to use systemd-run to + # load the credentials just as in a service unit. + # NOTE: If there are no credentials that are required at runtime then there's no need + # to load any credentials. + if [[ $requiresRuntimeSystemdCredentials == true && -z "''${CREDENTIALS_DIRECTORY:-}" ]]; then + exec ${lib.getExe' config.systemd.package "systemd-run"} \ + ${ + lib.escapeShellArgs ( + map (credential: "--property=LoadCredential=${credential}") runtimeSystemdCredentials + ) + } \ + --uid=nextcloud \ + --same-dir \ + --pty \ + ${command} + elif [[ "$USER" != nextcloud ]]; then + exec /run/wrappers/bin/sudo \ + --preserve-env=CREDENTIALS_DIRECTORY \ + --user=nextcloud \ + ${command} + else + exec ${command} + fi ''; - in '' - cd ${webroot} - - # NOTE: This is templated at eval time - requiresRuntimeSystemdCredentials=${lib.boolToString requiresRuntimeSystemdCredentials} - - # NOTE: This wrapper is both used in the internal nextcloud service units - # and by users outside a service context for administration. As such, - # when there's an existing CREDENTIALS_DIRECTORY, we inherit it for use - # in the nix_read_secret() php function. - # When there's no CREDENTIALS_DIRECTORY we try to use systemd-run to - # load the credentials just as in a service unit. - # NOTE: If there are no credentials that are required at runtime then there's no need - # to load any credentials. - if [[ $requiresRuntimeSystemdCredentials == true && -z "''${CREDENTIALS_DIRECTORY:-}" ]]; then - exec ${lib.getExe' config.systemd.package "systemd-run"} \ - ${lib.escapeShellArgs (map (credential: "--property=LoadCredential=${credential}") runtimeSystemdCredentials)} \ - --uid=nextcloud \ - --same-dir \ - --pty \ - ${command} - elif [[ "$USER" != nextcloud ]]; then - exec /run/wrappers/bin/sudo \ - --preserve-env=CREDENTIALS_DIRECTORY \ - --user=nextcloud \ - ${command} - else - exec ${command} - fi - ''; }; inherit (config.system) stateVersion; @@ -145,97 +177,108 @@ let nextcloudOlderThan = versionOlder cfg.package.version; # https://github.com/nextcloud/documentation/pull/11179 - ocmProviderIsNotAStaticDirAnymore = nextcloudGreaterOrEqualThan "27.1.2" + ocmProviderIsNotAStaticDirAnymore = + nextcloudGreaterOrEqualThan "27.1.2" || (nextcloudOlderThan "27.0.0" && nextcloudGreaterOrEqualThan "26.0.8"); - overrideConfig = let - c = cfg.config; - requiresReadSecretFunction = c.dbpassFile != null || c.objectstore.s3.enable; - objectstoreConfig = let s3 = c.objectstore.s3; in optionalString s3.enable '' - 'objectstore' => [ - 'class' => '\\OC\\Files\\ObjectStore\\S3', - 'arguments' => [ - 'bucket' => '${s3.bucket}', - 'autocreate' => ${boolToString s3.autocreate}, - 'key' => '${s3.key}', - 'secret' => nix_read_secret('s3_secret'), - ${optionalString (s3.hostname != null) "'hostname' => '${s3.hostname}',"} - ${optionalString (s3.port != null) "'port' => ${toString s3.port},"} - 'use_ssl' => ${boolToString s3.useSsl}, - ${optionalString (s3.region != null) "'region' => '${s3.region}',"} - 'use_path_style' => ${boolToString s3.usePathStyle}, - ${optionalString (s3.sseCKeyFile != null) "'sse_c_key' => nix_read_secret('s3_sse_c_key'),"} - ], - ] - ''; - showAppStoreSetting = cfg.appstoreEnable != null || cfg.extraApps != {}; - renderedAppStoreSetting = - let - x = cfg.appstoreEnable; - in - if x == null then "false" - else boolToString x; - mkAppStoreConfig = name: { enabled, writable, ... }: optionalString enabled '' - [ 'path' => '${webroot}/${name}', 'url' => '/${name}', 'writable' => ${boolToString writable} ], - ''; - in pkgs.writeText "nextcloud-config.php" '' - [ + 'class' => '\\OC\\Files\\ObjectStore\\S3', + 'arguments' => [ + 'bucket' => '${s3.bucket}', + 'autocreate' => ${boolToString s3.autocreate}, + 'key' => '${s3.key}', + 'secret' => nix_read_secret('s3_secret'), + ${optionalString (s3.hostname != null) "'hostname' => '${s3.hostname}',"} + ${optionalString (s3.port != null) "'port' => ${toString s3.port},"} + 'use_ssl' => ${boolToString s3.useSsl}, + ${optionalString (s3.region != null) "'region' => '${s3.region}',"} + 'use_path_style' => ${boolToString s3.usePathStyle}, + ${optionalString (s3.sseCKeyFile != null) "'sse_c_key' => nix_read_secret('s3_sse_c_key'),"} + ], + ] + ''; + showAppStoreSetting = cfg.appstoreEnable != null || cfg.extraApps != { }; + renderedAppStoreSetting = + let + x = cfg.appstoreEnable; + in + if x == null then "false" else boolToString x; + mkAppStoreConfig = + name: + { enabled, writable, ... }: + optionalString enabled '' + [ 'path' => '${webroot}/${name}', 'url' => '/${name}', 'writable' => ${boolToString writable} ], + ''; + in + pkgs.writeText "nextcloud-config.php" '' + [ + ${concatStrings (mapAttrsToList mkAppStoreConfig appStores)} + ], + ${optionalString (showAppStoreSetting) "'appstoreenabled' => ${renderedAppStoreSetting},"} + ${optionalString cfg.caching.apcu "'memcache.local' => '\\OC\\Memcache\\APCu',"} + ${optionalString (c.dbname != null) "'dbname' => '${c.dbname}',"} + ${optionalString (c.dbhost != null) "'dbhost' => '${c.dbhost}',"} + ${optionalString (c.dbuser != null) "'dbuser' => '${c.dbuser}',"} + ${optionalString (c.dbtableprefix != null) "'dbtableprefix' => '${toString c.dbtableprefix}',"} + ${optionalString (c.dbpassFile != null) "'dbpassword' => nix_read_secret('dbpass'),"} + 'dbtype' => '${c.dbtype}', + ${objectstoreConfig} + ]; - return $decoded; - } - $CONFIG = [ - 'apps_paths' => [ - ${concatStrings (mapAttrsToList mkAppStoreConfig appStores)} - ], - ${optionalString (showAppStoreSetting) "'appstoreenabled' => ${renderedAppStoreSetting},"} - ${optionalString cfg.caching.apcu "'memcache.local' => '\\OC\\Memcache\\APCu',"} - ${optionalString (c.dbname != null) "'dbname' => '${c.dbname}',"} - ${optionalString (c.dbhost != null) "'dbhost' => '${c.dbhost}',"} - ${optionalString (c.dbuser != null) "'dbuser' => '${c.dbuser}',"} - ${optionalString (c.dbtableprefix != null) "'dbtableprefix' => '${toString c.dbtableprefix}',"} - ${optionalString (c.dbpassFile != null) "'dbpassword' => nix_read_secret('dbpass'),"} - 'dbtype' => '${c.dbtype}', - ${objectstoreConfig} - ]; - - $CONFIG = array_replace_recursive($CONFIG, nix_decode_json_file( - "${jsonFormat.generate "nextcloud-settings.json" cfg.settings}", - "impossible: this should never happen (decoding generated settings file %s failed)" - )); - - ${optionalString (cfg.secretFile != null) '' $CONFIG = array_replace_recursive($CONFIG, nix_decode_json_file( - "${cfg.secretFile}", - "Cannot start Nextcloud, secrets file %s set by NixOS doesn't exist!" + "${jsonFormat.generate "nextcloud-settings.json" cfg.settings}", + "impossible: this should never happen (decoding generated settings file %s failed)" )); - ''} - ''; -in { + + ${optionalString (cfg.secretFile != null) '' + $CONFIG = array_replace_recursive($CONFIG, nix_decode_json_file( + "${cfg.secretFile}", + "Cannot start Nextcloud, secrets file %s set by NixOS doesn't exist!" + )); + ''} + ''; +in +{ imports = [ (mkRenamedOptionModule [ "services" "nextcloud" "cron" "memoryLimit" ] - [ "services" "nextcloud" "cli" "memoryLimit" ]) + [ "services" "nextcloud" "cli" "memoryLimit" ] + ) (mkRemovedOptionModule [ "services" "nextcloud" "enableBrokenCiphersForSSE" ] '' This option has no effect since there's no supported Nextcloud version packaged here using OpenSSL for RC4 SSE. @@ -244,22 +287,41 @@ in { Add port to services.nextcloud.config.dbhost instead. '') (mkRenamedOptionModule - [ "services" "nextcloud" "logLevel" ] [ "services" "nextcloud" "settings" "loglevel" ]) + [ "services" "nextcloud" "logLevel" ] + [ "services" "nextcloud" "settings" "loglevel" ] + ) + (mkRenamedOptionModule + [ "services" "nextcloud" "logType" ] + [ "services" "nextcloud" "settings" "log_type" ] + ) (mkRenamedOptionModule - [ "services" "nextcloud" "logType" ] [ "services" "nextcloud" "settings" "log_type" ]) + [ "services" "nextcloud" "config" "defaultPhoneRegion" ] + [ "services" "nextcloud" "settings" "default_phone_region" ] + ) (mkRenamedOptionModule - [ "services" "nextcloud" "config" "defaultPhoneRegion" ] [ "services" "nextcloud" "settings" "default_phone_region" ]) + [ "services" "nextcloud" "config" "overwriteProtocol" ] + [ "services" "nextcloud" "settings" "overwriteprotocol" ] + ) (mkRenamedOptionModule - [ "services" "nextcloud" "config" "overwriteProtocol" ] [ "services" "nextcloud" "settings" "overwriteprotocol" ]) + [ "services" "nextcloud" "skeletonDirectory" ] + [ "services" "nextcloud" "settings" "skeletondirectory" ] + ) (mkRenamedOptionModule - [ "services" "nextcloud" "skeletonDirectory" ] [ "services" "nextcloud" "settings" "skeletondirectory" ]) + [ "services" "nextcloud" "globalProfiles" ] + [ "services" "nextcloud" "settings" "profile.enabled" ] + ) (mkRenamedOptionModule - [ "services" "nextcloud" "globalProfiles" ] [ "services" "nextcloud" "settings" "profile.enabled" ]) + [ "services" "nextcloud" "config" "extraTrustedDomains" ] + [ "services" "nextcloud" "settings" "trusted_domains" ] + ) (mkRenamedOptionModule - [ "services" "nextcloud" "config" "extraTrustedDomains" ] [ "services" "nextcloud" "settings" "trusted_domains" ]) + [ "services" "nextcloud" "config" "trustedProxies" ] + [ "services" "nextcloud" "settings" "trusted_proxies" ] + ) (mkRenamedOptionModule - [ "services" "nextcloud" "config" "trustedProxies" ] [ "services" "nextcloud" "settings" "trusted_proxies" ]) - (mkRenamedOptionModule ["services" "nextcloud" "extraOptions" ] [ "services" "nextcloud" "settings" ]) + [ "services" "nextcloud" "extraOptions" ] + [ "services" "nextcloud" "settings" ] + ) ]; options.services.nextcloud = { @@ -302,7 +364,7 @@ in { version = "0.6.9"; }; } - ''; + ''; }; extraAppsEnable = mkOption { type = types.bool; @@ -331,7 +393,11 @@ in { package = mkOption { type = types.package; description = "Which package to use for the Nextcloud instance."; - relatedPackages = [ "nextcloud28" "nextcloud29" "nextcloud30" ]; + relatedPackages = [ + "nextcloud28" + "nextcloud29" + "nextcloud30" + ]; }; phpPackage = mkPackageOption pkgs "php" { example = "php82"; @@ -357,7 +423,7 @@ in { phpExtraExtensions = mkOption { type = with types; functionTo (listOf package); - default = all: []; + default = all: [ ]; defaultText = literalExpression "all: []"; description = '' Additional PHP extensions to use for Nextcloud. @@ -371,7 +437,12 @@ in { }; phpOptions = mkOption { - type = with types; attrsOf (oneOf [ str int ]); + type = + with types; + attrsOf (oneOf [ + str + int + ]); defaultText = literalExpression (generators.toPretty { } defaultPHPSettings); description = '' Options for PHP's php.ini file for nextcloud. @@ -403,7 +474,13 @@ in { }; poolSettings = mkOption { - type = with types; attrsOf (oneOf [ str int bool ]); + type = + with types; + attrsOf (oneOf [ + str + int + bool + ]); default = { "pm" = "dynamic"; "pm.max_children" = "32"; @@ -447,7 +524,11 @@ in { config = { dbtype = mkOption { - type = types.enum [ "sqlite" "pgsql" "mysql" ]; + type = types.enum [ + "sqlite" + "pgsql" + "mysql" + ]; default = "sqlite"; description = "Database type."; }; @@ -471,9 +552,12 @@ in { dbhost = mkOption { type = types.nullOr types.str; default = - if pgsqlLocal then "/run/postgresql" - else if mysqlLocal then "localhost:/run/mysqld/mysqld.sock" - else "localhost"; + if pgsqlLocal then + "/run/postgresql" + else if mysqlLocal then + "localhost:/run/mysqld/mysqld.sock" + else + "localhost"; defaultText = "localhost"; example = "localhost:5000"; description = '' @@ -613,15 +697,17 @@ in { }; }; - enableImagemagick = mkEnableOption '' + enableImagemagick = + mkEnableOption '' the ImageMagick module for PHP. This is used by the theming app and for generating previews of certain images (e.g. SVG and HEIF). You may want to disable it for increased security. In that case, previews will still be available for some images (e.g. JPEG and PNG). See - '' // { - default = true; - }; + '' + // { + default = true; + }; configureRedis = lib.mkOption { type = lib.types.bool; @@ -712,7 +798,12 @@ in { ''; }; log_type = mkOption { - type = types.enum [ "errorlog" "file" "syslog" "systemd" ]; + type = types.enum [ + "errorlog" + "file" + "syslog" + "systemd" + ]; default = "syslog"; description = '' Logging backend to use. @@ -731,7 +822,7 @@ in { }; trusted_domains = mkOption { type = types.listOf types.str; - default = []; + default = [ ]; description = '' Trusted domains, from which the nextcloud installation will be accessible. You don't need to add @@ -740,14 +831,18 @@ in { }; trusted_proxies = mkOption { type = types.listOf types.str; - default = []; + default = [ ]; description = '' Trusted proxies, to provide if the nextcloud installation is being proxied to secure against e.g. spoofing. ''; }; overwriteprotocol = mkOption { - type = types.enum [ "" "http" "https" ]; + type = types.enum [ + "" + "http" + "https" + ]; default = ""; example = "https"; description = '' @@ -791,19 +886,20 @@ in { }; }; }; - default = {}; + default = { }; description = '' Extra options which should be appended to Nextcloud's config.php file. ''; - example = literalExpression '' { - redis = { - host = "/run/redis/redis.sock"; - port = 0; - dbindex = 0; - password = "secret"; - timeout = 1.5; - }; - } ''; + example = literalExpression '' + { + redis = { + host = "/run/redis/redis.sock"; + port = 0; + dbindex = 0; + password = "secret"; + timeout = 1.5; + }; + } ''; }; secretFile = mkOption { @@ -847,10 +943,11 @@ in { }; config = mkIf cfg.enable (mkMerge [ - { warnings = let - latest = 30; - upgradeWarning = major: nixos: - '' + { + warnings = + let + latest = 30; + upgradeWarning = major: nixos: '' A legacy Nextcloud install (from before NixOS ${nixos}) may be installed. After nextcloud${toString major} is installed successfully, you can safely upgrade @@ -863,7 +960,8 @@ in { `services.nextcloud.package`. ''; - in (optional (cfg.poolConfig != null) '' + in + (optional (cfg.poolConfig != null) '' Using config.services.nextcloud.poolConfig is deprecated and will become unsupported in a future release. Please migrate your configuration to config.services.nextcloud.poolSettings. '') @@ -880,22 +978,25 @@ in { ++ (optional (versionOlder cfg.package.version "29") (upgradeWarning 28 "24.11")) ++ (optional (versionOlder cfg.package.version "30") (upgradeWarning 29 "24.11")); - services.nextcloud.package = with pkgs; + services.nextcloud.package = + with pkgs; mkDefault ( - if pkgs ? nextcloud - then throw '' + if pkgs ? nextcloud then + throw '' The `pkgs.nextcloud`-attribute has been removed. If it's supposed to be the default nextcloud defined in an overlay, please set `services.nextcloud.package` to `pkgs.nextcloud`. '' - else if versionOlder stateVersion "24.05" then nextcloud27 - else if versionOlder stateVersion "24.11" then nextcloud29 - else nextcloud30 + else if versionOlder stateVersion "24.05" then + nextcloud27 + else if versionOlder stateVersion "24.11" then + nextcloud29 + else + nextcloud30 ); services.nextcloud.phpPackage = - if versionOlder cfg.package.version "29" then pkgs.php82 - else pkgs.php83; + if versionOlder cfg.package.version "29" then pkgs.php82 else pkgs.php83; services.nextcloud.phpOptions = mkMerge [ (mapAttrs (const mkOptionDefault) defaultPHPSettings) @@ -910,26 +1011,30 @@ in { ]; } - { assertions = [ - { assertion = cfg.database.createLocally -> cfg.config.dbpassFile == null; - message = '' - Using `services.nextcloud.database.createLocally` with database - password authentication is no longer supported. - - If you use an external database (or want to use password auth for any - other reason), set `services.nextcloud.database.createLocally` to - `false`. The database won't be managed for you (use `services.mysql` - if you want to set it up). - - If you want this module to manage your nextcloud database for you, - unset `services.nextcloud.config.dbpassFile` and - `services.nextcloud.config.dbhost` to use socket authentication - instead of password. - ''; - } - ]; } + { + assertions = [ + { + assertion = cfg.database.createLocally -> cfg.config.dbpassFile == null; + message = '' + Using `services.nextcloud.database.createLocally` with database + password authentication is no longer supported. + + If you use an external database (or want to use password auth for any + other reason), set `services.nextcloud.database.createLocally` to + `false`. The database won't be managed for you (use `services.mysql` + if you want to set it up). + + If you want this module to manage your nextcloud database for you, + unset `services.nextcloud.config.dbpassFile` and + `services.nextcloud.config.dbhost` to use socket authentication + instead of password. + ''; + } + ]; + } - { systemd.timers.nextcloud-cron = { + { + systemd.timers.nextcloud-cron = { wantedBy = [ "timers.target" ]; after = [ "nextcloud-setup.service" ]; timerConfig = { @@ -939,109 +1044,125 @@ in { }; }; - systemd.tmpfiles.rules = map (dir: "d ${dir} 0750 nextcloud nextcloud - -") [ - "${cfg.home}" - "${datadir}/config" - "${datadir}/data" - "${cfg.home}/store-apps" - ] ++ [ - "L+ ${datadir}/config/override.config.php - - - - ${overrideConfig}" - ]; + systemd.tmpfiles.rules = + map (dir: "d ${dir} 0750 nextcloud nextcloud - -") [ + "${cfg.home}" + "${datadir}/config" + "${datadir}/data" + "${cfg.home}/store-apps" + ] + ++ [ + "L+ ${datadir}/config/override.config.php - - - - ${overrideConfig}" + ]; systemd.services = { - nextcloud-setup = let - c = cfg.config; - occInstallCmd = let - mkExport = { arg, value }: '' - ${arg}=${value}; - export ${arg}; - ''; - dbpass = { - arg = "DBPASS"; - value = if c.dbpassFile != null - then ''"$(<"$CREDENTIALS_DIRECTORY/dbpass")"'' - else ''""''; - }; - adminpass = { - arg = "ADMINPASS"; - value = ''"$(<"$CREDENTIALS_DIRECTORY/adminpass")"''; - }; - installFlags = concatStringsSep " \\\n " - (mapAttrsToList (k: v: "${k} ${toString v}") { - "--database" = ''"${c.dbtype}"''; - # The following attributes are optional depending on the type of - # database. Those that evaluate to null on the left hand side - # will be omitted. - ${if c.dbname != null then "--database-name" else null} = ''"${c.dbname}"''; - ${if c.dbhost != null then "--database-host" else null} = ''"${c.dbhost}"''; - ${if c.dbuser != null then "--database-user" else null} = ''"${c.dbuser}"''; - "--database-pass" = "\"\$${dbpass.arg}\""; - "--admin-user" = ''"${c.adminuser}"''; - "--admin-pass" = "\"\$${adminpass.arg}\""; - "--data-dir" = ''"${datadir}/data"''; - }); - in '' - ${mkExport dbpass} - ${mkExport adminpass} - ${lib.getExe occ} maintenance:install \ - ${installFlags} - ''; - occSetTrustedDomainsCmd = concatStringsSep "\n" (imap0 - (i: v: '' - ${lib.getExe occ} config:system:set trusted_domains \ - ${toString i} --value="${toString v}" - '') ([ cfg.hostName ] ++ cfg.settings.trusted_domains)); - - in { - wantedBy = [ "multi-user.target" ]; - wants = [ "nextcloud-update-db.service" ]; - before = [ "phpfpm-nextcloud.service" ]; - after = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service"; - requires = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service"; - path = [ occ ]; - restartTriggers = [ overrideConfig ]; - script = '' - ${optionalString (c.dbpassFile != null) '' - if [ -z "$(<$CREDENTIALS_DIRECTORY/dbpass)" ]; then - echo "dbpassFile ${c.dbpassFile} is empty!" + nextcloud-setup = + let + c = cfg.config; + occInstallCmd = + let + mkExport = + { arg, value }: + '' + ${arg}=${value}; + export ${arg}; + ''; + dbpass = { + arg = "DBPASS"; + value = if c.dbpassFile != null then ''"$(<"$CREDENTIALS_DIRECTORY/dbpass")"'' else ''""''; + }; + adminpass = { + arg = "ADMINPASS"; + value = ''"$(<"$CREDENTIALS_DIRECTORY/adminpass")"''; + }; + installFlags = concatStringsSep " \\\n " ( + mapAttrsToList (k: v: "${k} ${toString v}") { + "--database" = ''"${c.dbtype}"''; + # The following attributes are optional depending on the type of + # database. Those that evaluate to null on the left hand side + # will be omitted. + ${if c.dbname != null then "--database-name" else null} = ''"${c.dbname}"''; + ${if c.dbhost != null then "--database-host" else null} = ''"${c.dbhost}"''; + ${if c.dbuser != null then "--database-user" else null} = ''"${c.dbuser}"''; + "--database-pass" = "\"\$${dbpass.arg}\""; + "--admin-user" = ''"${c.adminuser}"''; + "--admin-pass" = "\"\$${adminpass.arg}\""; + "--data-dir" = ''"${datadir}/data"''; + } + ); + in + '' + ${mkExport dbpass} + ${mkExport adminpass} + ${lib.getExe occ} maintenance:install \ + ${installFlags} + ''; + occSetTrustedDomainsCmd = concatStringsSep "\n" ( + imap0 (i: v: '' + ${lib.getExe occ} config:system:set trusted_domains \ + ${toString i} --value="${toString v}" + '') ([ cfg.hostName ] ++ cfg.settings.trusted_domains) + ); + + in + { + wantedBy = [ "multi-user.target" ]; + wants = [ "nextcloud-update-db.service" ]; + before = [ "phpfpm-nextcloud.service" ]; + after = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service"; + requires = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service"; + path = [ occ ]; + restartTriggers = [ overrideConfig ]; + script = '' + ${optionalString (c.dbpassFile != null) '' + if [ -z "$(<$CREDENTIALS_DIRECTORY/dbpass)" ]; then + echo "dbpassFile ${c.dbpassFile} is empty!" + exit 1 + fi + ''} + if [ -z "$(<$CREDENTIALS_DIRECTORY/adminpass)" ]; then + echo "adminpassFile ${c.adminpassFile} is empty!" exit 1 fi - ''} - if [ -z "$(<$CREDENTIALS_DIRECTORY/adminpass)" ]; then - echo "adminpassFile ${c.adminpassFile} is empty!" - exit 1 - fi - - ${concatMapStrings (name: '' - if [ -d "${cfg.home}"/${name} ]; then - echo "Cleaning up ${name}; these are now bundled in the webroot store-path!" - rm -r "${cfg.home}"/${name} - fi - '') [ "nix-apps" "apps" ]} - # Do not install if already installed - if [[ ! -e ${datadir}/config/config.php ]]; then - ${occInstallCmd} - fi + ${concatMapStrings + (name: '' + if [ -d "${cfg.home}"/${name} ]; then + echo "Cleaning up ${name}; these are now bundled in the webroot store-path!" + rm -r "${cfg.home}"/${name} + fi + '') + [ + "nix-apps" + "apps" + ] + } + + # Do not install if already installed + if [[ ! -e ${datadir}/config/config.php ]]; then + ${occInstallCmd} + fi - ${lib.getExe occ} upgrade + ${lib.getExe occ} upgrade - ${lib.getExe occ} config:system:delete trusted_domains + ${lib.getExe occ} config:system:delete trusted_domains - ${optionalString (cfg.extraAppsEnable && cfg.extraApps != { }) '' + ${optionalString (cfg.extraAppsEnable && cfg.extraApps != { }) '' # Try to enable apps ${lib.getExe occ} app:enable ${concatStringsSep " " (attrNames cfg.extraApps)} - ''} + ''} - ${occSetTrustedDomainsCmd} - ''; - serviceConfig.Type = "oneshot"; - serviceConfig.User = "nextcloud"; - serviceConfig.LoadCredential = [ "adminpass:${cfg.config.adminpassFile}" ] ++ runtimeSystemdCredentials; - # On Nextcloud ≥ 26, it is not necessary to patch the database files to prevent - # an automatic creation of the database user. - environment.NC_setup_create_db_user = lib.mkIf (nextcloudGreaterOrEqualThan "26") "false"; - }; + ${occSetTrustedDomainsCmd} + ''; + serviceConfig.Type = "oneshot"; + serviceConfig.User = "nextcloud"; + serviceConfig.LoadCredential = [ + "adminpass:${cfg.config.adminpassFile}" + ] ++ runtimeSystemdCredentials; + # On Nextcloud ≥ 26, it is not necessary to patch the database files to prevent + # an automatic creation of the database user. + environment.NC_setup_create_db_user = lib.mkIf (nextcloudGreaterOrEqualThan "26") "false"; + }; nextcloud-cron = { after = [ "nextcloud-setup.service" ]; # FIXME: In contrast to the occ wrapper script running phpCli directly will not @@ -1091,27 +1212,32 @@ in { }; }; - phpfpm-nextcloud = { - # When upgrading the Nextcloud package, Nextcloud can report errors such as - # "The files of the app [all apps in /var/lib/nextcloud/apps] were not replaced correctly" - # Restarting phpfpm on Nextcloud package update fixes these issues (but this is a workaround). - restartTriggers = [ webroot overrideConfig ]; - } // lib.optionalAttrs requiresRuntimeSystemdCredentials { - serviceConfig.LoadCredential = runtimeSystemdCredentials; - - # FIXME: We use a hack to make the credential files readable by the nextcloud - # user by copying them somewhere else and overriding CREDENTIALS_DIRECTORY - # for php. This is currently necessary as the unit runs as root. - preStart = '' - umask 0077 - # NOTE: The phpfpm runtime directory is currently preserved - # between restarts. - rm -rf /run/phpfpm/nextcloud-credentials/ - mkdir -p /run/phpfpm/nextcloud-credentials/ - cp $CREDENTIALS_DIRECTORY/* /run/phpfpm/nextcloud-credentials/ - chown -R nextcloud:nextcloud /run/phpfpm/nextcloud-credentials/ - ''; - }; + phpfpm-nextcloud = + { + # When upgrading the Nextcloud package, Nextcloud can report errors such as + # "The files of the app [all apps in /var/lib/nextcloud/apps] were not replaced correctly" + # Restarting phpfpm on Nextcloud package update fixes these issues (but this is a workaround). + restartTriggers = [ + webroot + overrideConfig + ]; + } + // lib.optionalAttrs requiresRuntimeSystemdCredentials { + serviceConfig.LoadCredential = runtimeSystemdCredentials; + + # FIXME: We use a hack to make the credential files readable by the nextcloud + # user by copying them somewhere else and overriding CREDENTIALS_DIRECTORY + # for php. This is currently necessary as the unit runs as root. + preStart = '' + umask 0077 + # NOTE: The phpfpm runtime directory is currently preserved + # between restarts. + rm -rf /run/phpfpm/nextcloud-credentials/ + mkdir -p /run/phpfpm/nextcloud-credentials/ + cp $CREDENTIALS_DIRECTORY/* /run/phpfpm/nextcloud-credentials/ + chown -R nextcloud:nextcloud /run/phpfpm/nextcloud-credentials/ + ''; + }; }; services.phpfpm = { @@ -1124,10 +1250,12 @@ in { NEXTCLOUD_CONFIG_DIR = "${datadir}/config"; PATH = "/run/wrappers/bin:/nix/var/nix/profiles/default/bin:/run/current-system/sw/bin:/usr/bin:/bin"; }; - settings = mapAttrs (name: mkDefault) { - "listen.owner" = config.services.nginx.user; - "listen.group" = config.services.nginx.group; - } // cfg.poolSettings; + settings = + mapAttrs (name: mkDefault) { + "listen.owner" = config.services.nginx.user; + "listen.group" = config.services.nginx.group; + } + // cfg.poolSettings; extraConfig = cfg.poolConfig; }; }; @@ -1137,7 +1265,10 @@ in { group = "nextcloud"; isSystemUser = true; }; - users.groups.nextcloud.members = [ "nextcloud" config.services.nginx.user ]; + users.groups.nextcloud.members = [ + "nextcloud" + config.services.nginx.user + ]; environment.systemPackages = [ occ ]; @@ -1145,19 +1276,25 @@ in { enable = true; package = lib.mkDefault pkgs.mariadb; ensureDatabases = [ cfg.config.dbname ]; - ensureUsers = [{ - name = cfg.config.dbuser; - ensurePermissions = { "${cfg.config.dbname}.*" = "ALL PRIVILEGES"; }; - }]; + ensureUsers = [ + { + name = cfg.config.dbuser; + ensurePermissions = { + "${cfg.config.dbname}.*" = "ALL PRIVILEGES"; + }; + } + ]; }; services.postgresql = mkIf pgsqlLocal { enable = true; ensureDatabases = [ cfg.config.dbname ]; - ensureUsers = [{ - name = cfg.config.dbuser; - ensureDBOwnership = true; - }]; + ensureUsers = [ + { + name = cfg.config.dbuser; + ensureDBOwnership = true; + } + ]; }; services.redis.servers.nextcloud = lib.mkIf cfg.configureRedis { @@ -1167,17 +1304,20 @@ in { services.nextcloud = { caching.redis = lib.mkIf cfg.configureRedis true; - settings = mkMerge [({ - datadirectory = lib.mkDefault "${datadir}/data"; - trusted_domains = [ cfg.hostName ]; - }) (lib.mkIf cfg.configureRedis { - "memcache.distributed" = ''\OC\Memcache\Redis''; - "memcache.locking" = ''\OC\Memcache\Redis''; - redis = { - host = config.services.redis.servers.nextcloud.unixSocket; - port = 0; - }; - })]; + settings = mkMerge [ + ({ + datadirectory = lib.mkDefault "${datadir}/data"; + trusted_domains = [ cfg.hostName ]; + }) + (lib.mkIf cfg.configureRedis { + "memcache.distributed" = ''\OC\Memcache\Redis''; + "memcache.locking" = ''\OC\Memcache\Redis''; + redis = { + host = config.services.redis.servers.nextcloud.unixSocket; + port = 0; + }; + }) + ]; }; services.nginx.enable = mkDefault true; @@ -1232,7 +1372,9 @@ in { priority = 500; extraConfig = '' # legacy support (i.e. static files and directories in cfg.package) - rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[s${optionalString (!ocmProviderIsNotAStaticDirAnymore) "m"}]-provider\/.+|.+\/richdocumentscode\/proxy) /index.php$request_uri; + rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[s${ + optionalString (!ocmProviderIsNotAStaticDirAnymore) "m" + }]-provider\/.+|.+\/richdocumentscode\/proxy) /index.php$request_uri; include ${config.services.nginx.package}/conf/fastcgi.conf; fastcgi_split_path_info ^(.+?\.php)(\\/.*)$; set $path_info $fastcgi_path_info; @@ -1248,21 +1390,25 @@ in { fastcgi_read_timeout ${builtins.toString cfg.fastcgiTimeout}s; ''; }; - "~ \\.(?:css|js|mjs|svg|gif|png|jpg|jpeg|ico|wasm|tflite|map|html|ttf|bcmap|mp4|webm|ogg|flac)$".extraConfig = '' - try_files $uri /index.php$request_uri; - expires 6M; - access_log off; - location ~ \.mjs$ { - default_type text/javascript; - } - location ~ \.wasm$ { - default_type application/wasm; - } - ''; - "~ ^\\/(?:updater|ocs-provider${optionalString (!ocmProviderIsNotAStaticDirAnymore) "|ocm-provider"})(?:$|\\/)".extraConfig = '' - try_files $uri/ =404; - index index.php; - ''; + "~ \\.(?:css|js|mjs|svg|gif|png|jpg|jpeg|ico|wasm|tflite|map|html|ttf|bcmap|mp4|webm|ogg|flac)$".extraConfig = + '' + try_files $uri /index.php$request_uri; + expires 6M; + access_log off; + location ~ \.mjs$ { + default_type text/javascript; + } + location ~ \.wasm$ { + default_type application/wasm; + } + ''; + "~ ^\\/(?:updater|ocs-provider${ + optionalString (!ocmProviderIsNotAStaticDirAnymore) "|ocm-provider" + })(?:$|\\/)".extraConfig = + '' + try_files $uri/ =404; + index index.php; + ''; "/remote" = { priority = 1500; extraConfig = ''