基础信息展示

This commit is contained in:
xmdhs 2023-09-27 01:36:31 +08:00
parent 034260391d
commit bf5e5cae87
No known key found for this signature in database
GPG Key ID: E809D6D43DEFCC95
14 changed files with 156 additions and 40 deletions

View File

@ -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) { export async function login(username: string, password: string) {
const v = await fetch(import.meta.env.VITE_APIADDR + "/api/yggdrasil/authserver/authenticate", { 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 "CaptchaToken": captchaToken
}) })
}) })
const data = await v.json() return await apiWrapGet(v)
if (!v.ok) {
throw data.msg
}
return
} }
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)
}

View File

@ -19,3 +19,10 @@ interface captcha {
} }
export type ApiCaptcha = Api<captcha> export type ApiCaptcha = Api<captcha>
interface user {
uid: string
uuid: string
}
export type ApiUser = Api<user>

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

View File

@ -20,7 +20,7 @@ const CaptchaWidget = forwardRef<refType, prop>(({ onSuccess }, ref) => {
const Turnstileref = useRef<TurnstileInstance>(null) const Turnstileref = useRef<TurnstileInstance>(null)
const [key, setKey] = useState(1) 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>), { 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, () => { useImperativeHandle(ref, () => {

View File

@ -1,15 +1,28 @@
import { token, username } from "@/store/store" import { token, username } from "@/store/store"
import { useRequest } from "ahooks"
import { useAtomValue } from "jotai" import { useAtomValue } from "jotai"
import { userInfo } from '@/apis/apis'
export default function User() { export default function User() {
const nowToken = useAtomValue(token) const nowToken = useAtomValue(token)
const nowUsername = useAtomValue(username) const nowUsername = useAtomValue(username)
const { data, error } = useRequest(() => userInfo(nowToken), {
refreshDeps: [nowToken],
cacheKey: "/api/v1/user/reg",
cacheTime: 10000
})
return ( return (
<> <>
<p>: {nowUsername}</p> <p>: {nowUsername}</p>
<p>token: {nowToken} </p> <p>token: {nowToken} </p>
{error && String(error)}
{!error && <>
<p>uid: {data?.data.uid}</p>
<p>uuid: {data?.data.uuid}</p>
</>}
</> </>
) )
} }

View File

@ -1,10 +1,19 @@
package handle package handle
import ( import (
"context"
"encoding/json"
"fmt"
"io"
"log/slog" "log/slog"
"net/http"
"net/netip"
"strings"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
"github.com/samber/lo"
"github.com/xmdhs/authlib-skin/config" "github.com/xmdhs/authlib-skin/config"
"github.com/xmdhs/authlib-skin/model"
"github.com/xmdhs/authlib-skin/service" "github.com/xmdhs/authlib-skin/service"
) )
@ -23,3 +32,34 @@ func NewHandel(webService *service.WebService, validate *validator.Validate, con
logger: logger, 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
}

View File

@ -1,16 +1,13 @@
package handle package handle
import ( import (
"encoding/json"
"errors" "errors"
"fmt"
"net/http" "net/http"
"net/netip"
"github.com/julienschmidt/httprouter" "github.com/julienschmidt/httprouter"
"github.com/samber/lo"
"github.com/xmdhs/authlib-skin/model" "github.com/xmdhs/authlib-skin/model"
"github.com/xmdhs/authlib-skin/service" "github.com/xmdhs/authlib-skin/service"
utilsService "github.com/xmdhs/authlib-skin/service/utils"
"github.com/xmdhs/authlib-skin/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) handleError(ctx, w, err.Error(), model.ErrService, 500)
return return
} }
json.NewEncoder(w).Encode(model.API[any]{ encodeJson(w, model.API[any]{
Code: 0, Code: 0,
Data: nil,
Msg: "",
}) })
} }
} }
func getPrefix(ip string) (string, error) { func (h *Handel) UserInfo() httprouter.Handle {
ipa, err := netip.ParseAddr(ip) return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
if err != nil { ctx := r.Context()
return "", fmt.Errorf("getPrefix: %w", err) 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
} }

View File

@ -9,4 +9,5 @@ const (
ErrService ErrService
ErrExistUser ErrExistUser
ErrRegLimit ErrRegLimit
ErrAuth
) )

View File

@ -29,3 +29,8 @@ type Captcha struct {
Type string `json:"type"` Type string `json:"type"`
SiteKey string `json:"siteKey"` SiteKey string `json:"siteKey"`
} }
type UserInfo struct {
UID int `json:"uid"`
UUID string `json:"uuid"`
}

