SSE 헤더 인증과 연결 수명 검증 및 HTTP 오류 정보 보호

This commit is contained in:
Hide_D committed 2026-10-09 02:35:45 +09:00
1 parent 87f810a878
commit 9dceaf8e4e
22 files changed
+1023 -170

No files matched your search

+12 -5
View File
@@ -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' });