diff --git a/veza-backend-api/docs/ISSUES_P2_BACKLOG.md b/veza-backend-api/docs/ISSUES_P2_BACKLOG.md new file mode 100644 index 000000000..e79955894 --- /dev/null +++ b/veza-backend-api/docs/ISSUES_P2_BACKLOG.md @@ -0,0 +1,43 @@ +# P2 Issues Backlog — GitHub Issues à créer + +**Date** : 2026-02-17 +**Source** : [TODOS_AUDIT.md](./TODOS_AUDIT.md) + +Ce document liste les items P2 identifiés dans l'audit des TODOs. Créer des issues GitHub pour les traiter par sprint. + +## Items P2 à prioriser + +| Priorité | Fichier | Description | Catégorie | +|----------|---------|-------------|-----------| +| 1 | `internal/core/track/service.go` | Enqueue job pour traitement asynchrone (metadata, waveform) selon ORIGIN_ASYNC_PROCESSING | Performance | +| 2 | `internal/handlers/oauth_handlers.go` | ~~Get from config (frontendURL)~~ — Résolu 2026-02 | - | +| 3 | `internal/database/database.go` | Implémenter OAuth user lookup | Feature | +| 4 | `internal/database/database.go` | Implémenter avec vraie DB (3 occurrences) | Implementation | +| 5 | `internal/handlers/session.go` | Déterminer si c'est la session actuelle | Feature | +| 6 | `internal/logging/logger.go` | Implémenter avec AtomicLevel lors de la création du logger | Enhancement | +| 7 | `internal/services/job_service.go` | Intégrer asynq ou autre système de queue (3 occurrences) | Architecture | +| 8 | `internal/api/user/service.go` | Parse JSON strings to structs / Serialize structs to JSON | Implementation | +| 9 | `internal/api/admin/service.go` | Implement based on doc_admin_handler.md (3 occurrences) | Feature | +| 10 | `internal/config/config.go` | Améliorer la configuration CORS pour utiliser c.CORSOrigins depuis la config | Code quality | + +## Template issue GitHub + +```markdown +**Fichier** : `internal/...` +**Priorité** : P2 +**Catégorie** : [Performance|Feature|Implementation|Architecture|Code quality] + +## Description +[Description du TODO] + +## Contexte +Voir TODOS_AUDIT.md + +## Critères d'acceptation +- [ ] ... +``` + +## Références + +- [TODOS_AUDIT.md](./TODOS_AUDIT.md) — Audit complet +- [103_RAPPORT_ETAT_FEATURES_2026_02_16.md](../../103_RAPPORT_ETAT_FEATURES_2026_02_16.md) — État des features diff --git a/veza-backend-api/docs/ROUTES_ORPHANES.md b/veza-backend-api/docs/ROUTES_ORPHANES.md new file mode 100644 index 000000000..25263d249 --- /dev/null +++ b/veza-backend-api/docs/ROUTES_ORPHANES.md @@ -0,0 +1,39 @@ +# Routes orphelines — Backend sans UI ou partiellement exposées + +**Date** : 2026-02-17 +**Objectif** : Documenter les routes backend dont l'UI est absente, partielle ou non connectée. + +## Définition + +- **Complet** : Route utilisée par une UI dédiée +- **Partiel** : API/service prêt côté frontend mais pas d'UI (bouton, formulaire, etc.) +- **Absent** : Aucune utilisation frontend + +## Routes par statut + +### Routes sans UI (API prête, pas de composant) + +| Méthode | Path | Handler | Note | +|---------|------|---------|------| +| POST | `/api/v1/tracks/batch/delete` | `TrackHandler.BatchDeleteTracks` | `trackApi.batchDeleteTracks`, `tracksApi.batchDelete` — aucun composant n'appelle | +| POST | `/api/v1/tracks/batch/update` | `TrackHandler.BatchUpdateTracks` | `trackApi.batchUpdateTracks`, `tracksApi.batchUpdate` — aucun composant n'appelle | +| POST | `/api/v1/uploads/batch` | `UploadHandler.BatchUpload` | Pas de service frontend | + +### Routes avec UI complète + +| Méthode | Path | Handler | Note | +|---------|------|---------|------| +| GET | `/api/v1/users/me/export` | `routes_users.go` (exportHandler) | `useAccountSettings` → bouton Export Data | +| GET | `/api/v1/playlists/:id/export/json` | `PlaylistExportHandler.ExportPlaylistJSON` | `ExportPlaylistButton` + `exportUtils` | +| GET | `/api/v1/playlists/:id/export/csv` | `PlaylistExportHandler.ExportPlaylistCSV` | Idem | + +## Recommandations + +1. **Batch delete/update** : Ajouter une UI (sélection multiple dans la bibliothèque, menu contextuel → actions groupées) +2. **Batch upload** : Évaluer si nécessaire ; sinon documenter comme API avancée pour clients tiers + +## Références + +- [API_DOCUMENTATION.md](./API_DOCUMENTATION.md) +- [FEATURE_STATUS.md](../../apps/web/docs/FEATURE_STATUS.md) +- [TODOS_AUDIT.md](./TODOS_AUDIT.md) diff --git a/veza-backend-api/docs/TODOS_AUDIT.md b/veza-backend-api/docs/TODOS_AUDIT.md index e0ba8b7aa..6c2bcdbbc 100644 --- a/veza-backend-api/docs/TODOS_AUDIT.md +++ b/veza-backend-api/docs/TODOS_AUDIT.md @@ -33,14 +33,9 @@ This document lists all TODO, FIXME, HACK, and XXX comments found in the codebas - **Category**: Performance optimization ### `internal/core/track/handler.go` -- **TODO(P2-GO-004)**: trackUploadService attend int64 - Migration UUID partielle à compléter - - **Priority**: P1 - - **Status**: Open - - **Category**: Migration incomplete -- **TODO(P2-GO-004)**: Migration UUID partielle - trackUploadService nécessite migration vers UUID - - **Priority**: P1 - - **Status**: Open - - **Category**: Migration incomplete +- ~~**TODO(P2-GO-004)**: trackUploadService attend int64 - Migration UUID partielle à compléter~~ +- ~~**TODO(P2-GO-004)**: Migration UUID partielle - trackUploadService nécessite migration vers UUID~~ + - **Status**: **Resolved** (2026-02) — trackUploadService utilise uuid.UUID (GetUploadProgress, UpdateUploadStatus, GetUploadStats) ### `internal/database/database.go` - **TODO**: Implémenter OAuth user lookup @@ -53,10 +48,8 @@ This document lists all TODO, FIXME, HACK, and XXX comments found in the codebas - **Category**: Implementation placeholder ### `internal/handlers/oauth_handlers.go` -- **TODO**: Get from config (frontendURL hardcoded) - - **Priority**: P2 - - **Status**: Open - - **Category**: Configuration +- ~~**TODO**: Get from config (frontendURL hardcoded)~~ + - **Status**: **Resolved** (2026-02) — frontendURL lu depuis config.FrontendURL (FRONTEND_URL or VITE_FRONTEND_URL) ### `internal/handlers/session.go` - **TODO**: Déterminer si c'est la session actuelle @@ -71,22 +64,16 @@ This document lists all TODO, FIXME, HACK, and XXX comments found in the codebas - **Category**: Enhancement ### `internal/repositories/playlist_collaborator_repository.go` -- **FIXME**: Assurer que le modèle PlaylistCollaborator utilise UUID - - **Priority**: P1 - - **Status**: Open - - **Category**: Migration incomplete +- ~~**FIXME**: Assurer que le modèle PlaylistCollaborator utilise UUID~~ + - **Status**: **Resolved** (2026-02) — Interface et modèle utilisent uuid.UUID ### `internal/services/playlist_version_service.go` -- **FIXME**: models.PlaylistVersion ID types need check. Assuming repo handles UUID if struct updated. - - **Priority**: P1 - - **Status**: Open - - **Category**: Migration incomplete +- ~~**FIXME**: models.PlaylistVersion ID types need check. Assuming repo handles UUID if struct updated.~~ + - **Status**: **Resolved** (2026-02) — models.PlaylistVersion utilise uuid.UUID (ID, PlaylistID, UserID) ### `internal/services/track_history_service.go` -- **FIXME**: models.TrackHistory needs UUID too if not updated - - **Priority**: P1 - - **Status**: Open - - **Category**: Migration incomplete +- ~~**FIXME**: models.TrackHistory needs UUID too if not updated~~ + - **Status**: **Resolved** (2026-02) — models.TrackHistory et RecordHistoryParams utilisent uuid.UUID ### `internal/services/job_service.go` - **TODO**: Intégrer asynq ou autre système de queue (3 occurrences) @@ -95,10 +82,8 @@ This document lists all TODO, FIXME, HACK, and XXX comments found in the codebas - **Category**: Architecture ### `internal/services/playlist_service.go` -- **FIXME**: PlaylistVersionService likely needs update for UUID too, but assuming it takes what we give or we handle it later - - **Priority**: P1 - - **Status**: Open - - **Category**: Migration incomplete +- ~~**FIXME**: PlaylistVersionService likely needs update for UUID too, but assuming it takes what we give or we handle it later~~ + - **Status**: **Resolved** (2026-02) — PlaylistVersionService et modèles alignés sur UUID ### `internal/api/archive/api_manager.go` (archived, build-ignored) - **TODO**: Réactiver api_manager.go après stabilisation du noyau et alignement des services (graphql, grpc, websocket, features) @@ -179,13 +164,13 @@ This document lists all TODO, FIXME, HACK, and XXX comments found in the codebas ## Summary by Priority - **P0**: 0 items -- **P1**: 7 items (UUID migration incomplete) +- **P1**: 0 items (UUID migration completed — 7 items resolved 2026-02) - **P2**: 18 items (various improvements) - **P3**: 6 items (deferred/future features) ## Recommendations -1. **P1 Items (UUID Migration)**: Complete UUID migration for remaining services +1. ~~**P1 Items (UUID Migration)**~~: Completed (2026-02) 2. **P2 Items**: Prioritize based on impact and effort 3. **P3 Items**: Defer to future sprints or remove if no longer relevant @@ -198,5 +183,5 @@ This document lists all TODO, FIXME, HACK, and XXX comments found in the codebas --- -**Last Updated**: 2025-01-27 +**Last Updated**: 2026-02-17 **Maintained By**: Veza Backend Team diff --git a/veza-backend-api/internal/api/routes_auth.go b/veza-backend-api/internal/api/routes_auth.go index 8782c40a7..180e90613 100644 --- a/veza-backend-api/internal/api/routes_auth.go +++ b/veza-backend-api/internal/api/routes_auth.go @@ -87,7 +87,12 @@ func (r *APIRouter) setupAuthRoutes(router *gin.RouterGroup) error { loginGroup.POST("", handlers.Login(authService, sessionService, twoFactorService, r.logger, r.config)) loginGroup.POST("/2fa", handlers.LoginWith2FA(authService, sessionService, twoFactorService, r.logger, r.config)) - authGroup.POST("/refresh", handlers.Refresh(authService, sessionService, r.logger, r.config)) + // SEC-010: Rate limit refresh to prevent token grinding + refreshGroup := authGroup.Group("") + if r.config.EndpointLimiter != nil { + refreshGroup.Use(r.config.EndpointLimiter.RefreshRateLimit()) + } + refreshGroup.POST("/refresh", handlers.Refresh(authService, sessionService, r.logger, r.config)) // BE-SEC-005: Apply rate limiting to email verification endpoints verifyEmailGroup := authGroup.Group("/verify-email") @@ -102,7 +107,12 @@ func (r *APIRouter) setupAuthRoutes(router *gin.RouterGroup) error { } resendVerificationGroup.POST("", handlers.ResendVerification(authService, r.logger)) - authGroup.GET("/check-username", handlers.CheckUsername(authService)) + // SEC-009: Rate limit check-username to prevent enumeration + checkUsernameGroup := authGroup.Group("") + if r.config.EndpointLimiter != nil { + checkUsernameGroup.Use(r.config.EndpointLimiter.CheckUsernameRateLimit()) + } + checkUsernameGroup.GET("/check-username", handlers.CheckUsername(authService)) // BE-API-042: OAuth routes jwtSecretBytes := []byte(r.config.JWTSecret) @@ -126,7 +136,7 @@ func (r *APIRouter) setupAuthRoutes(router *gin.RouterGroup) error { oauthService.InitializeConfigs(googleClientID, googleClientSecret, githubClientID, githubClientSecret, discordClientID, discordClientSecret, baseURL) } - oauthHandler := handlers.NewOAuthHandler(oauthService, r.logger, r.config.CORSOrigins) + oauthHandler := handlers.NewOAuthHandler(oauthService, r.logger, r.config.CORSOrigins, r.config.FrontendURL) oauthGroup := authGroup.Group("/oauth") { oauthGroup.GET("/providers", oauthHandler.GetOAuthProviders) diff --git a/veza-backend-api/internal/config/config.go b/veza-backend-api/internal/config/config.go index 854973cae..602b03ef4 100644 --- a/veza-backend-api/internal/config/config.go +++ b/veza-backend-api/internal/config/config.go @@ -76,6 +76,7 @@ type Config struct { StreamServerInternalAPIKey string // API key for /internal/jobs/transcode (P1.1.2 - same as stream server INTERNAL_API_KEY) ChatServerURL string // URL du serveur de chat CORSOrigins []string // Liste des origines CORS autorisées + FrontendURL string // URL du frontend (OAuth redirect, password reset links). FRONTEND_URL ou VITE_FRONTEND_URL // S3 Storage Configuration (BE-SVC-005) S3Bucket string // Nom du bucket S3 @@ -273,6 +274,7 @@ func NewConfig() (*Config, error) { StreamServerInternalAPIKey: getEnv("STREAM_SERVER_INTERNAL_API_KEY", ""), ChatServerURL: getEnv("CHAT_SERVER_URL", "http://"+appDomain+":8081"), CORSOrigins: corsOrigins, + FrontendURL: getFrontendURL(), // OAuth callback, password reset, email links // S3 Storage Configuration (BE-SVC-005) S3Bucket: getEnv("AWS_S3_BUCKET", ""), diff --git a/veza-backend-api/internal/config/env_helpers.go b/veza-backend-api/internal/config/env_helpers.go index 669960905..e8d126f1b 100644 --- a/veza-backend-api/internal/config/env_helpers.go +++ b/veza-backend-api/internal/config/env_helpers.go @@ -143,3 +143,15 @@ func parseLogAggregationLabels(value string) map[string]string { return labels } + +// getFrontendURL returns the frontend URL for OAuth redirects, password reset links, etc. +// Reads FRONTEND_URL, then VITE_FRONTEND_URL, fallback to http://localhost:5173 for development. +func getFrontendURL() string { + if v := os.Getenv("FRONTEND_URL"); v != "" { + return strings.TrimSpace(v) + } + if v := os.Getenv("VITE_FRONTEND_URL"); v != "" { + return strings.TrimSpace(v) + } + return "http://localhost:5173" // Fallback for development only; set FRONTEND_URL in production +} diff --git a/veza-backend-api/internal/handlers/oauth_handlers.go b/veza-backend-api/internal/handlers/oauth_handlers.go index 6d22e9178..41c942e27 100644 --- a/veza-backend-api/internal/handlers/oauth_handlers.go +++ b/veza-backend-api/internal/handlers/oauth_handlers.go @@ -4,7 +4,6 @@ import ( "fmt" "net/http" "net/url" - "os" "strings" "veza-backend-api/internal/services" @@ -20,9 +19,10 @@ type OAuthServiceInterface interface { // OAuthHandlers handles OAuth authentication flows type OAuthHandlers struct { - oauthService OAuthServiceInterface - logger interface{} - allowedRedirectOrigins []string // SECURITY: allowlist for OAuth redirect URLs + oauthService OAuthServiceInterface + logger interface{} + allowedRedirectOrigins []string // SECURITY: allowlist for OAuth redirect URLs + frontendURL string // URL du frontend pour redirect OAuth (depuis config) } // OAuthHandlersInstance is the global instance @@ -37,11 +37,13 @@ func InitOAuthHandlers(oauthService *services.OAuthService) { // NewOAuthHandler creates a new OAuth handler instance // BE-API-042: Implement OAuth callback endpoint -func NewOAuthHandler(oauthService *services.OAuthService, logger interface{}, allowedRedirectOrigins []string) *OAuthHandlers { +// frontendURL: from config.FrontendURL (FRONTEND_URL or VITE_FRONTEND_URL env) +func NewOAuthHandler(oauthService *services.OAuthService, logger interface{}, allowedRedirectOrigins []string, frontendURL string) *OAuthHandlers { return &OAuthHandlers{ oauthService: oauthService, logger: logger, allowedRedirectOrigins: allowedRedirectOrigins, + frontendURL: frontendURL, } } @@ -50,7 +52,8 @@ func NewOAuthHandlerWithInterface(oauthService OAuthServiceInterface, logger int return &OAuthHandlers{ oauthService: oauthService, logger: logger, - allowedRedirectOrigins: nil, // Tests use nil = dev fallback + allowedRedirectOrigins: nil, // Tests use nil = dev fallback + frontendURL: "http://localhost:5173", // Tests use localhost } } @@ -115,11 +118,8 @@ func (oh *OAuthHandlers) OAuthCallback(c *gin.Context) { return } - // Redirect to frontend with token - frontendURL := os.Getenv("FRONTEND_URL") - if frontendURL == "" { - frontendURL = "http://localhost:5173" // Fallback for development - } + // Redirect to frontend with token (frontendURL from config) + frontendURL := oh.frontendURL // SECURITY: Validate redirect URL against allowlist to prevent open redirect if !oh.isAllowedRedirectOrigin(frontendURL) { c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid redirect configuration"})