mirror of
https://github.com/jwetzell/showbridge-webui.git
synced 2026-08-12 09:53:40 +00:00
add basics for connecting to running instance
This commit is contained in:
Generated
-74
@@ -20,7 +20,6 @@
|
||||
"ajv": "8.17.1",
|
||||
"js-yaml": "4.1.1",
|
||||
"lodash-es": "4.17.23",
|
||||
"ngx-timeago": "4.0.0",
|
||||
"rxjs": "7.8.2",
|
||||
"tslib": "2.3.0"
|
||||
},
|
||||
@@ -4153,66 +4152,6 @@
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": {
|
||||
"version": "1.4.5",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/wasi-threads": "1.0.4",
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": {
|
||||
"version": "1.4.5",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
|
||||
"version": "1.0.4",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": {
|
||||
"version": "0.2.12",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/core": "^1.4.3",
|
||||
"@emnapi/runtime": "^1.4.3",
|
||||
"@tybys/wasm-util": "^0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": {
|
||||
"version": "0.10.0",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": {
|
||||
"version": "2.8.0",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "0BSD",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
|
||||
"version": "4.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.12.tgz",
|
||||
@@ -7241,19 +7180,6 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/ngx-timeago": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ngx-timeago/-/ngx-timeago-4.0.0.tgz",
|
||||
"integrity": "sha512-pTzQyOzefsDGcaM5b4aVtM1gDiP8bteQBTJNyQ/pKR0Hbgue08Zq/SztJK8zZCo08kLi8WiC0DvgWzfQF30C1g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": ">=20.0.0",
|
||||
"rxjs": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-addon-api": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz",
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
"ajv": "8.17.1",
|
||||
"js-yaml": "4.1.1",
|
||||
"lodash-es": "4.17.23",
|
||||
"ngx-timeago": "4.0.0",
|
||||
"rxjs": "7.8.2",
|
||||
"tslib": "2.3.0"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
<div class="h-screen max-h-screen flex flex-col">
|
||||
<mat-toolbar color="primary" class="w-full">
|
||||
@if(eventsService.status() === 'open') {
|
||||
<div matTooltip="Connected" class="h-3 w-3 rounded-full bg-green-400 mr-2"></div>
|
||||
} @else if (eventsService.status() === 'closed') {
|
||||
<div matTooltip="Disconnected" class="h-3 w-3 rounded-full bg-orange-400 mr-2"></div>
|
||||
} @else if (eventsService.status() === 'error') {
|
||||
<div matTooltip="Error" class="h-3 w-3 rounded-full bg-red-400 mr-2"></div>
|
||||
}
|
||||
<span>showbridge</span>
|
||||
<span class="flex-auto"></span>
|
||||
<button mat-icon-button matTooltip="Download Config" (click)="downloadConfig()">
|
||||
|
||||
+8
-6
@@ -1,18 +1,18 @@
|
||||
import { Component, effect, inject } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import { TimeagoModule } from 'ngx-timeago';
|
||||
import { RouterOutlet } from '@angular/router';
|
||||
import * as yaml from 'js-yaml';
|
||||
import { Config } from './models/config.models';
|
||||
import { ConfigService } from './services/config.service';
|
||||
import { EventsService } from './services/events.service';
|
||||
import { SchemaService } from './services/schema.service';
|
||||
import { ConfigComponent } from './components/config/config.component';
|
||||
import * as yaml from 'js-yaml';
|
||||
import { RouterOutlet } from '@angular/router';
|
||||
import { SettingsService } from './services/settings.service';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@@ -20,9 +20,9 @@ import { RouterOutlet } from '@angular/router';
|
||||
MatToolbarModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatIconModule,
|
||||
TimeagoModule,
|
||||
MatMenuModule,
|
||||
MatButtonModule,
|
||||
MatTooltipModule,
|
||||
RouterOutlet
|
||||
],
|
||||
templateUrl: './app.html',
|
||||
@@ -32,6 +32,8 @@ export class App {
|
||||
public schemaService = inject(SchemaService);
|
||||
public configService = inject(ConfigService);
|
||||
|
||||
public settingsService = inject(SettingsService);
|
||||
public eventsService = inject(EventsService);
|
||||
private snackBar = inject(MatSnackBar);
|
||||
constructor() {
|
||||
effect(() => {
|
||||
|
||||
@@ -8,6 +8,7 @@ import { ConfigService } from '../../services/config.service';
|
||||
import { SchemaService } from '../../services/schema.service';
|
||||
import { ModuleListComponent } from '../module-list/module-list.component';
|
||||
import { RouteListComponent } from '../route-list.component/route-list.component';
|
||||
import { EventsService } from '../../services/events.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-config',
|
||||
@@ -39,6 +40,8 @@ export class ConfigComponent {
|
||||
public configService = inject(ConfigService);
|
||||
public schemaService = inject(SchemaService);
|
||||
|
||||
public eventsService = inject(EventsService);
|
||||
|
||||
modulesUpdated(modules: ModuleConfiguration[] | undefined) {
|
||||
if (modules === undefined) {
|
||||
console.error('modules is undefined, not updating');
|
||||
|
||||
@@ -4,14 +4,22 @@
|
||||
isInError() ? 'border-red-500' : 'border-gray-600'
|
||||
}} border-solid w-full h-full"
|
||||
>
|
||||
<div class="flex items-center justify-center border-gray-600 border-b-2">
|
||||
<div class="flex-grow ml-1 mr-3 text-white">
|
||||
<div class="flex items-center justify-center mx-2">
|
||||
<div class="flex items-center">
|
||||
<mat-icon [style.color]="inputIndicatorColor()" matTooltip="Input Indicator">south</mat-icon>
|
||||
</div>
|
||||
<div class="text-white">
|
||||
{{ schema().title || module()?.type }}
|
||||
</div>
|
||||
<div class="flex items-center justify-center" (click)="delete.emit()">
|
||||
<div class="flex items-center">
|
||||
<mat-icon [style.color]="outputIndicatorColor()" matTooltip="Output Indicator">north</mat-icon>
|
||||
</div>
|
||||
<div class="flex-grow"></div>
|
||||
<div class="flex items-center justify-center hover:bg-gray-700" (click)="delete.emit()">
|
||||
<mat-icon class="!text-red-500">close</mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="bg-gray-600 h-0.5 border-0">
|
||||
<div>
|
||||
<form [formGroup]="formGroup" class="h-full">
|
||||
<div class="m-2">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, computed, inject, input, model, output } from '@angular/core';
|
||||
import { Component, computed, inject, input, model, output, signal } from '@angular/core';
|
||||
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
@@ -8,6 +8,8 @@ import { SchemaService } from '../../services/schema.service';
|
||||
import { ParamsFormComponent } from '../params-form/params-form.component';
|
||||
import { JsonPipe } from '@angular/common';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { EventsService } from '../../services/events.service';
|
||||
import { tap, debounceTime } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'app-module',
|
||||
@@ -42,8 +44,11 @@ export class ModuleComponent {
|
||||
type: new FormControl(''),
|
||||
});
|
||||
|
||||
private schemaService = inject(SchemaService);
|
||||
inputIndicatorColor = signal<string>('gray');
|
||||
outputIndicatorColor = signal<string>('gray');
|
||||
|
||||
private schemaService = inject(SchemaService);
|
||||
private eventsService = inject(EventsService);
|
||||
ngOnInit(): void {
|
||||
this.formGroup.patchValue({
|
||||
id: this.module()?.id,
|
||||
@@ -64,6 +69,30 @@ export class ModuleComponent {
|
||||
return undefined;
|
||||
});
|
||||
});
|
||||
if (this.id() !== undefined) {
|
||||
this.eventsService
|
||||
.getInputEventsForSource(this.id()!)
|
||||
.pipe(
|
||||
tap((inputEvent) => {
|
||||
this.inputIndicatorColor.set(inputEvent.error ? 'red' : 'greenyellow');
|
||||
}),
|
||||
debounceTime(100),
|
||||
)
|
||||
.subscribe((inputEvent) => {
|
||||
this.inputIndicatorColor.set('gray');
|
||||
});
|
||||
this.eventsService
|
||||
.getOutputEventsForDestination(this.id()!)
|
||||
.pipe(
|
||||
tap((outputEvent) => {
|
||||
this.outputIndicatorColor.set(outputEvent.error ? 'red' : 'greenyellow');
|
||||
}),
|
||||
debounceTime(100),
|
||||
)
|
||||
.subscribe((outputEvent) => {
|
||||
this.outputIndicatorColor.set('gray');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
paramsUpdated(params: any) {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
isInError() ? 'border-red-500' : 'border-gray-600'
|
||||
}} border-solid w-fit h-full"
|
||||
>
|
||||
<div class="flex items-center justify-center border-gray-600 border-b-2">
|
||||
<div class="flex items-center justify-center">
|
||||
<div class="flex-grow ml-1 mr-3 text-white">
|
||||
{{ schema()?.title || processor()?.type }}
|
||||
</div>
|
||||
@@ -14,6 +14,7 @@
|
||||
</div>
|
||||
</div>
|
||||
@if (hasParams()) {
|
||||
<hr class="bg-gray-600 h-0.5 border-0">
|
||||
<div>
|
||||
<app-params-form
|
||||
[paramsSchema]="schema()?.properties?.params"
|
||||
|
||||
@@ -5,12 +5,14 @@
|
||||
isInError() ? 'border-red-500' : 'border-gray-600'
|
||||
}} border-solid w-full h-full"
|
||||
>
|
||||
<div class="flex items-center justify-end border-gray-600 border-b-2">
|
||||
<div class="flex items-center justify-end mx-2">
|
||||
<div [style.backgroundColor]="indicatorColor()" class="h-2 w-2 rounded-full"></div>
|
||||
<div class="flex-grow text-left ml-2 text-white">Route {{ index() }}</div>
|
||||
<div class="flex items-center justify-center hover:bg-gray-700" (click)="delete.emit()">
|
||||
<mat-icon class="!text-red-500">close</mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="bg-gray-600 h-0.5 border-0">
|
||||
<div>
|
||||
<form [formGroup]="formGroup">
|
||||
<div class="m-2">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, computed, inject, input, model, output } from '@angular/core';
|
||||
import { Component, computed, inject, input, model, output, signal } from '@angular/core';
|
||||
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
@@ -9,6 +9,8 @@ import { JsonPipe } from '@angular/common';
|
||||
import { ProcessorComponent } from '../processor/processor.component';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { EventsService } from '../../services/events.service';
|
||||
import { debounceTime, tap } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'app-route',
|
||||
@@ -55,6 +57,9 @@ export class RouteComponent {
|
||||
|
||||
public schemaService = inject(SchemaService);
|
||||
private snackBar = inject(MatSnackBar);
|
||||
private eventsService = inject(EventsService);
|
||||
|
||||
indicatorColor = signal<string>('gray');
|
||||
|
||||
ngOnInit(): void {
|
||||
this.formGroup.patchValue({
|
||||
@@ -73,6 +78,17 @@ export class RouteComponent {
|
||||
return undefined;
|
||||
});
|
||||
});
|
||||
|
||||
if (this.index() !== undefined) {
|
||||
this.eventsService.getRouteEventsForIndex(this.index()!).pipe(
|
||||
tap((routeEvent)=>{
|
||||
this.indicatorColor.set(routeEvent.error ? 'red' : 'greenyellow');
|
||||
}),
|
||||
debounceTime(100)
|
||||
).subscribe((routeEvent) => {
|
||||
this.indicatorColor.set('gray')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
isInError(): boolean {
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
|
||||
export type RouterEvent<T, D> = {
|
||||
type: T,
|
||||
data?: D,
|
||||
error?: string,
|
||||
}
|
||||
|
||||
export type RouteEventData = {
|
||||
index: number
|
||||
}
|
||||
|
||||
export type InputEventData = {
|
||||
source: string
|
||||
}
|
||||
|
||||
export type OutputEventData = {
|
||||
destination: string
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
import { computed, effect, Injectable, signal } from '@angular/core';
|
||||
import { computed, effect, inject, Injectable, signal } from '@angular/core';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { Config } from '../models/config.models';
|
||||
import { SchemaService } from './schema.service';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { SettingsService } from './settings.service';
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
@@ -15,6 +17,9 @@ export class ConfigService {
|
||||
});
|
||||
currentlyShownConfig = signal<Config | undefined>(undefined);
|
||||
|
||||
private http = inject(HttpClient);
|
||||
private settingsService = inject(SettingsService);
|
||||
|
||||
constructor(private schemaService: SchemaService) {
|
||||
this.loadConfig();
|
||||
effect(() => {
|
||||
@@ -23,25 +28,45 @@ export class ConfigService {
|
||||
}
|
||||
|
||||
loadConfig() {
|
||||
const configString = localStorage.getItem('config');
|
||||
if (configString) {
|
||||
try {
|
||||
const config = JSON.parse(configString);
|
||||
const valid = this.schemaService.validate(config);
|
||||
if (valid) {
|
||||
console.log('Loaded config from local storage', config);
|
||||
// const configString = localStorage.getItem('config');
|
||||
// if (configString) {
|
||||
// try {
|
||||
// const config = JSON.parse(configString);
|
||||
// const valid = this.schemaService.validate(config);
|
||||
// if (valid) {
|
||||
// console.log('Loaded config from local storage', config);
|
||||
// this.updateCurrentlyShownConfig(config);
|
||||
// } else {
|
||||
// console.error('Config in local storage is invalid', config);
|
||||
// this.setEmptyConfig();
|
||||
// }
|
||||
// } catch (e) {
|
||||
// console.error('Failed to parse config from local storage', e);
|
||||
// this.setEmptyConfig();
|
||||
// }
|
||||
// } else {
|
||||
// this.setEmptyConfig();
|
||||
// }
|
||||
const configUrl = this.settingsService.configUrl();
|
||||
if (!configUrl) {
|
||||
console.error('Config URL is not set');
|
||||
this.setEmptyConfig();
|
||||
return;
|
||||
}
|
||||
this.http.get<Config>(configUrl.toString()).subscribe({
|
||||
next: (config) => {
|
||||
if (this.schemaService.validate(config)) {
|
||||
this.updateCurrentlyShownConfig(config);
|
||||
} else {
|
||||
console.error('Config in local storage is invalid', config);
|
||||
console.error('Config from server is invalid', config);
|
||||
this.setEmptyConfig();
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to parse config from local storage', e);
|
||||
},
|
||||
error: (err) => {
|
||||
console.error('Failed to load config from server', err);
|
||||
this.setEmptyConfig();
|
||||
}
|
||||
} else {
|
||||
this.setEmptyConfig();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
setEmptyConfig() {
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
import { effect, inject, Injectable, signal } from '@angular/core';
|
||||
import { InputEventData, OutputEventData, RouteEventData, RouterEvent } from '../models/events.models';
|
||||
import { filter, Subject } from 'rxjs';
|
||||
import { SettingsService } from './settings.service';
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class EventsService {
|
||||
|
||||
status = signal<string>('closed');
|
||||
socket?: WebSocket;
|
||||
private routeEvents$ = new Subject<RouterEvent<'route', RouteEventData>>();
|
||||
private inputEvents$ = new Subject<RouterEvent<'input', InputEventData>>();
|
||||
private outputEvents$ = new Subject<RouterEvent<'output', OutputEventData>>();
|
||||
|
||||
private settingsService = inject(SettingsService);
|
||||
|
||||
constructor() {
|
||||
effect(() => {
|
||||
console.log('Websocket URL changed:', this.settingsService.wsUrl());
|
||||
this.reload();
|
||||
});
|
||||
}
|
||||
|
||||
reload() {
|
||||
try {
|
||||
this.socket = new WebSocket(this.settingsService.wsUrl());
|
||||
this.socket.onopen = () => {
|
||||
this.status.set('open');
|
||||
}
|
||||
|
||||
this.socket.onclose = () => {
|
||||
this.status.set('closed');
|
||||
setTimeout(() => {
|
||||
this.reload();
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
this.socket.onerror = (error) => {
|
||||
this.status.set('error');
|
||||
}
|
||||
|
||||
this.socket.onmessage = (event) => {
|
||||
const messageObj = JSON.parse(event.data);
|
||||
switch (messageObj.type) {
|
||||
case 'route':
|
||||
this.routeEvents$.next(messageObj);
|
||||
break;
|
||||
case 'input':
|
||||
this.inputEvents$.next(messageObj);
|
||||
break;
|
||||
case 'output':
|
||||
this.outputEvents$.next(messageObj);
|
||||
break;
|
||||
default:
|
||||
console.warn('Unknown event type:', messageObj.type);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to connect to WebSocket:', error);
|
||||
}
|
||||
}
|
||||
|
||||
getRouteEventsForIndex(index: number) {
|
||||
return this.routeEvents$.pipe(
|
||||
filter(event => event.data?.index === index)
|
||||
);
|
||||
}
|
||||
|
||||
getInputEventsForSource(source: string) {
|
||||
return this.inputEvents$.pipe(
|
||||
filter(event => event.data?.source === source)
|
||||
);
|
||||
}
|
||||
|
||||
getOutputEventsForDestination(destination: string) {
|
||||
return this.outputEvents$.pipe(
|
||||
filter(event => event.data?.destination === destination)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,11 +5,17 @@ import { computed, Injectable, signal } from '@angular/core';
|
||||
})
|
||||
export class SettingsService {
|
||||
public isDummySite: boolean = false;
|
||||
public configSchemaPath = signal('/config.schema.json');
|
||||
public modulesSchemaPath = signal('/modules.schema.json');
|
||||
public routesSchemaPath = signal('/routes.schema.json');
|
||||
public processorsSchemaPath = signal('/processors.schema.json');
|
||||
public baseUrl = signal(window.location.href);
|
||||
public configPath = signal('/api/v1/config');
|
||||
public configSchemaPath = signal('/api/v1/schema/config');
|
||||
public modulesSchemaPath = signal('/api/v1/schema/modules');
|
||||
public routesSchemaPath = signal('/api/v1/schema/routes');
|
||||
public processorsSchemaPath = signal('/api/v1/schema/processors');
|
||||
public wsPath = signal('/ws');
|
||||
public baseUrl = signal('http://localhost:8080');
|
||||
|
||||
public configUrl = computed(() => {
|
||||
return new URL(this.configPath(), this.baseUrl());
|
||||
});
|
||||
|
||||
public configSchemaUrl = computed(() => {
|
||||
return new URL(this.configSchemaPath(), this.baseUrl());
|
||||
@@ -27,6 +33,13 @@ export class SettingsService {
|
||||
return new URL(this.processorsSchemaPath(), this.baseUrl());
|
||||
});
|
||||
|
||||
public wsUrl = computed(() => {
|
||||
const url = new URL(this.baseUrl());
|
||||
url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
url.pathname = this.wsPath();
|
||||
return url;
|
||||
});
|
||||
|
||||
constructor() {}
|
||||
|
||||
updateBaseUrl(baseUrl: string) {
|
||||
|
||||
Reference in New Issue
Block a user