View File

@ -11,6 +11,10 @@ import (
func NewRoute(yggService *yggdrasil.Yggdrasil, handel *handle.Handel) (*httprouter.Router, error) { func NewRoute(yggService *yggdrasil.Yggdrasil, handel *handle.Handel) (*httprouter.Router, error) {
r := httprouter.New() r := httprouter.New()
r.HandleOPTIONS = true
r.GlobalOPTIONS = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(204)
})
err := newYggdrasil(r, *yggService) err := newYggdrasil(r, *yggService)
if err != nil { if err != nil {
return nil, fmt.Errorf("NewRoute: %w", err) return nil, fmt.Errorf("NewRoute: %w", err)
@ -19,17 +23,6 @@ func NewRoute(yggService *yggdrasil.Yggdrasil, handel *handle.Handel) (*httprout
if err != nil { if err != nil {
return nil, fmt.Errorf("NewRoute: %w", err) 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 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 { func newSkinApi(r *httprouter.Router, handel *handle.Handel) error {
r.PUT("/api/v1/user/reg", handel.Reg()) r.PUT("/api/v1/user/reg", handel.Reg())
r.GET("/api/v1/captcha", handel.GetCaptcha()) r.GET("/api/v1/captcha", handel.GetCaptcha())
r.GET("/api/v1/user", handel.UserInfo())
return nil return nil
} }

View File

@ -33,9 +33,22 @@ func NewServer(c config.Config, sl *slog.Logger, route *httprouter.Router) (*htt
if c.Debug && sl.Enabled(ctx, slog.LevelDebug) { if c.Debug && sl.Enabled(ctx, slog.LevelDebug) {
sl.DebugContext(ctx, r.Method) sl.DebugContext(ctx, r.Method)
} }
w.Header().Set("Access-Control-Allow-Origin", "*") cors(route).ServeHTTP(w, r)
route.ServeHTTP(w, r)
}), }),
} }
return s, func() { s.Close() } 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)
})
}

View File

@ -52,7 +52,7 @@ func InitializeRoute(ctx context.Context, c config.Config) (*http.Server, func()
} }
yggdrasil3 := yggdrasil2.NewYggdrasil(logger, validate, yggdrasilYggdrasil, c, pubRsaKey) yggdrasil3 := yggdrasil2.NewYggdrasil(logger, validate, yggdrasilYggdrasil, c, pubRsaKey)
httpClient := ProvideHttpClient() httpClient := ProvideHttpClient()
webService := service.NewWebService(c, client, httpClient) webService := service.NewWebService(c, client, httpClient, cache, privateKey)
handel := handle.NewHandel(webService, validate, c, logger) handel := handle.NewHandel(webService, validate, c, logger)
router, err := route.NewRoute(yggdrasil3, handel) router, err := route.NewRoute(yggdrasil3, handel)
if err != nil { if err != nil {

View File

@ -12,6 +12,8 @@ import (
"github.com/xmdhs/authlib-skin/db/ent/user" "github.com/xmdhs/authlib-skin/db/ent/user"
"github.com/xmdhs/authlib-skin/db/ent/userprofile" "github.com/xmdhs/authlib-skin/db/ent/userprofile"
"github.com/xmdhs/authlib-skin/model" "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" "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 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
}

View File

@ -1,9 +1,11 @@
package service package service
import ( import (
"crypto/rsa"
"net/http" "net/http"
"github.com/xmdhs/authlib-skin/config" "github.com/xmdhs/authlib-skin/config"
"github.com/xmdhs/authlib-skin/db/cache"
"github.com/xmdhs/authlib-skin/db/ent" "github.com/xmdhs/authlib-skin/db/ent"
) )
@ -11,12 +13,16 @@ type WebService struct {
config config.Config config config.Config
client *ent.Client client *ent.Client
httpClient *http.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{ return &WebService{
config: c, config: c,
client: e, client: e,
httpClient: hc, httpClient: hc,
cache: cache,
prikey: prikey,
} }
} }