Files
Toju/toju-app/src/app/infrastructure/realtime/signaling/signaling-transport-handler.ts
Myx bd21568726
All checks were successful
Queue Release Build / prepare (push) Successful in 28s
Deploy Web Apps / deploy (push) Successful in 5m2s
Queue Release Build / build-windows (push) Successful in 16m44s
Queue Release Build / build-linux (push) Successful in 27m12s
Queue Release Build / finalize (push) Successful in 22s
feat: Add user metadata changing display name and description with sync
2026-04-17 22:55:50 +02:00

221 lines
7.0 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;
}
getIdentifyDescription(): string | undefined {
return this.lastIdentifyCredentials?.description;
}
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;
const messageType = typeof message['type'] === 'string' ? message['type'] : 'unknown';
if (targetPeerId) {
const targetSignalUrl = this.dependencies.signalingCoordinator.getPeerSignalUrl(targetPeerId);
if (targetSignalUrl && this.sendRawMessageToSignalUrl(targetSignalUrl, message)) {
return;
}
this.dependencies.logger.warn('[signaling] Missing peer signal route for outbound raw message', {
targetPeerId,
type: messageType
});
}
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;
}
this.dependencies.logger.warn('[signaling] Missing server signal route for outbound raw message', {
serverId,
type: messageType
});
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: messageType
});
return;
}
this.dependencies.logger.warn('[signaling] Broadcasting raw message to all signaling managers due to unresolved route', {
connectedSignalUrls: connectedManagers.map(({ signalUrl }) => signalUrl),
serverId,
targetPeerId,
type: messageType
});
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,
profile?: Pick<IdentifyCredentials, 'description' | 'profileUpdatedAt'>
): void {
const normalizedDisplayName = displayName.trim() || DEFAULT_DISPLAY_NAME;
const normalizedDescription = typeof profile?.description === 'string'
? (profile.description.trim() || undefined)
: undefined;
const normalizedProfileUpdatedAt = typeof profile?.profileUpdatedAt === 'number'
&& Number.isFinite(profile.profileUpdatedAt)
&& profile.profileUpdatedAt > 0
? profile.profileUpdatedAt
: undefined;
this.lastIdentifyCredentials = {
oderId,
displayName: normalizedDisplayName,
description: normalizedDescription,
profileUpdatedAt: normalizedProfileUpdatedAt
};
if (signalUrl) {
this.sendRawMessageToSignalUrl(signalUrl, {
type: SIGNALING_TYPE_IDENTIFY,
oderId,
displayName: normalizedDisplayName,
description: normalizedDescription,
profileUpdatedAt: normalizedProfileUpdatedAt,
connectionScope: signalUrl
});
return;
}
const connectedManagers = this.getConnectedSignalingManagers();
if (connectedManagers.length === 0) {
return;
}
for (const { signalUrl: managerSignalUrl, manager } of connectedManagers) {
manager.sendRawMessage({
type: SIGNALING_TYPE_IDENTIFY,
oderId,
displayName: normalizedDisplayName,
description: normalizedDescription,
profileUpdatedAt: normalizedProfileUpdatedAt,
connectionScope: managerSignalUrl
});
}
}
}