Files
warehouse-management-BE/internal/services/profile_service.go

124 lines
3.7 KiB
Go

package services
import (
"context"
"net/http"
"time"
"wm-backend/global"
"wm-backend/internal/models/responses"
"wm-backend/internal/repositories"
redisRepo "wm-backend/internal/repositories/redis"
"wm-backend/response"
"github.com/gin-gonic/gin"
"github.com/rs/zerolog/log"
)
// GetProfile returns the authenticated user's profile including roles and permissions.
// It first attempts to read from Redis cache; on miss it queries the database.
//
// @Summary Get current user profile
// @Description Returns user info with roles and permissions (requires auth)
// @Tags auth
// @Security BearerAuth
// @Produce json
// @Success 200 {object} response.SuccessResponse{data=responses.BodyProfileResponse}
// @Failure 401 {object} response.ErrorResponse
// @Router /profile [get]
func GetProfile(c *gin.Context) error {
userID := c.GetString("user_id")
ctx := c.Request.Context()
var roles []responses.RoleItem
var permissions []string
// 1. Try Redis cache first (graceful fallback on error)
cachedData, found := redisRepo.GetCachedUserPermissions(ctx, userID)
if found {
// Cache hit - refresh TTL
redisRepo.RefreshTTL(ctx, userID, 60*time.Minute)
roles = make([]responses.RoleItem, 0, len(cachedData.Roles))
for _, r := range cachedData.Roles {
roles = append(roles, responses.RoleItem{
ID: r.ID,
Name: r.Name,
Description: r.Description,
})
}
permissions = cachedData.Permissions
} else {
// 2. Cache miss - fetch from DB
dbRoles, err := repositories.GetUserRolesByUserID(ctx, global.Queries, userID)
if err != nil {
log.Error().Err(err).Str("userID", userID).Msg("Failed to fetch roles from DB")
response.InternalServerError(c, http.StatusInternalServerError)
return nil
}
roles = make([]responses.RoleItem, 0, len(dbRoles))
for _, r := range dbRoles {
roles = append(roles, responses.RoleItem{
ID: r.ID,
Name: r.Name,
Description: r.Description,
})
}
dbPerms, err := repositories.GetPermissionsByUserID(ctx, global.Queries, userID)
if err != nil {
log.Error().Err(err).Str("userID", userID).Msg("Failed to fetch permissions from DB")
response.InternalServerError(c, http.StatusInternalServerError)
return nil
}
permissions = make([]string, 0, len(dbPerms))
for _, p := range dbPerms {
permissions = append(permissions, p.Name)
}
// 3. Save to Redis cache for future requests (use background context)
cacheRoles := make([]redisRepo.RoleCacheItem, 0, len(dbRoles))
for _, r := range dbRoles {
cacheRoles = append(cacheRoles, redisRepo.RoleCacheItem{
ID: r.ID,
Name: r.Name,
Description: r.Description,
})
}
go func() {
bgCtx := context.Background()
if err := redisRepo.CacheUserPermissions(bgCtx, userID, redisRepo.RBACCachedData{
Roles: cacheRoles,
Permissions: permissions,
}, 60*time.Minute); err != nil {
log.Error().Err(err).Str("userID", userID).Msg("Failed to cache RBAC data in profile")
}
}()
}
// 4. Fetch user info from DB
user, err := repositories.GetUserByID(ctx, global.Queries, userID)
if err != nil {
log.Error().Err(err).Str("userID", userID).Msg("Failed to fetch user from DB")
response.InternalServerError(c, http.StatusInternalServerError)
return nil
}
if user == nil {
response.NotFoundError(c, http.StatusNotFound, "User not found")
return nil
}
// 5. Return response
response.Ok(c, "Profile fetched", responses.BodyProfileResponse{
Info: responses.UserInfoResponse{
ID: user.ID,
Username: user.Username,
Email: user.Email,
FullName: user.FullName,
IsActive: user.IsActive,
},
Roles: roles,
Permissions: permissions,
})
return nil
}