You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
OneAuth/oaer/lib/logic.ts

214 lines
5.1 KiB
TypeScript

/*
* logic.ts
* Copyright (C) 2024 veypi <i@veypi.com>
* 2024-10-24 16:36
* Distributed under terms of the GPL license.
*/
import bus from './bus'
import api, { apitoken } from './api'
import * as typs from './typs'
import { proxy } from './v2dom'
class Token {
iat?: string
// oa_id
iss?: string
// token id
jti?: string
exp?: number
aid?: string
icon?: string
name?: string
uid?: string
access?: typs.Auths
private _raw: string
private _typ: 'refresh' | 'oa' | 'app'
constructor(typ: 'refresh' | 'oa' | 'app') {
this._typ = typ
this._raw = localStorage.getItem(typ) || ''
if (this._raw) {
this.set(this._raw)
}
}
isVaild() {
if (this.exp) {
const now = Math.floor(Date.now() / 1000);
return now < this.exp
}
return false
}
set(t: string) {
try {
const parts = t.split('.');
// header = JSON.parse(atob(parts[0]));
let body = JSON.parse(atob(parts[1]));
// sign = parts[2]
this._raw = t
Object.assign(this, body)
this.access = typs.NewAuths(body.access || [])
localStorage.setItem(this._typ, t)
} catch (error) {
console.warn('Error parsing JWT:', error);
}
}
clear() {
localStorage.removeItem(this._typ)
this.exp = undefined
}
raw() {
return this._raw
}
update() {
return new Promise<Token>((resolve, reject) => {
if (!logic.token.refresh.isVaild() || this._typ === 'refresh') {
reject()
}
if (this.isVaild()) {
return resolve(this)
}
let aid = logic.oa_id
if (this._typ === 'app') {
aid = logic.app_id
}
api.token.Post({ refresh: logic.token.refresh.raw(), app_id: aid }, { needRetry: false }).then(e => {
if (this._typ === 'oa' && logic.app_id == logic.oa_id) {
logic.token.app.set(e)
}
this.set(e)
resolve(this)
}).catch(e => {
console.warn(`refresh token failed: ${e.err || e} `)
bus.emit('logout')
reject()
})
})
}
}
function objectToUrlParams(obj: { [key: string]: any }) {
return Object.keys(obj).map(key => {
return encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]);
}).join('&');
}
// interface Token {
// aid: string
// exp: number
// iat: string
// icon: string
// iss: string
// jti: string
// name: string
// uid: string
// isVaild: () => boolean
// row: () => string
// }
const logic = proxy.Watch({
oa_id: '',
app_id: '',
token: {
refresh: {} as Token,
oa: {} as Token,
app: {} as Token,
},
user: {} as typs.User,
myapps: [
{ name: 'a' },
{ name: 'b' },
],
ready: false,
getDetailUser() {
api.user.Get(logic.token.oa.uid!).then((u) => {
Object.assign(logic.user, u)
})
},
init: () => {
return new Promise<Token>((resolve, reject) => {
api.info().then(e => {
logic.oa_id = e.id
if (logic.token.refresh.isVaild()) {
logic.app_id = logic.token.refresh.aid!
logic.token.oa.update().then((e) => {
apitoken.value = e.raw()
apitoken.set_updator(() => new Promise<string>((resolve, reject) => {
logic.token.oa.update().then(e => { resolve(e.raw()) }).catch(() => reject)
})
)
logic.getDetailUser()
if (logic.app_id !== logic.oa_id) {
logic.token.app.update().then((e) => {
logic.ready = true
resolve(e);
}).catch((e) => reject(e))
} else {
logic.ready = true
resolve(e);
}
}).catch((e) => {
reject(e)
})
} else {
reject()
}
}).catch(e => {
console.warn(`can not get info from ${logic.host}: ${e} `)
reject(e);
})
})
},
host: window.location.origin,
Host() {
return logic.host
},
urlwarp(url: string, params?: { [key: string]: any }) {
let h = logic.host
if (params) {
if (url.includes('?')) {
url += '&' + objectToUrlParams(params)
} else {
url += '?' + objectToUrlParams(params)
}
}
if (!url) {
return ''
}
if (url.startsWith('http')) {
return url
}
if (!url.startsWith('/')) {
url = '/' + url
}
return h + url
},
goto(url: string, params?: { [key: string]: any }, newtab = false) {
if (newtab) {
window.open(logic.urlwarp(url, params), '_blank');
} else {
window.location.href = logic.urlwarp(url, params)
}
},
})
bus.on('login', () => {
logic.goto('/login', { redirect: window.location.href, uuid: logic.app_id })
})
bus.on('logout', () => {
apitoken.value = ''
if (logic.token.refresh.jti) {
api.token.Delete(logic.token.refresh.jti)
}
logic.ready = false
logic.token.refresh.clear()
logic.token.oa.clear()
logic.token.app.clear()
})
// load token from localStorage
logic.token.refresh = new Token('refresh')
logic.app_id = logic.token.refresh.aid || ''
logic.token.oa = new Token('oa')
logic.token.app = new Token('app')
export default logic