1{ pkgs, lib, ... }:
2{
3 name = "immich-public-proxy";
4
5 nodes.machine =
6 { pkgs, ... }@args:
7 {
8 environment.systemPackages = [
9 pkgs.imagemagick
10 pkgs.immich-cli
11 ];
12 services.immich = {
13 enable = true;
14 port = 2283;
15 # disable a lot of features that aren't needed for this test
16 machine-learning.enable = false;
17 settings = {
18 backup.database.enabled = false;
19 machineLearning.enabled = false;
20 map.enabled = false;
21 reverseGeocoding.enabled = false;
22 metadata.faces.import = false;
23 newVersionCheck.enabled = false;
24 notifications.smtp.enabled = false;
25 };
26 };
27 services.immich-public-proxy = {
28 enable = true;
29 immichUrl = "http://localhost:2283";
30 port = 8002;
31 settings.ipp.responseHeaders."X-NixOS" = "Rules";
32 };
33
34 # TODO: Remove when PostgreSQL 17 is supported.
35 services.postgresql.package = pkgs.postgresql_16;
36 };
37
38 testScript = ''
39 import json
40
41 machine.wait_for_unit("immich-server.service")
42 machine.wait_for_unit("immich-public-proxy.service")
43 machine.wait_for_open_port(2283)
44 machine.wait_for_open_port(8002)
45
46 # The proxy should be up
47 machine.succeed("curl -sf http://localhost:8002")
48
49 # Verify the static assets are served
50 machine.succeed("curl -sf http://localhost:8002/robots.txt")
51 machine.succeed("curl -sf http://localhost:8002/share/static/style.css")
52
53 # Check that the response header in the settings is sent
54 res = machine.succeed("""
55 curl -sD - http://localhost:8002 -o /dev/null
56 """)
57 assert "x-nixos: rules" in res.lower(), res
58
59 # Log in to Immich and create an access key
60 machine.succeed("""
61 curl -sf --json '{ "email": "test@example.com", "name": "Admin", "password": "admin" }' http://localhost:2283/api/auth/admin-sign-up
62 """)
63 res = machine.succeed("""
64 curl -sf --json '{ "email": "test@example.com", "password": "admin" }' http://localhost:2283/api/auth/login
65 """)
66 token = json.loads(res)['accessToken']
67 res = machine.succeed("""
68 curl -sf -H 'Cookie: immich_access_token=%s' --json '{ "name": "API Key", "permissions": ["all"] }' http://localhost:2283/api/api-keys
69 """ % token)
70 key = json.loads(res)['secret']
71 machine.succeed(f"immich login http://localhost:2283/api {key}")
72 res = machine.succeed("immich server-info")
73 print(res)
74
75 # Upload some blank images to a new album
76 # If there's only one image, the proxy serves the image directly
77 machine.succeed("magick -size 800x600 canvas:white /tmp/white.png")
78 machine.succeed("immich upload -A '✨ Reproducible Moments ✨' /tmp/white.png")
79 machine.succeed("magick -size 800x600 canvas:black /tmp/black.png")
80 machine.succeed("immich upload -A '✨ Reproducible Moments ✨' /tmp/black.png")
81 res = machine.succeed("immich server-info")
82 print(res)
83
84 # Get the new album id
85 res = machine.succeed("""
86 curl -sf -H 'Cookie: immich_access_token=%s' http://localhost:2283/api/albums
87 """ % token)
88 album_id = json.loads(res)[0]['id']
89
90 # Create a shared link
91 res = machine.succeed("""
92 curl -sf -H 'Cookie: immich_access_token=%s' --json '{ "albumId": "%s", "type": "ALBUM" }' http://localhost:2283/api/shared-links
93 """ % (token, album_id))
94 share_key = json.loads(res)['key']
95
96 # Access the share
97 machine.succeed("""
98 curl -sf http://localhost:2283/share/%s
99 """ % share_key)
100
101 # Access the share through the proxy
102 machine.succeed("""
103 curl -sf http://localhost:8002/share/%s
104 """ % share_key)
105 '';
106}