fix&refact: improve code and add field in trash c and trash cdetail

This commit is contained in:
pahmiudahgede 2025-05-06 09:31:13 +07:00
parent 9a2481be09
commit 9f1a4eb8fa
13 changed files with 433 additions and 436 deletions

View File

@ -4,13 +4,15 @@ import "strings"
type RequestTrashCategoryDTO struct { type RequestTrashCategoryDTO struct {
Name string `json:"name"` Name string `json:"name"`
Icon string `json:"icon"`
} }
type ResponseTrashCategoryDTO struct { type ResponseTrashCategoryDTO struct {
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
CreatedAt string `json:"createdAt"` Icon string `json:"icon"`
UpdatedAt string `json:"updatedAt"` CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
Details []ResponseTrashDetailDTO `json:"details,omitempty"` Details []ResponseTrashDetailDTO `json:"details,omitempty"`
} }

View File

@ -1,7 +1,7 @@
package dto package dto
import ( import (
"regexp" "rijig/utils"
"strings" "strings"
) )
@ -18,13 +18,13 @@ type UserResponseDTO struct {
UpdatedAt string `json:"updatedAt"` UpdatedAt string `json:"updatedAt"`
} }
type UpdateUserDTO struct { type RequestUserDTO struct {
Name string `json:"name"` Name string `json:"name"`
Phone string `json:"phone"` Phone string `json:"phone"`
Email string `json:"email"` Email string `json:"email"`
} }
func (r *UpdateUserDTO) Validate() (map[string][]string, bool) { func (r *RequestUserDTO) Validate() (map[string][]string, bool) {
errors := make(map[string][]string) errors := make(map[string][]string)
if strings.TrimSpace(r.Name) == "" { if strings.TrimSpace(r.Name) == "" {
@ -33,10 +33,14 @@ func (r *UpdateUserDTO) Validate() (map[string][]string, bool) {
if strings.TrimSpace(r.Phone) == "" { if strings.TrimSpace(r.Phone) == "" {
errors["phone"] = append(errors["phone"], "Phone number is required") errors["phone"] = append(errors["phone"], "Phone number is required")
} else if !IsValidPhoneNumber(r.Phone) { } else if !utils.IsValidPhoneNumber(r.Phone) {
errors["phone"] = append(errors["phone"], "Invalid phone number format. Use +62 followed by 9-13 digits") errors["phone"] = append(errors["phone"], "Invalid phone number format. Use +62 followed by 9-13 digits")
} }
if strings.TrimSpace(r.Email) != "" && !utils.IsValidEmail(r.Email) {
errors["email"] = append(errors["email"], "Invalid email format")
}
if len(errors) > 0 { if len(errors) > 0 {
return errors, false return errors, false
} }
@ -44,18 +48,6 @@ func (r *UpdateUserDTO) Validate() (map[string][]string, bool) {
return nil, true return nil, true
} }
func IsUpdateValidPhoneNumber(phone string) bool {
re := regexp.MustCompile(`^\+62\d{9,13}$`)
return re.MatchString(phone)
}
func IsUPdateValidEmail(email string) bool {
re := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
return re.MatchString(email)
}
type UpdatePasswordDTO struct { type UpdatePasswordDTO struct {
OldPassword string `json:"old_password"` OldPassword string `json:"old_password"`
NewPassword string `json:"new_password"` NewPassword string `json:"new_password"`
@ -71,8 +63,8 @@ func (u *UpdatePasswordDTO) Validate() (map[string][]string, bool) {
if u.NewPassword == "" { if u.NewPassword == "" {
errors["new_password"] = append(errors["new_password"], "New password is required") errors["new_password"] = append(errors["new_password"], "New password is required")
} else if len(u.NewPassword) < 8 { } else if !utils.IsValidPassword(u.NewPassword) {
errors["new_password"] = append(errors["new_password"], "Password must be at least 8 characters long") errors["new_password"] = append(errors["new_password"], "Password must contain at least one uppercase letter, one digit, and one special character")
} }
if u.ConfirmNewPassword == "" { if u.ConfirmNewPassword == "" {

View File

@ -1,6 +1,7 @@
package handler package handler
import ( import (
"log"
"rijig/dto" "rijig/dto"
"rijig/internal/services" "rijig/internal/services"
"rijig/utils" "rijig/utils"
@ -22,7 +23,13 @@ func (h *TrashHandler) CreateCategory(c *fiber.Ctx) error {
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}}) return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
} }
categoryResponse, err := h.TrashService.CreateCategory(request) iconTrash, err := c.FormFile("icon")
if err != nil {
log.Printf("Error retrieving card photo from request: %v", err)
return utils.ErrorResponse(c, "Card photo is required")
}
categoryResponse, err := h.TrashService.CreateCategory(request, iconTrash)
if err != nil { if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Failed to create category: "+err.Error()) return utils.GenericResponse(c, fiber.StatusInternalServerError, "Failed to create category: "+err.Error())
} }
@ -84,7 +91,13 @@ func (h *TrashHandler) UpdateCategory(c *fiber.Ctx) error {
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid request body"}}) return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid request body"}})
} }
updatedCategory, err := h.TrashService.UpdateCategory(id, request) iconTrash, err := c.FormFile("icon")
if err != nil && err.Error() != "File not found" {
log.Printf("Error retrieving icon trash from request: %v", err)
return utils.ErrorResponse(c, "icon trash is required")
}
updatedCategory, err := h.TrashService.UpdateCategory(id, request, iconTrash)
if err != nil { if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Error updating category: "+err.Error()) return utils.GenericResponse(c, fiber.StatusInternalServerError, "Error updating category: "+err.Error())
} }

View File

@ -4,138 +4,98 @@ import (
"rijig/dto" "rijig/dto"
"rijig/internal/services" "rijig/internal/services"
"rijig/utils" "rijig/utils"
"strconv"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
type UserProfileHandler struct { type UserHandler struct {
UserProfileService services.UserProfileService userService services.UserService
} }
func NewUserProfileHandler(userProfileService services.UserProfileService) *UserProfileHandler { func NewUserHandler(userService services.UserService) *UserHandler {
return &UserProfileHandler{UserProfileService: userProfileService} return &UserHandler{userService: userService}
} }
func (h *UserProfileHandler) GetUserProfile(c *fiber.Ctx) error { func (h *UserHandler) UpdateUserAvatarHandler(c *fiber.Ctx) error {
userID := c.Locals("userID").(string)
avatar, err := c.FormFile("avatar")
if err != nil {
return utils.GenericResponse(c, fiber.StatusBadRequest, "No avatar file provided")
}
updatedUser, err := h.userService.UpdateUserAvatar(userID, avatar)
if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error())
}
return utils.SuccessResponse(c, updatedUser, "Avatar updated successfully")
}
func (h *UserHandler) GetUserByIDHandler(c *fiber.Ctx) error {
// userID := c.Params("id")
userID, ok := c.Locals("userID").(string) userID, ok := c.Locals("userID").(string)
if !ok || userID == "" { if !ok || userID == "" {
return utils.GenericResponse(c, fiber.StatusUnauthorized, "Unauthorized: User session not found") return utils.GenericResponse(c, fiber.StatusUnauthorized, "Unauthorized: User session not found")
} }
userProfile, err := h.UserProfileService.GetUserProfile(userID) user, err := h.userService.GetUserByID(userID)
if err != nil { if err != nil {
return utils.GenericResponse(c, fiber.StatusNotFound, err.Error()) return utils.GenericResponse(c, fiber.StatusNotFound, err.Error())
} }
return utils.SuccessResponse(c, userProfile, "User profile retrieved successfully") return utils.SuccessResponse(c, user, "User retrieved successfully")
} }
func (h *UserProfileHandler) GetUserProfileById(c *fiber.Ctx) error { func (h *UserHandler) GetAllUsersHandler(c *fiber.Ctx) error {
userID := c.Params("userid")
if userID == "" { page := 1
return utils.ValidationErrorResponse(c, map[string][]string{"userid": {"user ID is required"}}) limit := 10
if p := c.Query("page"); p != "" {
page, _ = strconv.Atoi(p)
} }
// userID, ok := c.Locals("userID").(string) if l := c.Query("limit"); l != "" {
// if !ok || userID == "" { limit, _ = strconv.Atoi(l)
// return utils.GenericResponse(c, fiber.StatusUnauthorized, "Unauthorized: User session not found")
// }
userProfile, err := h.UserProfileService.GetUserProfile(userID)
if err != nil {
return utils.GenericResponse(c, fiber.StatusNotFound, err.Error())
} }
return utils.SuccessResponse(c, userProfile, "User profile retrieved successfully") users, err := h.userService.GetAllUsers(page, limit)
}
func (h *UserProfileHandler) GetAllUsers(c *fiber.Ctx) error {
users, err := h.UserProfileService.GetAllUsers()
if err != nil { if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error()) return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error())
} }
return utils.SuccessResponse(c, users, "All users retrieved successfully") return utils.PaginatedResponse(c, users, page, limit, len(users), "Users retrieved successfully")
} }
func (h *UserProfileHandler) GetUsersByRoleID(c *fiber.Ctx) error { func (h *UserHandler) UpdateUserHandler(c *fiber.Ctx) error {
roleID := c.Params("roleid") var request dto.RequestUserDTO
if roleID == "" { if err := c.BodyParser(&request); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{"roleId": {"Role ID is required"}}) return utils.GenericResponse(c, fiber.StatusBadRequest, "Invalid request body")
} }
users, err := h.UserProfileService.GetUsersByRoleID(roleID) userID := c.Locals("userID").(string)
updatedUser, err := h.userService.UpdateUser(userID, &request)
if err != nil { if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error()) return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error())
} }
return utils.SuccessResponse(c, users, "Users retrieved successfully") return utils.SuccessResponse(c, updatedUser, "User profile updated successfully")
} }
func (h *UserProfileHandler) UpdateUserProfile(c *fiber.Ctx) error { func (h *UserHandler) UpdateUserPasswordHandler(c *fiber.Ctx) error {
var updateData dto.UpdateUserDTO var request dto.UpdatePasswordDTO
if err := c.BodyParser(&updateData); err != nil { if err := c.BodyParser(&request); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}}) return utils.GenericResponse(c, fiber.StatusBadRequest, "Invalid request body")
} }
userID, ok := c.Locals("userID").(string) userID := c.Locals("userID").(string)
if !ok || userID == "" { err := h.userService.UpdateUserPassword(userID, request.OldPassword, request.NewPassword, request.ConfirmNewPassword)
return utils.GenericResponse(c, fiber.StatusUnauthorized, "Unauthorized: User session not found")
}
errors, valid := updateData.Validate()
if !valid {
return utils.ValidationErrorResponse(c, errors)
}
userResponse, err := h.UserProfileService.UpdateUserProfile(userID, updateData)
if err != nil {
return utils.GenericResponse(c, fiber.StatusConflict, err.Error())
}
return utils.SuccessResponse(c, userResponse, "User profile updated successfully")
}
// func (h *UserProfileHandler) UpdateUserPassword(c *fiber.Ctx) error {
// var passwordData dto.UpdatePasswordDTO
// if err := c.BodyParser(&passwordData); err != nil {
// return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
// }
// userID, ok := c.Locals("userID").(string)
// if !ok || userID == "" {
// return utils.GenericResponse(c, fiber.StatusUnauthorized, "Unauthorized: User session not found")
// }
// errors, valid := passwordData.Validate()
// if !valid {
// return utils.ValidationErrorResponse(c, errors)
// }
// message, err := h.UserProfileService.UpdateUserPassword(userID, passwordData)
// if err != nil {
// return utils.GenericResponse(c, fiber.StatusBadRequest, err.Error())
// }
// return utils.GenericResponse(c, fiber.StatusOK, message)
// }
func (h *UserProfileHandler) UpdateUserAvatar(c *fiber.Ctx) error {
userID, ok := c.Locals("userID").(string)
if !ok || userID == "" {
return utils.GenericResponse(c, fiber.StatusUnauthorized, "Unauthorized: User session not found")
}
file, err := c.FormFile("avatar")
if err != nil {
return utils.GenericResponse(c, fiber.StatusBadRequest, "No avatar file uploaded")
}
message, err := h.UserProfileService.UpdateUserAvatar(userID, file)
if err != nil { if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error()) return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error())
} }
return utils.GenericResponse(c, fiber.StatusOK, message) return utils.SuccessResponse(c, nil, "Password updated successfully")
} }

