mirror of https://github.com/cloudreve/Cloudreve
255 lines
7.3 KiB
Go
255 lines
7.3 KiB
Go
package admin
|
|
|
|
import (
|
|
"context"
|
|
"strconv"
|
|
|
|
"github.com/cloudreve/Cloudreve/v4/application/dependency"
|
|
"github.com/cloudreve/Cloudreve/v4/ent"
|
|
"github.com/cloudreve/Cloudreve/v4/ent/user"
|
|
"github.com/cloudreve/Cloudreve/v4/inventory"
|
|
"github.com/cloudreve/Cloudreve/v4/inventory/types"
|
|
"github.com/cloudreve/Cloudreve/v4/pkg/filemanager/manager"
|
|
"github.com/cloudreve/Cloudreve/v4/pkg/hashid"
|
|
"github.com/cloudreve/Cloudreve/v4/pkg/serializer"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/samber/lo"
|
|
)
|
|
|
|
// AddUserService 用户添加服务
|
|
type AddUserService struct {
|
|
//User model.User `json:"User" binding:"required"`
|
|
Password string `json:"password"`
|
|
}
|
|
|
|
// UserService 用户ID服务
|
|
type UserService struct {
|
|
ID uint `uri:"id" json:"id" binding:"required"`
|
|
}
|
|
|
|
// UserBatchService 用户批量操作服务
|
|
type UserBatchService struct {
|
|
ID []uint `json:"id" binding:"min=1"`
|
|
}
|
|
|
|
const (
|
|
userStatusCondition = "user_status"
|
|
userGroupCondition = "user_group"
|
|
userNickCondition = "user_nick"
|
|
userEmailCondition = "user_email"
|
|
)
|
|
|
|
func (service *AdminListService) Users(c *gin.Context) (*ListUserResponse, error) {
|
|
dep := dependency.FromContext(c)
|
|
hasher := dep.HashIDEncoder()
|
|
userClient := dep.UserClient()
|
|
|
|
ctx := context.WithValue(c, inventory.LoadUserGroup{}, true)
|
|
ctx = context.WithValue(ctx, inventory.LoadUserPasskey{}, true)
|
|
|
|
var (
|
|
err error
|
|
groupID int
|
|
)
|
|
if service.Conditions[userGroupCondition] != "" {
|
|
groupID, err = strconv.Atoi(service.Conditions[userGroupCondition])
|
|
if err != nil {
|
|
return nil, serializer.NewError(serializer.CodeParamErr, "Invalid group ID", err)
|
|
}
|
|
}
|
|
|
|
res, err := userClient.ListUsers(ctx, &inventory.ListUserParameters{
|
|
PaginationArgs: &inventory.PaginationArgs{
|
|
Page: service.Page - 1,
|
|
PageSize: service.PageSize,
|
|
OrderBy: service.OrderBy,
|
|
Order: inventory.OrderDirection(service.OrderDirection),
|
|
},
|
|
Status: user.Status(service.Conditions[userStatusCondition]),
|
|
GroupID: groupID,
|
|
Nick: service.Conditions[userNickCondition],
|
|
Email: service.Conditions[userEmailCondition],
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, serializer.NewError(serializer.CodeDBError, "Failed to list users", err)
|
|
}
|
|
|
|
return &ListUserResponse{
|
|
Pagination: res.PaginationResults,
|
|
Users: lo.Map(res.Users, func(user *ent.User, _ int) GetUserResponse {
|
|
return GetUserResponse{
|
|
User: user,
|
|
HashID: hashid.EncodeUserID(hasher, user.ID),
|
|
TwoFAEnabled: user.TwoFactorSecret != "",
|
|
}
|
|
}),
|
|
}, nil
|
|
}
|
|
|
|
type (
|
|
SingleUserService struct {
|
|
ID int `uri:"id" json:"id" binding:"required"`
|
|
}
|
|
SingleUserParamCtx struct{}
|
|
)
|
|
|
|
func (service *SingleUserService) Get(c *gin.Context) (*GetUserResponse, error) {
|
|
dep := dependency.FromContext(c)
|
|
hasher := dep.HashIDEncoder()
|
|
userClient := dep.UserClient()
|
|
|
|
ctx := context.WithValue(c, inventory.LoadUserGroup{}, true)
|
|
ctx = context.WithValue(ctx, inventory.LoadUserPasskey{}, true)
|
|
|
|
user, err := userClient.GetByID(ctx, service.ID)
|
|
if err != nil {
|
|
return nil, serializer.NewError(serializer.CodeDBError, "Failed to get user", err)
|
|
}
|
|
|
|
m := manager.NewFileManager(dep, user)
|
|
capacity, err := m.Capacity(ctx)
|
|
if err != nil {
|
|
return nil, serializer.NewError(serializer.CodeInternalSetting, "Failed to get user capacity", err)
|
|
}
|
|
|
|
return &GetUserResponse{
|
|
User: user,
|
|
HashID: hashid.EncodeUserID(hasher, user.ID),
|
|
TwoFAEnabled: user.TwoFactorSecret != "",
|
|
Capacity: capacity,
|
|
}, nil
|
|
}
|
|
|
|
func (service *SingleUserService) CalibrateStorage(c *gin.Context) (*GetUserResponse, error) {
|
|
dep := dependency.FromContext(c)
|
|
userClient := dep.UserClient()
|
|
|
|
ctx := context.WithValue(c, inventory.LoadUserGroup{}, true)
|
|
_, err := userClient.CalculateStorage(ctx, service.ID)
|
|
if err != nil {
|
|
return nil, serializer.NewError(serializer.CodeDBError, "Failed to calculate storage", err)
|
|
}
|
|
|
|
subService := &SingleUserService{ID: service.ID}
|
|
return subService.Get(c)
|
|
}
|
|
|
|
type (
|
|
UpsertUserService struct {
|
|
User *ent.User `json:"user" binding:"required"`
|
|
Password string `json:"password"`
|
|
TwoFA string `json:"two_fa"`
|
|
}
|
|
UpsertUserParamCtx struct{}
|
|
)
|
|
|
|
func (s *UpsertUserService) Update(c *gin.Context) (*GetUserResponse, error) {
|
|
dep := dependency.FromContext(c)
|
|
userClient := dep.UserClient()
|
|
|
|
ctx := context.WithValue(c, inventory.LoadUserGroup{}, true)
|
|
existing, err := userClient.GetByID(ctx, s.User.ID)
|
|
if err != nil {
|
|
return nil, serializer.NewError(serializer.CodeDBError, "Failed to get user", err)
|
|
}
|
|
|
|
if s.User.ID == 1 && existing.Edges.Group.Permissions.Enabled(int(types.GroupPermissionIsAdmin)) {
|
|
if s.User.GroupUsers != existing.GroupUsers {
|
|
return nil, serializer.NewError(serializer.CodeInvalidActionOnDefaultUser, "Cannot change default user's group", nil)
|
|
}
|
|
|
|
if s.User.Status != user.StatusActive {
|
|
return nil, serializer.NewError(serializer.CodeInvalidActionOnDefaultUser, "Cannot change default user's status", nil)
|
|
}
|
|
|
|
}
|
|
|
|
if s.Password != "" && len(s.Password) > 128 {
|
|
return nil, serializer.NewError(serializer.CodeParamErr, "Password too long", nil)
|
|
}
|
|
|
|
newUser, err := userClient.Upsert(ctx, s.User, s.Password, s.TwoFA)
|
|
if err != nil {
|
|
return nil, serializer.NewError(serializer.CodeDBError, "Failed to update user", err)
|
|
}
|
|
|
|
service := &SingleUserService{ID: newUser.ID}
|
|
return service.Get(c)
|
|
}
|
|
|
|
func (s *UpsertUserService) Create(c *gin.Context) (*GetUserResponse, error) {
|
|
dep := dependency.FromContext(c)
|
|
userClient := dep.UserClient()
|
|
|
|
if s.Password == "" {
|
|
return nil, serializer.NewError(serializer.CodeParamErr, "Password is required", nil)
|
|
}
|
|
|
|
if s.User.ID != 0 {
|
|
return nil, serializer.NewError(serializer.CodeParamErr, "ID must be 0", nil)
|
|
}
|
|
|
|
user, err := userClient.Upsert(c, s.User, s.Password, s.TwoFA)
|
|
if err != nil {
|
|
return nil, serializer.NewError(serializer.CodeDBError, "Failed to create user", err)
|
|
}
|
|
|
|
service := &SingleUserService{ID: user.ID}
|
|
return service.Get(c)
|
|
|
|
}
|
|
|
|
type (
|
|
BatchUserService struct {
|
|
IDs []int `json:"ids" binding:"min=1"`
|
|
}
|
|
BatchUserParamCtx struct{}
|
|
)
|
|
|
|
func (s *BatchUserService) Delete(c *gin.Context) error {
|
|
dep := dependency.FromContext(c)
|
|
userClient := dep.UserClient()
|
|
fileClient := dep.FileClient()
|
|
|
|
current := inventory.UserFromContext(c)
|
|
ae := serializer.NewAggregateError()
|
|
for _, id := range s.IDs {
|
|
if current.ID == id || id == 1 {
|
|
ae.Add(strconv.Itoa(id), serializer.NewError(serializer.CodeInvalidActionOnDefaultUser, "Cannot delete current user", nil))
|
|
continue
|
|
}
|
|
|
|
fc, tx, ctx, err := inventory.WithTx(c, fileClient)
|
|
if err != nil {
|
|
ae.Add(strconv.Itoa(id), serializer.NewError(serializer.CodeDBError, "Failed to start transaction", err))
|
|
continue
|
|
}
|
|
|
|
uc, _, ctx, err := inventory.WithTx(ctx, userClient)
|
|
if err != nil {
|
|
ae.Add(strconv.Itoa(id), serializer.NewError(serializer.CodeDBError, "Failed to start transaction", err))
|
|
continue
|
|
}
|
|
|
|
if err := fc.DeleteByUser(ctx, id); err != nil {
|
|
_ = inventory.Rollback(tx)
|
|
ae.Add(strconv.Itoa(id), serializer.NewError(serializer.CodeDBError, "Failed to delete user files", err))
|
|
continue
|
|
}
|
|
|
|
if err := uc.Delete(ctx, id); err != nil {
|
|
_ = inventory.Rollback(tx)
|
|
ae.Add(strconv.Itoa(id), serializer.NewError(serializer.CodeDBError, "Failed to delete user", err))
|
|
continue
|
|
}
|
|
|
|
if err := inventory.Commit(tx); err != nil {
|
|
ae.Add(strconv.Itoa(id), serializer.NewError(serializer.CodeDBError, "Failed to commit transaction", err))
|
|
continue
|
|
}
|
|
}
|
|
|
|
return ae.Aggregate()
|
|
}
|