2024-02-15 15:19:33 +00:00
|
|
|
import { Analytics } from '@hcengineering/analytics'
|
2022-09-21 08:08:25 +00:00
|
|
|
import client from '@hcengineering/client'
|
2023-05-19 11:46:05 +00:00
|
|
|
import core, {
|
2023-10-13 09:28:02 +00:00
|
|
|
ClientConnectEvent,
|
2023-06-09 14:46:09 +00:00
|
|
|
getCurrentAccount,
|
2024-04-06 08:14:06 +00:00
|
|
|
MeasureMetricsContext,
|
|
|
|
metricsToString,
|
2023-06-09 14:46:09 +00:00
|
|
|
setCurrentAccount,
|
2024-02-02 09:27:27 +00:00
|
|
|
versionToString,
|
2024-04-25 15:15:11 +00:00
|
|
|
type Account,
|
2024-02-02 09:27:27 +00:00
|
|
|
type AccountClient,
|
|
|
|
type Client,
|
|
|
|
type Version
|
2023-05-19 11:46:05 +00:00
|
|
|
} from '@hcengineering/core'
|
2023-01-16 04:30:47 +00:00
|
|
|
import login, { loginId } from '@hcengineering/login'
|
2023-06-20 05:47:00 +00:00
|
|
|
import { addEventListener, broadcastEvent, getMetadata, getResource, setMetadata } from '@hcengineering/platform'
|
2024-03-01 16:39:46 +00:00
|
|
|
import presentation, { closeClient, purgeClient, refreshClient, setClient } from '@hcengineering/presentation'
|
2023-11-08 15:25:25 +00:00
|
|
|
import {
|
2023-04-20 10:11:22 +00:00
|
|
|
fetchMetadataLocalStorage,
|
|
|
|
getCurrentLocation,
|
2024-01-05 08:40:29 +00:00
|
|
|
locationStorageKeyId,
|
2023-04-20 10:11:22 +00:00
|
|
|
navigate,
|
|
|
|
networkStatus,
|
2023-11-08 15:25:25 +00:00
|
|
|
setMetadataLocalStorage
|
2023-04-20 10:11:22 +00:00
|
|
|
} from '@hcengineering/ui'
|
2024-04-17 15:18:15 +00:00
|
|
|
import { writable } from 'svelte/store'
|
2023-06-20 05:47:00 +00:00
|
|
|
import plugin from './plugin'
|
2024-04-10 13:33:17 +00:00
|
|
|
import { workspaceCreating } from './utils'
|
2022-03-31 08:32:42 +00:00
|
|
|
|
2024-04-17 15:18:15 +00:00
|
|
|
export const versionError = writable<string | undefined>(undefined)
|
2022-03-31 08:32:42 +00:00
|
|
|
|
2023-03-24 04:53:12 +00:00
|
|
|
let _token: string | undefined
|
2023-05-19 11:46:05 +00:00
|
|
|
let _client: AccountClient | undefined
|
2023-10-13 09:28:02 +00:00
|
|
|
let _clientSet: boolean = false
|
2023-03-24 04:53:12 +00:00
|
|
|
|
2023-04-20 10:11:22 +00:00
|
|
|
addEventListener(client.event.NetworkRequests, async (event: string, val: number) => {
|
|
|
|
networkStatus.set(val)
|
|
|
|
})
|
|
|
|
|
2024-03-21 03:39:00 +00:00
|
|
|
export async function disconnect (): Promise<void> {
|
|
|
|
if (_client !== undefined) {
|
|
|
|
await _client.close()
|
|
|
|
_client = undefined
|
|
|
|
_clientSet = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-31 08:32:42 +00:00
|
|
|
export async function connect (title: string): Promise<Client | undefined> {
|
2024-04-06 08:14:06 +00:00
|
|
|
const ctx = new MeasureMetricsContext('connect', {})
|
2022-07-06 07:02:01 +00:00
|
|
|
const loc = getCurrentLocation()
|
|
|
|
const ws = loc.path[1]
|
2024-01-05 08:40:29 +00:00
|
|
|
if (ws === undefined) {
|
|
|
|
const lastLoc = localStorage.getItem(locationStorageKeyId)
|
|
|
|
if (lastLoc !== null) {
|
|
|
|
const lastLocObj = JSON.parse(lastLoc)
|
|
|
|
if (lastLocObj.path !== undefined && lastLocObj.path[0] === loc.path[0]) {
|
|
|
|
navigate(lastLocObj)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
navigate({
|
|
|
|
path: [loginId]
|
|
|
|
})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2022-07-06 07:02:01 +00:00
|
|
|
const tokens: Record<string, string> = fetchMetadataLocalStorage(login.metadata.LoginTokens) ?? {}
|
2024-02-23 12:52:00 +00:00
|
|
|
let token = tokens[ws]
|
2024-04-10 13:33:17 +00:00
|
|
|
|
2024-05-08 17:20:00 +00:00
|
|
|
if (
|
|
|
|
token === undefined &&
|
|
|
|
(getMetadata(presentation.metadata.Token) !== undefined ||
|
|
|
|
fetchMetadataLocalStorage(login.metadata.LastToken) != null)
|
|
|
|
) {
|
2024-02-23 12:52:00 +00:00
|
|
|
const selectWorkspace = await getResource(login.function.SelectWorkspace)
|
2024-04-06 08:14:06 +00:00
|
|
|
const loginInfo = await ctx.with('select-workspace', {}, async () => (await selectWorkspace(ws))[1])
|
2024-02-23 12:52:00 +00:00
|
|
|
if (loginInfo !== undefined) {
|
|
|
|
tokens[ws] = loginInfo.token
|
|
|
|
token = loginInfo.token
|
|
|
|
setMetadataLocalStorage(login.metadata.LoginTokens, tokens)
|
|
|
|
}
|
|
|
|
}
|
2023-03-22 02:48:57 +00:00
|
|
|
setMetadata(presentation.metadata.Token, token)
|
2024-04-10 13:33:17 +00:00
|
|
|
|
|
|
|
const fetchWorkspace = await getResource(login.function.FetchWorkspace)
|
|
|
|
let loginInfo = await ctx.with('select-workspace', {}, async () => (await fetchWorkspace(ws))[1])
|
|
|
|
if (loginInfo?.creating === true) {
|
|
|
|
while (true) {
|
|
|
|
workspaceCreating.set(loginInfo?.createProgress ?? 0)
|
|
|
|
loginInfo = await ctx.with('select-workspace', {}, async () => (await fetchWorkspace(ws))[1])
|
|
|
|
workspaceCreating.set(loginInfo?.createProgress)
|
|
|
|
if (loginInfo?.creating === false) {
|
|
|
|
workspaceCreating.set(-1)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
await new Promise<void>((resolve) => setTimeout(resolve, 1000))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-19 09:00:43 +00:00
|
|
|
document.cookie =
|
2024-02-02 16:11:38 +00:00
|
|
|
encodeURIComponent(presentation.metadata.Token.replaceAll(':', '-')) + '=' + encodeURIComponent(token) + '; path=/'
|
2023-03-22 02:48:57 +00:00
|
|
|
|
2022-03-31 08:32:42 +00:00
|
|
|
const endpoint = fetchMetadataLocalStorage(login.metadata.LoginEndpoint)
|
|
|
|
const email = fetchMetadataLocalStorage(login.metadata.LoginEmail)
|
2022-07-06 07:02:01 +00:00
|
|
|
if (token === undefined || endpoint === null || email === null) {
|
2024-03-07 07:14:43 +00:00
|
|
|
const navigateUrl = encodeURIComponent(JSON.stringify(loc))
|
2022-04-29 05:27:17 +00:00
|
|
|
navigate({
|
2023-01-16 04:30:47 +00:00
|
|
|
path: [loginId],
|
2024-03-07 07:14:43 +00:00
|
|
|
query: { navigateUrl }
|
2022-04-29 05:27:17 +00:00
|
|
|
})
|
2022-03-31 08:32:42 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-04-02 12:40:52 +00:00
|
|
|
let tokenChanged = false
|
|
|
|
|
2023-03-24 04:53:12 +00:00
|
|
|
if (_token !== token && _client !== undefined) {
|
2024-03-01 16:39:46 +00:00
|
|
|
// We need to flush all data from memory
|
2024-04-06 08:14:06 +00:00
|
|
|
await ctx.with('purge-client', {}, async () => {
|
|
|
|
await purgeClient()
|
|
|
|
})
|
|
|
|
await ctx.with('close previous client', {}, async () => {
|
|
|
|
await _client?.close()
|
|
|
|
})
|
2023-03-24 04:53:12 +00:00
|
|
|
_client = undefined
|
2024-04-02 12:40:52 +00:00
|
|
|
tokenChanged = true
|
2023-03-24 04:53:12 +00:00
|
|
|
}
|
|
|
|
if (_client !== undefined) {
|
|
|
|
return _client
|
|
|
|
}
|
|
|
|
_token = token
|
|
|
|
|
2023-04-07 05:18:07 +00:00
|
|
|
let version: Version | undefined
|
2023-09-15 08:32:53 +00:00
|
|
|
let serverEndpoint = endpoint.replace(/^ws/g, 'http')
|
|
|
|
if (serverEndpoint.endsWith('/')) {
|
|
|
|
serverEndpoint = serverEndpoint.substring(0, serverEndpoint.length - 1)
|
|
|
|
}
|
2023-03-24 04:53:12 +00:00
|
|
|
const clientFactory = await getResource(client.function.GetClient)
|
2024-04-06 08:14:06 +00:00
|
|
|
const newClient = await ctx.with(
|
|
|
|
'create-client',
|
|
|
|
{},
|
|
|
|
async (ctx) =>
|
|
|
|
await clientFactory(
|
|
|
|
token,
|
|
|
|
endpoint,
|
|
|
|
() => {
|
|
|
|
location.reload()
|
|
|
|
},
|
|
|
|
() => {
|
|
|
|
clearMetadata(ws)
|
|
|
|
navigate({
|
|
|
|
path: [loginId],
|
|
|
|
query: {}
|
|
|
|
})
|
|
|
|
},
|
|
|
|
// We need to refresh all active live queries and clear old queries.
|
2024-04-19 04:20:49 +00:00
|
|
|
(event: ClientConnectEvent, data: any) => {
|
2024-04-06 08:14:06 +00:00
|
|
|
console.log('WorkbenchClient: onConnect', event)
|
2024-04-19 04:20:49 +00:00
|
|
|
if (event === ClientConnectEvent.Maintenance) {
|
2024-04-19 18:48:46 +00:00
|
|
|
if (data != null && data.total !== 0) {
|
2024-04-19 04:20:49 +00:00
|
|
|
versionError.set(`Maintenance ${Math.floor((100 / data.total) * (data.total - data.toProcess))}%`)
|
|
|
|
} else {
|
|
|
|
versionError.set('Maintenance...')
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
2024-04-06 08:14:06 +00:00
|
|
|
try {
|
|
|
|
if ((_clientSet && event === ClientConnectEvent.Connected) || event === ClientConnectEvent.Refresh) {
|
|
|
|
void ctx.with('refresh client', {}, async () => {
|
|
|
|
await refreshClient(tokenChanged)
|
|
|
|
})
|
|
|
|
tokenChanged = false
|
2023-09-15 08:32:53 +00:00
|
|
|
}
|
|
|
|
|
2024-04-06 08:14:06 +00:00
|
|
|
if (event === ClientConnectEvent.Upgraded) {
|
|
|
|
window.location.reload()
|
2023-09-15 08:32:53 +00:00
|
|
|
}
|
2024-04-06 08:14:06 +00:00
|
|
|
|
|
|
|
void (async () => {
|
|
|
|
if (_client !== undefined) {
|
|
|
|
const newVersion = await ctx.with(
|
|
|
|
'find-version',
|
|
|
|
{},
|
|
|
|
async () => await newClient.findOne<Version>(core.class.Version, {})
|
|
|
|
)
|
|
|
|
console.log('Reconnect Model version', newVersion)
|
|
|
|
|
|
|
|
const currentVersionStr = versionToString(version as Version)
|
|
|
|
const reconnectVersionStr = versionToString(newVersion as Version)
|
|
|
|
|
|
|
|
if (currentVersionStr !== reconnectVersionStr) {
|
|
|
|
// It seems upgrade happened
|
|
|
|
// location.reload()
|
2024-04-17 15:18:15 +00:00
|
|
|
versionError.set(`${currentVersionStr} != ${reconnectVersionStr}`)
|
2024-04-06 08:14:06 +00:00
|
|
|
}
|
|
|
|
const serverVersion: { version: string } = await ctx.with(
|
|
|
|
'fetch-server-version',
|
|
|
|
{},
|
|
|
|
async () => await (await fetch(serverEndpoint + '/api/v1/version', {})).json()
|
|
|
|
)
|
|
|
|
|
2024-04-17 15:18:15 +00:00
|
|
|
console.log(
|
|
|
|
'Server version',
|
|
|
|
serverVersion.version,
|
|
|
|
version !== undefined ? versionToString(version) : ''
|
|
|
|
)
|
2024-04-06 08:14:06 +00:00
|
|
|
if (serverVersion.version !== '' && serverVersion.version !== currentVersionStr) {
|
2024-04-17 15:18:15 +00:00
|
|
|
versionError.set(`${currentVersionStr} => ${serverVersion.version}`)
|
|
|
|
} else {
|
|
|
|
versionError.set(undefined)
|
2024-04-06 08:14:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
})()
|
|
|
|
} catch (err) {
|
|
|
|
console.error(err)
|
2023-09-14 16:24:15 +00:00
|
|
|
}
|
2024-04-06 08:14:06 +00:00
|
|
|
},
|
|
|
|
ctx
|
|
|
|
)
|
2022-07-05 05:43:17 +00:00
|
|
|
)
|
2024-04-06 08:14:06 +00:00
|
|
|
|
|
|
|
_client = newClient
|
2022-03-31 08:32:42 +00:00
|
|
|
console.log('logging in as', email)
|
|
|
|
|
2024-04-25 15:15:11 +00:00
|
|
|
let me: Account | undefined = await ctx.with('get-account', {}, async () => await newClient.getAccount())
|
|
|
|
if (me === undefined) {
|
|
|
|
me = await createEmployee(ctx, ws, me, newClient)
|
|
|
|
}
|
2022-03-31 08:32:42 +00:00
|
|
|
if (me !== undefined) {
|
2024-02-15 15:19:33 +00:00
|
|
|
Analytics.setUser(me.email)
|
|
|
|
Analytics.setTag('workspace', ws)
|
2022-03-31 08:32:42 +00:00
|
|
|
console.log('login: employee account', me)
|
|
|
|
setCurrentAccount(me)
|
|
|
|
} else {
|
|
|
|
console.error('WARNING: no employee account found.')
|
2024-04-25 15:15:11 +00:00
|
|
|
|
2022-07-06 07:02:01 +00:00
|
|
|
clearMetadata(ws)
|
2022-04-29 05:27:17 +00:00
|
|
|
navigate({
|
2023-01-16 04:30:47 +00:00
|
|
|
path: [loginId],
|
2022-04-29 05:27:17 +00:00
|
|
|
query: { navigateUrl: encodeURIComponent(JSON.stringify(getCurrentLocation())) }
|
|
|
|
})
|
2023-03-24 04:53:12 +00:00
|
|
|
|
|
|
|
// Update on connect, so it will be triggered
|
2023-10-13 09:28:02 +00:00
|
|
|
_clientSet = true
|
2024-04-06 08:14:06 +00:00
|
|
|
const client = _client
|
|
|
|
await ctx.with('set-client', {}, async () => {
|
|
|
|
await setClient(client)
|
|
|
|
})
|
2022-03-31 08:32:42 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
try {
|
2024-04-06 08:14:06 +00:00
|
|
|
version = await ctx.with(
|
|
|
|
'find-model-version',
|
|
|
|
{},
|
|
|
|
async () => await newClient.findOne<Version>(core.class.Version, {})
|
|
|
|
)
|
2022-03-31 08:32:42 +00:00
|
|
|
console.log('Model version', version)
|
|
|
|
|
2023-04-20 10:11:22 +00:00
|
|
|
const requiredVersion = getMetadata(presentation.metadata.RequiredVersion)
|
2024-03-15 10:02:00 +00:00
|
|
|
if (requiredVersion !== undefined && version !== undefined && requiredVersion !== '') {
|
2023-04-20 10:11:22 +00:00
|
|
|
console.log('checking min model version', requiredVersion)
|
2023-04-07 05:18:07 +00:00
|
|
|
const versionStr = versionToString(version)
|
2022-03-31 08:32:42 +00:00
|
|
|
|
2023-04-20 10:11:22 +00:00
|
|
|
if (version === undefined || requiredVersion !== versionStr) {
|
2024-04-17 15:18:15 +00:00
|
|
|
versionError.set(`${versionStr} => ${requiredVersion}`)
|
2022-03-31 08:32:42 +00:00
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
}
|
2023-09-14 16:24:15 +00:00
|
|
|
|
|
|
|
try {
|
2024-04-06 08:14:06 +00:00
|
|
|
const serverVersion: { version: string } = await ctx.with(
|
|
|
|
'find-server-version',
|
|
|
|
{},
|
|
|
|
async () => await (await fetch(serverEndpoint + '/api/v1/version', {})).json()
|
|
|
|
)
|
2023-09-14 16:24:15 +00:00
|
|
|
|
2024-04-17 15:18:15 +00:00
|
|
|
console.log('Server version', serverVersion.version, version !== undefined ? versionToString(version) : '')
|
2023-09-14 16:24:15 +00:00
|
|
|
if (
|
|
|
|
serverVersion.version !== '' &&
|
|
|
|
(version === undefined || serverVersion.version !== versionToString(version))
|
|
|
|
) {
|
|
|
|
const versionStr = version !== undefined ? versionToString(version) : 'unknown'
|
2024-04-17 15:18:15 +00:00
|
|
|
versionError.set(`${versionStr} => ${serverVersion.version}`)
|
2023-09-14 16:24:15 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
} catch (err: any) {
|
2024-04-17 15:18:15 +00:00
|
|
|
versionError.set('server version not available')
|
2023-09-14 16:24:15 +00:00
|
|
|
return
|
|
|
|
}
|
2022-03-31 08:32:42 +00:00
|
|
|
} catch (err: any) {
|
2024-04-12 05:23:32 +00:00
|
|
|
console.error(err)
|
|
|
|
Analytics.handleError(err)
|
2024-04-17 15:18:15 +00:00
|
|
|
const requiredVersion = getMetadata(presentation.metadata.RequiredVersion)
|
|
|
|
console.log('checking min model version', requiredVersion)
|
|
|
|
if (requiredVersion !== undefined) {
|
|
|
|
versionError.set(`'unknown' => ${requiredVersion}`)
|
2022-03-31 08:32:42 +00:00
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-17 15:18:15 +00:00
|
|
|
versionError.set(undefined)
|
|
|
|
|
2022-03-31 08:32:42 +00:00
|
|
|
// Update window title
|
2022-07-06 07:02:01 +00:00
|
|
|
document.title = [ws, title].filter((it) => it).join(' - ')
|
2023-10-13 09:28:02 +00:00
|
|
|
_clientSet = true
|
2024-04-06 08:14:06 +00:00
|
|
|
await ctx.with('set-client', {}, async () => {
|
|
|
|
await setClient(newClient)
|
|
|
|
})
|
|
|
|
await ctx.with('broadcast-connected', {}, async () => {
|
|
|
|
await broadcastEvent(plugin.event.NotifyConnection, getCurrentAccount())
|
|
|
|
})
|
|
|
|
console.log(metricsToString(ctx.metrics, 'connect', 50))
|
|
|
|
return newClient
|
2022-03-31 08:32:42 +00:00
|
|
|
}
|
2023-03-27 15:33:23 +00:00
|
|
|
|
2024-04-25 15:15:11 +00:00
|
|
|
async function createEmployee (
|
|
|
|
ctx: MeasureMetricsContext,
|
|
|
|
ws: string,
|
|
|
|
me: Account,
|
|
|
|
newClient: AccountClient
|
|
|
|
): Promise<Account | undefined> {
|
|
|
|
const createEmployee = await getResource(login.function.CreateEmployee)
|
|
|
|
await ctx.with('create-missing-employee', {}, async () => {
|
|
|
|
await createEmployee(ws)
|
|
|
|
})
|
|
|
|
for (let i = 0; i < 5; i++) {
|
|
|
|
me = await ctx.with('get-account', {}, async () => await newClient.getAccount())
|
|
|
|
if (me !== undefined) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
await new Promise((resolve) => {
|
|
|
|
setTimeout(resolve, 100)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return me
|
|
|
|
}
|
|
|
|
|
2022-07-06 07:02:01 +00:00
|
|
|
function clearMetadata (ws: string): void {
|
|
|
|
const tokens = fetchMetadataLocalStorage(login.metadata.LoginTokens)
|
|
|
|
if (tokens !== null) {
|
|
|
|
const loc = getCurrentLocation()
|
|
|
|
// eslint-disable-next-line
|
|
|
|
delete tokens[loc.path[1]]
|
|
|
|
setMetadataLocalStorage(login.metadata.LoginTokens, tokens)
|
|
|
|
}
|
2023-03-22 02:48:57 +00:00
|
|
|
setMetadata(presentation.metadata.Token, null)
|
2024-02-02 09:27:27 +00:00
|
|
|
setMetadataLocalStorage(login.metadata.LastToken, null)
|
2023-08-19 09:00:43 +00:00
|
|
|
document.cookie =
|
|
|
|
encodeURIComponent(presentation.metadata.Token.replaceAll(':', '-')) + '=' + encodeURIComponent('') + '; path=/'
|
2022-07-05 05:43:17 +00:00
|
|
|
setMetadataLocalStorage(login.metadata.LoginEndpoint, null)
|
|
|
|
setMetadataLocalStorage(login.metadata.LoginEmail, null)
|
2023-06-09 14:46:09 +00:00
|
|
|
void closeClient()
|
2022-07-05 05:43:17 +00:00
|
|
|
}
|