fix(synology): correct multi-album passphrase assignment and stale trek_photos

- ProviderPicker now tracks per-asset album passphrase in a Map; on confirm,
  assets are grouped by passphrase and submitted as separate batches so each
  asset receives its own album's passphrase instead of the last-selected one
- getOrCreateTrekPhoto unconditionally overwrites the stored passphrase when
  a fresh one is supplied, allowing re-adds to heal a stuck bad passphrase
- deleteTrekPhotoIfOrphan purges the trek_photos row for provider assets when
  no trip_photos or journey_photos reference it anymore; wired into
  removeTripPhoto, removeAlbumLink, and deletePhoto so remove + re-add is a
  clean slate
- Three new integration tests: SYNO-090 (passphrase overwrite), SYNO-091
  (orphan cleanup), SYNO-092 (remove + re-add restores correct passphrase)
This commit is contained in:
jubnl
2026-04-17 19:48:12 +02:00
parent c0aa252f9a
commit 8cd5aa0d23
5 changed files with 124 additions and 16 deletions
@@ -22,7 +22,7 @@ export function getOrCreateTrekPhoto(
).get(provider, assetId, ownerId) as { id: number } | undefined;
if (existing) {
if (passphrase) {
db.prepare('UPDATE trek_photos SET passphrase = ? WHERE id = ? AND passphrase IS NULL')
db.prepare('UPDATE trek_photos SET passphrase = ? WHERE id = ?')
.run(encrypt_api_key(passphrase), existing.id);
}
return existing.id;
@@ -145,6 +145,19 @@ export function setTrekPhotoProvider(
).run(provider, assetId, ownerId, trekPhotoId);
}
// ── Orphan cleanup ───────────────────────────────────────────────────────
export function deleteTrekPhotoIfOrphan(photoId: number): void {
const stillUsed = db.prepare(`
SELECT 1 FROM trip_photos WHERE photo_id = ?
UNION ALL
SELECT 1 FROM journey_photos WHERE photo_id = ?
LIMIT 1
`).get(photoId, photoId);
if (stillUsed) return;
db.prepare("DELETE FROM trek_photos WHERE id = ? AND provider != 'local'").run(photoId);
}
// ── Delete local file for a trek_photo ──────────────────────────────────
export function getTrekPhotoFilePath(photoId: number): string | null {