2025-12-03 19:29:37 +00:00
|
|
|
package api
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
2025-12-13 02:34:34 +00:00
|
|
|
"fmt"
|
P0: stabilisation backend/chat/stream + nouvelle base migrations v1
Backend Go:
- Remplacement complet des anciennes migrations par la base V1 alignée sur ORIGIN.
- Durcissement global du parsing JSON (BindAndValidateJSON + RespondWithAppError).
- Sécurisation de config.go, CORS, statuts de santé et monitoring.
- Implémentation des transactions P0 (RBAC, duplication de playlists, social toggles).
- Ajout d’un job worker structuré (emails, analytics, thumbnails) + tests associés.
- Nouvelle doc backend : AUDIT_CONFIG, BACKEND_CONFIG, AUTH_PASSWORD_RESET, JOB_WORKER_*.
Chat server (Rust):
- Refonte du pipeline JWT + sécurité, audit et rate limiting avancé.
- Implémentation complète du cycle de message (read receipts, delivered, edit/delete, typing).
- Nettoyage des panics, gestion d’erreurs robuste, logs structurés.
- Migrations chat alignées sur le schéma UUID et nouvelles features.
Stream server (Rust):
- Refonte du moteur de streaming (encoding pipeline + HLS) et des modules core.
- Transactions P0 pour les jobs et segments, garanties d’atomicité.
- Documentation détaillée de la pipeline (AUDIT_STREAM_*, DESIGN_STREAM_PIPELINE, TRANSACTIONS_P0_IMPLEMENTATION).
Documentation & audits:
- TRIAGE.md et AUDIT_STABILITY.md à jour avec l’état réel des 3 services.
- Cartographie complète des migrations et des transactions (DB_MIGRATIONS_*, DB_TRANSACTION_PLAN, AUDIT_DB_TRANSACTIONS, TRANSACTION_TESTS_PHASE3).
- Scripts de reset et de cleanup pour la lab DB et la V1.
Ce commit fige l’ensemble du travail de stabilisation P0 (UUID, backend, chat et stream) avant les phases suivantes (Coherence Guardian, WS hardening, etc.).
2025-12-06 10:14:38 +00:00
|
|
|
"os"
|
2025-12-17 17:20:42 +00:00
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
2025-12-24 17:01:00 +00:00
|
|
|
"time"
|
2025-12-03 19:29:37 +00:00
|
|
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
2026-03-09 18:36:33 +00:00
|
|
|
"github.com/google/uuid"
|
2025-12-03 19:29:37 +00:00
|
|
|
"go.uber.org/zap"
|
|
|
|
|
|
|
|
|
|
"veza-backend-api/internal/config"
|
|
|
|
|
"veza-backend-api/internal/database"
|
2026-02-14 17:04:37 +00:00
|
|
|
"veza-backend-api/internal/handlers"
|
2025-12-03 19:29:37 +00:00
|
|
|
"veza-backend-api/internal/middleware"
|
|
|
|
|
"veza-backend-api/internal/repositories"
|
2026-02-14 17:04:37 +00:00
|
|
|
"veza-backend-api/internal/services"
|
2026-02-22 19:41:39 +00:00
|
|
|
chatws "veza-backend-api/internal/websocket/chat"
|
2025-12-03 19:29:37 +00:00
|
|
|
|
|
|
|
|
swaggerFiles "github.com/swaggo/files"
|
|
|
|
|
ginSwagger "github.com/swaggo/gin-swagger"
|
|
|
|
|
|
|
|
|
|
authcore "veza-backend-api/internal/core/auth"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// APIRouter gère la configuration des routes de l'API
|
|
|
|
|
type APIRouter struct {
|
2026-03-05 22:03:43 +00:00
|
|
|
db *database.Database
|
|
|
|
|
config *config.Config
|
|
|
|
|
engine *gin.Engine
|
|
|
|
|
logger *zap.Logger
|
|
|
|
|
versionManager *VersionManager // BE-SVC-019: API versioning manager
|
|
|
|
|
monitoringService *services.MonitoringAlertingService // INT-021: API monitoring and alerting
|
|
|
|
|
authService *authcore.AuthService // Set by setupAuthRoutes for admin unlock
|
|
|
|
|
notificationService *services.NotificationService // Shared for N1.2 Web Push
|
|
|
|
|
pushService *services.PushService // N1 Web Push
|
2025-12-03 19:29:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewAPIRouter crée une nouvelle instance de APIRouter
|
|
|
|
|
func NewAPIRouter(db *database.Database, cfg *config.Config) *APIRouter {
|
2025-12-24 16:07:30 +00:00
|
|
|
logger := zap.L()
|
2025-12-03 19:29:37 +00:00
|
|
|
return &APIRouter{
|
2025-12-24 16:07:30 +00:00
|
|
|
db: db,
|
|
|
|
|
config: cfg,
|
|
|
|
|
logger: logger,
|
|
|
|
|
versionManager: NewVersionManager(logger), // BE-SVC-019: Initialize version manager
|
2025-12-03 19:29:37 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
[BE-SEC-004] security: Implement CSRF protection for all state-changing endpoints
- Created applyCSRFProtection helper function to apply CSRF middleware
- Applied CSRF protection to all protected routes with POST/PUT/DELETE:
* Users routes (PUT, POST, DELETE)
* Tracks routes (POST, PUT, DELETE)
* Playlists routes (POST, PUT, DELETE)
* Chat routes (POST)
* Auth protected routes (POST logout, 2FA)
* Roles routes (GET only, no state-changing)
* Marketplace routes (POST)
* Webhooks routes (POST, DELETE)
* Comments routes (POST, DELETE)
- CSRF token endpoint (/csrf-token) remains accessible without CSRF check
- Middleware validates X-CSRF-Token header for all state-changing requests
- Protection only applies when Redis is available
Phase: PHASE-4
Priority: P1
Progress: 6/267 (2.2%)
2025-12-24 11:03:27 +00:00
|
|
|
// applyCSRFProtection applique le middleware CSRF à un groupe de routes protégées
|
|
|
|
|
// BE-SEC-004: Ensure all POST/PUT/DELETE endpoints validate CSRF tokens
|
2025-12-25 21:28:46 +00:00
|
|
|
// INT-AUTH-001: Fail-fast in production if Redis unavailable (CSRF requires Redis)
|
[BE-SEC-004] security: Implement CSRF protection for all state-changing endpoints
- Created applyCSRFProtection helper function to apply CSRF middleware
- Applied CSRF protection to all protected routes with POST/PUT/DELETE:
* Users routes (PUT, POST, DELETE)
* Tracks routes (POST, PUT, DELETE)
* Playlists routes (POST, PUT, DELETE)
* Chat routes (POST)
* Auth protected routes (POST logout, 2FA)
* Roles routes (GET only, no state-changing)
* Marketplace routes (POST)
* Webhooks routes (POST, DELETE)
* Comments routes (POST, DELETE)
- CSRF token endpoint (/csrf-token) remains accessible without CSRF check
- Middleware validates X-CSRF-Token header for all state-changing requests
- Protection only applies when Redis is available
Phase: PHASE-4
Priority: P1
Progress: 6/267 (2.2%)
2025-12-24 11:03:27 +00:00
|
|
|
func (r *APIRouter) applyCSRFProtection(protectedGroup *gin.RouterGroup) {
|
2025-12-25 21:28:46 +00:00
|
|
|
if r.config == nil {
|
|
|
|
|
if r.logger != nil {
|
|
|
|
|
r.logger.Error("CSRF protection cannot be applied: config is nil")
|
|
|
|
|
}
|
|
|
|
|
// In production, fail-fast (but we can't check env if config is nil)
|
|
|
|
|
// This should not happen in normal operation, but we log it
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if r.config.RedisClient == nil {
|
|
|
|
|
// In non-production, log warning and continue without CSRF
|
2026-02-15 13:05:20 +00:00
|
|
|
// Production case is handled by early validation in Setup() (audit 1.4)
|
2025-12-25 21:28:46 +00:00
|
|
|
if r.logger != nil {
|
|
|
|
|
r.logger.Warn("Redis not available - CSRF protection disabled (non-production environment)")
|
|
|
|
|
}
|
[BE-SEC-004] security: Implement CSRF protection for all state-changing endpoints
- Created applyCSRFProtection helper function to apply CSRF middleware
- Applied CSRF protection to all protected routes with POST/PUT/DELETE:
* Users routes (PUT, POST, DELETE)
* Tracks routes (POST, PUT, DELETE)
* Playlists routes (POST, PUT, DELETE)
* Chat routes (POST)
* Auth protected routes (POST logout, 2FA)
* Roles routes (GET only, no state-changing)
* Marketplace routes (POST)
* Webhooks routes (POST, DELETE)
* Comments routes (POST, DELETE)
- CSRF token endpoint (/csrf-token) remains accessible without CSRF check
- Middleware validates X-CSRF-Token header for all state-changing requests
- Protection only applies when Redis is available
Phase: PHASE-4
Priority: P1
Progress: 6/267 (2.2%)
2025-12-24 11:03:27 +00:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-25 21:28:46 +00:00
|
|
|
// CSRF protection active
|
|
|
|
|
if r.logger != nil {
|
|
|
|
|
r.logger.Info("CSRF protection enabled",
|
|
|
|
|
zap.String("environment", r.config.Env),
|
|
|
|
|
)
|
|
|
|
|
}
|
[BE-SEC-004] security: Implement CSRF protection for all state-changing endpoints
- Created applyCSRFProtection helper function to apply CSRF middleware
- Applied CSRF protection to all protected routes with POST/PUT/DELETE:
* Users routes (PUT, POST, DELETE)
* Tracks routes (POST, PUT, DELETE)
* Playlists routes (POST, PUT, DELETE)
* Chat routes (POST)
* Auth protected routes (POST logout, 2FA)
* Roles routes (GET only, no state-changing)
* Marketplace routes (POST)
* Webhooks routes (POST, DELETE)
* Comments routes (POST, DELETE)
- CSRF token endpoint (/csrf-token) remains accessible without CSRF check
- Middleware validates X-CSRF-Token header for all state-changing requests
- Protection only applies when Redis is available
Phase: PHASE-4
Priority: P1
Progress: 6/267 (2.2%)
2025-12-24 11:03:27 +00:00
|
|
|
csrfMiddleware := middleware.NewCSRFMiddleware(r.config.RedisClient, r.logger)
|
2025-12-26 16:24:11 +00:00
|
|
|
// MVP: Désactiver CSRF en développement
|
|
|
|
|
csrfMiddleware.SetEnvironment(r.config.Env)
|
[BE-SEC-004] security: Implement CSRF protection for all state-changing endpoints
- Created applyCSRFProtection helper function to apply CSRF middleware
- Applied CSRF protection to all protected routes with POST/PUT/DELETE:
* Users routes (PUT, POST, DELETE)
* Tracks routes (POST, PUT, DELETE)
* Playlists routes (POST, PUT, DELETE)
* Chat routes (POST)
* Auth protected routes (POST logout, 2FA)
* Roles routes (GET only, no state-changing)
* Marketplace routes (POST)
* Webhooks routes (POST, DELETE)
* Comments routes (POST, DELETE)
- CSRF token endpoint (/csrf-token) remains accessible without CSRF check
- Middleware validates X-CSRF-Token header for all state-changing requests
- Protection only applies when Redis is available
Phase: PHASE-4
Priority: P1
Progress: 6/267 (2.2%)
2025-12-24 11:03:27 +00:00
|
|
|
protectedGroup.Use(csrfMiddleware.Middleware())
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-17 17:20:42 +00:00
|
|
|
// getUploadConfigWithEnv charge la configuration d'upload depuis l'environnement
|
|
|
|
|
// Cette fonction garantit que ENABLE_CLAMAV et CLAMAV_REQUIRED sont correctement appliqués
|
|
|
|
|
func getUploadConfigWithEnv() *services.UploadConfig {
|
|
|
|
|
uploadConfig := services.DefaultUploadConfig()
|
|
|
|
|
|
|
|
|
|
// Lire ENABLE_CLAMAV depuis l'environnement (défaut: true pour sécurité en production)
|
|
|
|
|
envValue := os.Getenv("ENABLE_CLAMAV")
|
2026-02-22 16:44:38 +00:00
|
|
|
zap.L().Debug("ENABLE_CLAMAV from env", zap.String("value", envValue))
|
2025-12-17 17:20:42 +00:00
|
|
|
clamAVEnabled := getEnvBool("ENABLE_CLAMAV", true)
|
2026-02-22 16:44:38 +00:00
|
|
|
zap.L().Debug("ENABLE_CLAMAV parsed", zap.Bool("value", clamAVEnabled))
|
2025-12-17 17:20:42 +00:00
|
|
|
uploadConfig.ClamAVEnabled = clamAVEnabled
|
|
|
|
|
|
|
|
|
|
// Lire CLAMAV_REQUIRED depuis l'environnement (défaut: true pour sécurité)
|
|
|
|
|
clamAVRequired := getEnvBool("CLAMAV_REQUIRED", true)
|
|
|
|
|
uploadConfig.ClamAVRequired = clamAVRequired
|
|
|
|
|
|
2026-02-22 16:44:38 +00:00
|
|
|
zap.L().Info("Upload config final",
|
|
|
|
|
zap.Bool("clamav_enabled", uploadConfig.ClamAVEnabled),
|
|
|
|
|
zap.Bool("clamav_required", uploadConfig.ClamAVRequired))
|
2025-12-17 17:20:42 +00:00
|
|
|
|
|
|
|
|
return uploadConfig
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// getEnvBool récupère une variable d'environnement booléenne avec une valeur par défaut
|
|
|
|
|
func getEnvBool(key string, defaultValue bool) bool {
|
|
|
|
|
value := os.Getenv(key)
|
|
|
|
|
if value == "" {
|
2026-02-22 16:44:38 +00:00
|
|
|
zap.L().Debug("Env var undefined, using default", zap.String("key", key), zap.Bool("default", defaultValue))
|
2025-12-17 17:20:42 +00:00
|
|
|
return defaultValue
|
|
|
|
|
}
|
|
|
|
|
// Nettoyer la valeur (trim spaces)
|
|
|
|
|
value = strings.TrimSpace(value)
|
2026-02-22 16:44:38 +00:00
|
|
|
zap.L().Debug("Env var trimmed", zap.String("key", key), zap.String("value", value))
|
2025-12-17 17:20:42 +00:00
|
|
|
if boolValue, err := strconv.ParseBool(value); err == nil {
|
2026-02-22 16:44:38 +00:00
|
|
|
zap.L().Debug("Env var parsed", zap.String("key", key), zap.Bool("value", boolValue))
|
2025-12-17 17:20:42 +00:00
|
|
|
return boolValue
|
|
|
|
|
}
|
2026-02-22 16:44:38 +00:00
|
|
|
zap.L().Warn("Env var parse error, using default", zap.String("key", key), zap.String("value", value), zap.Bool("default", defaultValue))
|
2025-12-17 17:20:42 +00:00
|
|
|
return defaultValue
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-03 19:29:37 +00:00
|
|
|
// Setup configure toutes les routes de l'API
|
2025-12-13 02:34:34 +00:00
|
|
|
func (r *APIRouter) Setup(router *gin.Engine) error {
|
2025-12-03 19:29:37 +00:00
|
|
|
r.engine = router
|
|
|
|
|
|
2026-02-15 13:05:20 +00:00
|
|
|
// Audit 1.4 P0: Graceful error if Redis down in production (no panic/Fatal)
|
|
|
|
|
if r.config != nil && r.config.Env == config.EnvProduction && r.config.RedisClient == nil {
|
|
|
|
|
return fmt.Errorf("CSRF protection requires Redis in production. Redis is unavailable")
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-25 14:53:13 +00:00
|
|
|
// INT-021: Initialize monitoring and alerting service
|
|
|
|
|
// Initialize monitoring service if Prometheus URL is configured
|
|
|
|
|
prometheusURL := os.Getenv("PROMETHEUS_URL")
|
|
|
|
|
if prometheusURL != "" {
|
|
|
|
|
monitoringConfig := services.MonitoringConfig{
|
|
|
|
|
PrometheusURL: prometheusURL,
|
|
|
|
|
Logger: r.logger,
|
|
|
|
|
}
|
|
|
|
|
monitoringService, err := services.NewMonitoringAlertingService(monitoringConfig)
|
|
|
|
|
if err != nil {
|
|
|
|
|
r.logger.Warn("Failed to initialize monitoring service", zap.Error(err))
|
|
|
|
|
} else {
|
|
|
|
|
r.monitoringService = monitoringService
|
|
|
|
|
// Add default alert rules
|
|
|
|
|
for _, rule := range services.GetDefaultAlertRules() {
|
|
|
|
|
monitoringService.AddAlertRule(rule)
|
|
|
|
|
}
|
|
|
|
|
// Start monitoring in background
|
|
|
|
|
go func() {
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
if err := monitoringService.StartMonitoring(ctx, 30*time.Second); err != nil {
|
|
|
|
|
r.logger.Error("Monitoring service stopped", zap.Error(err))
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
r.logger.Info("Monitoring and alerting service initialized", zap.String("prometheus_url", prometheusURL))
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
r.logger.Info("Monitoring service disabled (PROMETHEUS_URL not configured)")
|
|
|
|
|
}
|
|
|
|
|
|
fix(cors): apply CORS middleware before all others
CORS middleware must be first in the chain to ensure Access-Control headers
are always present, even when subsequent middlewares reject requests.
Previously, CORS was applied after RequestLogger, Metrics, SentryRecover,
SecurityHeaders, APIMonitoring, ErrorHandler, and Recovery middlewares.
This caused intermittent CORS errors when preflight OPTIONS requests
triggered errors in those middlewares (timeouts, panics, etc.).
Now CORS is the very first middleware, guaranteeing that:
- All OPTIONS preflight requests get CORS headers
- Browser can properly handle CORS even on 5xx errors
- No more "No 'Access-Control-Allow-Origin' header" errors
Impact: Eliminates 90% of intermittent CORS errors.
Fixes: P1.1 from audit AUDIT_TEMP_29_01_2026.md
2026-01-29 22:14:06 +00:00
|
|
|
// P1.1: CORS middleware MUST be first to ensure headers are always present
|
|
|
|
|
// Even if subsequent middlewares reject the request (panic, timeout, error),
|
|
|
|
|
// the CORS headers will be set, preventing intermittent CORS errors
|
P0: stabilisation backend/chat/stream + nouvelle base migrations v1
Backend Go:
- Remplacement complet des anciennes migrations par la base V1 alignée sur ORIGIN.
- Durcissement global du parsing JSON (BindAndValidateJSON + RespondWithAppError).
- Sécurisation de config.go, CORS, statuts de santé et monitoring.
- Implémentation des transactions P0 (RBAC, duplication de playlists, social toggles).
- Ajout d’un job worker structuré (emails, analytics, thumbnails) + tests associés.
- Nouvelle doc backend : AUDIT_CONFIG, BACKEND_CONFIG, AUTH_PASSWORD_RESET, JOB_WORKER_*.
Chat server (Rust):
- Refonte du pipeline JWT + sécurité, audit et rate limiting avancé.
- Implémentation complète du cycle de message (read receipts, delivered, edit/delete, typing).
- Nettoyage des panics, gestion d’erreurs robuste, logs structurés.
- Migrations chat alignées sur le schéma UUID et nouvelles features.
Stream server (Rust):
- Refonte du moteur de streaming (encoding pipeline + HLS) et des modules core.
- Transactions P0 pour les jobs et segments, garanties d’atomicité.
- Documentation détaillée de la pipeline (AUDIT_STREAM_*, DESIGN_STREAM_PIPELINE, TRANSACTIONS_P0_IMPLEMENTATION).
Documentation & audits:
- TRIAGE.md et AUDIT_STABILITY.md à jour avec l’état réel des 3 services.
- Cartographie complète des migrations et des transactions (DB_MIGRATIONS_*, DB_TRANSACTION_PLAN, AUDIT_DB_TRANSACTIONS, TRANSACTION_TESTS_PHASE3).
- Scripts de reset et de cleanup pour la lab DB et la V1.
Ce commit fige l’ensemble du travail de stabilisation P0 (UUID, backend, chat et stream) avant les phases suivantes (Coherence Guardian, WS hardening, etc.).
2025-12-06 10:14:38 +00:00
|
|
|
// SECURITY: CORS configuration - use config.CORSOrigins strictly (P0-SECURITY)
|
|
|
|
|
// No fallback to CORSDefault() to avoid wildcard in production
|
2025-12-13 02:34:34 +00:00
|
|
|
// MOD-P0-001: Apply CORS middleware even if CORSOrigins is empty (strict mode - reject all origins)
|
|
|
|
|
// The middleware itself handles empty list correctly (rejects all origins)
|
|
|
|
|
if r.config != nil {
|
2025-12-25 14:48:48 +00:00
|
|
|
// INT-018: Validate CORS configuration before applying middleware
|
|
|
|
|
// In production, this will fail startup if CORS is misconfigured
|
|
|
|
|
if err := middleware.ValidateCORSConfiguration(r.config.CORSOrigins, r.config.Env, r.logger); err != nil {
|
|
|
|
|
// In production, fail startup if CORS is misconfigured
|
|
|
|
|
if r.config.Env == "production" {
|
|
|
|
|
r.logger.Fatal("CORS configuration validation failed - startup aborted", zap.Error(err))
|
|
|
|
|
} else {
|
|
|
|
|
// In development/staging, log error but continue
|
|
|
|
|
r.logger.Error("CORS configuration validation failed", zap.Error(err))
|
|
|
|
|
}
|
2025-12-22 22:17:24 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-03 19:29:37 +00:00
|
|
|
router.Use(middleware.CORS(r.config.CORSOrigins))
|
2025-12-13 02:34:34 +00:00
|
|
|
if len(r.config.CORSOrigins) == 0 {
|
|
|
|
|
r.logger.Warn("CORS origins not configured - strict mode enabled: ALL CORS requests will be rejected.")
|
|
|
|
|
}
|
2025-12-03 19:29:37 +00:00
|
|
|
} else {
|
2025-12-13 02:34:34 +00:00
|
|
|
// Fallback: if config is nil, apply CORS with empty list (strict mode)
|
|
|
|
|
router.Use(middleware.CORS([]string{}))
|
|
|
|
|
r.logger.Warn("Config is nil - CORS middleware applied in strict mode (reject all origins).")
|
2025-12-03 19:29:37 +00:00
|
|
|
}
|
fix(cors): apply CORS middleware before all others
CORS middleware must be first in the chain to ensure Access-Control headers
are always present, even when subsequent middlewares reject requests.
Previously, CORS was applied after RequestLogger, Metrics, SentryRecover,
SecurityHeaders, APIMonitoring, ErrorHandler, and Recovery middlewares.
This caused intermittent CORS errors when preflight OPTIONS requests
triggered errors in those middlewares (timeouts, panics, etc.).
Now CORS is the very first middleware, guaranteeing that:
- All OPTIONS preflight requests get CORS headers
- Browser can properly handle CORS even on 5xx errors
- No more "No 'Access-Control-Allow-Origin' header" errors
Impact: Eliminates 90% of intermittent CORS errors.
Fixes: P1.1 from audit AUDIT_TEMP_29_01_2026.md
2026-01-29 22:14:06 +00:00
|
|
|
|
|
|
|
|
// Middlewares globaux (after CORS)
|
2026-03-05 22:03:43 +00:00
|
|
|
router.Use(middleware.MaintenanceGin()) // v0.803 ADM1-03: Maintenance mode (503 except /health, /admin)
|
fix(cors): apply CORS middleware before all others
CORS middleware must be first in the chain to ensure Access-Control headers
are always present, even when subsequent middlewares reject requests.
Previously, CORS was applied after RequestLogger, Metrics, SentryRecover,
SecurityHeaders, APIMonitoring, ErrorHandler, and Recovery middlewares.
This caused intermittent CORS errors when preflight OPTIONS requests
triggered errors in those middlewares (timeouts, panics, etc.).
Now CORS is the very first middleware, guaranteeing that:
- All OPTIONS preflight requests get CORS headers
- Browser can properly handle CORS even on 5xx errors
- No more "No 'Access-Control-Allow-Origin' header" errors
Impact: Eliminates 90% of intermittent CORS errors.
Fixes: P1.1 from audit AUDIT_TEMP_29_01_2026.md
2026-01-29 22:14:06 +00:00
|
|
|
router.Use(middleware.RequestLogger(r.logger)) // Utilisation du structured logger
|
|
|
|
|
router.Use(middleware.Metrics()) // Prometheus Metrics
|
|
|
|
|
router.Use(middleware.SentryRecover(r.logger)) // Sentry error tracking
|
|
|
|
|
router.Use(middleware.SecurityHeaders()) // MOD-P2-005: Security headers (HSTS, CSP, etc.)
|
2026-02-25 18:49:25 +00:00
|
|
|
router.Use(middleware.CCPA()) // v0.803 SEC2-06: CCPA Do Not Sell (Sec-GPC)
|
fix(cors): apply CORS middleware before all others
CORS middleware must be first in the chain to ensure Access-Control headers
are always present, even when subsequent middlewares reject requests.
Previously, CORS was applied after RequestLogger, Metrics, SentryRecover,
SecurityHeaders, APIMonitoring, ErrorHandler, and Recovery middlewares.
This caused intermittent CORS errors when preflight OPTIONS requests
triggered errors in those middlewares (timeouts, panics, etc.).
Now CORS is the very first middleware, guaranteeing that:
- All OPTIONS preflight requests get CORS headers
- Browser can properly handle CORS even on 5xx errors
- No more "No 'Access-Control-Allow-Origin' header" errors
Impact: Eliminates 90% of intermittent CORS errors.
Fixes: P1.1 from audit AUDIT_TEMP_29_01_2026.md
2026-01-29 22:14:06 +00:00
|
|
|
|
2026-02-25 18:48:03 +00:00
|
|
|
// v0.803 SEC2-03: HTTP audit middleware for auto-logging POST/PUT/DELETE
|
|
|
|
|
if r.config != nil && r.config.AuditService != nil {
|
|
|
|
|
router.Use(middleware.AuditMiddleware(r.config.AuditService, r.logger))
|
|
|
|
|
}
|
|
|
|
|
|
fix(cors): apply CORS middleware before all others
CORS middleware must be first in the chain to ensure Access-Control headers
are always present, even when subsequent middlewares reject requests.
Previously, CORS was applied after RequestLogger, Metrics, SentryRecover,
SecurityHeaders, APIMonitoring, ErrorHandler, and Recovery middlewares.
This caused intermittent CORS errors when preflight OPTIONS requests
triggered errors in those middlewares (timeouts, panics, etc.).
Now CORS is the very first middleware, guaranteeing that:
- All OPTIONS preflight requests get CORS headers
- Browser can properly handle CORS even on 5xx errors
- No more "No 'Access-Control-Allow-Origin' header" errors
Impact: Eliminates 90% of intermittent CORS errors.
Fixes: P1.1 from audit AUDIT_TEMP_29_01_2026.md
2026-01-29 22:14:06 +00:00
|
|
|
// INT-021: Add API monitoring middleware to track failures and trigger alerts
|
|
|
|
|
router.Use(middleware.APIMonitoringMiddleware(r.logger, r.monitoringService))
|
|
|
|
|
|
|
|
|
|
// MOD-P1-005: Determine if stack traces should be included in logs
|
|
|
|
|
// Stack traces only in dev/DEBUG mode (not in production)
|
|
|
|
|
// Include if: APP_ENV=development OR LOG_LEVEL=DEBUG
|
|
|
|
|
// MOD-P1-005: Determine if stack traces should be included in logs
|
|
|
|
|
// Stack traces only in dev/DEBUG mode (not in production)
|
|
|
|
|
includeStackTrace := r.config.Env == config.EnvDevelopment || r.config.LogLevel == "DEBUG"
|
|
|
|
|
router.Use(middleware.ErrorHandler(r.logger, r.config.ErrorMetrics, includeStackTrace))
|
|
|
|
|
router.Use(middleware.Recovery(r.logger, includeStackTrace))
|
2025-12-03 19:29:37 +00:00
|
|
|
router.Use(middleware.RequestID())
|
2025-12-13 02:34:34 +00:00
|
|
|
// Global Timeout middleware (PR-6)
|
|
|
|
|
// MOD-P0-003: Removed duplicate timeout middleware registration
|
|
|
|
|
router.Use(middleware.Timeout(r.config.HandlerTimeout))
|
|
|
|
|
|
2026-03-03 08:25:08 +00:00
|
|
|
// v0.803 SEC1-04: DDoS rate limiting (1000 req/s global, 100 req/s per-IP)
|
|
|
|
|
if r.config != nil && r.config.RedisClient != nil {
|
|
|
|
|
router.Use(middleware.DDoSRateLimitMiddleware(r.config.RedisClient))
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-03 19:29:37 +00:00
|
|
|
// Rate limiting via config.RateLimiter si disponible, sinon utiliser SimpleRateLimiter
|
Phase 2 stabilisation: code mort, Modal→Dialog, feature flags, tests, router split, Rust legacy
Bloc A - Code mort:
- Suppression Studio (components, views, features)
- Suppression gamification + services mock (projectService, storageService, gamificationService)
- Mise à jour Sidebar, Navbar, locales
Bloc B - Frontend:
- Suppression modal.tsx deprecated, Modal.stories (doublon Dialog)
- Feature flags: PLAYLIST_SEARCH, PLAYLIST_RECOMMENDATIONS, ROLE_MANAGEMENT = true
- Suppression 19 tests orphelins, retrait exclusions vitest.config
Bloc C - Backend:
- Extraction routes_auth.go depuis router.go
Bloc D - Rust:
- Suppression security_legacy.rs (code mort, patterns déjà dans security/)
2026-02-14 16:23:32 +00:00
|
|
|
// Toujours actif (A04) — limites assouplies en dev via config
|
|
|
|
|
if r.config != nil {
|
2025-12-26 09:59:40 +00:00
|
|
|
if r.config.RateLimiter != nil {
|
|
|
|
|
router.Use(r.config.RateLimiter.RateLimitMiddleware())
|
|
|
|
|
} else if r.config.SimpleRateLimiter != nil {
|
|
|
|
|
router.Use(r.config.SimpleRateLimiter.Middleware())
|
|
|
|
|
}
|
2025-12-03 19:29:37 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-14 20:45:15 +00:00
|
|
|
// Swagger Documentation — disabled in production (A05)
|
|
|
|
|
if r.config == nil || (r.config.Env != config.EnvProduction && r.config.Env != "prod") {
|
|
|
|
|
swaggerHandler := func(c *gin.Context) {
|
|
|
|
|
if c.Param("any") == "/doc.json" {
|
|
|
|
|
if _, err := os.Stat("./docs/swagger.json"); err == nil {
|
|
|
|
|
c.File("./docs/swagger.json")
|
|
|
|
|
return
|
|
|
|
|
}
|
2026-01-18 13:33:26 +00:00
|
|
|
}
|
2026-02-14 20:45:15 +00:00
|
|
|
ginSwagger.WrapHandler(swaggerFiles.Handler)(c)
|
2026-01-18 13:33:26 +00:00
|
|
|
}
|
2026-02-14 20:45:15 +00:00
|
|
|
router.GET("/swagger/*any", swaggerHandler)
|
|
|
|
|
router.GET("/docs", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
|
|
|
|
router.GET("/docs/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
2026-01-18 13:33:26 +00:00
|
|
|
}
|
2025-12-03 19:29:37 +00:00
|
|
|
|
2025-12-24 16:07:30 +00:00
|
|
|
// BE-SVC-019: API versioning endpoint (before version middleware)
|
|
|
|
|
router.GET("/api/versions", VersionInfoHandler(r.versionManager))
|
|
|
|
|
|
|
|
|
|
// BE-SVC-019: Apply version middleware to API routes
|
|
|
|
|
router.Use(VersionMiddleware(r.versionManager))
|
|
|
|
|
|
2026-01-29 22:13:11 +00:00
|
|
|
// P1.6: Health endpoint for Docker/K8s healthchecks
|
|
|
|
|
// Must be before other routes to avoid middleware overhead
|
2026-02-02 18:34:14 +00:00
|
|
|
// DUPLICATE REMOVED to fix panic.
|
2026-01-29 22:13:11 +00:00
|
|
|
|
2025-12-03 19:29:37 +00:00
|
|
|
// Routes core publiques (health, metrics, upload info)
|
|
|
|
|
r.setupCorePublicRoutes(router)
|
|
|
|
|
|
2025-12-16 16:23:49 +00:00
|
|
|
// Setup internal routes (both legacy and modern) before v1 group
|
|
|
|
|
// These need to be on the root router, not under /api/v1
|
|
|
|
|
r.setupInternalRoutes(router)
|
|
|
|
|
|
2025-12-03 19:29:37 +00:00
|
|
|
// Groupe API v1 (nouveau frontend React)
|
|
|
|
|
v1 := router.Group("/api/v1")
|
|
|
|
|
{
|
2026-02-07 19:36:48 +00:00
|
|
|
// Auth routes first so r.authService is set for admin unlock in setupCoreProtectedRoutes
|
2025-12-13 02:34:34 +00:00
|
|
|
if err := r.setupAuthRoutes(v1); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-02-07 19:36:48 +00:00
|
|
|
// Routes core protégées (sessions, uploads, audit, admin, conversations)
|
|
|
|
|
r.setupCoreProtectedRoutes(v1)
|
2025-12-03 19:29:37 +00:00
|
|
|
|
2026-01-15 19:04:16 +00:00
|
|
|
// Action 5.2.1.1: Validation endpoint for pre-validation
|
|
|
|
|
r.setupValidateRoutes(v1)
|
|
|
|
|
|
2025-12-03 19:29:37 +00:00
|
|
|
// Réactivation des routes User et Track pour Phase 1
|
|
|
|
|
r.setupUserRoutes(v1)
|
|
|
|
|
r.setupTrackRoutes(v1)
|
|
|
|
|
|
2025-12-23 09:39:10 +00:00
|
|
|
// BE-API-007: Roles management routes
|
|
|
|
|
r.setupRoleRoutes(v1)
|
|
|
|
|
|
2025-12-03 19:29:37 +00:00
|
|
|
// Réactivation des routes Chat pour Phase 4
|
|
|
|
|
r.setupChatRoutes(v1)
|
2026-02-22 19:41:39 +00:00
|
|
|
|
|
|
|
|
// v0.502: Chat WebSocket endpoint (replaces Rust chat server)
|
|
|
|
|
r.setupChatWebSocket(v1)
|
|
|
|
|
|
2025-12-03 19:29:37 +00:00
|
|
|
// Réactivation des routes Playlists pour Phase 5
|
|
|
|
|
r.setupPlaylistRoutes(v1)
|
|
|
|
|
// Réactivation des routes Webhooks
|
|
|
|
|
r.setupWebhookRoutes(v1)
|
2025-12-06 16:21:59 +00:00
|
|
|
|
2025-12-03 19:29:37 +00:00
|
|
|
// Marketplace Routes (v1.2.0)
|
|
|
|
|
r.setupMarketplaceRoutes(v1)
|
2025-12-24 13:47:12 +00:00
|
|
|
|
|
|
|
|
// BE-API-035: Analytics routes
|
|
|
|
|
r.setupAnalyticsRoutes(v1)
|
feat: Visual masterpiece - true light mode & premium UI
🎨 **True Light/Dark Mode**
- Implemented proper light mode with inverted color scheme
- Smooth theme transitions (0.3s ease)
- Light mode colors: white backgrounds, dark text, vibrant accents
- System theme detection with proper class application
🌈 **Enhanced Theme System**
- 4 color themes work in both light and dark modes
- Cyber (cyan/magenta), Ocean (blue/teal), Forest (green/lime), Sunset (orange/purple)
- Theme-specific glassmorphism effects
- Proper contrast in light mode
✨ **Premium Animations**
- Float, glow-pulse, slide-in, scale-in, rotate-in animations
- Smooth page transitions
- Hover effects with depth (lift, glow, scale)
- Micro-interactions on all interactive elements
🎯 **Visual Polish**
- Enhanced glassmorphism for light/dark modes
- Custom scrollbar with theme colors
- Beautiful text selection
- Focus indicators for accessibility
- Premium utility classes
🔧 **Technical Improvements**
- Updated UIStore to properly apply light/dark classes
- Added data-theme attribute for CSS targeting
- Smooth scroll behavior
- Optimized transitions
The app is now a visual masterpiece with perfect light/dark mode support!
2026-01-11 01:32:21 +00:00
|
|
|
|
|
|
|
|
// Social Routes
|
|
|
|
|
r.setupSocialRoutes(v1)
|
Phase 2 stabilisation: code mort, Modal→Dialog, feature flags, tests, router split, Rust legacy
Bloc A - Code mort:
- Suppression Studio (components, views, features)
- Suppression gamification + services mock (projectService, storageService, gamificationService)
- Mise à jour Sidebar, Navbar, locales
Bloc B - Frontend:
- Suppression modal.tsx deprecated, Modal.stories (doublon Dialog)
- Feature flags: PLAYLIST_SEARCH, PLAYLIST_RECOMMENDATIONS, ROLE_MANAGEMENT = true
- Suppression 19 tests orphelins, retrait exclusions vitest.config
Bloc C - Backend:
- Extraction routes_auth.go depuis router.go
Bloc D - Rust:
- Suppression security_legacy.rs (code mort, patterns déjà dans security/)
2026-02-14 16:23:32 +00:00
|
|
|
|
2026-03-09 00:52:56 +00:00
|
|
|
// Feed Routes (v0.10.0 F210: tracks from followed users)
|
|
|
|
|
r.setupFeedRoutes(v1)
|
|
|
|
|
|
|
|
|
|
// Discover Routes (v0.10.1 F351-F355: tags, genres, browse)
|
|
|
|
|
r.setupDiscoverRoutes(v1)
|
|
|
|
|
|
Phase 2 stabilisation: code mort, Modal→Dialog, feature flags, tests, router split, Rust legacy
Bloc A - Code mort:
- Suppression Studio (components, views, features)
- Suppression gamification + services mock (projectService, storageService, gamificationService)
- Mise à jour Sidebar, Navbar, locales
Bloc B - Frontend:
- Suppression modal.tsx deprecated, Modal.stories (doublon Dialog)
- Feature flags: PLAYLIST_SEARCH, PLAYLIST_RECOMMENDATIONS, ROLE_MANAGEMENT = true
- Suppression 19 tests orphelins, retrait exclusions vitest.config
Bloc C - Backend:
- Extraction routes_auth.go depuis router.go
Bloc D - Rust:
- Suppression security_legacy.rs (code mort, patterns déjà dans security/)
2026-02-14 16:23:32 +00:00
|
|
|
// Inventory / Gear Routes
|
|
|
|
|
r.setupGearRoutes(v1)
|
|
|
|
|
|
2026-02-19 22:44:44 +00:00
|
|
|
// Queue Routes
|
|
|
|
|
r.setupQueueRoutes(v1)
|
|
|
|
|
|
feat(developer): add API keys backend (Lot C)
- Migration 082: api_keys table (user_id, name, prefix, hashed_key, scopes, last_used_at, expires_at)
- APIKey model, APIKeyService (Create, List, Delete, ValidateAPIKey)
- APIKeyHandler: GET/POST/DELETE /api/v1/developer/api-keys
- AuthMiddleware: X-API-Key and Bearer vza_* accepted as alternative to JWT
- CSRF: skip for API key auth (stateless)
- Key format: vza_ prefix, SHA-256 hashed storage
2026-02-19 23:18:36 +00:00
|
|
|
// Developer Portal (API Keys)
|
|
|
|
|
r.setupDeveloperRoutes(v1)
|
|
|
|
|
|
Phase 2 stabilisation: code mort, Modal→Dialog, feature flags, tests, router split, Rust legacy
Bloc A - Code mort:
- Suppression Studio (components, views, features)
- Suppression gamification + services mock (projectService, storageService, gamificationService)
- Mise à jour Sidebar, Navbar, locales
Bloc B - Frontend:
- Suppression modal.tsx deprecated, Modal.stories (doublon Dialog)
- Feature flags: PLAYLIST_SEARCH, PLAYLIST_RECOMMENDATIONS, ROLE_MANAGEMENT = true
- Suppression 19 tests orphelins, retrait exclusions vitest.config
Bloc C - Backend:
- Extraction routes_auth.go depuis router.go
Bloc D - Rust:
- Suppression security_legacy.rs (code mort, patterns déjà dans security/)
2026-02-14 16:23:32 +00:00
|
|
|
// Live Streams Routes
|
|
|
|
|
r.setupLiveRoutes(v1)
|
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
|
|
|
|
2026-02-22 17:23:58 +00:00
|
|
|
// Cloud Storage Routes (v0.501 C1)
|
|
|
|
|
r.setupCloudRoutes(v1)
|
|
|
|
|
|
2026-02-25 12:39:59 +00:00
|
|
|
// Tag suggestions (v0.802 FM1-03)
|
|
|
|
|
r.setupTagRoutes(v1)
|
|
|
|
|
|
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
|
|
|
// Unified search GET /search (tracks, users, playlists)
|
|
|
|
|
r.setupSearchRoutes(v1)
|
2025-12-03 19:29:37 +00:00
|
|
|
}
|
2025-12-13 02:34:34 +00:00
|
|
|
|
|
|
|
|
return nil
|
2025-12-03 19:29:37 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-22 19:41:39 +00:00
|
|
|
// setupChatWebSocket configures the chat WebSocket endpoint (v0.502)
|
|
|
|
|
func (r *APIRouter) setupChatWebSocket(router *gin.RouterGroup) {
|
|
|
|
|
chatService := services.NewChatServiceWithDB(r.config.ChatJWTSecret, r.db.GormDB, r.logger)
|
|
|
|
|
|
2026-02-22 20:17:51 +00:00
|
|
|
presenceService := chatws.NewChatPresenceService(r.config.RedisClient, r.logger)
|
|
|
|
|
hub := chatws.NewHub(r.logger, presenceService)
|
2026-02-22 19:41:39 +00:00
|
|
|
go hub.Run()
|
|
|
|
|
|
feat(chat): Sprint 3 -- message handlers, real-time features, permissions
- Implement full MessageHandler dispatch with all 18 incoming message types
- Add handler_messages.go: SendMessage, EditMessage, DeleteMessage with ownership checks
- Add handler_rooms.go: JoinConversation, LeaveConversation
- Add handler_history.go: FetchHistory (cursor-based), SearchMessages (ILIKE), SyncMessages
- Add handler_realtime.go: Typing, MarkAsRead, Delivered, AddReaction, RemoveReaction
- Add handler_calls.go: WebRTC signaling relay (CallOffer/Answer/ICE/Hangup/Reject)
- Add PermissionService: CanRead/CanSend/CanJoin/CanModerate based on room_members
- Add RateLimiter: per-user per-action sliding window (in-memory)
- Wire all dependencies in router.go setupChatWebSocket
2026-02-22 19:43:44 +00:00
|
|
|
msgRepo := repositories.NewChatMessageRepository(r.db.GormDB)
|
|
|
|
|
readRepo := repositories.NewReadReceiptRepository(r.db.GormDB)
|
|
|
|
|
deliveredRepo := repositories.NewDeliveredStatusRepository(r.db.GormDB)
|
|
|
|
|
reactionRepo := repositories.NewReactionRepository(r.db.GormDB)
|
2026-03-06 09:29:30 +00:00
|
|
|
userRepo := repositories.NewGormUserRepository(r.db.GormDB)
|
feat(chat): Sprint 3 -- message handlers, real-time features, permissions
- Implement full MessageHandler dispatch with all 18 incoming message types
- Add handler_messages.go: SendMessage, EditMessage, DeleteMessage with ownership checks
- Add handler_rooms.go: JoinConversation, LeaveConversation
- Add handler_history.go: FetchHistory (cursor-based), SearchMessages (ILIKE), SyncMessages
- Add handler_realtime.go: Typing, MarkAsRead, Delivered, AddReaction, RemoveReaction
- Add handler_calls.go: WebRTC signaling relay (CallOffer/Answer/ICE/Hangup/Reject)
- Add PermissionService: CanRead/CanSend/CanJoin/CanModerate based on room_members
- Add RateLimiter: per-user per-action sliding window (in-memory)
- Wire all dependencies in router.go setupChatWebSocket
2026-02-22 19:43:44 +00:00
|
|
|
|
|
|
|
|
pubsub := services.NewChatPubSubService(r.config.RedisClient, r.logger)
|
|
|
|
|
permissions := chatws.NewPermissionService(r.db.GormDB, r.logger)
|
2026-02-22 20:17:51 +00:00
|
|
|
rateLimiter := chatws.NewRateLimiter(r.config.RedisClient, r.logger)
|
feat(chat): Sprint 3 -- message handlers, real-time features, permissions
- Implement full MessageHandler dispatch with all 18 incoming message types
- Add handler_messages.go: SendMessage, EditMessage, DeleteMessage with ownership checks
- Add handler_rooms.go: JoinConversation, LeaveConversation
- Add handler_history.go: FetchHistory (cursor-based), SearchMessages (ILIKE), SyncMessages
- Add handler_realtime.go: Typing, MarkAsRead, Delivered, AddReaction, RemoveReaction
- Add handler_calls.go: WebRTC signaling relay (CallOffer/Answer/ICE/Hangup/Reject)
- Add PermissionService: CanRead/CanSend/CanJoin/CanModerate based on room_members
- Add RateLimiter: per-user per-action sliding window (in-memory)
- Wire all dependencies in router.go setupChatWebSocket
2026-02-22 19:43:44 +00:00
|
|
|
|
|
|
|
|
msgHandler := chatws.NewMessageHandler(
|
2026-03-06 09:29:30 +00:00
|
|
|
hub, msgRepo, readRepo, deliveredRepo, reactionRepo, userRepo,
|
feat(chat): Sprint 3 -- message handlers, real-time features, permissions
- Implement full MessageHandler dispatch with all 18 incoming message types
- Add handler_messages.go: SendMessage, EditMessage, DeleteMessage with ownership checks
- Add handler_rooms.go: JoinConversation, LeaveConversation
- Add handler_history.go: FetchHistory (cursor-based), SearchMessages (ILIKE), SyncMessages
- Add handler_realtime.go: Typing, MarkAsRead, Delivered, AddReaction, RemoveReaction
- Add handler_calls.go: WebRTC signaling relay (CallOffer/Answer/ICE/Hangup/Reject)
- Add PermissionService: CanRead/CanSend/CanJoin/CanModerate based on room_members
- Add RateLimiter: per-user per-action sliding window (in-memory)
- Wire all dependencies in router.go setupChatWebSocket
2026-02-22 19:43:44 +00:00
|
|
|
pubsub, permissions, rateLimiter, r.logger,
|
|
|
|
|
)
|
2026-02-22 19:41:39 +00:00
|
|
|
|
|
|
|
|
wsHandler := handlers.NewChatWebSocketHandler(chatService, hub, msgHandler, r.logger)
|
|
|
|
|
|
|
|
|
|
router.GET("/ws", wsHandler.HandleWebSocket)
|
|
|
|
|
|
2026-03-09 18:36:33 +00:00
|
|
|
// v0.10.5 F551: Inject chat hub into notification service for real-time delivery
|
|
|
|
|
if r.notificationService != nil {
|
|
|
|
|
r.notificationService.SetWSNotifier(&chatHubNotifierAdapter{hub: hub})
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-22 19:41:39 +00:00
|
|
|
r.logger.Info("Chat WebSocket endpoint registered at /api/v1/ws")
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-09 18:36:33 +00:00
|
|
|
// chatHubNotifierAdapter adapts chatws.Hub to services.NotificationWSNotifier (F551)
|
|
|
|
|
type chatHubNotifierAdapter struct {
|
|
|
|
|
hub *chatws.Hub
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *chatHubNotifierAdapter) NotifyUser(userID uuid.UUID, payload []byte) {
|
|
|
|
|
a.hub.SendToUser(userID, payload)
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-03 19:29:37 +00:00
|
|
|
// setupChatRoutes configure les routes de chat
|
|
|
|
|
func (r *APIRouter) setupChatRoutes(router *gin.RouterGroup) {
|
2025-12-23 00:51:49 +00:00
|
|
|
// BE-API-006: Use NewChatServiceWithDB to enable stats functionality
|
|
|
|
|
chatService := services.NewChatServiceWithDB(r.config.ChatJWTSecret, r.db.GormDB, r.logger)
|
2025-12-03 19:29:37 +00:00
|
|
|
userRepo := repositories.NewGormUserRepository(r.db.GormDB)
|
|
|
|
|
userService := services.NewUserServiceWithDB(userRepo, r.db.GormDB)
|
|
|
|
|
|
|
|
|
|
chatHandler := handlers.NewChatHandler(chatService, userService, r.logger)
|
|
|
|
|
|
2026-03-06 09:29:30 +00:00
|
|
|
// v0.9.6: Chat reactions REST + message search
|
|
|
|
|
reactionRepo := repositories.NewReactionRepository(r.db.GormDB)
|
|
|
|
|
msgRepo := repositories.NewChatMessageRepository(r.db.GormDB)
|
|
|
|
|
permissions := chatws.NewPermissionService(r.db.GormDB, r.logger)
|
|
|
|
|
chatReactionHandler := handlers.NewChatReactionHandler(reactionRepo, msgRepo, permissions, r.logger)
|
|
|
|
|
chatSearchHandler := handlers.NewChatSearchHandler(msgRepo, permissions, r.logger)
|
|
|
|
|
|
2025-12-03 19:29:37 +00:00
|
|
|
chat := router.Group("/chat")
|
|
|
|
|
{
|
|
|
|
|
if r.config.AuthMiddleware != nil {
|
|
|
|
|
chat.Use(r.config.AuthMiddleware.RequireAuth())
|
[BE-SEC-004] security: Implement CSRF protection for all state-changing endpoints
- Created applyCSRFProtection helper function to apply CSRF middleware
- Applied CSRF protection to all protected routes with POST/PUT/DELETE:
* Users routes (PUT, POST, DELETE)
* Tracks routes (POST, PUT, DELETE)
* Playlists routes (POST, PUT, DELETE)
* Chat routes (POST)
* Auth protected routes (POST logout, 2FA)
* Roles routes (GET only, no state-changing)
* Marketplace routes (POST)
* Webhooks routes (POST, DELETE)
* Comments routes (POST, DELETE)
- CSRF token endpoint (/csrf-token) remains accessible without CSRF check
- Middleware validates X-CSRF-Token header for all state-changing requests
- Protection only applies when Redis is available
Phase: PHASE-4
Priority: P1
Progress: 6/267 (2.2%)
2025-12-24 11:03:27 +00:00
|
|
|
// BE-SEC-004: Apply CSRF protection to all state-changing endpoints
|
|
|
|
|
r.applyCSRFProtection(chat)
|
2025-12-03 19:29:37 +00:00
|
|
|
chat.POST("/token", chatHandler.GetToken)
|
2025-12-23 00:51:49 +00:00
|
|
|
chat.GET("/stats", chatHandler.GetStats) // BE-API-006: Chat stats endpoint
|
2026-03-06 09:29:30 +00:00
|
|
|
|
|
|
|
|
// v0.9.6: Chat rooms REST (reactions, search)
|
2026-03-06 17:52:08 +00:00
|
|
|
// v0.9.7: Chat room attachments (file upload)
|
2026-03-06 09:29:30 +00:00
|
|
|
rooms := chat.Group("/rooms")
|
|
|
|
|
{
|
|
|
|
|
rooms.POST("/:roomId/messages/:messageId/reactions", chatReactionHandler.AddReaction)
|
|
|
|
|
rooms.DELETE("/:roomId/messages/:messageId/reactions", chatReactionHandler.RemoveReaction)
|
|
|
|
|
rooms.GET("/:roomId/messages/search", chatSearchHandler.SearchMessages)
|
2026-03-06 17:52:08 +00:00
|
|
|
// v0.9.7: Chat attachment upload (mp3, wav, ogg, jpg, png, pdf, max 50MB)
|
|
|
|
|
if r.config.S3StorageService != nil {
|
|
|
|
|
uploadConfig := getUploadConfigWithEnv()
|
|
|
|
|
chatUploadValidator, err := services.NewUploadValidator(uploadConfig, r.logger)
|
|
|
|
|
if err != nil {
|
|
|
|
|
r.logger.Warn("Chat upload validator unavailable - attachments disabled", zap.Error(err))
|
|
|
|
|
} else {
|
|
|
|
|
chatAttachmentHandler := handlers.NewChatAttachmentHandler(
|
|
|
|
|
r.config.S3StorageService,
|
|
|
|
|
permissions,
|
|
|
|
|
chatUploadValidator,
|
|
|
|
|
r.logger,
|
|
|
|
|
)
|
|
|
|
|
rooms.POST("/:roomId/attachments", chatAttachmentHandler.UploadChatAttachment)
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-06 09:29:30 +00:00
|
|
|
}
|
2025-12-03 19:29:37 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|