diff --git a/public/app/features/live/centrifuge/service.ts b/public/app/features/live/centrifuge/service.ts index 52194e7a120..6a3858945d9 100644 --- a/public/app/features/live/centrifuge/service.ts +++ b/public/app/features/live/centrifuge/service.ts @@ -3,6 +3,7 @@ import { ConnectedContext, ConnectingContext, DisconnectedContext, + ErrorContext, ServerPublicationContext, State, } from 'centrifuge'; @@ -25,6 +26,7 @@ import { StreamingFrameAction, StreamingFrameOptions, BackendDataSourceResponse, + getBackendSrv, } from '@grafana/runtime'; import { StreamingResponseData } from '../data/utils'; @@ -71,6 +73,7 @@ export class CentrifugeService implements CentrifugeSrv { readonly connectionState: BehaviorSubject; readonly connectionBlocker: Promise; private readonly dataStreamSubscriberReadiness: Observable; + private lastAuthCheck = 0; constructor(private deps: CentrifugeSrvDeps) { this.dataStreamSubscriberReadiness = deps.dataStreamSubscriberReadiness.pipe(share(), startWith(true)); @@ -106,6 +109,7 @@ export class CentrifugeService implements CentrifugeSrv { this.centrifuge.on('connecting', this.onDisconnect); this.centrifuge.on('disconnected', this.onDisconnect); this.centrifuge.on('publication', this.onServerSideMessage); + this.centrifuge.on('error', this.onError); } //---------------------------------------------------------- @@ -124,6 +128,27 @@ export class CentrifugeService implements CentrifugeSrv { console.log('Publication from server-side channel', context); }; + private onError = (context: ErrorContext) => { + /** + * This is a workaround to handle the case where the authentication token + * has expired, but we still try to reconnect inside a page with Grafana Live enabled. + * See: https://github.com/grafana/grafana/issues/72792 + */ + if (context.type === 'transport' && context.error?.code === 2) { + const now = Date.now(); + // Check every 5 seconds to avoid hammering the + // API if there is a case like this + if (now - this.lastAuthCheck > 5000) { + this.lastAuthCheck = now; + getBackendSrv() + .get('/api/login/ping') + .catch(() => { + // Just swallow this error - it's non-critical + }); + } + } + }; + /** * Get a channel. If the scope, namespace, or path is invalid, a shutdown * channel will be returned with an error state indicated in its status