refactor: share realtime socket auth helpers
This commit is contained in:
parent
e1bb6e790e
commit
8f7d5358aa
@ -30,7 +30,7 @@ It assumes:
|
|||||||
- [x] Backend migrated into `backend/` and passing typecheck, build, test, and backend verification gates
|
- [x] Backend migrated into `backend/` and passing typecheck, build, test, and backend verification gates
|
||||||
- [x] Web migrated into `web/` with shared runtime, shared kill-switch gate, shared telemetry bootstrap, and normalized backend URL resolution
|
- [x] Web migrated into `web/` with shared runtime, shared kill-switch gate, shared telemetry bootstrap, and normalized backend URL resolution
|
||||||
- [x] Mobile migrated into `mobile/` with product identity, shared runtime bootstrap, launch-time kill-switch gate, transitional Supabase auth, live backend polling plus websocket-backed updates, startup/error telemetry capture, secure session storage with invalidation handling, and explicit degraded/offline status surfacing
|
- [x] Mobile migrated into `mobile/` with product identity, shared runtime bootstrap, launch-time kill-switch gate, transitional Supabase auth, live backend polling plus websocket-backed updates, startup/error telemetry capture, secure session storage with invalidation handling, and explicit degraded/offline status surfacing
|
||||||
- [-] DRY cleanup completed for runtime/config/bootstrap concerns and shared Supabase bootstrap, but not yet for all auth/session internals
|
- [-] DRY cleanup completed for runtime/config/bootstrap concerns, shared Supabase bootstrap, and shared websocket auth helpers, but not yet for all auth/session internals
|
||||||
- [!] Full common-platform auth replacement remains a follow-up for web and mobile; current implementation uses transitional Supabase-backed auth to stay compatible with the backend's current JWT boundary
|
- [!] Full common-platform auth replacement remains a follow-up for web and mobile; current implementation uses transitional Supabase-backed auth to stay compatible with the backend's current JWT boundary
|
||||||
|
|
||||||
## 3. Guiding Rules
|
## 3. Guiding Rules
|
||||||
@ -261,7 +261,7 @@ Move the web dashboard onto the new repo and onto shared platform bootstrap patt
|
|||||||
- [x] Move runtime config to common conventions
|
- [x] Move runtime config to common conventions
|
||||||
- [x] Define product config
|
- [x] Define product config
|
||||||
- [x] Define API client and websocket client
|
- [x] Define API client and websocket client
|
||||||
- [ ] Standardize websocket token propagation
|
- [x] Standardize websocket token propagation
|
||||||
- [x] Integrate maintenance and kill-switch UX states
|
- [x] Integrate maintenance and kill-switch UX states
|
||||||
- [x] Define shell-level maintenance and kill-switch behavior
|
- [x] Define shell-level maintenance and kill-switch behavior
|
||||||
- [ ] Classify each current web tab as ship, defer, or redesign
|
- [ ] Classify each current web tab as ship, defer, or redesign
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { io, type Socket } from 'socket.io-client';
|
|||||||
import { mobileRuntime } from '@/lib/runtime';
|
import { mobileRuntime } from '@/lib/runtime';
|
||||||
import { mobileTelemetry, trackMobileError } from '@/lib/telemetry';
|
import { mobileTelemetry, trackMobileError } from '@/lib/telemetry';
|
||||||
import { useMobileAuth } from '@/providers/MobileAuthProvider';
|
import { useMobileAuth } from '@/providers/MobileAuthProvider';
|
||||||
|
import { buildTradingSocketOptions, isUnauthorizedSocketError } from '../../shared/realtime.js';
|
||||||
|
|
||||||
type HealthSnapshot = {
|
type HealthSnapshot = {
|
||||||
tradingControl?: {
|
tradingControl?: {
|
||||||
@ -223,10 +224,7 @@ export function TradingDataProvider({ children }: { children: ReactNode }) {
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
socket = io(tradingSocketUrl, {
|
socket = io(tradingSocketUrl, buildTradingSocketOptions(accessToken));
|
||||||
transports: ['polling', 'websocket'],
|
|
||||||
auth: { token: accessToken },
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on('connect', () => {
|
socket.on('connect', () => {
|
||||||
setConnected(true);
|
setConnected(true);
|
||||||
@ -242,7 +240,7 @@ export function TradingDataProvider({ children }: { children: ReactNode }) {
|
|||||||
socket.on('connect_error', (socketError) => {
|
socket.on('connect_error', (socketError) => {
|
||||||
setError(socketError.message);
|
setError(socketError.message);
|
||||||
trackMobileError('realtime', 'socket_connect_failed', socketError);
|
trackMobileError('realtime', 'socket_connect_failed', socketError);
|
||||||
if (socketError.message.toLowerCase().includes('unauthorized')) {
|
if (isUnauthorizedSocketError(socketError.message)) {
|
||||||
void invalidateSession(socketError.message);
|
void invalidateSession(socketError.message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
12
shared/realtime.ts
Normal file
12
shared/realtime.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
export function buildTradingSocketOptions(token: string, socketPath?: string) {
|
||||||
|
return {
|
||||||
|
transports: ['polling', 'websocket'] as ('polling' | 'websocket')[],
|
||||||
|
auth: { token },
|
||||||
|
...(socketPath ? { path: socketPath } : {}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isUnauthorizedSocketError(message: string) {
|
||||||
|
const normalizedMessage = message.toLowerCase();
|
||||||
|
return normalizedMessage.includes('unauthorized') || normalizedMessage.includes('invalid token');
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { io, Socket } from 'socket.io-client';
|
import { io, Socket } from 'socket.io-client';
|
||||||
|
import { buildTradingSocketOptions } from '../../../shared/realtime.js';
|
||||||
import { supabase } from '../lib/supabaseClient';
|
import { supabase } from '../lib/supabaseClient';
|
||||||
|
|
||||||
export interface TradingControlSnapshot {
|
export interface TradingControlSnapshot {
|
||||||
@ -284,11 +285,7 @@ export const useWebSocket = (url: string) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const socketOptions = {
|
const socketOptions = buildTradingSocketOptions(token, import.meta.env.VITE_SOCKET_PATH);
|
||||||
transports: ['polling', 'websocket'] as ('polling' | 'websocket')[],
|
|
||||||
auth: { token },
|
|
||||||
...(import.meta.env.VITE_SOCKET_PATH ? { path: import.meta.env.VITE_SOCKET_PATH } : {})
|
|
||||||
};
|
|
||||||
|
|
||||||
newSocket = io(url, socketOptions);
|
newSocket = io(url, socketOptions);
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user