修改用户
This commit is contained in:
parent
b2eb6ee28e
commit
4cf7c8aca0
@ -1,4 +1,4 @@
|
|||||||
import type { tokenData, ApiUser, ApiServerInfo, YggProfile, ApiConfig, List, UserInfo } from '@/apis/model'
|
import type { tokenData, ApiUser, ApiServerInfo, YggProfile, ApiConfig, List, UserInfo, EditUser } from '@/apis/model'
|
||||||
import { apiGet } from '@/apis/utils'
|
import { apiGet } from '@/apis/utils'
|
||||||
|
|
||||||
export async function login(username: string, password: string) {
|
export async function login(username: string, password: string) {
|
||||||
@ -117,3 +117,14 @@ export async function ListUser(page: number, token: string, email: string, name:
|
|||||||
})
|
})
|
||||||
return await apiGet<List<UserInfo>>(r)
|
return await apiGet<List<UserInfo>>(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function editUser(u: EditUser, token: string, uid: string) {
|
||||||
|
const r = await fetch(import.meta.env.VITE_APIADDR + "/api/v1/admin/user/" + uid,{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Authorization": "Bearer " + token
|
||||||
|
},
|
||||||
|
body: JSON.stringify(u)
|
||||||
|
})
|
||||||
|
return await apiGet<unknown>(r)
|
||||||
|
}
|
@ -57,3 +57,10 @@ export interface UserInfo {
|
|||||||
reg_ip: string
|
reg_ip: string
|
||||||
name: string
|
name: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface EditUser {
|
||||||
|
email: string
|
||||||
|
name: string
|
||||||
|
password: string
|
||||||
|
is_admin: boolean
|
||||||
|
}
|
@ -6,7 +6,7 @@ import TableHead from '@mui/material/TableHead';
|
|||||||
import TableRow from '@mui/material/TableRow';
|
import TableRow from '@mui/material/TableRow';
|
||||||
import Paper from '@mui/material/Paper';
|
import Paper from '@mui/material/Paper';
|
||||||
import useTitle from '@/hooks/useTitle';
|
import useTitle from '@/hooks/useTitle';
|
||||||
import { useMemoizedFn, useRequest } from 'ahooks';
|
import { useRequest } from 'ahooks';
|
||||||
import { ListUser } from '@/apis/apis';
|
import { ListUser } from '@/apis/apis';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useAtomValue } from 'jotai';
|
import { useAtomValue } from 'jotai';
|
||||||
@ -113,15 +113,19 @@ interface MyDialogProp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function MyDialog({ open, row, setOpen }: MyDialogProp) {
|
function MyDialog({ open, row, setOpen }: MyDialogProp) {
|
||||||
const handleClose = useMemoizedFn(() => {
|
const handleClose = () => {
|
||||||
setOpen(false)
|
setOpen(false)
|
||||||
})
|
}
|
||||||
const [nrow, setNrow] = useState(row)
|
const [nrow, setNrow] = useState(row)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setNrow(row)
|
setNrow(row)
|
||||||
}, [row])
|
}, [row])
|
||||||
|
|
||||||
|
const handleOpen = () => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open}>
|
<Dialog open={open}>
|
||||||
@ -143,7 +147,7 @@ function MyDialog({ open, row, setOpen }: MyDialogProp) {
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button onClick={handleClose}>取消</Button>
|
<Button onClick={handleClose}>取消</Button>
|
||||||
<Button onClick={handleClose}>确认</Button>
|
<Button onClick={handleOpen}>确认</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
)
|
)
|
||||||
|
@ -7,9 +7,12 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/xmdhs/authlib-skin/model"
|
"github.com/xmdhs/authlib-skin/model"
|
||||||
"github.com/xmdhs/authlib-skin/service"
|
"github.com/xmdhs/authlib-skin/service"
|
||||||
"github.com/xmdhs/authlib-skin/service/utils"
|
"github.com/xmdhs/authlib-skin/service/utils"
|
||||||
|
|
||||||
|
U "github.com/xmdhs/authlib-skin/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type tokenValue string
|
type tokenValue string
|
||||||
@ -81,3 +84,33 @@ func (h *Handel) ListUser() http.HandlerFunc {
|
|||||||
encodeJson(w, model.API[model.List[model.UserList]]{Data: model.List[model.UserList]{List: ul, Total: uc}})
|
encodeJson(w, model.API[model.List[model.UserList]]{Data: model.List[model.UserList]{List: ul, Total: uc}})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handel) EditUser() http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
uid := chi.URLParamFromCtx(ctx, "uid")
|
||||||
|
if uid == "" {
|
||||||
|
h.handleError(ctx, w, "uid 为空", model.ErrInput, 400, slog.LevelDebug)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uidi, err := strconv.Atoi(uid)
|
||||||
|
if err != nil {
|
||||||
|
h.handleError(ctx, w, err.Error(), model.ErrInput, 400, slog.LevelDebug)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
a, err := U.DeCodeBody[model.EditUser](r.Body, h.validate)
|
||||||
|
if err != nil {
|
||||||
|
h.handleError(ctx, w, err.Error(), model.ErrInput, 400, slog.LevelDebug)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = h.webService.EditUser(ctx, a, uidi)
|
||||||
|
if err != nil {
|
||||||
|
h.handleError(ctx, w, err.Error(), model.ErrService, 500, slog.LevelWarn)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
encodeJson[any](w, model.API[any]{
|
||||||
|
Code: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -61,3 +61,12 @@ type Config struct {
|
|||||||
Captcha Captcha `json:"captcha"`
|
Captcha Captcha `json:"captcha"`
|
||||||
AllowChangeName bool
|
AllowChangeName bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type EditUser struct {
|
||||||
|
Email string `json:"email" validate:"required,email"`
|
||||||
|
Name string `json:"name" validate:"required,min=3,max=16"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
IsAdmin bool `json:"is_admin"`
|
||||||
|
IsDisable bool `json:"is_disable"`
|
||||||
|
DelTextures bool `json:"del_textures"`
|
||||||
|
}
|
||||||
|
@ -81,6 +81,7 @@ func newSkinApi(handel *handle.Handel) http.Handler {
|
|||||||
r.Use(handel.NeedAuth)
|
r.Use(handel.NeedAuth)
|
||||||
r.Use(handel.NeedAdmin)
|
r.Use(handel.NeedAdmin)
|
||||||
r.Get("/admin/users", handel.ListUser())
|
r.Get("/admin/users", handel.ListUser())
|
||||||
|
r.Post("/admin/user/{uid}", handel.EditUser())
|
||||||
})
|
})
|
||||||
|
|
||||||
return r
|
return r
|
||||||
|
@ -5,12 +5,15 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/xmdhs/authlib-skin/db/ent"
|
||||||
"github.com/xmdhs/authlib-skin/db/ent/predicate"
|
"github.com/xmdhs/authlib-skin/db/ent/predicate"
|
||||||
"github.com/xmdhs/authlib-skin/db/ent/user"
|
"github.com/xmdhs/authlib-skin/db/ent/user"
|
||||||
"github.com/xmdhs/authlib-skin/db/ent/userprofile"
|
"github.com/xmdhs/authlib-skin/db/ent/userprofile"
|
||||||
|
"github.com/xmdhs/authlib-skin/db/ent/usertoken"
|
||||||
"github.com/xmdhs/authlib-skin/model"
|
"github.com/xmdhs/authlib-skin/model"
|
||||||
"github.com/xmdhs/authlib-skin/model/yggdrasil"
|
"github.com/xmdhs/authlib-skin/model/yggdrasil"
|
||||||
utilsService "github.com/xmdhs/authlib-skin/service/utils"
|
utilsService "github.com/xmdhs/authlib-skin/service/utils"
|
||||||
|
"github.com/xmdhs/authlib-skin/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrNotAdmin = errors.New("无权限")
|
var ErrNotAdmin = errors.New("无权限")
|
||||||
@ -72,3 +75,56 @@ func (w *WebService) ListUser(ctx context.Context, page int, email, name string)
|
|||||||
}
|
}
|
||||||
return ul, uc, nil
|
return ul, uc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *WebService) EditUser(ctx context.Context, u model.EditUser, uid int) error {
|
||||||
|
err := utils.WithTx(ctx, w.client, func(tx *ent.Tx) error {
|
||||||
|
up := tx.User.UpdateOneID(uid).SetEmail(u.Email)
|
||||||
|
if u.Password != "" {
|
||||||
|
p, s := utils.Argon2ID(u.Password)
|
||||||
|
up = up.SetPassword(p).SetSalt(s)
|
||||||
|
err := tx.UserToken.Update().Where(usertoken.HasUserWith(user.ID(uid))).AddTokenID(1).Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err := tx.UserProfile.Update().Where(userprofile.HasUserWith(user.ID(uid))).SetName(u.Name).Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.DelTextures {
|
||||||
|
up, err := tx.UserProfile.Query().Where(userprofile.ID(uid)).First(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tl := []string{"skin", "cape"}
|
||||||
|
for _, v := range tl {
|
||||||
|
err := utilsService.DelTexture(ctx, up.ID, v, w.client, w.config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state := 0
|
||||||
|
if u.IsAdmin {
|
||||||
|
state = utilsService.SetAdmin(state)
|
||||||
|
}
|
||||||
|
if u.IsDisable {
|
||||||
|
state = utilsService.SetDisable(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
up = up.SetState(state)
|
||||||
|
|
||||||
|
err = up.Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("EditUser: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -87,3 +87,11 @@ func IsAdmin(state int) bool {
|
|||||||
func IsDisable(state int) bool {
|
func IsDisable(state int) bool {
|
||||||
return state&2 == 2
|
return state&2 == 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetAdmin(state int) int {
|
||||||
|
return state | 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetDisable(state int) int {
|
||||||
|
return state | 2
|
||||||
|
}
|
||||||
|
69
service/utils/texture.go
Normal file
69
service/utils/texture.go
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/samber/lo"
|
||||||
|
"github.com/xmdhs/authlib-skin/config"
|
||||||
|
"github.com/xmdhs/authlib-skin/db/ent"
|
||||||
|
"github.com/xmdhs/authlib-skin/db/ent/texture"
|
||||||
|
"github.com/xmdhs/authlib-skin/db/ent/usertexture"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DelTexture(ctx context.Context, userProfileID int, textureType string, client *ent.Client, config config.Config) error {
|
||||||
|
// 查找此用户该类型下是否已经存在皮肤
|
||||||
|
tl, err := client.UserTexture.Query().Where(usertexture.And(
|
||||||
|
usertexture.UserProfileID(userProfileID),
|
||||||
|
usertexture.Type(textureType),
|
||||||
|
)).All(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("DelTexture: %w", err)
|
||||||
|
}
|
||||||
|
if len(tl) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// 若存在,查找是否被引用
|
||||||
|
for _, v := range tl {
|
||||||
|
c, err := client.UserTexture.Query().Where(usertexture.TextureID(v.TextureID)).Count(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("DelTexture: %w", err)
|
||||||
|
}
|
||||||
|
if c == 1 {
|
||||||
|
// 若没有其他用户使用该皮肤,删除文件和记录
|
||||||
|
t, err := client.Texture.Query().Where(texture.ID(v.TextureID)).Only(ctx)
|
||||||
|
if err != nil {
|
||||||
|
var nf *ent.NotFoundError
|
||||||
|
if errors.As(err, &nf) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return fmt.Errorf("DelTexture: %w", err)
|
||||||
|
}
|
||||||
|
path := filepath.Join(config.TexturePath, t.TextureHash[:2], t.TextureHash[2:4], t.TextureHash)
|
||||||
|
err = os.Remove(path)
|
||||||
|
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||||
|
return fmt.Errorf("DelTexture: %w", err)
|
||||||
|
}
|
||||||
|
// Texture 表中删除记录
|
||||||
|
err = client.Texture.DeleteOneID(v.TextureID).Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("DelTexture: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ids := lo.Map[*ent.UserTexture, int](tl, func(item *ent.UserTexture, index int) int {
|
||||||
|
return item.ID
|
||||||
|
})
|
||||||
|
// 中间表删除记录
|
||||||
|
// UserProfile 上没有于此相关的字段,所以无需操作
|
||||||
|
_, err = client.UserTexture.Delete().Where(usertexture.IDIn(ids...)).Exec(ctx)
|
||||||
|
// 小概率皮肤上传后,高并发时被此处清理。问题不大重新上传一遍就行。
|
||||||
|
// 条件为使用一个独一无二的皮肤的用户,更换皮肤时,另一个用户同时更换自己的皮肤到这个独一无二的皮肤上。
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("DelTexture: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -9,13 +9,12 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/samber/lo"
|
|
||||||
"github.com/xmdhs/authlib-skin/db/ent"
|
"github.com/xmdhs/authlib-skin/db/ent"
|
||||||
"github.com/xmdhs/authlib-skin/db/ent/texture"
|
"github.com/xmdhs/authlib-skin/db/ent/texture"
|
||||||
"github.com/xmdhs/authlib-skin/db/ent/user"
|
"github.com/xmdhs/authlib-skin/db/ent/user"
|
||||||
"github.com/xmdhs/authlib-skin/db/ent/userprofile"
|
"github.com/xmdhs/authlib-skin/db/ent/userprofile"
|
||||||
"github.com/xmdhs/authlib-skin/db/ent/usertexture"
|
|
||||||
"github.com/xmdhs/authlib-skin/model"
|
"github.com/xmdhs/authlib-skin/model"
|
||||||
|
utilsService "github.com/xmdhs/authlib-skin/service/utils"
|
||||||
"github.com/xmdhs/authlib-skin/utils"
|
"github.com/xmdhs/authlib-skin/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -24,57 +23,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (y *Yggdrasil) delTexture(ctx context.Context, userProfileID int, textureType string) error {
|
func (y *Yggdrasil) delTexture(ctx context.Context, userProfileID int, textureType string) error {
|
||||||
// 查找此用户该类型下是否已经存在皮肤
|
return utilsService.DelTexture(ctx, userProfileID, textureType, y.client, y.config)
|
||||||
tl, err := y.client.UserTexture.Query().Where(usertexture.And(
|
|
||||||
usertexture.UserProfileID(userProfileID),
|
|
||||||
usertexture.Type(textureType),
|
|
||||||
)).All(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("delTexture: %w", err)
|
|
||||||
}
|
|
||||||
if len(tl) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// 若存在,查找是否被引用
|
|
||||||
for _, v := range tl {
|
|
||||||
c, err := y.client.UserTexture.Query().Where(usertexture.TextureID(v.TextureID)).Count(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("delTexture: %w", err)
|
|
||||||
}
|
|
||||||
if c == 1 {
|
|
||||||
// 若没有其他用户使用该皮肤,删除文件和记录
|
|
||||||
t, err := y.client.Texture.Query().Where(texture.ID(v.TextureID)).Only(ctx)
|
|
||||||
if err != nil {
|
|
||||||
var nf *ent.NotFoundError
|
|
||||||
if errors.As(err, &nf) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return fmt.Errorf("delTexture: %w", err)
|
|
||||||
}
|
|
||||||
path := filepath.Join(y.config.TexturePath, t.TextureHash[:2], t.TextureHash[2:4], t.TextureHash)
|
|
||||||
err = os.Remove(path)
|
|
||||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
|
||||||
return fmt.Errorf("delTexture: %w", err)
|
|
||||||
}
|
|
||||||
// Texture 表中删除记录
|
|
||||||
err = y.client.Texture.DeleteOneID(v.TextureID).Exec(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("delTexture: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ids := lo.Map[*ent.UserTexture, int](tl, func(item *ent.UserTexture, index int) int {
|
|
||||||
return item.ID
|
|
||||||
})
|
|
||||||
// 中间表删除记录
|
|
||||||
// UserProfile 上没有于此相关的字段,所以无需操作
|
|
||||||
_, err = y.client.UserTexture.Delete().Where(usertexture.IDIn(ids...)).Exec(ctx)
|
|
||||||
// 小概率皮肤上传后,高并发时被此处清理。问题不大重新上传一遍就行。
|
|
||||||
// 条件为使用一个独一无二的皮肤的用户,更换皮肤时,另一个用户同时更换自己的皮肤到这个独一无二的皮肤上。
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("delTexture: %w", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (y *Yggdrasil) DelTexture(ctx context.Context, uuid string, t *model.TokenClaims, textureType string) error {
|
func (y *Yggdrasil) DelTexture(ctx context.Context, uuid string, t *model.TokenClaims, textureType string) error {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user