refactor: stricter domain: server-directory
This commit is contained in:
@@ -14,15 +14,23 @@ server-directory/
|
|||||||
│ └── server-endpoint-state.service.ts Signal-based endpoint list, reconciliation with defaults, localStorage persistence
|
│ └── server-endpoint-state.service.ts Signal-based endpoint list, reconciliation with defaults, localStorage persistence
|
||||||
│
|
│
|
||||||
├── domain/
|
├── domain/
|
||||||
│ ├── server-directory.models.ts ServerEndpoint, ServerInfo, ServerJoinAccessResponse, invite/ban/kick types
|
│ ├── constants/
|
||||||
│ ├── server-directory.constants.ts CLIENT_UPDATE_REQUIRED_MESSAGE
|
│ │ └── server-directory.constants.ts CLIENT_UPDATE_REQUIRED_MESSAGE
|
||||||
│ └── server-endpoint-defaults.ts Default endpoint templates, URL sanitisation, reconciliation helpers
|
│ ├── logic/
|
||||||
|
│ │ ├── room-signal-source.logic.ts Room → signal-source selector resolution
|
||||||
|
│ │ ├── room-signal-source.logic.spec.ts Unit tests
|
||||||
|
│ │ └── server-endpoint-defaults.logic.ts Default endpoint templates, URL sanitisation, reconciliation helpers
|
||||||
|
│ └── models/
|
||||||
|
│ └── server-directory.model.ts ServerEndpoint, ServerInfo, ServerJoinAccessResponse, invite/ban/kick types
|
||||||
│
|
│
|
||||||
├── infrastructure/
|
├── infrastructure/
|
||||||
│ ├── server-directory-api.service.ts HTTP client for all server API calls
|
│ ├── constants/
|
||||||
│ ├── server-endpoint-health.service.ts Health probe (GET /api/health with 5 s timeout, fallback to /api/servers)
|
│ │ └── server-directory.infrastructure.constants.ts Health-check timeout, localStorage keys
|
||||||
│ ├── server-endpoint-compatibility.service.ts Semantic version comparison for client/server compatibility
|
│ └── services/
|
||||||
│ └── server-endpoint-storage.service.ts localStorage read/write for endpoint list and removed-default tracking
|
│ ├── server-directory-api.service.ts HTTP client for all server API calls
|
||||||
|
│ ├── server-endpoint-compatibility.service.ts Semantic version comparison for client/server compatibility
|
||||||
|
│ ├── server-endpoint-health.service.ts Health probe (GET /api/health with 5 s timeout, fallback to /api/servers)
|
||||||
|
│ └── server-endpoint-storage.service.ts localStorage read/write for endpoint list and removed-default tracking
|
||||||
│
|
│
|
||||||
├── feature/
|
├── feature/
|
||||||
│ ├── invite/ Invite creation and resolution UI
|
│ ├── invite/ Invite creation and resolution UI
|
||||||
@@ -45,8 +53,8 @@ graph TD
|
|||||||
Health[ServerEndpointHealthService]
|
Health[ServerEndpointHealthService]
|
||||||
Compat[ServerEndpointCompatibilityService]
|
Compat[ServerEndpointCompatibilityService]
|
||||||
Storage[ServerEndpointStorageService]
|
Storage[ServerEndpointStorageService]
|
||||||
Defaults[server-endpoint-defaults]
|
Defaults[server-endpoint-defaults.logic]
|
||||||
Models[server-directory.models]
|
Models[server-directory.model]
|
||||||
|
|
||||||
Facade --> Service
|
Facade --> Service
|
||||||
Service --> API
|
Service --> API
|
||||||
@@ -61,12 +69,12 @@ graph TD
|
|||||||
click Facade "application/facades/server-directory.facade.ts" "Thin domain boundary" _blank
|
click Facade "application/facades/server-directory.facade.ts" "Thin domain boundary" _blank
|
||||||
click Service "application/services/server-directory.service.ts" "Orchestrator" _blank
|
click Service "application/services/server-directory.service.ts" "Orchestrator" _blank
|
||||||
click State "application/services/server-endpoint-state.service.ts" "Signal-based endpoint state" _blank
|
click State "application/services/server-endpoint-state.service.ts" "Signal-based endpoint state" _blank
|
||||||
click API "infrastructure/server-directory-api.service.ts" "HTTP client for server API" _blank
|
click API "infrastructure/services/server-directory-api.service.ts" "HTTP client for server API" _blank
|
||||||
click Health "infrastructure/server-endpoint-health.service.ts" "Health probe" _blank
|
click Health "infrastructure/services/server-endpoint-health.service.ts" "Health probe" _blank
|
||||||
click Compat "infrastructure/server-endpoint-compatibility.service.ts" "Version compatibility" _blank
|
click Compat "infrastructure/services/server-endpoint-compatibility.service.ts" "Version compatibility" _blank
|
||||||
click Storage "infrastructure/server-endpoint-storage.service.ts" "localStorage persistence" _blank
|
click Storage "infrastructure/services/server-endpoint-storage.service.ts" "localStorage persistence" _blank
|
||||||
click Defaults "domain/server-endpoint-defaults.ts" "Default endpoint templates" _blank
|
click Defaults "domain/logic/server-endpoint-defaults.logic.ts" "Default endpoint templates" _blank
|
||||||
click Models "domain/server-directory.models.ts" "Domain types" _blank
|
click Models "domain/models/server-directory.model.ts" "Domain types" _blank
|
||||||
```
|
```
|
||||||
|
|
||||||
## Endpoint lifecycle
|
## Endpoint lifecycle
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { STORAGE_KEY_CONNECTION_SETTINGS } from '../../../../core/constants';
|
import { STORAGE_KEY_CONNECTION_SETTINGS } from '../../../../core/constants';
|
||||||
import { User } from '../../../../shared-kernel';
|
import { User } from '../../../../shared-kernel';
|
||||||
import { ServerDirectoryApiService } from '../../infrastructure/server-directory-api.service';
|
import { ServerDirectoryApiService } from '../../infrastructure/services/server-directory-api.service';
|
||||||
import type {
|
import type {
|
||||||
BanServerMemberRequest,
|
BanServerMemberRequest,
|
||||||
CreateServerInviteRequest,
|
CreateServerInviteRequest,
|
||||||
@@ -19,15 +19,15 @@ import type {
|
|||||||
ServerJoinAccessResponse,
|
ServerJoinAccessResponse,
|
||||||
ServerSourceSelector,
|
ServerSourceSelector,
|
||||||
UnbanServerMemberRequest
|
UnbanServerMemberRequest
|
||||||
} from '../../domain/server-directory.models';
|
} from '../../domain/models/server-directory.model';
|
||||||
import {
|
import {
|
||||||
buildRoomSignalSelector,
|
buildRoomSignalSelector,
|
||||||
buildRoomSignalSource,
|
buildRoomSignalSource,
|
||||||
type RoomSignalSource,
|
type RoomSignalSource,
|
||||||
type RoomSignalSourceInput
|
type RoomSignalSourceInput
|
||||||
} from '../../domain/room-signal-source';
|
} from '../../domain/logic/room-signal-source.logic';
|
||||||
import { ServerEndpointCompatibilityService } from '../../infrastructure/server-endpoint-compatibility.service';
|
import { ServerEndpointCompatibilityService } from '../../infrastructure/services/server-endpoint-compatibility.service';
|
||||||
import { ServerEndpointHealthService } from '../../infrastructure/server-endpoint-health.service';
|
import { ServerEndpointHealthService } from '../../infrastructure/services/server-endpoint-health.service';
|
||||||
import { ServerEndpointStateService } from './server-endpoint-state.service';
|
import { ServerEndpointStateService } from './server-endpoint-state.service';
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ import {
|
|||||||
hasEndpointForDefault,
|
hasEndpointForDefault,
|
||||||
matchDefaultEndpointTemplate,
|
matchDefaultEndpointTemplate,
|
||||||
sanitiseServerBaseUrl
|
sanitiseServerBaseUrl
|
||||||
} from '../../domain/server-endpoint-defaults';
|
} from '../../domain/logic/server-endpoint-defaults.logic';
|
||||||
import { ServerEndpointStorageService } from '../../infrastructure/server-endpoint-storage.service';
|
import { ServerEndpointStorageService } from '../../infrastructure/services/server-endpoint-storage.service';
|
||||||
import type {
|
import type {
|
||||||
ConfiguredDefaultServerDefinition,
|
ConfiguredDefaultServerDefinition,
|
||||||
DefaultEndpointTemplate,
|
DefaultEndpointTemplate,
|
||||||
ServerEndpoint,
|
ServerEndpoint,
|
||||||
ServerEndpointVersions
|
ServerEndpointVersions
|
||||||
} from '../../domain/server-directory.models';
|
} from '../../domain/models/server-directory.model';
|
||||||
|
|
||||||
function resolveDefaultHttpProtocol(): 'http' | 'https' {
|
function resolveDefaultHttpProtocol(): 'http' | 'https' {
|
||||||
return typeof window !== 'undefined' && window.location?.protocol === 'https:'
|
return typeof window !== 'undefined' && window.location?.protocol === 'https:'
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import {
|
|||||||
buildRoomSignalSelector,
|
buildRoomSignalSelector,
|
||||||
buildRoomSignalSource,
|
buildRoomSignalSource,
|
||||||
getSourceUrlFromSignalingUrl
|
getSourceUrlFromSignalingUrl
|
||||||
} from './room-signal-source';
|
} from './room-signal-source.logic';
|
||||||
|
|
||||||
describe('room-signal-source helpers', () => {
|
describe('room-signal-source helpers', () => {
|
||||||
it('converts signaling urls back to normalized source urls', () => {
|
it('converts signaling urls back to normalized source urls', () => {
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { ServerEndpoint, ServerSourceSelector } from './server-directory.models';
|
import type { ServerEndpoint, ServerSourceSelector } from '../models/server-directory.model';
|
||||||
import { normaliseConfiguredServerUrl, sanitiseServerBaseUrl } from './server-endpoint-defaults';
|
import { normaliseConfiguredServerUrl, sanitiseServerBaseUrl } from './server-endpoint-defaults.logic';
|
||||||
|
|
||||||
export interface RoomSignalSource {
|
export interface RoomSignalSource {
|
||||||
sourceId?: string;
|
sourceId?: string;
|
||||||
@@ -3,7 +3,7 @@ import type {
|
|||||||
DefaultEndpointTemplate,
|
DefaultEndpointTemplate,
|
||||||
DefaultServerDefinition,
|
DefaultServerDefinition,
|
||||||
ServerEndpoint
|
ServerEndpoint
|
||||||
} from './server-directory.models';
|
} from '../models/server-directory.model';
|
||||||
|
|
||||||
export function sanitiseServerBaseUrl(rawUrl: string): string {
|
export function sanitiseServerBaseUrl(rawUrl: string): string {
|
||||||
let cleaned = rawUrl.trim().replace(/\/+$/, '');
|
let cleaned = rawUrl.trim().replace(/\/+$/, '');
|
||||||
@@ -3,7 +3,7 @@ import type {
|
|||||||
ChannelPermissionOverride,
|
ChannelPermissionOverride,
|
||||||
RoomRole,
|
RoomRole,
|
||||||
RoomRoleAssignment
|
RoomRoleAssignment
|
||||||
} from '../../../shared-kernel';
|
} from '../../../../shared-kernel';
|
||||||
|
|
||||||
export type ServerEndpointStatus = 'online' | 'offline' | 'checking' | 'unknown' | 'incompatible';
|
export type ServerEndpointStatus = 'online' | 'offline' | 'checking' | 'unknown' | 'incompatible';
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ import { Store } from '@ngrx/store';
|
|||||||
import { RoomsActions } from '../../../../store/rooms/rooms.actions';
|
import { RoomsActions } from '../../../../store/rooms/rooms.actions';
|
||||||
import { UsersActions } from '../../../../store/users/users.actions';
|
import { UsersActions } from '../../../../store/users/users.actions';
|
||||||
import { selectCurrentUser } from '../../../../store/users/users.selectors';
|
import { selectCurrentUser } from '../../../../store/users/users.selectors';
|
||||||
import type { ServerInviteInfo } from '../../domain/server-directory.models';
|
import type { ServerInviteInfo } from '../../domain/models/server-directory.model';
|
||||||
import { STORAGE_KEY_CURRENT_USER_ID } from '../../../../core/constants';
|
import { STORAGE_KEY_CURRENT_USER_ID } from '../../../../core/constants';
|
||||||
import { DatabaseService } from '../../../../infrastructure/persistence';
|
import { DatabaseService } from '../../../../infrastructure/persistence';
|
||||||
import { ServerDirectoryFacade } from '../../application/facades/server-directory.facade';
|
import { ServerDirectoryFacade } from '../../application/facades/server-directory.facade';
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ import {
|
|||||||
import { Room, User } from '../../../../shared-kernel';
|
import { Room, User } from '../../../../shared-kernel';
|
||||||
import { SettingsModalService } from '../../../../core/services/settings-modal.service';
|
import { SettingsModalService } from '../../../../core/services/settings-modal.service';
|
||||||
import { DatabaseService } from '../../../../infrastructure/persistence';
|
import { DatabaseService } from '../../../../infrastructure/persistence';
|
||||||
import { type ServerInfo } from '../../domain/server-directory.models';
|
import { type ServerInfo } from '../../domain/models/server-directory.model';
|
||||||
import { ServerDirectoryFacade } from '../../application/facades/server-directory.facade';
|
import { ServerDirectoryFacade } from '../../application/facades/server-directory.facade';
|
||||||
import { selectCurrentUser } from '../../../../store/users/users.selectors';
|
import { selectCurrentUser } from '../../../../store/users/users.selectors';
|
||||||
import { ConfirmDialogComponent } from '../../../../shared';
|
import { ConfirmDialogComponent } from '../../../../shared';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export * from './application/facades/server-directory.facade';
|
export * from './application/facades/server-directory.facade';
|
||||||
export * from './domain/server-directory.constants';
|
export * from './domain/constants/server-directory.constants';
|
||||||
export * from './domain/server-directory.models';
|
export * from './domain/models/server-directory.model';
|
||||||
export * from './domain/room-signal-source';
|
export * from './domain/logic/room-signal-source.logic';
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ import {
|
|||||||
RoomRole,
|
RoomRole,
|
||||||
RoomRoleAssignment,
|
RoomRoleAssignment,
|
||||||
User
|
User
|
||||||
} from '../../../shared-kernel';
|
} from '../../../../shared-kernel';
|
||||||
import { ServerEndpointStateService } from '../application/services/server-endpoint-state.service';
|
import { ServerEndpointStateService } from '../../application/services/server-endpoint-state.service';
|
||||||
import type {
|
import type {
|
||||||
BanServerMemberRequest,
|
BanServerMemberRequest,
|
||||||
CreateServerInviteRequest,
|
CreateServerInviteRequest,
|
||||||
@@ -28,8 +28,8 @@ import type {
|
|||||||
ServerJoinAccessResponse,
|
ServerJoinAccessResponse,
|
||||||
ServerSourceSelector,
|
ServerSourceSelector,
|
||||||
UnbanServerMemberRequest
|
UnbanServerMemberRequest
|
||||||
} from '../domain/server-directory.models';
|
} from '../../domain/models/server-directory.model';
|
||||||
import type { RoomSignalSourceInput } from '../domain/room-signal-source';
|
import type { RoomSignalSourceInput } from '../../domain/logic/room-signal-source.logic';
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class ServerDirectoryApiService {
|
export class ServerDirectoryApiService {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Injectable, inject } from '@angular/core';
|
import { Injectable, inject } from '@angular/core';
|
||||||
import { ElectronBridgeService } from '../../../core/platform/electron/electron-bridge.service';
|
import { ElectronBridgeService } from '../../../../core/platform/electron/electron-bridge.service';
|
||||||
import type { ServerVersionCompatibilityResult } from '../domain/server-directory.models';
|
import type { ServerVersionCompatibilityResult } from '../../domain/models/server-directory.model';
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class ServerEndpointCompatibilityService {
|
export class ServerEndpointCompatibilityService {
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import { Injectable, inject } from '@angular/core';
|
import { Injectable, inject } from '@angular/core';
|
||||||
import { SERVER_HEALTH_CHECK_TIMEOUT_MS } from './server-directory.infrastructure.constants';
|
import { SERVER_HEALTH_CHECK_TIMEOUT_MS } from '../constants/server-directory.infrastructure.constants';
|
||||||
import type {
|
import type {
|
||||||
ServerEndpoint,
|
ServerEndpoint,
|
||||||
ServerEndpointHealthResult,
|
ServerEndpointHealthResult,
|
||||||
ServerHealthCheckPayload
|
ServerHealthCheckPayload
|
||||||
} from '../domain/server-directory.models';
|
} from '../../domain/models/server-directory.model';
|
||||||
import { ServerEndpointCompatibilityService } from './server-endpoint-compatibility.service';
|
import { ServerEndpointCompatibilityService } from './server-endpoint-compatibility.service';
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { REMOVED_DEFAULT_SERVER_KEYS_STORAGE_KEY, SERVER_ENDPOINTS_STORAGE_KEY } from './server-directory.infrastructure.constants';
|
import { REMOVED_DEFAULT_SERVER_KEYS_STORAGE_KEY, SERVER_ENDPOINTS_STORAGE_KEY } from '../constants/server-directory.infrastructure.constants';
|
||||||
import type { ServerEndpoint } from '../domain/server-directory.models';
|
import type { ServerEndpoint } from '../../domain/models/server-directory.model';
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class ServerEndpointStorageService {
|
export class ServerEndpointStorageService {
|
||||||
Reference in New Issue
Block a user