v0.9.8 beta
Some checks failed
Backend API CI / test-unit (push) Failing after 0s
Backend API CI / test-integration (push) Failing after 0s

This commit is contained in:
senke 2026-03-07 00:54:35 +01:00
parent 2a4de3ce21
commit 9024fa92a0
18 changed files with 269 additions and 260 deletions

View file

@ -9,6 +9,7 @@ import (
"go.uber.org/zap"
"gorm.io/gorm"
apperrors "veza-backend-api/internal/errors"
"veza-backend-api/internal/core/marketplace"
)
@ -41,7 +42,7 @@ func (h *AdminTransferHandler) GetTransfers(c *gin.Context) {
if sellerIDStr := c.Query("seller_id"); sellerIDStr != "" {
sellerID, err := uuid.Parse(sellerIDStr)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid seller_id"})
RespondWithAppError(c, apperrors.NewValidationError("invalid seller_id"))
return
}
query = query.Where("seller_id = ?", sellerID)
@ -56,7 +57,7 @@ func (h *AdminTransferHandler) GetTransfers(c *gin.Context) {
var total int64
if err := query.Count(&total).Error; err != nil {
h.logger.Error("GetTransfers count failed", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to count transfers"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to count transfers", err))
return
}
@ -76,7 +77,7 @@ func (h *AdminTransferHandler) GetTransfers(c *gin.Context) {
var transfers []marketplace.SellerTransfer
if err := query.Order("created_at DESC").Limit(limit).Offset(offset).Find(&transfers).Error; err != nil {
h.logger.Error("GetTransfers find failed", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch transfers"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to fetch transfers", err))
return
}
@ -89,34 +90,34 @@ func (h *AdminTransferHandler) GetTransfers(c *gin.Context) {
// RetryTransfer manually retries a failed transfer.
func (h *AdminTransferHandler) RetryTransfer(c *gin.Context) {
if h.ts == nil {
c.JSON(http.StatusServiceUnavailable, gin.H{"error": "Stripe Connect is not enabled"})
RespondWithAppError(c, apperrors.NewServiceUnavailableError("Stripe Connect is not enabled"))
return
}
idStr := c.Param("id")
if idStr == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "transfer id required"})
RespondWithAppError(c, apperrors.NewValidationError("transfer id required"))
return
}
transferID, err := uuid.Parse(idStr)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid transfer id"})
RespondWithAppError(c, apperrors.NewValidationError("invalid transfer id"))
return
}
var t marketplace.SellerTransfer
if err := h.db.First(&t, transferID).Error; err != nil {
if err == gorm.ErrRecordNotFound {
c.JSON(http.StatusNotFound, gin.H{"error": "transfer not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("transfer"))
return
}
h.logger.Error("RetryTransfer find failed", zap.Error(err), zap.String("transfer_id", idStr))
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch transfer"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to fetch transfer", err))
return
}
if t.Status != "failed" {
c.JSON(http.StatusBadRequest, gin.H{"error": "only failed transfers can be retried"})
RespondWithAppError(c, apperrors.NewValidationError("only failed transfers can be retried"))
return
}
@ -129,7 +130,7 @@ func (h *AdminTransferHandler) RetryTransfer(c *gin.Context) {
h.logger.Error("RetryTransfer save failed", zap.Error(saveErr))
}
h.logger.Error("RetryTransfer CreateTransfer failed", zap.Error(err), zap.String("transfer_id", idStr))
c.JSON(http.StatusInternalServerError, gin.H{"error": "Transfer failed: " + err.Error()})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Transfer failed", err))
return
}
@ -137,7 +138,7 @@ func (h *AdminTransferHandler) RetryTransfer(c *gin.Context) {
t.ErrorMessage = ""
if err := h.db.Save(&t).Error; err != nil {
h.logger.Error("RetryTransfer save failed", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update transfer"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to update transfer", err))
return
}

View file

@ -6,8 +6,8 @@ import (
"net/http"
"path/filepath"
apperrors "veza-backend-api/internal/errors"
"veza-backend-api/internal/services"
chatws "veza-backend-api/internal/websocket/chat"
"github.com/gin-gonic/gin"
@ -55,64 +55,64 @@ func (h *ChatAttachmentHandler) UploadChatAttachment(c *gin.Context) {
roomID, err := uuid.Parse(c.Param("roomId"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid room ID"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid room ID"))
return
}
if !h.permissions.CanRead(c.Request.Context(), userID, roomID) {
c.JSON(http.StatusForbidden, gin.H{"error": "Not allowed to access this conversation"})
RespondWithAppError(c, apperrors.NewForbiddenError("Not allowed to access this conversation"))
return
}
fileHeader, err := c.FormFile("file")
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "No file provided"})
RespondWithAppError(c, apperrors.NewValidationError("No file provided"))
return
}
if fileHeader.Size > chatAttachmentMaxSize {
c.JSON(http.StatusBadRequest, gin.H{"error": "File too large. Max 50MB"})
RespondWithAppError(c, apperrors.NewValidationError("File too large. Max 50MB"))
return
}
if fileHeader.Size == 0 {
c.JSON(http.StatusBadRequest, gin.H{"error": "Empty file"})
RespondWithAppError(c, apperrors.NewValidationError("Empty file"))
return
}
fileType := h.validator.GetFileTypeFromPath(fileHeader.Filename)
if fileType == "unknown" {
c.JSON(http.StatusBadRequest, gin.H{"error": "Unsupported file type. Allowed: mp3, wav, ogg, jpg, png, pdf"})
RespondWithAppError(c, apperrors.NewValidationError("Unsupported file type. Allowed: mp3, wav, ogg, jpg, png, pdf"))
return
}
validationResult, err := h.validator.ValidateFile(c.Request.Context(), fileHeader, fileType)
if err != nil {
h.logger.Warn("Chat attachment validation failed", zap.Error(err), zap.String("filename", fileHeader.Filename))
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": "File validation failed: " + err.Error()})
RespondWithAppError(c, apperrors.NewValidationError("File validation failed: "+err.Error()))
return
}
if !validationResult.Valid {
c.JSON(http.StatusBadRequest, gin.H{"error": validationResult.Error})
RespondWithAppError(c, apperrors.NewValidationError(validationResult.Error))
return
}
if validationResult.Quarantined {
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": "File rejected: virus detected"})
RespondWithAppError(c, apperrors.NewForbiddenError("File rejected: virus detected"))
return
}
file, err := fileHeader.Open()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to read file"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to read file", err))
return
}
defer file.Close()
data, err := io.ReadAll(file)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to read file"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to read file", err))
return
}
@ -125,7 +125,7 @@ func (h *ChatAttachmentHandler) UploadChatAttachment(c *gin.Context) {
_, err = h.s3Service.UploadFile(c.Request.Context(), data, key, fileHeader.Header.Get("Content-Type"))
if err != nil {
h.logger.Error("Failed to upload chat attachment", zap.Error(err), zap.String("key", key))
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to upload file"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to upload file", err))
return
}

View file

