179 lines
5.0 KiB
Go
179 lines
5.0 KiB
Go
package gormstore
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"git.itzana.me/StrafesNET/dev-service/pkg/datastore"
|
|
"git.itzana.me/StrafesNET/dev-service/pkg/model"
|
|
"gorm.io/gorm"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
// CreateUser creates a new User in the database.
|
|
func (g *Gormstore) CreateUser(ctx context.Context, user *model.User) error {
|
|
if err := g.db.WithContext(ctx).Create(user).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
return g.loadUserWithAssociations(ctx, user.ID, user)
|
|
}
|
|
|
|
// GetUser retrieves a single User by ID.
|
|
func (g *Gormstore) GetUser(ctx context.Context, id uint64) (*model.User, error) {
|
|
var user model.User
|
|
if err := g.loadUserWithAssociations(ctx, id, &user); err != nil {
|
|
return nil, err
|
|
}
|
|
return &user, nil
|
|
}
|
|
|
|
// GetAllUsers retrieves users from the database using cursor-based pagination.
|
|
func (g *Gormstore) GetAllUsers(ctx context.Context, pagination *datastore.CursorPagination) ([]model.User, error) {
|
|
// Validate and set default limit
|
|
if pagination.Limit < 1 {
|
|
pagination.Limit = datastore.DefaultPageLimit
|
|
}
|
|
|
|
// Fetch one extra record to determine if there are more results
|
|
limit := pagination.Limit + 1
|
|
|
|
query := g.db.WithContext(ctx).
|
|
Preload("RateLimit").
|
|
Preload("Permissions", func(db *gorm.DB) *gorm.DB {
|
|
return db.Order("permissions.id asc")
|
|
})
|
|
|
|
// Apply cursor filter if provided
|
|
if pagination.Cursor != "" {
|
|
cursorID, err := decodeCursor(pagination.Cursor)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
query = query.Where("id > ?", cursorID)
|
|
}
|
|
|
|
var users []model.User
|
|
if err := query.Order("id ASC").Limit(limit).Find(&users).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Handle pagination
|
|
pagination.HasMore = len(users) > pagination.Limit
|
|
if pagination.HasMore && len(users) > 0 {
|
|
pagination.NextCursor = encodeCursor(users[pagination.Limit-1].ID)
|
|
users = users[:pagination.Limit]
|
|
} else {
|
|
pagination.NextCursor = ""
|
|
}
|
|
|
|
// Add default permissions to each user
|
|
for i := range users {
|
|
if err := g.addDefaultPermissionsToUser(ctx, &users[i]); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return users, nil
|
|
}
|
|
|
|
// SearchUsers searches users by ID (exact) or username (case-insensitive partial match).
|
|
func (g *Gormstore) SearchUsers(ctx context.Context, query string) ([]model.User, error) {
|
|
q := g.db.WithContext(ctx).
|
|
Preload("RateLimit").
|
|
Preload("Permissions", func(db *gorm.DB) *gorm.DB {
|
|
return db.Order("permissions.id asc")
|
|
})
|
|
|
|
if id, err := strconv.ParseUint(query, 10, 64); err == nil {
|
|
q = q.Where("id = ?", id)
|
|
} else {
|
|
q = q.Where("username ILIKE ?", "%"+query+"%")
|
|
}
|
|
|
|
var users []model.User
|
|
if err := q.Order("id ASC").Find(&users).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for i := range users {
|
|
if err := g.addDefaultPermissionsToUser(ctx, &users[i]); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return users, nil
|
|
}
|
|
|
|
// UpdateUser updates an existing User in the database.
|
|
func (g *Gormstore) UpdateUser(ctx context.Context, user *model.User) error {
|
|
user.UpdatedAt = time.Now()
|
|
return g.db.WithContext(ctx).Save(user).Error
|
|
}
|
|
|
|
// DeleteUser deletes a User by ID from the database.
|
|
func (g *Gormstore) DeleteUser(ctx context.Context, id uint64) error {
|
|
result := g.db.WithContext(ctx).Delete(&model.User{}, id)
|
|
if result.RowsAffected == 0 {
|
|
return datastore.ErrNotExists
|
|
}
|
|
return result.Error
|
|
}
|
|
|
|
// AddPermissionToUser associates a Permission with a User by their IDs.
|
|
func (g *Gormstore) AddPermissionToUser(ctx context.Context, userID uint64, permissionID uint32) error {
|
|
return g.db.WithContext(ctx).Model(&model.User{ID: userID}).
|
|
Association("Permissions").
|
|
Append(&model.Permission{ID: permissionID})
|
|
}
|
|
|
|
// RemovePermissionFromUser removes the association between a Permission and a User by their IDs.
|
|
func (g *Gormstore) RemovePermissionFromUser(ctx context.Context, userID uint64, permissionID uint32) error {
|
|
return g.db.WithContext(ctx).Model(&model.User{ID: userID}).
|
|
Association("Permissions").
|
|
Delete(&model.Permission{ID: permissionID})
|
|
}
|
|
|
|
// loadUserWithAssociations loads a user with all associated data and adds default permissions.
|
|
func (g *Gormstore) loadUserWithAssociations(ctx context.Context, userID uint64, user *model.User) error {
|
|
result := g.db.WithContext(ctx).
|
|
Preload("RateLimit").
|
|
Preload("Permissions", func(db *gorm.DB) *gorm.DB {
|
|
return db.Order("permissions.id asc")
|
|
}).
|
|
First(user, userID)
|
|
|
|
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
|
return datastore.ErrNotExists
|
|
}
|
|
|
|
if result.Error != nil {
|
|
return result.Error
|
|
}
|
|
|
|
return g.addDefaultPermissionsToUser(ctx, user)
|
|
}
|
|
|
|
// addDefaultPermissionsToUser adds any missing default permissions to a user.
|
|
func (g *Gormstore) addDefaultPermissionsToUser(ctx context.Context, user *model.User) error {
|
|
defaultPerms, err := g.GetDefaultPermissions(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Create a map of existing permission IDs for efficient lookup
|
|
existingPermIDs := make(map[uint32]bool)
|
|
for _, p := range user.Permissions {
|
|
existingPermIDs[p.ID] = true
|
|
}
|
|
|
|
// Add any missing default permissions
|
|
for _, dp := range defaultPerms {
|
|
if !existingPermIDs[dp.ID] {
|
|
user.Permissions = append(user.Permissions, dp)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|