aslan-h5/src/utils/routeGuard.js
2025-11-17 15:06:42 +08:00

480 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 路由守卫
* 提供APP连接检查、页面访问权限控制和自动重定向功能
*/
import { permissionManager, PAGES, USER_ROLES } from './permissionManager.js'
import { getUserIdentity, getMemberProfile } from '../api/wallet.js'
import { getUserId, setUserInfo } from './userStore.js'
import {
connectApplication,
parseAccessOrigin,
parseHeader,
setHttpHeaders,
isInApp,
} from './appBridge.js'
import { appConnectionManager, isAppConnected } from './appConnectionManager.js'
/**
* APP连接检查器
*/
class AppConnectionChecker {
constructor() {
this.isConnecting = false
}
/**
* 确保APP连接
* @returns {Promise<boolean>} 连接是否成功
*/
async ensureConnection() {
console.debug('🔗 Checking APP connection...')
// 如果不在APP环境中直接返回成功
console.log('我是app环境', window.app)
console.log('我是app环境', window.webkit)
console.log('我是app环境', window.FlutterPageControl)
if (!isInApp()) {
console.debug('🌐 Browser environment, no connection needed')
return true
}
// 如果已经连接且未超时,直接返回成功
if (isAppConnected()) {
console.debug('✅ APP already connected')
return true
}
// 如果正在连接中,等待现有连接
if (appConnectionManager.isConnecting()) {
console.debug('⏳ Connection already in progress, waiting...')
try {
await appConnectionManager.getConnectionPromise()
return isAppConnected()
} catch (error) {
console.error('❌ Connection wait failed:', error)
return false
}
}
// 开始新的连接
return await this._performConnection()
}
/**
* 执行APP连接
* @returns {Promise<boolean>}
*/
async _performConnection() {
try {
console.debug('📱 Starting new APP connection...')
const connectionPromise = new Promise((resolve, reject) => {
connectApplication(async (access) => {
try {
const result = parseAccessOrigin(access)
if (result.success) {
// 解析头部信息
const headerInfo = parseHeader(result.data)
// 设置HTTP请求头
await setHttpHeaders(headerInfo)
// 更新连接状态
appConnectionManager.setConnected(headerInfo)
console.debug('🎉 APP connection established successfully')
// APP连接成功后立即获取用户信息
await this._fetchUserInfoAfterConnection()
resolve(true)
} else {
console.error('❌ Failed to parse access origin:', result.error)
appConnectionManager.reset()
reject(new Error(result.error))
}
} catch (error) {
console.error('❌ Connection process failed:', error)
appConnectionManager.reset()
reject(error)
}
})
})
// 设置连接状态
appConnectionManager.setConnecting(connectionPromise)
// 等待连接完成
return await connectionPromise
} catch (error) {
console.error('❌ APP connection failed:', error)
appConnectionManager.reset()
return false
}
}
/**
* APP连接成功后获取用户信息
* @private
*/
async _fetchUserInfoAfterConnection() {
try {
console.debug('👤 Fetching user info after connection...')
const response = await getMemberProfile()
if (response && response.status && response.body) {
// 缓存用户信息
console.debug('✅ User info cached successfully')
setUserInfo(response.body, null)
} else {
console.warn('⚠️ Failed to get team entry or invalid response')
}
} catch (error) {
// 这里不抛出错误因为APP连接已经成功
// 用户信息获取失败可以在后续的身份检查中处理
console.warn('⚠️ Failed to fetch user info after connection:', error)
}
}
/**
* 重置连接状态
*/
reset() {
appConnectionManager.reset()
}
}
/**
* 身份检查器
*/
class IdentityChecker {
constructor() {
this.isChecking = false
this.checkPromise = null
}
/**
* 检查并设置用户身份
* @param {string} userId - 用户ID
* @returns {Promise<string>} 返回主要身份
*/
async checkUserIdentity(userId) {
if (!userId) {
console.warn('⚠️ No user ID provided')
permissionManager.setUserIdentity(null)
return USER_ROLES.GUEST
}
// 如果正在检查中,等待现有的检查完成
if (this.isChecking && this.checkPromise) {
console.debug('⏳ Identity check already in progress, waiting...')
return await this.checkPromise
}
this.isChecking = true
this.checkPromise = this._performIdentityCheck(userId)
try {
const primaryRole = await this.checkPromise
return primaryRole
} finally {
this.isChecking = false
this.checkPromise = null
}
}
/**
* 执行实际的身份检查
* @param {string} userId - 用户ID
* @returns {Promise<string>} 主要身份
*/
async _performIdentityCheck(userId) {
try {
console.debug('🔍 Checking user identity for:', userId)
const response = await getUserIdentity()
if (response && response.status && response.body) {
const identity = response.body
console.debug('✅ Identity fetched:', identity)
// 设置权限信息
permissionManager.setUserIdentity(identity)
return permissionManager.getPrimaryRole()
} else {
console.warn('⚠️ Invalid identity response')
permissionManager.setUserIdentity(null)
return USER_ROLES.GUEST
}
} catch (error) {
console.error('❌ Failed to fetch user identity:', error)
permissionManager.setUserIdentity(null)
return USER_ROLES.GUEST
}
}
/**
* 重置检查状态
*/
reset() {
this.isChecking = false
this.checkPromise = null
}
}
/**
* 页面重定向器
*/
class PageRedirector {
/**
* 根据身份自动重定向到正确页面
* @param {Object} router - Vue Router 实例
* @param {string} targetPath - 目标页面路径
* @param {string} currentPath - 当前页面路径
* @returns {Promise<boolean>} 是否进行了重定向
*/
async redirectToCorrectPage(router, targetPath, currentPath) {
const primaryRole = permissionManager.getPrimaryRole()
// 检查目标页面权限
const hasPermission = permissionManager.hasPagePermission(targetPath)
console.debug('🔄 Redirect check:', {
targetPath,
currentPath,
primaryRole,
hasPermission,
})
if (hasPermission) {
// 有权限访问目标页面
if (targetPath !== currentPath) {
console.debug('✅ Access granted, navigating to:', targetPath)
return false // 不需要重定向,让路由正常进行
}
return false
} else {
// 无权限访问,重定向到默认页面
const defaultPage = permissionManager.getDefaultPage()
console.debug('❌ Access denied, redirecting to:', defaultPage)
if (defaultPage !== currentPath) {
await router.replace(defaultPage)
return true // 已进行重定向
}
return false
}
}
/**
* 处理越权访问
* @param {Object} router - Vue Router 实例
* @param {string} attemptedPage - 尝试访问的页面
*/
async handleUnauthorizedAccess(router, attemptedPage) {
const primaryRole = permissionManager.getPrimaryRole()
const defaultPage = permissionManager.getDefaultPage()
console.warn('🚫 Unauthorized access attempt:', {
attemptedPage,
primaryRole,
redirectingTo: defaultPage,
})
// 重定向到用户有权限的默认页面
await router.replace(defaultPage)
}
}
/**
* 路由守卫类
*/
class RouteGuard {
constructor() {
this.appConnectionChecker = new AppConnectionChecker()
this.identityChecker = new IdentityChecker()
this.pageRedirector = new PageRedirector()
this.isGuardActive = false
}
/**
* 安装路由守卫
* @param {Object} router - Vue Router 实例
*/
install(router) {
router.beforeEach(async (to, from, next) => {
await this.handleRouteChange(to, from, next)
})
this.isGuardActive = true
console.debug('🛡️ Route guard installed')
}
/**
* 处理路由变化
* @param {Object} to - 目标路由
* @param {Object} from - 来源路由
* @param {Function} next - 路由继续函数
*/
async handleRouteChange(to, from, next) {
console.group('🛡️ Route Guard Check')
console.debug('📍 Route change:', from.path, '→', to.path)
try {
// 1. 检查是否为公共页面(无需任何验证)
if (this.isPublicPage(to.path)) {
console.debug('🌐 Public page, allowing access')
console.groupEnd()
next()
return
}
// 2. 确保APP连接
console.debug('🔗 Ensuring APP connection...')
const isConnected = await this.appConnectionChecker.ensureConnection()
if (!isConnected) {
console.error('❌ APP connection failed, redirecting to error page')
console.groupEnd()
next(PAGES.NOT_APP)
return
}
// 3. 检查用户身份是否已加载
if (!permissionManager.isLoaded()) {
console.debug('🔍 Identity not loaded, checking...')
const userId = getUserId()
if (!userId) {
console.debug('❌ No user ID available, redirecting to apply')
console.groupEnd()
next(PAGES.APPLY)
return
}
// 检查用户身份
await this.identityChecker.checkUserIdentity(userId)
}
// 4. 检查页面访问权限
const hasPermission = permissionManager.hasPagePermission(to.path)
if (hasPermission) {
console.debug('✅ Permission granted, allowing access')
console.groupEnd()
next()
} else {
console.debug('❌ Permission denied')
const defaultPage = permissionManager.getDefaultPage()
console.debug('🔄 Redirecting to default page:', defaultPage)
console.groupEnd()
next(defaultPage)
}
} catch (error) {
console.error('❌ Route guard error:', error)
console.groupEnd()
// 根据错误类型决定重定向目标
if (error.message && error.message.includes('connection')) {
next(PAGES.NOT_APP) // 连接相关错误
} else {
next(PAGES.APPLY) // 其他错误
}
}
}
/**
* 检查是否为公共页面无需APP连接和权限验证
* @param {string} path - 页面路径
* @returns {boolean}
*/
isPublicPage(path) {
// 活动页面
const ACTIVITIES = [
'/top-list', //排行榜
'/weekly-star', //每周明星
'/ranking', //总排行榜
'/halloween', //万圣节活动页面
'/lottery', // 抽奖活动页面
'/recharge-reward', //充值奖励页面
'/invitation-to-register', //邀请新用户注册页面
'/revolution-unity-day', //孟加拉国民革命与团结日
'/heroes-day', //印度尼西亚英雄日
'/independence-day', //巴勒斯坦独立日
'/oman-national-day', //阿曼国庆日
]
const publicPages = [
PAGES.APPLY, // 申请页面 - 重要:申请页面不需要权限检查
PAGES.NOT_APP, // 错误页面 - APP连接失败时的页面
PAGES.MAP, // 地图选择页面
...ACTIVITIES, // 活动页面
'/404', // 404页面
'/error', // 通用错误页面
'/recharge',
'/recharge-freight-agent',
'/pay-result',
'/recharge-agency-recruit', //充值代理介绍页面
'/cash-out', // 提现页面
'/cash-out-details', //提现详情页面
'/KYC', //提现资料提交
'/bank-card', //选择银行卡
]
return publicPages.includes(path)
}
/**
* 手动触发身份检查和重定向
* @param {Object} router - Vue Router 实例
* @param {string} userId - 用户ID
*/
async checkAndRedirect(router, userId) {
try {
console.debug('🔄 Manual identity check and redirect')
// 检查身份
const primaryRole = await this.identityChecker.checkUserIdentity(userId)
// 获取当前页面
const currentPath = router.currentRoute.value.path
// 检查当前页面权限
const hasPermission = permissionManager.hasPagePermission(currentPath)
if (!hasPermission) {
// 重定向到默认页面
await this.pageRedirector.redirectToCorrectPage(router, currentPath, currentPath)
}
return primaryRole
} catch (error) {
console.error('❌ Manual check and redirect failed:', error)
throw error
}
}
/**
* 重置守卫状态
*/
reset() {
this.appConnectionChecker.reset()
this.identityChecker.reset()
permissionManager.reset()
console.debug('🔄 Route guard reset')
}
}
// 创建全局路由守卫实例
export const routeGuard = new RouteGuard()
// 便捷方法
export const installRouteGuard = (router) => routeGuard.install(router)
export const checkAndRedirect = (router, userId) => routeGuard.checkAndRedirect(router, userId)
export const resetRouteGuard = () => routeGuard.reset()