基础信息展示
This commit is contained in:
parent
034260391d
commit
bf5e5cae87
@ -1,4 +1,5 @@
|
||||
import type { tokenData } from '@/apis/model'
|
||||
import type { tokenData, ApiUser } from '@/apis/model'
|
||||
import { apiWrapGet } from '@/apis/utils'
|
||||
|
||||
export async function login(username: string, password: string) {
|
||||
const v = await fetch(import.meta.env.VITE_APIADDR + "/api/yggdrasil/authserver/authenticate", {
|
||||
@ -25,9 +26,16 @@ export async function register(email: string, username: string, password: string
|
||||
"CaptchaToken": captchaToken
|
||||
})
|
||||
})
|
||||
const data = await v.json()
|
||||
if (!v.ok) {
|
||||
throw data.msg
|
||||
}
|
||||
return
|
||||
return await apiWrapGet(v)
|
||||
}
|
||||
|
||||
export async function userInfo(token: string) {
|
||||
if (token == "") return
|
||||
const v = await fetch(import.meta.env.VITE_APIADDR + "/api/v1/user", {
|
||||
headers: {
|
||||
"Authorization": "Bearer " + token
|
||||
}
|
||||
})
|
||||
return await apiWrapGet<ApiUser>(v)
|
||||
}
|
||||
|
||||
|
@ -19,3 +19,10 @@ interface captcha {
|
||||
}
|
||||
|
||||
export type ApiCaptcha = Api<captcha>
|
||||
|
||||
interface user {
|
||||
uid: string
|
||||
uuid: string
|
||||
}
|
||||
|
||||
export type ApiUser = Api<user>
|
||||
|
7
frontend/src/apis/utils.ts
Normal file
7
frontend/src/apis/utils.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export async function apiWrapGet<T>(v: Response) {
|
||||
const data = await v.json()
|
||||
if (!v.ok) {
|
||||
throw data.msg
|
||||
}
|
||||
return data as T
|
||||
}
|
@ -20,7 +20,7 @@ const CaptchaWidget = forwardRef<refType, prop>(({ onSuccess }, ref) => {
|
||||
const Turnstileref = useRef<TurnstileInstance>(null)
|
||||
const [key, setKey] = useState(1)
|
||||
const { data, error, loading } = useRequest(() => fetch(import.meta.env.VITE_APIADDR + '/api/v1/captcha').then(v => v.json() as Promise<ApiCaptcha>), {
|
||||
loadingDelay: 500
|
||||
loadingDelay: 200
|
||||
})
|
||||
|
||||
useImperativeHandle(ref, () => {
|
||||
|
@ -1,15 +1,28 @@
|
||||
import { token, username } from "@/store/store"
|
||||
import { useRequest } from "ahooks"
|
||||
import { useAtomValue } from "jotai"
|
||||
import { userInfo } from '@/apis/apis'
|
||||
|
||||
export default function User() {
|
||||
const nowToken = useAtomValue(token)
|
||||
const nowUsername = useAtomValue(username)
|
||||
|
||||
const { data, error } = useRequest(() => userInfo(nowToken), {
|
||||
refreshDeps: [nowToken],
|
||||
cacheKey: "/api/v1/user/reg",
|
||||
cacheTime: 10000
|
||||
})
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>你好: {nowUsername}</p>
|
||||
<p>token: {nowToken} </p>
|
||||
{error && String(error)}
|
||||
{!error && <>
|
||||
<p>uid: {data?.data.uid}</p>
|
||||
<p>uuid: {data?.data.uuid}</p>
|
||||
</>}
|
||||
</>
|
||||
)
|
||||
}
|
@ -1,10 +1,19 @@
|
||||
package handle
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"strings"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/samber/lo"
|
||||
"github.com/xmdhs/authlib-skin/config"
|
||||
"github.com/xmdhs/authlib-skin/model"
|
||||
"github.com/xmdhs/authlib-skin/service"
|
||||
)
|
||||
|
||||
@ -23,3 +32,34 @@ func NewHandel(webService *service.WebService, validate *validator.Validate, con
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func encodeJson[T any](w io.Writer, m model.API[T]) {
|
||||
json.NewEncoder(w).Encode(m)
|
||||
}
|
||||
|
||||
func (h *Handel) getTokenbyAuthorization(ctx context.Context, w http.ResponseWriter, r *http.Request) string {
|
||||
auth := r.Header.Get("Authorization")
|
||||
if auth == "" {
|
||||
h.logger.DebugContext(ctx, "缺少 Authorization")
|
||||
handleError(ctx, w, "缺少 Authorization", model.ErrAuth, 401)
|
||||
return ""
|
||||
}
|
||||
al := strings.Split(auth, " ")
|
||||
if len(al) != 2 || al[0] != "Bearer" {
|
||||
h.logger.DebugContext(ctx, "Authorization 格式错误")
|
||||
handleError(ctx, w, "Authorization 格式错误", model.ErrAuth, 401)
|
||||
return ""
|
||||
}
|
||||
return al[1]
|
||||
}
|
||||
|
||||
func getPrefix(ip string) (string, error) {
|
||||
ipa, err := netip.ParseAddr(ip)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("getPrefix: %w", err)
|
||||
}
|
||||
if ipa.Is6() {
|
||||
return lo.Must1(ipa.Prefix(48)).String(), nil
|
||||
}
|
||||
return lo.Must1(ipa.Prefix(24)).String(), nil
|
||||
}
|
||||
|
@ -1,16 +1,13 @@
|
||||
package handle
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/samber/lo"
|
||||
"github.com/xmdhs/authlib-skin/model"
|
||||
"github.com/xmdhs/authlib-skin/service"
|
||||
utilsService "github.com/xmdhs/authlib-skin/service/utils"
|
||||
"github.com/xmdhs/authlib-skin/utils"
|
||||
)
|
||||
|
||||
@ -53,22 +50,34 @@ func (h *Handel) Reg() httprouter.Handle {
|
||||
handleError(ctx, w, err.Error(), model.ErrService, 500)
|
||||
return
|
||||
}
|
||||
json.NewEncoder(w).Encode(model.API[any]{
|
||||
encodeJson(w, model.API[any]{
|
||||
Code: 0,
|
||||
Data: nil,
|
||||
Msg: "",
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func getPrefix(ip string) (string, error) {
|
||||
ipa, err := netip.ParseAddr(ip)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("getPrefix: %w", err)
|
||||
func (h *Handel) UserInfo() 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
|
||||
}
|
||||
|
||||
u, err := h.webService.Info(ctx, token)
|
||||
if err != nil {
|
||||
if errors.Is(err, utilsService.ErrTokenInvalid) {
|
||||
h.logger.DebugContext(ctx, "token 无效")
|
||||
handleError(ctx, w, "token 无效", model.ErrAuth, 401)
|
||||
return
|
||||
}
|
||||
h.logger.InfoContext(ctx, err.Error())
|
||||
handleError(ctx, w, err.Error(), model.ErrUnknown, 500)
|
||||
return
|
||||
}
|
||||
encodeJson(w, model.API[model.UserInfo]{
|
||||
Code: 0,
|
||||
Data: u,
|
||||
})
|
||||
}
|
||||
if ipa.Is6() {
|
||||
return lo.Must1(ipa.Prefix(48)).String(), nil
|
||||
}
|
||||
return lo.Must1(ipa.Prefix(24)).String(), nil
|
||||
}
|
||||
|
@ -9,4 +9,5 @@ const (
|
||||
ErrService
|
||||
ErrExistUser
|
||||
ErrRegLimit
|
||||
ErrAuth
|
||||
)
|
||||
|
@ -29,3 +29,8 @@ type Captcha struct {
|
||||
Type string `json:"type"`
|
||||
SiteKey string `json:"siteKey"`
|
||||
}
|
||||
|
||||
type UserInfo struct {
|
||||
UID int `json:"uid"`
|
||||
UUID string `json:"uuid"`
|
||||
}
|
||||
|
@ -11,6 +11,10 @@ import (
|
||||
|
||||
func NewRoute(yggService *yggdrasil.Yggdrasil, handel *handle.Handel) (*httprouter.Router, error) {
|
||||
r := httprouter.New()
|
||||
r.HandleOPTIONS = true
|
||||
r.GlobalOPTIONS = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(204)
|
||||
})
|
||||
err := newYggdrasil(r, *yggService)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("NewRoute: %w", err)
|
||||
@ -19,17 +23,6 @@ func NewRoute(yggService *yggdrasil.Yggdrasil, handel *handle.Handel) (*httprout
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("NewRoute: %w", err)
|
||||
}
|
||||
r.GlobalOPTIONS = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Header.Get("Access-Control-Request-Method") != "" {
|
||||
// Set CORS headers
|
||||
header := w.Header()
|
||||
header.Set("Access-Control-Allow-Methods", header.Get("Allow"))
|
||||
header.Set("Access-Control-Allow-Origin", "*")
|
||||
}
|
||||
|
||||
// Adjust status code to 204
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
return r, nil
|
||||
}
|
||||
|
||||
@ -61,5 +54,6 @@ func newYggdrasil(r *httprouter.Router, handelY yggdrasil.Yggdrasil) error {
|
||||
func newSkinApi(r *httprouter.Router, handel *handle.Handel) error {
|
||||
r.PUT("/api/v1/user/reg", handel.Reg())
|
||||
r.GET("/api/v1/captcha", handel.GetCaptcha())
|
||||
r.GET("/api/v1/user", handel.UserInfo())
|
||||
return nil
|
||||
}
|
||||
|
@ -33,9 +33,22 @@ func NewServer(c config.Config, sl *slog.Logger, route *httprouter.Router) (*htt
|
||||
if c.Debug && sl.Enabled(ctx, slog.LevelDebug) {
|
||||
sl.DebugContext(ctx, r.Method)
|
||||
}
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
route.ServeHTTP(w, r)
|
||||
cors(route).ServeHTTP(w, r)
|
||||
}),
|
||||
}
|
||||
return s, func() { s.Close() }
|
||||
}
|
||||
|
||||
func cors(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
header := w.Header()
|
||||
if r.Header.Get("Access-Control-Request-Method") != "" {
|
||||
header.Set("Access-Control-Allow-Methods", r.Header.Get("Access-Control-Request-Method"))
|
||||
}
|
||||
header.Set("Access-Control-Allow-Origin", "*")
|
||||
header.Set("Access-Control-Allow-Headers", "*")
|
||||
header.Set("Access-Control-Allow-Private-Network", "true")
|
||||
header.Set("Access-Control-Max-Age", "3600")
|
||||
h.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ func InitializeRoute(ctx context.Context, c config.Config) (*http.Server, func()
|
||||
}
|
||||
yggdrasil3 := yggdrasil2.NewYggdrasil(logger, validate, yggdrasilYggdrasil, c, pubRsaKey)
|
||||
httpClient := ProvideHttpClient()
|
||||
webService := service.NewWebService(c, client, httpClient)
|
||||
webService := service.NewWebService(c, client, httpClient, cache, privateKey)
|
||||
handel := handle.NewHandel(webService, validate, c, logger)
|
||||
router, err := route.NewRoute(yggdrasil3, handel)
|
||||
if err != nil {
|
||||
|
@ -12,6 +12,8 @@ import (
|
||||
"github.com/xmdhs/authlib-skin/db/ent/user"
|
||||
"github.com/xmdhs/authlib-skin/db/ent/userprofile"
|
||||
"github.com/xmdhs/authlib-skin/model"
|
||||
"github.com/xmdhs/authlib-skin/model/yggdrasil"
|
||||
utilsService "github.com/xmdhs/authlib-skin/service/utils"
|
||||
"github.com/xmdhs/authlib-skin/utils"
|
||||
)
|
||||
|
||||
@ -88,3 +90,14 @@ func (w *WebService) Reg(ctx context.Context, u model.User, ipPrefix, ip string)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WebService) Info(ctx context.Context, token string) (model.UserInfo, error) {
|
||||
t, err := utilsService.Auth(ctx, yggdrasil.ValidateToken{AccessToken: token}, w.client, w.cache, &w.prikey.PublicKey, false)
|
||||
if err != nil {
|
||||
return model.UserInfo{}, fmt.Errorf("Info: %w", err)
|
||||
}
|
||||
return model.UserInfo{
|
||||
UID: t.UID,
|
||||
UUID: t.Subject,
|
||||
}, nil
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"net/http"
|
||||
|
||||
"github.com/xmdhs/authlib-skin/config"
|
||||
"github.com/xmdhs/authlib-skin/db/cache"
|
||||
"github.com/xmdhs/authlib-skin/db/ent"
|
||||
)
|
||||
|
||||
@ -11,12 +13,16 @@ type WebService struct {
|
||||
config config.Config
|
||||
client *ent.Client
|
||||
httpClient *http.Client
|
||||
cache cache.Cache
|
||||
prikey *rsa.PrivateKey
|
||||
}
|
||||
|
||||
func NewWebService(c config.Config, e *ent.Client, hc *http.Client) *WebService {
|
||||
func NewWebService(c config.Config, e *ent.Client, hc *http.Client, cache cache.Cache, prikey *rsa.PrivateKey) *WebService {
|
||||
return &WebService{
|
||||
config: c,
|
||||
client: e,
|
||||
httpClient: hc,
|
||||
cache: cache,
|
||||
prikey: prikey,
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user