未验证 提交 0e7ba429 编写于 作者: M Méril 提交者: GitHub

feat(console): poll refreshRoute when AUTH_PAYLOAD is present in localStorage (#801)

上级 a292c070
......@@ -2954,7 +2954,7 @@
"buffer-from": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
"integrity": "sha1-MnE7wCj3XAL9txDXx7zsHyxgcO8=",
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
"dev": true
},
"buffer-indexof": {
......
......@@ -4,6 +4,10 @@ const bootstrap = (): ConsoleAction => ({
type: ConsoleAT.BOOTSTRAP,
})
const refreshAuthToken = (): ConsoleAction => ({
type: ConsoleAT.REFRESH_AUTH_TOKEN,
})
const setConfiguration = (payload: ConfigurationShape): ConsoleAction => ({
payload,
type: ConsoleAT.SET_CONFIGURATION,
......@@ -13,4 +17,4 @@ const toggleSideMenu = (): ConsoleAction => ({
type: ConsoleAT.TOGGLE_SIDE_MENU,
})
export default { bootstrap, setConfiguration, toggleSideMenu }
export default { bootstrap, refreshAuthToken, setConfiguration, toggleSideMenu }
import { Epic, ofType } from "redux-observable"
import { filter, map, switchMap } from "rxjs/operators"
import { filter, map, switchMap, tap } from "rxjs/operators"
import { NEVER, of, timer } from "rxjs"
import { actions } from "store"
import {
......@@ -7,11 +8,19 @@ import {
ConfigurationShape,
ConsoleAction,
ConsoleAT,
RefreshAuthTokenAction,
StoreAction,
StoreShape,
} from "types"
import { fromFetch } from "utils"
type AuthPayload = Readonly<
Partial<{
expiry: number
refreshRoute: string
}>
>
export const getConfiguration: Epic<StoreAction, ConsoleAction, StoreShape> = (
action$,
) =>
......@@ -29,4 +38,65 @@ export const getConfiguration: Epic<StoreAction, ConsoleAction, StoreShape> = (
),
)
export default [getConfiguration]
export const triggerRefreshTokenOnBootstrap: Epic<
StoreAction,
ConsoleAction,
StoreShape
> = (action$) =>
action$.pipe(
ofType<StoreAction, BootstrapAction>(ConsoleAT.BOOTSTRAP),
switchMap(() => {
const authPayload = localStorage.getItem("AUTH_PAYLOAD")
if (authPayload != null) {
return of(actions.console.refreshAuthToken())
}
return NEVER
}),
)
export const refreshToken: Epic<StoreAction, ConsoleAction, StoreShape> = (
action$,
) =>
action$.pipe(
ofType<StoreAction, RefreshAuthTokenAction>(ConsoleAT.REFRESH_AUTH_TOKEN),
switchMap(() => {
const authPayload = localStorage.getItem("AUTH_PAYLOAD")
if (authPayload != null) {
try {
const { expiry, refreshRoute } = JSON.parse(
authPayload,
) as AuthPayload
if (expiry != null && refreshRoute != null) {
const waitUntil = expiry * 1e3 - 30e3 - Date.now()
if (waitUntil < 0) {
return NEVER
}
return timer(waitUntil).pipe(
switchMap(() => fromFetch<AuthPayload>(refreshRoute)),
tap((response) => {
if (!response.error) {
localStorage.setItem(
"AUTH_PAYLOAD",
JSON.stringify(response.data),
)
}
}),
switchMap(() => of(actions.console.refreshAuthToken())),
)
}
} catch (error) {
return NEVER
}
}
return NEVER
}),
)
export default [getConfiguration, triggerRefreshTokenOnBootstrap, refreshToken]
......@@ -16,6 +16,7 @@ export type ConsoleStateShape = Readonly<{
export enum ConsoleAT {
BOOTSTRAP = "CONSOLE/BOOTSTRAP",
REFRESH_AUTH_TOKEN = "CONSOLE/REFRESH_AUTH_TOKEN",
SET_CONFIGURATION = "CONSOLE/SET_CONFIGURATION",
TOGGLE_SIDE_MENU = "CONSOLE/TOGGLE_SIDE_MENU",
}
......@@ -24,6 +25,10 @@ export type BootstrapAction = Readonly<{
type: ConsoleAT.BOOTSTRAP
}>
export type RefreshAuthTokenAction = Readonly<{
type: ConsoleAT.REFRESH_AUTH_TOKEN
}>
type SetConfigurationAction = Readonly<{
payload: ConfigurationShape
type: ConsoleAT.SET_CONFIGURATION
......@@ -35,5 +40,6 @@ type ToggleSideMenuAction = Readonly<{
export type ConsoleAction =
| BootstrapAction
| RefreshAuthTokenAction
| SetConfigurationAction
| ToggleSideMenuAction
......@@ -11,7 +11,7 @@ const quest = new QuestDB.Client()
const start = async () => {
const yearOffset = 24 * 60 * 60 * 1000 * 31 * 12
const result = await quest.query<ConfigShape>(Table.CONFIG)
const result = await quest.query<ConfigShape>(`${Table.CONFIG} limit -1`)
let lastUpdated: string | undefined
// If the user enabled telemetry then we start the webworker
......
......@@ -15,8 +15,10 @@ type SuccessShape<T> = Readonly<{
export const fromFetch = <T extends Record<string, any>>(
uri: string,
init: RequestInit = {},
): Observable<SuccessShape<T> | ErrorShape> =>
rxFromFetch(`http://${window.location.host}/${uri}`, init).pipe(
): Observable<SuccessShape<T> | ErrorShape> => {
const separator = uri[0] === "/" ? "" : "/"
return rxFromFetch(`${window.location.origin}${separator}${uri}`, init).pipe(
switchMap((response) => {
if (response.ok) {
return response.json()
......@@ -41,3 +43,4 @@ export const fromFetch = <T extends Record<string, any>>(
return response as ErrorShape
}),
)
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册