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