[api] only show public images in slideshow

This commit is contained in:
Devin Zuczek
2026-09-29 23:09:36 -04:00
parent e8dcaa3051
commit ef5bfece59
3 changed files with 42 additions and 8 deletions
+5 -4
View File
@@ -468,10 +468,11 @@ export const imageRoutes = new Hono<App>({ strict: false })
}
)
// Global slideshow feed — the most recent publicly-listable ShareCamera photos
// (Accessibility 0 or 1, Type 1) across all rooms, newest first, each joined to its
// creator's username and room name. Public (no auth): it only surfaces already-public
// images and backs the anonymous homepage slideshow. Returns `{ Images, ValidTill }`,
// Global slideshow feed — the most recent public ShareCamera photos (Accessibility 1,
// Type 1) across all rooms, newest first, each joined to its creator's username and
// room name. Public (no auth): it only surfaces already-public images and backs the
// anonymous homepage slideshow. Private photos (Accessibility 0) never appear — the
// feed is anonymous, so there is no viewer it could be unlocked for. Returns `{ Images, ValidTill }`,
// where ValidTill is a short (2-minute) cache hint the client refreshes against.
// Serves 10 by default and never more than SLIDESHOW_MAX_LIMIT (100): it's public and
// unauthenticated, so an unclamped `take` would let anyone ask for the whole image
+31
View File
@@ -6550,6 +6550,37 @@ describe('images', () => {
})
})
// The feed is anonymous, so a private photo (Accessibility 0) must never reach it —
// there is no viewer it could be unlocked for. It once accepted 0 alongside 1.
test('GET /api/images/v1/slideshow leaves private photos out', async () => {
const pub = await createImage(env.DB, {
imageName: 'slidepublic.jpg',
playerId: 42,
accessibility: 1,
})
const priv = await createImage(env.DB, {
imageName: 'slideprivate.jpg',
playerId: 42,
accessibility: 0,
})
// A non-photo (room thumbnail) that happens to be public stays out too.
const thumb = await createImage(env.DB, {
imageName: 'slidethumb.jpg',
playerId: 42,
type: 3,
accessibility: 1,
})
const res = await exports.default.fetch(`${ORIGIN}/api/images/v1/slideshow?take=100`)
expect(res.status).toBe(200)
const ids = ((await res.json()) as { Images: Array<{ SavedImageId: number }> }).Images.map(
(i) => i.SavedImageId
)
expect(ids).toContain(pub.Id)
expect(ids).not.toContain(priv.Id)
expect(ids).not.toContain(thumb.Id)
})
// The feed is public and unauthenticated, so `take` is clamped rather than trusted:
// without the cap a single anonymous request could pull the whole image table through
// the two joins behind it.