@ -84,12 +84,12 @@ func NewChatHandlerWithInterface(chatService ChatServiceInterfaceForChatHandler,
func (h *ChatHandler) GetToken(c *gin.Context) {
userIDVal, exists := c.Get("user_id")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
RespondWithAppError(c, apperrors.NewUnauthorizedError("unauthorized"))
return
}
userID, ok := userIDVal.(uuid.UUID)
if !ok || userID == uuid.Nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
RespondWithAppError(c, apperrors.NewUnauthorizedError("unauthorized"))
return
}
@ -106,7 +106,7 @@ func (h *ChatHandler) GetToken(c *gin.Context) {
token, err := h.chatService.GenerateToken(userID, username)
if err != nil {
h.logger.Error("Failed to generate chat token", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to generate token"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("failed to generate token", err))
return
}

View file

@ -4,8 +4,8 @@ import (
"net/http"
"strconv"
apperrors "veza-backend-api/internal/errors"
"veza-backend-api/internal/repositories"
chatws "veza-backend-api/internal/websocket/chat"
"github.com/gin-gonic/gin"
@ -51,18 +51,18 @@ func (h *ChatSearchHandler) SearchMessages(c *gin.Context) {
roomID, err := uuid.Parse(c.Param("roomId"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid room ID"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid room ID"))
return
}
if !h.permissions.CanRead(c.Request.Context(), userID, roomID) {
c.JSON(http.StatusForbidden, gin.H{"error": "Not allowed to access this conversation"})
RespondWithAppError(c, apperrors.NewForbiddenError("Not allowed to access this conversation"))
return
}
query := c.Query("q")
if query == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "query parameter 'q' is required"})
RespondWithAppError(c, apperrors.NewValidationError("query parameter 'q' is required"))
return
}
@ -85,7 +85,7 @@ func (h *ChatSearchHandler) SearchMessages(c *gin.Context) {
messages, total, err := h.msgRepo.Search(c.Request.Context(), roomID, query, limit, offset)
if err != nil {
h.logger.Error("Failed to search messages", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to search messages"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to search messages", err))
return
}

View file

