Commit graph

8 commits

Author SHA1 Message Date
ScreenTinker 2954fd1a84 Phase 2.1: tenancy middleware, permission helpers, JWT workspace context, frontend + backend role-rename compat 2026-05-11 20:02:00 -05:00
ScreenTinker 470197d203 Fix 8 security findings from Phase 3 audit + device-detail banner refresh
Security fixes:
- Critical: Add ownership checks to assignments PUT/:id and DELETE/:id (IDOR)
- Critical: Add ownership checks to assignments copy-to endpoint for both devices
- High: Validate device ownership when adding to device groups
- High: UUID-validate content ID before LIKE query + scope to owner's playlists
- Low: Handle FK violations gracefully in playlist discard (deleted content/widgets)
- Low: Escape mime_type with esc() in playlist item display (XSS)

Bug fix:
- Device-detail mutation handlers now reload full page to show draft banner

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 21:36:16 -05:00
ScreenTinker 436a3be7f6 Phase 3: playlist publish/draft state with auto-publish from device detail
Schema: add status and published_snapshot columns to playlists table.
Migration snapshots all existing playlists as published (idempotent via schema_migrations).

Devices always receive the published_snapshot, not live playlist_items.
Edits from device-detail/groups auto-publish immediately (display updates instantly).
Edits from playlist detail page go to draft (requires explicit publish).
POST /playlists/:id/publish snapshots and pushes to all devices.
POST /playlists/:id/discard reverts playlist_items from published snapshot.
Content deletion scrubs references from all published snapshots.

Frontend: draft badge in playlist list, prominent yellow banner with publish/discard
buttons on playlist detail and device detail pages.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 20:52:29 -05:00
ScreenTinker 6f01d319f5 Phase 2: playlists API returns display_count, is_auto_generated + assign endpoint
List and detail endpoints now include display_count (devices using this playlist).
New POST /:id/assign endpoint sets a playlist on a device and pushes the update.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-11 22:09:32 -05:00
ScreenTinker 19fc38a59e Make ffprobe re-probe async to avoid blocking the event loop
Swap execFileSync to execFile with promise wrapper in
probeAndUpdateDuration(). Wrap the add-item handler in try/catch
for Express 4.x async safety (Express 4 doesn't catch rejected
promises from async handlers).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-11 21:30:11 -05:00
ScreenTinker 1ad390229b Re-probe video duration with ffprobe when adding to playlist
If a video's duration_sec is NULL in the content table (e.g. ffprobe
wasn't available at upload time), re-probe it when the content is
added to a playlist. Backfills the content table so subsequent adds
skip the probe. Non-video content and probe failures fall back to
the 10s default.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-11 21:24:41 -05:00
ScreenTinker adb107f228 Use content's native duration for videos added to playlists
When adding a content item to a playlist without an explicit
duration_sec, use the content's own duration (from ffprobe at upload
time) instead of defaulting to 10s. Falls back to 10s for images
or content without a detected duration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-11 21:23:07 -05:00
ScreenTinker e262216c58 Add playlist API routes with full CRUD and item management
Routes: GET/POST /playlists, GET/PUT/DELETE /playlists/:id,
GET/POST /playlists/:id/items, PUT/DELETE /playlists/:id/items/:itemId,
POST /playlists/:id/items/reorder.

Follows device-groups.js patterns: ownership middleware, parameterized
queries, content/widget ownership validation, input validation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-11 21:09:58 -05:00