Added support for subpaths in siteURL (#1110)
* Added support for subpaths in siteURL * Removed a debug statement Co-authored-by: Jesús Espino <jespinog@gmail.com> * Updated channel header to support subpaths Co-authored-by: Jesús Espino <jespinog@gmail.com>
This commit is contained in:
parent
c56e6edc51
commit
de5f06c426
3 changed files with 81 additions and 30 deletions
|
@ -20,6 +20,7 @@ import store from '../../../webapp/src/store'
|
||||||
import GlobalHeader from '../../../webapp/src/components/globalHeader/globalHeader'
|
import GlobalHeader from '../../../webapp/src/components/globalHeader/globalHeader'
|
||||||
import FocalboardIcon from '../../../webapp/src/widgets/icons/logo'
|
import FocalboardIcon from '../../../webapp/src/widgets/icons/logo'
|
||||||
import {setMattermostTheme} from '../../../webapp/src/theme'
|
import {setMattermostTheme} from '../../../webapp/src/theme'
|
||||||
|
|
||||||
import wsClient, {MMWebSocketClient, ACTION_UPDATE_BLOCK} from './../../../webapp/src/wsclient'
|
import wsClient, {MMWebSocketClient, ACTION_UPDATE_BLOCK} from './../../../webapp/src/wsclient'
|
||||||
|
|
||||||
import TelemetryClient from '../../../webapp/src/telemetry/telemetryClient'
|
import TelemetryClient from '../../../webapp/src/telemetry/telemetryClient'
|
||||||
|
@ -37,6 +38,13 @@ import {PluginRegistry} from './types/mattermost-webapp'
|
||||||
|
|
||||||
import './plugin.scss'
|
import './plugin.scss'
|
||||||
|
|
||||||
|
function getSubpath(siteURL: string): string {
|
||||||
|
const url = new URL(siteURL)
|
||||||
|
|
||||||
|
// remove trailing slashes
|
||||||
|
return url.pathname.replace(/\/+$/, '')
|
||||||
|
}
|
||||||
|
|
||||||
const TELEMETRY_RUDDER_KEY = 'placeholder_rudder_key'
|
const TELEMETRY_RUDDER_KEY = 'placeholder_rudder_key'
|
||||||
const TELEMETRY_RUDDER_DATAPLANE_URL = 'placeholder_rudder_dataplane_url'
|
const TELEMETRY_RUDDER_DATAPLANE_URL = 'placeholder_rudder_dataplane_url'
|
||||||
const TELEMETRY_OPTIONS = {
|
const TELEMETRY_OPTIONS = {
|
||||||
|
@ -115,6 +123,11 @@ export default class Plugin {
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
|
||||||
async initialize(registry: PluginRegistry, mmStore: Store<GlobalState, Action<Record<string, unknown>>>): Promise<void> {
|
async initialize(registry: PluginRegistry, mmStore: Store<GlobalState, Action<Record<string, unknown>>>): Promise<void> {
|
||||||
|
const siteURL = mmStore.getState().entities.general.config.SiteURL
|
||||||
|
const subpath = siteURL ? getSubpath(siteURL) : ''
|
||||||
|
windowAny.frontendBaseURL = subpath + windowAny.frontendBaseURL
|
||||||
|
windowAny.baseURL = subpath + windowAny.baseURL
|
||||||
|
|
||||||
this.registry = registry
|
this.registry = registry
|
||||||
|
|
||||||
let theme = getTheme(mmStore.getState())
|
let theme = getTheme(mmStore.getState())
|
||||||
|
@ -136,10 +149,10 @@ export default class Plugin {
|
||||||
})
|
})
|
||||||
|
|
||||||
if (this.registry.registerProduct) {
|
if (this.registry.registerProduct) {
|
||||||
windowAny.frontendBaseURL = '/boards'
|
windowAny.frontendBaseURL = subpath + '/boards'
|
||||||
const goToFocalboardWorkspace = () => {
|
const goToFocalboardWorkspace = () => {
|
||||||
const currentChannel = mmStore.getState().entities.channels.currentChannelId
|
const currentChannel = mmStore.getState().entities.channels.currentChannelId
|
||||||
window.open(`${window.location.origin}/boards/workspace/${currentChannel}`)
|
window.open(`${windowAny.frontendBaseURL}/workspace/${currentChannel}`)
|
||||||
}
|
}
|
||||||
this.channelHeaderButtonId = registry.registerChannelHeaderButtonAction(<FocalboardIcon/>, goToFocalboardWorkspace, '', 'Boards')
|
this.channelHeaderButtonId = registry.registerChannelHeaderButtonAction(<FocalboardIcon/>, goToFocalboardWorkspace, '', 'Boards')
|
||||||
|
|
||||||
|
@ -163,7 +176,7 @@ export default class Plugin {
|
||||||
})
|
})
|
||||||
this.registry.registerProduct('/boards', 'product-boards', 'Boards', '/plug/focalboard/go-to-current-workspace', MainApp, HeaderComponent)
|
this.registry.registerProduct('/boards', 'product-boards', 'Boards', '/plug/focalboard/go-to-current-workspace', MainApp, HeaderComponent)
|
||||||
} else {
|
} else {
|
||||||
windowAny.frontendBaseURL = '/plug/focalboard'
|
windowAny.frontendBaseURL = subpath + '/plug/focalboard'
|
||||||
this.channelHeaderButtonId = registry.registerChannelHeaderButtonAction(<FocalboardIcon/>, () => {
|
this.channelHeaderButtonId = registry.registerChannelHeaderButtonAction(<FocalboardIcon/>, () => {
|
||||||
const currentChannel = mmStore.getState().entities.channels.currentChannelId
|
const currentChannel = mmStore.getState().entities.channels.currentChannelId
|
||||||
window.open(`${window.location.origin}/plug/focalboard/workspace/${currentChannel}`)
|
window.open(`${window.location.origin}/plug/focalboard/workspace/${currentChannel}`)
|
||||||
|
|
|
@ -12,7 +12,27 @@ import {ClientConfig} from './config/clientConfig'
|
||||||
// OctoClient is the client interface to the server APIs
|
// OctoClient is the client interface to the server APIs
|
||||||
//
|
//
|
||||||
class OctoClient {
|
class OctoClient {
|
||||||
readonly serverUrl: string
|
readonly serverUrl: string | undefined
|
||||||
|
private logged = false
|
||||||
|
|
||||||
|
// this need to be a function rather than a const because
|
||||||
|
// one of the global variable (`window.baseURL`) is set at runtime
|
||||||
|
// after the first instance of OctoClient is created.
|
||||||
|
// Avoiding the race condition becomes more complex than making
|
||||||
|
// the base URL dynamic though a function
|
||||||
|
private getBaseURL(): string {
|
||||||
|
const baseURL = (this.serverUrl || Utils.getBaseURL(true)).replace(/\/$/, '')
|
||||||
|
|
||||||
|
// Logging this for debugging.
|
||||||
|
// Logging just once to avoid log noise.
|
||||||
|
if (!this.logged) {
|
||||||
|
Utils.log(`OctoClient baseURL: ${baseURL}`)
|
||||||
|
this.logged = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseURL
|
||||||
|
}
|
||||||
|
|
||||||
get token(): string {
|
get token(): string {
|
||||||
return localStorage.getItem('focalboardSessionId') || ''
|
return localStorage.getItem('focalboardSessionId') || ''
|
||||||
}
|
}
|
||||||
|
@ -27,8 +47,7 @@ class OctoClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(serverUrl?: string, public workspaceId = '0') {
|
constructor(serverUrl?: string, public workspaceId = '0') {
|
||||||
this.serverUrl = (serverUrl || Utils.getBaseURL(true)).replace(/\/$/, '')
|
this.serverUrl = serverUrl
|
||||||
Utils.log(`OctoClient serverUrl: ${this.serverUrl}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getJson(response: Response, defaultValue: any): Promise<any> {
|
private async getJson(response: Response, defaultValue: any): Promise<any> {
|
||||||
|
@ -44,7 +63,7 @@ class OctoClient {
|
||||||
async login(username: string, password: string): Promise<boolean> {
|
async login(username: string, password: string): Promise<boolean> {
|
||||||
const path = '/api/v1/login'
|
const path = '/api/v1/login'
|
||||||
const body = JSON.stringify({username, password, type: 'normal'})
|
const body = JSON.stringify({username, password, type: 'normal'})
|
||||||
const response = await fetch(this.serverUrl + path, {
|
const response = await fetch(this.getBaseURL() + path, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: this.headers(),
|
headers: this.headers(),
|
||||||
body,
|
body,
|
||||||
|
@ -82,7 +101,7 @@ class OctoClient {
|
||||||
async register(email: string, username: string, password: string, token?: string): Promise<{code: number, json: any}> {
|
async register(email: string, username: string, password: string, token?: string): Promise<{code: number, json: any}> {
|
||||||
const path = '/api/v1/register'
|
const path = '/api/v1/register'
|
||||||
const body = JSON.stringify({email, username, password, token})
|
const body = JSON.stringify({email, username, password, token})
|
||||||
const response = await fetch(this.serverUrl + path, {
|
const response = await fetch(this.getBaseURL() + path, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: this.headers(),
|
headers: this.headers(),
|
||||||
body,
|
body,
|
||||||
|
@ -94,7 +113,7 @@ class OctoClient {
|
||||||
async changePassword(userId: string, oldPassword: string, newPassword: string): Promise<{code: number, json: any}> {
|
async changePassword(userId: string, oldPassword: string, newPassword: string): Promise<{code: number, json: any}> {
|
||||||
const path = `/api/v1/users/${encodeURIComponent(userId)}/changepassword`
|
const path = `/api/v1/users/${encodeURIComponent(userId)}/changepassword`
|
||||||
const body = JSON.stringify({oldPassword, newPassword})
|
const body = JSON.stringify({oldPassword, newPassword})
|
||||||
const response = await fetch(this.serverUrl + path, {
|
const response = await fetch(this.getBaseURL() + path, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: this.headers(),
|
headers: this.headers(),
|
||||||
body,
|
body,
|
||||||
|
@ -118,7 +137,7 @@ class OctoClient {
|
||||||
|
|
||||||
async getMe(): Promise<IUser | undefined> {
|
async getMe(): Promise<IUser | undefined> {
|
||||||
const path = '/api/v1/users/me'
|
const path = '/api/v1/users/me'
|
||||||
const response = await fetch(this.serverUrl + path, {headers: this.headers()})
|
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
@ -128,7 +147,7 @@ class OctoClient {
|
||||||
|
|
||||||
async getUser(userId: string): Promise<IUser | undefined> {
|
async getUser(userId: string): Promise<IUser | undefined> {
|
||||||
const path = `/api/v1/users/${encodeURIComponent(userId)}`
|
const path = `/api/v1/users/${encodeURIComponent(userId)}`
|
||||||
const response = await fetch(this.serverUrl + path, {headers: this.headers()})
|
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
@ -142,7 +161,7 @@ class OctoClient {
|
||||||
if (readToken) {
|
if (readToken) {
|
||||||
path += `&read_token=${readToken}`
|
path += `&read_token=${readToken}`
|
||||||
}
|
}
|
||||||
const response = await fetch(this.serverUrl + path, {headers: this.headers()})
|
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
@ -153,7 +172,7 @@ class OctoClient {
|
||||||
// If no boardID is provided, it will export the entire archive
|
// If no boardID is provided, it will export the entire archive
|
||||||
async exportArchive(boardID = ''): Promise<Block[]> {
|
async exportArchive(boardID = ''): Promise<Block[]> {
|
||||||
const path = `${this.workspacePath()}/blocks/export?root_id=${boardID}`
|
const path = `${this.workspacePath()}/blocks/export?root_id=${boardID}`
|
||||||
const response = await fetch(this.serverUrl + path, {headers: this.headers()})
|
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
@ -168,7 +187,7 @@ class OctoClient {
|
||||||
// Utils.log(`\t ${block.type}, ${block.id}`)
|
// Utils.log(`\t ${block.type}, ${block.id}`)
|
||||||
// })
|
// })
|
||||||
const body = JSON.stringify(blocks)
|
const body = JSON.stringify(blocks)
|
||||||
return fetch(this.serverUrl + this.workspacePath() + '/blocks/import', {
|
return fetch(this.getBaseURL() + this.workspacePath() + '/blocks/import', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: this.headers(),
|
headers: this.headers(),
|
||||||
body,
|
body,
|
||||||
|
@ -196,7 +215,7 @@ class OctoClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getBlocksWithPath(path: string): Promise<Block[]> {
|
private async getBlocksWithPath(path: string): Promise<Block[]> {
|
||||||
const response = await fetch(this.serverUrl + path, {headers: this.headers()})
|
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
@ -243,7 +262,7 @@ class OctoClient {
|
||||||
async patchBlock(blockId: string, blockPatch: BlockPatch): Promise<Response> {
|
async patchBlock(blockId: string, blockPatch: BlockPatch): Promise<Response> {
|
||||||
Utils.log(`patchBlocks: ${blockId} block`)
|
Utils.log(`patchBlocks: ${blockId} block`)
|
||||||
const body = JSON.stringify(blockPatch)
|
const body = JSON.stringify(blockPatch)
|
||||||
return fetch(this.serverUrl + this.workspacePath() + '/blocks/' + blockId, {
|
return fetch(this.getBaseURL() + this.workspacePath() + '/blocks/' + blockId, {
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
headers: this.headers(),
|
headers: this.headers(),
|
||||||
body,
|
body,
|
||||||
|
@ -256,7 +275,7 @@ class OctoClient {
|
||||||
|
|
||||||
async deleteBlock(blockId: string): Promise<Response> {
|
async deleteBlock(blockId: string): Promise<Response> {
|
||||||
Utils.log(`deleteBlock: ${blockId}`)
|
Utils.log(`deleteBlock: ${blockId}`)
|
||||||
return fetch(this.serverUrl + this.workspacePath() + `/blocks/${encodeURIComponent(blockId)}`, {
|
return fetch(this.getBaseURL() + this.workspacePath() + `/blocks/${encodeURIComponent(blockId)}`, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
headers: this.headers(),
|
headers: this.headers(),
|
||||||
})
|
})
|
||||||
|
@ -272,7 +291,7 @@ class OctoClient {
|
||||||
Utils.log(`\t ${block.type}, ${block.id}, ${block.title?.substr(0, 50) || ''}`)
|
Utils.log(`\t ${block.type}, ${block.id}, ${block.title?.substr(0, 50) || ''}`)
|
||||||
})
|
})
|
||||||
const body = JSON.stringify(blocks)
|
const body = JSON.stringify(blocks)
|
||||||
return fetch(this.serverUrl + this.workspacePath() + '/blocks', {
|
return fetch(this.getBaseURL() + this.workspacePath() + '/blocks', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: this.headers(),
|
headers: this.headers(),
|
||||||
body,
|
body,
|
||||||
|
@ -283,7 +302,7 @@ class OctoClient {
|
||||||
|
|
||||||
async getSharing(rootId: string): Promise<ISharing | undefined> {
|
async getSharing(rootId: string): Promise<ISharing | undefined> {
|
||||||
const path = this.workspacePath() + `/sharing/${rootId}`
|
const path = this.workspacePath() + `/sharing/${rootId}`
|
||||||
const response = await fetch(this.serverUrl + path, {headers: this.headers()})
|
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
@ -295,7 +314,7 @@ class OctoClient {
|
||||||
const path = this.workspacePath() + `/sharing/${sharing.id}`
|
const path = this.workspacePath() + `/sharing/${sharing.id}`
|
||||||
const body = JSON.stringify(sharing)
|
const body = JSON.stringify(sharing)
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
this.serverUrl + path,
|
this.getBaseURL() + path,
|
||||||
{
|
{
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: this.headers(),
|
headers: this.headers(),
|
||||||
|
@ -313,7 +332,7 @@ class OctoClient {
|
||||||
|
|
||||||
async getWorkspace(): Promise<IWorkspace | undefined> {
|
async getWorkspace(): Promise<IWorkspace | undefined> {
|
||||||
const path = this.workspacePath()
|
const path = this.workspacePath()
|
||||||
const response = await fetch(this.serverUrl + path, {headers: this.headers()})
|
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
@ -323,7 +342,7 @@ class OctoClient {
|
||||||
|
|
||||||
async regenerateWorkspaceSignupToken(): Promise<boolean> {
|
async regenerateWorkspaceSignupToken(): Promise<boolean> {
|
||||||
const path = this.workspacePath() + '/regenerate_signup_token'
|
const path = this.workspacePath() + '/regenerate_signup_token'
|
||||||
const response = await fetch(this.serverUrl + path, {
|
const response = await fetch(this.getBaseURL() + path, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: this.headers(),
|
headers: this.headers(),
|
||||||
})
|
})
|
||||||
|
@ -348,7 +367,7 @@ class OctoClient {
|
||||||
// TIPTIP: Leave out Content-Type here, it will be automatically set by the browser
|
// TIPTIP: Leave out Content-Type here, it will be automatically set by the browser
|
||||||
delete headers['Content-Type']
|
delete headers['Content-Type']
|
||||||
|
|
||||||
const response = await fetch(this.serverUrl + this.workspacePath() + '/' + rootID + '/files', {
|
const response = await fetch(this.getBaseURL() + this.workspacePath() + '/' + rootID + '/files', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers,
|
headers,
|
||||||
body: formData,
|
body: formData,
|
||||||
|
@ -380,7 +399,7 @@ class OctoClient {
|
||||||
if (readToken) {
|
if (readToken) {
|
||||||
path += `?read_token=${readToken}`
|
path += `?read_token=${readToken}`
|
||||||
}
|
}
|
||||||
const response = await fetch(this.serverUrl + path, {headers: this.headers()})
|
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
@ -390,7 +409,7 @@ class OctoClient {
|
||||||
|
|
||||||
async getWorkspaceUsers(): Promise<IUser[]> {
|
async getWorkspaceUsers(): Promise<IUser[]> {
|
||||||
const path = this.workspacePath() + '/users'
|
const path = this.workspacePath() + '/users'
|
||||||
const response = await fetch(this.serverUrl + path, {headers: this.headers()})
|
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,22 +42,41 @@ class WSClient {
|
||||||
ws: WebSocket|null = null
|
ws: WebSocket|null = null
|
||||||
client: MMWebSocketClient|null = null
|
client: MMWebSocketClient|null = null
|
||||||
clientPrefix = ''
|
clientPrefix = ''
|
||||||
serverUrl: string
|
serverUrl: string | undefined
|
||||||
state: 'init'|'open'|'close' = 'init'
|
state: 'init'|'open'|'close' = 'init'
|
||||||
onStateChange: OnStateChangeHandler[] = []
|
onStateChange: OnStateChangeHandler[] = []
|
||||||
onReconnect: OnReconnectHandler[] = []
|
onReconnect: OnReconnectHandler[] = []
|
||||||
onChange: OnChangeHandler[] = []
|
onChange: OnChangeHandler[] = []
|
||||||
onError: OnErrorHandler[] = []
|
onError: OnErrorHandler[] = []
|
||||||
private mmWSMaxRetries = 10
|
private mmWSMaxRetries = 100
|
||||||
private mmWSRetryDelay = 300
|
private mmWSRetryDelay = 300
|
||||||
private notificationDelay = 100
|
private notificationDelay = 100
|
||||||
private reopenDelay = 3000
|
private reopenDelay = 3000
|
||||||
private updatedBlocks: Block[] = []
|
private updatedBlocks: Block[] = []
|
||||||
private updateTimeout?: NodeJS.Timeout
|
private updateTimeout?: NodeJS.Timeout
|
||||||
|
|
||||||
|
private logged = false
|
||||||
|
|
||||||
|
// this need to be a function rather than a const because
|
||||||
|
// one of the global variable (`window.baseURL`) is set at runtime
|
||||||
|
// after the first instance of OctoClient is created.
|
||||||
|
// Avoiding the race condition becomes more complex than making
|
||||||
|
// the base URL dynamic though a function
|
||||||
|
private getBaseURL(): string {
|
||||||
|
const baseURL = (this.serverUrl || Utils.getBaseURL(true)).replace(/\/$/, '')
|
||||||
|
|
||||||
|
// Logging this for debugging.
|
||||||
|
// Logging just once to avoid log noise.
|
||||||
|
if (!this.logged) {
|
||||||
|
Utils.log(`WSClient serverUrl: ${baseURL}`)
|
||||||
|
this.logged = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseURL
|
||||||
|
}
|
||||||
|
|
||||||
constructor(serverUrl?: string) {
|
constructor(serverUrl?: string) {
|
||||||
this.serverUrl = (serverUrl || Utils.getBaseURL(true)).replace(/\/$/, '')
|
this.serverUrl = serverUrl
|
||||||
Utils.log(`WSClient serverUrl: ${this.serverUrl}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
initPlugin(pluginId: string, client: MMWebSocketClient): void {
|
initPlugin(pluginId: string, client: MMWebSocketClient): void {
|
||||||
|
@ -148,7 +167,7 @@ class WSClient {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = new URL(this.serverUrl)
|
const url = new URL(this.getBaseURL())
|
||||||
const protocol = (url.protocol === 'https:') ? 'wss:' : 'ws:'
|
const protocol = (url.protocol === 'https:') ? 'wss:' : 'ws:'
|
||||||
const wsServerUrl = `${protocol}//${url.host}${url.pathname.replace(/\/$/, '')}/ws`
|
const wsServerUrl = `${protocol}//${url.host}${url.pathname.replace(/\/$/, '')}/ws`
|
||||||
Utils.log(`WSClient open: ${wsServerUrl}`)
|
Utils.log(`WSClient open: ${wsServerUrl}`)
|
||||||
|
|
Loading…
Reference in a new issue