Add comprehensive i18n localization support
- Add go-i18n package for internationalization - Create i18n middleware to extract Accept-Language header - Add translation files for en, es, fr, de, pt languages - Localize all handler error messages and responses - Add language context to all API handlers Supported languages: English, Spanish, French, German, Portuguese 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/treytartt/casera-api/internal/dto/requests"
|
||||
"github.com/treytartt/casera-api/internal/dto/responses"
|
||||
"github.com/treytartt/casera-api/internal/i18n"
|
||||
"github.com/treytartt/casera-api/internal/middleware"
|
||||
"github.com/treytartt/casera-api/internal/services"
|
||||
)
|
||||
@@ -40,7 +41,7 @@ func (h *AuthHandler) Login(c *gin.Context) {
|
||||
var req requests.LoginRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, responses.ErrorResponse{
|
||||
Error: "Invalid request body",
|
||||
Error: i18n.LocalizedMessage(c, "error.invalid_request_body"),
|
||||
Details: map[string]string{
|
||||
"validation": err.Error(),
|
||||
},
|
||||
@@ -51,10 +52,10 @@ func (h *AuthHandler) Login(c *gin.Context) {
|
||||
response, err := h.authService.Login(&req)
|
||||
if err != nil {
|
||||
status := http.StatusUnauthorized
|
||||
message := "Invalid credentials"
|
||||
message := i18n.LocalizedMessage(c, "error.invalid_credentials")
|
||||
|
||||
if errors.Is(err, services.ErrUserInactive) {
|
||||
message = "Account is inactive"
|
||||
message = i18n.LocalizedMessage(c, "error.account_inactive")
|
||||
}
|
||||
|
||||
log.Debug().Err(err).Str("identifier", req.Username).Msg("Login failed")
|
||||
@@ -70,7 +71,7 @@ func (h *AuthHandler) Register(c *gin.Context) {
|
||||
var req requests.RegisterRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, responses.ErrorResponse{
|
||||
Error: "Invalid request body",
|
||||
Error: i18n.LocalizedMessage(c, "error.invalid_request_body"),
|
||||
Details: map[string]string{
|
||||
"validation": err.Error(),
|
||||
},
|
||||
@@ -84,12 +85,12 @@ func (h *AuthHandler) Register(c *gin.Context) {
|
||||
message := err.Error()
|
||||
|
||||
if errors.Is(err, services.ErrUsernameTaken) {
|
||||
message = "Username already taken"
|
||||
message = i18n.LocalizedMessage(c, "error.username_taken")
|
||||
} else if errors.Is(err, services.ErrEmailTaken) {
|
||||
message = "Email already registered"
|
||||
message = i18n.LocalizedMessage(c, "error.email_taken")
|
||||
} else {
|
||||
status = http.StatusInternalServerError
|
||||
message = "Registration failed"
|
||||
message = i18n.LocalizedMessage(c, "error.registration_failed")
|
||||
log.Error().Err(err).Msg("Registration failed")
|
||||
}
|
||||
|
||||
@@ -113,7 +114,7 @@ func (h *AuthHandler) Register(c *gin.Context) {
|
||||
func (h *AuthHandler) Logout(c *gin.Context) {
|
||||
token := middleware.GetAuthToken(c)
|
||||
if token == "" {
|
||||
c.JSON(http.StatusUnauthorized, responses.ErrorResponse{Error: "Not authenticated"})
|
||||
c.JSON(http.StatusUnauthorized, responses.ErrorResponse{Error: i18n.LocalizedMessage(c, "error.not_authenticated")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -129,7 +130,7 @@ func (h *AuthHandler) Logout(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, responses.MessageResponse{Message: "Logged out successfully"})
|
||||
c.JSON(http.StatusOK, responses.MessageResponse{Message: i18n.LocalizedMessage(c, "message.logged_out")})
|
||||
}
|
||||
|
||||
// CurrentUser handles GET /api/auth/me/
|
||||
@@ -142,7 +143,7 @@ func (h *AuthHandler) CurrentUser(c *gin.Context) {
|
||||
response, err := h.authService.GetCurrentUser(user.ID)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Uint("user_id", user.ID).Msg("Failed to get current user")
|
||||
c.JSON(http.StatusInternalServerError, responses.ErrorResponse{Error: "Failed to get user"})
|
||||
c.JSON(http.StatusInternalServerError, responses.ErrorResponse{Error: i18n.LocalizedMessage(c, "error.failed_to_get_user")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -159,7 +160,7 @@ func (h *AuthHandler) UpdateProfile(c *gin.Context) {
|
||||
var req requests.UpdateProfileRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, responses.ErrorResponse{
|
||||
Error: "Invalid request body",
|
||||
Error: i18n.LocalizedMessage(c, "error.invalid_request_body"),
|
||||
Details: map[string]string{
|
||||
"validation": err.Error(),
|
||||
},
|
||||
@@ -170,12 +171,12 @@ func (h *AuthHandler) UpdateProfile(c *gin.Context) {
|
||||
response, err := h.authService.UpdateProfile(user.ID, &req)
|
||||
if err != nil {
|
||||
if errors.Is(err, services.ErrEmailTaken) {
|
||||
c.JSON(http.StatusBadRequest, responses.ErrorResponse{Error: "Email already taken"})
|
||||
c.JSON(http.StatusBadRequest, responses.ErrorResponse{Error: i18n.LocalizedMessage(c, "error.email_already_taken")})
|
||||
return
|
||||
}
|
||||
|
||||
log.Error().Err(err).Uint("user_id", user.ID).Msg("Failed to update profile")
|
||||
c.JSON(http.StatusInternalServerError, responses.ErrorResponse{Error: "Failed to update profile"})
|
||||
c.JSON(http.StatusInternalServerError, responses.ErrorResponse{Error: i18n.LocalizedMessage(c, "error.failed_to_update_profile")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -192,7 +193,7 @@ func (h *AuthHandler) VerifyEmail(c *gin.Context) {
|
||||
var req requests.VerifyEmailRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, responses.ErrorResponse{
|
||||
Error: "Invalid request body",
|
||||
Error: i18n.LocalizedMessage(c, "error.invalid_request_body"),
|
||||
Details: map[string]string{
|
||||
"validation": err.Error(),
|
||||
},
|
||||
@@ -206,14 +207,14 @@ func (h *AuthHandler) VerifyEmail(c *gin.Context) {
|
||||
message := err.Error()
|
||||
|
||||
if errors.Is(err, services.ErrInvalidCode) {
|
||||
message = "Invalid verification code"
|
||||
message = i18n.LocalizedMessage(c, "error.invalid_verification_code")
|
||||
} else if errors.Is(err, services.ErrCodeExpired) {
|
||||
message = "Verification code has expired"
|
||||
message = i18n.LocalizedMessage(c, "error.verification_code_expired")
|
||||
} else if errors.Is(err, services.ErrAlreadyVerified) {
|
||||
message = "Email already verified"
|
||||
message = i18n.LocalizedMessage(c, "error.email_already_verified")
|
||||
} else {
|
||||
status = http.StatusInternalServerError
|
||||
message = "Verification failed"
|
||||
message = i18n.LocalizedMessage(c, "error.verification_failed")
|
||||
log.Error().Err(err).Uint("user_id", user.ID).Msg("Email verification failed")
|
||||
}
|
||||
|
||||
@@ -222,7 +223,7 @@ func (h *AuthHandler) VerifyEmail(c *gin.Context) {
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, responses.VerifyEmailResponse{
|
||||
Message: "Email verified successfully",
|
||||
Message: i18n.LocalizedMessage(c, "message.email_verified"),
|
||||
Verified: true,
|
||||
})
|
||||
}
|
||||
@@ -237,12 +238,12 @@ func (h *AuthHandler) ResendVerification(c *gin.Context) {
|
||||
code, err := h.authService.ResendVerificationCode(user.ID)
|
||||
if err != nil {
|
||||
if errors.Is(err, services.ErrAlreadyVerified) {
|
||||
c.JSON(http.StatusBadRequest, responses.ErrorResponse{Error: "Email already verified"})
|
||||
c.JSON(http.StatusBadRequest, responses.ErrorResponse{Error: i18n.LocalizedMessage(c, "error.email_already_verified")})
|
||||
return
|
||||
}
|
||||
|
||||
log.Error().Err(err).Uint("user_id", user.ID).Msg("Failed to resend verification")
|
||||
c.JSON(http.StatusInternalServerError, responses.ErrorResponse{Error: "Failed to resend verification"})
|
||||
c.JSON(http.StatusInternalServerError, responses.ErrorResponse{Error: i18n.LocalizedMessage(c, "error.failed_to_resend_verification")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -255,7 +256,7 @@ func (h *AuthHandler) ResendVerification(c *gin.Context) {
|
||||
}()
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, responses.MessageResponse{Message: "Verification email sent"})
|
||||
c.JSON(http.StatusOK, responses.MessageResponse{Message: i18n.LocalizedMessage(c, "message.verification_email_sent")})
|
||||
}
|
||||
|
||||
// ForgotPassword handles POST /api/auth/forgot-password/
|
||||
@@ -263,7 +264,7 @@ func (h *AuthHandler) ForgotPassword(c *gin.Context) {
|
||||
var req requests.ForgotPasswordRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, responses.ErrorResponse{
|
||||
Error: "Invalid request body",
|
||||
Error: i18n.LocalizedMessage(c, "error.invalid_request_body"),
|
||||
Details: map[string]string{
|
||||
"validation": err.Error(),
|
||||
},
|
||||
@@ -275,7 +276,7 @@ func (h *AuthHandler) ForgotPassword(c *gin.Context) {
|
||||
if err != nil {
|
||||
if errors.Is(err, services.ErrRateLimitExceeded) {
|
||||
c.JSON(http.StatusTooManyRequests, responses.ErrorResponse{
|
||||
Error: "Too many password reset requests. Please try again later.",
|
||||
Error: i18n.LocalizedMessage(c, "error.rate_limit_exceeded"),
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -295,7 +296,7 @@ func (h *AuthHandler) ForgotPassword(c *gin.Context) {
|
||||
|
||||
// Always return success to prevent email enumeration
|
||||
c.JSON(http.StatusOK, responses.ForgotPasswordResponse{
|
||||
Message: "If an account with that email exists, a password reset code has been sent.",
|
||||
Message: i18n.LocalizedMessage(c, "message.password_reset_email_sent"),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -304,7 +305,7 @@ func (h *AuthHandler) VerifyResetCode(c *gin.Context) {
|
||||
var req requests.VerifyResetCodeRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, responses.ErrorResponse{
|
||||
Error: "Invalid request body",
|
||||
Error: i18n.LocalizedMessage(c, "error.invalid_request_body"),
|
||||
Details: map[string]string{
|
||||
"validation": err.Error(),
|
||||
},
|
||||
@@ -315,13 +316,13 @@ func (h *AuthHandler) VerifyResetCode(c *gin.Context) {
|
||||
resetToken, err := h.authService.VerifyResetCode(req.Email, req.Code)
|
||||
if err != nil {
|
||||
status := http.StatusBadRequest
|
||||
message := "Invalid verification code"
|
||||
message := i18n.LocalizedMessage(c, "error.invalid_verification_code")
|
||||
|
||||
if errors.Is(err, services.ErrCodeExpired) {
|
||||
message = "Verification code has expired"
|
||||
message = i18n.LocalizedMessage(c, "error.verification_code_expired")
|
||||
} else if errors.Is(err, services.ErrRateLimitExceeded) {
|
||||
status = http.StatusTooManyRequests
|
||||
message = "Too many attempts. Please request a new code."
|
||||
message = i18n.LocalizedMessage(c, "error.too_many_attempts")
|
||||
}
|
||||
|
||||
c.JSON(status, responses.ErrorResponse{Error: message})
|
||||
@@ -329,7 +330,7 @@ func (h *AuthHandler) VerifyResetCode(c *gin.Context) {
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, responses.VerifyResetCodeResponse{
|
||||
Message: "Code verified successfully",
|
||||
Message: i18n.LocalizedMessage(c, "message.reset_code_verified"),
|
||||
ResetToken: resetToken,
|
||||
})
|
||||
}
|
||||
@@ -339,7 +340,7 @@ func (h *AuthHandler) ResetPassword(c *gin.Context) {
|
||||
var req requests.ResetPasswordRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, responses.ErrorResponse{
|
||||
Error: "Invalid request body",
|
||||
Error: i18n.LocalizedMessage(c, "error.invalid_request_body"),
|
||||
Details: map[string]string{
|
||||
"validation": err.Error(),
|
||||
},
|
||||
@@ -350,13 +351,13 @@ func (h *AuthHandler) ResetPassword(c *gin.Context) {
|
||||
err := h.authService.ResetPassword(req.ResetToken, req.NewPassword)
|
||||
if err != nil {
|
||||
status := http.StatusBadRequest
|
||||
message := "Invalid or expired reset token"
|
||||
message := i18n.LocalizedMessage(c, "error.invalid_reset_token")
|
||||
|
||||
if errors.Is(err, services.ErrInvalidResetToken) {
|
||||
message = "Invalid or expired reset token"
|
||||
message = i18n.LocalizedMessage(c, "error.invalid_reset_token")
|
||||
} else {
|
||||
status = http.StatusInternalServerError
|
||||
message = "Password reset failed"
|
||||
message = i18n.LocalizedMessage(c, "error.password_reset_failed")
|
||||
log.Error().Err(err).Msg("Password reset failed")
|
||||
}
|
||||
|
||||
@@ -365,7 +366,7 @@ func (h *AuthHandler) ResetPassword(c *gin.Context) {
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, responses.ResetPasswordResponse{
|
||||
Message: "Password reset successfully. Please log in with your new password.",
|
||||
Message: i18n.LocalizedMessage(c, "message.password_reset_success"),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -374,7 +375,7 @@ func (h *AuthHandler) AppleSignIn(c *gin.Context) {
|
||||
var req requests.AppleSignInRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, responses.ErrorResponse{
|
||||
Error: "Invalid request body",
|
||||
Error: i18n.LocalizedMessage(c, "error.invalid_request_body"),
|
||||
Details: map[string]string{
|
||||
"validation": err.Error(),
|
||||
},
|
||||
@@ -385,7 +386,7 @@ func (h *AuthHandler) AppleSignIn(c *gin.Context) {
|
||||
if h.appleAuthService == nil {
|
||||
log.Error().Msg("Apple auth service not configured")
|
||||
c.JSON(http.StatusInternalServerError, responses.ErrorResponse{
|
||||
Error: "Apple Sign In is not configured",
|
||||
Error: i18n.LocalizedMessage(c, "error.apple_signin_not_configured"),
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -393,12 +394,12 @@ func (h *AuthHandler) AppleSignIn(c *gin.Context) {
|
||||
response, err := h.authService.AppleSignIn(c.Request.Context(), h.appleAuthService, &req)
|
||||
if err != nil {
|
||||
status := http.StatusUnauthorized
|
||||
message := "Apple Sign In failed"
|
||||
message := i18n.LocalizedMessage(c, "error.apple_signin_failed")
|
||||
|
||||
if errors.Is(err, services.ErrUserInactive) {
|
||||
message = "Account is inactive"
|
||||
message = i18n.LocalizedMessage(c, "error.account_inactive")
|
||||
} else if errors.Is(err, services.ErrAppleSignInFailed) {
|
||||
message = "Invalid Apple identity token"
|
||||
message = i18n.LocalizedMessage(c, "error.invalid_apple_token")
|
||||
}
|
||||
|
||||
log.Debug().Err(err).Msg("Apple Sign In failed")
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/treytartt/casera-api/internal/dto/requests"
|
||||
"github.com/treytartt/casera-api/internal/i18n"
|
||||
"github.com/treytartt/casera-api/internal/middleware"
|
||||
"github.com/treytartt/casera-api/internal/models"
|
||||
"github.com/treytartt/casera-api/internal/services"
|
||||
@@ -39,7 +40,7 @@ func (h *ContractorHandler) GetContractor(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
contractorID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid contractor ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_contractor_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -47,9 +48,9 @@ func (h *ContractorHandler) GetContractor(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrContractorNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.contractor_not_found")})
|
||||
case errors.Is(err, services.ErrContractorAccessDenied):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.contractor_access_denied")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
@@ -70,7 +71,7 @@ func (h *ContractorHandler) CreateContractor(c *gin.Context) {
|
||||
response, err := h.contractorService.CreateContractor(&req, user.ID)
|
||||
if err != nil {
|
||||
if errors.Is(err, services.ErrResidenceAccessDenied) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.residence_access_denied")})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
@@ -84,7 +85,7 @@ func (h *ContractorHandler) UpdateContractor(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
contractorID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid contractor ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_contractor_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -98,9 +99,9 @@ func (h *ContractorHandler) UpdateContractor(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrContractorNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.contractor_not_found")})
|
||||
case errors.Is(err, services.ErrContractorAccessDenied):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.contractor_access_denied")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
@@ -114,7 +115,7 @@ func (h *ContractorHandler) DeleteContractor(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
contractorID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid contractor ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_contractor_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -122,15 +123,15 @@ func (h *ContractorHandler) DeleteContractor(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrContractorNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.contractor_not_found")})
|
||||
case errors.Is(err, services.ErrContractorAccessDenied):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.contractor_access_denied")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Contractor deleted successfully"})
|
||||
c.JSON(http.StatusOK, gin.H{"message": i18n.LocalizedMessage(c, "message.contractor_deleted")})
|
||||
}
|
||||
|
||||
// ToggleFavorite handles POST /api/contractors/:id/toggle-favorite/
|
||||
@@ -138,7 +139,7 @@ func (h *ContractorHandler) ToggleFavorite(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
contractorID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid contractor ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_contractor_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -146,9 +147,9 @@ func (h *ContractorHandler) ToggleFavorite(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrContractorNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.contractor_not_found")})
|
||||
case errors.Is(err, services.ErrContractorAccessDenied):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.contractor_access_denied")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
@@ -162,7 +163,7 @@ func (h *ContractorHandler) GetContractorTasks(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
contractorID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid contractor ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_contractor_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -170,9 +171,9 @@ func (h *ContractorHandler) GetContractorTasks(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrContractorNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.contractor_not_found")})
|
||||
case errors.Is(err, services.ErrContractorAccessDenied):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.contractor_access_denied")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
@@ -186,14 +187,14 @@ func (h *ContractorHandler) ListContractorsByResidence(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
residenceID, err := strconv.ParseUint(c.Param("residence_id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid residence ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_residence_id")})
|
||||
return
|
||||
}
|
||||
|
||||
response, err := h.contractorService.ListContractorsByResidence(uint(residenceID), user.ID)
|
||||
if err != nil {
|
||||
if errors.Is(err, services.ErrResidenceAccessDenied) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.residence_access_denied")})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/shopspring/decimal"
|
||||
|
||||
"github.com/treytartt/casera-api/internal/dto/requests"
|
||||
"github.com/treytartt/casera-api/internal/i18n"
|
||||
"github.com/treytartt/casera-api/internal/middleware"
|
||||
"github.com/treytartt/casera-api/internal/models"
|
||||
"github.com/treytartt/casera-api/internal/services"
|
||||
@@ -47,7 +48,7 @@ func (h *DocumentHandler) GetDocument(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
documentID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid document ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_document_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -55,9 +56,9 @@ func (h *DocumentHandler) GetDocument(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrDocumentNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.document_not_found")})
|
||||
case errors.Is(err, services.ErrDocumentAccessDenied):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.document_access_denied")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
@@ -89,19 +90,19 @@ func (h *DocumentHandler) CreateDocument(c *gin.Context) {
|
||||
if strings.HasPrefix(contentType, "multipart/form-data") {
|
||||
// Parse multipart form
|
||||
if err := c.Request.ParseMultipartForm(32 << 20); err != nil { // 32MB max
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "failed to parse multipart form: " + err.Error()})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.failed_to_parse_form")})
|
||||
return
|
||||
}
|
||||
|
||||
// Parse residence_id (required)
|
||||
residenceIDStr := c.PostForm("residence_id")
|
||||
if residenceIDStr == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "residence_id is required"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.residence_id_required")})
|
||||
return
|
||||
}
|
||||
residenceID, err := strconv.ParseUint(residenceIDStr, 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid residence_id"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_residence_id")})
|
||||
return
|
||||
}
|
||||
req.ResidenceID = uint(residenceID)
|
||||
@@ -109,7 +110,7 @@ func (h *DocumentHandler) CreateDocument(c *gin.Context) {
|
||||
// Parse title (required)
|
||||
req.Title = c.PostForm("title")
|
||||
if req.Title == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "title is required"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.title_required")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -170,7 +171,7 @@ func (h *DocumentHandler) CreateDocument(c *gin.Context) {
|
||||
if uploadedFile != nil && h.storageService != nil {
|
||||
result, err := h.storageService.Upload(uploadedFile, "documents")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "failed to upload file: " + err.Error()})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.failed_to_upload_file")})
|
||||
return
|
||||
}
|
||||
req.FileURL = result.URL
|
||||
@@ -190,7 +191,7 @@ func (h *DocumentHandler) CreateDocument(c *gin.Context) {
|
||||
response, err := h.documentService.CreateDocument(&req, user.ID)
|
||||
if err != nil {
|
||||
if errors.Is(err, services.ErrResidenceAccessDenied) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.residence_access_denied")})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
@@ -204,7 +205,7 @@ func (h *DocumentHandler) UpdateDocument(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
documentID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid document ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_document_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -218,9 +219,9 @@ func (h *DocumentHandler) UpdateDocument(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrDocumentNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.document_not_found")})
|
||||
case errors.Is(err, services.ErrDocumentAccessDenied):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.document_access_denied")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
@@ -234,7 +235,7 @@ func (h *DocumentHandler) DeleteDocument(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
documentID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid document ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_document_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -242,15 +243,15 @@ func (h *DocumentHandler) DeleteDocument(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrDocumentNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.document_not_found")})
|
||||
case errors.Is(err, services.ErrDocumentAccessDenied):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.document_access_denied")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Document deleted successfully"})
|
||||
c.JSON(http.StatusOK, gin.H{"message": i18n.LocalizedMessage(c, "message.document_deleted")})
|
||||
}
|
||||
|
||||
// ActivateDocument handles POST /api/documents/:id/activate/
|
||||
@@ -258,7 +259,7 @@ func (h *DocumentHandler) ActivateDocument(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
documentID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid document ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_document_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -266,15 +267,15 @@ func (h *DocumentHandler) ActivateDocument(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrDocumentNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.document_not_found")})
|
||||
case errors.Is(err, services.ErrDocumentAccessDenied):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.document_access_denied")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Document activated", "document": response})
|
||||
c.JSON(http.StatusOK, gin.H{"message": i18n.LocalizedMessage(c, "message.document_activated"), "document": response})
|
||||
}
|
||||
|
||||
// DeactivateDocument handles POST /api/documents/:id/deactivate/
|
||||
@@ -282,7 +283,7 @@ func (h *DocumentHandler) DeactivateDocument(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
documentID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid document ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_document_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -290,13 +291,13 @@ func (h *DocumentHandler) DeactivateDocument(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrDocumentNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.document_not_found")})
|
||||
case errors.Is(err, services.ErrDocumentAccessDenied):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.document_access_denied")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Document deactivated", "document": response})
|
||||
c.JSON(http.StatusOK, gin.H{"message": i18n.LocalizedMessage(c, "message.document_deactivated"), "document": response})
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/treytartt/casera-api/internal/i18n"
|
||||
"github.com/treytartt/casera-api/internal/middleware"
|
||||
"github.com/treytartt/casera-api/internal/models"
|
||||
"github.com/treytartt/casera-api/internal/services"
|
||||
@@ -70,21 +71,21 @@ func (h *NotificationHandler) MarkAsRead(c *gin.Context) {
|
||||
|
||||
notificationID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid notification ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_notification_id")})
|
||||
return
|
||||
}
|
||||
|
||||
err = h.notificationService.MarkAsRead(uint(notificationID), user.ID)
|
||||
if err != nil {
|
||||
if errors.Is(err, services.ErrNotificationNotFound) {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.notification_not_found")})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Notification marked as read"})
|
||||
c.JSON(http.StatusOK, gin.H{"message": i18n.LocalizedMessage(c, "message.notification_marked_read")})
|
||||
}
|
||||
|
||||
// MarkAllAsRead handles POST /api/notifications/mark-all-read/
|
||||
@@ -97,7 +98,7 @@ func (h *NotificationHandler) MarkAllAsRead(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "All notifications marked as read"})
|
||||
c.JSON(http.StatusOK, gin.H{"message": i18n.LocalizedMessage(c, "message.all_notifications_marked_read")})
|
||||
}
|
||||
|
||||
// GetPreferences handles GET /api/notifications/preferences/
|
||||
@@ -145,7 +146,7 @@ func (h *NotificationHandler) RegisterDevice(c *gin.Context) {
|
||||
device, err := h.notificationService.RegisterDevice(user.ID, &req)
|
||||
if err != nil {
|
||||
if errors.Is(err, services.ErrInvalidPlatform) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_platform")})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
@@ -174,7 +175,7 @@ func (h *NotificationHandler) DeleteDevice(c *gin.Context) {
|
||||
|
||||
deviceID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid device ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_device_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -186,12 +187,12 @@ func (h *NotificationHandler) DeleteDevice(c *gin.Context) {
|
||||
err = h.notificationService.DeleteDevice(uint(deviceID), platform, user.ID)
|
||||
if err != nil {
|
||||
if errors.Is(err, services.ErrInvalidPlatform) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_platform")})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Device removed"})
|
||||
c.JSON(http.StatusOK, gin.H{"message": i18n.LocalizedMessage(c, "message.device_removed")})
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/treytartt/casera-api/internal/dto/requests"
|
||||
"github.com/treytartt/casera-api/internal/i18n"
|
||||
"github.com/treytartt/casera-api/internal/middleware"
|
||||
"github.com/treytartt/casera-api/internal/models"
|
||||
"github.com/treytartt/casera-api/internal/services"
|
||||
@@ -61,7 +62,7 @@ func (h *ResidenceHandler) GetResidence(c *gin.Context) {
|
||||
|
||||
residenceID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid residence ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_residence_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -69,9 +70,9 @@ func (h *ResidenceHandler) GetResidence(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrResidenceNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.residence_not_found")})
|
||||
case errors.Is(err, services.ErrResidenceAccessDenied):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.residence_access_denied")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
@@ -94,7 +95,7 @@ func (h *ResidenceHandler) CreateResidence(c *gin.Context) {
|
||||
response, err := h.residenceService.CreateResidence(&req, user.ID)
|
||||
if err != nil {
|
||||
if errors.Is(err, services.ErrPropertiesLimitReached) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.properties_limit_reached")})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
@@ -110,7 +111,7 @@ func (h *ResidenceHandler) UpdateResidence(c *gin.Context) {
|
||||
|
||||
residenceID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid residence ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_residence_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -124,9 +125,9 @@ func (h *ResidenceHandler) UpdateResidence(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrResidenceNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.residence_not_found")})
|
||||
case errors.Is(err, services.ErrNotResidenceOwner):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.not_residence_owner")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
@@ -142,7 +143,7 @@ func (h *ResidenceHandler) DeleteResidence(c *gin.Context) {
|
||||
|
||||
residenceID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid residence ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_residence_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -150,16 +151,16 @@ func (h *ResidenceHandler) DeleteResidence(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrResidenceNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.residence_not_found")})
|
||||
case errors.Is(err, services.ErrNotResidenceOwner):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.not_residence_owner")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Residence deleted successfully"})
|
||||
c.JSON(http.StatusOK, gin.H{"message": i18n.LocalizedMessage(c, "message.residence_deleted")})
|
||||
}
|
||||
|
||||
// GenerateShareCode handles POST /api/residences/:id/generate-share-code/
|
||||
@@ -168,7 +169,7 @@ func (h *ResidenceHandler) GenerateShareCode(c *gin.Context) {
|
||||
|
||||
residenceID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid residence ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_residence_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -180,9 +181,9 @@ func (h *ResidenceHandler) GenerateShareCode(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrResidenceNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.residence_not_found")})
|
||||
case errors.Is(err, services.ErrNotResidenceOwner):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.not_residence_owner")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
@@ -206,11 +207,11 @@ func (h *ResidenceHandler) JoinWithCode(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrShareCodeInvalid):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.share_code_invalid")})
|
||||
case errors.Is(err, services.ErrShareCodeExpired):
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.share_code_expired")})
|
||||
case errors.Is(err, services.ErrUserAlreadyMember):
|
||||
c.JSON(http.StatusConflict, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusConflict, gin.H{"error": i18n.LocalizedMessage(c, "error.user_already_member")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
@@ -226,7 +227,7 @@ func (h *ResidenceHandler) GetResidenceUsers(c *gin.Context) {
|
||||
|
||||
residenceID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid residence ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_residence_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -234,9 +235,9 @@ func (h *ResidenceHandler) GetResidenceUsers(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrResidenceNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.residence_not_found")})
|
||||
case errors.Is(err, services.ErrResidenceAccessDenied):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.residence_access_denied")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
@@ -252,13 +253,13 @@ func (h *ResidenceHandler) RemoveResidenceUser(c *gin.Context) {
|
||||
|
||||
residenceID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid residence ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_residence_id")})
|
||||
return
|
||||
}
|
||||
|
||||
userIDToRemove, err := strconv.ParseUint(c.Param("user_id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_user_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -266,18 +267,18 @@ func (h *ResidenceHandler) RemoveResidenceUser(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrResidenceNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.residence_not_found")})
|
||||
case errors.Is(err, services.ErrNotResidenceOwner):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.not_residence_owner")})
|
||||
case errors.Is(err, services.ErrCannotRemoveOwner):
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.cannot_remove_owner")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "User removed from residence"})
|
||||
c.JSON(http.StatusOK, gin.H{"message": i18n.LocalizedMessage(c, "message.user_removed")})
|
||||
}
|
||||
|
||||
// GetResidenceTypes handles GET /api/residences/types/
|
||||
@@ -298,7 +299,7 @@ func (h *ResidenceHandler) GenerateTasksReport(c *gin.Context) {
|
||||
|
||||
residenceID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid residence ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_residence_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -313,9 +314,9 @@ func (h *ResidenceHandler) GenerateTasksReport(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrResidenceNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.residence_not_found")})
|
||||
case errors.Is(err, services.ErrResidenceAccessDenied):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.residence_access_denied")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
@@ -360,11 +361,11 @@ func (h *ResidenceHandler) GenerateTasksReport(c *gin.Context) {
|
||||
}
|
||||
|
||||
// Build response message
|
||||
message := "Tasks report generated successfully"
|
||||
message := i18n.LocalizedMessage(c, "message.tasks_report_generated")
|
||||
if pdfGenerated && emailSent {
|
||||
message = "Tasks report generated and sent to " + recipientEmail
|
||||
message = i18n.LocalizedMessageWithData(c, "message.tasks_report_sent", map[string]interface{}{"Email": recipientEmail})
|
||||
} else if pdfGenerated && !emailSent {
|
||||
message = "Tasks report generated but email could not be sent"
|
||||
message = i18n.LocalizedMessage(c, "message.tasks_report_email_failed")
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/treytartt/casera-api/internal/i18n"
|
||||
"github.com/treytartt/casera-api/internal/services"
|
||||
)
|
||||
|
||||
@@ -34,37 +35,37 @@ func (h *StaticDataHandler) GetStaticData(c *gin.Context) {
|
||||
// Get all lookup data
|
||||
residenceTypes, err := h.residenceService.GetResidenceTypes()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch residence types"})
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": i18n.LocalizedMessage(c, "error.failed_to_fetch_residence_types")})
|
||||
return
|
||||
}
|
||||
|
||||
taskCategories, err := h.taskService.GetCategories()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch task categories"})
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": i18n.LocalizedMessage(c, "error.failed_to_fetch_task_categories")})
|
||||
return
|
||||
}
|
||||
|
||||
taskPriorities, err := h.taskService.GetPriorities()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch task priorities"})
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": i18n.LocalizedMessage(c, "error.failed_to_fetch_task_priorities")})
|
||||
return
|
||||
}
|
||||
|
||||
taskFrequencies, err := h.taskService.GetFrequencies()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch task frequencies"})
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": i18n.LocalizedMessage(c, "error.failed_to_fetch_task_frequencies")})
|
||||
return
|
||||
}
|
||||
|
||||
taskStatuses, err := h.taskService.GetStatuses()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch task statuses"})
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": i18n.LocalizedMessage(c, "error.failed_to_fetch_task_statuses")})
|
||||
return
|
||||
}
|
||||
|
||||
contractorSpecialties, err := h.contractorService.GetSpecialties()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch contractor specialties"})
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": i18n.LocalizedMessage(c, "error.failed_to_fetch_contractor_specialties")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -83,7 +84,7 @@ func (h *StaticDataHandler) GetStaticData(c *gin.Context) {
|
||||
// Kept for API compatibility with mobile clients
|
||||
func (h *StaticDataHandler) RefreshStaticData(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "Static data refreshed",
|
||||
"message": i18n.LocalizedMessage(c, "message.static_data_refreshed"),
|
||||
"status": "success",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/treytartt/casera-api/internal/i18n"
|
||||
"github.com/treytartt/casera-api/internal/middleware"
|
||||
"github.com/treytartt/casera-api/internal/models"
|
||||
"github.com/treytartt/casera-api/internal/services"
|
||||
@@ -54,7 +55,7 @@ func (h *SubscriptionHandler) GetUpgradeTrigger(c *gin.Context) {
|
||||
trigger, err := h.subscriptionService.GetUpgradeTrigger(key)
|
||||
if err != nil {
|
||||
if errors.Is(err, services.ErrUpgradeTriggerNotFound) {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.upgrade_trigger_not_found")})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
@@ -115,13 +116,13 @@ func (h *SubscriptionHandler) ProcessPurchase(c *gin.Context) {
|
||||
switch req.Platform {
|
||||
case "ios":
|
||||
if req.ReceiptData == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "receipt_data is required for iOS"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.receipt_data_required")})
|
||||
return
|
||||
}
|
||||
subscription, err = h.subscriptionService.ProcessApplePurchase(user.ID, req.ReceiptData)
|
||||
case "android":
|
||||
if req.PurchaseToken == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "purchase_token is required for Android"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.purchase_token_required")})
|
||||
return
|
||||
}
|
||||
subscription, err = h.subscriptionService.ProcessGooglePurchase(user.ID, req.PurchaseToken)
|
||||
@@ -133,7 +134,7 @@ func (h *SubscriptionHandler) ProcessPurchase(c *gin.Context) {
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "Subscription upgraded successfully",
|
||||
"message": i18n.LocalizedMessage(c, "message.subscription_upgraded"),
|
||||
"subscription": subscription,
|
||||
})
|
||||
}
|
||||
@@ -149,7 +150,7 @@ func (h *SubscriptionHandler) CancelSubscription(c *gin.Context) {
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "Subscription cancelled. You will retain Pro benefits until the end of your billing period.",
|
||||
"message": i18n.LocalizedMessage(c, "message.subscription_cancelled"),
|
||||
"subscription": subscription,
|
||||
})
|
||||
}
|
||||
@@ -181,7 +182,7 @@ func (h *SubscriptionHandler) RestoreSubscription(c *gin.Context) {
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "Subscription restored successfully",
|
||||
"message": i18n.LocalizedMessage(c, "message.subscription_restored"),
|
||||
"subscription": subscription,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/shopspring/decimal"
|
||||
|
||||
"github.com/treytartt/casera-api/internal/dto/requests"
|
||||
"github.com/treytartt/casera-api/internal/i18n"
|
||||
"github.com/treytartt/casera-api/internal/middleware"
|
||||
"github.com/treytartt/casera-api/internal/models"
|
||||
"github.com/treytartt/casera-api/internal/services"
|
||||
@@ -47,7 +48,7 @@ func (h *TaskHandler) GetTask(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
taskID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid task ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_task_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -55,9 +56,9 @@ func (h *TaskHandler) GetTask(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrTaskNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.task_not_found")})
|
||||
case errors.Is(err, services.ErrTaskAccessDenied):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.task_access_denied")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
@@ -71,7 +72,7 @@ func (h *TaskHandler) GetTasksByResidence(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
residenceID, err := strconv.ParseUint(c.Param("residence_id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid residence ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_residence_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -86,7 +87,7 @@ func (h *TaskHandler) GetTasksByResidence(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrResidenceAccessDenied):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.residence_access_denied")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
@@ -107,7 +108,7 @@ func (h *TaskHandler) CreateTask(c *gin.Context) {
|
||||
response, err := h.taskService.CreateTask(&req, user.ID)
|
||||
if err != nil {
|
||||
if errors.Is(err, services.ErrResidenceAccessDenied) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.residence_access_denied")})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
@@ -121,7 +122,7 @@ func (h *TaskHandler) UpdateTask(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
taskID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid task ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_task_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -135,9 +136,9 @@ func (h *TaskHandler) UpdateTask(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrTaskNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.task_not_found")})
|
||||
case errors.Is(err, services.ErrTaskAccessDenied):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.task_access_denied")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
@@ -151,7 +152,7 @@ func (h *TaskHandler) DeleteTask(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
taskID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid task ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_task_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -159,15 +160,15 @@ func (h *TaskHandler) DeleteTask(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrTaskNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.task_not_found")})
|
||||
case errors.Is(err, services.ErrTaskAccessDenied):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.task_access_denied")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Task deleted successfully"})
|
||||
c.JSON(http.StatusOK, gin.H{"message": i18n.LocalizedMessage(c, "message.task_deleted")})
|
||||
}
|
||||
|
||||
// MarkInProgress handles POST /api/tasks/:id/mark-in-progress/
|
||||
@@ -175,7 +176,7 @@ func (h *TaskHandler) MarkInProgress(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
taskID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid task ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_task_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -183,15 +184,15 @@ func (h *TaskHandler) MarkInProgress(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrTaskNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.task_not_found")})
|
||||
case errors.Is(err, services.ErrTaskAccessDenied):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.task_access_denied")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Task marked as in progress", "task": response})
|
||||
c.JSON(http.StatusOK, gin.H{"message": i18n.LocalizedMessage(c, "message.task_in_progress"), "task": response})
|
||||
}
|
||||
|
||||
// CancelTask handles POST /api/tasks/:id/cancel/
|
||||
@@ -199,7 +200,7 @@ func (h *TaskHandler) CancelTask(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
taskID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid task ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_task_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -207,17 +208,17 @@ func (h *TaskHandler) CancelTask(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrTaskNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.task_not_found")})
|
||||
case errors.Is(err, services.ErrTaskAccessDenied):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.task_access_denied")})
|
||||
case errors.Is(err, services.ErrTaskAlreadyCancelled):
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.task_already_cancelled")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Task cancelled", "task": response})
|
||||
c.JSON(http.StatusOK, gin.H{"message": i18n.LocalizedMessage(c, "message.task_cancelled"), "task": response})
|
||||
}
|
||||
|
||||
// UncancelTask handles POST /api/tasks/:id/uncancel/
|
||||
@@ -225,7 +226,7 @@ func (h *TaskHandler) UncancelTask(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
taskID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid task ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_task_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -233,15 +234,15 @@ func (h *TaskHandler) UncancelTask(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrTaskNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.task_not_found")})
|
||||
case errors.Is(err, services.ErrTaskAccessDenied):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.task_access_denied")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Task uncancelled", "task": response})
|
||||
c.JSON(http.StatusOK, gin.H{"message": i18n.LocalizedMessage(c, "message.task_uncancelled"), "task": response})
|
||||
}
|
||||
|
||||
// ArchiveTask handles POST /api/tasks/:id/archive/
|
||||
@@ -249,7 +250,7 @@ func (h *TaskHandler) ArchiveTask(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
taskID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid task ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_task_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -257,17 +258,17 @@ func (h *TaskHandler) ArchiveTask(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrTaskNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.task_not_found")})
|
||||
case errors.Is(err, services.ErrTaskAccessDenied):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.task_access_denied")})
|
||||
case errors.Is(err, services.ErrTaskAlreadyArchived):
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.task_already_archived")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Task archived", "task": response})
|
||||
c.JSON(http.StatusOK, gin.H{"message": i18n.LocalizedMessage(c, "message.task_archived"), "task": response})
|
||||
}
|
||||
|
||||
// UnarchiveTask handles POST /api/tasks/:id/unarchive/
|
||||
@@ -275,7 +276,7 @@ func (h *TaskHandler) UnarchiveTask(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
taskID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid task ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_task_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -283,15 +284,15 @@ func (h *TaskHandler) UnarchiveTask(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrTaskNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.task_not_found")})
|
||||
case errors.Is(err, services.ErrTaskAccessDenied):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.task_access_denied")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Task unarchived", "task": response})
|
||||
c.JSON(http.StatusOK, gin.H{"message": i18n.LocalizedMessage(c, "message.task_unarchived"), "task": response})
|
||||
}
|
||||
|
||||
// === Task Completions ===
|
||||
@@ -301,7 +302,7 @@ func (h *TaskHandler) GetTaskCompletions(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
taskID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid task ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_task_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -309,9 +310,9 @@ func (h *TaskHandler) GetTaskCompletions(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrTaskNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.task_not_found")})
|
||||
case errors.Is(err, services.ErrTaskAccessDenied):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.task_access_denied")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
@@ -336,7 +337,7 @@ func (h *TaskHandler) GetCompletion(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
completionID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid completion ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_completion_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -344,9 +345,9 @@ func (h *TaskHandler) GetCompletion(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrCompletionNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.completion_not_found")})
|
||||
case errors.Is(err, services.ErrTaskAccessDenied):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.task_access_denied")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
@@ -367,19 +368,19 @@ func (h *TaskHandler) CreateCompletion(c *gin.Context) {
|
||||
if strings.HasPrefix(contentType, "multipart/form-data") {
|
||||
// Parse multipart form
|
||||
if err := c.Request.ParseMultipartForm(32 << 20); err != nil { // 32MB max
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "failed to parse multipart form: " + err.Error()})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.failed_to_parse_form")})
|
||||
return
|
||||
}
|
||||
|
||||
// Parse task_id (required)
|
||||
taskIDStr := c.PostForm("task_id")
|
||||
if taskIDStr == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "task_id is required"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.task_id_required")})
|
||||
return
|
||||
}
|
||||
taskID, err := strconv.ParseUint(taskIDStr, 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid task_id"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_task_id_value")})
|
||||
return
|
||||
}
|
||||
req.TaskID = uint(taskID)
|
||||
@@ -416,7 +417,7 @@ func (h *TaskHandler) CreateCompletion(c *gin.Context) {
|
||||
if h.storageService != nil {
|
||||
result, err := h.storageService.Upload(file, "completions")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "failed to upload image: " + err.Error()})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.failed_to_upload_image")})
|
||||
return
|
||||
}
|
||||
req.ImageURLs = append(req.ImageURLs, result.URL)
|
||||
@@ -434,9 +435,9 @@ func (h *TaskHandler) CreateCompletion(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrTaskNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.task_not_found")})
|
||||
case errors.Is(err, services.ErrTaskAccessDenied):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.task_access_denied")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
@@ -450,7 +451,7 @@ func (h *TaskHandler) DeleteCompletion(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
completionID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid completion ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_completion_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -458,15 +459,15 @@ func (h *TaskHandler) DeleteCompletion(c *gin.Context) {
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrCompletionNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.completion_not_found")})
|
||||
case errors.Is(err, services.ErrTaskAccessDenied):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.task_access_denied")})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Completion deleted successfully"})
|
||||
c.JSON(http.StatusOK, gin.H{"message": i18n.LocalizedMessage(c, "message.completion_deleted")})
|
||||
}
|
||||
|
||||
// === Lookups ===
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/treytartt/casera-api/internal/i18n"
|
||||
"github.com/treytartt/casera-api/internal/services"
|
||||
)
|
||||
|
||||
@@ -23,7 +24,7 @@ func NewUploadHandler(storageService *services.StorageService) *UploadHandler {
|
||||
func (h *UploadHandler) UploadImage(c *gin.Context) {
|
||||
file, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "No file provided"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.no_file_provided")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -44,7 +45,7 @@ func (h *UploadHandler) UploadImage(c *gin.Context) {
|
||||
func (h *UploadHandler) UploadDocument(c *gin.Context) {
|
||||
file, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "No file provided"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.no_file_provided")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -62,7 +63,7 @@ func (h *UploadHandler) UploadDocument(c *gin.Context) {
|
||||
func (h *UploadHandler) UploadCompletion(c *gin.Context) {
|
||||
file, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "No file provided"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.no_file_provided")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -92,5 +93,5 @@ func (h *UploadHandler) DeleteFile(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "File deleted successfully"})
|
||||
c.JSON(http.StatusOK, gin.H{"message": i18n.LocalizedMessage(c, "message.file_deleted")})
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/treytartt/casera-api/internal/i18n"
|
||||
"github.com/treytartt/casera-api/internal/middleware"
|
||||
"github.com/treytartt/casera-api/internal/models"
|
||||
"github.com/treytartt/casera-api/internal/services"
|
||||
@@ -46,7 +47,7 @@ func (h *UserHandler) GetUser(c *gin.Context) {
|
||||
|
||||
userID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_user_id")})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -54,7 +55,7 @@ func (h *UserHandler) GetUser(c *gin.Context) {
|
||||
targetUser, err := h.userService.GetUserIfSharedResidence(uint(userID), user.ID)
|
||||
if err != nil {
|
||||
if err == services.ErrUserNotFound {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": i18n.LocalizedMessage(c, "error.user_not_found")})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
|
||||
Reference in New Issue
Block a user