SSE 헤더 인증과 연결 수명 검증 및 HTTP 오류 정보 보호
This commit is contained in:
1 parent
87f810a878
commit
9dceaf8e4e
22 files changed
+1023
-170
No files matched your search
@@ -1,3 +1,4 @@
|
||||
import { createAuthenticatedEventSource, type AuthenticatedEventSource } from '../utils/authenticatedEventSource';
|
||||
import { receiveClockEngineState } from '../composables/useClockDisplay';
|
||||
import { computed, ref, toRaw, watch } from 'vue';
|
||||
import { defineStore } from 'pinia';
|
||||
@@ -1139,16 +1140,15 @@ export const useMainDashboardStore = defineStore('mainDashboard', () => {
|
||||
}
|
||||
};
|
||||
|
||||
let realtimeSource: EventSource | null = null;
|
||||
let realtimeSource: AuthenticatedEventSource | null = null;
|
||||
let realtimeToken: string | null = null;
|
||||
let visibilityListenerInstalled = false;
|
||||
|
||||
const isAccessToken = (token: string | null): boolean => Boolean(token?.startsWith('ga_'));
|
||||
|
||||
const buildRealtimeUrl = (token: string): string => {
|
||||
const buildRealtimeUrl = (): string => {
|
||||
const base = gameFrontendRuntimeConfig.gameSseUrl;
|
||||
const url = new URL(base, window.location.origin);
|
||||
url.searchParams.set('token', token);
|
||||
return url.toString();
|
||||
};
|
||||
|
||||
@@ -1284,15 +1284,22 @@ export const useMainDashboardStore = defineStore('mainDashboard', () => {
|
||||
realtimeToken = token;
|
||||
realtimeStatus.value = 'idle';
|
||||
|
||||
const source = new EventSource(buildRealtimeUrl(token));
|
||||
let needsRecovery = false;
|
||||
const source = createAuthenticatedEventSource(buildRealtimeUrl(), token);
|
||||
realtimeSource = source;
|
||||
|
||||
source.addEventListener('open', () => {
|
||||
markGameServerContact();
|
||||
realtimeStatus.value = 'connected';
|
||||
realtimeCoordinator?.postFromLeader({ kind: 'status', status: 'connected' });
|
||||
if (needsRecovery) {
|
||||
needsRecovery = false;
|
||||
realtimeRefreshQueue.request();
|
||||
}
|
||||
});
|
||||
source.addEventListener('error', () => {
|
||||
source.addEventListener('error', (event) => {
|
||||
if (event.code === 401) void session.refreshGeneralStatus();
|
||||
needsRecovery = true;
|
||||
realtimeStatus.value = realtimeEnabled.value ? 'idle' : 'paused';
|
||||
realtimeCoordinator?.postFromLeader({ kind: 'status', status: 'idle' });
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { createAuthenticatedEventSource, type AuthenticatedEventSource } from '../utils/authenticatedEventSource';
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref, watch } from 'vue';
|
||||
import type { PublicRealtimeEvent, TournamentViewInvalidation } from '@sammo-ts/common';
|
||||
@@ -49,7 +50,7 @@ export const useTournamentPagesStore = defineStore('tournamentPages', () => {
|
||||
const realtimeStatus = ref<'idle' | 'connected'>('idle');
|
||||
|
||||
let activeConsumers = 0;
|
||||
let realtimeSource: EventSource | null = null;
|
||||
let realtimeSource: AuthenticatedEventSource | null = null;
|
||||
let realtimeToken: string | null = null;
|
||||
let realtimeCoordinator: BroadcastTabCoordinator<TournamentTabMessage> | null = null;
|
||||
let realtimeCoordinatorScope: string | null = null;
|
||||
@@ -136,10 +137,9 @@ export const useTournamentPagesStore = defineStore('tournamentPages', () => {
|
||||
if (!(await session.exchangeGatewayToken())) return null;
|
||||
return session.gameToken && isAccessToken(session.gameToken) ? session.gameToken : null;
|
||||
};
|
||||
const buildRealtimeUrl = (token: string): string => {
|
||||
const buildRealtimeUrl = (): string => {
|
||||
const base = gameFrontendRuntimeConfig.gameSseUrl;
|
||||
const url = new URL(base, window.location.origin);
|
||||
url.searchParams.set('token', token);
|
||||
url.searchParams.set('scope', 'tournament');
|
||||
return url.toString();
|
||||
};
|
||||
@@ -181,7 +181,7 @@ export const useTournamentPagesStore = defineStore('tournamentPages', () => {
|
||||
if (realtimeSource && realtimeToken === token) return;
|
||||
closeRealtimeSource();
|
||||
realtimeToken = token;
|
||||
const source = new EventSource(buildRealtimeUrl(token));
|
||||
const source = createAuthenticatedEventSource(buildRealtimeUrl(), token);
|
||||
realtimeSource = source;
|
||||
source.addEventListener('open', () => {
|
||||
realtimeStatus.value = 'connected';
|
||||
@@ -196,7 +196,8 @@ export const useTournamentPagesStore = defineStore('tournamentPages', () => {
|
||||
});
|
||||
}
|
||||
});
|
||||
source.addEventListener('error', () => {
|
||||
source.addEventListener('error', (event) => {
|
||||
if (event.code === 401) void session.refreshGeneralStatus();
|
||||
needsRecovery = true;
|
||||
realtimeStatus.value = 'idle';
|
||||
realtimeCoordinator?.postFromLeader({ kind: 'status', status: 'idle' });
|
||||
|
||||
Reference in new issue
Block a user