@ -12,6 +12,7 @@ import (
"github.com/google/uuid"
"go.uber.org/zap"
apperrors "veza-backend-api/internal/errors"
"veza-backend-api/internal/services"
)
@ -45,7 +46,7 @@ func (h *CloudHandler) getUserID(c *gin.Context) (uuid.UUID, error) {
func (h *CloudHandler) ListFolders(c *gin.Context) {
userID, err := h.getUserID(c)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
RespondWithAppError(c, apperrors.NewUnauthorizedError("unauthorized"))
return
}
@ -53,7 +54,7 @@ func (h *CloudHandler) ListFolders(c *gin.Context) {
if pidStr := c.Query("parent_id"); pidStr != "" {
pid, err := uuid.Parse(pidStr)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid parent_id"})
RespondWithAppError(c, apperrors.NewValidationError("invalid parent_id"))
return
}
parentID = &pid
@ -62,7 +63,7 @@ func (h *CloudHandler) ListFolders(c *gin.Context) {
folders, err := h.cloudService.ListFolders(c.Request.Context(), userID, parentID)
if err != nil {
h.logger.Error("Failed to list folders", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list folders"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("failed to list folders", err))
return
}
@ -72,7 +73,7 @@ func (h *CloudHandler) ListFolders(c *gin.Context) {
func (h *CloudHandler) CreateFolder(c *gin.Context) {
userID, err := h.getUserID(c)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
RespondWithAppError(c, apperrors.NewUnauthorizedError("unauthorized"))
return
}
@ -81,7 +82,7 @@ func (h *CloudHandler) CreateFolder(c *gin.Context) {
ParentID *string `json:"parent_id"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "name is required"})
RespondWithAppError(c, apperrors.NewValidationError("name is required"))
return
}
@ -89,7 +90,7 @@ func (h *CloudHandler) CreateFolder(c *gin.Context) {
if req.ParentID != nil && *req.ParentID != "" {
pid, err := uuid.Parse(*req.ParentID)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid parent_id"})
RespondWithAppError(c, apperrors.NewValidationError("invalid parent_id"))
return
}
parentID = &pid
@ -98,11 +99,11 @@ func (h *CloudHandler) CreateFolder(c *gin.Context) {
folder, err := h.cloudService.CreateFolder(c.Request.Context(), userID, req.Name, parentID)
if err != nil {
if errors.Is(err, services.ErrFolderNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "parent folder not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("parent folder"))
return
}
h.logger.Error("Failed to create folder", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create folder"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("failed to create folder", err))
return
}
@ -112,13 +113,13 @@ func (h *CloudHandler) CreateFolder(c *gin.Context) {
func (h *CloudHandler) RenameFolder(c *gin.Context) {
userID, err := h.getUserID(c)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
RespondWithAppError(c, apperrors.NewUnauthorizedError("unauthorized"))
return
}
folderID, err := uuid.Parse(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid folder id"})
RespondWithAppError(c, apperrors.NewValidationError("invalid folder id"))
return
}
@ -126,16 +127,16 @@ func (h *CloudHandler) RenameFolder(c *gin.Context) {
Name string `json:"name" binding:"required"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "name is required"})
RespondWithAppError(c, apperrors.NewValidationError("name is required"))
return
}
if err := h.cloudService.RenameFolder(c.Request.Context(), userID, folderID, req.Name); err != nil {
if errors.Is(err, services.ErrFolderNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "folder not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("folder"))
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to rename folder"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("failed to rename folder", err))
return
}
@ -145,22 +146,22 @@ func (h *CloudHandler) RenameFolder(c *gin.Context) {
func (h *CloudHandler) DeleteFolder(c *gin.Context) {
userID, err := h.getUserID(c)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
RespondWithAppError(c, apperrors.NewUnauthorizedError("unauthorized"))
return
}
folderID, err := uuid.Parse(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid folder id"})
RespondWithAppError(c, apperrors.NewValidationError("invalid folder id"))
return
}
if err := h.cloudService.DeleteFolder(c.Request.Context(), userID, folderID); err != nil {
if errors.Is(err, services.ErrFolderNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "folder not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("folder"))
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete folder"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("failed to delete folder", err))
return
}
@ -170,7 +171,7 @@ func (h *CloudHandler) DeleteFolder(c *gin.Context) {
func (h *CloudHandler) ListFiles(c *gin.Context) {
userID, err := h.getUserID(c)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
RespondWithAppError(c, apperrors.NewUnauthorizedError("unauthorized"))
return
}
@ -178,7 +179,7 @@ func (h *CloudHandler) ListFiles(c *gin.Context) {
if fidStr := c.Query("folder_id"); fidStr != "" {
fid, err := uuid.Parse(fidStr)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid folder_id"})
RespondWithAppError(c, apperrors.NewValidationError("invalid folder_id"))
return
}
folderID = &fid
@ -187,7 +188,7 @@ func (h *CloudHandler) ListFiles(c *gin.Context) {
files, err := h.cloudService.ListFiles(c.Request.Context(), userID, folderID)
if err != nil {
h.logger.Error("Failed to list files", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list files"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("failed to list files", err))
return
}
@ -197,20 +198,20 @@ func (h *CloudHandler) ListFiles(c *gin.Context) {
func (h *CloudHandler) UploadFile(c *gin.Context) {
userID, err := h.getUserID(c)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
RespondWithAppError(c, apperrors.NewUnauthorizedError("unauthorized"))
return
}
file, header, err := c.Request.FormFile("file")
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "file is required"})
RespondWithAppError(c, apperrors.NewValidationError("file is required"))
return
}
defer file.Close()
data, err := io.ReadAll(file)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to read file"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("failed to read file", err))
return
}
@ -223,7 +224,7 @@ func (h *CloudHandler) UploadFile(c *gin.Context) {
if fidStr := c.PostForm("folder_id"); fidStr != "" {
fid, err := uuid.Parse(fidStr)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid folder_id"})
RespondWithAppError(c, apperrors.NewValidationError("invalid folder_id"))
return
}
folderID = &fid
@ -232,19 +233,19 @@ func (h *CloudHandler) UploadFile(c *gin.Context) {
result, err := h.cloudService.UploadFile(c.Request.Context(), userID, folderID, header.Filename, data, mimeType)
if err != nil {
if errors.Is(err, services.ErrQuotaExceeded) {
c.JSON(http.StatusForbidden, gin.H{"error": "storage quota exceeded"})
RespondWithAppError(c, apperrors.NewForbiddenError("storage quota exceeded"))
return
}
if errors.Is(err, services.ErrInvalidMimeType) {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid file type"})
RespondWithAppError(c, apperrors.NewValidationError("invalid file type"))
return
}
if errors.Is(err, services.ErrFileTooLarge) {
c.JSON(http.StatusBadRequest, gin.H{"error": "file too large (max 500MB)"})
RespondWithAppError(c, apperrors.NewValidationError("file too large (max 500MB)"))
return
}
h.logger.Error("Failed to upload file", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to upload file"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("failed to upload file", err))
return
}
@ -254,19 +255,19 @@ func (h *CloudHandler) UploadFile(c *gin.Context) {
func (h *CloudHandler) GetFile(c *gin.Context) {
userID, err := h.getUserID(c)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
RespondWithAppError(c, apperrors.NewUnauthorizedError("unauthorized"))
return
}
fileID, err := uuid.Parse(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid file id"})
RespondWithAppError(c, apperrors.NewValidationError("invalid file id"))
return
}
file, err := h.cloudService.GetFileByID(c.Request.Context(), userID, fileID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "file not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("file"))
return
}
@ -276,22 +277,22 @@ func (h *CloudHandler) GetFile(c *gin.Context) {
func (h *CloudHandler) DeleteFile(c *gin.Context) {
userID, err := h.getUserID(c)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
RespondWithAppError(c, apperrors.NewUnauthorizedError("unauthorized"))
return
}
fileID, err := uuid.Parse(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid file id"})
RespondWithAppError(c, apperrors.NewValidationError("invalid file id"))
return
}
if err := h.cloudService.DeleteFile(c.Request.Context(), userID, fileID); err != nil {
if errors.Is(err, services.ErrFileNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "file not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("file"))
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete file"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("failed to delete file", err))
return
}
@ -302,23 +303,23 @@ func (h *CloudHandler) DeleteFile(c *gin.Context) {
func (h *CloudHandler) StreamFile(c *gin.Context) {
userID, err := h.getUserID(c)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
RespondWithAppError(c, apperrors.NewUnauthorizedError("unauthorized"))
return
}
fileID, err := uuid.Parse(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid file id"})
RespondWithAppError(c, apperrors.NewValidationError("invalid file id"))
return
}
data, file, err := h.cloudService.StreamFile(c.Request.Context(), userID, fileID)
if err != nil {
if errors.Is(err, services.ErrFileNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "file not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("file"))
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to stream file"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("failed to stream file", err))
return
}
@ -336,7 +337,7 @@ func (h *CloudHandler) StreamFile(c *gin.Context) {
rangeStr := strings.TrimPrefix(rangeHeader, "bytes=")
parts := strings.SplitN(rangeStr, "-", 2)
if len(parts) != 2 {
c.JSON(http.StatusRequestedRangeNotSatisfiable, gin.H{"error": "invalid range"})
RespondWithAppError(c, apperrors.NewValidationError("invalid range"))
return
}
@ -344,7 +345,7 @@ func (h *CloudHandler) StreamFile(c *gin.Context) {
if parts[0] != "" {
start, err = strconv.ParseInt(parts[0], 10, 64)
if err != nil || start < 0 || start >= totalSize {
c.JSON(http.StatusRequestedRangeNotSatisfiable, gin.H{"error": "invalid range start"})
RespondWithAppError(c, apperrors.NewValidationError("invalid range start"))
return
}
}
@ -359,7 +360,7 @@ func (h *CloudHandler) StreamFile(c *gin.Context) {
}
if start > end {
c.JSON(http.StatusRequestedRangeNotSatisfiable, gin.H{"error": "invalid range"})
RespondWithAppError(c, apperrors.NewValidationError("invalid range"))
return
}
@ -372,13 +373,13 @@ func (h *CloudHandler) StreamFile(c *gin.Context) {
func (h *CloudHandler) GetQuota(c *gin.Context) {
userID, err := h.getUserID(c)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
RespondWithAppError(c, apperrors.NewUnauthorizedError("unauthorized"))
return
}
quota, err := h.cloudService.GetQuota(c.Request.Context(), userID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get quota"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("failed to get quota", err))
return
}
@ -397,24 +398,24 @@ func (h *CloudHandler) GetQuota(c *gin.Context) {
func (h *CloudHandler) ListVersions(c *gin.Context) {
userID, err := h.getUserID(c)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
RespondWithAppError(c, apperrors.NewUnauthorizedError("unauthorized"))
return
}
fileID, err := uuid.Parse(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid file id"})
RespondWithAppError(c, apperrors.NewValidationError("invalid file id"))
return
}
versions, err := h.cloudService.ListVersions(c.Request.Context(), userID, fileID)
if err != nil {
if errors.Is(err, services.ErrFileNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "file not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("file"))
return
}
h.logger.Error("Failed to list versions", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list versions"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("failed to list versions", err))
return
}
@ -425,30 +426,30 @@ func (h *CloudHandler) ListVersions(c *gin.Context) {
func (h *CloudHandler) RestoreVersion(c *gin.Context) {
userID, err := h.getUserID(c)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
RespondWithAppError(c, apperrors.NewUnauthorizedError("unauthorized"))
return
}
fileID, err := uuid.Parse(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid file id"})
RespondWithAppError(c, apperrors.NewValidationError("invalid file id"))
return
}
versionStr := c.Param("version")
version, err := strconv.Atoi(versionStr)
if err != nil || version < 1 {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid version"})
RespondWithAppError(c, apperrors.NewValidationError("invalid version"))
return
}
if err := h.cloudService.RestoreVersion(c.Request.Context(), userID, fileID, version); err != nil {
if errors.Is(err, services.ErrFileNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "file not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("file"))
return
}
h.logger.Error("Failed to restore version", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to restore version"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("failed to restore version", err))
return
}
@ -459,13 +460,13 @@ func (h *CloudHandler) RestoreVersion(c *gin.Context) {
func (h *CloudHandler) ShareFile(c *gin.Context) {
userID, err := h.getUserID(c)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
RespondWithAppError(c, apperrors.NewUnauthorizedError("unauthorized"))
return
}
fileID, err := uuid.Parse(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid file id"})
RespondWithAppError(c, apperrors.NewValidationError("invalid file id"))
return
}
@ -490,11 +491,11 @@ func (h *CloudHandler) ShareFile(c *gin.Context) {
share, err := h.cloudService.ShareFile(c.Request.Context(), userID, fileID, req.Permissions, req.ExpiresInHours)
if err != nil {
if errors.Is(err, services.ErrFileNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "file not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("file"))
return
}
h.logger.Error("Failed to share file", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to share file"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("failed to share file", err))
return
}
@ -512,17 +513,17 @@ func (h *CloudHandler) ShareFile(c *gin.Context) {
func (h *CloudHandler) GetSharedFile(c *gin.Context) {
token := c.Param("token")
if token == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "token required"})
RespondWithAppError(c, apperrors.NewValidationError("token required"))
return
}
file, err := h.cloudService.GetSharedFile(c.Request.Context(), token)
if err != nil {
if errors.Is(err, services.ErrShareExpired) || errors.Is(err, services.ErrShareNotFound) || errors.Is(err, services.ErrFileNotFound) {
c.JSON(http.StatusGone, gin.H{"error": "share link expired"})
RespondWithAppError(c, apperrors.NewNotFoundError("share link expired"))
return
}
c.JSON(http.StatusNotFound, gin.H{"error": "file not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("file"))
return
}
@ -533,24 +534,24 @@ func (h *CloudHandler) GetSharedFile(c *gin.Context) {
func (h *CloudHandler) CreateVersion(c *gin.Context) {
userID, err := h.getUserID(c)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
RespondWithAppError(c, apperrors.NewUnauthorizedError("unauthorized"))
return
}
fileID, err := uuid.Parse(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid file id"})
RespondWithAppError(c, apperrors.NewValidationError("invalid file id"))
return
}
version, err := h.cloudService.CreateVersion(c.Request.Context(), userID, fileID)
if err != nil {
if errors.Is(err, services.ErrFileNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "file not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("file"))
return
}
h.logger.Error("Failed to create version", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create version"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("failed to create version", err))
return
}
@ -561,24 +562,24 @@ func (h *CloudHandler) CreateVersion(c *gin.Context) {
func (h *CloudHandler) PublishAsTrack(c *gin.Context) {
userID, err := h.getUserID(c)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
RespondWithAppError(c, apperrors.NewUnauthorizedError("unauthorized"))
return
}
fileID, err := uuid.Parse(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid file id"})
RespondWithAppError(c, apperrors.NewValidationError("invalid file id"))
return
}
file, err := h.cloudService.GetFileByID(c.Request.Context(), userID, fileID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "file not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("file"))
return
}
if !strings.HasPrefix(file.MimeType, "audio/") {
c.JSON(http.StatusBadRequest, gin.H{"error": "only audio files can be published as tracks"})
RespondWithAppError(c, apperrors.NewValidationError("only audio files can be published as tracks"))
return
}

View file

@ -228,19 +228,19 @@ func (h *CommentHandler) UpdateComment(c *gin.Context) {
return // Erreur déjà envoyée par GetUserIDUUID
}
if userID == uuid.Nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
RespondWithAppError(c, apperrors.NewUnauthorizedError("unauthorized"))
return
}
commentIDStr := c.Param("id")
if commentIDStr == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "comment id is required"})
RespondWithAppError(c, apperrors.NewValidationError("comment id is required"))
return
}
commentID, err := uuid.Parse(commentIDStr) // Changed to uuid.Parse
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid comment id"})
RespondWithAppError(c, apperrors.NewValidationError("invalid comment id"))
return
}
@ -256,18 +256,18 @@ func (h *CommentHandler) UpdateComment(c *gin.Context) {
comment, err := h.commentService.UpdateComment(c.Request.Context(), commentID, userID, req.Content)
if err != nil {
if errors.Is(err, services.ErrCommentNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "comment not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("comment"))
return
}
if errors.Is(err, services.ErrForbidden) {
c.JSON(http.StatusForbidden, gin.H{"error": "unauthorized: you can only edit your own comments"})
RespondWithAppError(c, apperrors.NewForbiddenError("unauthorized: you can only edit your own comments"))
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to update comment", err))
return
}
c.JSON(http.StatusOK, gin.H{"comment": comment})
RespondSuccess(c, http.StatusOK, gin.H{"comment": comment})
}
// DeleteComment gère la suppression d'un commentaire
@ -342,13 +342,13 @@ func (h *CommentHandler) DeleteComment(c *gin.Context) {
func (h *CommentHandler) GetReplies(c *gin.Context) {
parentIDStr := c.Param("id")
if parentIDStr == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "parent comment id is required"})
RespondWithAppError(c, apperrors.NewValidationError("parent comment id is required"))
return
}
parentID, err := uuid.Parse(parentIDStr) // Changed to uuid.Parse
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid parent comment id"})
RespondWithAppError(c, apperrors.NewValidationError("invalid parent comment id"))
return
}
@ -368,14 +368,14 @@ func (h *CommentHandler) GetReplies(c *gin.Context) {
replies, total, err := h.commentService.GetReplies(c.Request.Context(), parentID, page, limit)
if err != nil {
if errors.Is(err, services.ErrParentCommentNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "parent comment not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("parent comment"))
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to get replies", err))
return
}
c.JSON(http.StatusOK, gin.H{
RespondSuccess(c, http.StatusOK, gin.H{
"replies": replies,
"total": total,
"page": page,

View file

@ -5,6 +5,7 @@ import (
"github.com/gin-gonic/gin"
apperrors "veza-backend-api/internal/errors"
"veza-backend-api/internal/services"
)
@ -22,7 +23,7 @@ func NewFeatureFlagHandler(svc *services.FeatureFlagService) *FeatureFlagHandler
func (h *FeatureFlagHandler) List(c *gin.Context) {
list, err := h.svc.List(c.Request.Context())
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to list feature flags"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to list feature flags", err))
return
}
c.JSON(http.StatusOK, gin.H{"feature_flags": list})
@ -32,7 +33,7 @@ func (h *FeatureFlagHandler) List(c *gin.Context) {
func (h *FeatureFlagHandler) Toggle(c *gin.Context) {
name := c.Param("name")
if name == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "name is required"})
RespondWithAppError(c, apperrors.NewValidationError("name is required"))
return
}
@ -40,13 +41,13 @@ func (h *FeatureFlagHandler) Toggle(c *gin.Context) {
Enabled bool `json:"enabled"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "enabled is required"})
RespondWithAppError(c, apperrors.NewValidationError("enabled is required"))
return
}
flag, err := h.svc.Toggle(c.Request.Context(), name, req.Enabled)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to toggle feature flag", err))
return
}
c.JSON(http.StatusOK, flag)

View file

@ -330,14 +330,14 @@ func (h *GearHandler) DeleteGear(c *gin.Context) {
func (h *GearHandler) ListPublicGear(c *gin.Context) {
username := c.Param("id") // route uses :id to match /users/:id/* pattern
if username == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "username is required"})
RespondWithAppError(c, apperrors.NewValidationError("username is required"))
return
}
items, err := h.gearService.ListPublicGearByUsername(c.Request.Context(), username)
if err != nil {
h.logger.Error("Failed to list public gear", zap.String("username", username), zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list gear"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("failed to list gear", err))
return
}

View file

@ -5,13 +5,13 @@ import (
"encoding/json"
"errors"
"net"
"net/http"
"strconv"
"sync"
"time"
"github.com/google/uuid"
apperrors "veza-backend-api/internal/errors"
"veza-backend-api/internal/models"
"veza-backend-api/internal/services"
wsmsg "veza-backend-api/internal/websocket"
@ -97,7 +97,7 @@ func (h *PlaybackWebSocketHandler) WebSocketHandler(c *gin.Context) {
return // Erreur déjà envoyée par GetUserIDUUID
}
if userID == uuid.Nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
RespondWithAppError(c, apperrors.NewUnauthorizedError("unauthorized"))
return
}

View file

@ -46,7 +46,7 @@ func (h *PresenceHandler) GetPresence(c *gin.Context) {
p, err := h.presenceService.GetPresenceForViewer(c.Request.Context(), userID, viewerID)
if err != nil {
h.logger.Error("Failed to get presence", zap.Error(err), zap.String("user_id", userID.String()))
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get presence"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to get presence", err))
return
}
if p == nil {
@ -117,7 +117,7 @@ func (h *PresenceHandler) UpdatePresence(c *gin.Context) {
if err := h.presenceService.UpdatePresenceFull(c.Request.Context(), userID, input); err != nil {
h.logger.Error("Failed to update presence", zap.Error(err), zap.String("user_id", userID.String()))
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update presence"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to update presence", err))
return
}
RespondSuccess(c, http.StatusOK, gin.H{"message": "Presence updated"})

View file

@ -6,6 +6,8 @@ import (
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"gorm.io/gorm"
apperrors "veza-backend-api/internal/errors"
)
// PrivacyOptOut sets the CCPA "Do Not Sell" preference for the authenticated user.
@ -23,7 +25,7 @@ func PrivacyOptOut(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
userID, ok := GetUserIDUUID(c)
if !ok || userID == uuid.Nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication required"})
RespondWithAppError(c, apperrors.NewUnauthorizedError("Authentication required"))
return
}
@ -38,7 +40,7 @@ func PrivacyOptOut(db *gorm.DB) gin.HandlerFunc {
`, userID)
if result.Error != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save privacy preference"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to save privacy preference", result.Error))
return
}

View file

@ -7,6 +7,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/google/uuid"
apperrors "veza-backend-api/internal/errors"
"veza-backend-api/internal/services"
)
@ -32,7 +33,7 @@ func (h *ReportHandler) ListReports(c *gin.Context) {
Offset: offset,
})
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to list reports"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to list reports", err))
return
}
@ -51,24 +52,24 @@ func (h *ReportHandler) ResolveReport(c *gin.Context) {
reportIDStr := c.Param("id")
reportID, err := uuid.Parse(reportIDStr)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid report ID"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid report ID"))
return
}
userID, ok := GetUserIDUUID(c)
if !ok || userID == uuid.Nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication required"})
RespondWithAppError(c, apperrors.NewUnauthorizedError("Authentication required"))
return
}
var req services.ResolveReportRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "action is required"})
RespondWithAppError(c, apperrors.NewValidationError("action is required"))
return
}
if err := h.reportService.Resolve(c.Request.Context(), reportID, userID, req.Action); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to resolve report", err))
return
}
@ -79,19 +80,19 @@ func (h *ReportHandler) ResolveReport(c *gin.Context) {
func (h *ReportHandler) CreateReport(c *gin.Context) {
userID, ok := GetUserIDUUID(c)
if !ok || userID == uuid.Nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication required"})
RespondWithAppError(c, apperrors.NewUnauthorizedError("Authentication required"))
return
}
var req services.CreateReportRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "content_type and reason are required"})
RespondWithAppError(c, apperrors.NewValidationError("content_type and reason are required"))
return
}
report, err := h.reportService.Create(c.Request.Context(), userID, req)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create report"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to create report", err))
return
}

View file

@ -54,14 +54,14 @@ func (h *RoomHandler) CreateRoom(c *gin.Context) {
// Récupérer l'ID utilisateur du contexte
userIDInterface, exists := c.Get("user_id")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
RespondWithAppError(c, apperrors.NewUnauthorizedError("User not authenticated"))
return
}
// Convertir userID en uuid.UUID
userID, ok := userIDInterface.(uuid.UUID)
if !ok {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user ID type in context"})
RespondWithAppError(c, apperrors.NewInternalError("Invalid user ID type in context"))
return
}
@ -84,7 +84,7 @@ func (h *RoomHandler) CreateRoom(c *gin.Context) {
zap.Error(err),
zap.String("user_id", userID.String()),
zap.String("room_name", req.Name))
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create conversation"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to create conversation", err))
return
}
@ -102,14 +102,14 @@ func (h *RoomHandler) GetUserRooms(c *gin.Context) {
// Récupérer l'ID utilisateur du contexte
userIDInterface, exists := c.Get("user_id")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
RespondWithAppError(c, apperrors.NewUnauthorizedError("User not authenticated"))
return
}
// Convertir userID en uuid.UUID
userID, ok := userIDInterface.(uuid.UUID)
if !ok {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user ID type in context"})
RespondWithAppError(c, apperrors.NewInternalError("Invalid user ID type in context"))
return
}
@ -119,7 +119,7 @@ func (h *RoomHandler) GetUserRooms(c *gin.Context) {
h.logger.Error("failed to get user rooms",
zap.Error(err),
zap.String("user_id", userID.String()))
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch conversations"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to fetch conversations", err))
return
}
@ -136,7 +136,7 @@ func (h *RoomHandler) GetRoom(c *gin.Context) {
roomIDStr := c.Param("id")
roomID, err := uuid.Parse(roomIDStr)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid room ID"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid room ID"))
return
}
@ -144,13 +144,13 @@ func (h *RoomHandler) GetRoom(c *gin.Context) {
room, err := h.roomService.GetRoom(c.Request.Context(), roomID)
if err != nil {
if errors.Is(err, services.ErrRoomNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "Conversation not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("Conversation"))
return
}
h.logger.Error("failed to get room",
zap.Error(err),
zap.String("room_id", roomID.String()))
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get conversation"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to get conversation", err))
return
}
@ -221,7 +221,7 @@ func (h *RoomHandler) AddMember(c *gin.Context) {
roomIDStr := c.Param("id")
roomID, err := uuid.Parse(roomIDStr)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid room ID"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid room ID"))
return
}
@ -238,7 +238,7 @@ func (h *RoomHandler) AddMember(c *gin.Context) {
zap.Error(err),
zap.String("room_id", roomID.String()),
zap.String("user_id", req.UserID.String()))
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to add member"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to add member", err))
return
}
@ -256,7 +256,7 @@ func (h *RoomHandler) GetRoomHistory(c *gin.Context) {
conversationIDStr := c.Param("id")
conversationID, err := uuid.Parse(conversationIDStr)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid conversation ID"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid conversation ID"))
return
}
@ -274,13 +274,13 @@ func (h *RoomHandler) GetRoomHistory(c *gin.Context) {
result, err := h.roomService.GetRoomHistoryWithCursor(c.Request.Context(), conversationID, limitInt, cursor)
if err != nil {
if errors.Is(err, services.ErrRoomNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "Conversation not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("Conversation"))
return
}
h.logger.Error("failed to get room history",
zap.Error(err),
zap.String("conversation_id", conversationID.String()))
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get conversation history"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to get conversation history", err))
return
}
resp := gin.H{"messages": result.Messages}
@ -300,13 +300,13 @@ func (h *RoomHandler) GetRoomHistory(c *gin.Context) {
messages, err := h.roomService.GetRoomHistory(c.Request.Context(), conversationID, limitInt, offsetInt)
if err != nil {
if errors.Is(err, services.ErrRoomNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "Conversation not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("Conversation"))
return
}
h.logger.Error("failed to get room history",
zap.Error(err),
zap.String("conversation_id", conversationID.String()))
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get conversation history"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to get conversation history", err))
return
}
@ -446,21 +446,21 @@ func (h *RoomHandler) CreateInvitation(c *gin.Context) {
}
roomID, err := uuid.Parse(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid room ID"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid room ID"))
return
}
resp, err := h.roomService.CreateInvitation(c.Request.Context(), roomID, userID)
if err != nil {
if err.Error() == "forbidden: only owner or admin can create invitations" {
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
RespondWithAppError(c, apperrors.NewForbiddenError(err.Error()))
return
}
if errors.Is(err, services.ErrRoomNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "Conversation not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("Conversation"))
return
}
h.logger.Error("failed to create invitation", zap.Error(err), zap.String("room_id", roomID.String()))
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create invitation"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to create invitation", err))
return
}
RespondSuccess(c, http.StatusCreated, resp)
@ -475,17 +475,17 @@ func (h *RoomHandler) JoinByToken(c *gin.Context) {
}
token, err := uuid.Parse(c.Param("token"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid token"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid token"))
return
}
roomID, err := h.roomService.JoinByToken(c.Request.Context(), token, userID)
if err != nil {
if err.Error() == "invitation not found or expired" {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
RespondWithAppError(c, apperrors.NewNotFoundError(err.Error()))
return
}
h.logger.Error("failed to join via token", zap.Error(err), zap.String("token", token.String()))
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to join"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to join", err))
return
}
RespondSuccess(c, http.StatusOK, gin.H{"room_id": roomID})
@ -500,25 +500,25 @@ func (h *RoomHandler) KickMember(c *gin.Context) {
}
roomID, err := uuid.Parse(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid room ID"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid room ID"))
return
}
targetUserID, err := uuid.Parse(c.Param("userId"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid user ID"))
return
}
if err := h.roomService.KickMember(c.Request.Context(), roomID, targetUserID, requestUserID); err != nil {
if err.Error() == "forbidden: only owner or admin can remove members" {
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
RespondWithAppError(c, apperrors.NewForbiddenError(err.Error()))
return
}
if errors.Is(err, services.ErrRoomNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "Conversation not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("Conversation"))
return
}
h.logger.Error("failed to kick member", zap.Error(err), zap.String("room_id", roomID.String()))
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to remove member"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to remove member", err))
return
}
RespondSuccess(c, http.StatusOK, gin.H{"message": "Member removed successfully"})
@ -533,19 +533,19 @@ func (h *RoomHandler) LeaveRoom(c *gin.Context) {
}
roomID, err := uuid.Parse(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid conversation ID"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid conversation ID"))
return
}
if err := h.roomService.RemoveMember(c.Request.Context(), roomID, userID); err != nil {
if errors.Is(err, services.ErrRoomNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "Conversation not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("Conversation"))
return
}
h.logger.Error("failed to leave room",
zap.Error(err),
zap.String("room_id", roomID.String()),
zap.String("user_id", userID.String()))
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to leave conversation"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to leave conversation", err))
return
}
h.logger.Info("user left room",
@ -563,12 +563,12 @@ func (h *RoomHandler) UpdateMemberRole(c *gin.Context) {
}
roomID, err := uuid.Parse(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid conversation ID"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid conversation ID"))
return
}
targetUserID, err := uuid.Parse(c.Param("userId"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid user ID"))
return
}
var req services.UpdateMemberRoleRequest
@ -581,15 +581,15 @@ func (h *RoomHandler) UpdateMemberRole(c *gin.Context) {
err.Error() == "forbidden: cannot change owner role" ||
err.Error() == "forbidden: owner cannot change their own role" ||
err.Error() == "forbidden: cannot promote to owner via this endpoint" {
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
RespondWithAppError(c, apperrors.NewForbiddenError(err.Error()))
return
}
if errors.Is(err, services.ErrRoomNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "Conversation or member not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("Conversation or member"))
return
}
h.logger.Error("failed to update member role", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update member role"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to update member role", err))
return
}
RespondSuccess(c, http.StatusOK, gin.H{"message": "Member role updated successfully"})
@ -605,17 +605,17 @@ func (h *RoomHandler) GetMembers(c *gin.Context) {
roomIDStr := c.Param("id")
roomID, err := uuid.Parse(roomIDStr)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid room ID"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid room ID"))
return
}
resp, err := h.roomService.GetRoomMembers(c.Request.Context(), roomID, userID)
if err != nil {
if errors.Is(err, services.ErrRoomNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "Conversation not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("Conversation"))
return
}
h.logger.Error("failed to get room members", zap.Error(err), zap.String("room_id", roomID.String()))
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get members"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to get members", err))
return
}
RespondSuccess(c, http.StatusOK, resp)

