From 63be2bc13f8a20103ee5eab638abbcf76bdb6888 Mon Sep 17 00:00:00 2001 From: xmdhs Date: Tue, 10 Oct 2023 18:43:35 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E4=BF=AE=E6=94=B9=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/apis/apis.ts | 2 +- frontend/src/apis/model.ts | 12 ++--- frontend/src/views/admin/UserAdmin.tsx | 67 ++++++++++++++----------- frontend/src/views/profile/Security.tsx | 6 +-- handle/yggdrasil/texture.go | 23 ++++++--- model/model.go | 8 +-- server/route/route.go | 2 +- service/admin.go | 56 +++++++++++++-------- service/utils/auth.go | 14 ++++-- service/yggdrasil/texture.go | 15 ++---- 10 files changed, 119 insertions(+), 86 deletions(-) diff --git a/frontend/src/apis/apis.ts b/frontend/src/apis/apis.ts index 0ad2eb2..117ea23 100644 --- a/frontend/src/apis/apis.ts +++ b/frontend/src/apis/apis.ts @@ -117,7 +117,7 @@ export async function ListUser(page: number, token: string, email: string, name: 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", + method: "PATCH", headers: { "Authorization": "Bearer " + token }, diff --git a/frontend/src/apis/model.ts b/frontend/src/apis/model.ts index 516237d..861a435 100644 --- a/frontend/src/apis/model.ts +++ b/frontend/src/apis/model.ts @@ -58,10 +58,10 @@ export interface UserInfo { } export interface EditUser { - email: string - name: string - password: string - is_admin: boolean - is_disable: boolean - del_textures: boolean + email?: string + name?: string + password?: string + is_admin?: boolean + is_disable?: boolean + del_textures?: boolean } \ No newline at end of file diff --git a/frontend/src/views/admin/UserAdmin.tsx b/frontend/src/views/admin/UserAdmin.tsx index b3388d8..4948b71 100644 --- a/frontend/src/views/admin/UserAdmin.tsx +++ b/frontend/src/views/admin/UserAdmin.tsx @@ -8,7 +8,7 @@ import Paper from '@mui/material/Paper'; import useTitle from '@/hooks/useTitle'; import { useRequest } from 'ahooks'; import { ListUser, editUser } from '@/apis/apis'; -import { useEffect, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { useAtomValue } from 'jotai'; import { token } from '@/store/store'; import TablePagination from '@mui/material/TablePagination'; @@ -133,25 +133,50 @@ function MyDialog({ open, row, setOpen, onUpdate }: MyDialogProp) { const [load, setLoad] = useState(false) const nowToken = useAtomValue(token) const [err, setErr] = useState("") + const editValue = useRef({}); useEffect(() => { + if (!row) return setErow({ - email: row?.email ?? "", - name: row?.name ?? "", + email: row.email, + name: row.name, password: "", - is_admin: row?.is_admin ?? false, - is_disable: row?.is_disable ?? false, + is_admin: row.is_admin, + is_disable: row.is_disable, del_textures: false, }) - }, [row]) + editValue.current = {} + }, [row, open]) const handleOpen = () => { if (load) return setLoad(true) - editUser(erow, nowToken, String(row?.uid)).then(() => [setOpen(false), onUpdate()]).finally(() => setLoad(false)). + editUser(editValue.current, nowToken, String(row?.uid)).then(() => [setOpen(false), onUpdate(), editValue.current = {}]).finally(() => setLoad(false)). catch(e => setErr(String(e))) } + type StringKeys = { + [K in keyof T]: T[K] extends string ? K : never; + }[keyof T]; + + function handleSetValue(key: StringKeys>, value: string) { + setErow(produce(v => { + v[key] = value + editValue.current[key] = value + })) + } + + type BoolKeys = { + [K in keyof T]: T[K] extends boolean ? K : never; + }[keyof T]; + + function handleSetChecked(key: BoolKeys>, value: boolean) { + setErow(produce(v => { + v[key] = value + editValue.current[key] = value + })) + } + return (<> @@ -171,10 +196,7 @@ function MyDialog({ open, row, setOpen, onUpdate }: MyDialogProp) { type="email" variant="standard" value={erow?.email} - onChange={e => setErow(produce(v => { - v.email = e.target.value - return - }))} + onChange={e => handleSetValue('email', e.target.value)} /> setErow(produce(v => { - v.name = e.target.value - return - }))} + onChange={e => handleSetValue('name', e.target.value)} /> setErow(produce(v => { - v.password = e.target.value - return - }))} + onChange={e => handleSetValue('password', e.target.value)} /> - setErow(produce(v => { - v.is_admin = e.target.checked - }))} />} label="管理权限" /> - setErow(produce(v => { - v.is_disable = e.target.checked - }))} />} label="禁用" /> - setErow(produce(v => { - v.del_textures = e.target.checked - }))} />} label="清空材质" /> + handleSetChecked('is_admin', e.target.checked)} />} label="管理权限" /> + handleSetChecked('is_disable', e.target.checked)} />} label="禁用" /> + handleSetChecked('del_textures', e.target.checked)} />} label="清空材质" /> diff --git a/frontend/src/views/profile/Security.tsx b/frontend/src/views/profile/Security.tsx index 100c864..71ad72f 100644 --- a/frontend/src/views/profile/Security.tsx +++ b/frontend/src/views/profile/Security.tsx @@ -93,7 +93,7 @@ function ChangePasswd() { required error={oldPassErr} helperText={oldPassErr ? "旧密码错误" : ""} - onChange={p => setPass(produce(v => { v.old = p.target.value; return v }))} + onChange={p => setPass(produce(v => { v.old = p.target.value }))} autoComplete="current-password" /> setPass(produce(v => { v.pass1 = p.target.value; return v }))} + onChange={p => setPass(produce(v => { v.pass1 = p.target.value }))} autoComplete="new-password" /> setPass(produce(v => { v.pass2 = p.target.value; return v }))} + onChange={p => setPass(produce(v => { v.pass2 = p.target.value }))} autoComplete="new-password" /> diff --git a/handle/yggdrasil/texture.go b/handle/yggdrasil/texture.go index 25f05f9..39a1abb 100644 --- a/handle/yggdrasil/texture.go +++ b/handle/yggdrasil/texture.go @@ -55,6 +55,12 @@ func (y *Yggdrasil) PutTexture() http.HandlerFunc { } t := ctx.Value(tokenKey).(*model.TokenClaims) + if uuid != t.Subject { + y.logger.DebugContext(ctx, "uuid 不相同") + w.WriteHeader(401) + return + } + model := r.FormValue("model") skin, err := func() ([]byte, error) { @@ -99,14 +105,8 @@ func (y *Yggdrasil) PutTexture() http.HandlerFunc { return } - err = y.yggdrasilService.PutTexture(ctx, t, skin, model, uuid, textureType) + err = y.yggdrasilService.PutTexture(ctx, t, skin, model, textureType) if err != nil { - if errors.Is(err, yggdrasilS.ErrUUIDNotEq) { - y.logger.DebugContext(ctx, err.Error()) - w.WriteHeader(401) - return - } - y.handleYgError(ctx, w, err) return } @@ -139,7 +139,14 @@ func (y *Yggdrasil) DelTexture() http.HandlerFunc { return } t := ctx.Value(tokenKey).(*model.TokenClaims) - err := y.yggdrasilService.DelTexture(ctx, uuid, t, textureType) + + if uuid != t.Subject { + y.logger.DebugContext(ctx, "uuid 不相同") + w.WriteHeader(401) + return + } + + err := y.yggdrasilService.DelTexture(ctx, t, textureType) if err != nil { if errors.Is(err, yggdrasilS.ErrUUIDNotEq) { y.logger.DebugContext(ctx, err.Error()) diff --git a/model/model.go b/model/model.go index 6e3960b..b31b33b 100644 --- a/model/model.go +++ b/model/model.go @@ -64,11 +64,11 @@ type Config struct { } type EditUser struct { - Email string `json:"email" validate:"required,email"` - Name string `json:"name" validate:"required,min=3,max=16"` + Email string `json:"email" validate:"omitempty,email"` + Name string `json:"name" validate:"omitempty,min=3,max=16"` Password string `json:"password"` - IsAdmin bool `json:"is_admin"` - IsDisable bool `json:"is_disable"` + IsAdmin *bool `json:"is_admin"` + IsDisable *bool `json:"is_disable"` DelTextures bool `json:"del_textures"` } diff --git a/server/route/route.go b/server/route/route.go index 5a3c2ef..e19a79e 100644 --- a/server/route/route.go +++ b/server/route/route.go @@ -82,7 +82,7 @@ func newSkinApi(handel *handle.Handel) http.Handler { r.Use(handel.NeedAuth) r.Use(handel.NeedAdmin) r.Get("/admin/users", handel.ListUser()) - r.Post("/admin/user/{uid}", handel.EditUser()) + r.Patch("/admin/user/{uid}", handel.EditUser()) }) return r diff --git a/service/admin.go b/service/admin.go index 355a3e4..d2cd2df 100644 --- a/service/admin.go +++ b/service/admin.go @@ -10,7 +10,6 @@ import ( "github.com/xmdhs/authlib-skin/db/ent/predicate" "github.com/xmdhs/authlib-skin/db/ent/user" "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/yggdrasil" utilsService "github.com/xmdhs/authlib-skin/service/utils" @@ -82,19 +81,32 @@ func (w *WebService) EditUser(ctx context.Context, u model.EditUser, uid int) er uuid := "" changePasswd := false 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 u.Email != "" { + c, err := tx.User.Query().Where(user.Email(u.Email)).Count(ctx) + if err != nil { + return err + } + if c != 0 { + return ErrExistUser + } + err = tx.User.UpdateOneID(uid).SetEmail(u.Email).Exec(ctx) if err != nil { return err } - changePasswd = true } - err := tx.UserProfile.Update().Where(userprofile.HasUserWith(user.ID(uid))).SetName(u.Name).Exec(ctx) - if err != nil { - return err + + if u.Name != "" { + c, err := tx.UserProfile.Query().Where(userprofile.Name(u.Name)).Count(ctx) + if err != nil { + return err + } + if c != 0 { + return ErrExitsName + } + err = tx.UserProfile.Update().Where(userprofile.HasUserWith(user.ID(uid))).SetName(u.Name).Exec(ctx) + if err != nil { + return err + } } if u.DelTextures { @@ -111,21 +123,25 @@ func (w *WebService) EditUser(ctx context.Context, u model.EditUser, uid int) er } } } - state := 0 - if u.IsAdmin { - state = utilsService.SetAdmin(state) - } - if u.IsDisable { - state = utilsService.SetDisable(state) - } - up = up.SetState(state) - - err = up.Exec(ctx) + aUser, err := tx.User.Get(ctx, uid) if err != nil { return err } + state := aUser.State + if u.IsAdmin != nil { + state = utilsService.SetAdmin(state, *u.IsAdmin) + } + if u.IsDisable != nil { + state = utilsService.SetDisable(state, *u.IsDisable) + } + if state != aUser.State { + err := tx.User.UpdateOneID(uid).SetState(state).Exec(ctx) + if err != nil { + return err + } + } return nil }) if err != nil { diff --git a/service/utils/auth.go b/service/utils/auth.go index 6780b71..ce61daa 100644 --- a/service/utils/auth.go +++ b/service/utils/auth.go @@ -147,10 +147,16 @@ func IsDisable(state int) bool { return state&2 == 2 } -func SetAdmin(state int) int { - return state | 1 +func SetAdmin(state int, is bool) int { + if is { + return state | 1 + } + return state & (state ^ 1) } -func SetDisable(state int) int { - return state | 2 +func SetDisable(state int, is bool) int { + if is { + return state | 2 + } + return state & (state ^ 2) } diff --git a/service/yggdrasil/texture.go b/service/yggdrasil/texture.go index 38bae00..190de74 100644 --- a/service/yggdrasil/texture.go +++ b/service/yggdrasil/texture.go @@ -26,10 +26,7 @@ func (y *Yggdrasil) delTexture(ctx context.Context, userProfileID int, textureTy return utilsService.DelTexture(ctx, userProfileID, textureType, y.client, y.config) } -func (y *Yggdrasil) DelTexture(ctx context.Context, uuid string, t *model.TokenClaims, textureType string) error { - if uuid != t.Subject { - return fmt.Errorf("PutTexture: %w", ErrUUIDNotEq) - } +func (y *Yggdrasil) DelTexture(ctx context.Context, t *model.TokenClaims, textureType string) error { up, err := y.client.UserProfile.Query().Where(userprofile.HasUserWith(user.ID(t.UID))).First(ctx) if err != nil { return fmt.Errorf("DelTexture: %w", err) @@ -38,18 +35,14 @@ func (y *Yggdrasil) DelTexture(ctx context.Context, uuid string, t *model.TokenC if err != nil { return fmt.Errorf("DelTexture: %w", err) } - err = y.cache.Del([]byte("Profile" + uuid)) + err = y.cache.Del([]byte("Profile" + t.Subject)) if err != nil { return fmt.Errorf("DelTexture: %w", err) } return nil } -func (y *Yggdrasil) PutTexture(ctx context.Context, t *model.TokenClaims, texturebyte []byte, model string, uuid string, textureType string) error { - if uuid != t.Subject { - return fmt.Errorf("PutTexture: %w", ErrUUIDNotEq) - } - +func (y *Yggdrasil) PutTexture(ctx context.Context, t *model.TokenClaims, texturebyte []byte, model string, textureType string) error { up, err := y.client.UserProfile.Query().Where(userprofile.HasUserWith(user.ID(t.UID))).First(ctx) if err != nil { return fmt.Errorf("PutTexture: %w", err) @@ -96,7 +89,7 @@ func (y *Yggdrasil) PutTexture(ctx context.Context, t *model.TokenClaims, textur if err != nil { return fmt.Errorf("PutTexture: %w", err) } - err = y.cache.Del([]byte("Profile" + uuid)) + err = y.cache.Del([]byte("Profile" + t.Subject)) if err != nil { return fmt.Errorf("PutTexture: %w", err) }