{ 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";
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 = {};
};
};
}