列出所有用户接口
This commit is contained in:
parent
838652503c
commit
f86c8e4f80
@ -8,6 +8,9 @@ import Security from '@/views/profile/Security'
|
|||||||
import Layout from '@/views/Layout'
|
import Layout from '@/views/Layout'
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import { token } from "@/store/store";
|
import { token } from "@/store/store";
|
||||||
|
import { ApiErr } from "@/apis/error";
|
||||||
|
import { userInfo } from "@/apis/apis";
|
||||||
|
import { useRequest } from "ahooks";
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
const router = createBrowserRouter([
|
||||||
{ path: "*", Component: Root },
|
{ path: "*", Component: Root },
|
||||||
@ -46,6 +49,17 @@ export function PageRoute() {
|
|||||||
function NeedLogin({ children }: { children: JSX.Element }) {
|
function NeedLogin({ children }: { children: JSX.Element }) {
|
||||||
const t = useAtomValue(token)
|
const t = useAtomValue(token)
|
||||||
const navigate = useNavigate();
|
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 == "") {
|
if (t == "") {
|
||||||
navigate("/login")
|
navigate("/login")
|
||||||
return
|
return
|
||||||
|
@ -28,7 +28,6 @@ import Snackbar from '@mui/material/Snackbar';
|
|||||||
import Alert from '@mui/material/Alert';
|
import Alert from '@mui/material/Alert';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||||
import { ApiErr } from '@/apis/error';
|
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import Container from '@mui/material/Container';
|
import Container from '@mui/material/Container';
|
||||||
import PersonIcon from '@mui/icons-material/Person';
|
import PersonIcon from '@mui/icons-material/Person';
|
||||||
@ -209,7 +208,6 @@ const MyListItem = function MyListItem(p: ListItem) {
|
|||||||
const MyDrawer = function MyDrawer() {
|
const MyDrawer = function MyDrawer() {
|
||||||
const nowToken = useAtomValue(token)
|
const nowToken = useAtomValue(token)
|
||||||
const setErr = useSetAtom(LayoutAlertErr)
|
const setErr = useSetAtom(LayoutAlertErr)
|
||||||
const navigate = useNavigate();
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isLg = useMediaQuery(theme.breakpoints.up('lg'))
|
const isLg = useMediaQuery(theme.breakpoints.up('lg'))
|
||||||
const [open, setOpen] = useAtom(DrawerOpen)
|
const [open, setOpen] = useAtom(DrawerOpen)
|
||||||
@ -219,9 +217,6 @@ const MyDrawer = function MyDrawer() {
|
|||||||
cacheKey: "/api/v1/user" + nowToken,
|
cacheKey: "/api/v1/user" + nowToken,
|
||||||
staleTime: 60000,
|
staleTime: 60000,
|
||||||
onError: e => {
|
onError: e => {
|
||||||
if (e instanceof ApiErr && e.code == 5) {
|
|
||||||
navigate("/login")
|
|
||||||
}
|
|
||||||
console.warn(e)
|
console.warn(e)
|
||||||
setErr(String(e))
|
setErr(String(e))
|
||||||
},
|
},
|
||||||
|
5
frontend/src/views/admin/UserAdmin.tsx
Normal file
5
frontend/src/views/admin/UserAdmin.tsx
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
export default function UserAdmin() {
|
||||||
|
|
||||||
|
return <></>
|
||||||
|
}
|
@ -8,7 +8,6 @@ import Radio from "@mui/material/Radio";
|
|||||||
import RadioGroup from "@mui/material/RadioGroup";
|
import RadioGroup from "@mui/material/RadioGroup";
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
import { CardHeader } from "@mui/material";
|
import { CardHeader } from "@mui/material";
|
||||||
import useTilg from "tilg";
|
|
||||||
import useTitle from "@/hooks/useTitle";
|
import useTitle from "@/hooks/useTitle";
|
||||||
import { MuiFileInput } from 'mui-file-input'
|
import { MuiFileInput } from 'mui-file-input'
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
|
58
handle/admin.go
Normal file
58
handle/admin.go
Normal file
@ -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}})
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,12 @@ import "github.com/golang-jwt/jwt/v5"
|
|||||||
type API[T any] struct {
|
type API[T any] struct {
|
||||||
Code APIStatus `json:"code"`
|
Code APIStatus `json:"code"`
|
||||||
Data T `json:"data"`
|
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 {
|
type User struct {
|
||||||
@ -38,5 +43,11 @@ type UserInfo struct {
|
|||||||
|
|
||||||
type ChangePasswd struct {
|
type ChangePasswd struct {
|
||||||
Old string `json:"old"`
|
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"`
|
||||||
}
|
}
|
||||||
|
@ -56,5 +56,6 @@ func newSkinApi(r *httprouter.Router, handel *handle.Handel) error {
|
|||||||
r.GET("/api/v1/captcha", handel.GetCaptcha())
|
r.GET("/api/v1/captcha", handel.GetCaptcha())
|
||||||
r.GET("/api/v1/user", handel.UserInfo())
|
r.GET("/api/v1/user", handel.UserInfo())
|
||||||
r.POST("/api/v1/user/password", handel.ChangePasswd())
|
r.POST("/api/v1/user/password", handel.ChangePasswd())
|
||||||
|
r.GET("/api/v1/admin/users", handel.NeedAdmin(handel.ListUser()))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
55
service/admin.go
Normal file
55
service/admin.go
Normal file
@ -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
|
||||||
|
}
|
@ -102,10 +102,7 @@ func (w *WebService) Info(ctx context.Context, token string) (model.UserInfo, er
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return model.UserInfo{}, fmt.Errorf("Info: %w", err)
|
return model.UserInfo{}, fmt.Errorf("Info: %w", err)
|
||||||
}
|
}
|
||||||
isAdmin := false
|
isAdmin := utilsService.IsAdmin(u.State)
|
||||||
if u.State&1 == 1 {
|
|
||||||
isAdmin = true
|
|
||||||
}
|
|
||||||
return model.UserInfo{
|
return model.UserInfo{
|
||||||
UID: t.UID,
|
UID: t.UID,
|
||||||
UUID: t.Subject,
|
UUID: t.Subject,
|
||||||
@ -131,8 +128,10 @@ func (w *WebService) ChangePasswd(ctx context.Context, p model.ChangePasswd, tok
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("ChangePasswd: %w", err)
|
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)
|
err = w.client.User.UpdateOne(u).SetPassword(pass).SetSalt(salt).Exec(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("ChangePasswd: %w", err)
|
return fmt.Errorf("ChangePasswd: %w", err)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user