173 lines
5.4 KiB
TypeScript
173 lines
5.4 KiB
TypeScript
import { Observable, of } from 'rxjs';
|
|
import type { SignalingMessage } from '../../../shared-kernel';
|
|
import { DEFAULT_DISPLAY_NAME, SIGNALING_TYPE_IDENTIFY } from '../realtime.constants';
|
|
import { IdentifyCredentials } from '../realtime.types';
|
|
import { ConnectedSignalingManager, ServerSignalingCoordinator } from './server-signaling-coordinator';
|
|
import { WebRTCLogger } from '../logging/webrtc-logger';
|
|
|
|
interface SignalingTransportHandlerDependencies<TMessage> {
|
|
signalingCoordinator: ServerSignalingCoordinator<TMessage>;
|
|
logger: WebRTCLogger;
|
|
getLocalPeerId(): string;
|
|
}
|
|
|
|
export class SignalingTransportHandler<TMessage> {
|
|
private lastIdentifyCredentials: IdentifyCredentials | null = null;
|
|
|
|
constructor(
|
|
private readonly dependencies: SignalingTransportHandlerDependencies<TMessage>
|
|
) {}
|
|
|
|
getIdentifyCredentials(): IdentifyCredentials | null {
|
|
return this.lastIdentifyCredentials;
|
|
}
|
|
|
|
getIdentifyOderId(): string {
|
|
return this.lastIdentifyCredentials?.oderId || this.dependencies.getLocalPeerId();
|
|
}
|
|
|
|
getIdentifyDisplayName(): string {
|
|
return this.lastIdentifyCredentials?.displayName || DEFAULT_DISPLAY_NAME;
|
|
}
|
|
|
|
getConnectedSignalingManagers(): ConnectedSignalingManager[] {
|
|
return this.dependencies.signalingCoordinator.getConnectedSignalingManagers();
|
|
}
|
|
|
|
getCurrentSignalingUrl(activeServerId: string | null): string | null {
|
|
if (activeServerId) {
|
|
const activeServerSignalUrl = this.dependencies.signalingCoordinator.getServerSignalUrl(activeServerId);
|
|
|
|
if (activeServerSignalUrl) {
|
|
return activeServerSignalUrl;
|
|
}
|
|
}
|
|
|
|
return this.getConnectedSignalingManagers()[0]?.signalUrl ?? null;
|
|
}
|
|
|
|
connectToSignalingServer(serverUrl: string): Observable<boolean> {
|
|
const manager = this.dependencies.signalingCoordinator.ensureSignalingManager(serverUrl);
|
|
|
|
if (manager.isSocketOpen()) {
|
|
return of(true);
|
|
}
|
|
|
|
return manager.connect(serverUrl);
|
|
}
|
|
|
|
isSignalingConnectedTo(serverUrl: string): boolean {
|
|
return this.dependencies.signalingCoordinator.isSignalingConnectedTo(serverUrl);
|
|
}
|
|
|
|
async ensureSignalingConnected(timeoutMs?: number): Promise<boolean> {
|
|
return await this.dependencies.signalingCoordinator.ensureAnySignalingConnected(timeoutMs);
|
|
}
|
|
|
|
sendSignalingMessage(message: Omit<SignalingMessage, 'from' | 'timestamp'>): void {
|
|
const targetPeerId = message.to;
|
|
|
|
if (targetPeerId) {
|
|
const targetSignalUrl = this.dependencies.signalingCoordinator.getPeerSignalUrl(targetPeerId);
|
|
|
|
if (targetSignalUrl) {
|
|
const targetManager = this.dependencies.signalingCoordinator.ensureSignalingManager(targetSignalUrl);
|
|
|
|
targetManager.sendSignalingMessage(message, this.dependencies.getLocalPeerId());
|
|
return;
|
|
}
|
|
}
|
|
|
|
const connectedManagers = this.getConnectedSignalingManagers();
|
|
|
|
if (connectedManagers.length === 0) {
|
|
this.dependencies.logger.error('[signaling] No active signaling connection for outbound message', new Error('No signaling manager available'), {
|
|
type: message.type
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
for (const { manager } of connectedManagers) {
|
|
manager.sendSignalingMessage(message, this.dependencies.getLocalPeerId());
|
|
}
|
|
}
|
|
|
|
sendRawMessage(message: Record<string, unknown>): void {
|
|
const targetPeerId = typeof message['targetUserId'] === 'string' ? message['targetUserId'] : null;
|
|
|
|
if (targetPeerId) {
|
|
const targetSignalUrl = this.dependencies.signalingCoordinator.getPeerSignalUrl(targetPeerId);
|
|
|
|
if (targetSignalUrl && this.sendRawMessageToSignalUrl(targetSignalUrl, message)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
const serverId = typeof message['serverId'] === 'string' ? message['serverId'] : null;
|
|
|
|
if (serverId) {
|
|
const serverSignalUrl = this.dependencies.signalingCoordinator.getServerSignalUrl(serverId);
|
|
|
|
if (serverSignalUrl && this.sendRawMessageToSignalUrl(serverSignalUrl, message)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
const connectedManagers = this.getConnectedSignalingManagers();
|
|
|
|
if (connectedManagers.length === 0) {
|
|
this.dependencies.logger.error('[signaling] No active signaling connection for outbound message', new Error('No signaling manager available'), {
|
|
type: typeof message['type'] === 'string' ? message['type'] : 'unknown'
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
for (const { manager } of connectedManagers) {
|
|
manager.sendRawMessage(message);
|
|
}
|
|
}
|
|
|
|
sendRawMessageToSignalUrl(signalUrl: string, message: Record<string, unknown>): boolean {
|
|
const manager = this.dependencies.signalingCoordinator.getSignalingManager(signalUrl);
|
|
|
|
if (!manager) {
|
|
return false;
|
|
}
|
|
|
|
manager.sendRawMessage(message);
|
|
return true;
|
|
}
|
|
|
|
identify(oderId: string, displayName: string, signalUrl?: string): void {
|
|
const normalizedDisplayName = displayName.trim() || DEFAULT_DISPLAY_NAME;
|
|
|
|
this.lastIdentifyCredentials = {
|
|
oderId,
|
|
displayName: normalizedDisplayName
|
|
};
|
|
|
|
const identifyMessage = {
|
|
type: SIGNALING_TYPE_IDENTIFY,
|
|
oderId,
|
|
displayName: normalizedDisplayName
|
|
};
|
|
|
|
if (signalUrl) {
|
|
this.sendRawMessageToSignalUrl(signalUrl, identifyMessage);
|
|
return;
|
|
}
|
|
|
|
const connectedManagers = this.getConnectedSignalingManagers();
|
|
|
|
if (connectedManagers.length === 0) {
|
|
return;
|
|
}
|
|
|
|
for (const { manager } of connectedManagers) {
|
|
manager.sendRawMessage(identifyMessage);
|
|
}
|
|
}
|
|
}
|