oa token js

v3
veypi 4 weeks ago
parent 3b65aea4ad
commit 90a1f1d3a3

@ -27,14 +27,31 @@ export const token = {
update: () => { update: () => {
return new Promise<string>((resolve) => { resolve(token.value) }) return new Promise<string>((resolve) => { resolve(token.value) })
}, },
set_updator: (fn: () => Promise<{ raw: () => string }>) => { set_updator: (fn: () => Promise<string>) => {
let locked = false
token.update = () => { token.update = () => {
if (locked) {
return new Promise<void>((resolve) => {
setTimeout(() => {
resolve();
}, 1000)
}).then(() => {
return token.update()
})
}
locked = true
return new Promise<string>((resolve, reject) => { return new Promise<string>((resolve, reject) => {
if (token.value) {
locked = false
resolve(token.value)
return
}
fn().then((e) => { fn().then((e) => {
resolve(e.raw()) token.value = e
resolve(e)
}).catch(() => { }).catch(() => {
reject() reject()
}) }).finally(() => { locked = false })
}) })
} }
} }
@ -78,12 +95,23 @@ const responseFailed = (error: AxiosError) => {
needRetry = false needRetry = false
} else if (response?.status == 401) { } else if (response?.status == 401) {
needRetry = false needRetry = false
if (data.code === 40103) { // AuthNotFound = New(40100, "auth not found")
// AuthExpired = New(40102, "auth expired")
if (data.code === 40102 || data.code === 40100) {
token.value = ''
return token.update().then(() => {
return requestRetry(1000, response!)
})
} }
} }
if (!needRetry) { if (!needRetry) {
return Promise.reject(data || response) return Promise.reject(data || response)
}; };
return requestRetry(1000, response!)
}
const requestRetry = (delay = 0, response: AxiosResponse) => {
const config = response?.config
// @ts-ignore // @ts-ignore
const { __retryCount = 0, retryDelay = 1000, retryTimes } = config; const { __retryCount = 0, retryDelay = 1000, retryTimes } = config;
// 在请求对象上设置重试次数 // 在请求对象上设置重试次数
@ -93,18 +121,20 @@ const responseFailed = (error: AxiosError) => {
if (__retryCount >= retryTimes) { if (__retryCount >= retryTimes) {
return Promise.reject(response?.data || response?.headers.error) return Promise.reject(response?.data || response?.headers.error)
} }
if (delay <= 0) {
return proxy.request(config as any)
}
// 延时处理 // 延时处理
const delay = new Promise<void>((resolve) => { return new Promise<void>((resolve) => {
setTimeout(() => { setTimeout(() => {
resolve(); resolve();
}, retryDelay); }, delay)
}); }).then(() => {
// 重新发起请求 return proxy.request(config as any)
return delay.then(function() { })
return proxy.request(config as any);
});
} }
proxy.interceptors.response.use(responseSuccess, responseFailed) proxy.interceptors.response.use(responseSuccess, responseFailed)
interface data { interface data {

@ -8,6 +8,7 @@
import slide from './slide' import slide from './slide'
import v from './v2dom' import v from './v2dom'
import logic from '../logic' import logic from '../logic'
import bus from '../bus'
export default class { export default class {
slide: slide slide: slide
frame: HTMLElement frame: HTMLElement
@ -34,11 +35,10 @@ export default class {
inner: '登录', inner: '登录',
onclick: () => { onclick: () => {
console.log('click login') console.log('click login')
logic.login() bus.emit('login')
} }
}) })
return () => { return () => {
console.log(logic.ready)
if (logic.ready) { if (logic.ready) {
return frame_user return frame_user
} else { } else {

@ -7,6 +7,7 @@
import bus from './bus' import bus from './bus'
import proxy from './components/proxy' import proxy from './components/proxy'
import api, { apitoken } from './api' import api, { apitoken } from './api'
import * as typs from './typs'
class Token { class Token {
iat?: string iat?: string
@ -17,7 +18,7 @@ class Token {
icon?: string icon?: string
name?: string name?: string
uid?: string uid?: string
access?: any access?: typs.Auths
private _raw: string private _raw: string
private _typ: 'refresh' | 'oa' | 'app' private _typ: 'refresh' | 'oa' | 'app'
constructor(typ: 'refresh' | 'oa' | 'app') { constructor(typ: 'refresh' | 'oa' | 'app') {
@ -42,6 +43,7 @@ class Token {
// sign = parts[2] // sign = parts[2]
this._raw = t this._raw = t
Object.assign(this, body) Object.assign(this, body)
this.access = typs.NewAuths(body.access || [])
localStorage.setItem(this._typ, t) localStorage.setItem(this._typ, t)
} catch (error) { } catch (error) {
console.warn('Error parsing JWT:', error); console.warn('Error parsing JWT:', error);
@ -59,6 +61,9 @@ class Token {
if (!logic.token.refresh.isVaild() || this._typ === 'refresh') { if (!logic.token.refresh.isVaild() || this._typ === 'refresh') {
reject() reject()
} }
if (this.isVaild()) {
return resolve(this)
}
let aid = logic.oa_id let aid = logic.oa_id
if (this._typ === 'app') { if (this._typ === 'app') {
aid = logic.app_id aid = logic.app_id
@ -119,25 +124,29 @@ const logic = proxy.Watch({
if (logic.token.refresh.isVaild()) { if (logic.token.refresh.isVaild()) {
logic.app_id = logic.token.refresh.aid! logic.app_id = logic.token.refresh.aid!
logic.token.oa.update().then((e) => { logic.token.oa.update().then((e) => {
apitoken.set_updator(logic.token.oa.update) apitoken.value = e.raw()
apitoken.set_updator(() => new Promise<string>((resolve, reject) => {
logic.token.oa.update().then(e => { resolve(e.raw()) }).catch(() => reject)
})
)
if (logic.app_id !== logic.oa_id) { if (logic.app_id !== logic.oa_id) {
logic.token.app.update().then((e) => { logic.token.app.update().then((e) => {
logic.ready = true logic.ready = true
resolve(e); resolve(e);
}).catch(() => reject()) }).catch((e) => reject(e))
} else { } else {
logic.ready = true logic.ready = true
resolve(e); resolve(e);
} }
}).catch(() => { }).catch((e) => {
reject() reject(e)
}) })
} else { } else {
reject() reject()
} }
}).catch(e => { }).catch(e => {
console.warn(`can not get info from ${logic.host}: ${e} `) console.warn(`can not get info from ${logic.host}: ${e} `)
reject(); reject(e);
}) })
}) })
}, },

@ -9,6 +9,9 @@ import './assets/css/oaer.scss'
import bus from './bus' import bus from './bus'
import logic from './logic' import logic from './logic'
import ui from './components' import ui from './components'
import * as typs from './typs'
import api from './api'
export default new class { export default new class {
private ui?: ui private ui?: ui
@ -21,7 +24,17 @@ export default new class {
if (code) { if (code) {
logic.token.refresh.set(code) logic.token.refresh.set(code)
} }
return logic.init() return new Promise<typs.UserData>((resolve, reject) => {
logic.init().then((e) => {
api.user.Get(e.uid!).then((u) => {
resolve(Object.assign(u, { access: e.access! }))
}).catch((e) => {
reject(e)
})
}).catch((e) => {
reject(e)
})
})
} }
render_ui(domid = 'voa') { render_ui(domid = 'voa') {
if (!this.ui) { if (!this.ui) {
@ -46,6 +59,26 @@ export default new class {
on(evt: string, fn: (d?: any) => void) { on(evt: string, fn: (d?: any) => void) {
bus.on(evt, fn) bus.on(evt, fn)
} }
}() api() {
return {
user: {
Get: api.user.Get,
List: api.user.List,
},
app: {
Get: api.app.Get,
List: api.app.List,
},
}
}
Token() {
return logic.token.app.raw()
}
TokenRefresh() {
return new Promise<string>((resolve, reject) => {
logic.token.app.update().then((e) => { resolve(e.raw()) }).catch(() => reject)
})
}
}

@ -0,0 +1,106 @@
/*
* typ.ts
* Copyright (C) 2024 veypi <i@veypi.com>
* 2024-10-28 14:19
* Distributed under terms of the GPL license.
*/
export * from './api/models'
import * as models from './api/models'
export interface UserData extends models.User {
access: Auths
}
export interface modelsSimpleAuth {
level: number
name: string
tid: string
}
export const R = {
// 应用管理配置权限
App: 'app',
// 用户管理和绑定应用权限
User: 'user',
// 权限资源定义权限
Resource: 'resource',
// 角色管理和绑定用户权限
Role: 'role',
// 权限管理和绑定角色权限
Auth: 'auth',
}
export enum AccessLevel {
None = 0,
Do = 1,
Read = 1,
Create = 2,
Update = 3,
Delete = 4,
All = 5
}
export const LevelOptions = [
{ key: 0, name: '无权限' },
{ key: 1, name: '读取数据权限' },
{ key: 2, name: '创建数据权限' },
{ key: 3, name: '更新数据权限' },
{ key: 4, name: '删除数据权限' },
{ key: 5, name: '管理员权限' },
]
class authLevel {
level = AccessLevel.None
constructor(level: number) {
this.level = level
}
CanDo(): boolean {
return this.level >= AccessLevel.Do
}
CanRead(): boolean {
return this.level >= AccessLevel.Read
}
CanCreate(): boolean {
return this.level >= AccessLevel.Create
}
CanUpdate(): boolean {
return this.level >= AccessLevel.Update
}
CanDelete(): boolean {
return this.level >= AccessLevel.Delete
}
CanDoAny(): boolean {
return this.level >= AccessLevel.All
}
}
export class auths {
private readonly list: modelsSimpleAuth[]
constructor(auths: modelsSimpleAuth[]) {
this.list = auths
}
Get(name: string, rid: string): authLevel {
let l = AccessLevel.None
for (let i of this.list) {
if (i.name == name && (!i.tid || i.tid === rid) && i.level > l) {
l = i.level
}
}
return new authLevel(l)
}
}
export interface Auths {
Get(name: string, rid: string): authLevel
}
export function NewAuths(a: modelsSimpleAuth[]): Auths {
return new auths(a)
}
Loading…
Cancel
Save