View file

@ -4,6 +4,7 @@ import (
"errors"
"net/http"
apperrors "veza-backend-api/internal/errors"
"veza-backend-api/internal/core/marketplace"
"veza-backend-api/internal/services"
@ -37,7 +38,7 @@ type ConnectOnboardRequest struct {
// ConnectOnboard starts Stripe Connect onboarding and returns the onboarding URL
func (h *SellHandler) ConnectOnboard(c *gin.Context) {
if h.stripeConnect == nil {
c.JSON(http.StatusServiceUnavailable, gin.H{"error": "Stripe Connect is not enabled"})
RespondWithAppError(c, apperrors.NewServiceUnavailableError("Stripe Connect is not enabled"))
return
}
@ -60,11 +61,11 @@ func (h *SellHandler) ConnectOnboard(c *gin.Context) {
url, err := h.stripeConnect.CreateOnboardingLink(c.Request.Context(), userID, returnURL, refreshURL)
if err != nil {
if errors.Is(err, services.ErrStripeConnectDisabled) {
c.JSON(http.StatusServiceUnavailable, gin.H{"error": "Stripe Connect is not enabled"})
RespondWithAppError(c, apperrors.NewServiceUnavailableError("Stripe Connect is not enabled"))
return
}
h.logger.Error("CreateOnboardingLink failed", zap.Error(err), zap.String("user_id", userID.String()))
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create onboarding link"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to create onboarding link", err))
return
}
@ -74,7 +75,7 @@ func (h *SellHandler) ConnectOnboard(c *gin.Context) {
// ConnectCallback syncs account status after Stripe redirect (called by frontend after return)
func (h *SellHandler) ConnectCallback(c *gin.Context) {
if h.stripeConnect == nil {
c.JSON(http.StatusServiceUnavailable, gin.H{"error": "Stripe Connect is not enabled"})
RespondWithAppError(c, apperrors.NewServiceUnavailableError("Stripe Connect is not enabled"))
return
}
@ -85,15 +86,15 @@ func (h *SellHandler) ConnectCallback(c *gin.Context) {
if err := h.stripeConnect.HandleOnboardingCallback(c.Request.Context(), userID); err != nil {
if errors.Is(err, services.ErrNoStripeAccount) {
c.JSON(http.StatusNotFound, gin.H{"error": "No Stripe account found"})
RespondWithAppError(c, apperrors.NewNotFoundError("Stripe account"))
return
}
if errors.Is(err, services.ErrStripeConnectDisabled) {
c.JSON(http.StatusServiceUnavailable, gin.H{"error": "Stripe Connect is not enabled"})
RespondWithAppError(c, apperrors.NewServiceUnavailableError("Stripe Connect is not enabled"))
return
}
h.logger.Error("HandleOnboardingCallback failed", zap.Error(err), zap.String("user_id", userID.String()))
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to sync account status"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to sync account status", err))
return
}
@ -103,7 +104,7 @@ func (h *SellHandler) ConnectCallback(c *gin.Context) {
// GetBalance returns the seller's Stripe Connect balance
func (h *SellHandler) GetBalance(c *gin.Context) {
if h.stripeConnect == nil {
c.JSON(http.StatusServiceUnavailable, gin.H{"error": "Stripe Connect is not enabled"})
RespondWithAppError(c, apperrors.NewServiceUnavailableError("Stripe Connect is not enabled"))
return
}
@ -115,11 +116,11 @@ func (h *SellHandler) GetBalance(c *gin.Context) {
bal, err := h.stripeConnect.GetBalance(c.Request.Context(), userID)
if err != nil {
if errors.Is(err, services.ErrStripeConnectDisabled) {
c.JSON(http.StatusServiceUnavailable, gin.H{"error": "Stripe Connect is not enabled"})
RespondWithAppError(c, apperrors.NewServiceUnavailableError("Stripe Connect is not enabled"))
return
}
h.logger.Error("GetBalance failed", zap.Error(err), zap.String("user_id", userID.String()))
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get balance"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to get balance", err))
return
}
@ -140,7 +141,7 @@ func (h *SellHandler) GetSellerTransfers(c *gin.Context) {
var transfers []marketplace.SellerTransfer
if err := h.db.Where("seller_id = ?", userID).Order("created_at DESC").Find(&transfers).Error; err != nil {
h.logger.Error("GetSellerTransfers failed", zap.Error(err), zap.String("user_id", userID.String()))
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch transfers"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to fetch transfers", err))
return
}

View file

@ -125,7 +125,7 @@ func (sh *SessionHandler) Logout() gin.HandlerFunc {
// Récupérer l'ID utilisateur depuis le contexte
userIDInterface, exists := c.Get("user_id")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
RespondWithAppError(c, apperrors.NewUnauthorizedError("User not authenticated"))
return
}
@ -137,25 +137,25 @@ func (sh *SessionHandler) Logout() gin.HandlerFunc {
var err error
userID, err = uuid.Parse(v)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user ID format"})
RespondWithAppError(c, apperrors.NewInternalError("Invalid user ID format"))
return
}
default:
c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user ID type"})
RespondWithAppError(c, apperrors.NewInternalError("Invalid user ID type"))
return
}
// Récupérer le token depuis le header Authorization
authHeader := c.GetHeader("Authorization")
if authHeader == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "Authorization header required"})
RespondWithAppError(c, apperrors.NewValidationError("Authorization header required"))
return
}
// Extraire le token
tokenParts := strings.Split(authHeader, " ")
if len(tokenParts) != 2 || tokenParts[0] != "Bearer" {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid Authorization header format"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid Authorization header format"))
return
}
@ -168,7 +168,7 @@ func (sh *SessionHandler) Logout() gin.HandlerFunc {
zap.Error(err),
zap.String("user_id", userID.String()),
)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to logout"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to logout", err))
return
}
@ -189,7 +189,7 @@ func (sh *SessionHandler) LogoutAll() gin.HandlerFunc {
// Récupérer l'ID utilisateur depuis le contexte
userIDInterface, exists := c.Get("user_id")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
RespondWithAppError(c, apperrors.NewUnauthorizedError("User not authenticated"))
return
}
@ -201,11 +201,11 @@ func (sh *SessionHandler) LogoutAll() gin.HandlerFunc {
var err error
userID, err = uuid.Parse(v)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user ID format"})
RespondWithAppError(c, apperrors.NewInternalError("Invalid user ID format"))
return
}
default:
c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user ID type"})
RespondWithAppError(c, apperrors.NewInternalError("Invalid user ID type"))
return
}
@ -216,7 +216,7 @@ func (sh *SessionHandler) LogoutAll() gin.HandlerFunc {
zap.Error(err),
zap.String("user_id", userID.String()),
)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to logout all sessions"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to logout all sessions", err))
return
}
@ -239,7 +239,7 @@ func (sh *SessionHandler) LogoutOthers() gin.HandlerFunc {
return func(c *gin.Context) {
userIDInterface, exists := c.Get("user_id")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
RespondWithAppError(c, apperrors.NewUnauthorizedError("User not authenticated"))
return
}
@ -251,11 +251,11 @@ func (sh *SessionHandler) LogoutOthers() gin.HandlerFunc {
var err error
userID, err = uuid.Parse(v)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user ID format"})
RespondWithAppError(c, apperrors.NewInternalError("Invalid user ID format"))
return
}
default:
c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user ID type"})
RespondWithAppError(c, apperrors.NewInternalError("Invalid user ID type"))
return
}
@ -274,7 +274,7 @@ func (sh *SessionHandler) LogoutOthers() gin.HandlerFunc {
zap.Error(err),
zap.String("user_id", userID.String()),
)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to revoke other sessions"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to revoke other sessions", err))
return
}
@ -291,7 +291,7 @@ func (sh *SessionHandler) GetSessions() gin.HandlerFunc {
// Récupérer l'ID utilisateur depuis le contexte
userIDInterface, exists := c.Get("user_id")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
RespondWithAppError(c, apperrors.NewUnauthorizedError("User not authenticated"))
return
}
@ -303,11 +303,11 @@ func (sh *SessionHandler) GetSessions() gin.HandlerFunc {
var err error
userID, err = uuid.Parse(v)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user ID format"})
RespondWithAppError(c, apperrors.NewInternalError("Invalid user ID format"))
return
}
default:
c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user ID type"})
RespondWithAppError(c, apperrors.NewInternalError("Invalid user ID type"))
return
}
@ -328,7 +328,7 @@ func (sh *SessionHandler) GetSessions() gin.HandlerFunc {
zap.Error(err),
zap.String("user_id", userID.String()),
)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get sessions"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to get sessions", err))
return
}
@ -360,7 +360,7 @@ func (sh *SessionHandler) RevokeSession() gin.HandlerFunc {
// Récupérer l'ID utilisateur depuis le contexte
userIDInterface, exists := c.Get("user_id")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
RespondWithAppError(c, apperrors.NewUnauthorizedError("User not authenticated"))
return
}
@ -372,11 +372,11 @@ func (sh *SessionHandler) RevokeSession() gin.HandlerFunc {
var err error
userID, err = uuid.Parse(v)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user ID format"})
RespondWithAppError(c, apperrors.NewInternalError("Invalid user ID format"))
return
}
default:
c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user ID type"})
RespondWithAppError(c, apperrors.NewInternalError("Invalid user ID type"))
return
}
@ -384,7 +384,7 @@ func (sh *SessionHandler) RevokeSession() gin.HandlerFunc {
sessionIDStr := c.Param("session_id")
sessionID, err := uuid.Parse(sessionIDStr)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid session ID"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid session ID"))
return
}
@ -395,7 +395,7 @@ func (sh *SessionHandler) RevokeSession() gin.HandlerFunc {
zap.Error(err),
zap.String("user_id", userID.String()),
)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get sessions"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to get sessions", err))
return
}
@ -411,7 +411,7 @@ func (sh *SessionHandler) RevokeSession() gin.HandlerFunc {
}
if !sessionFound {
c.JSON(http.StatusNotFound, gin.H{"error": "Session not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("Session"))
return
}
@ -423,7 +423,7 @@ func (sh *SessionHandler) RevokeSession() gin.HandlerFunc {
zap.Error(err),
zap.String("user_id", userID.String()),
)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to revoke session"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to revoke session", err))
return
}
}

