122 lines
3.7 KiB
Go
122 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{
|
|
ID: user.ID,
|
|
Username: user.Username,
|
|
Email: user.Email,
|
|
FullName: user.FullName,
|
|
IsActive: user.IsActive,
|
|
Roles: roles,
|
|
Permissions: permissions,
|
|
})
|
|
return nil
|
|
}
|