From f86c8e4f80231c99c5cc422180204704dd9373af Mon Sep 17 00:00:00 2001 From: xmdhs Date: Thu, 5 Oct 2023 15:59:20 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=97=E5=87=BA=E6=89=80=E6=9C=89=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/Route.tsx | 14 ++++++ frontend/src/views/Layout.tsx | 5 --- frontend/src/views/admin/UserAdmin.tsx | 5 +++ frontend/src/views/profile/Textures.tsx | 1 - handle/admin.go | 58 +++++++++++++++++++++++++ model/model.go | 15 ++++++- server/route/route.go | 1 + service/admin.go | 55 +++++++++++++++++++++++ service/user.go | 11 +++-- 9 files changed, 151 insertions(+), 14 deletions(-) create mode 100644 frontend/src/views/admin/UserAdmin.tsx create mode 100644 handle/admin.go create mode 100644 service/admin.go diff --git a/frontend/src/Route.tsx b/frontend/src/Route.tsx index f244493..1967338 100644 --- a/frontend/src/Route.tsx +++ b/frontend/src/Route.tsx @@ -8,6 +8,9 @@ import Security from '@/views/profile/Security' import Layout from '@/views/Layout' import { useAtomValue } from "jotai"; import { token } from "@/store/store"; +import { ApiErr } from "@/apis/error"; +import { userInfo } from "@/apis/apis"; +import { useRequest } from "ahooks"; const router = createBrowserRouter([ { path: "*", Component: Root }, @@ -46,6 +49,17 @@ export function PageRoute() { function NeedLogin({ children }: { children: JSX.Element }) { const t = useAtomValue(token) const navigate = useNavigate(); + useRequest(() => userInfo(t), { + refreshDeps: [t], + cacheKey: "/api/v1/user" + t, + staleTime: 60000, + onError: e => { + if (e instanceof ApiErr && e.code == 5) { + navigate("/login") + } + console.warn(e) + }, + }) if (t == "") { navigate("/login") return diff --git a/frontend/src/views/Layout.tsx b/frontend/src/views/Layout.tsx index 1e881e5..47250ea 100644 --- a/frontend/src/views/Layout.tsx +++ b/frontend/src/views/Layout.tsx @@ -28,7 +28,6 @@ import Snackbar from '@mui/material/Snackbar'; import Alert from '@mui/material/Alert'; import { memo } from 'react'; import useMediaQuery from '@mui/material/useMediaQuery'; -import { ApiErr } from '@/apis/error'; import Typography from '@mui/material/Typography'; import Container from '@mui/material/Container'; import PersonIcon from '@mui/icons-material/Person'; @@ -209,7 +208,6 @@ const MyListItem = function MyListItem(p: ListItem) { const MyDrawer = function MyDrawer() { const nowToken = useAtomValue(token) const setErr = useSetAtom(LayoutAlertErr) - const navigate = useNavigate(); const theme = useTheme(); const isLg = useMediaQuery(theme.breakpoints.up('lg')) const [open, setOpen] = useAtom(DrawerOpen) @@ -219,9 +217,6 @@ const MyDrawer = function MyDrawer() { cacheKey: "/api/v1/user" + nowToken, staleTime: 60000, onError: e => { - if (e instanceof ApiErr && e.code == 5) { - navigate("/login") - } console.warn(e) setErr(String(e)) }, diff --git a/frontend/src/views/admin/UserAdmin.tsx b/frontend/src/views/admin/UserAdmin.tsx new file mode 100644 index 0000000..921ff72 --- /dev/null +++ b/frontend/src/views/admin/UserAdmin.tsx @@ -0,0 +1,5 @@ + +export default function UserAdmin() { + + return <> +} \ No newline at end of file diff --git a/frontend/src/views/profile/Textures.tsx b/frontend/src/views/profile/Textures.tsx index 87cc30b..6f42a7c 100644 --- a/frontend/src/views/profile/Textures.tsx +++ b/frontend/src/views/profile/Textures.tsx @@ -8,7 +8,6 @@ import Radio from "@mui/material/Radio"; import RadioGroup from "@mui/material/RadioGroup"; import Button from "@mui/material/Button"; import { CardHeader } from "@mui/material"; -import useTilg from "tilg"; import useTitle from "@/hooks/useTitle"; import { MuiFileInput } from 'mui-file-input' import Box from "@mui/material/Box"; diff --git a/handle/admin.go b/handle/admin.go new file mode 100644 index 0000000..f3c779b --- /dev/null +++ b/handle/admin.go @@ -0,0 +1,58 @@ +package handle + +import ( + "errors" + "log/slog" + "net/http" + "strconv" + + "github.com/julienschmidt/httprouter" + "github.com/xmdhs/authlib-skin/model" + utilsService "github.com/xmdhs/authlib-skin/service/utils" +) + +func (h *Handel) NeedAdmin(handle httprouter.Handle) httprouter.Handle { + return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) { + ctx := r.Context() + token := h.getTokenbyAuthorization(ctx, w, r) + if token == "" { + return + } + err := h.webService.IsAdmin(ctx, token) + if err != nil { + if errors.Is(err, utilsService.ErrTokenInvalid) { + h.handleError(ctx, w, "token 无效", model.ErrAuth, 401, slog.LevelDebug) + return + } + h.handleError(ctx, w, err.Error(), model.ErrService, 500, slog.LevelWarn) + return + } + handle(w, r, p) + } +} + +func (h *Handel) ListUser() httprouter.Handle { + return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) { + ctx := r.Context() + page := r.FormValue("page") + pagei := 1 + if page != "" { + p, err := strconv.Atoi(page) + if err != nil { + h.handleError(ctx, w, "page 必须为数字", model.ErrInput, 400, slog.LevelDebug) + return + } + if p == 0 { + p = 1 + } + pagei = p + } + + ul, uc, err := h.webService.ListUser(ctx, pagei) + if err != nil { + h.handleError(ctx, w, err.Error(), model.ErrService, 500, slog.LevelWarn) + return + } + encodeJson(w, model.API[model.List[model.UserList]]{Data: model.List[model.UserList]{List: ul, Total: uc}}) + } +} diff --git a/model/model.go b/model/model.go index c231eb5..596dfc3 100644 --- a/model/model.go +++ b/model/model.go @@ -5,7 +5,12 @@ import "github.com/golang-jwt/jwt/v5" type API[T any] struct { Code APIStatus `json:"code"` Data T `json:"data"` - Msg string `json:"msg"` + Msg string `json:"msg,omitempty"` +} + +type List[T any] struct { + Total int `json:"total"` + List []T `json:"list"` } type User struct { @@ -38,5 +43,11 @@ type UserInfo struct { type ChangePasswd struct { Old string `json:"old"` - New string `json:"new"` + New string `json:"new" validate:"required,min=6,max=50"` +} + +type UserList struct { + UserInfo + Email string `json:"email"` + RegIp string `json:"reg_ip"` } diff --git a/server/route/route.go b/server/route/route.go index 68c9ab2..f0ac03e 100644 --- a/server/route/route.go +++ b/server/route/route.go @@ -56,5 +56,6 @@ func newSkinApi(r *httprouter.Router, handel *handle.Handel) error { r.GET("/api/v1/captcha", handel.GetCaptcha()) r.GET("/api/v1/user", handel.UserInfo()) r.POST("/api/v1/user/password", handel.ChangePasswd()) + r.GET("/api/v1/admin/users", handel.NeedAdmin(handel.ListUser())) return nil } diff --git a/service/admin.go b/service/admin.go new file mode 100644 index 0000000..0bfa672 --- /dev/null +++ b/service/admin.go @@ -0,0 +1,55 @@ +package service + +import ( + "context" + "errors" + "fmt" + + "github.com/xmdhs/authlib-skin/db/ent/user" + "github.com/xmdhs/authlib-skin/model" + "github.com/xmdhs/authlib-skin/model/yggdrasil" + utilsService "github.com/xmdhs/authlib-skin/service/utils" +) + +var ErrNotAdmin = errors.New("无权限") + +func (w *WebService) IsAdmin(ctx context.Context, token string) error { + t, err := utilsService.Auth(ctx, yggdrasil.ValidateToken{AccessToken: token}, w.client, w.cache, &w.prikey.PublicKey, false) + if err != nil { + return fmt.Errorf("IsAdmin: %w", err) + } + u, err := w.client.User.Query().Where(user.ID(t.UID)).First(ctx) + if err != nil { + return fmt.Errorf("IsAdmin: %w", err) + } + if !utilsService.IsAdmin(u.State) { + return fmt.Errorf("IsAdmin: %w", ErrNotAdmin) + } + return nil +} + +func (w *WebService) ListUser(ctx context.Context, page int) ([]model.UserList, int, error) { + u, err := w.client.User.Query().WithProfile().Limit(20).Offset((page - 1) * 20).All(ctx) + if err != nil { + return nil, 0, fmt.Errorf("ListUser: %w", err) + } + ul := make([]model.UserList, 0, len(u)) + + for _, v := range u { + ul = append(ul, model.UserList{ + UserInfo: model.UserInfo{ + UID: v.ID, + UUID: v.Edges.Profile.UUID, + IsAdmin: utilsService.IsAdmin(v.State), + }, + Email: v.Email, + RegIp: v.RegIP, + }) + } + + uc, err := w.client.User.Query().Count(ctx) + if err != nil { + return nil, 0, fmt.Errorf("ListUser: %w", err) + } + return ul, uc, nil +} diff --git a/service/user.go b/service/user.go index 26de0ae..445839f 100644 --- a/service/user.go +++ b/service/user.go @@ -102,10 +102,7 @@ func (w *WebService) Info(ctx context.Context, token string) (model.UserInfo, er if err != nil { return model.UserInfo{}, fmt.Errorf("Info: %w", err) } - isAdmin := false - if u.State&1 == 1 { - isAdmin = true - } + isAdmin := utilsService.IsAdmin(u.State) return model.UserInfo{ UID: t.UID, UUID: t.Subject, @@ -131,8 +128,10 @@ func (w *WebService) ChangePasswd(ctx context.Context, p model.ChangePasswd, tok if err != nil { return fmt.Errorf("ChangePasswd: %w", err) } - w.cache.Del([]byte("auth" + strconv.Itoa(t.UID))) - + err = w.cache.Del([]byte("auth" + strconv.Itoa(t.UID))) + if err != nil { + return fmt.Errorf("ChangePasswd: %w", err) + } err = w.client.User.UpdateOne(u).SetPassword(pass).SetSalt(salt).Exec(ctx) if err != nil { return fmt.Errorf("ChangePasswd: %w", err)