2026-02-14 17:04:37 +00:00
|
|
|
package api
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
|
"github.com/google/uuid"
|
|
|
|
|
"github.com/redis/go-redis/v9"
|
|
|
|
|
"go.uber.org/zap"
|
|
|
|
|
|
2026-03-09 00:52:56 +00:00
|
|
|
discovercore "veza-backend-api/internal/core/discover"
|
2026-02-14 17:04:37 +00:00
|
|
|
trackcore "veza-backend-api/internal/core/track"
|
|
|
|
|
"veza-backend-api/internal/handlers"
|
|
|
|
|
"veza-backend-api/internal/services"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// setupTrackRoutes configure les routes de gestion des tracks
|
|
|
|
|
func (r *APIRouter) setupTrackRoutes(router *gin.RouterGroup) {
|
|
|
|
|
uploadDir := r.config.UploadDir
|
|
|
|
|
if uploadDir == "" {
|
|
|
|
|
uploadDir = "uploads/tracks"
|
|
|
|
|
}
|
|
|
|
|
chunksDir := uploadDir + "/chunks"
|
|
|
|
|
|
2026-02-14 21:50:23 +00:00
|
|
|
trackService := trackcore.NewTrackServiceWithDB(r.db, r.logger, uploadDir)
|
2026-02-14 17:04:37 +00:00
|
|
|
if r.config.CacheService != nil {
|
|
|
|
|
trackService.SetCacheService(r.config.CacheService)
|
|
|
|
|
}
|
2026-02-22 16:52:39 +00:00
|
|
|
streamService := services.NewStreamServiceWithAPIKey(r.config.StreamServerURL, r.config.StreamServerInternalAPIKey, r.logger)
|
|
|
|
|
trackService.SetStreamService(streamService) // INT-02: Enable HLS pipeline for regular uploads
|
2026-03-09 00:52:56 +00:00
|
|
|
discoverService := discovercore.NewService(r.db.GormDB, r.logger)
|
|
|
|
|
trackService.SetDiscoverService(discoverService) // v0.10.1: tags/genres sync
|
2026-02-14 17:04:37 +00:00
|
|
|
trackUploadService := services.NewTrackUploadService(r.db.GormDB, r.logger)
|
|
|
|
|
var redisClient *redis.Client
|
|
|
|
|
if r.config != nil {
|
|
|
|
|
redisClient = r.config.RedisClient
|
|
|
|
|
}
|
|
|
|
|
chunkService := services.NewTrackChunkService(chunksDir, redisClient, r.logger)
|
|
|
|
|
likeService := services.NewTrackLikeService(r.db.GormDB, r.logger)
|
|
|
|
|
|
|
|
|
|
trackHandler := trackcore.NewTrackHandler(
|
|
|
|
|
trackService,
|
|
|
|
|
trackUploadService,
|
|
|
|
|
chunkService,
|
|
|
|
|
likeService,
|
|
|
|
|
streamService,
|
|
|
|
|
)
|
chore: consolidate CI, E2E, backend and frontend updates
- CI: workflows updates (cd, ci), remove playwright.yml
- E2E: global-setup, auth/playlists/profile specs
- Remove playwright-report and test-results artifacts from tracking
- Backend: auth, handlers, services, workers, migrations
- Frontend: components, features, vite config
- Add e2e-results.json to gitignore
- Docs: REMEDIATION_PROGRESS, audit archive
- Rust: chat-server, stream-server updates
2026-02-17 15:43:21 +00:00
|
|
|
if r.config != nil {
|
|
|
|
|
if r.config.PermissionService != nil {
|
|
|
|
|
trackHandler.SetPermissionService(r.config.PermissionService)
|
|
|
|
|
}
|
|
|
|
|
if r.config.JobWorker != nil {
|
|
|
|
|
trackHandler.SetJobEnqueuer(r.config.JobWorker)
|
|
|
|
|
}
|
2026-02-14 17:04:37 +00:00
|
|
|
}
|
|
|
|
|
uploadConfig := getUploadConfigWithEnv()
|
|
|
|
|
uploadValidator, err := services.NewUploadValidator(uploadConfig, r.logger)
|
|
|
|
|
if err != nil {
|
|
|
|
|
r.logger.Warn("Upload validator created with ClamAV unavailable - uploads will be rejected", zap.Error(err))
|
|
|
|
|
uploadConfig.ClamAVEnabled = false
|
|
|
|
|
uploadValidator, _ = services.NewUploadValidator(uploadConfig, r.logger)
|
|
|
|
|
}
|
|
|
|
|
trackHandler.SetUploadValidator(uploadValidator)
|
|
|
|
|
|
2026-02-14 21:50:23 +00:00
|
|
|
trackSearchService := services.NewTrackSearchServiceWithDB(r.db)
|
2026-02-14 17:04:37 +00:00
|
|
|
trackHandler.SetSearchService(trackSearchService)
|
|
|
|
|
|
|
|
|
|
trackVersionService := services.NewTrackVersionService(r.db.GormDB, r.logger, uploadDir)
|
|
|
|
|
trackHandler.SetVersionService(trackVersionService)
|
|
|
|
|
|
|
|
|
|
playbackAnalyticsService := services.NewPlaybackAnalyticsService(r.db.GormDB, r.logger)
|
|
|
|
|
trackHandler.SetPlaybackAnalyticsService(playbackAnalyticsService)
|
|
|
|
|
|
2026-02-15 15:10:33 +00:00
|
|
|
trackHistoryService := services.NewTrackHistoryService(r.db.GormDB, r.logger)
|
|
|
|
|
trackHandler.SetHistoryService(trackHistoryService)
|
|
|
|
|
|
2026-02-16 09:23:41 +00:00
|
|
|
licenseChecker := services.NewDBTrackDownloadLicenseChecker(r.db.GormDB, r.logger)
|
|
|
|
|
trackHandler.SetLicenseChecker(licenseChecker)
|
|
|
|
|
|
2026-02-21 15:41:39 +00:00
|
|
|
if r.notificationService == nil {
|
|
|
|
|
r.notificationService = services.NewNotificationService(r.db, r.logger)
|
|
|
|
|
if r.pushService != nil {
|
|
|
|
|
r.notificationService.SetPushService(r.pushService)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
trackHandler.SetNotificationService(r.notificationService)
|
chore: consolidate CI, E2E, backend and frontend updates
- CI: workflows updates (cd, ci), remove playwright.yml
- E2E: global-setup, auth/playlists/profile specs
- Remove playwright-report and test-results artifacts from tracking
- Backend: auth, handlers, services, workers, migrations
- Frontend: components, features, vite config
- Add e2e-results.json to gitignore
- Docs: REMEDIATION_PROGRESS, audit archive
- Rust: chat-server, stream-server updates
2026-02-17 15:43:21 +00:00
|
|
|
|
feat(v0.10.3): Commentaires & Interactions Sociales - F201-F215
- F201: Commentaires avec timestamp cliquable, modération mots-clés
- F202: Likes privés (compteur visible créateur uniquement)
- F203: Reposts de tracks sur le profil, bouton Repost, onglet Reposts
- F204: Notifications (commentaire, repost), pas de gamification
Backend: migrations 127/128, comment_moderation_service, track_repost_service,
GetTrackLikes/GetTrack masquent like_count pour non-créateurs
Frontend: LikeButton isCreator, RepostButton, Reposts tab profil, timestamp seek
2026-03-09 09:30:47 +00:00
|
|
|
repostService := services.NewTrackRepostService(r.db.GormDB)
|
|
|
|
|
trackHandler.SetRepostService(repostService) // v0.10.3 F203
|
|
|
|
|
|
feat(release): v0.202 — Lots G, H, F, C, D
- Lot G: Recherche avancée (musical_key, tri pertinence, autocomplete, facettes, historique)
- Lot H: Analytics créateur (stats, charts, completion rate, export CSV/JSON)
- Lot F: Seller dashboard (GET /sell/stats, liste produits)
- Lot C: Player (crossfade, gapless preload, PiP)
- Lot D2: Autoplay (GET /tracks/recommendations, section À écouter ensuite)
Backend: GetRecommendations handler, route /tracks/recommendations
Frontend: PlayerQueue recommendations, fix TS errors (GlobalPlayer, AnalyticsViewKpiGrid, etc.)
Docs: FEATURE_STATUS, PROJECT_STATE, CHANGELOG, SCOPE_CONTROL
2026-02-20 17:16:17 +00:00
|
|
|
trackRecommendationService := services.NewTrackRecommendationService(r.db.GormDB, r.logger)
|
|
|
|
|
trackHandler.SetTrackRecommendationService(trackRecommendationService)
|
|
|
|
|
|
2026-02-22 17:16:37 +00:00
|
|
|
waveformService := services.NewWaveformService(r.db.GormDB, r.logger, r.config.S3StorageService)
|
|
|
|
|
if r.config.CacheService != nil {
|
|
|
|
|
waveformService.SetCacheService(r.config.CacheService)
|
|
|
|
|
}
|
|
|
|
|
trackHandler.SetWaveformService(waveformService)
|
|
|
|
|
|
2026-02-14 17:04:37 +00:00
|
|
|
tracks := router.Group("/tracks")
|
|
|
|
|
{
|
feat: backend, stream server & infra improvements
Backend (Go):
- Config: CORS, RabbitMQ, rate limit, main config updates
- Routes: core, distribution, tracks routing changes
- Middleware: rate limiter, endpoint limiter, response cache hardening
- Handlers: distribution, search handler fixes
- Workers: job worker improvements
- Upload validator and logging config additions
- New migrations: products, orders, performance indexes
- Seed tooling and data
Stream Server (Rust):
- Audio processing, config, routes, simple stream server updates
- Dockerfile improvements
Infrastructure:
- docker-compose.yml updates
- nginx-rtmp config changes
- Makefile improvements (config, dev, high, infra)
- Root package.json and lock file updates
- .env.example updates
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:36:06 +00:00
|
|
|
if r.config.AuthMiddleware != nil {
|
|
|
|
|
tracks.GET("", r.config.AuthMiddleware.OptionalAuth(), trackHandler.ListTracks)
|
|
|
|
|
} else {
|
|
|
|
|
tracks.GET("", trackHandler.ListTracks)
|
|
|
|
|
}
|
2026-02-14 17:04:37 +00:00
|
|
|
tracks.GET("/search", trackHandler.SearchTracks)
|
2026-02-20 14:38:51 +00:00
|
|
|
tracks.GET("/suggested-tags", trackHandler.GetSuggestedTags)
|
feat: backend, stream server & infra improvements
Backend (Go):
- Config: CORS, RabbitMQ, rate limit, main config updates
- Routes: core, distribution, tracks routing changes
- Middleware: rate limiter, endpoint limiter, response cache hardening
- Handlers: distribution, search handler fixes
- Workers: job worker improvements
- Upload validator and logging config additions
- New migrations: products, orders, performance indexes
- Seed tooling and data
Stream Server (Rust):
- Audio processing, config, routes, simple stream server updates
- Dockerfile improvements
Infrastructure:
- docker-compose.yml updates
- nginx-rtmp config changes
- Makefile improvements (config, dev, high, infra)
- Root package.json and lock file updates
- .env.example updates
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:36:06 +00:00
|
|
|
if r.config.AuthMiddleware != nil {
|
|
|
|
|
tracks.GET("/:id", r.config.AuthMiddleware.OptionalAuth(), trackHandler.GetTrack)
|
|
|
|
|
} else {
|
|
|
|
|
tracks.GET("/:id", trackHandler.GetTrack)
|
|
|
|
|
}
|
2026-02-20 14:36:28 +00:00
|
|
|
tracks.GET("/:id/lyrics", trackHandler.GetLyrics)
|
2026-02-14 17:04:37 +00:00
|
|
|
tracks.GET("/:id/stats", trackHandler.GetTrackStats)
|
2026-02-22 17:16:37 +00:00
|
|
|
tracks.GET("/:id/waveform", trackHandler.GetWaveform)
|
2026-02-14 17:04:37 +00:00
|
|
|
tracks.GET("/:id/history", trackHandler.GetTrackHistory)
|
|
|
|
|
tracks.GET("/:id/download", trackHandler.DownloadTrack)
|
|
|
|
|
tracks.GET("/shared/:token", trackHandler.GetSharedTrack)
|
feat(v0.10.3): Commentaires & Interactions Sociales - F201-F215
- F201: Commentaires avec timestamp cliquable, modération mots-clés
- F202: Likes privés (compteur visible créateur uniquement)
- F203: Reposts de tracks sur le profil, bouton Repost, onglet Reposts
- F204: Notifications (commentaire, repost), pas de gamification
Backend: migrations 127/128, comment_moderation_service, track_repost_service,
GetTrackLikes/GetTrack masquent like_count pour non-créateurs
Frontend: LikeButton isCreator, RepostButton, Reposts tab profil, timestamp seek
2026-03-09 09:30:47 +00:00
|
|
|
if r.config.AuthMiddleware != nil {
|
|
|
|
|
tracks.GET("/:id/repost", r.config.AuthMiddleware.OptionalAuth(), trackHandler.GetRepostStatus) // v0.10.3 F203
|
|
|
|
|
} else {
|
|
|
|
|
tracks.GET("/:id/repost", trackHandler.GetRepostStatus)
|
|
|
|
|
}
|
2026-02-14 17:04:37 +00:00
|
|
|
|
|
|
|
|
if r.config.AuthMiddleware != nil {
|
|
|
|
|
protected := tracks.Group("")
|
|
|
|
|
protected.Use(r.config.AuthMiddleware.RequireAuth())
|
|
|
|
|
r.applyCSRFProtection(protected)
|
|
|
|
|
|
feat(release): v0.202 — Lots G, H, F, C, D
- Lot G: Recherche avancée (musical_key, tri pertinence, autocomplete, facettes, historique)
- Lot H: Analytics créateur (stats, charts, completion rate, export CSV/JSON)
- Lot F: Seller dashboard (GET /sell/stats, liste produits)
- Lot C: Player (crossfade, gapless preload, PiP)
- Lot D2: Autoplay (GET /tracks/recommendations, section À écouter ensuite)
Backend: GetRecommendations handler, route /tracks/recommendations
Frontend: PlayerQueue recommendations, fix TS errors (GlobalPlayer, AnalyticsViewKpiGrid, etc.)
Docs: FEATURE_STATUS, PROJECT_STATE, CHANGELOG, SCOPE_CONTROL
2026-02-20 17:16:17 +00:00
|
|
|
protected.GET("/recommendations", trackHandler.GetRecommendations)
|
|
|
|
|
|
2026-02-14 17:04:37 +00:00
|
|
|
uploadGroup := protected.Group("")
|
|
|
|
|
uploadGroup.Use(r.config.AuthMiddleware.RequireContentCreatorRole())
|
|
|
|
|
uploadGroup.POST("", trackHandler.UploadTrack)
|
|
|
|
|
|
|
|
|
|
trackOwnerResolver := func(c *gin.Context) (uuid.UUID, error) {
|
|
|
|
|
trackIDStr := c.Param("id")
|
|
|
|
|
trackID, err := uuid.Parse(trackIDStr)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return uuid.Nil, err
|
|
|
|
|
}
|
|
|
|
|
track, err := trackService.GetTrackByID(c.Request.Context(), trackID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return uuid.Nil, err
|
|
|
|
|
}
|
|
|
|
|
return track.UserID, nil
|
|
|
|
|
}
|
|
|
|
|
protected.PUT("/:id", r.config.AuthMiddleware.RequireOwnershipOrAdmin("track", trackOwnerResolver), trackHandler.UpdateTrack)
|
2026-02-20 14:36:28 +00:00
|
|
|
protected.PUT("/:id/lyrics", r.config.AuthMiddleware.RequireOwnershipOrAdmin("track", trackOwnerResolver), trackHandler.UpdateLyrics)
|
2026-02-14 17:04:37 +00:00
|
|
|
protected.DELETE("/:id", r.config.AuthMiddleware.RequireOwnershipOrAdmin("track", trackOwnerResolver), trackHandler.DeleteTrack)
|
|
|
|
|
|
|
|
|
|
protected.GET("/:id/status", trackHandler.GetUploadStatus)
|
|
|
|
|
protected.POST("/initiate", trackHandler.InitiateChunkedUpload)
|
|
|
|
|
protected.POST("/chunk", trackHandler.UploadChunk)
|
|
|
|
|
protected.POST("/complete", trackHandler.CompleteChunkedUpload)
|
|
|
|
|
protected.GET("/quota/:id", trackHandler.GetUploadQuota)
|
|
|
|
|
protected.GET("/resume/:uploadId", trackHandler.ResumeUpload)
|
|
|
|
|
|
|
|
|
|
protected.POST("/batch/delete", trackHandler.BatchDeleteTracks)
|
|
|
|
|
protected.POST("/batch/update", trackHandler.BatchUpdateTracks)
|
|
|
|
|
|
|
|
|
|
protected.POST("/:id/like", trackHandler.LikeTrack)
|
|
|
|
|
protected.DELETE("/:id/like", trackHandler.UnlikeTrack)
|
|
|
|
|
protected.GET("/:id/likes", trackHandler.GetTrackLikes)
|
|
|
|
|
|
feat(v0.10.3): Commentaires & Interactions Sociales - F201-F215
- F201: Commentaires avec timestamp cliquable, modération mots-clés
- F202: Likes privés (compteur visible créateur uniquement)
- F203: Reposts de tracks sur le profil, bouton Repost, onglet Reposts
- F204: Notifications (commentaire, repost), pas de gamification
Backend: migrations 127/128, comment_moderation_service, track_repost_service,
GetTrackLikes/GetTrack masquent like_count pour non-créateurs
Frontend: LikeButton isCreator, RepostButton, Reposts tab profil, timestamp seek
2026-03-09 09:30:47 +00:00
|
|
|
protected.POST("/:id/repost", trackHandler.RepostTrack)
|
|
|
|
|
protected.DELETE("/:id/repost", trackHandler.UnrepostTrack)
|
|
|
|
|
|
2026-02-14 17:04:37 +00:00
|
|
|
protected.POST("/:id/share", trackHandler.CreateShare)
|
|
|
|
|
protected.DELETE("/share/:id", trackHandler.RevokeShare)
|
|
|
|
|
|
|
|
|
|
protected.POST("/:id/versions/:versionId/restore", trackHandler.RestoreVersion)
|
|
|
|
|
|
|
|
|
|
protected.POST("/:id/play", trackHandler.RecordPlay)
|
|
|
|
|
|
2026-03-10 12:34:16 +00:00
|
|
|
// v0.10.7 F482: Stem sharing
|
|
|
|
|
stemUploadDir := uploadDir
|
|
|
|
|
if stemUploadDir == "" {
|
|
|
|
|
stemUploadDir = "uploads/tracks"
|
|
|
|
|
}
|
|
|
|
|
stemService := services.NewTrackStemService(r.db.GormDB, stemUploadDir, r.logger)
|
|
|
|
|
stemHandler := handlers.NewTrackStemHandler(stemService, trackService, r.logger)
|
|
|
|
|
protected.GET("/:id/stems/:name/download", stemHandler.DownloadStem)
|
|
|
|
|
protected.POST("/:id/stems", r.config.AuthMiddleware.RequireOwnershipOrAdmin("track", trackOwnerResolver), stemHandler.UploadStem)
|
|
|
|
|
protected.GET("/:id/stems", stemHandler.ListStems)
|
|
|
|
|
|
2026-02-14 17:04:37 +00:00
|
|
|
hlsOutputDir := r.config.UploadDir
|
|
|
|
|
if hlsOutputDir == "" {
|
|
|
|
|
hlsOutputDir = "uploads/tracks"
|
|
|
|
|
}
|
|
|
|
|
hlsService := services.NewHLSService(r.db.GormDB, hlsOutputDir, r.logger)
|
|
|
|
|
hlsHandler := handlers.NewHLSHandler(hlsService)
|
|
|
|
|
tracks.GET("/:id/hls/info", hlsHandler.GetStreamInfo)
|
|
|
|
|
tracks.GET("/:id/hls/status", hlsHandler.GetStreamStatus)
|
2026-02-22 20:20:35 +00:00
|
|
|
|
|
|
|
|
if r.config.HLSEnabled {
|
|
|
|
|
hlsStreaming := tracks.Group("/:id/hls")
|
|
|
|
|
{
|
|
|
|
|
hlsStreaming.GET("/master.m3u8", hlsHandler.ServeMasterPlaylist)
|
|
|
|
|
hlsStreaming.GET("/:bitrate/playlist.m3u8", hlsHandler.ServeQualityPlaylist)
|
|
|
|
|
hlsStreaming.GET("/:bitrate/:segment", hlsHandler.ServeSegment)
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-02-14 17:04:37 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
commentService := services.NewCommentService(r.db.GormDB, r.logger)
|
feat(v0.10.3): Commentaires & Interactions Sociales - F201-F215
- F201: Commentaires avec timestamp cliquable, modération mots-clés
- F202: Likes privés (compteur visible créateur uniquement)
- F203: Reposts de tracks sur le profil, bouton Repost, onglet Reposts
- F204: Notifications (commentaire, repost), pas de gamification
Backend: migrations 127/128, comment_moderation_service, track_repost_service,
GetTrackLikes/GetTrack masquent like_count pour non-créateurs
Frontend: LikeButton isCreator, RepostButton, Reposts tab profil, timestamp seek
2026-03-09 09:30:47 +00:00
|
|
|
commentService.SetModerationService(services.NewCommentModerationService(r.db.GormDB)) // v0.10.3 F201
|
2026-02-14 17:04:37 +00:00
|
|
|
commentHandler := handlers.NewCommentHandler(commentService, r.logger)
|
2026-02-21 15:41:39 +00:00
|
|
|
commentHandler.SetNotificationService(r.notificationService)
|
2026-02-14 17:04:37 +00:00
|
|
|
|
|
|
|
|
comments := router.Group("/tracks")
|
|
|
|
|
{
|
|
|
|
|
comments.GET("/:id/comments", commentHandler.GetComments)
|
|
|
|
|
|
|
|
|
|
if r.config.AuthMiddleware != nil {
|
|
|
|
|
protected := comments.Group("")
|
|
|
|
|
protected.Use(r.config.AuthMiddleware.RequireAuth())
|
|
|
|
|
r.applyCSRFProtection(protected)
|
|
|
|
|
{
|
|
|
|
|
protected.POST("/:id/comments", commentHandler.CreateComment)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
commentsProtected := router.Group("/comments")
|
|
|
|
|
{
|
|
|
|
|
if r.config.AuthMiddleware != nil {
|
|
|
|
|
commentsProtected.Use(r.config.AuthMiddleware.RequireAuth())
|
|
|
|
|
r.applyCSRFProtection(commentsProtected)
|
|
|
|
|
{
|
|
|
|
|
commentsProtected.DELETE("/:id", commentHandler.DeleteComment)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|