View file

@ -5,6 +5,7 @@ import (
"strconv"
"veza-backend-api/internal/core/social"
apperrors "veza-backend-api/internal/errors"
"veza-backend-api/internal/utils"
"github.com/gin-gonic/gin"
@ -69,7 +70,7 @@ func (h *SocialHandler) CreatePost(c *gin.Context) {
post, err := h.service.CreatePost(c.Request.Context(), userID, req.Content, attachments)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create post"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to create post", err))
return
}
@ -102,13 +103,13 @@ func (h *SocialHandler) ToggleLike(c *gin.Context) {
// UUID validation déjà fait par binding tag, mais on garde le parse pour compatibilité
targetID, err := uuid.Parse(req.TargetID)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid target_id format"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid target_id format"))
return
}
liked, err := h.service.ToggleLike(c.Request.Context(), userID, targetID, req.TargetType)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to toggle like"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to toggle like", err))
return
}
@ -142,13 +143,13 @@ func (h *SocialHandler) AddComment(c *gin.Context) {
// UUID validation déjà fait par binding tag, mais on garde le parse pour compatibilité
targetID, err := uuid.Parse(req.TargetID)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid target_id format"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid target_id format"))
return
}
comment, err := h.service.AddComment(c.Request.Context(), userID, targetID, req.TargetType, req.Content)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to add comment"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to add comment", err))
return
}
@ -197,7 +198,7 @@ func (h *SocialHandler) GetFeed(c *gin.Context) {
if useCursorKeyset {
feed, nextCursor, err := h.service.GetGlobalFeedWithCursor(c.Request.Context(), limit, cursor, feedType, userID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get feed"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to get feed", err))
return
}
RespondSuccess(c, http.StatusOK, gin.H{
@ -209,7 +210,7 @@ func (h *SocialHandler) GetFeed(c *gin.Context) {
feed, err := h.service.GetGlobalFeed(c.Request.Context(), limit, offset, feedType, userID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get feed"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to get feed", err))
return
}
nextCursor := ""
@ -234,7 +235,7 @@ func (h *SocialHandler) GetExplore(c *gin.Context) {
}
tags, err := h.service.GetTrendingHashtags(c.Request.Context(), limit)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get explore data"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to get explore data", err))
return
}
// suggested_users: placeholder - can add users not followed with most followers
@ -257,7 +258,7 @@ func (h *SocialHandler) GetTrending(c *gin.Context) {
tags, err := h.service.GetTrendingHashtags(c.Request.Context(), limit)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get trending"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to get trending", err))
return
}
RespondSuccess(c, http.StatusOK, gin.H{"tags": tags})
@ -268,7 +269,7 @@ func (h *SocialHandler) GetPostsByUser(c *gin.Context) {
userIDStr := c.Param("user_id")
userID, err := uuid.Parse(userIDStr)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid user ID"))
return
}
@ -289,7 +290,7 @@ func (h *SocialHandler) GetPostsByUser(c *gin.Context) {
posts, err := h.service.GetPostsByUser(c.Request.Context(), userID, limit, offset)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get posts"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to get posts", err))
return
}

