@ -16,18 +16,18 @@ export const Level = {
Admin : 7 , // 111 管理员 (完全控制)
Admin : 7 , // 111 管理员 (完全控制)
} ;
} ;
const REFRESH _INTERVAL = 12 * 60 * 1000 ; // 12 分钟( access_token 15min 过期)
class VBase {
class VBase {
constructor ( scope , baseURL , login _page , users = { } ) {
constructor ( baseURL , login _page , users = { } ) {
if ( ! scope ) throw new Error ( 'VBase: scope is required' ) ;
if ( ! baseURL ) baseURL = window . location . origin ;
if ( ! baseURL ) baseURL = window . location . origin ;
if ( baseURL === '' || baseURL === '/' ) baseURL = window . location . origin ;
if ( baseURL === '' || baseURL === '/' ) baseURL = window . location . origin ;
this . baseURL = baseURL ;
this . baseURL = baseURL ;
this . scope = scope ;
this . login _page = login _page || ( baseURL + '/login' ) ;
this . login _page = login _page || ( baseURL + '/login' ) ;
this . userKey = 'vbase_user' ;
this . userKey = 'vbase_user' ;
this . refreshTimer = null ;
this . users = users ;
this . users = users ;
this . _user = null ;
this . _user = null ;
try {
try {
@ -36,15 +36,58 @@ class VBase {
this . _user = cached ;
this . _user = cached ;
this . _cachePublicUser ( cached ) ;
this . _cachePublicUser ( cached ) ;
}
}
} catch ( e ) { }
} catch ( e ) { }
this . _pendingUserIDs = new Set ( ) ;
this . _pendingUserIDs = new Set ( ) ;
this . _loadingUserIDs = new Set ( ) ;
this . _loadingUserIDs = new Set ( ) ;
this . _resolvedUserIDs = new Set ( ) ;
this . _resolvedUserIDs = new Set ( ) ;
this . _pendingUserFlush = null ;
this . _pendingUserFlush = null ;
// 验证登录状态
// 初始化鉴权:有用户缓存就验证 + 定时刷新
this . fetchUser ( ) . catch ( ( ) => { } ) ;
if ( this . _user ) {
this . _ensureAuth ( ) ;
}
}
async _ensureAuth ( ) {
const now = Date . now ( ) ;
const lastRefresh = parseInt ( localStorage . getItem ( 'vbase_last_refresh' ) , 10 ) || 0 ;
// 超过 12 分钟没刷新,先刷新再拉用户
if ( now - lastRefresh > REFRESH _INTERVAL ) {
const ok = await this . refresh ( ) ;
if ( ! ok ) {
this . clear ( ) ;
return ;
}
}
// 无用户信息或刷新后重新拉
try {
await this . fetchUser ( ) ;
} catch ( e ) {
this . clear ( ) ;
return ;
}
// 启动定时刷新
this . _startRefreshTimer ( ) ;
}
_startRefreshTimer ( ) {
this . _stopRefreshTimer ( ) ;
this . refreshTimer = setInterval ( ( ) => {
this . refresh ( ) . then ( ok => {
if ( ! ok ) this . clear ( ) ;
} ) ;
} , REFRESH _INTERVAL ) ;
}
_stopRefreshTimer ( ) {
if ( this . refreshTimer ) {
clearInterval ( this . refreshTimer ) ;
this . refreshTimer = null ;
}
}
}
// ========== Getters / Setters ==========
// ========== Getters / Setters ==========
@ -57,10 +100,15 @@ class VBase {
localStorage . setItem ( this . userKey , JSON . stringify ( val ) ) ;
localStorage . setItem ( this . userKey , JSON . stringify ( val ) ) ;
} else {
} else {
localStorage . removeItem ( this . userKey ) ;
localStorage . removeItem ( this . userKey ) ;
localStorage . removeItem ( 'vbase_last_refresh' ) ;
}
}
this . _cachePublicUser ( val ) ;
this . _cachePublicUser ( val ) ;
}
}
_touchRefresh ( ) {
localStorage . setItem ( 'vbase_last_refresh' , Date . now ( ) . toString ( ) ) ;
}
// ========== API 请求 ==========
// ========== API 请求 ==========
async request ( method , path , data = null , headers = { } ) {
async request ( method , path , data = null , headers = { } ) {
@ -94,12 +142,19 @@ class VBase {
// ========== 认证 ==========
// ========== 认证 ==========
/** 登录成功后初始化鉴权状态( 密码登录之外的方式: 验证码、OAuth、注册后自动登录等) */
async onAuthSuccess ( user ) {
this . _touchRefresh ( ) ;
this . user = user ;
await this . fetchUser ( ) ;
this . _startRefreshTimer ( ) ;
}
/** 用户名密码登录 */
/** 用户名密码登录 */
async login ( username , password ) {
async login ( username , password ) {
const data = await this . request ( 'POST' , '/api/auth/login' , { username , password } ) ;
const data = await this . request ( 'POST' , '/api/auth/login' , { username , password } ) ;
if ( data . user ) {
if ( data . user ) {
this . user = data . user ;
await this . onAuthSuccess ( data . user ) ;
await this . fetchUser ( ) ;
return true ;
return true ;
}
}
return false ;
return false ;
@ -108,38 +163,25 @@ class VBase {
/** OAuth 回调 */
/** OAuth 回调 */
async oauthCallback ( provider , code , state ) {
async oauthCallback ( provider , code , state ) {
const data = await this . request ( 'GET' , ` /api/auth/callback/ ${ provider } ?code= ${ code } &state= ${ state } ` ) ;
const data = await this . request ( 'GET' , ` /api/auth/callback/ ${ provider } ?code= ${ code } &state= ${ state } ` ) ;
if ( data . user ) {
if ( data . user ) await this . onAuthSuccess ( data . user ) ;
this . user = data . user ;
await this . fetchUser ( ) ;
}
return data ;
return data ;
}
}
/** 绑定已有账号 */
/** 绑定已有账号 */
async bindAccount ( tempToken , username , password ) {
async bindAccount ( tempToken , username , password ) {
const data = await this . request ( 'POST' , '/api/auth/bind' , {
const data = await this . request ( 'POST' , '/api/auth/bind' , {
temp _token : tempToken ,
temp _token : tempToken , username , password ,
username ,
password ,
} ) ;
} ) ;
if ( data . user ) {
if ( data . user ) await this . onAuthSuccess ( data . user ) ;
this . user = data . user ;
await this . fetchUser ( ) ;
}
return data ;
return data ;
}
}
/** 绑定并注册 */
/** 绑定并注册 */
async bindRegister ( tempToken , username , email ) {
async bindRegister ( tempToken , username , email ) {
const data = await this . request ( 'POST' , '/api/auth/bind-register' , {
const data = await this . request ( 'POST' , '/api/auth/bind-register' , {
temp _token : tempToken ,
temp _token : tempToken , username , email ,
username ,
email ,
} ) ;
} ) ;
if ( data . user ) {
if ( data . user ) await this . onAuthSuccess ( data . user ) ;
this . user = data . user ;
await this . fetchUser ( ) ;
}
return data ;
return data ;
}
}
@ -156,10 +198,11 @@ class VBase {
}
}
}
}
/** Token 刷新 (后端自动处理,前端可主动调用) */
/** Token 刷新 */
async refresh ( ) {
async refresh ( ) {
try {
try {
await this . request ( 'POST' , '/api/auth/refresh' , { } ) ;
await this . request ( 'POST' , '/api/auth/refresh' , { } ) ;
this . _touchRefresh ( ) ;
return true ;
return true ;
} catch ( e ) {
} catch ( e ) {
return false ;
return false ;
@ -180,6 +223,7 @@ class VBase {
/** 清除登录状态 */
/** 清除登录状态 */
clear ( ) {
clear ( ) {
this . _stopRefreshTimer ( ) ;
this . user = null ;
this . user = null ;
for ( const id of Object . keys ( this . users ) ) {
for ( const id of Object . keys ( this . users ) ) {
delete this . users [ id ] ;
delete this . users [ id ] ;