Skip to content

fix: route song update, delete, lyrics, cover, versions and report endpoints - #851

Merged
Akatenvictor merged 2 commits into
AudioBitsStellar:mainfrom
zane1502:fix/song-routes
Oct 18, 2026
Merged

Akatenvictor merged 2 commits into
AudioBitsStellar:mainfrom
zane1502:fix/song-routes

Conversation

@zane1502

Copy link
Copy Markdown
Contributor

Summary

Routes the song handlers that already existed but were never reachable: update/delete, lyrics, cover art, version history and content reports. Update and delete are made safe to route (field allow-list, ownership checks, storage cleanup).

Changes

  • PATCH /api/song/:id and DELETE /api/song/:id (authArtistMiddleware). New UpdateSongDTO allow-lists title, description, genreId, composers, lyrics, language (validateDTO strips everything else, and the service only copies those fields). Ownership (owner or admin) is enforced in SongService, returning 403. For a minted song, changing title, description or genreId (the fields in the on-chain metadata) returns 409. Search index and CacheService.clearSong are refreshed.
  • Delete: an unminted song is removed and its original upload, HLS prefix (songs/<id>/hls/) and S3-hosted cover art are deleted from S3 (best-effort, errors are logged). A minted or pending song is hidden instead (flagged, removed from search and streaming), since the on-chain token still exists; the response has data.hidden. There is no waveform asset in the codebase, so none is deleted. Cleanup runs inline rather than through a queue.
  • GET /api/song/:id/lyrics (public). SongService.getLyrics now returns 404 unless lyricsRightsConfirmedAt is set.
  • PUT /api/song/:id/cover (owner only, multipart field cover, JPG/PNG, 5 MB, same filter and limit as the existing cover upload; the existing magic-byte check in CoverArtService still applies) and GET /api/song/:id/cover (returns the original and thumbnail URLs). Replacing a cover regenerates thumbnails (existing behavior), deletes the previous cover from S3 if it lived there, and clears the song cache. For a minted song only the off-chain cover changes; the image in the on-chain metadata is immutable (documented in OpenAPI and code).
  • GET /api/song/:id/versions (paginated, page/limit) and GET /api/song/:id/versions/:version, restricted to the owning artist and admins (403 otherwise) via a new SongVersionService.assertCanViewHistory.
  • POST /api/song/:id/report with requireAuth, validateDTO(ReportSongDTO) and a new per-user limit of 10 reports per hour (429). A duplicate report returns 409 (existing ReportService behavior, documented). Reports are stored as pending and are listed by the existing GET /api/admin/reports.
  • OpenAPI entries for all new routes in docs/openapi.yaml.
  • Tests: new src/__tests__/SongRoutes.test.ts (ownership, protected fields, minted conflict, S3 deletion with a mocked S3 client, lyrics gating, cover type/size/ownership, versions access and pagination, report duplicate/rate limit/admin queue); updated SongVersionService.test.ts and CoverArtService.test.ts.

Notes and limits:

  • src/validators/ImageFile.ts provides class-validator decorators for DTO fields, which do not apply to a multer upload, so the route reuses the existing multer JPG/PNG filter and size limit instead.
  • Lyrics confirmation is currently only recorded by the lyric transcription flow, so lyrics entered via PATCH are not served until rights are confirmed.
  • The route parameter for a single version is the version number (:version), matching the existing controller and service.

Issues

Resolves #817
Resolves #816
Resolves #815
Resolves #813

Verification

  • Manual review of the complete diff and git status; only relevant files are staged, no snapshots or generated files.
  • cargo build and cargo test were not run (TypeScript repository).
  • Ran npx jest src/__tests__/SongRoutes.test.ts src/__tests__/CoverArtService.test.ts src/services/__tests__/SongVersionService.test.ts src/__tests__/SongService.test.ts --forceExit: 55 passed, 2 failed. The 2 failures are in CoverArtService.test.ts ("pins the original and three thumbnails" and "accepts png and webp uploads"); they fail identically on unmodified main because the fixture buffer is not a valid image under the existing magic-byte check.
  • npx tsc --noEmit reports no errors in the touched files (filtered to them).
  • The full test suite was not run, and nothing was exercised against a real database, Redis or S3.

@drips-wave

drips-wave Bot commented Oct 18, 2026

Copy link
Copy Markdown

@zane1502 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@Akatenvictor
Akatenvictor merged commit 7193058 into AudioBitsStellar:main Oct 18, 2026
0 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants