From 88f68cd698cb8a9db09048c192738f09168ba002 Mon Sep 17 00:00:00 2001 From: Ruben Hensen Date: Sat, 24 Oct 2026 14:10:30 +0200 Subject: [PATCH] immich: share albums that kaderleden create automatically MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nobody uses the group account anymore, so new albums belong to the leiding who make them, and share-sync only shared albums the group account was in. It now first adds the group account as editor to every album an uploader owns (straight in the database: the API only lets an album's owner or editors share it), after which the album goes to everyone like the others. Albums whose name starts with "Privé" or "Prive" are left alone. --- immich/share-sync.yaml | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/immich/share-sync.yaml b/immich/share-sync.yaml index 68e7987..85a0aa5 100644 --- a/immich/share-sync.yaml +++ b/immich/share-sync.yaml @@ -1,5 +1,7 @@ # Shares the Scouting MBG photos with every immich user. immich has no user # groups (on its roadmap as "Soon"), so this does it per user: +# - albums that uploaders (kaderleden) create get the group account as +# editor, unless their name starts with "Privé" or "Prive"; # - every album the group account owns or edits gets every user, as editor # when they may upload (quota other than 0, which authentik sets from # Scouts Online) and as viewer otherwise; @@ -11,8 +13,9 @@ # - sessions of OAuth users older than SESSION_MAX_AGE_DAYS are ended, so # someone who left the group loses access at the next login at Scouts # Online. -# Albums and partners go through the API; the timeline flag (which only -# the receiving user can set through the API), the notifications and the +# Albums and partners go through the API. Joining other people's albums +# (the API only lets an album's owner or editors share it), the timeline +# flag (which only the receiving user can set), the notifications and the # sessions go straight to the database. apiVersion: v1 kind: ConfigMap @@ -69,6 +72,34 @@ data: # says: no uploads. wanted = {u[0]: "viewer" if u[3] == "0" else "editor" for u in users if u[0] in members} + # Albums of uploaders that the group account isn't in yet + UNJOINED = """ + FROM album_user au + JOIN "user" u ON u.id = au."userId" + JOIN album a ON a.id = au."albumId" + WHERE au.role = 'owner' AND u.id != :'group' + AND u."deletedAt" IS NULL AND a."deletedAt" IS NULL + AND (u."quotaSizeInBytes" IS NULL OR u."quotaSizeInBytes" > 0) + AND a."albumName" !~* '^\\s*priv[eé]' + AND NOT EXISTS (SELECT 1 FROM album_user g WHERE g."albumId" = a.id AND g."userId" = :'group') + """ + if DRY_RUN: + for (name,) in sql("SELECT a.\"albumName\"" + UNJOINED, group=group_id): + print(f"would join {name}", flush=True) + else: + for (name,) in sql( + f""" + WITH joined AS ( + INSERT INTO album_user ("albumId", "userId", role) + SELECT a.id, :'group', 'editor' {UNJOINED} + RETURNING "albumId" + ) + SELECT a."albumName" FROM joined JOIN album a ON a.id = joined."albumId" + """, + group=group_id, + ): + print(f"join {name}", flush=True) + for album in api("GET", "/albums"): roles = {au["user"]["id"]: au["role"] for au in album["albumUsers"]} if roles.get(group_id) not in ("owner", "editor"): @@ -181,6 +212,9 @@ spec: value: immich-postgres-17-rw - name: PGSSLMODE value: require + # album names are UTF-8 (and so is the Privé check) + - name: PGCLIENTENCODING + value: UTF8 - name: PGDATABASE value: immich - name: PGUSER