diff --git a/hosts/hetzner-arm/profiles/photoprism.nix b/hosts/hetzner-arm/profiles/photoprism.nix index 632d7b1..f2cda5f 100644 --- a/hosts/hetzner-arm/profiles/photoprism.nix +++ b/hosts/hetzner-arm/profiles/photoprism.nix @@ -1,6 +1,52 @@ -{...}: { +{lib, config, pkgs, ...}: let + inherit (config.services.secrets) secrets; + + rclonePhotos = pkgs.writeShellScriptBin "rclone-photos" '' + ${pkgs.rclone}/bin/rclone --config ${secrets.photos_rclone_config.path} "$@" + ''; + + mountPhotos = pkgs.writeShellScriptBin "mount-photos" '' + umount -flR /Photos || true + ${rclonePhotos}/bin/rclone-photos mount Photos: /Photos \ + --allow-other \ + --uid=${toString config.users.users.photoprism.uid} \ + --gid=${toString config.users.groups.photoprism.gid} \ + --fast-list \ + --umask=666 \ + --cache-dir=/PhotosCache \ + --dir-cache-time=1m \ + --vfs-cache-mode=full \ + --vfs-cache-max-size=5g \ + --vfs-cache-max-age=120m \ + --log-level=INFO "$@" + ''; + + inherit (lib.modules) mkMerge mkForce; + inherit (builtins) toFile; +in { + environment.systemPackages = with pkgs; [ + rclone + rclonePhotos + fuse + fuse3 + mountPhotos + ]; + + systemd.tmpfiles.rules = [ + "d /PhotosCache - storage storage" + "d /Photos - storage storage" + ]; + + users.users.photoprism = { + isSystemUser = true; + uid = 1290; + group = "photoprism"; + }; + users.groups.photoprism.gid = 1290; + services.photoprism = { enable = true; + originalsPath = "/Photos"; }; services.nginx.virtualHosts."vault.owo.monster" = { @@ -19,4 +65,16 @@ }; }; }; -} + + programs.fuse.userAllowOther = true; + + systemd.services.photos-mount = { + wantedBy = ["photoprism.service"]; + partOf = ["photoprism.service"]; + path = with pkgs; [ + fuse + fuse3 + ]; + serviceConfig.ExecStart = "${mountPhotos}/bin/mount-photos --syslog"; + }; +} \ No newline at end of file diff --git a/hosts/hetzner-arm/secrets.nix b/hosts/hetzner-arm/secrets.nix index 9efc963..4f5e2f9 100644 --- a/hosts/hetzner-arm/secrets.nix +++ b/hosts/hetzner-arm/secrets.nix @@ -9,6 +9,7 @@ packages = with pkgs; [ apacheHttpd + rclone ]; requiredVaultPaths = [ @@ -22,6 +23,8 @@ "private-public-keys/data/restic/Forgejo" + "private-public-keys/data/rclone/Chaos-Photos-Crypt" + "api-keys/data/mpd" "api-keys/data/music-stream" @@ -31,6 +34,39 @@ "private-public-keys/data/restic/Vault" ]; + extraFunctions = '' + replace_slash_for_sed() { + sed "s#/#\\\/#" + } + + simple_get_obscure() { + rclone obscure "$(simple_get "$@")" + } + + simple_get_replace_b2() { + api_account=$(simple_get "$1" .keyID | replace_slash_for_sed) + api_key=$(simple_get "$1" .applicationKey | replace_slash_for_sed) + + replace_account=''${2}_ACCOUNT + replace_key=''${2}_KEY + + sed -i "s/$replace_account/$api_account/" "$3" + sed -i "s/$replace_key/$api_key/" "$3" + } + + simple_get_replace_crypt() { + password=$(simple_get_obscure "$1" .password) + salt=$(simple_get_obscure "$1" .salt) + + replace_password=''${2}_PASSWORD + replace_salt=''${2}_SALT + + sed -i "s/$replace_password/$password/" "$3" + sed -i "s/$replace_salt/$salt/" "$3" + } + ''; + + secrets = { vault_password = { manual = true; @@ -140,6 +176,35 @@ simple_get "/private-public-keys/restic/Vault" .password > "$secretFile" ''; }; + + photos_rclone_config = { + user = "photoprism"; + group = "photoprism"; + fetchScript = let + template = builtins.toFile "template.conf" '' + [B2] + type = b2 + account = B2_ACCOUNT + key = B2_KEY + hard_delete = true + + [Photos-Crypt] + type = crypt + remote = B2:Chaos-Photos + password = PHOTOS_CRYPT_PASSWORD + password2 = PHOTOS_CRYPT_SALT + + [Photos] + type = chunker + remote = Photos-Crypt: + chunk_size = 128Mi + ''; + in '' + cat ${template} > "$secretFile" + simple_get_replace_b2 "/api-keys/backblaze/Backblaze" "B2" "$secretFile" + simple_get_replace_crypt "/private-public-keys/rclone/Chaos-Photos-Crypt" "PHOTOS_CRYPT" "$secretFile" + ''; + }; }; }; }