490 lines
13 KiB
JavaScript
490 lines
13 KiB
JavaScript
/**
|
||
* 路由守卫
|
||
* 提供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 RANKING_PAGES = [
|
||
'/top-list', //排行榜
|
||
'/weekly-star', //每周明星
|
||
'/ranking', //总排行榜
|
||
'/games-king', //游戏排行榜
|
||
'/couple', //情侣排行榜
|
||
]
|
||
|
||
// 活动页面
|
||
const ACTIVITIES = [
|
||
'/invitation-to-register', //邀请新用户注册页面
|
||
'/recharge-reward', //充值奖励
|
||
|
||
'/new-year', //新年抽奖
|
||
'/jack-pot', //水果游戏打榜
|
||
]
|
||
|
||
const publicPages = [
|
||
PAGES.APPLY, // 申请页面 - 重要:申请页面不需要权限检查
|
||
PAGES.NOT_APP, // 错误页面 - APP连接失败时的页面
|
||
PAGES.MAP, // 地图选择页面
|
||
...RANKING_PAGES, //排行榜
|
||
...ACTIVITIES, // 活动页面
|
||
'/404', // 404页面
|
||
'/error', // 通用错误页面
|
||
|
||
'/recharge', //搜索充值对象页面
|
||
'/recharge-pay-way', //充值方式页面
|
||
'/recharge-guide', //充值引导页面
|
||
'/recharge-agency-recruit', //充值代理介绍页面
|
||
|
||
'/pay-result', //支付结果页
|
||
'/search-payee', //搜索收款人
|
||
'/cash-out', // 提现页面
|
||
'/cash-out-details', //提现详情页面
|
||
'/KYC', //提现资料提交
|
||
'/bank-card', //选择银行卡
|
||
|
||
'/good-ID', //靓号申请页
|
||
]
|
||
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()
|