From c0fa4b729951770843dbe5950d6d2cd11f6a4c5a Mon Sep 17 00:00:00 2001 From: Alexey Zinoviev Date: Thu, 11 Sep 2025 22:49:33 +0700 Subject: [PATCH] UBERF-13511: Support guest tokens with open account (#9834) Signed-off-by: Alexey Zinoviev --- packages/account-client/src/client.ts | 14 + packages/platform/src/platform.ts | 2 + packages/ui/src/components/TimeLeft.svelte | 15 +- plugins/login-assets/lang/cs.json | 4 +- plugins/login-assets/lang/de.json | 4 +- plugins/login-assets/lang/en.json | 4 +- plugins/login-assets/lang/es.json | 4 +- plugins/login-assets/lang/fr.json | 4 +- plugins/login-assets/lang/it.json | 4 +- plugins/login-assets/lang/ja.json | 4 +- plugins/login-assets/lang/pt.json | 4 +- plugins/login-assets/lang/ru.json | 4 +- plugins/login-assets/lang/zh.json | 4 +- .../src/components/Auth.svelte | 47 ++- plugins/login-resources/src/plugin.ts | 4 +- .../account/src/__tests__/operations.test.ts | 325 ++++++++++++++++-- server/account/src/operations.ts | 92 ++++- server/account/src/utils.ts | 22 +- server/token/src/token.ts | 21 +- 19 files changed, 510 insertions(+), 72 deletions(-) diff --git a/packages/account-client/src/client.ts b/packages/account-client/src/client.ts index 1f0fbfb14e..268dd4dc52 100644 --- a/packages/account-client/src/client.ts +++ b/packages/account-client/src/client.ts @@ -97,6 +97,14 @@ export interface AccountClient { ) => Promise join: (email: string, password: string, inviteId: string, workspaceUrl: string) => Promise createInvite: (exp: number, emailMask: string, limit: number, role: AccountRole) => Promise + /** + * @param options.personalized + * If true, will generate a link with a personalized token for one person access + * If false, will generate a link with an open-ended account in the token. Every token use will generate a new account. + * When false, notBefore and expiration parameters are mandatory. + * @param options.notBefore - not valid before; timestamp in seconds + * @param options.expiration - expires after; timestamp in seconds + */ createAccessLink: ( role: AccountRole, options?: { @@ -105,6 +113,9 @@ export interface AccountClient { navigateUrl?: string extra?: Record spaces?: string[] + notBefore?: number + expiration?: number + personalized?: boolean } ) => Promise checkJoin: (inviteId: string) => Promise @@ -435,6 +446,9 @@ class AccountClientImpl implements AccountClient { navigateUrl?: string extra?: Record spaces?: string[] + notBefore?: number + expiration?: number + personalized?: boolean } ): Promise { const params: any = { ...(options ?? {}), role } diff --git a/packages/platform/src/platform.ts b/packages/platform/src/platform.ts index c4e9116aa6..93a7d3abfa 100644 --- a/packages/platform/src/platform.ts +++ b/packages/platform/src/platform.ts @@ -141,6 +141,8 @@ export default plugin(platformId, { BadRequest: '' as StatusCode, Forbidden: '' as StatusCode, // 403 Unauthorized: '' as StatusCode, // 401 + TokenExpired: '' as StatusCode, // 401 + TokenNotActive: '' as StatusCode<{ notBefore: number }>, // 401 Conflict: '' as StatusCode, // 409 ExpiredLink: '' as StatusCode, UnknownMethod: '' as StatusCode<{ method: string }>, diff --git a/packages/ui/src/components/TimeLeft.svelte b/packages/ui/src/components/TimeLeft.svelte index 1ca3d3e14c..813fbfcb44 100644 --- a/packages/ui/src/components/TimeLeft.svelte +++ b/packages/ui/src/components/TimeLeft.svelte @@ -17,13 +17,18 @@ import { createEventDispatcher, onDestroy } from 'svelte' import { Timestamp } from '@hcengineering/core' + import { Label } from '..' + import ui from '../plugin' + export let time: Timestamp + export let showHours = false const dispatch = createEventDispatcher() let displayTime = time let notified = false let intervalId: any | undefined = undefined + const dayMs = 1000 * 60 * 60 * 24 function applyTimer (time: number): void { if (intervalId !== undefined) { @@ -56,11 +61,19 @@ function getDisplayTime (time: number): string { const options: Intl.DateTimeFormatOptions = { minute: 'numeric', second: 'numeric' } + if (showHours) { + options.timeZone = 'UTC' + options.hour = 'numeric' + } return new Date(time).toLocaleString('default', options) } {#if displayTime > 0} - {getDisplayTime(displayTime)} + {#if displayTime < dayMs} + {getDisplayTime(displayTime)} + {:else} +