基础信息展示

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) {
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 await apiWrapGet(v)
}
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>
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 [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, () => {

View File

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

View File

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

View File

@ -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)
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 {
return "", fmt.Errorf("getPrefix: %w", err)
if errors.Is(err, utilsService.ErrTokenInvalid) {
h.logger.DebugContext(ctx, "token 无效")
handleError(ctx, w, "token 无效", model.ErrAuth, 401)
return
}
if ipa.Is6() {
return lo.Must1(ipa.Prefix(48)).String(), nil
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,
})
}
return lo.Must1(ipa.Prefix(24)).String(), nil
}

View File

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

View File

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

View File

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

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) {
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)
})
}

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

View File

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

View File

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