列出所有用户接口

This commit is contained in:
xmdhs 2023-10-05 15:59:20 +08:00
parent 838652503c
commit f86c8e4f80
No known key found for this signature in database
GPG Key ID: E809D6D43DEFCC95
9 changed files with 151 additions and 14 deletions

View File

@ -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

View File

@ -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))
}, },

View File

@ -0,0 +1,5 @@
export default function UserAdmin() {
return <></>
}

View File

@ -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
View 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}})
}
}

View File

@ -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"`
} }

View File

@ -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
View 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
}

View File

@ -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)