at 16.09-beta 18 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4let 5 cfg = config.services.tt-rss; 6 7 configVersion = 26; 8 9 boolToString = b: if b then "true" else "false"; 10 11 cacheDir = "cache"; 12 lockDir = "lock"; 13 feedIconsDir = "feed-icons"; 14 15 dbPort = if cfg.database.port == null 16 then (if cfg.database.type == "pgsql" then 5432 else 3306) 17 else cfg.database.port; 18 19 poolName = "tt-rss"; 20 phpfpmSocketName = "/var/run/phpfpm/${poolName}.sock"; 21 virtualHostName = "tt-rss"; 22 23 tt-rss-config = pkgs.writeText "config.php" '' 24 <?php 25 26 define('PHP_EXECUTABLE', '${pkgs.php}/bin/php'); 27 28 define('LOCK_DIRECTORY', '${lockDir}'); 29 define('CACHE_DIR', '${cacheDir}'); 30 define('ICONS_DIR', '${feedIconsDir}'); 31 define('ICONS_URL', '${feedIconsDir}'); 32 define('SELF_URL_PATH', '${cfg.selfUrlPath}'); 33 34 define('MYSQL_CHARSET', 'UTF8'); 35 36 define('DB_TYPE', '${cfg.database.type}'); 37 define('DB_HOST', '${cfg.database.host}'); 38 define('DB_USER', '${cfg.database.user}'); 39 define('DB_NAME', '${cfg.database.name}'); 40 define('DB_PASS', '${escape ["'" "\\"] cfg.database.password}'); 41 define('DB_PORT', '${toString dbPort}'); 42 43 define('AUTH_AUTO_CREATE', ${boolToString cfg.auth.autoCreate}); 44 define('AUTH_AUTO_LOGIN', ${boolToString cfg.auth.autoLogin}); 45 46 define('FEED_CRYPT_KEY', '${escape ["'" "\\"] cfg.feedCryptKey}'); 47 48 49 define('SINGLE_USER_MODE', ${boolToString cfg.singleUserMode}); 50 51 define('SIMPLE_UPDATE_MODE', ${boolToString cfg.simpleUpdateMode}); 52 define('CHECK_FOR_UPDATES', ${boolToString cfg.checkForUpdates}); 53 54 define('FORCE_ARTICLE_PURGE', ${toString cfg.forceArticlePurge}); 55 define('SESSION_COOKIE_LIFETIME', ${toString cfg.sessionCookieLifetime}); 56 define('ENABLE_GZIP_OUTPUT', ${boolToString cfg.enableGZipOutput}); 57 58 define('PLUGINS', '${builtins.concatStringsSep "," cfg.plugins}'); 59 60 define('LOG_DESTINATION', '${cfg.logDestination}'); 61 define('CONFIG_VERSION', ${toString configVersion}); 62 63 64 define('PUBSUBHUBBUB_ENABLED', ${boolToString cfg.pubSubHubbub.enable}); 65 define('PUBSUBHUBBUB_HUB', '${cfg.pubSubHubbub.hub}'); 66 67 define('SPHINX_SERVER', '${cfg.sphinx.server}'); 68 define('SPHINX_INDEX', '${builtins.concatStringsSep "," cfg.sphinx.index}'); 69 70 define('ENABLE_REGISTRATION', ${boolToString cfg.registration.enable}); 71 define('REG_NOTIFY_ADDRESS', '${cfg.registration.notifyAddress}'); 72 define('REG_MAX_USERS', ${toString cfg.registration.maxUsers}); 73 74 define('SMTP_SERVER', '${cfg.email.server}'); 75 define('SMTP_LOGIN', '${cfg.email.login}'); 76 define('SMTP_PASSWORD', '${escape ["'" "\\"] cfg.email.password}'); 77 define('SMTP_SECURE', '${cfg.email.security}'); 78 79 define('SMTP_FROM_NAME', '${escape ["'" "\\"] cfg.email.fromName}'); 80 define('SMTP_FROM_ADDRESS', '${escape ["'" "\\"] cfg.email.fromAddress}'); 81 define('DIGEST_SUBJECT', '${escape ["'" "\\"] cfg.email.digestSubject}'); 82 ''; 83 84 in { 85 86 ###### interface 87 88 options = { 89 90 services.tt-rss = { 91 92 enable = mkEnableOption "tt-rss"; 93 94 user = mkOption { 95 type = types.str; 96 default = "nginx"; 97 example = "nginx"; 98 description = '' 99 User account under which both the service and the web-application run. 100 ''; 101 }; 102 103 pool = mkOption { 104 type = types.str; 105 default = "${poolName}"; 106 description = '' 107 Name of existing phpfpm pool that is used to run web-application. 108 If not specified a pool will be created automatically with 109 default values. 110 ''; 111 }; 112 113 # TODO: Re-enable after https://github.com/NixOS/nixpkgs/pull/15862 is merged 114 115 # virtualHost = mkOption { 116 # type = types.str; 117 # default = "${virtualHostName}"; 118 # description = '' 119 # Name of existing nginx virtual host that is used to run web-application. 120 # If not specified a host will be created automatically with 121 # default values. 122 # ''; 123 # }; 124 125 database = { 126 type = mkOption { 127 type = types.enum ["pgsql" "mysql"]; 128 default = "pgsql"; 129 description = '' 130 Database to store feeds. Supported are pgsql and mysql. 131 ''; 132 }; 133 134 host = mkOption { 135 type = types.str; 136 default = "localhost"; 137 description = '' 138 Host of the database. 139 ''; 140 }; 141 142 name = mkOption { 143 type = types.str; 144 default = "tt_rss"; 145 description = '' 146 Name of the existing database. 147 ''; 148 }; 149 150 user = mkOption { 151 type = types.str; 152 default = "tt_rss"; 153 description = '' 154 The database user. The user must exist and has access to 155 the specified database. 156 ''; 157 }; 158 159 password = mkOption { 160 type = types.nullOr types.str; 161 default = null; 162 description = '' 163 The database user's password. 164 ''; 165 }; 166 167 port = mkOption { 168 type = types.nullOr types.int; 169 default = null; 170 description = '' 171 The database's port. If not set, the default ports will be provided (5432 172 and 3306 for pgsql and mysql respectively). 173 ''; 174 }; 175 }; 176 177 auth = { 178 autoCreate = mkOption { 179 type = types.bool; 180 default = true; 181 description = '' 182 Allow authentication modules to auto-create users in tt-rss internal 183 database when authenticated successfully. 184 ''; 185 }; 186 187 autoLogin = mkOption { 188 type = types.bool; 189 default = true; 190 description = '' 191 Automatically login user on remote or other kind of externally supplied 192 authentication, otherwise redirect to login form as normal. 193 If set to true, users won't be able to set application language 194 and settings profile. 195 ''; 196 }; 197 }; 198 199 pubSubHubbub = { 200 hub = mkOption { 201 type = types.str; 202 default = ""; 203 description = '' 204 URL to a PubSubHubbub-compatible hub server. If defined, "Published 205 articles" generated feed would automatically become PUSH-enabled. 206 ''; 207 }; 208 209 enable = mkOption { 210 type = types.bool; 211 default = false; 212 description = '' 213 Enable client PubSubHubbub support in tt-rss. When disabled, tt-rss 214 won't try to subscribe to PUSH feed updates. 215 ''; 216 }; 217 }; 218 219 sphinx = { 220 server = mkOption { 221 type = types.str; 222 default = "localhost:9312"; 223 description = '' 224 Hostname:port combination for the Sphinx server. 225 ''; 226 }; 227 228 index = mkOption { 229 type = types.listOf types.str; 230 default = ["ttrss" "delta"]; 231 description = '' 232 Index names in Sphinx configuration. Example configuration 233 files are available on tt-rss wiki. 234 ''; 235 }; 236 }; 237 238 registration = { 239 enable = mkOption { 240 type = types.bool; 241 default = false; 242 description = '' 243 Allow users to register themselves. Please be aware that allowing 244 random people to access your tt-rss installation is a security risk 245 and potentially might lead to data loss or server exploit. Disabled 246 by default. 247 ''; 248 }; 249 250 notifyAddress = mkOption { 251 type = types.str; 252 default = ""; 253 description = '' 254 Email address to send new user notifications to. 255 ''; 256 }; 257 258 maxUsers = mkOption { 259 type = types.int; 260 default = 0; 261 description = '' 262 Maximum amount of users which will be allowed to register on this 263 system. 0 - no limit. 264 ''; 265 }; 266 }; 267 268 email = { 269 server = mkOption { 270 type = types.str; 271 default = ""; 272 example = "localhost:25"; 273 description = '' 274 Hostname:port combination to send outgoing mail. Blank - use system 275 MTA. 276 ''; 277 }; 278 279 login = mkOption { 280 type = types.str; 281 default = ""; 282 description = '' 283 SMTP authentication login used when sending outgoing mail. 284 ''; 285 }; 286 287 password = mkOption { 288 type = types.str; 289 default = ""; 290 description = '' 291 SMTP authentication password used when sending outgoing mail. 292 ''; 293 }; 294 295 security = mkOption { 296 type = types.enum ["" "ssl" "tls"]; 297 default = ""; 298 description = '' 299 Used to select a secure SMTP connection. Allowed values: ssl, tls, 300 or empty. 301 ''; 302 }; 303 304 fromName = mkOption { 305 type = types.str; 306 default = "Tiny Tiny RSS"; 307 description = '' 308 Name for sending outgoing mail. This applies to password reset 309 notifications, digest emails and any other mail. 310 ''; 311 }; 312 313 fromAddress = mkOption { 314 type = types.str; 315 default = ""; 316 description = '' 317 Address for sending outgoing mail. This applies to password reset 318 notifications, digest emails and any other mail. 319 ''; 320 }; 321 322 digestSubject = mkOption { 323 type = types.str; 324 default = "[tt-rss] New headlines for last 24 hours"; 325 description = '' 326 Subject line for email digests. 327 ''; 328 }; 329 }; 330 331 sessionCookieLifetime = mkOption { 332 type = types.int; 333 default = 86400; 334 description = '' 335 Default lifetime of a session (e.g. login) cookie. In seconds, 336 0 means cookie will be deleted when browser closes. 337 ''; 338 }; 339 340 selfUrlPath = mkOption { 341 type = types.str; 342 description = '' 343 Full URL of your tt-rss installation. This should be set to the 344 location of tt-rss directory, e.g. http://example.org/tt-rss/ 345 You need to set this option correctly otherwise several features 346 including PUSH, bookmarklets and browser integration will not work properly. 347 ''; 348 example = "http://localhost"; 349 }; 350 351 feedCryptKey = mkOption { 352 type = types.str; 353 default = ""; 354 description = '' 355 Key used for encryption of passwords for password-protected feeds 356 in the database. A string of 24 random characters. If left blank, encryption 357 is not used. Requires mcrypt functions. 358 Warning: changing this key will make your stored feed passwords impossible 359 to decrypt. 360 ''; 361 }; 362 363 singleUserMode = mkOption { 364 type = types.bool; 365 default = true; 366 367 description = '' 368 Operate in single user mode, disables all functionality related to 369 multiple users and authentication. Enabling this assumes you have 370 your tt-rss directory protected by other means (e.g. http auth). 371 ''; 372 }; 373 374 simpleUpdateMode = mkOption { 375 type = types.bool; 376 default = false; 377 description = '' 378 Enables fallback update mode where tt-rss tries to update feeds in 379 background while tt-rss is open in your browser. 380 If you don't have a lot of feeds and don't want to or can't run 381 background processes while not running tt-rss, this method is generally 382 viable to keep your feeds up to date. 383 Still, there are more robust (and recommended) updating methods 384 available, you can read about them here: http://tt-rss.org/wiki/UpdatingFeeds 385 ''; 386 }; 387 388 forceArticlePurge = mkOption { 389 type = types.int; 390 default = 0; 391 description = '' 392 When this option is not 0, users ability to control feed purging 393 intervals is disabled and all articles (which are not starred) 394 older than this amount of days are purged. 395 ''; 396 }; 397 398 checkForUpdates = mkOption { 399 type = types.bool; 400 default = true; 401 description = '' 402 Check for updates automatically if running Git version 403 ''; 404 }; 405 406 enableGZipOutput = mkOption { 407 type = types.bool; 408 default = true; 409 description = '' 410 Selectively gzip output to improve wire performance. This requires 411 PHP Zlib extension on the server. 412 Enabling this can break tt-rss in several httpd/php configurations, 413 if you experience weird errors and tt-rss failing to start, blank pages 414 after login, or content encoding errors, disable it. 415 ''; 416 }; 417 418 plugins = mkOption { 419 type = types.listOf types.str; 420 default = ["auth_internal" "note"]; 421 description = '' 422 List of plugins to load automatically for all users. 423 System plugins have to be specified here. Please enable at least one 424 authentication plugin here (auth_*). 425 Users may enable other user plugins from Preferences/Plugins but may not 426 disable plugins specified in this list. 427 Disabling auth_internal in this list would automatically disable 428 reset password link on the login form. 429 ''; 430 }; 431 432 logDestination = mkOption { 433 type = types.enum ["" "sql" "syslog"]; 434 default = "sql"; 435 description = '' 436 Log destination to use. Possible values: sql (uses internal logging 437 you can read in Preferences -> System), syslog - logs to system log. 438 Setting this to blank uses PHP logging (usually to http server 439 error.log). 440 ''; 441 }; 442 }; 443 }; 444 445 446 ###### implementation 447 448 config = let 449 root = "/var/lib/tt-rss"; 450 in mkIf cfg.enable { 451 452 services.phpfpm.poolConfigs = if cfg.pool == "${poolName}" then { 453 "${poolName}" = '' 454 listen = "${phpfpmSocketName}"; 455 listen.owner = nginx 456 listen.group = nginx 457 listen.mode = 0600 458 user = nginx 459 pm = dynamic 460 pm.max_children = 75 461 pm.start_servers = 10 462 pm.min_spare_servers = 5 463 pm.max_spare_servers = 20 464 pm.max_requests = 500 465 catch_workers_output = 1 466 ''; 467 } else {}; 468 469 # TODO: Re-enable after https://github.com/NixOS/nixpkgs/pull/15862 is merged 470 471 # services.nginx.virtualHosts = if cfg.virtualHost == "${virtualHostName}" then { 472 # "${virtualHostName}" = { 473 # root = "${root}"; 474 # extraConfig = '' 475 # access_log /var/log/nginx-${virtualHostName}-access.log; 476 # error_log /var/log/nginx-${virtualHostName}-error.log; 477 # ''; 478 479 # locations."/" = { 480 # extraConfig = '' 481 # index index.php; 482 # ''; 483 # }; 484 485 # locations."~ \.php$" = { 486 # extraConfig = '' 487 # fastcgi_split_path_info ^(.+\.php)(/.+)$; 488 # fastcgi_pass unix:${phpfpmSocketName}; 489 # fastcgi_index index.php; 490 # fastcgi_param SCRIPT_FILENAME ${root}/$fastcgi_script_name; 491 492 # include ${pkgs.nginx}/conf/fastcgi_params; 493 # ''; 494 # }; 495 # }; 496 # } else {}; 497 498 499 systemd.services.tt-rss = let 500 dbService = if cfg.database.type == "pgsql" then "postgresql.service" else "mysql.service"; 501 in { 502 503 description = "Tiny Tiny RSS feeds update daemon"; 504 505 preStart = let 506 callSql = if cfg.database.type == "pgsql" then (e: '' 507 ${optionalString (cfg.database.password != null) 508 "PGPASSWORD=${cfg.database.password}"} ${pkgs.postgresql95}/bin/psql \ 509 -U ${cfg.database.user} \ 510 -h ${cfg.database.host} \ 511 --port ${toString dbPort} \ 512 -c '${e}' \ 513 ${cfg.database.name}'') 514 515 else if cfg.database.type == "mysql" then (e: '' 516 echo '${e}' | ${pkgs.mysql}/bin/mysql \ 517 ${optionalString (cfg.database.password != null) 518 "-p${cfg.database.password}"} \ 519 -u ${cfg.database.user} \ 520 -h ${cfg.database.host} \ 521 -P ${toString dbPort} \ 522 ${cfg.database.name}'') 523 524 else ""; 525 526 in '' 527 rm -rf "${root}/*" 528 mkdir -m 755 -p "${root}" 529 cp -r "${pkgs.tt-rss}/"* "${root}" 530 ln -sf "${tt-rss-config}" "${root}/config.php" 531 chown -R "${cfg.user}" "${root}" 532 chmod -R 755 "${root}" 533 '' + (optionalString (cfg.database.type == "pgsql") '' 534 535 exists=$(${callSql "select count(*) > 0 from pg_tables where tableowner = user"} \ 536 | tail -n+3 | head -n-2 | sed -e 's/[ \n\t]*//') 537 538 if [ "$exists" == 'f' ]; then 539 ${callSql "\\i ${pkgs.tt-rss}/schema/ttrss_schema_${cfg.database.type}.sql"} 540 else 541 echo 'The database contains some data. Leaving it as it is.' 542 fi; 543 '') + (optionalString (cfg.database.type == "mysql") '' 544 545 exists=$(${callSql "select count(*) > 0 from information_schema.tables where table_schema = schema()"} \ 546 | tail -n+2 | sed -e 's/[ \n\t]*//') 547 548 if [ "$exists" == '0' ]; then 549 ${callSql "\\. ${pkgs.tt-rss}/schema/ttrss_schema_${cfg.database.type}.sql"} 550 else 551 echo 'The database contains some data. Leaving it as it is.' 552 fi; 553 ''); 554 555 serviceConfig = { 556 User = "${cfg.user}"; 557 ExecStart = "${pkgs.php}/bin/php /var/lib/tt-rss/update.php --daemon"; 558 StandardOutput = "syslog"; 559 StandardError = "syslog"; 560 PermissionsStartOnly = true; 561 }; 562 563 wantedBy = [ "multi-user.target" ]; 564 requires = ["${dbService}"]; 565 after = ["network.target" "${dbService}"]; 566 }; 567 }; 568}