{ inputs, pkgs, lib, config, ... }: with lib; let name = "pelican"; cfg = config.services.skynet."${name}"; php_pool = name; domain_panel = "${cfg.panel.domain.sub}.${cfg.panel.domain.base}.${cfg.panel.domain.tld}"; in { imports = [ ]; options.services.skynet."${name}" = { panel = { enable = mkEnableOption "Pelican Panel"; dir = mkOption { type = types.str; default = "/var/www/pelican"; }; domain = { tld = mkOption { type = types.str; default = "ie"; }; base = mkOption { type = types.str; default = "skynet"; }; sub = mkOption { type = types.str; #default = name; default = "panel-test"; }; }; }; wing = { enable = mkEnableOption "Pelican Wing"; }; }; config = mkMerge [ (mkIf cfg.panel.enable { services.skynet.acme.domains = [ domain_panel ]; # using https://nixos.org/manual/nixos/stable/index.html#module-services-gitlab as a guide services.skynet.dns.records = [ { record = cfg.panel.domain.sub; r_type = "CNAME"; value = config.services.skynet.host.name; } ]; environment.systemPackages = let dir = cfg.panel.dir; in [ pkgs.curl pkgs.gnutar pkgs.unzip pkgs.php83 pkgs.php83Packages.composer pkgs.php83Extensions.gd pkgs.php83Extensions.mysqli pkgs.php83Extensions.mbstring pkgs.php83Extensions.bcmath pkgs.php83Extensions.xml pkgs.php83Extensions.curl pkgs.php83Extensions.zip pkgs.php83Extensions.intl pkgs.php83Extensions.sqlite3 (import ./pelican-panel-install.nix { inherit pkgs; inherit dir; }) (import ./pelican-panel-update.nix { inherit pkgs; inherit dir; }) ]; systemd.timers."pelican-cron" = { wantedBy = ["timers.target"]; timerConfig = { OnBootSec = "5m"; OnUnitActiveSec = "1m"; Unit = "pelican-cron.service"; }; }; systemd.services."pelican-cron" = { script = '' ${pkgs.php83}/bin/php ${cfg.panel.dir}/artisan schedule:run >> /dev/null 2>&1 ''; serviceConfig = { Type = "oneshot"; }; }; systemd.services.pelican-queue = { wantedBy = ["multi-user.target"]; serviceConfig = { User = config.services.nginx.user; Group = config.services.nginx.group; Restart = "always"; ExecStart = "${pkgs.php83}/bin/php -q ${cfg.panel.dir}/artisan queue:work --tries=3"; startLimitInterval = 180; startLimitBurst = 30; RestartSec = "5"; }; }; services.phpfpm.pools.${php_pool} = { user = config.services.nginx.user; group = config.services.nginx.group; settings = { "listen.owner" = config.services.nginx.user; "listen.group" = config.services.nginx.group; "listen.mode" = "0600"; "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; }; }; services.nginx.virtualHosts."${domain_panel}" = { root = "${cfg.panel.dir}/public"; forceSSL = true; useACMEHost = "skynet"; extraConfig = '' index index.html index.htm index.php; charset utf-8; access_log off; error_log /var/log/nginx/pelican.app-error.log error; client_max_body_size 100m; client_body_timeout 120s; sendfile off; ssl_session_cache shared:SSL:10m; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"; ssl_prefer_server_ciphers on; add_header X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; add_header X-Robots-Tag none; add_header Content-Security-Policy "frame-ancestors 'self'"; add_header X-Frame-Options DENY; add_header Referrer-Policy same-origin; ''; locations = { "/" = { extraConfig = '' try_files $uri $uri/ /index.php?$query_string; ''; }; "/favicon.ico".extraConfig = '' access_log off; log_not_found off; ''; "/robots.txt".extraConfig = '' access_log off; log_not_found off; ''; "~ \\.php$" = { extraConfig = '' fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass unix:${config.services.phpfpm.pools.${php_pool}.socket}; fastcgi_index index.php; include ${config.services.nginx.package}/conf/fastcgi_params; fastcgi_param PHP_VALUE "upload_max_filesize = 100M \n post_max_size=100M"; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param HTTP_PROXY ""; fastcgi_intercept_errors off; fastcgi_buffer_size 16k; fastcgi_buffers 4 16k; fastcgi_connect_timeout 300; fastcgi_send_timeout 300; fastcgi_read_timeout 300; ''; }; "~ /\\.ht".extraConfig = '' deny all; ''; }; }; }) (mkIf cfg.wing.enable { networking.firewall.allowedTCPPorts = [8080 8443]; virtualisation.docker.enable = true; environment.systemPackages = [ (pkgs.callPackage ./pelican-wing-package.nix {}) ]; systemd.services.pelican-wings = { description = "Wings Daemon"; after = ["docker.service"]; requires = ["docker.service"]; partOf = ["docker.service"]; serviceConfig = { User = "root"; WorkingDirectory = "/etc/pelican"; LimitNOFILE = 4096; PIDFile = "/var/run/wings/daemon.pid"; ExecStart = "/run/current-system/sw/bin/wings"; Restart = "on-failure"; startLimitInterval = 180; startLimitBurst = 30; RestartSec = "5"; }; wantedBy = ["multi-user.target"]; }; }) ]; }