{ pkgs, lib, config, ... }: let cfg = config.services.lychee; src = cfg.package; envConf = cfg.settings; in { options.services.lychee = { enable = lib.mkEnableOption "Whether to enable lychee"; website = lib.mkOption { type = lib.types.str; default = "localhost"; example = "www.example.com"; }; package = lib.mkOption { type = lib.types.path; }; forceSSL = lib.mkOption { type = lib.types.bool; default = false; description = "Whether to force SSL for the nginx virtual host"; }; enableACME = lib.mkOption { type = lib.types.bool; default = false; description = "Whether to enableACME for the nginx virtual host"; }; upload_max_filesize = lib.mkOption { type = lib.types.ints.positive; default = 30; description = "Max uploaded file size"; }; post_max_size = lib.mkOption { type = lib.types.ints.positive; default = 100; description = "Max post request size"; }; user = lib.mkOption { type = lib.types.str; default = "lychee"; description = "The user that will operate on mutable files"; }; settings = lib.mkOption { default = {}; type = lib.types.submodule { freeformType = with lib.types; attrsOf str; options = { APP_URL = lib.mkOption { type = lib.types.str; default = "http://${cfg.website}"; }; DB_CONNECTION = lib.mkOption { type = lib.types.str; default = "sqlite"; }; DB_LOG_SQL = lib.mkOption { type = lib.types.str; default = "\"false\""; }; CACHE_DRIVER = lib.mkOption { type = lib.types.str; default = "file"; }; SESSION_DRIVER = lib.mkOption { type = lib.types.str; default = "file"; }; SESSION_LIFETIME = lib.mkOption { type = lib.types.str; default = "120"; }; SECURITY_HEADER_HSTS_ENABLE = lib.mkOption { type = lib.types.str; default = "\"false\""; }; SESSION_SECURE_COOKIE = lib.mkOption { type = lib.types.str; default = "\"false\""; }; REDIS_PASSWORD = lib.mkOption { type = lib.types.str; default = "\"null\""; }; REDIS_PORT = lib.mkOption { type = lib.types.str; default = "6379"; }; MAIL_DRIVER = lib.mkOption { type = lib.types.str; default = "smtp"; }; }; }; }; }; config = lib.mkIf cfg.enable { services.nginx = { enable = true; virtualHosts.${cfg.website} = { root = "/var/lib/lychee/public/"; forceSSL = lib.mkDefault cfg.forceSSL; enableACME = lib.mkDefault cfg.enableACME; locations = { "^~ /index.php" = { fastcgiParams = { SCRIPT_FILENAME = "$document_root$fastcgi_script_name"; }; extraConfig = '' fastcgi_split_path_info ^(.+?\.php)(/.*)$; fastcgi_pass unix:${config.services.phpfpm.pools."${cfg.website}".socket}; fastcgi_index index.php; client_max_body_size ${builtins.toString cfg.upload_max_filesize}M; ''; }; "~ [^/]\.php(/|$)" = { return = "403"; }; }; extraConfig = '' index index.php; if (!-e $request_filename) { rewrite ^/(.*)$ /index.php?/$1 last; break; } ''; }; }; systemd.services."lychee-install" = { wantedBy = [ "phpfpm-${cfg.website}.service" ]; script = let rsync = pkgs.rsync; in '' ${rsync}/bin/rsync -a --ignore-existing ${src}/ $STATE_DIRECTORY chmod u+w $STATE_DIRECTORY/ chmod u+w $STATE_DIRECTORY/.env chmod u+w $STATE_DIRECTORY/database/ chmod u+w $STATE_DIRECTORY/database/database.sqlite chmod -R u+w $STATE_DIRECTORY/storage/ chmod -R u+w $STATE_DIRECTORY/public/ chmod -R u+w $STATE_DIRECTORY/bootstrap/cache/ ''; serviceConfig = { Type = "oneshot"; StateDirectory = "lychee"; User = cfg.user; Restart = "on-failure"; ProtectHome = true; ProtectSystem = "strict"; PrivateTmp = true; PrivateDevices = true; ProtectHostname = true; ProtectClock = true; ProtectKernelTunables = true; ProtectKernelModules = true; ProtectKernelLogs = true; ProtectControlGroups = true; NoNewPrivileges = true; RestrictRealtime = true; RestrictSUIDSGID = true; RemoveIPC = true; PrivateMounts = true; }; }; services.phpfpm.pools.${cfg.website} = { user = cfg.user; phpPackage = pkgs.php81.withExtensions ({ enabled, all }: enabled ++ [ all.imagick all.bcmath all.mbstring all.gd]); phpOptions = '' upload_max_filesize = ${builtins.toString cfg.upload_max_filesize}M post_max_size = ${builtins.toString cfg.post_max_size}M ''; settings = { "pm" = "dynamic"; "pm.max_children" = 75; "pm.start_servers" = 10; "pm.min_spare_servers" = 5; "pm.max_spare_servers" = 20; "pm.max_requests" = 500; "listen.owner" = config.services.nginx.user; "listen.group" = config.services.nginx.group; }; phpEnv = { "PATH" = lib.makeBinPath [ pkgs.ffmpeg ]; } // envConf; }; users.users.${cfg.user} = { isSystemUser = lib.mkDefault true; home = lib.mkDefault src; group = lib.mkDefault cfg.user; }; users.groups.${cfg.user} = { }; networking.firewall.allowedTCPPorts = [ 80 443 ]; }; }