mirror of
https://github.com/woodchen-ink/random-api-go.git
synced 2025-07-18 05:42:01 +08:00
重构Handlers结构,新增HandlePublicEndpoints方法以处理公开端点信息请求,优化URL统计缓存逻辑,更新Router以支持新路由,简化认证信息管理,移除不再使用的刷新令牌逻辑。
This commit is contained in:
parent
0465adbb36
commit
5176cc85db
@ -12,6 +12,7 @@ import (
|
|||||||
"random-api-go/stats"
|
"random-api-go/stats"
|
||||||
"random-api-go/utils"
|
"random-api-go/utils"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -22,6 +23,19 @@ type Router interface {
|
|||||||
type Handlers struct {
|
type Handlers struct {
|
||||||
Stats *stats.StatsManager
|
Stats *stats.StatsManager
|
||||||
endpointService *services.EndpointService
|
endpointService *services.EndpointService
|
||||||
|
urlStatsCache map[string]struct {
|
||||||
|
TotalURLs int `json:"total_urls"`
|
||||||
|
}
|
||||||
|
urlStatsCacheTime time.Time
|
||||||
|
urlStatsMutex sync.RWMutex
|
||||||
|
cacheDuration time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHandlers(statsManager *stats.StatsManager) *Handlers {
|
||||||
|
return &Handlers{
|
||||||
|
Stats: statsManager,
|
||||||
|
cacheDuration: 5 * time.Minute, // 缓存5分钟
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handlers) HandleAPIRequest(w http.ResponseWriter, r *http.Request) {
|
func (h *Handlers) HandleAPIRequest(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -116,10 +130,82 @@ func (h *Handlers) HandleStats(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handlers) HandlePublicEndpoints(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != http.MethodGet {
|
||||||
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
// 使用端点服务获取端点信息
|
||||||
|
if h.endpointService == nil {
|
||||||
|
h.endpointService = services.GetEndpointService()
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoints, err := h.endpointService.ListEndpoints()
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Error getting endpoints", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只返回公开信息,不包含数据源配置
|
||||||
|
type PublicEndpoint struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
IsActive bool `json:"is_active"`
|
||||||
|
ShowOnHomepage bool `json:"show_on_homepage"`
|
||||||
|
SortOrder int `json:"sort_order"`
|
||||||
|
CreatedAt string `json:"created_at"`
|
||||||
|
UpdatedAt string `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var publicEndpoints []PublicEndpoint
|
||||||
|
for _, endpoint := range endpoints {
|
||||||
|
publicEndpoints = append(publicEndpoints, PublicEndpoint{
|
||||||
|
ID: endpoint.ID,
|
||||||
|
Name: endpoint.Name,
|
||||||
|
URL: endpoint.URL,
|
||||||
|
Description: endpoint.Description,
|
||||||
|
IsActive: endpoint.IsActive,
|
||||||
|
ShowOnHomepage: endpoint.ShowOnHomepage,
|
||||||
|
SortOrder: endpoint.SortOrder,
|
||||||
|
CreatedAt: endpoint.CreatedAt.Format(time.RFC3339),
|
||||||
|
UpdatedAt: endpoint.UpdatedAt.Format(time.RFC3339),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
response := map[string]interface{}{
|
||||||
|
"success": true,
|
||||||
|
"data": publicEndpoints,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||||||
|
http.Error(w, "Error encoding response", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (h *Handlers) HandleURLStats(w http.ResponseWriter, r *http.Request) {
|
func (h *Handlers) HandleURLStats(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
// 使用新的端点服务获取统计信息
|
// 检查缓存是否有效
|
||||||
|
h.urlStatsMutex.RLock()
|
||||||
|
if h.urlStatsCache != nil && time.Since(h.urlStatsCacheTime) < h.cacheDuration {
|
||||||
|
// 使用缓存数据
|
||||||
|
cache := h.urlStatsCache
|
||||||
|
h.urlStatsMutex.RUnlock()
|
||||||
|
|
||||||
|
if err := json.NewEncoder(w).Encode(cache); err != nil {
|
||||||
|
http.Error(w, "Error encoding response", http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
h.urlStatsMutex.RUnlock()
|
||||||
|
|
||||||
|
// 缓存过期或不存在,重新计算
|
||||||
if h.endpointService == nil {
|
if h.endpointService == nil {
|
||||||
h.endpointService = services.GetEndpointService()
|
h.endpointService = services.GetEndpointService()
|
||||||
}
|
}
|
||||||
@ -166,6 +252,12 @@ func (h *Handlers) HandleURLStats(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新缓存
|
||||||
|
h.urlStatsMutex.Lock()
|
||||||
|
h.urlStatsCache = response
|
||||||
|
h.urlStatsCacheTime = time.Now()
|
||||||
|
h.urlStatsMutex.Unlock()
|
||||||
|
|
||||||
if err := json.NewEncoder(w).Encode(response); err != nil {
|
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||||||
http.Error(w, "Error encoding response", http.StatusInternalServerError)
|
http.Error(w, "Error encoding response", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
@ -186,4 +278,7 @@ func (h *Handlers) Setup(r *router.Router) {
|
|||||||
r.HandleFunc("/api/stats", h.HandleStats)
|
r.HandleFunc("/api/stats", h.HandleStats)
|
||||||
r.HandleFunc("/api/urlstats", h.HandleURLStats)
|
r.HandleFunc("/api/urlstats", h.HandleURLStats)
|
||||||
r.HandleFunc("/api/metrics", h.HandleMetrics)
|
r.HandleFunc("/api/metrics", h.HandleMetrics)
|
||||||
|
|
||||||
|
// 公开的端点信息接口
|
||||||
|
r.HandleFunc("/api/endpoints", h.HandlePublicEndpoints)
|
||||||
}
|
}
|
||||||
|
4
main.go
4
main.go
@ -81,9 +81,7 @@ func (a *App) Initialize() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 创建 handlers
|
// 创建 handlers
|
||||||
handlers := &handlers.Handlers{
|
handlers := handlers.NewHandlers(a.Stats)
|
||||||
Stats: a.Stats,
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置路由
|
// 设置路由
|
||||||
a.router.Setup(handlers)
|
a.router.Setup(handlers)
|
||||||
|
90
middleware/auth.go
Normal file
90
middleware/auth.go
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UserInfo OAuth用户信息结构
|
||||||
|
type UserInfo struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Nickname string `json:"nickname"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
Avatar string `json:"avatar"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthMiddleware 认证中间件
|
||||||
|
type AuthMiddleware struct{}
|
||||||
|
|
||||||
|
// NewAuthMiddleware 创建新的认证中间件
|
||||||
|
func NewAuthMiddleware() *AuthMiddleware {
|
||||||
|
return &AuthMiddleware{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequireAuth 认证中间件,验证 OAuth 令牌
|
||||||
|
func (am *AuthMiddleware) RequireAuth(next http.HandlerFunc) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// 从 Authorization header 获取令牌
|
||||||
|
authHeader := r.Header.Get("Authorization")
|
||||||
|
if authHeader == "" {
|
||||||
|
http.Error(w, "Authorization header required", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 Bearer 前缀
|
||||||
|
if !strings.HasPrefix(authHeader, "Bearer ") {
|
||||||
|
http.Error(w, "Invalid authorization header format", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
token := strings.TrimPrefix(authHeader, "Bearer ")
|
||||||
|
if token == "" {
|
||||||
|
http.Error(w, "Token required", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证令牌(通过调用用户信息接口)
|
||||||
|
userInfo, err := am.getUserInfo(token)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Token validation failed: %v", err)
|
||||||
|
http.Error(w, "Invalid token", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 令牌有效,继续处理请求
|
||||||
|
log.Printf("Authenticated user: %s (%s)", userInfo.Username, userInfo.Email)
|
||||||
|
next(w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getUserInfo 通过访问令牌获取用户信息
|
||||||
|
func (am *AuthMiddleware) getUserInfo(accessToken string) (*UserInfo, error) {
|
||||||
|
req, err := http.NewRequest("GET", "https://connect.czl.net/api/oauth2/userinfo", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Authorization", "Bearer "+accessToken)
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get user info: %w", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("failed to get user info, status: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
var userInfo UserInfo
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&userInfo); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode user info: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &userInfo, nil
|
||||||
|
}
|
@ -2,14 +2,17 @@ package router
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"random-api-go/middleware"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Router struct {
|
type Router struct {
|
||||||
mux *http.ServeMux
|
mux *http.ServeMux
|
||||||
staticHandler StaticHandler
|
staticHandler StaticHandler
|
||||||
|
authMiddleware *middleware.AuthMiddleware
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handler 接口定义处理器需要的方法
|
||||||
type Handler interface {
|
type Handler interface {
|
||||||
Setup(r *Router)
|
Setup(r *Router)
|
||||||
}
|
}
|
||||||
@ -54,7 +57,8 @@ type AdminHandler interface {
|
|||||||
|
|
||||||
func New() *Router {
|
func New() *Router {
|
||||||
return &Router{
|
return &Router{
|
||||||
mux: http.NewServeMux(),
|
mux: http.NewServeMux(),
|
||||||
|
authMiddleware: middleware.NewAuthMiddleware(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,44 +74,44 @@ func (r *Router) SetupStaticRoutes(staticHandler StaticHandler) {
|
|||||||
|
|
||||||
// SetupAdminRoutes 设置管理后台路由
|
// SetupAdminRoutes 设置管理后台路由
|
||||||
func (r *Router) SetupAdminRoutes(adminHandler AdminHandler) {
|
func (r *Router) SetupAdminRoutes(adminHandler AdminHandler) {
|
||||||
// OAuth配置API(前端需要获取client_id等信息)
|
// OAuth配置API(前端需要获取client_id等信息)- 不需要认证
|
||||||
r.HandleFunc("/api/admin/oauth-config", adminHandler.GetOAuthConfig)
|
r.HandleFunc("/api/admin/oauth-config", adminHandler.GetOAuthConfig)
|
||||||
// OAuth令牌验证API(保留,以防需要)
|
// OAuth令牌验证API(保留,以防需要)- 不需要认证
|
||||||
r.HandleFunc("/api/admin/oauth-verify", adminHandler.VerifyOAuthToken)
|
r.HandleFunc("/api/admin/oauth-verify", adminHandler.VerifyOAuthToken)
|
||||||
// OAuth回调处理(使用API前缀以便区分前后端)
|
// OAuth回调处理(使用API前缀以便区分前后端)- 不需要认证
|
||||||
r.HandleFunc("/api/admin/oauth/callback", adminHandler.HandleOAuthCallback)
|
r.HandleFunc("/api/admin/oauth/callback", adminHandler.HandleOAuthCallback)
|
||||||
|
|
||||||
// 管理后台API路由
|
// 管理后台API路由 - 需要认证
|
||||||
r.HandleFunc("/api/admin/endpoints", adminHandler.HandleEndpoints)
|
r.HandleFunc("/api/admin/endpoints", r.authMiddleware.RequireAuth(adminHandler.HandleEndpoints))
|
||||||
|
|
||||||
// 端点排序路由
|
// 端点排序路由 - 需要认证
|
||||||
r.HandleFunc("/api/admin/endpoints/sort-order", adminHandler.UpdateEndpointSortOrder)
|
r.HandleFunc("/api/admin/endpoints/sort-order", r.authMiddleware.RequireAuth(adminHandler.UpdateEndpointSortOrder))
|
||||||
|
|
||||||
// 数据源路由 - 需要在端点路由之前注册,因为路径更具体
|
// 数据源路由 - 需要认证
|
||||||
r.HandleFunc("/api/admin/data-sources", adminHandler.CreateDataSource)
|
r.HandleFunc("/api/admin/data-sources", r.authMiddleware.RequireAuth(adminHandler.CreateDataSource))
|
||||||
|
|
||||||
// 端点相关路由 - 使用通配符处理所有端点相关请求
|
// 端点相关路由 - 需要认证
|
||||||
r.HandleFunc("/api/admin/endpoints/", func(w http.ResponseWriter, r *http.Request) {
|
r.HandleFunc("/api/admin/endpoints/", r.authMiddleware.RequireAuth(func(w http.ResponseWriter, r *http.Request) {
|
||||||
path := r.URL.Path
|
path := r.URL.Path
|
||||||
if strings.Contains(path, "/data-sources") {
|
if strings.Contains(path, "/data-sources") {
|
||||||
adminHandler.HandleEndpointDataSources(w, r)
|
adminHandler.HandleEndpointDataSources(w, r)
|
||||||
} else {
|
} else {
|
||||||
adminHandler.HandleEndpointByID(w, r)
|
adminHandler.HandleEndpointByID(w, r)
|
||||||
}
|
}
|
||||||
})
|
}))
|
||||||
|
|
||||||
// 数据源操作路由 - 使用通配符处理所有数据源相关请求
|
// 数据源操作路由 - 需要认证
|
||||||
r.HandleFunc("/api/admin/data-sources/", func(w http.ResponseWriter, r *http.Request) {
|
r.HandleFunc("/api/admin/data-sources/", r.authMiddleware.RequireAuth(func(w http.ResponseWriter, r *http.Request) {
|
||||||
path := r.URL.Path
|
path := r.URL.Path
|
||||||
if strings.Contains(path, "/sync") {
|
if strings.Contains(path, "/sync") {
|
||||||
adminHandler.SyncDataSource(w, r)
|
adminHandler.SyncDataSource(w, r)
|
||||||
} else {
|
} else {
|
||||||
adminHandler.HandleDataSourceByID(w, r)
|
adminHandler.HandleDataSourceByID(w, r)
|
||||||
}
|
}
|
||||||
})
|
}))
|
||||||
|
|
||||||
// URL替换规则路由
|
// URL替换规则路由 - 需要认证
|
||||||
r.HandleFunc("/api/admin/url-replace-rules", func(w http.ResponseWriter, r *http.Request) {
|
r.HandleFunc("/api/admin/url-replace-rules", r.authMiddleware.RequireAuth(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method == http.MethodGet {
|
if r.Method == http.MethodGet {
|
||||||
adminHandler.ListURLReplaceRules(w, r)
|
adminHandler.ListURLReplaceRules(w, r)
|
||||||
} else if r.Method == http.MethodPost {
|
} else if r.Method == http.MethodPost {
|
||||||
@ -115,27 +119,27 @@ func (r *Router) SetupAdminRoutes(adminHandler AdminHandler) {
|
|||||||
} else {
|
} else {
|
||||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||||
}
|
}
|
||||||
})
|
}))
|
||||||
r.HandleFunc("/api/admin/url-replace-rules/", adminHandler.HandleURLReplaceRuleByID)
|
r.HandleFunc("/api/admin/url-replace-rules/", r.authMiddleware.RequireAuth(adminHandler.HandleURLReplaceRuleByID))
|
||||||
|
|
||||||
// 首页配置路由
|
// 首页配置路由 - 需要认证
|
||||||
r.HandleFunc("/api/admin/home-config", func(w http.ResponseWriter, r *http.Request) {
|
r.HandleFunc("/api/admin/home-config", r.authMiddleware.RequireAuth(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method == http.MethodGet {
|
if r.Method == http.MethodGet {
|
||||||
adminHandler.GetHomePageConfig(w, r)
|
adminHandler.GetHomePageConfig(w, r)
|
||||||
} else {
|
} else {
|
||||||
adminHandler.UpdateHomePageConfig(w, r)
|
adminHandler.UpdateHomePageConfig(w, r)
|
||||||
}
|
}
|
||||||
})
|
}))
|
||||||
|
|
||||||
// 通用配置管理路由
|
// 通用配置管理路由 - 需要认证
|
||||||
r.HandleFunc("/api/admin/configs", adminHandler.ListConfigs)
|
r.HandleFunc("/api/admin/configs", r.authMiddleware.RequireAuth(adminHandler.ListConfigs))
|
||||||
r.HandleFunc("/api/admin/configs/", func(w http.ResponseWriter, r *http.Request) {
|
r.HandleFunc("/api/admin/configs/", r.authMiddleware.RequireAuth(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method == http.MethodDelete {
|
if r.Method == http.MethodDelete {
|
||||||
adminHandler.DeleteConfigByKey(w, r)
|
adminHandler.DeleteConfigByKey(w, r)
|
||||||
} else {
|
} else {
|
||||||
adminHandler.CreateOrUpdateConfig(w, r)
|
adminHandler.CreateOrUpdateConfig(w, r)
|
||||||
}
|
}
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) {
|
func (r *Router) HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) {
|
||||||
|
@ -86,7 +86,7 @@ async function getSystemMetrics(): Promise<SystemMetrics | null> {
|
|||||||
|
|
||||||
async function getEndpoints() {
|
async function getEndpoints() {
|
||||||
try {
|
try {
|
||||||
const res = await apiFetch('/api/admin/endpoints')
|
const res = await apiFetch('/api/endpoints')
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
throw new Error('Failed to fetch endpoints')
|
throw new Error('Failed to fetch endpoints')
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import Cookies from 'js-cookie'
|
import Cookies from 'js-cookie'
|
||||||
|
|
||||||
const TOKEN_COOKIE_NAME = 'admin_token'
|
const TOKEN_COOKIE_NAME = 'admin_token'
|
||||||
const REFRESH_TOKEN_COOKIE_NAME = 'admin_refresh_token'
|
|
||||||
const USER_INFO_COOKIE_NAME = 'admin_user'
|
const USER_INFO_COOKIE_NAME = 'admin_user'
|
||||||
|
|
||||||
// Cookie配置
|
// Cookie配置
|
||||||
@ -19,13 +18,9 @@ export interface AuthUser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 保存认证信息
|
// 保存认证信息
|
||||||
export function saveAuthInfo(token: string, user: AuthUser, refreshToken?: string) {
|
export function saveAuthInfo(token: string, user: AuthUser) {
|
||||||
Cookies.set(TOKEN_COOKIE_NAME, token, COOKIE_OPTIONS)
|
Cookies.set(TOKEN_COOKIE_NAME, token, COOKIE_OPTIONS)
|
||||||
Cookies.set(USER_INFO_COOKIE_NAME, JSON.stringify(user), COOKIE_OPTIONS)
|
Cookies.set(USER_INFO_COOKIE_NAME, JSON.stringify(user), COOKIE_OPTIONS)
|
||||||
|
|
||||||
if (refreshToken) {
|
|
||||||
Cookies.set(REFRESH_TOKEN_COOKIE_NAME, refreshToken, COOKIE_OPTIONS)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取访问令牌
|
// 获取访问令牌
|
||||||
@ -33,11 +28,6 @@ export function getAccessToken(): string | null {
|
|||||||
return Cookies.get(TOKEN_COOKIE_NAME) || null
|
return Cookies.get(TOKEN_COOKIE_NAME) || null
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取刷新令牌
|
|
||||||
export function getRefreshToken(): string | null {
|
|
||||||
return Cookies.get(REFRESH_TOKEN_COOKIE_NAME) || null
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取用户信息
|
// 获取用户信息
|
||||||
export function getUserInfo(): AuthUser | null {
|
export function getUserInfo(): AuthUser | null {
|
||||||
const userStr = Cookies.get(USER_INFO_COOKIE_NAME)
|
const userStr = Cookies.get(USER_INFO_COOKIE_NAME)
|
||||||
@ -53,7 +43,6 @@ export function getUserInfo(): AuthUser | null {
|
|||||||
// 清除认证信息
|
// 清除认证信息
|
||||||
export function clearAuthInfo() {
|
export function clearAuthInfo() {
|
||||||
Cookies.remove(TOKEN_COOKIE_NAME, { path: '/' })
|
Cookies.remove(TOKEN_COOKIE_NAME, { path: '/' })
|
||||||
Cookies.remove(REFRESH_TOKEN_COOKIE_NAME, { path: '/' })
|
|
||||||
Cookies.remove(USER_INFO_COOKIE_NAME, { path: '/' })
|
Cookies.remove(USER_INFO_COOKIE_NAME, { path: '/' })
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,63 +67,20 @@ export async function authenticatedFetch(url: string, options: RequestInit = {})
|
|||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
...options,
|
...options,
|
||||||
headers,
|
headers,
|
||||||
credentials: 'include', // 包含cookie
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// 如果token过期,尝试刷新
|
// 如果token过期或无效,清除认证信息并重定向到登录
|
||||||
if (response.status === 401) {
|
if (response.status === 401) {
|
||||||
const refreshed = await refreshAccessToken()
|
|
||||||
if (refreshed) {
|
|
||||||
// 重新发送请求
|
|
||||||
const newToken = getAccessToken()
|
|
||||||
if (newToken) {
|
|
||||||
headers['Authorization'] = `Bearer ${newToken}`
|
|
||||||
return fetch(url, {
|
|
||||||
...options,
|
|
||||||
headers,
|
|
||||||
credentials: 'include',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 刷新失败,清除认证信息
|
|
||||||
clearAuthInfo()
|
clearAuthInfo()
|
||||||
|
// 可以选择重定向到登录页面或显示登录提示
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.location.href = '/admin'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
// 刷新访问令牌
|
|
||||||
async function refreshAccessToken(): Promise<boolean> {
|
|
||||||
const refreshToken = getRefreshToken()
|
|
||||||
if (!refreshToken) return false
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch('/api/admin/auth/refresh', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ refresh_token: refreshToken }),
|
|
||||||
credentials: 'include',
|
|
||||||
})
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
const data = await response.json()
|
|
||||||
if (data.success && data.data.access_token) {
|
|
||||||
const user = getUserInfo()
|
|
||||||
if (user) {
|
|
||||||
saveAuthInfo(data.data.access_token, user, data.data.refresh_token)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to refresh token:', error)
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// OAuth状态管理
|
// OAuth状态管理
|
||||||
export function saveOAuthState(state: string) {
|
export function saveOAuthState(state: string) {
|
||||||
sessionStorage.setItem('oauth_state', state)
|
sessionStorage.setItem('oauth_state', state)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user