Created
September 8, 2018 18:55
-
-
Save thanhpk/a1f620b40af10e414f17d70b8ff17bc0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
}) | |
}) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
const gStore = {} // require('store')
const gAjax = {} // require('@subiz/ajax')
const DEAD = 'dead'
const REFRESHING = 'refreshing'
const NORMAL = 'normal'
const JUST_REFRESHED = 'just_refreshed'
const loop4everInternal = (f, r) =>
f((delay, err) => (err ? r(err) : setTimeout(loop4everInternal, delay, f, r)))
const loop4ever = f => new Promise(resolve => loop4everInternal(f, resolve))
const run = (self, state, param) =>
loop4ever(resolve => {
self[state]((nextstate, nextparam, delay) => {
resolve(undefined, 'invalid state' + nextstate)
;[state, param] = [nextstate, nextparam]
resolve(delay)
}, param)
})
class Token {
constructor ({ tokenep, ajax, store, dry }) {
this.api = (ajax || gAjax)
.post(tokenep)
.setParser('json')
.setContentType('form')
this.refreshQ = []
this.restartQ = []
this.store = store || gStore
dry || run(this, NORMAL)
}
}
module.exports = { Token, loop4ever }