fix(journey): serve local file when uploading photos with Immich sync enabled

After upload, trek_photos.provider is immediately flipped to 'immich' even
though Immich's thumbnail generation is async. streamPhoto then routed to
Immich, which returned an error for the not-yet-processed asset. Because
Cache-Control was set before the proxy attempt, the error response was cached
by the browser for 24h — breaking thumbnails until a hard refresh bypassed
the cache and Immich had finished processing.

- streamPhoto now prefers the local file_path when it exists on disk,
  regardless of provider; Immich/Synology are only used when no local
  file is available (fixes the immediate broken-thumbnail symptom)
- pipeAsset sets Cache-Control: no-store on upstream errors and uses the
  caller-supplied default only on success (prevents cache poisoning)
- streamImmichAsset no longer pre-sets Cache-Control before the proxy
- streamSynologyAsset passes the same defaultCacheControl through pipeAsset

Closes #691
This commit is contained in:
jubnl
2026-04-16 21:20:38 +02:00
parent f7c6854059
commit ae4d317dc3
4 changed files with 20 additions and 12 deletions
@@ -69,15 +69,18 @@ export async function streamPhoto(
return;
}
if (photo.file_path) {
const localPath = path.join(__dirname, '../../../uploads', photo.file_path);
if (fs.existsSync(localPath)) {
res.set('Cache-Control', 'public, max-age=86400');
res.sendFile(localPath);
return;
}
}
switch (photo.provider) {
case 'local': {
const filePath = path.join(__dirname, '../../../uploads', photo.file_path!);
if (!fs.existsSync(filePath)) {
res.status(404).json({ error: 'File not found' });
return;
}
res.set('Cache-Control', 'public, max-age=86400');
res.sendFile(filePath);
res.status(404).json({ error: 'File not found' });
return;
}
case 'immich': {