View File

@ -1,7 +1,9 @@
package repositories package repositories
import ( import (
"errors"
"fmt" "fmt"
"log"
"rijig/model" "rijig/model"
@ -16,6 +18,7 @@ type TrashRepository interface {
GetTrashDetailByID(id string) (*model.TrashDetail, error) GetTrashDetailByID(id string) (*model.TrashDetail, error)
GetDetailsByCategoryID(categoryID string) ([]model.TrashDetail, error) GetDetailsByCategoryID(categoryID string) ([]model.TrashDetail, error)
UpdateCategoryName(id string, newName string) error UpdateCategoryName(id string, newName string) error
UpdateCategory(id string, updateTrashCategory *model.TrashCategory) (*model.TrashCategory, error)
UpdateTrashDetail(id string, description string, price float64) error UpdateTrashDetail(id string, description string, price float64) error
DeleteCategory(id string) error DeleteCategory(id string) error
DeleteTrashDetail(id string) error DeleteTrashDetail(id string) error
@ -84,6 +87,23 @@ func (r *trashRepository) UpdateCategoryName(id string, newName string) error {
return nil return nil
} }
func (r *trashRepository) UpdateCategory(id string, updateTrashCategory *model.TrashCategory) (*model.TrashCategory, error) {
var existingtrashCtgry model.TrashCategory
if err := r.DB.Where("id = ?", id).First(&existingtrashCtgry).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("trashCategory with ID %s not found", id)
}
log.Printf("Error fetching trash category for update: %v", err)
return nil, fmt.Errorf("error fetching trash category for update: %w", err)
}
if err := r.DB.Save(&existingtrashCtgry).Error; err != nil {
log.Printf("Error updating trash category: %v", err)
return nil, fmt.Errorf("failed to update trash category: %w", err)
}
return &existingtrashCtgry, nil
}
func (r *trashRepository) UpdateTrashDetail(id string, description string, price float64) error { func (r *trashRepository) UpdateTrashDetail(id string, description string, price float64) error {
if err := r.DB.Model(&model.TrashDetail{}).Where("id = ?", id).Updates(model.TrashDetail{Description: description, Price: price}).Error; err != nil { if err := r.DB.Model(&model.TrashDetail{}).Where("id = ?", id).Updates(model.TrashDetail{Description: description, Price: price}).Error; err != nil {
return fmt.Errorf("failed to update trash detail: %v", err) return fmt.Errorf("failed to update trash detail: %v", err)

View File

@ -2,77 +2,76 @@ package repositories
import ( import (
"fmt" "fmt"
"rijig/model" "rijig/model"
"gorm.io/gorm" "gorm.io/gorm"
) )
type UserProfileRepository interface { type UserProfilRepository interface {
FindByID(userID string) (*model.User, error) FindByID(userID string) (*model.User, error)
FindAll(page, limit int) ([]model.User, error)
Update(user *model.User) error Update(user *model.User) error
UpdateAvatar(userID, avatarURL string) error UpdateAvatar(userID, avatarURL string) error
UpdatePassword(userID string, newPassword string) error
FindAll() ([]model.User, error)
FindByRoleID(roleID string) ([]model.User, error)
} }
type userProfileRepository struct { type userProfilRepository struct {
DB *gorm.DB DB *gorm.DB
} }
func NewUserProfileRepository(db *gorm.DB) UserProfileRepository { func NewUserProfilRepository(db *gorm.DB) UserProfilRepository {
return &userProfileRepository{DB: db} return &userProfilRepository{DB: db}
} }
func (r *userProfileRepository) FindByID(userID string) (*model.User, error) { func (r *userProfilRepository) FindByID(userID string) (*model.User, error) {
var user model.User var user model.User
err := r.DB.Preload("Role").Where("id = ?", userID).First(&user).Error err := r.DB.Preload("Role").Where("id = ?", userID).First(&user).Error
if err != nil { if err != nil {
if err == gorm.ErrRecordNotFound { if err == gorm.ErrRecordNotFound {
return nil, fmt.Errorf("user with ID %s not found", userID) return nil, fmt.Errorf("user with ID %s not found", userID)
} }
return nil, err return nil, fmt.Errorf("error finding user with ID %s: %v", userID, err)
} }
if user.Role == nil { if user.Role == nil {
return nil, fmt.Errorf("role not found for this user") return nil, fmt.Errorf("role not found for user ID %s", userID)
} }
return &user, nil return &user, nil
} }
func (r *userProfileRepository) Update(user *model.User) error { func (r *userProfilRepository) FindAll(page, limit int) ([]model.User, error) {
var users []model.User
offset := (page - 1) * limit
err := r.DB.Preload("Role").Offset(offset).Limit(limit).Find(&users).Error
if err != nil {
return nil, fmt.Errorf("error finding all users: %v", err)
}
return users, nil
}
func (r *userProfilRepository) Update(user *model.User) error {
err := r.DB.Save(user).Error err := r.DB.Save(user).Error
if err != nil { if err != nil {
return err return fmt.Errorf("error updating user: %v", err)
} }
return nil return nil
} }
func (r *userProfileRepository) UpdateAvatar(userID, avatarURL string) error { func (r *userProfilRepository) UpdateAvatar(userID, avatarURL string) error {
var user model.User var user model.User
err := r.DB.Model(&user).Where("id = ?", userID).Update("avatar", avatarURL).Error err := r.DB.Model(&user).Where("id = ?", userID).Update("avatar", avatarURL).Error
if err != nil { if err != nil {
return err return fmt.Errorf("error updating avatar for user ID %s: %v", userID, err)
} }
return nil return nil
} }
func (r *userProfileRepository) FindAll() ([]model.User, error) { func (r *userProfilRepository) UpdatePassword(userID string, newPassword string) error {
var users []model.User var user model.User
err := r.DB.Preload("Role").Find(&users).Error err := r.DB.Model(&user).Where("id = ?", userID).Update("password", newPassword).Error
if err != nil { if err != nil {
return nil, err return fmt.Errorf("error updating password for user ID %s: %v", userID, err)
} }
return users, nil return nil
}
func (r *userProfileRepository) FindByRoleID(roleID string) ([]model.User, error) {
var users []model.User
err := r.DB.Preload("Role").Where("role_id = ?", roleID).Find(&users).Error
if err != nil {
return nil, err
}
return users, nil
} }

View File

@ -22,10 +22,10 @@ type IdentityCardService interface {
type identityCardService struct { type identityCardService struct {
identityCardRepo repositories.IdentityCardRepository identityCardRepo repositories.IdentityCardRepository
userRepo repositories.UserProfileRepository userRepo repositories.UserProfilRepository
} }
func NewIdentityCardService(identityCardRepo repositories.IdentityCardRepository, userRepo repositories.UserProfileRepository) IdentityCardService { func NewIdentityCardService(identityCardRepo repositories.IdentityCardRepository, userRepo repositories.UserProfilRepository) IdentityCardService {
return &identityCardService{ return &identityCardService{
identityCardRepo: identityCardRepo, identityCardRepo: identityCardRepo,
userRepo: userRepo, userRepo: userRepo,

View File

@ -2,23 +2,29 @@ package services
import ( import (
"fmt" "fmt"
"log"
"mime/multipart"
"os"
"path/filepath"
"time" "time"
"rijig/dto" "rijig/dto"
"rijig/internal/repositories" "rijig/internal/repositories"
"rijig/model" "rijig/model"
"rijig/utils" "rijig/utils"
"github.com/google/uuid"
) )
type TrashService interface { type TrashService interface {
CreateCategory(request dto.RequestTrashCategoryDTO) (*dto.ResponseTrashCategoryDTO, error) CreateCategory(request dto.RequestTrashCategoryDTO, iconTrash *multipart.FileHeader) (*dto.ResponseTrashCategoryDTO, error)
AddDetailToCategory(request dto.RequestTrashDetailDTO) (*dto.ResponseTrashDetailDTO, error) AddDetailToCategory(request dto.RequestTrashDetailDTO) (*dto.ResponseTrashDetailDTO, error)
GetCategories() ([]dto.ResponseTrashCategoryDTO, error) GetCategories() ([]dto.ResponseTrashCategoryDTO, error)
GetCategoryByID(id string) (*dto.ResponseTrashCategoryDTO, error) GetCategoryByID(id string) (*dto.ResponseTrashCategoryDTO, error)
GetTrashDetailByID(id string) (*dto.ResponseTrashDetailDTO, error) GetTrashDetailByID(id string) (*dto.ResponseTrashDetailDTO, error)
UpdateCategory(id string, request dto.RequestTrashCategoryDTO) (*dto.ResponseTrashCategoryDTO, error) UpdateCategory(id string, request dto.RequestTrashCategoryDTO, iconPath *multipart.FileHeader) (*dto.ResponseTrashCategoryDTO, error)
UpdateDetail(id string, request dto.RequestTrashDetailDTO) (*dto.ResponseTrashDetailDTO, error) UpdateDetail(id string, request dto.RequestTrashDetailDTO) (*dto.ResponseTrashDetailDTO, error)
DeleteCategory(id string) error DeleteCategory(id string) error
@ -33,14 +39,81 @@ func NewTrashService(trashRepo repositories.TrashRepository) TrashService {
return &trashService{TrashRepo: trashRepo} return &trashService{TrashRepo: trashRepo}
} }
func (s *trashService) CreateCategory(request dto.RequestTrashCategoryDTO) (*dto.ResponseTrashCategoryDTO, error) { func (s *trashService) saveIconOfTrash(iconTrash *multipart.FileHeader) (string, error) {
pathImage := "/uploads/icontrash/"
iconTrashDir := "./public" + os.Getenv("BASE_URL") + pathImage
if _, err := os.Stat(iconTrashDir); os.IsNotExist(err) {
if err := os.MkdirAll(iconTrashDir, os.ModePerm); err != nil {
return "", fmt.Errorf("failed to create directory for icon trash: %v", err)
}
}
allowedExtensions := map[string]bool{".jpg": true, ".jpeg": true, ".png": true, ".svg": true}
extension := filepath.Ext(iconTrash.Filename)
if !allowedExtensions[extension] {
return "", fmt.Errorf("invalid file type, only .jpg, .jpeg, and .png are allowed")
}
iconTrashFIleName := fmt.Sprintf("%s_icontrash%s", uuid.New().String(), extension)
iconTrashPath := filepath.Join(iconTrashDir, iconTrashFIleName)
src, err := iconTrash.Open()
if err != nil {
return "", fmt.Errorf("failed to open uploaded file: %v", err)
}
defer src.Close()
dst, err := os.Create(iconTrashPath)
if err != nil {
return "", fmt.Errorf("failed to create icon trash file: %v", err)
}
defer dst.Close()
if _, err := dst.ReadFrom(src); err != nil {
return "", fmt.Errorf("failed to save icon trash: %v", err)
}
iconTrashUrl := fmt.Sprintf("%s%s", pathImage, iconTrashFIleName)
return iconTrashUrl, nil
}
func deleteIconTrashFIle(imagePath string) error {
if imagePath == "" {
return nil
}
baseDir := "./public/" + os.Getenv("BASE_URL")
absolutePath := baseDir + imagePath
if _, err := os.Stat(absolutePath); os.IsNotExist(err) {
return fmt.Errorf("image file not found: %v", err)
}
err := os.Remove(absolutePath)
if err != nil {
return fmt.Errorf("failed to delete image: %v", err)
}
log.Printf("Image deleted successfully: %s", absolutePath)
return nil
}
func (s *trashService) CreateCategory(request dto.RequestTrashCategoryDTO, iconTrash *multipart.FileHeader) (*dto.ResponseTrashCategoryDTO, error) {
errors, valid := request.ValidateTrashCategoryInput() errors, valid := request.ValidateTrashCategoryInput()
if !valid { if !valid {
return nil, fmt.Errorf("validation error: %v", errors) return nil, fmt.Errorf("validation error: %v", errors)
} }
icontrashPath, err := s.saveIconOfTrash(iconTrash)
if err != nil {
return nil, fmt.Errorf("gagal menyimpan ikon sampah: %v", err)
}
category := model.TrashCategory{ category := model.TrashCategory{
Name: request.Name, Name: request.Name,
Icon: icontrashPath,
} }
if err := s.TrashRepo.CreateCategory(&category); err != nil { if err := s.TrashRepo.CreateCategory(&category); err != nil {
@ -53,6 +126,7 @@ func (s *trashService) CreateCategory(request dto.RequestTrashCategoryDTO) (*dto
categoryResponseDTO := &dto.ResponseTrashCategoryDTO{ categoryResponseDTO := &dto.ResponseTrashCategoryDTO{
ID: category.ID, ID: category.ID,
Name: category.Name, Name: category.Name,
Icon: category.Icon,
CreatedAt: createdAt, CreatedAt: createdAt,
UpdatedAt: updatedAt, UpdatedAt: updatedAt,
} }
@ -70,6 +144,7 @@ func (s *trashService) CreateCategory(request dto.RequestTrashCategoryDTO) (*dto
categoriesDTO = append(categoriesDTO, dto.ResponseTrashCategoryDTO{ categoriesDTO = append(categoriesDTO, dto.ResponseTrashCategoryDTO{
ID: c.ID, ID: c.ID,
Name: c.Name, Name: c.Name,
Icon: c.Icon,
CreatedAt: ccreatedAt, CreatedAt: ccreatedAt,
UpdatedAt: cupdatedAt, UpdatedAt: cupdatedAt,
}) })
@ -129,6 +204,7 @@ func (s *trashService) AddDetailToCategory(request dto.RequestTrashDetailDTO) (*
categoryResponseDTO := &dto.ResponseTrashCategoryDTO{ categoryResponseDTO := &dto.ResponseTrashCategoryDTO{
ID: category.ID, ID: category.ID,
Name: category.Name, Name: category.Name,
Icon: category.Icon,
CreatedAt: ccreatedAt, CreatedAt: ccreatedAt,
UpdatedAt: cupdatedAt, UpdatedAt: cupdatedAt,
} }
@ -153,6 +229,7 @@ func (s *trashService) GetCategories() ([]dto.ResponseTrashCategoryDTO, error) {
categoriesDTO = append(categoriesDTO, dto.ResponseTrashCategoryDTO{ categoriesDTO = append(categoriesDTO, dto.ResponseTrashCategoryDTO{
ID: categoryData["id"].(string), ID: categoryData["id"].(string),
Name: categoryData["name"].(string), Name: categoryData["name"].(string),
Icon: categoryData["icon"].(string),
CreatedAt: categoryData["createdAt"].(string), CreatedAt: categoryData["createdAt"].(string),
UpdatedAt: categoryData["updatedAt"].(string), UpdatedAt: categoryData["updatedAt"].(string),
}) })
@ -172,6 +249,7 @@ func (s *trashService) GetCategories() ([]dto.ResponseTrashCategoryDTO, error) {
categoriesDTO = append(categoriesDTO, dto.ResponseTrashCategoryDTO{ categoriesDTO = append(categoriesDTO, dto.ResponseTrashCategoryDTO{
ID: category.ID, ID: category.ID,
Name: category.Name, Name: category.Name,
Icon: category.Icon,
CreatedAt: createdAt, CreatedAt: createdAt,
UpdatedAt: updatedAt, UpdatedAt: updatedAt,
}) })
@ -196,6 +274,7 @@ func (s *trashService) GetCategoryByID(id string) (*dto.ResponseTrashCategoryDTO
return &dto.ResponseTrashCategoryDTO{ return &dto.ResponseTrashCategoryDTO{
ID: categoryData["id"].(string), ID: categoryData["id"].(string),
Name: categoryData["name"].(string), Name: categoryData["name"].(string),
Icon: categoryData["icon"].(string),
CreatedAt: categoryData["createdAt"].(string), CreatedAt: categoryData["createdAt"].(string),
UpdatedAt: categoryData["updatedAt"].(string), UpdatedAt: categoryData["updatedAt"].(string),
Details: details, Details: details,
@ -213,6 +292,7 @@ func (s *trashService) GetCategoryByID(id string) (*dto.ResponseTrashCategoryDTO
categoryDTO := &dto.ResponseTrashCategoryDTO{ categoryDTO := &dto.ResponseTrashCategoryDTO{
ID: category.ID, ID: category.ID,
Name: category.Name, Name: category.Name,
Icon: category.Icon,
CreatedAt: createdAt, CreatedAt: createdAt,
UpdatedAt: updatedAt, UpdatedAt: updatedAt,
} }
@ -220,13 +300,15 @@ func (s *trashService) GetCategoryByID(id string) (*dto.ResponseTrashCategoryDTO
if category.Details != nil { if category.Details != nil {
var detailsDTO []dto.ResponseTrashDetailDTO var detailsDTO []dto.ResponseTrashDetailDTO
for _, detail := range category.Details { for _, detail := range category.Details {
createdAt, _ := utils.FormatDateToIndonesianFormat(detail.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(detail.UpdatedAt)
detailsDTO = append(detailsDTO, dto.ResponseTrashDetailDTO{ detailsDTO = append(detailsDTO, dto.ResponseTrashDetailDTO{
ID: detail.ID, ID: detail.ID,
CategoryID: detail.CategoryID, CategoryID: detail.CategoryID,
Description: detail.Description, Description: detail.Description,
Price: detail.Price, Price: detail.Price,
CreatedAt: detail.CreatedAt.Format("02-01-2006 15:04"), CreatedAt: createdAt,
UpdatedAt: detail.UpdatedAt.Format("02-01-2006 15:04"), UpdatedAt: updatedAt,
}) })
} }
categoryDTO.Details = detailsDTO categoryDTO.Details = detailsDTO
@ -281,27 +363,49 @@ func (s *trashService) GetTrashDetailByID(id string) (*dto.ResponseTrashDetailDT
return detailDTO, nil return detailDTO, nil
} }
func (s *trashService) UpdateCategory(id string, request dto.RequestTrashCategoryDTO) (*dto.ResponseTrashCategoryDTO, error) { func (s *trashService) UpdateCategory(id string, request dto.RequestTrashCategoryDTO, iconPath *multipart.FileHeader) (*dto.ResponseTrashCategoryDTO, error) {
errors, valid := request.ValidateTrashCategoryInput() errors, valid := request.ValidateTrashCategoryInput()
if !valid { if !valid {
return nil, fmt.Errorf("validation error: %v", errors) return nil, fmt.Errorf("validation error: %v", errors)
} }
if err := s.TrashRepo.UpdateCategoryName(id, request.Name); err != nil {
return nil, fmt.Errorf("failed to update category: %v", err)
}
category, err := s.TrashRepo.GetCategoryByID(id) category, err := s.TrashRepo.GetCategoryByID(id)
if err != nil { if err != nil {
return nil, fmt.Errorf("category not found: %v", err) return nil, fmt.Errorf("category not found: %v", err)
} }
if category.Icon != "" {
err := deleteIconTrashFIle(category.Icon)
if err != nil {
return nil, fmt.Errorf("failed to delete old image: %v", err)
}
}
var iconTrashPath string
if iconPath != nil {
iconTrashPath, err = s.saveIconOfTrash(iconPath)
if err != nil {
return nil, fmt.Errorf("failed to save card photo: %v", err)
}
}
if iconTrashPath != "" {
category.Icon = iconTrashPath
}
category, err = s.TrashRepo.UpdateCategory(id, category)
if err != nil {
log.Printf("Error updating trash category: %v", err)
return nil, fmt.Errorf("failed to update category: %v", err)
}
createdAt, _ := utils.FormatDateToIndonesianFormat(category.CreatedAt) createdAt, _ := utils.FormatDateToIndonesianFormat(category.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(category.UpdatedAt) updatedAt, _ := utils.FormatDateToIndonesianFormat(category.UpdatedAt)
categoryResponseDTO := &dto.ResponseTrashCategoryDTO{ categoryResponseDTO := &dto.ResponseTrashCategoryDTO{
ID: category.ID, ID: category.ID,
Name: category.Name, Name: category.Name,
Icon: category.Icon,
CreatedAt: createdAt, CreatedAt: createdAt,
UpdatedAt: updatedAt, UpdatedAt: updatedAt,
} }
@ -319,6 +423,7 @@ func (s *trashService) UpdateCategory(id string, request dto.RequestTrashCategor
categoriesDTO = append(categoriesDTO, dto.ResponseTrashCategoryDTO{ categoriesDTO = append(categoriesDTO, dto.ResponseTrashCategoryDTO{
ID: c.ID, ID: c.ID,
Name: c.Name, Name: c.Name,
Icon: c.Icon,
CreatedAt: ccreatedAt, CreatedAt: ccreatedAt,
UpdatedAt: cupdatedAt, UpdatedAt: cupdatedAt,
}) })
@ -376,6 +481,7 @@ func (s *trashService) UpdateDetail(id string, request dto.RequestTrashDetailDTO
categoryResponseDTO := &dto.ResponseTrashCategoryDTO{ categoryResponseDTO := &dto.ResponseTrashCategoryDTO{
ID: category.ID, ID: category.ID,
Name: category.Name, Name: category.Name,
Icon: category.Icon,
CreatedAt: ccreatedAt, CreatedAt: ccreatedAt,
UpdatedAt: cupdatedAt, UpdatedAt: cupdatedAt,
} }

View File

@ -1,310 +1,193 @@
package services package services
import ( import (
"encoding/json"
"errors"
"fmt" "fmt"
"log" "log"
"mime/multipart" "mime/multipart"
"os" "os"
"path/filepath" "path/filepath"
"time"
"rijig/dto" "rijig/dto"
"rijig/internal/repositories" "rijig/internal/repositories"
"rijig/model" "rijig/model"
"rijig/utils" "rijig/utils"
// "golang.org/x/crypto/bcrypt"
) )
var allowedExtensions = []string{".jpg", ".jpeg", ".png"} type UserService interface {
GetUserByID(userID string) (*dto.UserResponseDTO, error)
type UserProfileService interface { GetAllUsers(page, limit int) ([]dto.UserResponseDTO, error)
GetUserProfile(userID string) (*dto.UserResponseDTO, error) UpdateUser(userID string, request *dto.RequestUserDTO) (*dto.UserResponseDTO, error)
UpdateUserProfile(userID string, updateData dto.UpdateUserDTO) (*dto.UserResponseDTO, error) UpdateUserAvatar(userID string, avatar *multipart.FileHeader) (*dto.UserResponseDTO, error)
// UpdateUserPassword(userID string, passwordData dto.UpdatePasswordDTO) (string, error) UpdateUserPassword(userID, oldPassword, newPassword, confirmNewPassword string) error
UpdateUserAvatar(userID string, file *multipart.FileHeader) (string, error)
GetAllUsers() ([]dto.UserResponseDTO, error)
GetUsersByRoleID(roleID string) ([]dto.UserResponseDTO, error)
} }
type userProfileService struct { type userService struct {
UserRepo repositories.UserRepository userRepo repositories.UserProfilRepository
RoleRepo repositories.RoleRepository
UserProfileRepo repositories.UserProfileRepository
} }
func NewUserProfileService(userProfileRepo repositories.UserProfileRepository) UserProfileService { func NewUserService(userRepo repositories.UserProfilRepository) UserService {
return &userProfileService{UserProfileRepo: userProfileRepo} return &userService{userRepo: userRepo}
} }
func (s *userProfileService) prepareUserResponse(user *model.User) *dto.UserResponseDTO { func (s *userService) GetUserByID(userID string) (*dto.UserResponseDTO, error) {
user, err := s.userRepo.FindByID(userID)
if err != nil {
return nil, fmt.Errorf("error retrieving user by ID: %v", err)
}
userDTO, err := s.formatUserResponse(user)
if err != nil {
return nil, fmt.Errorf("error formatting user response: %v", err)
}
return userDTO, nil
}
func (s *userService) GetAllUsers(page, limit int) ([]dto.UserResponseDTO, error) {
users, err := s.userRepo.FindAll(page, limit)
if err != nil {
return nil, fmt.Errorf("error retrieving all users: %v", err)
}
var userDTOs []dto.UserResponseDTO
for _, user := range users {
userDTO, err := s.formatUserResponse(&user)
if err != nil {
log.Printf("Error formatting user response for userID %s: %v", user.ID, err)
continue
}
userDTOs = append(userDTOs, *userDTO)
}
return userDTOs, nil
}
func (s *userService) UpdateUser(userID string, request *dto.RequestUserDTO) (*dto.UserResponseDTO, error) {
errors, valid := request.Validate()
if !valid {
return nil, fmt.Errorf("validation failed: %v", errors)
}
user, err := s.userRepo.FindByID(userID)
if err != nil {
return nil, fmt.Errorf("user not found: %v", err)
}
user.Name = request.Name
user.Phone = request.Phone
user.Email = request.Email
err = s.userRepo.Update(user)
if err != nil {
return nil, fmt.Errorf("error updating user: %v", err)
}
userDTO, err := s.formatUserResponse(user)
if err != nil {
return nil, fmt.Errorf("error formatting updated user response: %v", err)
}
return userDTO, nil
}
func (s *userService) UpdateUserAvatar(userID string, avatar *multipart.FileHeader) (*dto.UserResponseDTO, error) {
user, err := s.userRepo.FindByID(userID)
if err != nil {
return nil, fmt.Errorf("user not found: %v", err)
}
if *user.Avatar != "" {
err := s.deleteAvatarImage(*user.Avatar)
if err != nil {
return nil, fmt.Errorf("failed to delete old image: %v", err)
}
}
avatarURL, err := s.saveAvatarImage(userID, avatar)
if err != nil {
return nil, fmt.Errorf("failed to save avatar image: %v", err)
}
err = s.userRepo.UpdateAvatar(userID, avatarURL)
if err != nil {
return nil, fmt.Errorf("failed to update avatar in the database: %v", err)
}
userDTO, err := s.formatUserResponse(user)
if err != nil {
return nil, fmt.Errorf("failed to format user response: %v", err)
}
return userDTO, nil
}
func (s *userService) UpdateUserPassword(userID, oldPassword, newPassword, confirmNewPassword string) error {
// errors, valid := utils.ValidatePasswordUpdate(oldPassword, newPassword, confirmNewPassword)
// if !valid {
// return fmt.Errorf("password validation error: %v", errors)
// }
user, err := s.userRepo.FindByID(userID)
if err != nil {
return fmt.Errorf("user not found: %v", err)
}
if user.Password != oldPassword {
return fmt.Errorf("old password is incorrect")
}
err = s.userRepo.UpdatePassword(userID, newPassword)
if err != nil {
return fmt.Errorf("error updating password: %v", err)
}
return nil
}
func (s *userService) formatUserResponse(user *model.User) (*dto.UserResponseDTO, error) {
createdAt, _ := utils.FormatDateToIndonesianFormat(user.CreatedAt) createdAt, _ := utils.FormatDateToIndonesianFormat(user.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(user.UpdatedAt) updatedAt, _ := utils.FormatDateToIndonesianFormat(user.UpdatedAt)
return &dto.UserResponseDTO{ userDTO := &dto.UserResponseDTO{
ID: user.ID, ID: user.ID,
// Username: user.Username, Username: user.Name,
Avatar: user.Avatar, Avatar: user.Avatar,
Name: user.Name, Name: user.Name,
Phone: user.Phone, Phone: user.Phone,
Email: user.Email, Email: user.Email,
// EmailVerified: user.EmailVerified, EmailVerified: user.PhoneVerified,
RoleName: user.Role.RoleName, RoleName: user.Role.RoleName,
CreatedAt: createdAt, CreatedAt: createdAt,
UpdatedAt: updatedAt, UpdatedAt: updatedAt,
} }
return userDTO, nil
} }
func (s *userProfileService) GetUserProfile(userID string) (*dto.UserResponseDTO, error) { func (s *userService) saveAvatarImage(userID string, avatar *multipart.FileHeader) (string, error) {
cacheKey := fmt.Sprintf("userProfile:%s", userID) pathImage := "/uploads/avatars/"
cachedData, err := utils.GetJSONData(cacheKey) avatarDir := "./public" + os.Getenv("BASE_URL") + pathImage
if err == nil && cachedData != nil {
userResponse := &dto.UserResponseDTO{}
if data, ok := cachedData["data"].(string); ok {
if err := json.Unmarshal([]byte(data), userResponse); err != nil {
return nil, err
}
return userResponse, nil
}
}
user, err := s.UserProfileRepo.FindByID(userID)
if err != nil {
return nil, errors.New("user not found")
}
userResponse := s.prepareUserResponse(user)
cacheData := map[string]interface{}{
"data": userResponse,
}
err = utils.SetJSONData(cacheKey, cacheData, time.Hour*24)
if err != nil {
fmt.Printf("Error caching user profile to Redis: %v\n", err)
}
return userResponse, nil
}
func (s *userProfileService) GetAllUsers() ([]dto.UserResponseDTO, error) {
users, err := s.UserProfileRepo.FindAll()
if err != nil {
return nil, err
}
var response []dto.UserResponseDTO
for _, user := range users {
response = append(response, dto.UserResponseDTO{
ID: user.ID,
// Username: user.Username,
Avatar: user.Avatar,
Name: user.Name,
Phone: user.Phone,
// Email: user.Email,
// EmailVerified: user.EmailVerified,
RoleName: user.Role.RoleName,
CreatedAt: user.CreatedAt.Format(time.RFC3339),
UpdatedAt: user.UpdatedAt.Format(time.RFC3339),
})
}
return response, nil
}
func (s *userProfileService) GetUsersByRoleID(roleID string) ([]dto.UserResponseDTO, error) {
users, err := s.UserProfileRepo.FindByRoleID(roleID)
if err != nil {
return nil, err
}
var response []dto.UserResponseDTO
for _, user := range users {
response = append(response, dto.UserResponseDTO{
ID: user.ID,
// Username: user.Username,
Avatar: user.Avatar,
Name: user.Name,
Phone: user.Phone,
// Email: user.Email,
// EmailVerified: user.EmailVerified,
RoleName: user.Role.RoleName,
CreatedAt: user.CreatedAt.Format(time.RFC3339),
UpdatedAt: user.UpdatedAt.Format(time.RFC3339),
})
}
return response, nil
}
func (s *userProfileService) UpdateUserProfile(userID string, updateData dto.UpdateUserDTO) (*dto.UserResponseDTO, error) {
user, err := s.UserProfileRepo.FindByID(userID)
if err != nil {
return nil, errors.New("user not found")
}
validationErrors, valid := updateData.Validate()
if !valid {
return nil, fmt.Errorf("validation failed: %v", validationErrors)
}
if updateData.Name != "" {
user.Name = updateData.Name
}
// if updateData.Phone != "" && updateData.Phone != user.Phone {
// if err := s.updatePhoneIfNeeded(user, updateData.Phone); err != nil {
// return nil, err
// }
// user.Phone = updateData.Phone
// }
// if updateData.Email != "" && updateData.Email != user.Email {
// if err := s.updateEmailIfNeeded(user, updateData.Email); err != nil {
// return nil, err
// }
// user.Email = updateData.Email
// }
err = s.UserProfileRepo.Update(user)
if err != nil {
return nil, fmt.Errorf("failed to update user: %v", err)
}
userResponse := s.prepareUserResponse(user)
cacheKey := fmt.Sprintf("userProfile:%s", userID)
cacheData := map[string]interface{}{
"data": userResponse,
}
err = utils.SetJSONData(cacheKey, cacheData, time.Hour*24)
if err != nil {
fmt.Printf("Error updating cached user profile in Redis: %v\n", err)
}
return userResponse, nil
}
// func (s *userProfileService) updatePhoneIfNeeded(user *model.User, newPhone string) error {
// existingPhone, _ := s.UserRepo.FindByPhoneAndRole(newPhone, user.RoleID)
// if existingPhone != nil {
// return fmt.Errorf("phone number is already used for this role")
// }
// return nil
// }
// func (s *userProfileService) updateEmailIfNeeded(user *model.User, newEmail string) error {
// existingEmail, _ := s.UserRepo.FindByEmailAndRole(newEmail, user.RoleID)
// if existingEmail != nil {
// return fmt.Errorf("email is already used for this role")
// }
// return nil
// }
// func (s *userProfileService) UpdateUserPassword(userID string, passwordData dto.UpdatePasswordDTO) (string, error) {
// validationErrors, valid := passwordData.Validate()
// if !valid {
// return "", fmt.Errorf("validation failed: %v", validationErrors)
// }
// user, err := s.UserProfileRepo.FindByID(userID)
// if err != nil {
// return "", errors.New("user not found")
// }
// if !CheckPasswordHash(passwordData.OldPassword, user.Password) {
// return "", errors.New("old password is incorrect")
// }
// hashedPassword, err := bcrypt.GenerateFromPassword([]byte(passwordData.NewPassword), bcrypt.DefaultCost)
// if err != nil {
// return "", fmt.Errorf("failed to hash new password: %v", err)
// }
// user.Password = string(hashedPassword)
// err = s.UserProfileRepo.Update(user)
// if err != nil {
// return "", fmt.Errorf("failed to update password: %v", err)
// }
// return "Password berhasil diupdate", nil
// }
func (s *userProfileService) UpdateUserAvatar(userID string, file *multipart.FileHeader) (string, error) {
baseURL := os.Getenv("BASE_URL")
if baseURL == "" {
return "", fmt.Errorf("BASE_URL is not set in environment variables")
}
avatarDir := filepath.Join("./public", baseURL, "/uploads/avatars")
if err := ensureAvatarDirectoryExists(avatarDir); err != nil {
return "", err
}
if err := validateAvatarFile(file); err != nil {
return "", err
}
updatedUser, err := s.UserProfileRepo.FindByID(userID)
if err != nil {
return "", fmt.Errorf("failed to retrieve user data: %v", err)
}
if updatedUser.Avatar != nil && *updatedUser.Avatar != "" {
oldAvatarPath := filepath.Join("./public", *updatedUser.Avatar)
if _, err := os.Stat(oldAvatarPath); err == nil {
if err := os.Remove(oldAvatarPath); err != nil {
return "", fmt.Errorf("failed to remove old avatar: %v", err)
}
} else {
log.Printf("Old avatar file not found: %s", oldAvatarPath)
}
}
avatarURL, err := saveAvatarFile(file, userID, avatarDir)
if err != nil {
return "", err
}
err = s.UserProfileRepo.UpdateAvatar(userID, avatarURL)
if err != nil {
return "", fmt.Errorf("failed to update avatar in the database: %v", err)
}
return "Foto profil berhasil diupdate", nil
}
func ensureAvatarDirectoryExists(avatarDir string) error {
if _, err := os.Stat(avatarDir); os.IsNotExist(err) { if _, err := os.Stat(avatarDir); os.IsNotExist(err) {
if err := os.MkdirAll(avatarDir, os.ModePerm); err != nil { if err := os.MkdirAll(avatarDir, os.ModePerm); err != nil {
return fmt.Errorf("failed to create avatar directory: %v", err) return "", fmt.Errorf("failed to create directory for avatar: %v", err)
} }
} }
return nil
}
func validateAvatarFile(file *multipart.FileHeader) error { allowedExtensions := map[string]bool{".jpg": true, ".jpeg": true, ".png": true}
extension := filepath.Ext(file.Filename) extension := filepath.Ext(avatar.Filename)
for _, ext := range allowedExtensions { if !allowedExtensions[extension] {
if extension == ext { return "", fmt.Errorf("invalid file type, only .jpg, .jpeg, and .png are allowed")
return nil
}
} }
return fmt.Errorf("invalid file type, only .jpg, .jpeg, and .png are allowed")
}
func saveAvatarFile(file *multipart.FileHeader, userID, avatarDir string) (string, error) {
extension := filepath.Ext(file.Filename)
avatarFileName := fmt.Sprintf("%s_avatar%s", userID, extension) avatarFileName := fmt.Sprintf("%s_avatar%s", userID, extension)
avatarPath := filepath.Join(avatarDir, avatarFileName) avatarPath := filepath.Join(avatarDir, avatarFileName)
src, err := file.Open() src, err := avatar.Open()
if err != nil { if err != nil {
return "", fmt.Errorf("failed to open uploaded file: %v", err) return "", fmt.Errorf("failed to open uploaded file: %v", err)
} }
@ -312,15 +195,37 @@ func saveAvatarFile(file *multipart.FileHeader, userID, avatarDir string) (strin
dst, err := os.Create(avatarPath) dst, err := os.Create(avatarPath)
if err != nil { if err != nil {
return "", fmt.Errorf("failed to create file: %v", err) return "", fmt.Errorf("failed to create avatar file: %v", err)
} }
defer dst.Close() defer dst.Close()
_, err = dst.ReadFrom(src) if _, err := dst.ReadFrom(src); err != nil {
if err != nil { return "", fmt.Errorf("failed to save avatar: %v", err)
return "", fmt.Errorf("failed to save avatar file: %v", err)
} }
relativePath := filepath.Join("/uploads/avatars", avatarFileName) avatarURL := fmt.Sprintf("%s%s", pathImage, avatarFileName)
return relativePath, nil
return avatarURL, nil
}
func (s *userService) deleteAvatarImage(avatarPath string) error {
if avatarPath == "" {
return nil
}
baseDir := "./public/" + os.Getenv("BASE_URL")
absolutePath := baseDir + avatarPath
if _, err := os.Stat(absolutePath); os.IsNotExist(err) {
return fmt.Errorf("image file not found: %v", err)
}
err := os.Remove(absolutePath)
if err != nil {
return fmt.Errorf("failed to delete avatar image: %v", err)
}
log.Printf("Avatar image deleted successfully: %s", absolutePath)
return nil
} }

View File

@ -3,9 +3,8 @@ package model
import "time" import "time"
type Role struct { type Role struct {
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4();unique;not null" json:"id"` ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4();unique;not null" json:"id"`
RoleName string `gorm:"unique;not null" json:"roleName"` RoleName string `gorm:"unique;not null" json:"roleName"`
// Users []User `gorm:"foreignKey:RoleID" json:"users"`
CreatedAt time.Time `gorm:"default:current_timestamp" json:"createdAt"` CreatedAt time.Time `gorm:"default:current_timestamp" json:"createdAt"`
UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updatedAt"` UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updatedAt"`
} }

View File

@ -5,6 +5,7 @@ import "time"
type TrashCategory struct { type TrashCategory struct {
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"` ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"`
Name string `gorm:"not null" json:"name"` Name string `gorm:"not null" json:"name"`
Icon string `json:"icon,omitempty"`
Details []TrashDetail `gorm:"foreignKey:CategoryID;constraint:OnDelete:CASCADE;" json:"details"` Details []TrashDetail `gorm:"foreignKey:CategoryID;constraint:OnDelete:CASCADE;" json:"details"`
CreatedAt time.Time `gorm:"default:current_timestamp" json:"createdAt"` CreatedAt time.Time `gorm:"default:current_timestamp" json:"createdAt"`
UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updatedAt"` UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updatedAt"`

View File

@ -13,7 +13,7 @@ import (
func IdentityCardRouter(api fiber.Router) { func IdentityCardRouter(api fiber.Router) {
identityCardRepo := repositories.NewIdentityCardRepository(config.DB) identityCardRepo := repositories.NewIdentityCardRepository(config.DB)
userRepo := repositories.NewUserProfileRepository(config.DB) userRepo := repositories.NewUserProfilRepository(config.DB)
identityCardService := services.NewIdentityCardService(identityCardRepo, userRepo) identityCardService := services.NewIdentityCardService(identityCardRepo, userRepo)
identityCardHandler := handler.NewIdentityCardHandler(identityCardService) identityCardHandler := handler.NewIdentityCardHandler(identityCardService)

View File

@ -11,19 +11,19 @@ import (
) )
func UserProfileRouter(api fiber.Router) { func UserProfileRouter(api fiber.Router) {
userProfileRepo := repositories.NewUserProfileRepository(config.DB) userProfileRepo := repositories.NewUserProfilRepository(config.DB)
userProfileService := services.NewUserProfileService(userProfileRepo) userProfileService := services.NewUserService(userProfileRepo)
userProfileHandler := handler.NewUserProfileHandler(userProfileService) userProfileHandler := handler.NewUserHandler(userProfileService)
userProfilRoute := api.Group("/user") userProfilRoute := api.Group("/user")
userProfilRoute.Get("/info", middleware.AuthMiddleware, userProfileHandler.GetUserProfile) userProfilRoute.Get("/info", middleware.AuthMiddleware, userProfileHandler.GetUserByIDHandler)
userProfilRoute.Get("/show-all", middleware.AuthMiddleware, userProfileHandler.GetAllUsers) userProfilRoute.Get("/show-all", middleware.AuthMiddleware, userProfileHandler.GetAllUsersHandler)
userProfilRoute.Get("/:userid", middleware.AuthMiddleware, userProfileHandler.GetUserProfileById) // userProfilRoute.Get("/:userid", middleware.AuthMiddleware, userProfileHandler.GetUserProfileById)
userProfilRoute.Get("/:roleid", middleware.AuthMiddleware, userProfileHandler.GetUsersByRoleID) // userProfilRoute.Get("/:roleid", middleware.AuthMiddleware, userProfileHandler.GetUsersByRoleID)
userProfilRoute.Put("/update-user", middleware.AuthMiddleware, userProfileHandler.UpdateUserProfile) userProfilRoute.Put("/update-user", middleware.AuthMiddleware, userProfileHandler.UpdateUserHandler)
// userProfilRoute.Patch("/update-user-password", middleware.AuthMiddleware, userProfileHandler.UpdateUserPassword) userProfilRoute.Patch("/update-user-password", middleware.AuthMiddleware, userProfileHandler.UpdateUserPasswordHandler)
userProfilRoute.Patch("/upload-photoprofile", middleware.AuthMiddleware, userProfileHandler.UpdateUserAvatar) userProfilRoute.Patch("/upload-photoprofile", middleware.AuthMiddleware, userProfileHandler.UpdateUserAvatarHandler)
} }