mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-28 12:35:02 +02:00
UBERF-5603 (#4754)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Doc } from '@hcengineering/core'
|
||||
import presentaion, {
|
||||
Card,
|
||||
MessageBox,
|
||||
copyTextToClipboard,
|
||||
createQuery,
|
||||
getClient
|
||||
} from '@hcengineering/presentation'
|
||||
import guest from '../plugin'
|
||||
import { Button, Loading, Location, showPopup } from '@hcengineering/ui'
|
||||
import { getObjectLinkFragment } from '@hcengineering/view-resources'
|
||||
import view from '@hcengineering/view'
|
||||
import { PublicLink } from '@hcengineering/guest'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
export let value: Doc
|
||||
|
||||
const client = getClient()
|
||||
const query = createQuery()
|
||||
let loading = true
|
||||
let link: PublicLink | undefined
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
async function createLink (location: Location, revokable: boolean = true): Promise<void> {
|
||||
await client.createDoc(guest.class.PublicLink, guest.space.Links, {
|
||||
attachedTo: value._id,
|
||||
location,
|
||||
revokable,
|
||||
restrictions: {
|
||||
readonly: true,
|
||||
disableNavigation: true,
|
||||
disableActions: true,
|
||||
disableComments: true
|
||||
},
|
||||
url: ''
|
||||
})
|
||||
}
|
||||
|
||||
async function generate (object: Doc): Promise<void> {
|
||||
const panelComponent = client.getHierarchy().classHierarchyMixin(object._class, view.mixin.ObjectPanel)
|
||||
const comp = panelComponent?.component ?? view.component.EditDoc
|
||||
const loc = await getObjectLinkFragment(client.getHierarchy(), object, {}, comp)
|
||||
await createLink(loc)
|
||||
}
|
||||
|
||||
async function checkNeedGenerate (value: Doc, loading: boolean, link: PublicLink | undefined): Promise<void> {
|
||||
if (!loading || link !== undefined) return
|
||||
await generate(value)
|
||||
}
|
||||
|
||||
query.query(
|
||||
guest.class.PublicLink,
|
||||
{ attachedTo: value._id },
|
||||
(res) => {
|
||||
link = res[0]
|
||||
void checkNeedGenerate(value, loading, link)
|
||||
loading = false
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
|
||||
function close () {
|
||||
dispatch('close')
|
||||
}
|
||||
|
||||
function copy (): void {
|
||||
if (link?.url === undefined || link.url === '') return
|
||||
copyTextToClipboard(link.url)
|
||||
}
|
||||
|
||||
async function revoke (): Promise<void> {
|
||||
if (!revokable || link === undefined) return
|
||||
showPopup(
|
||||
MessageBox,
|
||||
{
|
||||
label: guest.string.Revoke,
|
||||
message: guest.string.RevokeConfirmation
|
||||
},
|
||||
'top',
|
||||
(res) => {
|
||||
if (res === true && link !== undefined) {
|
||||
client.remove(link)
|
||||
}
|
||||
dispatch('close')
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
$: revokable = link?.revokable ?? false
|
||||
</script>
|
||||
|
||||
<Card label={guest.string.PublicLink} canSave={true} okLabel={presentaion.string.Close} on:close okAction={close}>
|
||||
{#if link?.url === undefined || link.url === ''}
|
||||
<Loading />
|
||||
{:else}
|
||||
<div class="over-underline link w-full overflow-label" on:click={copy}>{link.url}</div>
|
||||
{/if}
|
||||
<svelte:fragment slot="buttons">
|
||||
{#if revokable}
|
||||
<Button label={guest.string.Revoke} kind={'dangerous'} size={'large'} on:click={revoke} />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</Card>
|
||||
@@ -0,0 +1,337 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Analytics } from '@hcengineering/analytics'
|
||||
import core, { Class, Doc, Ref, Space } from '@hcengineering/core'
|
||||
import { getMetadata, getResource } from '@hcengineering/platform'
|
||||
import { ActionContext, getClient } from '@hcengineering/presentation'
|
||||
import {
|
||||
AnyComponent,
|
||||
Component,
|
||||
Label,
|
||||
Location,
|
||||
PanelInstance,
|
||||
Popup,
|
||||
PopupAlignment,
|
||||
ResolvedLocation,
|
||||
Separator,
|
||||
TooltipInstance,
|
||||
areLocationsEqual,
|
||||
closePanel,
|
||||
getCurrentLocation,
|
||||
getLocation,
|
||||
navigate,
|
||||
openPanel,
|
||||
setResolvedLocation
|
||||
} from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
import { ListSelectionProvider, restrictionStore, updateFocus } from '@hcengineering/view-resources'
|
||||
import workbench, { Application, NavigatorModel, SpecialNavModel, ViewConfiguration } from '@hcengineering/workbench'
|
||||
import { buildNavModel, SpaceView } from '@hcengineering/workbench-resources'
|
||||
import guest from '../plugin'
|
||||
import { decodeTokenPayload } from '../utils'
|
||||
|
||||
const excludedApps = getMetadata(workbench.metadata.ExcludedApplications) ?? []
|
||||
|
||||
const client = getClient()
|
||||
|
||||
async function load (): Promise<boolean> {
|
||||
const loc = getCurrentLocation()
|
||||
const token = loc.query?.token
|
||||
if (token == null) return false
|
||||
const decoded = decodeTokenPayload(token)
|
||||
|
||||
const link = await client.findOne(guest.class.PublicLink, { _id: decoded.linkId })
|
||||
if (link == null) return false
|
||||
restrictionStore.set(link.restrictions)
|
||||
await doSyncLoc(link.location)
|
||||
return true
|
||||
}
|
||||
|
||||
let contentPanel: HTMLElement
|
||||
let panelInstance: PanelInstance
|
||||
let popupInstance: Popup
|
||||
|
||||
const apps: Application[] = client
|
||||
.getModel()
|
||||
.findAllSync<Application>(workbench.class.Application, { hidden: false, _id: { $nin: excludedApps } })
|
||||
|
||||
async function resolveShortLink (loc: Location): Promise<ResolvedLocation | undefined> {
|
||||
if (loc.path[2] !== undefined && loc.path[2].trim().length > 0) {
|
||||
const app = apps.find((p) => p.alias === loc.path[2])
|
||||
if (app?.locationResolver) {
|
||||
const resolver = await getResource(app.locationResolver)
|
||||
return await resolver(loc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function mergeLoc (loc: Location, resolved: ResolvedLocation): Location {
|
||||
const resolvedApp = resolved.loc.path[2]
|
||||
const resolvedSpace = resolved.loc.path[3]
|
||||
const resolvedSpecial = resolved.loc.path[4]
|
||||
if (resolvedApp === undefined) {
|
||||
loc.path = [loc.path[0], loc.path[1], ...resolved.defaultLocation.path.splice(2)]
|
||||
} else {
|
||||
loc.path[2] = resolvedApp
|
||||
if (resolvedSpace === undefined) {
|
||||
loc.path[3] = currentSpace ?? (currentSpecial as string) ?? resolved.defaultLocation.path[3]
|
||||
loc.path[4] = (currentSpecial as string) ?? resolved.defaultLocation.path[4]
|
||||
} else {
|
||||
loc.path[3] = resolvedSpace
|
||||
loc.path[4] = resolvedSpecial ?? currentSpecial ?? resolved.defaultLocation.path[4]
|
||||
}
|
||||
}
|
||||
for (let index = 0; index < loc.path.length; index++) {
|
||||
const path = loc.path[index]
|
||||
if (path === undefined) {
|
||||
loc.path.length = index
|
||||
break
|
||||
}
|
||||
}
|
||||
loc.fragment = resolved.loc.fragment ?? loc.fragment ?? resolved.defaultLocation.fragment
|
||||
return loc
|
||||
}
|
||||
|
||||
let currentSpace: Ref<Space> | undefined
|
||||
let currentSpecial: string | undefined
|
||||
let specialComponent: SpecialNavModel | undefined
|
||||
let asideId: string | undefined
|
||||
let currentFragment: string | undefined = ''
|
||||
|
||||
let currentApplication: Application | undefined
|
||||
let navigatorModel: NavigatorModel | undefined
|
||||
let currentView: ViewConfiguration | undefined
|
||||
|
||||
function setSpaceSpecial (spaceSpecial: string | undefined): void {
|
||||
if (currentSpecial !== undefined && spaceSpecial === currentSpecial) return
|
||||
if (asideId !== undefined && spaceSpecial === asideId) return
|
||||
if (spaceSpecial === undefined) return
|
||||
specialComponent = getSpecialComponent(spaceSpecial)
|
||||
if (specialComponent !== undefined) {
|
||||
currentSpecial = spaceSpecial
|
||||
} else if (navigatorModel?.aside !== undefined || currentApplication?.aside !== undefined) {
|
||||
asideId = spaceSpecial
|
||||
}
|
||||
}
|
||||
|
||||
function getSpecialComponent (id: string): SpecialNavModel | undefined {
|
||||
const sp = navigatorModel?.specials?.find((x) => x.id === id)
|
||||
if (sp !== undefined) {
|
||||
return sp
|
||||
}
|
||||
for (const s of navigatorModel?.spaces ?? []) {
|
||||
const sp = s.specials?.find((x) => x.id === id)
|
||||
if (sp !== undefined) {
|
||||
return sp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function syncLoc (loc: Location): Promise<void> {
|
||||
if (loc.path.length > 3 && getSpecialComponent(loc.path[3]) === undefined) {
|
||||
// resolve short links
|
||||
const resolvedLoc = await resolveShortLink(loc)
|
||||
if (resolvedLoc !== undefined && !areLocationsEqual(loc, resolvedLoc.loc)) {
|
||||
loc = mergeLoc(loc, resolvedLoc)
|
||||
}
|
||||
}
|
||||
setResolvedLocation(loc)
|
||||
const app = loc.path[2]
|
||||
const space = loc.path[3] as Ref<Space>
|
||||
const special = loc.path[4]
|
||||
const fragment = loc.fragment
|
||||
|
||||
currentApplication = await client.findOne<Application>(workbench.class.Application, { alias: app })
|
||||
navigatorModel = await buildNavModel(client, currentApplication)
|
||||
|
||||
if (currentSpecial === undefined || currentSpecial !== space) {
|
||||
const newSpecial = space !== undefined ? getSpecialComponent(space) : undefined
|
||||
if (newSpecial !== undefined) {
|
||||
specialComponent = newSpecial
|
||||
currentSpecial = space
|
||||
} else {
|
||||
await updateSpace(space)
|
||||
setSpaceSpecial(special)
|
||||
}
|
||||
}
|
||||
|
||||
if (special !== currentSpecial && (navigatorModel?.aside || currentApplication?.aside)) {
|
||||
asideId = special
|
||||
}
|
||||
|
||||
if (fragment !== currentFragment) {
|
||||
currentFragment = fragment
|
||||
if (fragment !== undefined && fragment.trim().length > 0) {
|
||||
await setOpenPanelFocus(fragment)
|
||||
} else {
|
||||
closePanel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function setOpenPanelFocus (fragment: string): Promise<void> {
|
||||
const props = decodeURIComponent(fragment).split('|')
|
||||
|
||||
if (props.length >= 3) {
|
||||
const doc = await client.findOne<Doc>(props[2] as Ref<Class<Doc>>, { _id: props[1] as Ref<Doc> })
|
||||
if (doc !== undefined) {
|
||||
const provider = ListSelectionProvider.Find(doc._id)
|
||||
updateFocus({
|
||||
provider,
|
||||
focus: doc
|
||||
})
|
||||
openPanel(
|
||||
props[0] as AnyComponent,
|
||||
props[1],
|
||||
props[2],
|
||||
(props[3] ?? undefined) as PopupAlignment,
|
||||
(props[4] ?? undefined) as AnyComponent
|
||||
)
|
||||
} else {
|
||||
closePanel(false)
|
||||
}
|
||||
} else {
|
||||
closePanel(false)
|
||||
}
|
||||
}
|
||||
|
||||
async function doSyncLoc (loc: Location): Promise<void> {
|
||||
await syncLoc(loc)
|
||||
await updateWindowTitle(loc)
|
||||
}
|
||||
|
||||
async function updateSpace (spaceId?: Ref<Space>): Promise<void> {
|
||||
if (spaceId === currentSpace) return
|
||||
if (spaceId === undefined) return
|
||||
const space = await client.findOne<Space>(core.class.Space, { _id: spaceId })
|
||||
if (space === undefined) return
|
||||
currentSpace = spaceId
|
||||
const spaceClass = client.getHierarchy().getClass(space._class)
|
||||
const view = client.getHierarchy().as(spaceClass, workbench.mixin.SpaceView)
|
||||
currentView = view.view
|
||||
}
|
||||
|
||||
async function updateWindowTitle (loc: Location): Promise<void> {
|
||||
const ws = loc.path[1]
|
||||
const docTitle = await getWindowTitle(loc)
|
||||
if (docTitle !== undefined && docTitle !== '') {
|
||||
document.title = ws == null ? docTitle : `${docTitle} - ${ws}`
|
||||
} else {
|
||||
const title = getMetadata(workbench.metadata.PlatformTitle) ?? 'Platform'
|
||||
document.title = ws == null ? title : `${ws} - ${title}`
|
||||
}
|
||||
}
|
||||
|
||||
function closeAside (): void {
|
||||
const loc = getLocation()
|
||||
loc.path.length = 4
|
||||
asideId = undefined
|
||||
navigate(loc)
|
||||
}
|
||||
|
||||
async function getWindowTitle (loc: Location): Promise<string | undefined> {
|
||||
if (loc.fragment == null) return
|
||||
const hierarchy = client.getHierarchy()
|
||||
const [, _id, _class] = decodeURIComponent(loc.fragment).split('|')
|
||||
if (_class == null) return
|
||||
|
||||
const mixin = hierarchy.classHierarchyMixin(_class as Ref<Class<Doc>>, view.mixin.ObjectTitle)
|
||||
if (mixin === undefined) return
|
||||
const titleProvider = await getResource(mixin.titleProvider)
|
||||
try {
|
||||
return await titleProvider(client, _id as Ref<Doc>)
|
||||
} catch (err: any) {
|
||||
Analytics.handleError(err)
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
let aside: HTMLElement
|
||||
let cover: HTMLElement
|
||||
</script>
|
||||
|
||||
{#await load() then res}
|
||||
{#if res}
|
||||
<div class="workbench-container" style:flex-direction={'row'}>
|
||||
<div class="workbench-container inner">
|
||||
<div class="antiPanel-component antiComponent" bind:this={contentPanel}>
|
||||
{#if currentApplication && currentApplication.component}
|
||||
<Component is={currentApplication.component} props={{ currentSpace, visibleNav: false }} />
|
||||
{:else if specialComponent}
|
||||
<Component
|
||||
is={specialComponent.component}
|
||||
props={{
|
||||
model: navigatorModel,
|
||||
...specialComponent.componentProps,
|
||||
currentSpace,
|
||||
visibleNav: false
|
||||
}}
|
||||
/>
|
||||
{:else if currentView?.component !== undefined}
|
||||
<Component
|
||||
is={currentView.component}
|
||||
props={{ ...currentView.componentProps, currentView, visibleNav: false }}
|
||||
/>
|
||||
{:else}
|
||||
<SpaceView {currentSpace} {currentView} />
|
||||
{/if}
|
||||
</div>
|
||||
{#if asideId}
|
||||
{@const asideComponent = navigatorModel?.aside ?? currentApplication?.aside}
|
||||
{#if asideComponent !== undefined}
|
||||
<Separator name={'workbench'} index={1} />
|
||||
<div class="antiPanel-component antiComponent aside" bind:this={aside}>
|
||||
<Component is={asideComponent} props={{ currentSpace, _id: asideId }} on:close={closeAside} />
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div bind:this={cover} class="cover" />
|
||||
<TooltipInstance />
|
||||
<PanelInstance bind:this={panelInstance} {contentPanel} readonly={$restrictionStore.readonly} embedded>
|
||||
<svelte:fragment slot="panel-header">
|
||||
<ActionContext context={{ mode: 'panel' }} />
|
||||
</svelte:fragment>
|
||||
</PanelInstance>
|
||||
<Popup bind:this={popupInstance} {contentPanel}>
|
||||
<svelte:fragment slot="popup-header">
|
||||
<ActionContext context={{ mode: 'popup' }} />
|
||||
</svelte:fragment>
|
||||
</Popup>
|
||||
{:else}
|
||||
<div class="version-wrapper">
|
||||
<div class="antiPopup version-popup">
|
||||
<h1><Label label={guest.string.LinkWasRevoked} /></h1>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/await}
|
||||
|
||||
<style lang="scss">
|
||||
.version-wrapper {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.version-popup {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 2rem;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,59 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { getMetadata } from '@hcengineering/platform'
|
||||
import { Label, Loading, Notifications, location } from '@hcengineering/ui'
|
||||
import { connect, versionError } from '../connect'
|
||||
|
||||
import { guestId } from '@hcengineering/guest'
|
||||
import workbench from '@hcengineering/workbench'
|
||||
import Guest from './Guest.svelte'
|
||||
</script>
|
||||
|
||||
{#if $location.path[0] === guestId}
|
||||
{#await connect(getMetadata(workbench.metadata.PlatformTitle) ?? 'Platform')}
|
||||
<Loading />
|
||||
{:then client}
|
||||
{#if !client && versionError}
|
||||
<div class="version-wrapper">
|
||||
<div class="antiPopup version-popup">
|
||||
<h1><Label label={workbench.string.ServerUnderMaintenance} /></h1>
|
||||
{versionError}
|
||||
</div>
|
||||
</div>
|
||||
{:else if client}
|
||||
<Notifications>
|
||||
<Guest />
|
||||
</Notifications>
|
||||
{/if}
|
||||
{:catch error}
|
||||
<div>{error} -- {error.stack}</div>
|
||||
{/await}
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.version-wrapper {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.version-popup {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 2rem;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,188 @@
|
||||
import { Analytics } from '@hcengineering/analytics'
|
||||
import client from '@hcengineering/client'
|
||||
import core, {
|
||||
ClientConnectEvent,
|
||||
versionToString,
|
||||
type AccountClient,
|
||||
type Client,
|
||||
type Version,
|
||||
setCurrentAccount
|
||||
} from '@hcengineering/core'
|
||||
import login, { loginId } from '@hcengineering/login'
|
||||
import { getMetadata, getResource, setMetadata } from '@hcengineering/platform'
|
||||
import presentation, { closeClient, refreshClient, setClient } from '@hcengineering/presentation'
|
||||
import { fetchMetadataLocalStorage, getCurrentLocation, navigate, setMetadataLocalStorage } from '@hcengineering/ui'
|
||||
|
||||
export let versionError: string | undefined = ''
|
||||
|
||||
let _token: string | undefined
|
||||
let _client: AccountClient | undefined
|
||||
let _clientSet: boolean = false
|
||||
|
||||
export async function connect (title: string): Promise<Client | undefined> {
|
||||
const loc = getCurrentLocation()
|
||||
const token = loc.query?.token
|
||||
const ws = loc.path[1]
|
||||
if (ws === undefined || token == null) {
|
||||
navigate({
|
||||
path: [loginId]
|
||||
})
|
||||
return
|
||||
}
|
||||
setMetadata(presentation.metadata.Token, token)
|
||||
document.cookie =
|
||||
encodeURIComponent(presentation.metadata.Token.replaceAll(':', '-')) + '=' + encodeURIComponent(token) + '; path=/'
|
||||
|
||||
const getEndpoint = await getResource(login.function.GetEndpoint)
|
||||
const endpoint = await getEndpoint()
|
||||
if (endpoint == null) {
|
||||
navigate({
|
||||
path: [loginId]
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (_token !== token && _client !== undefined) {
|
||||
await _client.close()
|
||||
_client = undefined
|
||||
}
|
||||
if (_client !== undefined) {
|
||||
return _client
|
||||
}
|
||||
_token = token
|
||||
|
||||
let version: Version | undefined
|
||||
let serverEndpoint = endpoint.replace(/^ws/g, 'http')
|
||||
if (serverEndpoint.endsWith('/')) {
|
||||
serverEndpoint = serverEndpoint.substring(0, serverEndpoint.length - 1)
|
||||
}
|
||||
const clientFactory = await getResource(client.function.GetClient)
|
||||
_client = await clientFactory(
|
||||
token,
|
||||
endpoint,
|
||||
() => {
|
||||
location.reload()
|
||||
},
|
||||
() => {
|
||||
clearMetadata(ws)
|
||||
navigate({
|
||||
path: [loginId],
|
||||
query: {}
|
||||
})
|
||||
},
|
||||
// We need to refresh all active live queries and clear old queries.
|
||||
(event: ClientConnectEvent) => {
|
||||
console.log('WorkbenchClient: onConnect', event)
|
||||
try {
|
||||
if ((_clientSet && event === ClientConnectEvent.Connected) || event === ClientConnectEvent.Refresh) {
|
||||
void refreshClient()
|
||||
}
|
||||
|
||||
if (event === ClientConnectEvent.Upgraded) {
|
||||
window.location.reload()
|
||||
}
|
||||
|
||||
void (async () => {
|
||||
if (_client !== undefined) {
|
||||
const newVersion = await _client.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()
|
||||
versionError = `${currentVersionStr} != ${reconnectVersionStr}`
|
||||
}
|
||||
const serverVersion: { version: string } = await (
|
||||
await fetch(serverEndpoint + '/api/v1/version', {})
|
||||
).json()
|
||||
|
||||
console.log('Server version', serverVersion.version)
|
||||
if (serverVersion.version !== '' && serverVersion.version !== currentVersionStr) {
|
||||
versionError = `${currentVersionStr} => ${serverVersion.version}`
|
||||
}
|
||||
}
|
||||
})()
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
)
|
||||
console.log('logging in as guest')
|
||||
Analytics.handleEvent('GUEST LOGIN')
|
||||
Analytics.setTag('workspace', ws)
|
||||
const me = await _client?.getAccount()
|
||||
if (me !== undefined) {
|
||||
Analytics.setUser(me.email)
|
||||
Analytics.setTag('workspace', ws)
|
||||
console.log('login: employee account', me)
|
||||
setCurrentAccount(me)
|
||||
}
|
||||
|
||||
try {
|
||||
version = await _client.findOne<Version>(core.class.Version, {})
|
||||
console.log('Model version', version)
|
||||
|
||||
const requiredVersion = getMetadata(presentation.metadata.RequiredVersion)
|
||||
if (requiredVersion !== undefined && version !== undefined) {
|
||||
console.log('checking min model version', requiredVersion)
|
||||
const versionStr = versionToString(version)
|
||||
|
||||
if (version === undefined || requiredVersion !== versionStr) {
|
||||
versionError = `${versionStr} => ${requiredVersion}`
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const serverVersion: { version: string } = await (await fetch(serverEndpoint + '/api/v1/version', {})).json()
|
||||
|
||||
console.log('Server version', serverVersion.version)
|
||||
if (
|
||||
serverVersion.version !== '' &&
|
||||
(version === undefined || serverVersion.version !== versionToString(version))
|
||||
) {
|
||||
const versionStr = version !== undefined ? versionToString(version) : 'unknown'
|
||||
versionError = `${versionStr} => ${serverVersion.version}`
|
||||
return
|
||||
}
|
||||
} catch (err: any) {
|
||||
versionError = 'server version not available'
|
||||
return
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.log(err)
|
||||
const requirdVersion = getMetadata(presentation.metadata.RequiredVersion)
|
||||
console.log('checking min model version', requirdVersion)
|
||||
if (requirdVersion !== undefined) {
|
||||
versionError = `'unknown' => ${requirdVersion}`
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
// Update window title
|
||||
document.title = [ws, title].filter((it) => it).join(' - ')
|
||||
_clientSet = true
|
||||
await setClient(_client)
|
||||
|
||||
return _client
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
setMetadata(presentation.metadata.Token, null)
|
||||
setMetadataLocalStorage(login.metadata.LastToken, null)
|
||||
document.cookie =
|
||||
encodeURIComponent(presentation.metadata.Token.replaceAll(':', '-')) + '=' + encodeURIComponent('') + '; path=/'
|
||||
setMetadataLocalStorage(login.metadata.LoginEndpoint, null)
|
||||
setMetadataLocalStorage(login.metadata.LoginEmail, null)
|
||||
void closeClient()
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { type Resources } from '@hcengineering/platform'
|
||||
import CreatePublicLink from './components/CreatePublicLink.svelte'
|
||||
import GuestApp from './components/GuestApp.svelte'
|
||||
|
||||
export default async (): Promise<Resources> => ({
|
||||
component: {
|
||||
GuestApp,
|
||||
CreatePublicLink
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,12 @@
|
||||
import guest, { guestId } from '@hcengineering/guest'
|
||||
import { type IntlString, mergeIds } from '@hcengineering/platform'
|
||||
|
||||
export default mergeIds(guestId, guest, {
|
||||
string: {
|
||||
CreatePublicLink: '' as IntlString,
|
||||
PublicLink: '' as IntlString,
|
||||
Revoke: '' as IntlString,
|
||||
RevokeConfirmation: '' as IntlString,
|
||||
LinkWasRevoked: '' as IntlString
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,11 @@
|
||||
export function decodeTokenPayload (token: string): any {
|
||||
const parts = token.split('.')
|
||||
|
||||
const payload = parts[1]
|
||||
|
||||
const decodedPayload = atob(payload)
|
||||
|
||||
const parsedPayload = JSON.parse(decodedPayload)
|
||||
|
||||
return parsedPayload
|
||||
}
|
||||
Reference in New Issue
Block a user