{ config, lib, pkgs, ... }: with lib; let cfg = config.services.tt-rss; configVersion = 26; cacheDir = "cache"; lockDir = "lock"; feedIconsDir = "feed-icons"; dbPort = if cfg.database.port == null then (if cfg.database.type == "pgsql" then 5432 else 3306) else cfg.database.port; poolName = "tt-rss"; phpfpmSocketName = "/run/phpfpm/${poolName}.sock"; tt-rss-config = pkgs.writeText "config.php" '' plugins.local directory. ''; }; themePackages = mkOption { type = types.listOf types.package; default = []; description = '' List of themes to install. The list elements are expected to be derivations. All elements in this derivation are automatically copied to the themes.local directory. ''; }; logDestination = mkOption { type = types.enum ["" "sql" "syslog"]; default = "sql"; description = '' Log destination to use. Possible values: sql (uses internal logging you can read in Preferences -> System), syslog - logs to system log. Setting this to blank uses PHP logging (usually to http server error.log). ''; }; extraConfig = mkOption { type = types.lines; default = ""; description = '' Additional lines to append to config.php. ''; }; }; }; imports = [ (mkRemovedOptionModule ["services" "tt-rss" "checkForUpdates"] '' This option was removed because setting this to true will cause TT-RSS to be unable to start if an automatic update of the code in services.tt-rss.root leads to a database schema upgrade that is not supported by the code active in the Nix store. '') ]; ###### implementation config = mkIf cfg.enable { assertions = [ { assertion = cfg.database.password != null -> cfg.database.passwordFile == null; message = "Cannot set both password and passwordFile"; } ]; services.phpfpm.pools = mkIf (cfg.pool == "${poolName}") { "${poolName}" = { listen = "/var/run/phpfpm/${poolName}.sock"; extraConfig = '' listen.owner = nginx listen.group = nginx listen.mode = 0600 user = ${cfg.user} pm = dynamic pm.max_children = 75 pm.start_servers = 10 pm.min_spare_servers = 5 pm.max_spare_servers = 20 pm.max_requests = 500 catch_workers_output = 1 ''; }; }; # NOTE: No configuration is done if not using virtual host services.nginx = mkIf (cfg.virtualHost != null) { enable = true; virtualHosts = { "${cfg.virtualHost}" = { root = "${cfg.root}"; locations."/" = { index = "index.php"; }; locations."~ \.php$" = { extraConfig = '' fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass unix:${config.services.phpfpm.pools.${cfg.pool}.listen}; fastcgi_index index.php; ''; }; }; }; }; systemd.services.tt-rss = let dbService = if cfg.database.type == "pgsql" then "postgresql.service" else "mysql.service"; in { description = "Tiny Tiny RSS feeds update daemon"; preStart = let callSql = e: if cfg.database.type == "pgsql" then '' ${optionalString (cfg.database.password != null) "PGPASSWORD=${cfg.database.password}"} \ ${optionalString (cfg.database.passwordFile != null) "PGPASSWORD=$(cat ${cfg.database.passwordFile})"} \ ${pkgs.sudo}/bin/sudo -u ${cfg.user} ${config.services.postgresql.package}/bin/psql \ -U ${cfg.database.user} \ ${optionalString (cfg.database.host != null) "-h ${cfg.database.host} --port ${toString dbPort}"} \ -c '${e}' \ ${cfg.database.name}'' else if cfg.database.type == "mysql" then '' echo '${e}' | ${pkgs.sudo}/bin/sudo -u ${cfg.user} ${config.services.mysql.package}/bin/mysql \ -u ${cfg.database.user} \ ${optionalString (cfg.database.password != null) "-p${cfg.database.password}"} \ ${optionalString (cfg.database.host != null) "-h ${cfg.database.host} -P ${toString dbPort}"} \ ${cfg.database.name}'' else ""; in '' rm -rf "${cfg.root}/*" mkdir -m 755 -p "${cfg.root}" cp -r "${pkgs.tt-rss}/"* "${cfg.root}" ${optionalString (cfg.pluginPackages != []) '' for plugin in ${concatStringsSep " " cfg.pluginPackages}; do cp -r "$plugin"/* "${cfg.root}/plugins.local/" done ''} ${optionalString (cfg.themePackages != []) '' for theme in ${concatStringsSep " " cfg.themePackages}; do cp -r "$theme"/* "${cfg.root}/themes.local/" done ''} ln -sf "${tt-rss-config}" "${cfg.root}/config.php" chown -R "${cfg.user}" "${cfg.root}" chmod -R 755 "${cfg.root}" '' + (optionalString (cfg.database.type == "pgsql") '' ${optionalString (cfg.database.host == null && cfg.database.password == null) '' if ! [ -e ${cfg.root}/.db-created ]; then ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} ${config.services.postgresql.package}/bin/createuser ${cfg.database.user} ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} ${config.services.postgresql.package}/bin/createdb -O ${cfg.database.user} ${cfg.database.name} touch ${cfg.root}/.db-created fi ''} exists=$(${callSql "select count(*) > 0 from pg_tables where tableowner = user"} \ | tail -n+3 | head -n-2 | sed -e 's/[ \n\t]*//') if [ "$exists" == 'f' ]; then ${callSql "\\i ${pkgs.tt-rss}/schema/ttrss_schema_${cfg.database.type}.sql"} else echo 'The database contains some data. Leaving it as it is.' fi; '') + (optionalString (cfg.database.type == "mysql") '' exists=$(${callSql "select count(*) > 0 from information_schema.tables where table_schema = schema()"} \ | tail -n+2 | sed -e 's/[ \n\t]*//') if [ "$exists" == '0' ]; then ${callSql "\\. ${pkgs.tt-rss}/schema/ttrss_schema_${cfg.database.type}.sql"} else echo 'The database contains some data. Leaving it as it is.' fi; ''); serviceConfig = { User = "${cfg.user}"; ExecStart = "${pkgs.php}/bin/php ${cfg.root}/update.php --daemon"; StandardOutput = "syslog"; StandardError = "syslog"; PermissionsStartOnly = true; }; wantedBy = [ "multi-user.target" ]; requires = ["${dbService}"]; after = ["network.target" "${dbService}"]; }; services.mysql = optionalAttrs (cfg.database.type == "mysql") { enable = true; package = mkDefault pkgs.mysql; ensureDatabases = [ cfg.database.name ]; ensureUsers = [ { name = cfg.user; ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; }; } ]; }; services.postgresql = optionalAttrs (cfg.database.type == "pgsql") { enable = mkDefault true; }; users = optionalAttrs (cfg.user == "tt_rss") { users.tt_rss = { description = "tt-rss service user"; isSystemUser = true; group = "tt_rss"; }; groups.tt_rss = {}; }; }; }