SEC-06: GetUploadStatus now verifies that the authenticated user owns the upload before returning status. Returns 404 for non-owners to prevent information disclosure.
167 lines
5.7 KiB
Go
167 lines
5.7 KiB
Go
package services
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"veza-backend-api/internal/models"
|
|
|
|
"github.com/google/uuid"
|
|
"go.uber.org/zap"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// TrackUploadService gère le suivi de progression des uploads de tracks
|
|
type TrackUploadService struct {
|
|
db *gorm.DB
|
|
logger *zap.Logger
|
|
}
|
|
|
|
// NewTrackUploadService crée un nouveau service de suivi d'upload
|
|
func NewTrackUploadService(db *gorm.DB, logger *zap.Logger) *TrackUploadService {
|
|
if logger == nil {
|
|
logger = zap.NewNop()
|
|
}
|
|
return &TrackUploadService{
|
|
db: db,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
// GetUploadProgress récupère la progression d'un upload de track
|
|
func (s *TrackUploadService) GetUploadProgress(ctx context.Context, trackID uuid.UUID) (*models.UploadProgress, error) { // Changed trackID to uuid.UUID
|
|
var track models.Track
|
|
if err := s.db.WithContext(ctx).First(&track, "id = ?", trackID).Error; err != nil { // Updated query
|
|
if err == gorm.ErrRecordNotFound {
|
|
return nil, fmt.Errorf("track not found")
|
|
}
|
|
return nil, fmt.Errorf("failed to get track: %w", err)
|
|
}
|
|
|
|
// Calculer le pourcentage de progression basé sur le statut
|
|
progress := s.calculateProgress(track.Status)
|
|
|
|
return &models.UploadProgress{
|
|
TrackID: trackID,
|
|
Status: track.Status,
|
|
Progress: progress,
|
|
Message: track.StatusMessage,
|
|
StreamStatus: track.StreamStatus,
|
|
StreamManifestURL: track.StreamManifestURL,
|
|
}, nil
|
|
}
|
|
|
|
// UpdateUploadStatus met à jour le statut d'un track
|
|
func (s *TrackUploadService) UpdateUploadStatus(ctx context.Context, trackID uuid.UUID, status models.TrackStatus, message string) error { // Changed trackID to uuid.UUID
|
|
updates := map[string]interface{}{
|
|
"status": status,
|
|
}
|
|
if message != "" {
|
|
updates["status_message"] = message
|
|
}
|
|
|
|
if err := s.db.WithContext(ctx).Model(&models.Track{}).Where("id = ?", trackID).Updates(updates).Error; err != nil {
|
|
return fmt.Errorf("failed to update status: %w", err)
|
|
}
|
|
|
|
s.logger.Info("Track upload status updated",
|
|
zap.Any("track_id", trackID), // Changed to zap.Any for uuid.UUID
|
|
zap.String("status", string(status)),
|
|
zap.String("message", message),
|
|
)
|
|
|
|
return nil
|
|
}
|
|
|
|
// calculateProgress calcule le pourcentage de progression basé sur le statut
|
|
func (s *TrackUploadService) calculateProgress(status models.TrackStatus) int {
|
|
switch status {
|
|
case models.TrackStatusUploading:
|
|
return 25 // 25% pendant l'upload
|
|
case models.TrackStatusProcessing:
|
|
return 50 // 50% pendant le traitement
|
|
case models.TrackStatusCompleted:
|
|
return 100 // 100% une fois terminé
|
|
case models.TrackStatusFailed:
|
|
return 0 // 0% en cas d'échec
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
// GetUploadStats récupère les statistiques d'upload pour un utilisateur
|
|
// BE-API-032: Implement upload stats endpoint
|
|
func (s *TrackUploadService) GetUploadStats(ctx context.Context, userID uuid.UUID) (map[string]interface{}, error) {
|
|
stats := map[string]interface{}{
|
|
"total_uploads": int64(0),
|
|
"total_size": int64(0),
|
|
"audio_files": int64(0),
|
|
"image_files": int64(0),
|
|
"video_files": int64(0),
|
|
}
|
|
|
|
// Compter le nombre total d'uploads (tracks) pour l'utilisateur
|
|
var totalUploads int64
|
|
if err := s.db.WithContext(ctx).Model(&models.Track{}).
|
|
Where("creator_id = ?", userID).
|
|
Count(&totalUploads).Error; err != nil {
|
|
return nil, fmt.Errorf("failed to count total uploads: %w", err)
|
|
}
|
|
stats["total_uploads"] = totalUploads
|
|
|
|
// Calculer la taille totale des fichiers
|
|
var totalSize int64
|
|
if err := s.db.WithContext(ctx).Model(&models.Track{}).
|
|
Where("creator_id = ?", userID).
|
|
Select("COALESCE(SUM(file_size), 0)").
|
|
Scan(&totalSize).Error; err != nil {
|
|
return nil, fmt.Errorf("failed to calculate total size: %w", err)
|
|
}
|
|
stats["total_size"] = totalSize
|
|
|
|
// Compter les fichiers audio (mp3, flac, wav, etc.)
|
|
var audioFiles int64
|
|
if err := s.db.WithContext(ctx).Model(&models.Track{}).
|
|
Where("creator_id = ? AND format IN (?)", userID, []string{"mp3", "flac", "wav", "aac", "ogg", "m4a"}).
|
|
Count(&audioFiles).Error; err != nil {
|
|
return nil, fmt.Errorf("failed to count audio files: %w", err)
|
|
}
|
|
stats["audio_files"] = audioFiles
|
|
|
|
// Compter les fichiers image (jpg, png, gif, etc.)
|
|
// Note: Les images sont généralement dans cover_art_path, mais on peut aussi chercher dans les tracks
|
|
// Pour l'instant, on se concentre sur les tracks audio
|
|
var imageFiles int64
|
|
if err := s.db.WithContext(ctx).Model(&models.Track{}).
|
|
Where("creator_id = ? AND format IN (?)", userID, []string{"jpg", "jpeg", "png", "gif", "webp"}).
|
|
Count(&imageFiles).Error; err != nil {
|
|
// Si la requête échoue (format non supporté pour images), on laisse à 0
|
|
s.logger.Debug("Image files count query failed, setting to 0", zap.Error(err))
|
|
}
|
|
stats["image_files"] = imageFiles
|
|
|
|
// Compter les fichiers vidéo (mp4, avi, etc.)
|
|
var videoFiles int64
|
|
if err := s.db.WithContext(ctx).Model(&models.Track{}).
|
|
Where("creator_id = ? AND format IN (?)", userID, []string{"mp4", "avi", "mov", "mkv", "webm"}).
|
|
Count(&videoFiles).Error; err != nil {
|
|
// Si la requête échoue (format non supporté pour vidéos), on laisse à 0
|
|
s.logger.Debug("Video files count query failed, setting to 0", zap.Error(err))
|
|
}
|
|
stats["video_files"] = videoFiles
|
|
|
|
return stats, nil
|
|
}
|
|
|
|
// GetUploadOwnerID returns the creator (owner) user ID for a track.
|
|
// SEC-06: Used for IDOR prevention in GetUploadStatus.
|
|
func (s *TrackUploadService) GetUploadOwnerID(ctx context.Context, trackID uuid.UUID) (uuid.UUID, error) {
|
|
var track models.Track
|
|
if err := s.db.WithContext(ctx).Select("id, creator_id").First(&track, "id = ?", trackID).Error; err != nil {
|
|
if err == gorm.ErrRecordNotFound {
|
|
return uuid.Nil, fmt.Errorf("track not found")
|
|
}
|
|
return uuid.Nil, fmt.Errorf("failed to get track: %w", err)
|
|
}
|
|
return track.UserID, nil
|
|
}
|