package repo import ( "errors" "fmt" "strings" "trustcontact/internal/models" "gorm.io/gorm" ) type UserRepo struct { db *gorm.DB } type UserListParams struct { Query string Sort string Dir string Page int PageSize int } func NewUserRepo(db *gorm.DB) *UserRepo { return &UserRepo{db: db} } func (r *UserRepo) FindByID(id string) (*models.User, error) { var user models.User if err := r.db.Preload("Properties").Where("id = ?", id).First(&user).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, nil } return nil, err } return &user, nil } func (r *UserRepo) FindByEmail(email string) (*models.User, error) { var user models.User if err := r.db.Preload("Properties").Where("email = ?", email).First(&user).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, nil } return nil, err } return &user, nil } func (r *UserRepo) Create(user *models.User) error { return r.db.Create(user).Error } func (r *UserRepo) SetEmailVerified(userID string, verified bool) error { return r.db.Model(&models.User{}). Where("id = ?", userID). Update("email_verified", verified).Error } func (r *UserRepo) UpdatePasswordHash(userID string, passwordHash string) error { return r.db.Model(&models.User{}). Where("id = ?", userID). Update("password_hash", passwordHash).Error } func (r *UserRepo) UpsertLanguagePreference(userID string, lang string) error { var existing models.UserProperties err := r.db.Where("user_id = ?", userID).First(&existing).Error if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { return err } if errors.Is(err, gorm.ErrRecordNotFound) { props := models.UserProperties{ UserId: userID, Lang: lang, } return r.db.Create(&props).Error } return r.db.Model(&models.UserProperties{}). Where("user_id = ?", userID). Update("lang", lang).Error } func (r *UserRepo) UpsertDarkPreference(userID string, dark bool) error { var existing models.UserProperties err := r.db.Where("user_id = ?", userID).First(&existing).Error if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { return err } if errors.Is(err, gorm.ErrRecordNotFound) { props := models.UserProperties{ UserId: userID, Dark: dark, } return r.db.Create(&props).Error } return r.db.Model(&models.UserProperties{}). Where("user_id = ?", userID). Update("dark", dark).Error } func (r *UserRepo) List(params UserListParams) ([]models.User, int64, error) { query := r.db.Model(&models.User{}) search := strings.TrimSpace(params.Query) if search != "" { like := "%" + strings.ToLower(search) + "%" query = query.Where("LOWER(name) LIKE ? OR LOWER(email) LIKE ?", like, like) } var total int64 if err := query.Count(&total).Error; err != nil { return nil, 0, err } orderBy := sanitizeSort(params.Sort) orderDir := sanitizeDir(params.Dir) orderClause := fmt.Sprintf("%s %s", orderBy, orderDir) page := params.Page if page < 1 { page = 1 } pageSize := params.PageSize if pageSize <= 0 { pageSize = 10 } if pageSize > 500 { pageSize = 500 } offset := (page - 1) * pageSize var users []models.User if err := query.Order(orderClause).Limit(pageSize).Offset(offset).Find(&users).Error; err != nil { return nil, 0, err } return users, total, nil } func sanitizeSort(sort string) string { switch strings.ToLower(strings.TrimSpace(sort)) { case "id": return "id" case "name": return "name" case "email": return "email" default: return "id" } } func sanitizeDir(dir string) string { switch strings.ToLower(strings.TrimSpace(dir)) { case "desc": return "desc" default: return "asc" } }