var store = require('store') var ajax = require('@subiz/ajax') const DEAD = 'dead' const REFRESHING = 'refreshing' const NORMAL = 'normal' const JUST_REFRESHED = 'just_refreshed' function loop (cb) { new Promise(cb).then(cont => { if (cont) setTimeout(() => loop(cb), 1) }) } function since (t) { return new Date() - t } function sleep (t) { return new Promise(resolve => setTimeout(() => resolve, t)) } export class Token { constructor (tokenhost) { this.actoken = '' this.rftoken = '' this.api = ajax .post(tokenhost, 'refresh-token') .setParser('json') .setContentType('form') this.refreshQ = [] this.restartQ = [] this.store = store this.run() } run () { let state = NORMAL // init state let param Token.init() loop(resolve => { Token[state](param).then((nextstate, nextparam) => { [state, param] = [nextstate, nextparam] resolve(true) }) }) } loadStore () { return this.store.get('subiz_token') || {} } load () { const lcs = this.loadStore() if (!this.actoken) this.actoken = lcs.access_token if (!this.rftoken) this.rftoken = lcs.refresh_token return [this.actoken, this.rftoken] } set (actoken, rftoken) { this.actoken = actoken this.rftoken = rftoken this.store.set('subiz_token', { refresh_token: rftoken, access_token: actoken }) } refresh () { return new Promise(resolve => this.refreshQ.push({ resolve })) } restart () { return new Promise(resolve => this.restartQ.push(new Date())) } NORMAL () { return new Promise(transition => loop(resolve => { let req = this.refreshQ.pop() if (!req) { sleep(100).then(() => resolve(true)) return } transition(REFRESHING, req) resolve(false) }) ) } JUST_REFRESHED () { return new Promise(transition => { let now = new Date() loop(resolve => { this.refreshQ.forEach(req => req.resolve([this.actoken, this.rftoken])) this.refreshQ = [] if (since(now) > 10000) { transition(NORMAL) resolve(false) return } setTimeout(() => resolve(true), 1000) }) }) } REFRESHING (req) { return new Promise(transition => { this.api .query({ 'refresh-token': this.rftoken }) .send() .then(([code, body, err]) => { if (err || code !== 200) { let st = this.loadStore() if (st.refresh_token !== this.rftoken) { /* someone change the token */ this.set(st.access_token, st.refresh_token) transition(JUST_REFRESHED) return } transition(DEAD) return } /* parsebody */ this.set(body.access_token, body.refresh_token) transition(JUST_REFRESHED) }) }) } DEAD () { return new Promise(transition => { loop(resolveloop => { this.refreshQ.forEach(req => req.resolve([undefined, undefined, 'dead']) ) this.refreshQ = [] if (this.restartQ.length > 0) { transition(NORMAL) resolveloop(false) return } resolveloop(true) }) }) } }