veza/veza-backend-api/internal/handlers/frontend_log_handler.go
senke b619e5a982 api-contracts: update backend handlers to use wrapped format
- Updated system_metrics.go to use RespondSuccess() helper
- Updated bitrate_handler.go success responses to use wrapped format
- Updated frontend_log_handler.go to use RespondSuccess() helper
- Updated csrf.go to use RespondSuccess() and RespondWithError() helpers
- Updated audit.go: all 30+ error and success responses now use wrapped format helpers
- Updated comment_handler.go error responses to use RespondWithError()
- Updated system_metrics_test.go to expect wrapped format {success, data}
- All handlers now consistently use wrapped format helpers
- Build and tests pass successfully
- Action 1.3.2.1 complete - backend handlers standardized to wrapped format
2026-01-15 17:32:02 +01:00

145 lines
4.3 KiB
Go

package handlers
import (
"fmt"
"net/http"
"os"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"veza-backend-api/internal/config"
"veza-backend-api/internal/logging"
)
// FrontendLogHandler gère la réception des logs du frontend
type FrontendLogHandler struct {
logger *zap.Logger
frontendLogger *logging.Logger
logDir string
commonHandler *CommonHandler
}
// NewFrontendLogHandler crée un nouveau handler pour les logs frontend
func NewFrontendLogHandler(cfg *config.Config, logger *zap.Logger) (*FrontendLogHandler, error) {
logDir := cfg.LogDir
if logDir == "" {
logDir = "/var/log/veza"
}
// Créer le répertoire de logs s'il n'existe pas
// En développement, utiliser un répertoire local si /var/log n'est pas accessible
if err := os.MkdirAll(logDir, 0755); err != nil {
// En développement, fallback vers un répertoire local
if cfg.Env == "development" || cfg.Env == "dev" {
fallbackDir := "./logs"
if err2 := os.MkdirAll(fallbackDir, 0755); err2 != nil {
return nil, fmt.Errorf("failed to create log directory %s (fallback %s also failed: %v): %w", logDir, fallbackDir, err2, err)
}
logDir = fallbackDir
} else {
return nil, fmt.Errorf("failed to create log directory %s: %w (hint: create it manually with 'sudo mkdir -p %s && sudo chown $USER:$USER %s')", logDir, err, logDir, logDir)
}
}
// Créer un logger spécifique pour le frontend avec fichiers séparés
frontendLogger, err := logging.NewLoggerWithFileRotation(logDir, "frontend", cfg.Env, cfg.LogLevel)
if err != nil {
return nil, fmt.Errorf("failed to create frontend logger: %w", err)
}
handler := &FrontendLogHandler{
logger: logger,
frontendLogger: frontendLogger,
logDir: logDir,
commonHandler: NewCommonHandler(logger),
}
return handler, nil
}
// FrontendLogRequest représente une requête de log du frontend
type FrontendLogRequest struct {
Timestamp string `json:"timestamp"`
Level string `json:"level"`
Message string `json:"message"`
Context map[string]interface{} `json:"context,omitempty"`
Data interface{} `json:"data,omitempty"`
}
// ReceiveLog gère la réception d'un log du frontend
// @Summary Receive frontend log
// @Description Receive and store a log entry from the frontend application
// @Tags Logging
// @Accept json
// @Produce json
// @Param log body FrontendLogRequest true "Frontend log entry"
// @Success 200 {object} handlers.APIResponse{data=object{received=bool}}
// @Failure 400 {object} handlers.APIResponse "Invalid log entry"
// @Failure 500 {object} handlers.APIResponse "Internal server error"
// @Router /api/v1/logs/frontend [post]
func (h *FrontendLogHandler) ReceiveLog(c *gin.Context) {
var logReq FrontendLogRequest
if err := c.ShouldBindJSON(&logReq); err != nil {
h.commonHandler.RespondWithError(c, http.StatusBadRequest, "Invalid log entry format", err)
return
}
// Valider le niveau de log
level := logReq.Level
if level == "" {
level = "INFO"
}
// Extraire le request_id du contexte si présent
requestID := ""
if logReq.Context != nil {
if rid, ok := logReq.Context["request_id"].(string); ok {
requestID = rid
}
}
// Construire les champs zap
fields := []zap.Field{
zap.String("source", "frontend"),
zap.String("level", level),
zap.String("message", logReq.Message),
}
if requestID != "" {
fields = append(fields, zap.String("request_id", requestID))
}
if logReq.Context != nil {
// Ajouter les champs du contexte (en évitant les doublons)
for k, v := range logReq.Context {
if k != "request_id" { // Déjà ajouté
fields = append(fields, zap.Any(k, v))
}
}
}
if logReq.Data != nil {
fields = append(fields, zap.Any("data", logReq.Data))
}
// Logger selon le niveau
switch level {
case "DEBUG":
h.frontendLogger.Debug(logReq.Message, fields...)
case "INFO":
h.frontendLogger.Info(logReq.Message, fields...)
case "WARN":
h.frontendLogger.Warn(logReq.Message, fields...)
case "ERROR":
h.frontendLogger.Error(logReq.Message, fields...)
default:
h.frontendLogger.Info(logReq.Message, fields...)
}
// Répondre avec succès
// Action 1.3.2.1: Use wrapped format helper
RespondSuccess(c, http.StatusOK, gin.H{
"received": true,
"level": level,
})
}