feat: signal server tag
This commit is contained in:
@@ -93,9 +93,10 @@ export class ServerDirectoryService {
|
||||
endpointId: string,
|
||||
status: ServerEndpoint['status'],
|
||||
latency?: number,
|
||||
versions?: ServerEndpointVersions
|
||||
versions?: ServerEndpointVersions,
|
||||
serverTag?: string
|
||||
): void {
|
||||
this.endpointState.updateServerStatus(endpointId, status, latency, versions);
|
||||
this.endpointState.updateServerStatus(endpointId, status, latency, versions, serverTag);
|
||||
}
|
||||
|
||||
async ensureEndpointVersionCompatibility(selector?: ServerSourceSelector): Promise<boolean> {
|
||||
@@ -212,7 +213,8 @@ export class ServerDirectoryService {
|
||||
endpointId,
|
||||
healthResult.status,
|
||||
healthResult.latency,
|
||||
healthResult.versions
|
||||
healthResult.versions,
|
||||
healthResult.serverTag
|
||||
);
|
||||
|
||||
return healthResult.status === 'online';
|
||||
|
||||
@@ -228,7 +228,8 @@ export class ServerEndpointStateService {
|
||||
endpointId: string,
|
||||
status: ServerEndpoint['status'],
|
||||
latency?: number,
|
||||
versions?: ServerEndpointVersions
|
||||
versions?: ServerEndpointVersions,
|
||||
serverTag?: string
|
||||
): void {
|
||||
this._servers.update((endpoints) => ensureCompatibleActiveEndpoint(endpoints.map((endpoint) => {
|
||||
if (endpoint.id !== endpointId) {
|
||||
@@ -240,6 +241,7 @@ export class ServerEndpointStateService {
|
||||
instanceId: versions?.serverInstanceId ?? endpoint.instanceId,
|
||||
status,
|
||||
latency,
|
||||
serverTag: serverTag ?? endpoint.serverTag,
|
||||
isActive: status === 'incompatible' && !endpoint.isDefault ? false : endpoint.isActive,
|
||||
serverVersion: versions?.serverVersion ?? endpoint.serverVersion,
|
||||
clientVersion: versions?.clientVersion ?? endpoint.clientVersion
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
isSignalServerTagUrl,
|
||||
presentSignalServerTag,
|
||||
resolveEndpointSignalServerTag,
|
||||
resolveUserHomeSignalServerTag
|
||||
} from './signal-server-tag.rules';
|
||||
import type { ServerEndpoint } from '../models/server-directory.model';
|
||||
|
||||
describe('signal-server-tag.rules', () => {
|
||||
const endpoints: ServerEndpoint[] = [
|
||||
{
|
||||
id: 'endpoint-1',
|
||||
name: 'Primary',
|
||||
url: 'http://signal.example.com:3001',
|
||||
isActive: true,
|
||||
isDefault: true,
|
||||
status: 'online',
|
||||
serverTag: 'EU'
|
||||
},
|
||||
{
|
||||
id: 'endpoint-2',
|
||||
name: 'Fallback',
|
||||
url: 'https://signal-backup.example.com',
|
||||
isActive: true,
|
||||
isDefault: false,
|
||||
status: 'online'
|
||||
}
|
||||
];
|
||||
|
||||
it('uses configured serverTag when present on an endpoint', () => {
|
||||
expect(resolveEndpointSignalServerTag(endpoints[0])).toBe('EU');
|
||||
});
|
||||
|
||||
it('falls back to endpoint url when serverTag is missing', () => {
|
||||
expect(resolveEndpointSignalServerTag(endpoints[1])).toBe('https://signal-backup.example.com');
|
||||
});
|
||||
|
||||
it('resolves a user home signal server tag from known endpoints', () => {
|
||||
expect(resolveUserHomeSignalServerTag('http://signal.example.com:3001/', endpoints)).toBe('EU');
|
||||
});
|
||||
|
||||
it('falls back to the home signal server url when the endpoint is unknown', () => {
|
||||
expect(resolveUserHomeSignalServerTag('http://unknown.example.com:3001', endpoints))
|
||||
.toBe('http://unknown.example.com:3001');
|
||||
});
|
||||
|
||||
it('returns undefined when the user has no home signal server url', () => {
|
||||
expect(resolveUserHomeSignalServerTag(undefined, endpoints)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('detects http and https values as urls', () => {
|
||||
expect(isSignalServerTagUrl('http://signal.example.com:3001')).toBe(true);
|
||||
expect(isSignalServerTagUrl('https://signal.example.com')).toBe(true);
|
||||
expect(isSignalServerTagUrl('EU')).toBe(false);
|
||||
});
|
||||
|
||||
it('prefixes non-url tags with #', () => {
|
||||
expect(presentSignalServerTag('EU')).toEqual({
|
||||
kind: 'label',
|
||||
label: 'EU',
|
||||
display: '#EU'
|
||||
});
|
||||
});
|
||||
|
||||
it('strips an existing # before re-prefixing label tags', () => {
|
||||
expect(presentSignalServerTag('#sweden')).toEqual({
|
||||
kind: 'label',
|
||||
label: 'sweden',
|
||||
display: '#sweden'
|
||||
});
|
||||
});
|
||||
|
||||
it('presents url tags as globe tooltip targets', () => {
|
||||
expect(presentSignalServerTag('https://signal.example.com')).toEqual({
|
||||
kind: 'url',
|
||||
url: 'https://signal.example.com'
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,52 @@
|
||||
import type { ServerEndpoint } from '../models/server-directory.model';
|
||||
import { sanitiseServerBaseUrl } from './server-endpoint-defaults.logic';
|
||||
|
||||
export type SignalServerTagPresentation =
|
||||
| { kind: 'url'; url: string }
|
||||
| { kind: 'label'; label: string; display: string };
|
||||
|
||||
export function isSignalServerTagUrl(value: string): boolean {
|
||||
return /^https?:\/\//i.test(value.trim());
|
||||
}
|
||||
|
||||
export function presentSignalServerTag(tag: string): SignalServerTagPresentation {
|
||||
const normalized = tag.trim();
|
||||
|
||||
if (isSignalServerTagUrl(normalized)) {
|
||||
return { kind: 'url', url: normalized };
|
||||
}
|
||||
|
||||
const label = normalized.replace(/^#+/, '');
|
||||
|
||||
return { kind: 'label', label, display: `#${label}` };
|
||||
}
|
||||
|
||||
export function resolveEndpointSignalServerTag(
|
||||
endpoint: Pick<ServerEndpoint, 'serverTag' | 'url'>
|
||||
): string {
|
||||
const configuredTag = endpoint.serverTag?.trim();
|
||||
|
||||
return configuredTag || endpoint.url;
|
||||
}
|
||||
|
||||
export function resolveUserHomeSignalServerTag(
|
||||
homeSignalServerUrl: string | undefined,
|
||||
endpoints: ServerEndpoint[]
|
||||
): string | undefined {
|
||||
const normalizedHomeUrl = homeSignalServerUrl?.trim();
|
||||
|
||||
if (!normalizedHomeUrl) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const sanitizedHomeUrl = sanitiseServerBaseUrl(normalizedHomeUrl);
|
||||
const matchingEndpoint = endpoints.find(
|
||||
(endpoint) => sanitiseServerBaseUrl(endpoint.url) === sanitizedHomeUrl
|
||||
);
|
||||
|
||||
if (matchingEndpoint) {
|
||||
return resolveEndpointSignalServerTag(matchingEndpoint);
|
||||
}
|
||||
|
||||
return sanitizedHomeUrl;
|
||||
}
|
||||
@@ -57,6 +57,8 @@ export interface ServerEndpoint {
|
||||
instanceId?: string;
|
||||
name: string;
|
||||
url: string;
|
||||
/** Display tag advertised by the signal server health endpoint. */
|
||||
serverTag?: string;
|
||||
isActive: boolean;
|
||||
isDefault: boolean;
|
||||
defaultKey?: string;
|
||||
@@ -141,10 +143,12 @@ export interface ServerVersionCompatibilityResult {
|
||||
export interface ServerHealthCheckPayload {
|
||||
serverInstanceId?: unknown;
|
||||
serverVersion?: unknown;
|
||||
serverTag?: unknown;
|
||||
}
|
||||
|
||||
export interface ServerEndpointHealthResult {
|
||||
status: ServerEndpointStatus;
|
||||
latency?: number;
|
||||
serverTag?: string;
|
||||
versions?: ServerEndpointVersions;
|
||||
}
|
||||
|
||||
@@ -91,7 +91,9 @@
|
||||
|
||||
<div class="relative shrink-0">
|
||||
@if (isJoinedServer(server)) {
|
||||
<div class="flex items-center overflow-hidden rounded-md border border-emerald-500/30 bg-emerald-500/10 text-xs font-semibold text-emerald-500">
|
||||
<div
|
||||
class="flex items-center overflow-hidden rounded-md border border-emerald-500/30 bg-emerald-500/10 text-xs font-semibold text-emerald-500"
|
||||
>
|
||||
<span class="px-2.5 py-1.5">Joined</span>
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -585,7 +585,8 @@ export class ServerBrowserComponent implements OnInit {
|
||||
await firstValueFrom(this.webrtc.connectToSignalingServer(wsUrl));
|
||||
this.webrtc.identify(currentUser.oderId || currentUser.id, currentUser.displayName || 'User', wsUrl, {
|
||||
description: currentUser.description,
|
||||
profileUpdatedAt: currentUser.profileUpdatedAt
|
||||
profileUpdatedAt: currentUser.profileUpdatedAt,
|
||||
homeSignalServerUrl: currentUser.homeSignalServerUrl
|
||||
});
|
||||
|
||||
this.webrtc.sendRawMessageToSignalUrl(wsUrl, {
|
||||
|
||||
@@ -33,11 +33,15 @@ export class ServerEndpointHealthService {
|
||||
const serverInstanceId = typeof payload.serverInstanceId === 'string' && payload.serverInstanceId.trim().length > 0
|
||||
? payload.serverInstanceId.trim()
|
||||
: undefined;
|
||||
const serverTag = typeof payload.serverTag === 'string' && payload.serverTag.trim().length > 0
|
||||
? payload.serverTag.trim()
|
||||
: undefined;
|
||||
|
||||
if (!versionCompatibility.isCompatible) {
|
||||
return {
|
||||
status: 'incompatible',
|
||||
latency,
|
||||
serverTag,
|
||||
versions: {
|
||||
serverInstanceId,
|
||||
serverVersion: versionCompatibility.serverVersion,
|
||||
@@ -49,6 +53,7 @@ export class ServerEndpointHealthService {
|
||||
return {
|
||||
status: 'online',
|
||||
latency,
|
||||
serverTag,
|
||||
versions: {
|
||||
serverInstanceId,
|
||||
serverVersion: versionCompatibility.serverVersion,
|
||||
|
||||
Reference in New Issue
Block a user