go-quasar-partial-ssr/backend/internal/user/repository.go

339 lines
9.2 KiB
Go

package users
import (
"encoding/base64"
"errors"
"server/internal/db"
"server/internal/systemUtils"
"time"
"github.com/gofiber/fiber/v3"
"github.com/google/uuid"
"gorm.io/gorm"
)
func repositoryDB() (*gorm.DB, error) {
database, err := db.GetDB()
if err != nil {
return nil, fiber.NewError(fiber.StatusInternalServerError, "database unavailable")
}
return database, nil
}
func CreateUser(createUserRequest UserCreateRequest) (*User, error) {
database, err := repositoryDB()
if err != nil {
return nil, err
}
var existing User
if err := database.Where("email = ?", createUserRequest.Email).First(&existing).Error; err == nil {
return nil, fiber.NewError(fiber.StatusConflict, "user already exists")
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fiber.NewError(fiber.StatusInternalServerError, "failed to check user")
}
hashedPassword, err := systemUtils.HashPassword(createUserRequest.Password)
if err != nil {
return nil, fiber.NewError(fiber.StatusInternalServerError, "failed to secure password")
}
now := time.Now().UTC()
user := User{
Email: createUserRequest.Email,
Name: createUserRequest.Name,
Password: hashedPassword,
Permission: createUserRequest.Permission,
Status: func() UserStatus {
if createUserRequest.Status == "" {
return UserStatusPending
}
return createUserRequest.Status
}(),
Type: func() UserType {
if createUserRequest.Type == "" {
return UserType("internal")
}
return createUserRequest.Type
}(),
Avatar: func() *string {
if createUserRequest.Avatar == nil {
return nil
}
return createUserRequest.Avatar
}(),
ID: uuid.NewString(),
Details: func() *UserDetails {
if createUserRequest.Details == nil {
return nil
}
return createUserRequest.Details
}(),
Preferences: func() *UserPreferences {
if createUserRequest.Preferences == nil {
return nil
}
return createUserRequest.Preferences
}(),
CreatedAt: &now,
UpdatedAt: &now,
}
if err := database.Create(&user).Error; err != nil {
return nil, fiber.NewError(fiber.StatusInternalServerError, "failed to create user")
}
return GetUserByID(user.ID)
}
func UpdateUser(updateUserRequest UpdateUserRequest) (*User, error) {
database, err := repositoryDB()
if err != nil {
return nil, err
}
user, err := GetUserByID(updateUserRequest.ID)
if err != nil {
return nil, err
}
now := time.Now().UTC()
user.Name = updateUserRequest.Name
user.Email = updateUserRequest.Email
user.UpdatedAt = &now
user.Permission = updateUserRequest.Permission
if updateUserRequest.Status != "" {
user.Status = updateUserRequest.Status
}
if updateUserRequest.Type != "" {
user.Type = updateUserRequest.Type
}
user.Permission = updateUserRequest.Permission
if err := database.Transaction(func(tx *gorm.DB) error {
if err := tx.Save(user).Error; err != nil {
return err
}
return nil
}); err != nil {
return nil, fiber.NewError(fiber.StatusInternalServerError, "failed to update user")
}
return GetUserByID(user.ID)
}
func UpdateUserDetails(updateUserDetails UpdateUserDetailsRequest) error {
database, err := repositoryDB()
if err != nil {
return err
}
if updateUserDetails.UserID == "" {
return fiber.NewError(fiber.StatusBadRequest, "invalid user id")
}
input := &UserDetails{
UserID: updateUserDetails.UserID,
Title: updateUserDetails.Title,
FirstName: updateUserDetails.FirstName,
LastName: updateUserDetails.LastName,
Address: updateUserDetails.Address,
City: updateUserDetails.City,
ZipCode: updateUserDetails.ZipCode,
Country: updateUserDetails.Country,
Phone: updateUserDetails.Phone,
}
if err := database.Transaction(func(tx *gorm.DB) error {
return syncUserDetails(tx, updateUserDetails.UserID, input)
}); err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "failed to update user details")
}
return nil
}
func UpdateUserPreferences(updateUserPreferences UpdateUserPreferencesRequest) error {
database, err := repositoryDB()
if err != nil {
return err
}
if updateUserPreferences.UserID == "" {
return fiber.NewError(fiber.StatusBadRequest, "invalid user id")
}
input := &UserPreferences{
UserID: updateUserPreferences.UserID,
UseIdle: updateUserPreferences.UseIdle,
IdleTimeout: updateUserPreferences.IdleTimeout,
UseIdlePassword: updateUserPreferences.UseIdlePassword,
IdlePin: updateUserPreferences.IdlePin,
UseDirectLogin: updateUserPreferences.UseDirectLogin,
UseQuadcodeLogin: updateUserPreferences.UseQuadcodeLogin,
SendNoticesMail: updateUserPreferences.SendNoticesMail,
Language: updateUserPreferences.Language,
}
if err := database.Transaction(func(tx *gorm.DB) error {
return syncUserPreferences(tx, updateUserPreferences.UserID, input)
}); err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "failed to update user preferences")
}
return nil
}
func UpdateUserAvatar(updateUserAvatarRequest UpdateUserAvatarRequest) (*User, error) {
database, err := repositoryDB()
if err != nil {
return nil, err
}
user, err := GetUserByID(updateUserAvatarRequest.ID)
if err != nil {
return nil, err
}
avatar := base64.StdEncoding.EncodeToString(updateUserAvatarRequest.Img)
now := time.Now().UTC()
if err := database.Model(user).Updates(map[string]any{
"avatar": avatar,
"updated_at": now,
}).Error; err != nil {
return nil, fiber.NewError(fiber.StatusInternalServerError, "failed to update user avatar")
}
return GetUserByID(user.ID)
}
func UpdateUserPassword(req UpdatePasswordRequest, id string) error {
database, err := repositoryDB()
if err != nil {
return err
}
user := User{}
if err := database.Where("uuid = ?", id).First(&user).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return fiber.NewError(fiber.StatusNotFound, "user not found")
}
return fiber.NewError(fiber.StatusInternalServerError, "failed to load user")
}
hashedPassword, err := systemUtils.HashPassword(req.Password)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "failed to secure password")
}
now := time.Now().UTC()
if err := database.Model(&user).Updates(map[string]any{
"password": hashedPassword,
"updated_at": now,
}).Error; err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "failed to update password")
}
if err := database.Where("user_id = ?", user.ID).Delete(&Session{}).Error; err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "failed to revoke sessions")
}
return nil
}
func GetUserByID(id string) (*User, error) {
if id == "" {
return nil, fiber.NewError(fiber.StatusBadRequest, "invalid user id")
}
database, err := repositoryDB()
if err != nil {
return nil, err
}
var user User
if err := database.Preload("Details").Preload("Preferences").First(&user, "id = ?", id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fiber.NewError(fiber.StatusNotFound, "user not found")
}
return nil, fiber.NewError(fiber.StatusInternalServerError, "failed to load user")
}
return &user, nil
}
func DeleteUser(uuid string) error {
database, err := repositoryDB()
if err != nil {
return err
}
user, err := GetUserByID(uuid)
if err != nil {
return err
}
if err := database.Transaction(func(tx *gorm.DB) error {
if err := tx.Where("user_id = ?", user.ID).Delete(&UserDetails{}).Error; err != nil {
return err
}
if err := tx.Where("user_id = ?", user.ID).Delete(&UserPreferences{}).Error; err != nil {
return err
}
return tx.Delete(user).Error
}); err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "failed to delete user")
}
return nil
}
func syncUserDetails(tx *gorm.DB, userID string, input *UserDetails) error {
if input == nil {
return tx.Where("user_id = ?", userID).Delete(&UserDetails{}).Error
}
var details UserDetails
if err := tx.Where("user_id = ?", userID).First(&details).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
details = UserDetails{UserID: userID}
} else {
return err
}
}
details.Title = input.Title
details.FirstName = input.FirstName
details.LastName = input.LastName
details.Address = input.Address
details.City = input.City
details.ZipCode = input.ZipCode
details.Country = input.Country
details.Phone = input.Phone
if details.ID == 0 {
return tx.Create(&details).Error
}
return tx.Save(&details).Error
}
func syncUserPreferences(tx *gorm.DB, userID string, input *UserPreferences) error {
if input == nil {
return tx.Where("user_id = ?", userID).Delete(&UserPreferences{}).Error
}
var preferences UserPreferences
if err := tx.Where("user_id = ?", userID).First(&preferences).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
preferences = UserPreferences{UserID: userID}
} else {
return err
}
}
preferences.UseIdle = input.UseIdle
preferences.IdleTimeout = input.IdleTimeout
preferences.UseIdlePassword = input.UseIdlePassword
preferences.IdlePin = input.IdlePin
preferences.UseDirectLogin = input.UseDirectLogin
preferences.UseQuadcodeLogin = input.UseQuadcodeLogin
preferences.SendNoticesMail = input.SendNoticesMail
preferences.Language = input.Language
if preferences.ID == 0 {
return tx.Create(&preferences).Error
}
return tx.Save(&preferences).Error
}