fix: Bug - Login screen shows up on unreachable signal servers
Skip authorize login navigation when a signal server endpoint is offline or unreachable; gate connection and credential flows on online status only. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -5,6 +5,8 @@ import { firstValueFrom } from 'rxjs';
|
||||
import { selectCurrentUser } from '../../../../store/users/users.selectors';
|
||||
import { ServerDirectoryFacade } from '../../../server-directory';
|
||||
import { AUTH_MODE_AUTHORIZE, buildLoginReturnQueryParams } from '../../domain/logic/auth-navigation.rules';
|
||||
import { isEndpointOnlineForConnection } from '../../../server-directory/domain/logic/server-endpoint-connectivity.rules';
|
||||
import { shouldNavigateToAuthorizeSignalServer } from '../../domain/logic/signal-server-authorize.rules';
|
||||
import { SignalServerAuthService } from './signal-server-auth.service';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
@@ -25,25 +27,43 @@ export class SignalServerAuthorizeService {
|
||||
return false;
|
||||
}
|
||||
|
||||
const result = await this.signalServerAuth.ensureProvisioned(serverUrl, currentUser);
|
||||
let result;
|
||||
|
||||
try {
|
||||
result = await this.signalServerAuth.ensureProvisioned(serverUrl, currentUser);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result.kind === 'existing' || result.kind === 'provisioned') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (result.kind === 'collision') {
|
||||
await this.navigateToAuthorize(serverUrl, this.router.url);
|
||||
return false;
|
||||
}
|
||||
const endpointStatus = await this.resolveEndpointStatusForAuthorize(serverUrl);
|
||||
|
||||
if (result.kind === 'skipped' && result.reason === 'no-provision-secret') {
|
||||
if (shouldNavigateToAuthorizeSignalServer(endpointStatus, result)) {
|
||||
await this.navigateToAuthorize(serverUrl, this.router.url);
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async resolveEndpointStatusForAuthorize(serverUrl: string) {
|
||||
const endpoint = this.serverDirectory.findServerByUrl(serverUrl);
|
||||
|
||||
if (!endpoint) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isEndpointOnlineForConnection(endpoint.status) || endpoint.status === 'offline' || endpoint.status === 'incompatible') {
|
||||
return endpoint.status;
|
||||
}
|
||||
|
||||
await this.serverDirectory.testServer(endpoint.id);
|
||||
|
||||
return this.serverDirectory.servers().find((candidate) => candidate.id === endpoint.id)?.status ?? endpoint.status;
|
||||
}
|
||||
|
||||
async navigateToAuthorize(serverUrl: string, returnUrl: string): Promise<void> {
|
||||
const endpoint = this.serverDirectory.ensureServerEndpoint({
|
||||
name: this.buildEndpointName(serverUrl),
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import {
|
||||
describe,
|
||||
expect,
|
||||
it
|
||||
} from 'vitest';
|
||||
import { shouldNavigateToAuthorizeSignalServer } from './signal-server-authorize.rules';
|
||||
|
||||
describe('signal-server-authorize rules', () => {
|
||||
it('does not navigate to authorize when the signal server is offline', () => {
|
||||
expect(shouldNavigateToAuthorizeSignalServer('offline', {
|
||||
kind: 'skipped',
|
||||
reason: 'no-provision-secret'
|
||||
})).toBe(false);
|
||||
|
||||
expect(shouldNavigateToAuthorizeSignalServer('offline', {
|
||||
kind: 'collision',
|
||||
error: new Error('collision') as never
|
||||
})).toBe(false);
|
||||
});
|
||||
|
||||
it('navigates to authorize on online servers that need manual sign-in', () => {
|
||||
expect(shouldNavigateToAuthorizeSignalServer('online', {
|
||||
kind: 'skipped',
|
||||
reason: 'no-provision-secret'
|
||||
})).toBe(true);
|
||||
|
||||
expect(shouldNavigateToAuthorizeSignalServer('online', {
|
||||
kind: 'collision',
|
||||
error: new Error('collision') as never
|
||||
})).toBe(true);
|
||||
});
|
||||
|
||||
it('does not navigate for unknown endpoint status or non-authorize provision outcomes', () => {
|
||||
expect(shouldNavigateToAuthorizeSignalServer('unknown', {
|
||||
kind: 'skipped',
|
||||
reason: 'no-provision-secret'
|
||||
})).toBe(false);
|
||||
|
||||
expect(shouldNavigateToAuthorizeSignalServer('online', {
|
||||
kind: 'skipped',
|
||||
reason: 'no-home-user'
|
||||
})).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
import type { EnsureProvisionedResult } from '../../application/services/signal-server-auth.service';
|
||||
import type { ServerEndpointStatus } from '../../../server-directory/domain/models/server-directory.model';
|
||||
import { isEndpointOnlineForConnection } from '../../../server-directory/domain/logic/server-endpoint-connectivity.rules';
|
||||
|
||||
export function shouldNavigateToAuthorizeSignalServer(
|
||||
endpointStatus: ServerEndpointStatus | undefined | null,
|
||||
provisionResult: EnsureProvisionedResult
|
||||
): boolean {
|
||||
if (!isEndpointOnlineForConnection(endpointStatus)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (provisionResult.kind === 'collision') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return provisionResult.kind === 'skipped' && provisionResult.reason === 'no-provision-secret';
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
type RoomSignalSource,
|
||||
type RoomSignalSourceInput
|
||||
} from '../../domain/logic/room-signal-source.logic';
|
||||
import { isEndpointOnlineForConnection } from '../../domain/logic/server-endpoint-connectivity.rules';
|
||||
import { ServerEndpointCompatibilityService } from '../../infrastructure/services/server-endpoint-compatibility.service';
|
||||
import { ServerEndpointHealthService } from '../../infrastructure/services/server-endpoint-health.service';
|
||||
import { ServerEndpointStateService } from './server-endpoint-state.service';
|
||||
@@ -110,7 +111,7 @@ export class ServerDirectoryService {
|
||||
|
||||
const clientVersion = await this.endpointCompatibility.getClientVersion();
|
||||
|
||||
if (!clientVersion) {
|
||||
if (!clientVersion && isEndpointOnlineForConnection(endpoint.status)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -118,7 +119,7 @@ export class ServerDirectoryService {
|
||||
|
||||
const refreshedEndpoint = this.servers().find((candidate) => candidate.id === endpoint.id);
|
||||
|
||||
return !!refreshedEndpoint && refreshedEndpoint.status !== 'incompatible';
|
||||
return isEndpointOnlineForConnection(refreshedEndpoint?.status);
|
||||
}
|
||||
|
||||
resolveRoomEndpoint(
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import {
|
||||
describe,
|
||||
expect,
|
||||
it
|
||||
} from 'vitest';
|
||||
import { isEndpointOnlineForConnection } from './server-endpoint-connectivity.rules';
|
||||
|
||||
describe('server-endpoint-connectivity rules', () => {
|
||||
it('treats only online endpoints as reachable for connection', () => {
|
||||
expect(isEndpointOnlineForConnection('online')).toBe(true);
|
||||
expect(isEndpointOnlineForConnection('offline')).toBe(false);
|
||||
expect(isEndpointOnlineForConnection('checking')).toBe(false);
|
||||
expect(isEndpointOnlineForConnection('unknown')).toBe(false);
|
||||
expect(isEndpointOnlineForConnection('incompatible')).toBe(false);
|
||||
expect(isEndpointOnlineForConnection(undefined)).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { ServerEndpointStatus } from '../models/server-directory.model';
|
||||
|
||||
/** True only when health checks report the endpoint is reachable. */
|
||||
export function isEndpointOnlineForConnection(
|
||||
status: ServerEndpointStatus | undefined | null
|
||||
): boolean {
|
||||
return status === 'online';
|
||||
}
|
||||
Reference in New Issue
Block a user