From d212f87e008092bbb6c3f09e7e0a66702d1bff2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ana=C3=AFs=20Saraiva?= <53227802+Kisakay@users.noreply.github.com> Date: Tue, 25 Aug 2026 13:09:51 +0200 Subject: [PATCH] updated nix --- deploy/nixos/download.nix | 188 +++++++++++++++++++++++++++++++------- 1 file changed, 156 insertions(+), 32 deletions(-) diff --git a/deploy/nixos/download.nix b/deploy/nixos/download.nix index 7d21131..2384d3d 100644 --- a/deploy/nixos/download.nix +++ b/deploy/nixos/download.nix @@ -6,20 +6,106 @@ }: let cfg = config.services.download; + + # The source tree the package is built from. By default it follows + # `workingDir` (the boot-cloned/pulled checkout) so `deployOnBoot` actually + # updates the built binary on the next rebuild; a caller may pin an explicit + # `src` path instead (e.g. from a flake) to build reproducibly. + sourcePath = if cfg.src != null then cfg.src else /. + cfg.workingDir; + + # ---- Backend: Rust binary built with nixpkgs cargo hooks (offline-safe). ---- + filehostBackend = pkgs.rustPlatform.buildRustPackage { + pname = "filehost"; + version = "1.0.0"; + + # The repository root (backend/ + frontend/). `cargoRoot` points the build + # at backend/Cargo.toml while keeping the rest of the tree available. + src = sourcePath; + cargoRoot = "backend"; + + # SHA-256 of all crates.io dependencies (from backend/Cargo.lock). + # Set this to `lib.fakeHash` on the first build, then copy the real hash + # from the error message (nixpkgs prints it). + cargoHash = cfg.cargoHash; + + buildInputs = [ ]; + nativeBuildInputs = with pkgs; [ ]; + + # We only need the binary; disable tests to keep the closure small. + doCheck = false; + + installPhase = '' + runHook preInstall + mkdir -p $out/bin + install -Dm755 target/release/filehost $out/bin/filehost + runHook postInstall + ''; + }; + + # ---- Frontend: Svelte static assets built with node/npm. ---- + filehostFrontend = pkgs.stdenv.mkDerivation { + pname = "filehost-frontend"; + version = "1.0.0"; + + src = sourcePath; + + nativeBuildInputs = with pkgs; [ + nodejs + (nodejs.pkgs.npm or nodePackages.npm) + ]; + + # `npm install` needs writable dirs that default to the source; redirect + # caches so the build stays sandbox-friendly. + npm_config_cache = "/tmp/npm-cache"; + + buildPhase = '' + runHook preBuild + cd frontend + npm install --no-audit --no-fund + npm run build + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + mkdir -p $out/share/filehost + cp -r frontend/dist $out/share/filehost/dist + runHook postInstall + ''; + }; in { options.services.download = { enable = lib.mkEnableOption "the File Drop private file host"; + # Optional: pin an explicit source path. When unset (default), the package + # is built from `workingDir` so `deployOnBoot` (git pull + rebuild) actually + # updates the binary. + src = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + description = "Path to the download source tree (backend/ + frontend/). Defaults to workingDir."; + }; + + # SHA-256 of the Cargo dependency vendor (crates.io). See `cargoHash` in + # `rustPlatform.buildRustPackage`. Required; default to fakeHash to force a + # first build that reports the correct value. + cargoHash = lib.mkOption { + type = lib.types.str; + default = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; + description = "cargo vendor hash (sha256) of the Rust crate dependencies."; + }; + user = lib.mkOption { type = lib.types.str; + default = "filehost"; description = "System user that runs the service."; }; - workingDir = lib.mkOption { + group = lib.mkOption { type = lib.types.str; - default = "/srv/download"; - description = "Directory containing the code (src/server.ts)."; + default = "filehost"; + description = "System group that runs the service."; }; storageDir = lib.mkOption { @@ -59,9 +145,9 @@ in }; keyLength = lib.mkOption { - type = lib.types.ints.positive; + type = lib.types.ints.between 4 64; default = 8; - description = "Length in characters of the upload/download key token."; + description = "Length in characters of the upload/download key token (a-z0-9)."; }; publicBaseUrl = lib.mkOption { @@ -70,40 +156,75 @@ in description = "Public domain of the site (e.g. https://files.example.com)."; }; + rateLimitEnabled = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Per-IP rate limiting on download/delete/auth endpoints."; + }; + + rateLimitMax = lib.mkOption { + type = lib.types.ints.positive; + default = 300; + description = "Max requests per rate-limit window."; + }; + + rateLimitWindowMs = lib.mkOption { + type = lib.types.ints.positive; + default = 60000; + description = "Rate-limit window in milliseconds."; + }; + trustProxy = lib.mkEnableOption "trust of X-Forwarded-* headers (behind a reverse proxy)"; + stripMetadata = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Strip embedded EXIF/GPS/author/comments and video metadata server-side."; + }; + repoUrl = lib.mkOption { type = lib.types.str; default = "https://github.com/Kisakay/download.git"; description = "Git repository to clone/pull from."; }; + workingDir = lib.mkOption { + type = lib.types.str; + default = "/srv/download"; + description = "Directory containing the code."; + }; + deployOnBoot = lib.mkOption { type = lib.types.bool; default = true; - description = "Pull the repo at boot (like the nightcord-deploy pattern)."; + description = "Pull the repo at boot and rebuild (like the nightcord-deploy pattern)."; + }; + + rebuildCommand = lib.mkOption { + type = lib.types.str; + default = "${config.system.build.nixos-rebuild}"; + description = "Command (run as root) to apply the pulled code after a boot deploy. Defaults to the system's own rebuild script (switch without re-evaluating the flake)."; }; }; config = lib.mkIf cfg.enable { systemd.tmpfiles.rules = [ "d ${cfg.storageDir} 0755 ${cfg.user} ${cfg.user} -" + "d ${cfg.workingDir} 0755 ${cfg.user} ${cfg.user} -" ]; systemd.services.download-deploy = { - description = "Update download from git"; + description = "Update download from git and rebuild"; after = [ "network-online.target" ]; wants = [ "network-online.target" ]; wantedBy = lib.mkIf cfg.deployOnBoot [ "multi-user.target" ]; - path = with pkgs; [ git bun ]; + path = with pkgs; [ git ]; serviceConfig = { Type = "oneshot"; - User = cfg.user; + # Rebuild requires root (updates /run/current-system, generations, etc.). WorkingDirectory = cfg.workingDir; - Environment = [ "HOME=/home/${cfg.user}" ]; - ExecStartPost = "+${pkgs.systemd}/bin/systemctl restart download.service"; }; script = '' @@ -113,8 +234,10 @@ in else git -C ${cfg.workingDir} pull --ff-only fi - cd ${cfg.workingDir} - bun install + + # Apply the freshly-pulled code. If the source is pinned via `src`, + # nothing changed on disk, but the rebuild is still harmless. + ${cfg.rebuildCommand} ''; }; @@ -124,28 +247,29 @@ in wants = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ]; - path = with pkgs; [ bun ]; - serviceConfig = { Type = "simple"; User = cfg.user; - Group = cfg.user; - WorkingDirectory = cfg.workingDir; - Environment = - [ - "HOME=/home/${cfg.user}" - "PORT=${toString cfg.port}" - "HOST=${cfg.host}" - "STORAGE_DIR=${cfg.storageDir}" - "MAX_FILE_SIZE=${cfg.maxFileSize}" - "FILE_TTL_MS=${toString cfg.fileTtlMs}" - "MAX_STORAGE_USAGE=${toString cfg.maxStorageUsage}" - "KEY_LENGTH=${toString cfg.keyLength}" - ] - ++ lib.optionals (cfg.publicBaseUrl != null) [ "PUBLIC_BASE_URL=${cfg.publicBaseUrl}" ] - ++ lib.optionals cfg.trustProxy [ "TRUST_PROXY=1" ]; + Group = cfg.group; + Environment = [ + "HOST=${cfg.host}" + "PORT=${toString cfg.port}" + "STORAGE_DIR=${cfg.storageDir}" + "STATIC_DIR=${filehostFrontend}/share/filehost/dist" + "MAX_FILE_SIZE=${cfg.maxFileSize}" + "FILE_TTL_MS=${toString cfg.fileTtlMs}" + "MAX_STORAGE_USAGE=${toString cfg.maxStorageUsage}" + "KEY_LENGTH=${toString cfg.keyLength}" + "RATE_LIMIT_ENABLED=${if cfg.rateLimitEnabled then "1" else "0"}" + "RATE_LIMIT_MAX=${toString cfg.rateLimitMax}" + "RATE_LIMIT_WINDOW_MS=${toString cfg.rateLimitWindowMs}" + "STRIP_METADATA=${if cfg.stripMetadata then "1" else "0"}" + ] + ++ lib.optionals (cfg.publicBaseUrl != null) [ "PUBLIC_BASE_URL=${cfg.publicBaseUrl}" ] + ++ lib.optionals cfg.trustProxy [ "TRUST_PROXY=1" ]; + + ExecStart = "${filehostBackend}/bin/filehost"; - ExecStart = "${pkgs.bun}/bin/bun src/server.ts"; Restart = "on-failure"; RestartSec = 3; TimeoutStopSec = 30; @@ -158,4 +282,4 @@ in }; }; }; -} \ No newline at end of file +}