From 91aaa6d494f512186a51ffc1cbdbd1202759ebdc Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 26 Jun 2026 11:57:11 +0000 Subject: [PATCH 1/4] feat(account): DISABLE_SIGNUP + DISABLE_GUEST_LOGIN env vars + getLoginCapabilities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When DISABLE_SIGNUP=true on the account service, the signUp and signUpOtp operations throw Forbidden — preventing manual sign-ups even via direct API calls, not just hiding the UI tab. When DISABLE_GUEST_LOGIN=true, the loginAsGuest operation throws Forbidden without touching the database. Combined with the existing AccountNotFound branch (no guest person row), the guest login is fully controllable via env or DB state. A new getLoginCapabilities operation lets the client query both flags so the UI can hide the corresponding affordances. It reports signUpEnabled (false iff DISABLE_SIGNUP=true) and guestLoginAvailable (false iff DISABLE_GUEST_LOGIN=true OR no read-only guest person row exists). The client RPC stub is added to AccountClient as well. Signed-off-by: Michael Uray Signed-off-by: Michael Uray --- .../packages/account-client/src/client.ts | 10 ++ .../account/src/__tests__/operations.test.ts | 142 ++++++++++++++++++ server/account/src/operations.ts | 32 ++++ 3 files changed, 184 insertions(+) diff --git a/foundations/core/packages/account-client/src/client.ts b/foundations/core/packages/account-client/src/client.ts index 0a15fa45ed4..f4a5abd9c6e 100644 --- a/foundations/core/packages/account-client/src/client.ts +++ b/foundations/core/packages/account-client/src/client.ts @@ -147,6 +147,7 @@ export interface AccountClient { signUp: (email: string, password: string, first: string, last: string) => Promise login: (email: string, password: string) => Promise loginAsGuest: () => Promise + getLoginCapabilities: () => Promise<{ signUpEnabled: boolean, guestLoginAvailable: boolean }> isReadOnlyGuest: () => Promise getPerson: () => Promise getPersonInfo: (account: PersonUuid) => Promise @@ -722,6 +723,15 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } + async getLoginCapabilities (): Promise<{ signUpEnabled: boolean, guestLoginAvailable: boolean }> { + const request = { + method: 'getLoginCapabilities' as const, + params: {} + } + + return await this.rpc(request) + } + async isReadOnlyGuest (): Promise { const request = { method: 'isReadOnlyGuest' as const, diff --git a/server/account/src/__tests__/operations.test.ts b/server/account/src/__tests__/operations.test.ts index 686f9c6e336..f627b1ea909 100644 --- a/server/account/src/__tests__/operations.test.ts +++ b/server/account/src/__tests__/operations.test.ts @@ -37,6 +37,7 @@ import { getLoginInfoByToken, releaseSocialId, loginAsGuest, + getLoginCapabilities, loginOtp, login, confirm, @@ -1562,6 +1563,102 @@ describe('account operations', () => { new PlatformError(new Status(Severity.ERROR, platform.status.AccountNotFound, {})) ) }) + + test('should fail with Forbidden when DISABLE_GUEST_LOGIN=true', async () => { + const prev = process.env.DISABLE_GUEST_LOGIN + process.env.DISABLE_GUEST_LOGIN = 'true' + try { + await expect(loginAsGuest(mockCtx, mockDb, mockBranding, mockToken)).rejects.toThrow( + new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {})) + ) + expect(mockDb.person.findOne).not.toHaveBeenCalled() + } finally { + if (prev === undefined) { + delete process.env.DISABLE_GUEST_LOGIN + } else { + process.env.DISABLE_GUEST_LOGIN = prev + } + } + }) + }) + + describe('getLoginCapabilities', () => { + const restoreEnv = (key: string, prev: string | undefined): void => { + if (prev === undefined) { + delete process.env[key] + } else { + process.env[key] = prev + } + } + + test('should report both signUp and guest available by default when guest person exists', async () => { + const prevSignup = process.env.DISABLE_SIGNUP + const prevGuest = process.env.DISABLE_GUEST_LOGIN + delete process.env.DISABLE_SIGNUP + delete process.env.DISABLE_GUEST_LOGIN + try { + ;(mockDb.person.findOne as jest.Mock).mockResolvedValue({ uuid: readOnlyGuestAccountUuid }) + + const result = await getLoginCapabilities(mockCtx, mockDb, mockBranding, mockToken) + + expect(result).toEqual({ signUpEnabled: true, guestLoginAvailable: true }) + } finally { + restoreEnv('DISABLE_SIGNUP', prevSignup) + restoreEnv('DISABLE_GUEST_LOGIN', prevGuest) + } + }) + + test('should report guestLoginAvailable=false when guest person missing', async () => { + const prevSignup = process.env.DISABLE_SIGNUP + const prevGuest = process.env.DISABLE_GUEST_LOGIN + delete process.env.DISABLE_SIGNUP + delete process.env.DISABLE_GUEST_LOGIN + try { + ;(mockDb.person.findOne as jest.Mock).mockResolvedValue(null) + + const result = await getLoginCapabilities(mockCtx, mockDb, mockBranding, mockToken) + + expect(result).toEqual({ signUpEnabled: true, guestLoginAvailable: false }) + } finally { + restoreEnv('DISABLE_SIGNUP', prevSignup) + restoreEnv('DISABLE_GUEST_LOGIN', prevGuest) + } + }) + + test('should report signUpEnabled=false when DISABLE_SIGNUP=true', async () => { + const prevSignup = process.env.DISABLE_SIGNUP + const prevGuest = process.env.DISABLE_GUEST_LOGIN + process.env.DISABLE_SIGNUP = 'true' + delete process.env.DISABLE_GUEST_LOGIN + try { + ;(mockDb.person.findOne as jest.Mock).mockResolvedValue({ uuid: readOnlyGuestAccountUuid }) + + const result = await getLoginCapabilities(mockCtx, mockDb, mockBranding, mockToken) + + expect(result).toEqual({ signUpEnabled: false, guestLoginAvailable: true }) + } finally { + restoreEnv('DISABLE_SIGNUP', prevSignup) + restoreEnv('DISABLE_GUEST_LOGIN', prevGuest) + } + }) + + test('should report guestLoginAvailable=false when DISABLE_GUEST_LOGIN=true (no DB lookup)', async () => { + const prevSignup = process.env.DISABLE_SIGNUP + const prevGuest = process.env.DISABLE_GUEST_LOGIN + delete process.env.DISABLE_SIGNUP + process.env.DISABLE_GUEST_LOGIN = 'true' + try { + ;(mockDb.person.findOne as jest.Mock).mockClear() + + const result = await getLoginCapabilities(mockCtx, mockDb, mockBranding, mockToken) + + expect(result).toEqual({ signUpEnabled: true, guestLoginAvailable: false }) + expect(mockDb.person.findOne).not.toHaveBeenCalled() + } finally { + restoreEnv('DISABLE_SIGNUP', prevSignup) + restoreEnv('DISABLE_GUEST_LOGIN', prevGuest) + } + }) }) }) @@ -1798,6 +1895,29 @@ describe('account operations', () => { ) } }) + + test('should fail with Forbidden when DISABLE_SIGNUP=true', async () => { + const prev = process.env.DISABLE_SIGNUP + process.env.DISABLE_SIGNUP = 'true' + const signUpByEmailSpy = jest.spyOn(utils, 'signUpByEmail') + try { + await expect( + signUp(mockCtx, mockDb, mockBranding, mockToken, { + email: mockEmail, + password: mockPassword, + firstName: mockFirstName, + lastName: mockLastName + }) + ).rejects.toThrow(new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {}))) + expect(signUpByEmailSpy).not.toHaveBeenCalled() + } finally { + if (prev === undefined) { + delete process.env.DISABLE_SIGNUP + } else { + process.env.DISABLE_SIGNUP = prev + } + } + }) }) describe('confirm', () => { @@ -2022,6 +2142,28 @@ describe('account operations', () => { ) } }) + + test('should fail with Forbidden when DISABLE_SIGNUP=true', async () => { + const prev = process.env.DISABLE_SIGNUP + process.env.DISABLE_SIGNUP = 'true' + const getEmailSocialIdSpy = jest.spyOn(utils, 'getEmailSocialId') + try { + await expect( + signUpOtp(mockCtx, mockDb, mockBranding, mockToken, { + email: mockEmail, + firstName: mockFirstName, + lastName: mockLastName + }) + ).rejects.toThrow(new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {}))) + expect(getEmailSocialIdSpy).not.toHaveBeenCalled() + } finally { + if (prev === undefined) { + delete process.env.DISABLE_SIGNUP + } else { + process.env.DISABLE_SIGNUP = prev + } + } + }) }) describe('validateOtp', () => { diff --git a/server/account/src/operations.ts b/server/account/src/operations.ts index 3f2f3d5921b..a183c79558e 100644 --- a/server/account/src/operations.ts +++ b/server/account/src/operations.ts @@ -152,6 +152,9 @@ export async function loginAsGuest ( branding: Branding | null, token: string ): Promise { + if (process.env.DISABLE_GUEST_LOGIN === 'true') { + throw new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {})) + } const guestPerson = await db.person.findOne({ uuid: readOnlyGuestAccountUuid as PersonUuid }) if (guestPerson == null) { throw new PlatformError(new Status(Severity.ERROR, platform.status.AccountNotFound, {})) @@ -162,6 +165,27 @@ export async function loginAsGuest ( } } +/** + * Returns login affordance flags so clients can hide sign-up and guest-login + * controls when they are disabled by env vars or unavailable (no guest person). + */ +export async function getLoginCapabilities ( + ctx: MeasureContext, + db: AccountDB, + branding: Branding | null, + token: string +): Promise<{ signUpEnabled: boolean, guestLoginAvailable: boolean }> { + const signUpEnabled = process.env.DISABLE_SIGNUP !== 'true' + + let guestLoginAvailable = false + if (process.env.DISABLE_GUEST_LOGIN !== 'true') { + const guestPerson = await db.person.findOne({ uuid: readOnlyGuestAccountUuid as PersonUuid }) + guestLoginAvailable = guestPerson != null + } + + return { signUpEnabled, guestLoginAvailable } +} + /** * Given an email and password, logs the user in and returns the account information and token. * If the account has too many failed login attempts, password login is blocked. @@ -305,6 +329,9 @@ export async function signUp ( }, meta?: Meta ): Promise { + if (process.env.DISABLE_SIGNUP === 'true') { + throw new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {})) + } const { email, password, firstName, lastName } = params if (email == null || password == null || firstName == null || email === '' || password === '' || firstName === '') { @@ -349,6 +376,9 @@ export async function signUpOtp ( lastName?: string } ): Promise { + if (process.env.DISABLE_SIGNUP === 'true') { + throw new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {})) + } const { email, firstName, lastName } = params if (email == null || firstName == null || email === '' || firstName === '') { @@ -3295,6 +3325,7 @@ export type AccountMethods = | 'login' | 'loginOtp' | 'loginAsGuest' + | 'getLoginCapabilities' | 'signUp' | 'signUpOtp' | 'validateOtp' @@ -3377,6 +3408,7 @@ export function getMethods (hasSignUp: boolean = true): Partial Date: Fri, 26 Jun 2026 11:57:17 +0000 Subject: [PATCH 2/4] feat(login-resources): hide signup tab + guest button based on capabilities LoginForm now queries getLoginCapabilities on mount and uses the result to gate two UI affordances: - effectiveSignUpDisabled OR-s the existing signUpDisabled prop with !signUpEnabled from the server, so the Sign Up tab disappears when the account service reports sign-up disabled (DISABLE_SIGNUP=true). - The "Continue as guest" button is rendered only when guestLoginAvailable is true, which the server reports as false when either DISABLE_GUEST_LOGIN=true or the read-only guest person row is not present in the DB. fetchLoginCapabilities returns { signUpEnabled: true, guestLoginAvailable: true } on any RPC error to keep both controls visible when talking to older account services without the endpoint. Signed-off-by: Michael Uray Signed-off-by: Michael Uray --- .../packages/account-client/src/client.ts | 250 +++++++++--------- .../src/components/LoginForm.svelte | 38 ++- plugins/login-resources/src/utils.ts | 131 +++++---- .../account/src/__tests__/operations.test.ts | 2 +- server/account/src/operations.ts | 80 +++--- 5 files changed, 274 insertions(+), 227 deletions(-) diff --git a/foundations/core/packages/account-client/src/client.ts b/foundations/core/packages/account-client/src/client.ts index f4a5abd9c6e..34b32c3b452 100644 --- a/foundations/core/packages/account-client/src/client.ts +++ b/foundations/core/packages/account-client/src/client.ts @@ -147,7 +147,7 @@ export interface AccountClient { signUp: (email: string, password: string, first: string, last: string) => Promise login: (email: string, password: string) => Promise loginAsGuest: () => Promise - getLoginCapabilities: () => Promise<{ signUpEnabled: boolean, guestLoginAvailable: boolean }> + getLoginCapabilities: () => Promise<{ signUpEnabled: boolean; guestLoginAvailable: boolean }> isReadOnlyGuest: () => Promise getPerson: () => Promise getPersonInfo: (account: PersonUuid) => Promise @@ -156,7 +156,7 @@ export interface AccountClient { updateWorkspaceRole: (account: string, role: AccountRole) => Promise updateAllowReadOnlyGuests: ( readOnlyGuestsAllowed: boolean - ) => Promise<{ guestPerson: Person, guestSocialIds: SocialId[] } | undefined> + ) => Promise<{ guestPerson: Person; guestSocialIds: SocialId[] } | undefined> updateAllowGuestSignUp: (guestSignUpAllowed: boolean) => Promise updateWorkspaceName: (name: string) => Promise deleteWorkspace: () => Promise @@ -167,7 +167,7 @@ export interface AccountClient { findFullSocialIds: (socialIds: PersonId[]) => Promise getMailboxOptions: () => Promise getMailboxSecret: (mailbox: string) => Promise - createMailbox: (name: string, domain: string) => Promise<{ mailbox: string, socialId: PersonId }> + createMailbox: (name: string, domain: string) => Promise<{ mailbox: string; socialId: PersonId }> getMailboxes: () => Promise deleteMailbox: (mailbox: string) => Promise listAccounts: (search?: string, skip?: number, limit?: number) => Promise @@ -201,7 +201,7 @@ export interface AccountClient { socialValue: string, firstName: string, lastName: string - ) => Promise<{ uuid: PersonUuid, socialId: PersonId }> + ) => Promise<{ uuid: PersonUuid; socialId: PersonId }> addSocialIdToPerson: ( person: PersonUuid, type: SocialIdType, @@ -254,10 +254,10 @@ export interface AccountClient { getSubscriptionById: (subscriptionId: string) => Promise upsertSubscription: (subscription: SubscriptionData) => Promise - batchAssignWorkspacePermission: (params: { accountIds: AccountUuid[], permission: string }) => Promise - batchRevokeWorkspacePermission: (params: { accountIds: AccountUuid[], permission: string }) => Promise - hasWorkspacePermission: (params: { accountId: AccountUuid, permission: string }) => Promise - getWorkspacePermissions: (params: { accountId: AccountUuid, permission: string }) => Promise + batchAssignWorkspacePermission: (params: { accountIds: AccountUuid[]; permission: string }) => Promise + batchRevokeWorkspacePermission: (params: { accountIds: AccountUuid[]; permission: string }) => Promise + hasWorkspacePermission: (params: { accountId: AccountUuid; permission: string }) => Promise + getWorkspacePermissions: (params: { accountId: AccountUuid; permission: string }) => Promise getWorkspaceUsersWithPermission: (params: { permission: string }) => Promise verify2fa: (code: string) => Promise @@ -265,13 +265,13 @@ export interface AccountClient { setCookie: () => Promise deleteCookie: () => Promise - generate2faSecret: () => Promise<{ secret: string, url: string }> + generate2faSecret: () => Promise<{ secret: string; url: string }> enable2fa: (secret: string, code: string) => Promise disable2fa: (code: string) => Promise } /** @public */ -export function getClient (accountsUrl?: string, token?: string, retryTimeoutMs?: number): AccountClient { +export function getClient(accountsUrl?: string, token?: string, retryTimeoutMs?: number): AccountClient { if (accountsUrl === undefined) { throw new Error('Accounts url not specified') } @@ -288,7 +288,7 @@ class AccountClientImpl implements AccountClient { private readonly request: RequestInit private readonly rpc: typeof this._rpc - constructor ( + constructor( private readonly url: string, private readonly token?: string, retryTimeoutMs?: number @@ -313,7 +313,7 @@ class AccountClientImpl implements AccountClient { this.rpc = withRetryUntilTimeout(this._rpc.bind(this), retryTimeoutMs ?? 5000) } - async getProviders (): Promise { + async getProviders(): Promise { return await withRetryUntilMaxAttempts(async () => { const response = await fetch(concatLink(this.url, '/providers')) @@ -344,7 +344,7 @@ class AccountClientImpl implements AccountClient { return result.result } - private flattenStatus (ws: any): WorkspaceInfoWithStatus { + private flattenStatus(ws: any): WorkspaceInfoWithStatus { if (ws === undefined) { throw new PlatformError(new Status(Severity.ERROR, platform.status.WorkspaceNotFound, {})) } @@ -360,7 +360,7 @@ class AccountClientImpl implements AccountClient { return result } - async getUserWorkspaces (): Promise { + async getUserWorkspaces(): Promise { const request = { method: 'getUserWorkspaces' as const, params: {} @@ -369,7 +369,7 @@ class AccountClientImpl implements AccountClient { return (await this.rpc(request)).map((ws) => this.flattenStatus(ws)) } - async selectWorkspace ( + async selectWorkspace( workspaceUrl: string, kind: 'external' | 'internal' | 'byregion' = 'external', externalRegions: string[] = [] @@ -382,7 +382,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async validateOtp (email: string, code: string, password?: string, action?: 'verify'): Promise { + async validateOtp(email: string, code: string, password?: string, action?: 'verify'): Promise { const request = { method: 'validateOtp' as const, params: { email, code, password, action } @@ -391,7 +391,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async loginOtp (email: string): Promise { + async loginOtp(email: string): Promise { const request = { method: 'loginOtp' as const, params: { email } @@ -400,7 +400,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async getLoginInfoByToken (data?: LoginInfoRequestData): Promise { + async getLoginInfoByToken(data?: LoginInfoRequestData): Promise { const request = { method: 'getLoginInfoByToken' as const, params: data ?? {} @@ -409,7 +409,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async getLoginWithWorkspaceInfo (): Promise { + async getLoginWithWorkspaceInfo(): Promise { const request = { method: 'getLoginWithWorkspaceInfo' as const, params: {} @@ -418,7 +418,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async restorePassword (password: string): Promise { + async restorePassword(password: string): Promise { const request = { method: 'restorePassword' as const, params: { password } @@ -427,7 +427,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async confirm (): Promise { + async confirm(): Promise { const request = { method: 'confirm' as const, params: {} @@ -436,7 +436,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async requestPasswordReset (email: string): Promise { + async requestPasswordReset(email: string): Promise { const request = { method: 'requestPasswordReset' as const, params: { email } @@ -445,7 +445,7 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async sendInvite (email: string, role: AccountRole): Promise { + async sendInvite(email: string, role: AccountRole): Promise { const request = { method: 'sendInvite' as const, params: { email, role } @@ -454,7 +454,7 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async resendInvite (email: string, role: AccountRole): Promise { + async resendInvite(email: string, role: AccountRole): Promise { const request = { method: 'resendInvite' as const, params: { email, role } @@ -463,7 +463,7 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async createInviteLink ( + async createInviteLink( email: string, role: AccountRole, autoJoin: boolean, @@ -480,7 +480,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async createAccessLink ( + async createAccessLink( role: AccountRole, options?: { firstName?: string @@ -506,7 +506,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async leaveWorkspace (account: AccountUuid): Promise { + async leaveWorkspace(account: AccountUuid): Promise { const request = { method: 'leaveWorkspace' as const, params: { account } @@ -515,7 +515,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async changeUsername (first: string, last: string): Promise { + async changeUsername(first: string, last: string): Promise { const request = { method: 'changeUsername' as const, params: { first, last } @@ -524,7 +524,7 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async checkHasPassword (): Promise { + async checkHasPassword(): Promise { const request = { method: 'checkHasPassword' as const, params: {} @@ -533,7 +533,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async changePassword (oldPassword: string, newPassword: string): Promise { + async changePassword(oldPassword: string, newPassword: string): Promise { const request = { method: 'changePassword' as const, params: { oldPassword, newPassword } @@ -542,7 +542,7 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async requestPasswordSetup (): Promise { + async requestPasswordSetup(): Promise { const request = { method: 'requestPasswordSetup' as const, params: {} @@ -551,7 +551,7 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async updatePasswordAgingRule (days?: number): Promise { + async updatePasswordAgingRule(days?: number): Promise { const request = { method: 'updatePasswordAgingRule' as const, params: { days } @@ -560,7 +560,7 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async checkPasswordAging (): Promise { + async checkPasswordAging(): Promise { const request = { method: 'checkPasswordAging' as const, params: {} @@ -569,7 +569,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async signUpJoin ( + async signUpJoin( email: string, password: string, first: string, @@ -585,7 +585,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async join (email: string, password: string, inviteId: string, workspaceUrl: string): Promise { + async join(email: string, password: string, inviteId: string, workspaceUrl: string): Promise { const request = { method: 'join' as const, params: { email, password, inviteId, workspaceUrl } @@ -594,7 +594,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async createInvite (exp: number, emailMask: string, limit: number, role: AccountRole): Promise { + async createInvite(exp: number, emailMask: string, limit: number, role: AccountRole): Promise { const request = { method: 'createInvite' as const, params: { exp, emailMask, limit, role } @@ -603,7 +603,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async checkJoin (inviteId: string): Promise { + async checkJoin(inviteId: string): Promise { const request = { method: 'checkJoin' as const, params: { inviteId } @@ -612,7 +612,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async joinByToken (inviteId: string): Promise { + async joinByToken(inviteId: string): Promise { const request = { method: 'joinByToken' as const, params: { inviteId } @@ -621,7 +621,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async checkAutoJoin (inviteId: string, firstName?: string, lastName?: string): Promise { + async checkAutoJoin(inviteId: string, firstName?: string, lastName?: string): Promise { const request = { method: 'checkAutoJoin' as const, params: { inviteId, firstName, lastName } @@ -630,7 +630,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async getInviteInfo (inviteId: string): Promise { + async getInviteInfo(inviteId: string): Promise { const request = { method: 'getInviteInfo' as const, params: { inviteId } @@ -639,7 +639,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async getWorkspacesInfo (ids: WorkspaceUuid[]): Promise { + async getWorkspacesInfo(ids: WorkspaceUuid[]): Promise { const request = { method: 'getWorkspacesInfo' as const, params: { ids } @@ -648,7 +648,7 @@ class AccountClientImpl implements AccountClient { return Array.from(infos).map((it) => this.flattenStatus(it)) } - async updateLastVisit (ids: WorkspaceUuid[]): Promise { + async updateLastVisit(ids: WorkspaceUuid[]): Promise { const request = { method: 'updateLastVisit' as const, params: { ids } @@ -656,7 +656,7 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async getWorkspaceInfo (updateLastVisit: boolean = false): Promise { + async getWorkspaceInfo(updateLastVisit: boolean = false): Promise { const request = { method: 'getWorkspaceInfo' as const, params: updateLastVisit ? { updateLastVisit: true } : {} @@ -665,7 +665,7 @@ class AccountClientImpl implements AccountClient { return this.flattenStatus(await this.rpc(request)) } - async getRegionInfo (): Promise { + async getRegionInfo(): Promise { const request = { method: 'getRegionInfo' as const, params: {} @@ -674,7 +674,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async createWorkspace ( + async createWorkspace( workspaceName: string, region?: string, configuration?: WorkspaceConfiguration @@ -687,7 +687,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async signUpOtp (email: string, firstName: string, lastName: string): Promise { + async signUpOtp(email: string, firstName: string, lastName: string): Promise { const request = { method: 'signUpOtp' as const, params: { email, firstName, lastName } @@ -696,7 +696,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async signUp (email: string, password: string, firstName: string, lastName: string): Promise { + async signUp(email: string, password: string, firstName: string, lastName: string): Promise { const request = { method: 'signUp' as const, params: { email, password, firstName, lastName } @@ -705,7 +705,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async login (email: string, password: string): Promise { + async login(email: string, password: string): Promise { const request = { method: 'login' as const, params: { email, password } @@ -714,7 +714,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async loginAsGuest (): Promise { + async loginAsGuest(): Promise { const request = { method: 'loginAsGuest' as const, params: {} @@ -723,7 +723,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async getLoginCapabilities (): Promise<{ signUpEnabled: boolean, guestLoginAvailable: boolean }> { + async getLoginCapabilities(): Promise<{ signUpEnabled: boolean; guestLoginAvailable: boolean }> { const request = { method: 'getLoginCapabilities' as const, params: {} @@ -732,7 +732,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async isReadOnlyGuest (): Promise { + async isReadOnlyGuest(): Promise { const request = { method: 'isReadOnlyGuest' as const, params: {} @@ -741,7 +741,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async getPerson (): Promise { + async getPerson(): Promise { const request = { method: 'getPerson' as const, params: {} @@ -750,7 +750,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async getPersonInfo (account: PersonUuid): Promise { + async getPersonInfo(account: PersonUuid): Promise { const request = { method: 'getPersonInfo' as const, params: { account } @@ -759,7 +759,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async getSocialIds (includeDeleted?: boolean): Promise { + async getSocialIds(includeDeleted?: boolean): Promise { const request = { method: 'getSocialIds' as const, params: { includeDeleted } @@ -768,7 +768,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async workerHandshake (region: string, version: Data, operation: WorkspaceOperation): Promise { + async workerHandshake(region: string, version: Data, operation: WorkspaceOperation): Promise { const request = { method: 'workerHandshake' as const, params: { region, version, operation } @@ -777,7 +777,7 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async getPendingWorkspace ( + async getPendingWorkspace( region: string, version: Data, operation: WorkspaceOperation @@ -795,7 +795,7 @@ class AccountClientImpl implements AccountClient { return this.flattenStatus(result) } - async updateWorkspaceInfo ( + async updateWorkspaceInfo( workspaceUuid: string, event: string, version: Data, @@ -810,9 +810,9 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async updateAllowReadOnlyGuests ( + async updateAllowReadOnlyGuests( readOnlyGuestsAllowed: boolean - ): Promise<{ guestPerson: Person, guestSocialIds: SocialId[] } | undefined> { + ): Promise<{ guestPerson: Person; guestSocialIds: SocialId[] } | undefined> { const request = { method: 'updateAllowReadOnlyGuests' as const, params: { readOnlyGuestsAllowed } @@ -821,7 +821,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async updateAllowGuestSignUp (guestSignUpAllowed: boolean): Promise { + async updateAllowGuestSignUp(guestSignUpAllowed: boolean): Promise { const request = { method: 'updateAllowGuestSignUp' as const, params: { guestSignUpAllowed } @@ -830,7 +830,7 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async getWorkspaceMembers (): Promise { + async getWorkspaceMembers(): Promise { const request = { method: 'getWorkspaceMembers' as const, params: {} @@ -839,7 +839,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async updateWorkspaceRole (targetAccount: string, targetRole: AccountRole): Promise { + async updateWorkspaceRole(targetAccount: string, targetRole: AccountRole): Promise { const request = { method: 'updateWorkspaceRole' as const, params: { targetAccount, targetRole } @@ -848,7 +848,7 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async updateWorkspaceName (name: string): Promise { + async updateWorkspaceName(name: string): Promise { const request = { method: 'updateWorkspaceName' as const, params: { name } @@ -857,7 +857,7 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async deleteWorkspace (): Promise { + async deleteWorkspace(): Promise { const request = { method: 'deleteWorkspace' as const, params: {} @@ -866,7 +866,7 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async findPersonBySocialKey (socialString: string, requireAccount?: boolean): Promise { + async findPersonBySocialKey(socialString: string, requireAccount?: boolean): Promise { const request = { method: 'findPersonBySocialKey' as const, params: { socialString, requireAccount } @@ -875,7 +875,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async findPersonBySocialId (socialId: PersonId, requireAccount?: boolean): Promise { + async findPersonBySocialId(socialId: PersonId, requireAccount?: boolean): Promise { const request = { method: 'findPersonBySocialId' as const, params: { socialId, requireAccount } @@ -884,7 +884,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async findSocialIdBySocialKey (socialKey: string): Promise { + async findSocialIdBySocialKey(socialKey: string): Promise { const request = { method: 'findSocialIdBySocialKey' as const, params: { socialKey } @@ -893,7 +893,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async findFullSocialIdBySocialKey (socialKey: string): Promise { + async findFullSocialIdBySocialKey(socialKey: string): Promise { const request = { method: 'findFullSocialIdBySocialKey' as const, params: { socialKey } @@ -901,7 +901,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async findFullSocialIds (socialIds: PersonId[]): Promise { + async findFullSocialIds(socialIds: PersonId[]): Promise { const request = { method: 'findFullSocialIds' as const, params: { socialIds } @@ -909,7 +909,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async listWorkspaces (region?: string | null, mode: WorkspaceMode | null = null): Promise { + async listWorkspaces(region?: string | null, mode: WorkspaceMode | null = null): Promise { const request = { method: 'listWorkspaces' as const, params: { region, mode } @@ -918,7 +918,7 @@ class AccountClientImpl implements AccountClient { return ((await this.rpc(request)) ?? []).map((ws) => this.flattenStatus(ws)) } - async performWorkspaceOperation ( + async performWorkspaceOperation( workspaceId: string | string[], event: WorkspaceUserOperation, ...params: any @@ -931,7 +931,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async updateBackupInfo (backupInfo: BackupStatus): Promise { + async updateBackupInfo(backupInfo: BackupStatus): Promise { const request = { method: 'updateBackupInfo' as const, params: { backupInfo } @@ -940,7 +940,7 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async updateUsageInfo (usageInfo: UsageStatus): Promise { + async updateUsageInfo(usageInfo: UsageStatus): Promise { const request = { method: 'updateUsageInfo' as const, params: { usageInfo } @@ -949,7 +949,7 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async assignWorkspace (email: string, workspaceUuid: string, role: AccountRole): Promise { + async assignWorkspace(email: string, workspaceUuid: string, role: AccountRole): Promise { const request = { method: 'assignWorkspace' as const, params: { email, workspaceUuid, role } @@ -958,7 +958,7 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async updateWorkspaceRoleBySocialKey (socialKey: string, targetRole: AccountRole): Promise { + async updateWorkspaceRoleBySocialKey(socialKey: string, targetRole: AccountRole): Promise { const request = { method: 'updateWorkspaceRoleBySocialKey' as const, params: { socialKey, targetRole } @@ -967,12 +967,12 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async ensurePerson ( + async ensurePerson( socialType: SocialIdType, socialValue: string, firstName: string, lastName: string - ): Promise<{ uuid: PersonUuid, socialId: PersonId }> { + ): Promise<{ uuid: PersonUuid; socialId: PersonId }> { const request = { method: 'ensurePerson' as const, params: { socialType, socialValue, firstName, lastName } @@ -981,7 +981,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async exchangeGuestToken (token: string): Promise { + async exchangeGuestToken(token: string): Promise { const request = { method: 'exchangeGuestToken' as const, params: { token } @@ -990,7 +990,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async addSocialIdToPerson ( + async addSocialIdToPerson( person: PersonUuid, type: SocialIdType, value: string, @@ -1005,7 +1005,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async updateSocialId (personId: PersonId, displayValue: string): Promise { + async updateSocialId(personId: PersonId, displayValue: string): Promise { const request = { method: 'updateSocialId' as const, params: { personId, displayValue } @@ -1013,7 +1013,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async getMailboxOptions (): Promise { + async getMailboxOptions(): Promise { const request = { method: 'getMailboxOptions' as const, params: {} @@ -1022,7 +1022,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async getMailboxSecret (mailbox: string): Promise { + async getMailboxSecret(mailbox: string): Promise { const request = { method: 'getMailboxSecret' as const, params: { mailbox } @@ -1031,7 +1031,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async createMailbox (name: string, domain: string): Promise<{ mailbox: string, socialId: PersonId }> { + async createMailbox(name: string, domain: string): Promise<{ mailbox: string; socialId: PersonId }> { const request = { method: 'createMailbox' as const, params: { name, domain } @@ -1040,7 +1040,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async getMailboxes (): Promise { + async getMailboxes(): Promise { const request = { method: 'getMailboxes' as const, params: {} @@ -1049,7 +1049,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async deleteMailbox (mailbox: string): Promise { + async deleteMailbox(mailbox: string): Promise { const request = { method: 'deleteMailbox' as const, params: { mailbox } @@ -1058,7 +1058,7 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async listAccounts (search?: string, skip?: number, limit?: number): Promise { + async listAccounts(search?: string, skip?: number, limit?: number): Promise { const request = { method: 'listAccounts' as const, params: { search, skip, limit } @@ -1067,7 +1067,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async deleteAccount (uuid: AccountUuid): Promise { + async deleteAccount(uuid: AccountUuid): Promise { const request = { method: 'deleteAccount' as const, params: { uuid } @@ -1076,7 +1076,7 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async releaseSocialId ( + async releaseSocialId( personUuid: PersonUuid | undefined, type: SocialIdType, value: string, @@ -1090,7 +1090,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async createIntegration (integration: Integration): Promise { + async createIntegration(integration: Integration): Promise { const request = { method: 'createIntegration' as const, params: integration @@ -1099,7 +1099,7 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async updateIntegration (integration: Integration): Promise { + async updateIntegration(integration: Integration): Promise { const request = { method: 'updateIntegration' as const, params: integration @@ -1108,7 +1108,7 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async deleteIntegration (integrationKey: IntegrationKey): Promise { + async deleteIntegration(integrationKey: IntegrationKey): Promise { const request = { method: 'deleteIntegration' as const, params: integrationKey @@ -1117,7 +1117,7 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async getIntegration (integrationKey: IntegrationKey): Promise { + async getIntegration(integrationKey: IntegrationKey): Promise { const request = { method: 'getIntegration' as const, params: integrationKey @@ -1126,7 +1126,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async listIntegrations (filter: Partial): Promise { + async listIntegrations(filter: Partial): Promise { const request = { method: 'listIntegrations' as const, params: filter @@ -1135,7 +1135,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async addIntegrationSecret (integrationSecret: IntegrationSecret): Promise { + async addIntegrationSecret(integrationSecret: IntegrationSecret): Promise { const request = { method: 'addIntegrationSecret' as const, params: integrationSecret @@ -1144,7 +1144,7 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async updateIntegrationSecret (integrationSecret: IntegrationSecret): Promise { + async updateIntegrationSecret(integrationSecret: IntegrationSecret): Promise { const request = { method: 'updateIntegrationSecret' as const, params: integrationSecret @@ -1153,7 +1153,7 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async deleteIntegrationSecret (integrationSecretKey: IntegrationSecretKey): Promise { + async deleteIntegrationSecret(integrationSecretKey: IntegrationSecretKey): Promise { const request = { method: 'deleteIntegrationSecret' as const, params: integrationSecretKey @@ -1162,7 +1162,7 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async getIntegrationSecret (integrationSecretKey: IntegrationSecretKey): Promise { + async getIntegrationSecret(integrationSecretKey: IntegrationSecretKey): Promise { const request = { method: 'getIntegrationSecret' as const, params: integrationSecretKey @@ -1171,7 +1171,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async listIntegrationsSecrets (filter: Partial): Promise { + async listIntegrationsSecrets(filter: Partial): Promise { const request = { method: 'listIntegrationsSecrets' as const, params: filter @@ -1180,7 +1180,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async getAccountInfo (uuid: AccountUuid): Promise { + async getAccountInfo(uuid: AccountUuid): Promise { const request = { method: 'getAccountInfo' as const, params: { accountId: uuid } @@ -1189,7 +1189,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async canMergeSpecifiedPersons (primaryPerson: PersonUuid, secondaryPerson: PersonUuid): Promise { + async canMergeSpecifiedPersons(primaryPerson: PersonUuid, secondaryPerson: PersonUuid): Promise { const request = { method: 'canMergeSpecifiedPersons' as const, params: { primaryPerson, secondaryPerson } @@ -1198,7 +1198,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async mergeSpecifiedPersons (primaryPerson: PersonUuid, secondaryPerson: PersonUuid): Promise { + async mergeSpecifiedPersons(primaryPerson: PersonUuid, secondaryPerson: PersonUuid): Promise { const request = { method: 'mergeSpecifiedPersons' as const, params: { primaryPerson, secondaryPerson } @@ -1207,7 +1207,7 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async mergeSpecifiedAccounts (primaryAccount: AccountUuid, secondaryAccount: AccountUuid): Promise { + async mergeSpecifiedAccounts(primaryAccount: AccountUuid, secondaryAccount: AccountUuid): Promise { const request = { method: 'mergeSpecifiedAccounts' as const, params: { primaryAccount, secondaryAccount } @@ -1216,7 +1216,7 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async addEmailSocialId (email: string): Promise { + async addEmailSocialId(email: string): Promise { const request = { method: 'addEmailSocialId' as const, params: { email } @@ -1225,7 +1225,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async addHulyAssistantSocialId (): Promise { + async addHulyAssistantSocialId(): Promise { const request = { method: 'addHulyAssistantSocialId' as const, params: {} @@ -1234,7 +1234,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async refreshHulyAssistantToken (): Promise { + async refreshHulyAssistantToken(): Promise { const request = { method: 'refreshHulyAssistantToken' as const, params: {} @@ -1243,7 +1243,7 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async setCookie (): Promise { + async setCookie(): Promise { const url = concatLink(this.url, '/cookie') const response = await fetch(url, { ...this.request, method: 'PUT' }) @@ -1255,7 +1255,7 @@ class AccountClientImpl implements AccountClient { } } - async deleteCookie (): Promise { + async deleteCookie(): Promise { const url = concatLink(this.url, '/cookie') const response = await fetch(url, { ...this.request, method: 'DELETE' }) @@ -1267,7 +1267,7 @@ class AccountClientImpl implements AccountClient { } } - async setMyProfile (profile: Partial>): Promise { + async setMyProfile(profile: Partial>): Promise { const request = { method: 'setMyProfile', params: { @@ -1278,7 +1278,7 @@ class AccountClientImpl implements AccountClient { await this._rpc(request) } - async getUserProfile (personUuid?: PersonUuid): Promise { + async getUserProfile(personUuid?: PersonUuid): Promise { return await this._rpc({ method: 'getUserProfile', params: { @@ -1287,7 +1287,7 @@ class AccountClientImpl implements AccountClient { }) } - async getSubscriptions ( + async getSubscriptions( workspaceUuid: WorkspaceUuid | undefined = undefined, activeOnly: boolean = true ): Promise { @@ -1300,7 +1300,7 @@ class AccountClientImpl implements AccountClient { }) } - async getSubscriptionByProviderId (provider: string, providerSubscriptionId: string): Promise { + async getSubscriptionByProviderId(provider: string, providerSubscriptionId: string): Promise { return await this._rpc({ method: 'getSubscriptionByProviderId', params: { @@ -1310,7 +1310,7 @@ class AccountClientImpl implements AccountClient { }) } - async getSubscriptionById (subscriptionId: string): Promise { + async getSubscriptionById(subscriptionId: string): Promise { return await this._rpc({ method: 'getSubscriptionById', params: { @@ -1319,49 +1319,49 @@ class AccountClientImpl implements AccountClient { }) } - async upsertSubscription (subscription: SubscriptionData): Promise { + async upsertSubscription(subscription: SubscriptionData): Promise { await this._rpc({ method: 'upsertSubscription', params: subscription }) } - async batchAssignWorkspacePermission (params: { accountIds: AccountUuid[], permission: string }): Promise { + async batchAssignWorkspacePermission(params: { accountIds: AccountUuid[]; permission: string }): Promise { await this._rpc({ method: 'batchAssignWorkspacePermission', params }) } - async batchRevokeWorkspacePermission (params: { accountIds: AccountUuid[], permission: string }): Promise { + async batchRevokeWorkspacePermission(params: { accountIds: AccountUuid[]; permission: string }): Promise { await this._rpc({ method: 'batchRevokeWorkspacePermission', params }) } - async hasWorkspacePermission (params: { accountId: AccountUuid, permission: string }): Promise { + async hasWorkspacePermission(params: { accountId: AccountUuid; permission: string }): Promise { return await this._rpc({ method: 'hasWorkspacePermission', params }) } - async getWorkspacePermissions (params: { accountId: AccountUuid, permission: string }): Promise { + async getWorkspacePermissions(params: { accountId: AccountUuid; permission: string }): Promise { return await this._rpc({ method: 'getWorkspacePermissions', params }) } - async getWorkspaceUsersWithPermission (params: { permission: string }): Promise { + async getWorkspaceUsersWithPermission(params: { permission: string }): Promise { return await this._rpc({ method: 'getWorkspaceUsersWithPermission', params }) } - async verify2fa (code: string): Promise { + async verify2fa(code: string): Promise { const request = { method: 'verify2fa' as const, params: { code } @@ -1370,7 +1370,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async generate2faSecret (): Promise<{ secret: string, url: string }> { + async generate2faSecret(): Promise<{ secret: string; url: string }> { const request = { method: 'generate2faSecret' as const, params: {} @@ -1379,7 +1379,7 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async enable2fa (secret: string, code: string): Promise { + async enable2fa(secret: string, code: string): Promise { const request = { method: 'enable2fa' as const, params: { secret, code } @@ -1388,7 +1388,7 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async disable2fa (code: string): Promise { + async disable2fa(code: string): Promise { const request = { method: 'disable2fa' as const, params: { code } @@ -1398,7 +1398,7 @@ class AccountClientImpl implements AccountClient { } } -function withRetry Promise> ( +function withRetry Promise>( f: F, shouldFail: (err: any, attempt: number) => boolean, intervalMs: number = 25 @@ -1423,14 +1423,14 @@ function withRetry Promise> ( } as F } -function withRetryUntilTimeout Promise> (f: F, timeoutMs: number = 5000): F { +function withRetryUntilTimeout Promise>(f: F, timeoutMs: number = 5000): F { const timeout = Date.now() + timeoutMs const shouldFail = (err: any): boolean => !isNetworkError(err) || timeout < Date.now() return withRetry(f, shouldFail) } -function withRetryUntilMaxAttempts Promise> (f: F, maxAttempts: number = 5): F { +function withRetryUntilMaxAttempts Promise>(f: F, maxAttempts: number = 5): F { const shouldFail = (err: any, attempt: number): boolean => !isNetworkError(err) || attempt === maxAttempts return withRetry(f, shouldFail) diff --git a/plugins/login-resources/src/components/LoginForm.svelte b/plugins/login-resources/src/components/LoginForm.svelte index ed4485488d0..02b752ec4b0 100644 --- a/plugins/login-resources/src/components/LoginForm.svelte +++ b/plugins/login-resources/src/components/LoginForm.svelte @@ -27,6 +27,7 @@ import BottomActionComponent from './BottomAction.svelte' import login from '../plugin' import { LoginInfo } from '@hcengineering/account-client' + import { fetchLoginCapabilities } from '../utils' export let navigateUrl: string | undefined = undefined export let signUpDisabled = false @@ -37,9 +38,18 @@ export let onLogin: ((loginInfo: LoginInfo | null, status: Status) => void | Promise) | undefined = undefined let method: LoginMethods = useOTP ? LoginMethods.Otp : LoginMethods.Password + let guestLoginAvailable = false + let capabilitiesLoaded = false + let signUpEnabled = true + $: effectiveSignUpDisabled = signUpDisabled || (capabilitiesLoaded && !signUpEnabled) onMount(() => { signupStore.setSignUpFlow(false) + void fetchLoginCapabilities().then((capabilities) => { + signUpEnabled = capabilities.signUpEnabled + guestLoginAvailable = capabilities.guestLoginAvailable + capabilitiesLoaded = true + }) }) function changeMethod (event: CustomEvent): void { @@ -87,15 +97,33 @@ {#if method === LoginMethods.Otp} - + {:else} - + {/if}
- + {#if guestLoginAvailable} + + {/if}