mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-19 13:21:46 +00:00
fix(journey): resolve Immich photos on public share by matching trek_photos.id
validateShareTokenForPhoto was querying journey_photos by jp.id but the public page sends p.photo_id (trek_photos.id) in the URL. In a fresh database the IDs coincidentally match, masking the bug. In production instances with many Immich-synced photos the trek_photos autoincrement is far ahead of journey_photos, causing a 404 for every Immich photo on the public share page. Fix: change the lookup to jp.photo_id = ? so validation is keyed on trek_photos.id, which is what the client sends and what streamPhoto needs. Updated the test helper to return trekId and added a regression test that pre-populates trek_photos to produce diverging IDs. Closes #675.
This commit is contained in:
@@ -63,7 +63,7 @@ export function validateShareTokenForPhoto(token: string, photoId: number): { jo
|
||||
FROM journey_photos jp
|
||||
JOIN trek_photos tkp ON tkp.id = jp.photo_id
|
||||
JOIN journey_entries je ON jp.entry_id = je.id
|
||||
WHERE jp.id = ? AND je.journey_id = ?
|
||||
WHERE jp.photo_id = ? AND je.journey_id = ?
|
||||
`).get(photoId, row.journey_id) as any;
|
||||
if (!photo) return null;
|
||||
const journey = db.prepare('SELECT user_id FROM journeys WHERE id = ?').get(row.journey_id) as any;
|
||||
|
||||
@@ -58,7 +58,7 @@ afterAll(() => {
|
||||
|
||||
// -- Helpers ------------------------------------------------------------------
|
||||
|
||||
/** Insert a journey_photos row and return its id. */
|
||||
/** Insert a trek_photos + journey_photos row and return the trek_photos id (used as photoId in public URLs). */
|
||||
function insertJourneyPhoto(
|
||||
entryId: number,
|
||||
opts: { filePath?: string; assetId?: string; ownerId?: number } = {}
|
||||
@@ -70,11 +70,13 @@ function insertJourneyPhoto(
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
`).run(provider, opts.assetId ?? null, filePath, opts.ownerId ?? null, Date.now());
|
||||
const trekId = trekResult.lastInsertRowid as number;
|
||||
const result = testDb.prepare(`
|
||||
testDb.prepare(`
|
||||
INSERT INTO journey_photos (entry_id, photo_id, caption, sort_order, created_at)
|
||||
VALUES (?, ?, NULL, 0, ?)
|
||||
`).run(entryId, trekId, Date.now());
|
||||
return result.lastInsertRowid as number;
|
||||
// Return trek_photos.id — this is p.photo_id in the public API response
|
||||
// and the value the client sends to /api/public/journey/:token/photos/:photoId/:kind
|
||||
return trekId;
|
||||
}
|
||||
|
||||
// -- Tests --------------------------------------------------------------------
|
||||
@@ -237,6 +239,31 @@ describe('validateShareTokenForPhoto', () => {
|
||||
expect(result).not.toBeNull();
|
||||
expect(result!.ownerId).toBe(user.id);
|
||||
});
|
||||
|
||||
it('JOURNEY-SHARE-016: resolves correctly when trek_photos.id differs from journey_photos.id (Immich bulk-sync scenario)', () => {
|
||||
// Simulate a user who has many trek_photos from Immich syncs before adding a journey photo.
|
||||
// trek_photos.id will be higher than journey_photos.id — the previous bug matched on jp.id
|
||||
// instead of jp.photo_id, causing a 404 for Immich photos in public shares.
|
||||
const { user } = createUser(testDb);
|
||||
const journey = createJourney(testDb, user.id);
|
||||
const entry = createJourneyEntry(testDb, journey.id, user.id);
|
||||
|
||||
// Pre-populate trek_photos to push the autoincrement higher
|
||||
for (let i = 0; i < 5; i++) {
|
||||
testDb.prepare(`INSERT INTO trek_photos (provider, asset_id, owner_id, created_at) VALUES ('immich', ?, ?, ?)`).run(`bulk-asset-${i}`, user.id, Date.now());
|
||||
}
|
||||
|
||||
// This trek_photos row gets a high id (e.g. 6) while journey_photos id will be 1
|
||||
const trekPhotoId = insertJourneyPhoto(entry.id, { assetId: 'journey-asset-xyz', ownerId: user.id });
|
||||
const { token } = createOrUpdateJourneyShareLink(journey.id, user.id, {});
|
||||
|
||||
// photoId = trek_photos.id (6), not journey_photos.id (1)
|
||||
const result = validateShareTokenForPhoto(token, trekPhotoId);
|
||||
|
||||
expect(result).not.toBeNull();
|
||||
expect(result!.ownerId).toBe(user.id);
|
||||
expect(result!.journeyId).toBe(journey.id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateShareTokenForAsset', () => {
|
||||
|
||||
Reference in New Issue
Block a user