View file

@ -139,20 +139,20 @@ func (h *GroupHandler) JoinGroup(c *gin.Context) {
groupID, err := uuid.Parse(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid group ID"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid group ID"))
return
}
if err := h.service.JoinGroup(c.Request.Context(), userID, groupID); err != nil {
if errors.Is(err, social.ErrGroupNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "Group not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("Group"))
return
}
if errors.Is(err, social.ErrAlreadyMember) {
c.JSON(http.StatusConflict, gin.H{"error": "Already a member"})
RespondWithAppError(c, apperrors.New(apperrors.ErrCodeConflict, "Already a member"))
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to join group"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to join group", err))
return
}
@ -168,24 +168,24 @@ func (h *GroupHandler) LeaveGroup(c *gin.Context) {
groupID, err := uuid.Parse(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid group ID"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid group ID"))
return
}
if err := h.service.LeaveGroup(c.Request.Context(), userID, groupID); err != nil {
if errors.Is(err, social.ErrGroupNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "Group not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("Group"))
return
}
if errors.Is(err, social.ErrNotMember) {
c.JSON(http.StatusBadRequest, gin.H{"error": "Not a member of this group"})
RespondWithAppError(c, apperrors.NewValidationError("Not a member of this group"))
return
}
if errors.Is(err, social.ErrCannotLeaveOwned) {
c.JSON(http.StatusForbidden, gin.H{"error": "Creator cannot leave group"})
RespondWithAppError(c, apperrors.NewForbiddenError("Creator cannot leave group"))
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to leave group"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to leave group", err))
return
}
@ -201,25 +201,25 @@ func (h *GroupHandler) RequestToJoin(c *gin.Context) {
groupID, err := uuid.Parse(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid group ID"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid group ID"))
return
}
req, err := h.service.RequestToJoin(c.Request.Context(), userID, groupID)
if err != nil {
if errors.Is(err, social.ErrGroupNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "Group not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("Group"))
return
}
if errors.Is(err, social.ErrAlreadyMember) {
c.JSON(http.StatusConflict, gin.H{"error": "Already a member"})
RespondWithAppError(c, apperrors.New(apperrors.ErrCodeConflict, "Already a member"))
return
}
if errors.Is(err, social.ErrRequestAlreadyExist) {
c.JSON(http.StatusConflict, gin.H{"error": "Join request already pending"})
RespondWithAppError(c, apperrors.New(apperrors.ErrCodeConflict, "Join request already pending"))
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to request join"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to request join", err))
return
}
@ -239,17 +239,17 @@ func (h *GroupHandler) ListJoinRequests(c *gin.Context) {
groupID, err := uuid.Parse(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid group ID"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid group ID"))
return
}
requests, err := h.service.ListJoinRequests(c.Request.Context(), groupID, userID)
if err != nil {
if errors.Is(err, social.ErrForbidden) {
c.JSON(http.StatusForbidden, gin.H{"error": "Forbidden"})
RespondWithAppError(c, apperrors.NewForbiddenError("Forbidden"))
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to list requests"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to list requests", err))
return
}
@ -265,20 +265,20 @@ func (h *GroupHandler) ApproveJoinRequest(c *gin.Context) {
requestID, err := uuid.Parse(c.Param("request_id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request ID"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid request ID"))
return
}
if err := h.service.ApproveJoinRequest(c.Request.Context(), requestID, userID); err != nil {
if errors.Is(err, social.ErrRequestNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "Request not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("Request"))
return
}
if errors.Is(err, social.ErrForbidden) {
c.JSON(http.StatusForbidden, gin.H{"error": "Forbidden"})
RespondWithAppError(c, apperrors.NewForbiddenError("Forbidden"))
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to approve request"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to approve request", err))
return
}
@ -294,20 +294,20 @@ func (h *GroupHandler) RejectJoinRequest(c *gin.Context) {
requestID, err := uuid.Parse(c.Param("request_id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request ID"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid request ID"))
return
}
if err := h.service.RejectJoinRequest(c.Request.Context(), requestID, userID); err != nil {
if errors.Is(err, social.ErrRequestNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "Request not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("Request"))
return
}
if errors.Is(err, social.ErrForbidden) {
c.JSON(http.StatusForbidden, gin.H{"error": "Forbidden"})
RespondWithAppError(c, apperrors.NewForbiddenError("Forbidden"))
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to reject request"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to reject request", err))
return
}
@ -329,13 +329,13 @@ func (h *GroupHandler) InviteMember(c *gin.Context) {
groupID, err := uuid.Parse(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid group ID"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid group ID"))
return
}
var req InviteMemberRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid request body"))
return
}
@ -346,35 +346,35 @@ func (h *GroupHandler) InviteMember(c *gin.Context) {
inviteeID, err = h.service.ResolveInviteeByEmailOrID(c.Request.Context(), *req.Email)
if err != nil {
if errors.Is(err, social.ErrUserNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("User"))
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to resolve user"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to resolve user", err))
return
}
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": "email or user_id required"})
RespondWithAppError(c, apperrors.NewValidationError("email or user_id required"))
return
}
if err := h.service.InviteMember(c.Request.Context(), userID, groupID, inviteeID); err != nil {
if errors.Is(err, social.ErrForbidden) {
c.JSON(http.StatusForbidden, gin.H{"error": "Forbidden"})
RespondWithAppError(c, apperrors.NewForbiddenError("Forbidden"))
return
}
if errors.Is(err, social.ErrAlreadyMember) {
c.JSON(http.StatusConflict, gin.H{"error": "User is already a member"})
RespondWithAppError(c, apperrors.New(apperrors.ErrCodeConflict, "User is already a member"))
return
}
if errors.Is(err, social.ErrUserNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
RespondWithAppError(c, apperrors.NewNotFoundError("User"))
return
}
if err.Error() == "invitation already pending" {
c.JSON(http.StatusConflict, gin.H{"error": "Invitation already pending"})
RespondWithAppError(c, apperrors.New(apperrors.ErrCodeConflict, "Invitation already pending"))
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to invite member"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to invite member", err))
return
}
@ -395,32 +395,32 @@ func (h *GroupHandler) UpdateMemberRole(c *gin.Context) {
groupID, err := uuid.Parse(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid group ID"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid group ID"))
return
}
targetUserID, err := uuid.Parse(c.Param("user_id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid user ID"))
return
}
var req UpdateMemberRoleRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
RespondWithAppError(c, apperrors.NewValidationError("Invalid request body"))
return
}
if err := h.service.UpdateMemberRole(c.Request.Context(), userID, groupID, targetUserID, req.Role); err != nil {
if errors.Is(err, social.ErrForbidden) {
c.JSON(http.StatusForbidden, gin.H{"error": "Forbidden"})
RespondWithAppError(c, apperrors.NewForbiddenError("Forbidden"))
return
}
if errors.Is(err, social.ErrNotMember) {
c.JSON(http.StatusNotFound, gin.H{"error": "User is not a member"})
RespondWithAppError(c, apperrors.NewNotFoundError("User"))
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update role"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to update role", err))
return
}

View file

@ -6,6 +6,7 @@ import (
"github.com/gin-gonic/gin"
apperrors "veza-backend-api/internal/errors"
"veza-backend-api/internal/services"
)
@ -32,7 +33,7 @@ func (h *TagHandler) Suggest(c *gin.Context) {
suggestions, err := h.tagService.Suggest(c.Request.Context(), q, limit)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get suggestions"})
RespondWithAppError(c, apperrors.NewInternalErrorWrap("failed to get suggestions", err))
return
}