at 25.11-pre 31 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7 8let 9 inherit (lib) 10 filterAttrsRecursive 11 generators 12 literalExpression 13 mkDefault 14 mkIf 15 mkOption 16 mkEnableOption 17 mkPackageOption 18 mkMerge 19 pipe 20 types 21 ; 22 23 cfg = config.services.movim; 24 25 defaultPHPCfg = { 26 "output_buffering" = 0; 27 "error_reporting" = "E_ALL & ~E_DEPRECATED & ~E_STRICT"; 28 "opcache.enable_cli" = 1; 29 "opcache.interned_strings_buffer" = 8; 30 "opcache.max_accelerated_files" = 6144; 31 "opcache.memory_consumption" = 128; 32 "opcache.revalidate_freq" = 2; 33 "opcache.fast_shutdown" = 1; 34 }; 35 36 phpCfg = generators.toKeyValue { mkKeyValue = generators.mkKeyValueDefault { } " = "; } ( 37 defaultPHPCfg // cfg.phpCfg 38 ); 39 40 podConfigFlags = 41 let 42 bevalue = a: lib.escapeShellArg (generators.mkValueStringDefault { } a); 43 in 44 lib.concatStringsSep " " ( 45 lib.attrsets.foldlAttrs ( 46 acc: k: v: 47 acc ++ lib.optional (v != null) "--${k}=${bevalue v}" 48 ) [ ] cfg.podConfig 49 ); 50 51 package = 52 let 53 p = cfg.package.override ( 54 { 55 inherit phpCfg; 56 inherit (cfg) minifyStaticFiles; 57 } 58 // lib.optionalAttrs (cfg.database.type == "postgresql") { 59 withPostgreSQL = true; 60 } 61 // lib.optionalAttrs (cfg.database.type == "mariadb") { 62 withMySQL = true; 63 } 64 ); 65 in 66 p.overrideAttrs ( 67 finalAttrs: prevAttrs: 68 let 69 appDir = "$out/share/php/${finalAttrs.pname}"; 70 71 stateDirectories = # sh 72 '' 73 # Symlinking in our state directories 74 rm -rf $out/{.env,cache} ${appDir}/{log,public/cache} 75 ln -s ${cfg.dataDir}/.env ${appDir}/.env 76 ln -s ${cfg.dataDir}/public/cache ${appDir}/public/cache 77 ln -s ${cfg.logDir} ${appDir}/log 78 ln -s ${cfg.runtimeDir}/cache ${appDir}/cache 79 ''; 80 81 exposeComposer = # sh 82 '' 83 # Expose PHP Composer for scripts 84 mkdir -p $out/bin 85 echo "#!${lib.getExe pkgs.dash}" > $out/bin/movim-composer 86 echo "${finalAttrs.php.packages.composer}/bin/composer --working-dir="${appDir}" \"\$@\"" >> $out/bin/movim-composer 87 chmod +x $out/bin/movim-composer 88 ''; 89 90 podConfigInputDisableReplace = lib.optionalString (podConfigFlags != "") ( 91 lib.concatStringsSep "\n" ( 92 lib.attrsets.foldlAttrs ( 93 acc: k: v: 94 acc 95 ++ 96 lib.optional (v != null) 97 # Disable all Admin panel options that were set in the 98 # `cfg.podConfig` to prevent confusing situtions where the 99 # values are rewritten on server reboot 100 # sh 101 '' 102 substituteInPlace ${appDir}/app/Widgets/AdminMain/adminmain.tpl \ 103 --replace-warn 'name="${k}"' 'name="${k}" readonly' 104 '' 105 ) [ ] cfg.podConfig 106 ) 107 ); 108 109 precompressStaticFilesJobs = 110 let 111 inherit (cfg.precompressStaticFiles) brotli gzip; 112 113 findTextFileNames = lib.concatStringsSep " -o " ( 114 builtins.map (n: ''-iname "*.${n}"'') [ 115 "css" 116 "ini" 117 "js" 118 "json" 119 "manifest" 120 "mjs" 121 "svg" 122 "webmanifest" 123 ] 124 ); 125 in 126 lib.concatStringsSep "\n" [ 127 (lib.optionalString brotli.enable # sh 128 '' 129 echo -n "Precompressing static files with Brotli " 130 find ${appDir}/public -type f ${findTextFileNames} -print0 \ 131 | xargs -0 -P$NIX_BUILD_CORES -n1 -I{} \ 132 ${lib.getExe brotli.package} --keep --quality=${builtins.toString brotli.compressionLevel} --output={}.br {} 133 echo " done." 134 '' 135 ) 136 (lib.optionalString gzip.enable # sh 137 '' 138 echo -n "Precompressing static files with Gzip " 139 find ${appDir}/public -type f ${findTextFileNames} -print0 \ 140 | xargs -0 -P$NIX_BUILD_CORES -n1 -I{} \ 141 ${lib.getExe gzip.package} -c -${builtins.toString gzip.compressionLevel} {} > {}.gz 142 echo " done." 143 '' 144 ) 145 ]; 146 in 147 { 148 postInstall = lib.concatStringsSep "\n\n" [ 149 prevAttrs.postInstall 150 stateDirectories 151 exposeComposer 152 podConfigInputDisableReplace 153 precompressStaticFilesJobs 154 ]; 155 } 156 ); 157 158 configFile = pipe cfg.settings [ 159 (filterAttrsRecursive (_: v: v != null)) 160 (generators.toKeyValue { }) 161 (pkgs.writeText "movim-env") 162 ]; 163 164 pool = "movim"; 165 fpm = config.services.phpfpm.pools.${pool}; 166 phpExecutionUnit = "phpfpm-${pool}"; 167 168 dbService = 169 { 170 "postgresql" = "postgresql.service"; 171 "mariadb" = "mysql.service"; 172 } 173 .${cfg.database.type}; 174 175 # exclusivity asserted in `assertions` 176 webServerService = 177 if cfg.h2o != null then 178 "h2o.service" 179 else if cfg.nginx != null then 180 "nginx.service" 181 else 182 null; 183 184 socketOwner = 185 if cfg.h2o != null then 186 config.services.h2o.user 187 else if cfg.nginx != null then 188 config.services.nginx.user 189 else 190 cfg.user; 191 192 # Movim needs a lot of unsafe values to function at this time. Perhaps if 193 # this is ever addressed in the future, the PHP application will send up the 194 # proper directive. For now this fairly conservative CSP will restrict a lot 195 # of potentially bad stuff as well as take in inventory of the features used. 196 # 197 # See: https://github.com/movim/movim/issues/314 198 movimCSP = lib.concatStringsSep "; " [ 199 "default-src 'self'" 200 "img-src 'self' aesgcm: data: https:" 201 "media-src 'self' aesgcm: https:" 202 "script-src 'self' 'unsafe-eval' 'unsafe-inline'" 203 "style-src 'self' 'unsafe-inline'" 204 ]; 205in 206{ 207 options.services = { 208 movim = { 209 enable = mkEnableOption "a Movim instance"; 210 package = mkPackageOption pkgs "movim" { }; 211 phpPackage = mkPackageOption pkgs "php" { }; 212 213 phpCfg = mkOption { 214 type = 215 with types; 216 attrsOf (oneOf [ 217 int 218 str 219 bool 220 ]); 221 defaultText = literalExpression (generators.toPretty { } defaultPHPCfg); 222 default = { }; 223 description = "Extra PHP INI options such as `memory_limit`, `max_execution_time`, etc."; 224 }; 225 226 user = mkOption { 227 type = types.nonEmptyStr; 228 default = "movim"; 229 description = "User running Movim service"; 230 }; 231 232 group = mkOption { 233 type = types.nonEmptyStr; 234 default = "movim"; 235 description = "Group running Movim service"; 236 }; 237 238 dataDir = mkOption { 239 type = types.path; 240 default = "/var/lib/movim"; 241 description = "State directory of the `movim` user which holds the applications state & data."; 242 }; 243 244 logDir = mkOption { 245 type = types.path; 246 default = "/var/log/movim"; 247 description = "Log directory of the `movim` user which holds the applications logs."; 248 }; 249 250 runtimeDir = mkOption { 251 type = types.path; 252 default = "/run/movim"; 253 description = "Runtime directory of the `movim` user which holds the applications caches & temporary files."; 254 }; 255 256 domain = mkOption { 257 type = types.nonEmptyStr; 258 description = "Fully-qualified domain name (FQDN) for the Movim instance."; 259 }; 260 261 port = mkOption { 262 type = types.port; 263 default = 8080; 264 description = "Movim daemon port."; 265 }; 266 267 debug = mkOption { 268 type = types.bool; 269 default = false; 270 description = "Debugging logs."; 271 }; 272 273 verbose = mkOption { 274 type = types.bool; 275 default = false; 276 description = "Verbose logs."; 277 }; 278 279 minifyStaticFiles = mkOption { 280 type = types.either types.bool ( 281 types.submodule { 282 options = { 283 script = mkOption { 284 type = types.submodule { 285 options = { 286 enable = mkEnableOption "Script minification via esbuild"; 287 target = mkOption { 288 type = types.nullOr types.nonEmptyStr; 289 default = null; 290 description = '' 291 esbuild target environment string. If not set, a sane 292 default will be provided. See: 293 <https://esbuild.github.io/api/#target>. 294 ''; 295 }; 296 }; 297 }; 298 }; 299 style = mkOption { 300 type = types.submodule { 301 options = { 302 enable = mkEnableOption "Script minification via Lightning CSS"; 303 target = mkOption { 304 type = types.nullOr types.nonEmptyStr; 305 default = null; 306 description = '' 307 Browserslists string target for browser compatibility. 308 If not set, a sane default will be provided. See: 309 <https://browsersl.ist>. 310 ''; 311 }; 312 }; 313 }; 314 }; 315 svg = mkOption { 316 type = types.submodule { 317 options = { 318 enable = mkEnableOption "SVG minification via Scour"; 319 }; 320 }; 321 }; 322 }; 323 } 324 ); 325 default = true; 326 description = '' 327 Do minification on public static files which reduces the size of 328 assetssaving data for the server & users as well as offering a 329 performance improvement. This adds typing for the `minifyStaticFiles` 330 attribute for the Movim package which *will* override any existing 331 override value. The default `true` will enable minification for all 332 supported asset types with sane defaults. 333 ''; 334 example = 335 lib.literalExpression # nix 336 '' 337 { 338 script.enable = false; 339 style = { 340 enable = true; 341 target = "> 0.5%, last 2 versions, Firefox ESR, not dead"; 342 }; 343 svg.enable = true; 344 } 345 ''; 346 }; 347 348 precompressStaticFiles = mkOption { 349 type = types.submodule { 350 options = { 351 brotli = { 352 enable = mkEnableOption "Brotli precompression"; 353 package = mkPackageOption pkgs "brotli" { }; 354 compressionLevel = mkOption { 355 type = types.ints.between 0 11; 356 default = 11; 357 description = "Brotli compression level"; 358 }; 359 }; 360 gzip = { 361 enable = mkEnableOption "Gzip precompression"; 362 package = mkPackageOption pkgs "gzip" { }; 363 compressionLevel = mkOption { 364 type = types.ints.between 1 9; 365 default = 9; 366 description = "Gzip compression level"; 367 }; 368 }; 369 }; 370 }; 371 default = { 372 brotli.enable = true; 373 gzip.enable = false; 374 }; 375 description = "Aggressively precompress static files"; 376 }; 377 378 podConfig = mkOption { 379 type = types.submodule { 380 options = { 381 info = mkOption { 382 type = types.nullOr types.nonEmptyStr; 383 default = null; 384 description = "Content of the info box on the login page"; 385 }; 386 387 description = mkOption { 388 type = types.nullOr types.nonEmptyStr; 389 default = null; 390 description = "General description of the instance"; 391 }; 392 393 timezone = mkOption { 394 type = types.nullOr types.nonEmptyStr; 395 default = null; 396 description = "The server timezone"; 397 }; 398 399 restrictsuggestions = mkOption { 400 type = types.nullOr types.bool; 401 default = null; 402 description = "Only suggest chatrooms, Communities and other contents that are available on the user XMPP server and related services"; 403 }; 404 405 chatonly = mkOption { 406 type = types.nullOr types.bool; 407 default = null; 408 description = "Disable all the social feature (Communities, Blog) and keep only the chat ones"; 409 }; 410 411 disableregistration = mkOption { 412 type = types.nullOr types.bool; 413 default = null; 414 description = "Remove the XMPP registration flow and buttons from the interface"; 415 }; 416 417 loglevel = mkOption { 418 type = types.nullOr (types.ints.between 0 3); 419 default = null; 420 description = "The server loglevel"; 421 }; 422 423 locale = mkOption { 424 type = types.nullOr types.nonEmptyStr; 425 default = null; 426 description = "The server main locale"; 427 }; 428 429 xmppdomain = mkOption { 430 type = types.nullOr types.nonEmptyStr; 431 default = null; 432 description = "The default XMPP server domain"; 433 }; 434 435 xmppdescription = mkOption { 436 type = types.nullOr types.nonEmptyStr; 437 default = null; 438 description = "The default XMPP server description"; 439 }; 440 441 xmppwhitelist = mkOption { 442 type = types.nullOr types.nonEmptyStr; 443 default = null; 444 description = "The allowlisted XMPP servers"; 445 }; 446 }; 447 }; 448 default = { }; 449 description = '' 450 Pod configuration (values from `php daemon.php config --help`). 451 Note that these values will now be disabled in the admin panel. 452 ''; 453 }; 454 455 settings = mkOption { 456 type = 457 with types; 458 attrsOf ( 459 nullOr (oneOf [ 460 int 461 str 462 bool 463 ]) 464 ); 465 default = { }; 466 description = ".env settings for Movim. Secrets should use `secretFile` option instead. `null`s will be culled."; 467 }; 468 469 secretFile = mkOption { 470 type = types.nullOr types.path; 471 default = null; 472 description = "The secret file to be sourced for the .env settings."; 473 }; 474 475 database = { 476 type = mkOption { 477 type = types.enum [ 478 "mariadb" 479 "postgresql" 480 ]; 481 example = "mariadb"; 482 default = "postgresql"; 483 description = "Database engine to use."; 484 }; 485 486 name = mkOption { 487 type = types.nonEmptyStr; 488 default = "movim"; 489 description = "Database name."; 490 }; 491 492 user = mkOption { 493 type = types.nonEmptyStr; 494 default = "movim"; 495 description = "Database username."; 496 }; 497 498 createLocally = mkOption { 499 type = types.bool; 500 default = true; 501 description = "local database using UNIX socket authentication"; 502 }; 503 }; 504 505 h2o = mkOption { 506 type = types.nullOr ( 507 types.submodule (import ../web-servers/h2o/vhost-options.nix { inherit config lib; }) 508 ); 509 default = null; 510 example = 511 lib.literalExpression # nix 512 '' 513 { 514 serverAliases = [ 515 "pics.''${config.movim.domain}" 516 ]; 517 acme.enable = true; 518 tls.policy = "force"; 519 } 520 ''; 521 description = '' 522 With this option, you can customize an H2O virtual host which already 523 has sensible defaults for Movim. Set to `{ }` if you do not need any 524 customization to the virtual host. If enabled, then by default, the 525 {option}`serverName` is `''${domain}`, If this is set to `null` (the 526 default), no H2O `hosts` will be configured. 527 ''; 528 }; 529 530 nginx = mkOption { 531 type = types.nullOr ( 532 types.submodule (import ../web-servers/nginx/vhost-options.nix { inherit config lib; }) 533 ); 534 default = null; 535 example = 536 lib.literalExpression # nix 537 '' 538 { 539 serverAliases = [ 540 "pics.''${config.movim.domain}" 541 ]; 542 enableACME = true; 543 forceHttps = true; 544 } 545 ''; 546 description = '' 547 With this option, you can customize an Nginx virtual host which 548 already has sensible defaults for Movim. Set to `{ }` if you do not 549 need any customization to the virtual host. If enabled, then by 550 default, the {option}`serverName` is `''${domain}`, If this is set to 551 `null` (the default), no Nginx `virtualHost` will be configured. 552 ''; 553 }; 554 555 poolConfig = mkOption { 556 type = 557 with types; 558 attrsOf (oneOf [ 559 int 560 str 561 bool 562 ]); 563 default = { }; 564 description = "Options for Movims PHP-FPM pool."; 565 }; 566 }; 567 }; 568 569 config = mkIf cfg.enable { 570 assertions = [ 571 ( 572 let 573 webServers = [ 574 "h2o" 575 "nginx" 576 ]; 577 checkConfigs = lib.concatMapStringsSep ", " (ws: "services.movim.${ws}") webServers; 578 in 579 { 580 assertion = builtins.length (lib.lists.filter (ws: cfg.${ws} != null) webServers) <= 1; 581 message = '' 582 At most 1 web server virtual host configuration should be enabled 583 for Movim at a time. Check ${checkConfigs}. 584 ''; 585 } 586 ) 587 ]; 588 589 environment.systemPackages = [ package ]; 590 591 users = { 592 users = 593 { 594 movim = mkIf (cfg.user == "movim") { 595 isSystemUser = true; 596 group = cfg.group; 597 }; 598 } 599 // lib.optionalAttrs (cfg.h2o != null) { 600 "${config.services.h2o.user}".extraGroups = [ cfg.group ]; 601 } 602 // lib.optionalAttrs (cfg.nginx != null) { 603 "${config.services.nginx.user}".extraGroups = [ cfg.group ]; 604 }; 605 groups = { 606 ${cfg.group} = { }; 607 }; 608 }; 609 610 services = { 611 movim = { 612 settings = mkMerge [ 613 { 614 DAEMON_URL = "//${cfg.domain}"; 615 DAEMON_PORT = cfg.port; 616 DAEMON_INTERFACE = "127.0.0.1"; 617 DAEMON_DEBUG = cfg.debug; 618 DAEMON_VERBOSE = cfg.verbose; 619 } 620 (mkIf cfg.database.createLocally { 621 DB_DRIVER = 622 { 623 "postgresql" = "pgsql"; 624 "mariadb" = "mysql"; 625 } 626 .${cfg.database.type}; 627 DB_HOST = "localhost"; 628 DB_PORT = config.services.${cfg.database.type}.settings.port; 629 DB_DATABASE = cfg.database.name; 630 DB_USERNAME = cfg.database.user; 631 DB_PASSWORD = ""; 632 }) 633 ]; 634 635 poolConfig = lib.mapAttrs' (n: v: lib.nameValuePair n (lib.mkDefault v)) { 636 "pm" = "dynamic"; 637 "php_admin_value[error_log]" = "stderr"; 638 "php_admin_flag[log_errors]" = true; 639 "catch_workers_output" = true; 640 "pm.max_children" = 32; 641 "pm.start_servers" = 2; 642 "pm.min_spare_servers" = 2; 643 "pm.max_spare_servers" = 8; 644 "pm.max_requests" = 500; 645 }; 646 }; 647 648 h2o = mkIf (cfg.h2o != null) { 649 enable = true; 650 hosts."${cfg.domain}" = mkMerge [ 651 { 652 settings = { 653 paths = { 654 "/ws/" = { 655 "proxy.preserve-host" = "ON"; 656 "proxy.tunnel" = "ON"; 657 "proxy.reverse.url" = "http://${cfg.settings.DAEMON_INTERFACE}:${builtins.toString cfg.port}/"; 658 }; 659 "/" = 660 { 661 "file.dir" = "${package}/share/php/movim/public"; 662 "file.index" = [ 663 "index.php" 664 "index.html" 665 ]; 666 redirect = { 667 url = "/index.php/"; 668 internal = "YES"; 669 status = 307; 670 }; 671 "header.set" = [ 672 "Content-Security-Policy: ${movimCSP}" 673 ]; 674 } 675 // lib.optionalAttrs (with cfg.precompressStaticFiles; brotli.enable || gzip.enable) { 676 "file.send-compressed" = "ON"; 677 }; 678 }; 679 "file.custom-handler" = { 680 extension = [ ".php" ]; 681 "fastcgi.document_root" = package; 682 "fastcgi.connect" = { 683 port = fpm.socket; 684 type = "unix"; 685 }; 686 }; 687 }; 688 } 689 cfg.h2o 690 ]; 691 }; 692 693 nginx = mkIf (cfg.nginx != null) ( 694 { 695 enable = true; 696 recommendedOptimisation = mkDefault true; 697 recommendedProxySettings = true; 698 # TODO: recommended cache options already in Nginx⁇ 699 appendHttpConfig = # nginx 700 '' 701 fastcgi_cache_path /tmp/nginx_cache levels=1:2 keys_zone=nginx_cache:100m inactive=60m; 702 fastcgi_cache_key "$scheme$request_method$host$request_uri"; 703 ''; 704 virtualHosts."${cfg.domain}" = mkMerge [ 705 cfg.nginx 706 { 707 root = lib.mkForce "${package}/share/php/movim/public"; 708 locations = { 709 "/favicon.ico" = { 710 priority = 100; 711 extraConfig = # nginx 712 '' 713 access_log off; 714 log_not_found off; 715 ''; 716 }; 717 "/robots.txt" = { 718 priority = 100; 719 extraConfig = # nginx 720 '' 721 access_log off; 722 log_not_found off; 723 ''; 724 }; 725 "~ /\\.(?!well-known).*" = { 726 priority = 210; 727 extraConfig = # nginx 728 '' 729 deny all; 730 ''; 731 }; 732 # Ask nginx to cache every URL starting with "/picture" 733 "/picture" = { 734 priority = 400; 735 tryFiles = "$uri $uri/ /index.php$is_args$args"; 736 extraConfig = # nginx 737 '' 738 set $no_cache 0; # Enable cache only there 739 ''; 740 }; 741 "/" = { 742 priority = 490; 743 tryFiles = "$uri $uri/ /index.php$is_args$args"; 744 extraConfig = # nginx 745 '' 746 add_header Content-Security-Policy "${movimCSP}"; 747 set $no_cache 1; 748 ''; 749 }; 750 "~ \\.php$" = { 751 priority = 500; 752 tryFiles = "$uri =404"; 753 extraConfig = # nginx 754 '' 755 include ${config.services.nginx.package}/conf/fastcgi.conf; 756 add_header X-Cache $upstream_cache_status; 757 fastcgi_ignore_headers "Cache-Control" "Expires" "Set-Cookie"; 758 fastcgi_cache nginx_cache; 759 fastcgi_cache_valid any 7d; 760 fastcgi_cache_bypass $no_cache; 761 fastcgi_no_cache $no_cache; 762 fastcgi_split_path_info ^(.+\.php)(/.+)$; 763 fastcgi_index index.php; 764 fastcgi_pass unix:${fpm.socket}; 765 ''; 766 }; 767 "/ws/" = { 768 priority = 900; 769 proxyPass = "http://${cfg.settings.DAEMON_INTERFACE}:${builtins.toString cfg.port}/"; 770 proxyWebsockets = true; 771 recommendedProxySettings = true; 772 extraConfig = # nginx 773 '' 774 proxy_set_header X-Forwarded-Proto $scheme; 775 proxy_redirect off; 776 ''; 777 }; 778 }; 779 extraConfig = # nginx 780 '' 781 index index.php; 782 ''; 783 } 784 ]; 785 } 786 // lib.optionalAttrs (cfg.precompressStaticFiles.gzip.enable) { 787 recommendedGzipSettings = mkDefault true; 788 } 789 // lib.optionalAttrs (cfg.precompressStaticFiles.brotli.enable) { 790 recommendedBrotliSettings = mkDefault true; 791 } 792 ); 793 794 mysql = mkIf (cfg.database.createLocally && cfg.database.type == "mariadb") { 795 enable = mkDefault true; 796 package = mkDefault pkgs.mariadb; 797 ensureDatabases = [ cfg.database.name ]; 798 ensureUsers = [ 799 { 800 name = cfg.database.user; 801 ensureDBOwnership = true; 802 } 803 ]; 804 }; 805 806 postgresql = mkIf (cfg.database.createLocally && cfg.database.type == "postgresql") { 807 enable = mkDefault true; 808 ensureDatabases = [ cfg.database.name ]; 809 ensureUsers = [ 810 { 811 name = cfg.database.user; 812 ensureDBOwnership = true; 813 } 814 ]; 815 authentication = '' 816 host ${cfg.database.name} ${cfg.database.user} localhost trust 817 ''; 818 }; 819 820 phpfpm.pools.${pool} = { 821 phpPackage = package.php; 822 user = cfg.user; 823 group = cfg.group; 824 825 phpOptions = '' 826 error_log = 'stderr' 827 log_errors = on 828 ''; 829 830 settings = { 831 "listen.owner" = socketOwner; 832 "listen.group" = cfg.group; 833 "listen.mode" = "0660"; 834 "catch_workers_output" = true; 835 } // cfg.poolConfig; 836 }; 837 }; 838 839 systemd = { 840 services.movim-data-setup = { 841 description = "Movim setup: .env file, databases init, cache reload"; 842 wantedBy = [ "multi-user.target" ]; 843 requiredBy = [ "${phpExecutionUnit}.service" ]; 844 before = [ "${phpExecutionUnit}.service" ]; 845 wants = [ "local-fs.target" ]; 846 requires = lib.optional cfg.database.createLocally dbService; 847 after = lib.optional cfg.database.createLocally dbService; 848 849 serviceConfig = 850 { 851 Type = "oneshot"; 852 User = cfg.user; 853 Group = cfg.group; 854 UMask = "077"; 855 } 856 // lib.optionalAttrs (cfg.secretFile != null) { 857 LoadCredential = "env-secrets:${cfg.secretFile}"; 858 }; 859 860 script = # sh 861 '' 862 # Env vars 863 rm -f ${cfg.dataDir}/.env 864 cp --no-preserve=all ${configFile} ${cfg.dataDir}/.env 865 echo -e '\n' >> ${cfg.dataDir}/.env 866 if [[ -f "$CREDENTIALS_DIRECTORY/env-secrets" ]]; then 867 cat "$CREDENTIALS_DIRECTORY/env-secrets" >> ${cfg.dataDir}/.env 868 echo -e '\n' >> ${cfg.dataDir}/.env 869 fi 870 871 # Caches, logs 872 mkdir -p ${cfg.dataDir}/public/cache ${cfg.logDir} ${cfg.runtimeDir}/cache 873 chmod -R ug+rw ${cfg.dataDir}/public/cache 874 chmod -R ug+rw ${cfg.logDir} 875 chmod -R ug+rwx ${cfg.runtimeDir}/cache 876 877 # Migrations 878 MOVIM_VERSION="${package.version}" 879 if [[ ! -f "${cfg.dataDir}/.migration-version" ]] || [[ "$MOVIM_VERSION" != "$(<${cfg.dataDir}/.migration-version)" ]]; then 880 ${package}/bin/movim-composer movim:migrate && echo $MOVIM_VERSION > ${cfg.dataDir}/.migration-version 881 fi 882 '' 883 + lib.optionalString (podConfigFlags != "") ( 884 let 885 flags = lib.concatStringsSep " " ( 886 [ "--no-interaction" ] 887 ++ lib.optional cfg.debug "-vvv" 888 ++ lib.optional (!cfg.debug && cfg.verbose) "-v" 889 ); 890 in 891 '' 892 ${lib.getExe package} config ${podConfigFlags} 893 '' 894 ); 895 }; 896 897 services.${phpExecutionUnit} = { 898 wantedBy = lib.optional (webServerService != null) webServerService; 899 requiredBy = [ "movim.service" ]; 900 before = [ "movim.service" ] ++ lib.optional (webServerService != null) webServerService; 901 wants = [ "network.target" ]; 902 requires = [ "movim-data-setup.service" ] ++ lib.optional cfg.database.createLocally dbService; 903 after = [ "movim-data-setup.service" ] ++ lib.optional cfg.database.createLocally dbService; 904 }; 905 906 services.movim = { 907 description = "Movim daemon"; 908 wantedBy = [ "multi-user.target" ]; 909 wants = [ 910 "network.target" 911 "local-fs.target" 912 ]; 913 requires = 914 [ 915 "movim-data-setup.service" 916 "${phpExecutionUnit}.service" 917 ] 918 ++ lib.optional cfg.database.createLocally dbService 919 ++ lib.optional (webServerService != null) webServerService; 920 after = 921 [ 922 "movim-data-setup.service" 923 "${phpExecutionUnit}.service" 924 ] 925 ++ lib.optional cfg.database.createLocally dbService 926 ++ lib.optional (webServerService != null) webServerService; 927 environment = { 928 PUBLIC_URL = "//${cfg.domain}"; 929 WS_PORT = builtins.toString cfg.port; 930 }; 931 932 serviceConfig = { 933 User = cfg.user; 934 Group = cfg.group; 935 WorkingDirectory = "${package}/share/php/movim"; 936 ExecStart = "${lib.getExe package} start"; 937 }; 938 }; 939 940 tmpfiles.settings."10-movim" = with cfg; { 941 "${dataDir}".d = { 942 inherit user group; 943 mode = "0710"; 944 }; 945 "${dataDir}/public".d = { 946 inherit user group; 947 mode = "0750"; 948 }; 949 "${dataDir}/public/cache".d = { 950 inherit user group; 951 mode = "0750"; 952 }; 953 "${runtimeDir}".d = { 954 inherit user group; 955 mode = "0700"; 956 }; 957 "${runtimeDir}/cache".d = { 958 inherit user group; 959 mode = "0700"; 960 }; 961 "${logDir}".d = { 962 inherit user group; 963 mode = "0700"; 964 }; 965 }; 966 }; 967 }; 968 969 imports = [ 970 (lib.mkRemovedOptionModule [ "minifyStaticFiles" "script" "package" ] '' 971 Override services.movim.package instead. 972 '') 973 (lib.mkRemovedOptionModule [ "minifyStaticFiles" "style" "package" ] '' 974 Override services.movim.package instead. 975 '') 976 (lib.mkRemovedOptionModule [ "minifyStaticFiles" "svg" "package" ] '' 977 Override services.movim.package instead. 978 '') 979 ]; 980}