diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 518b7672e..299a09614 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -2727,6 +2727,7 @@ export type User = { supportUserHash?: Maybe; updatedAt: Scalars['DateTime']; userVars?: Maybe; + userWorkspaces: Array; workspaceMember?: Maybe; workspaceMembers?: Maybe>; workspaces: Array; diff --git a/packages/twenty-front/src/generated/graphql.ts b/packages/twenty-front/src/generated/graphql.ts index 72e911bc8..7e7bb76f6 100644 --- a/packages/twenty-front/src/generated/graphql.ts +++ b/packages/twenty-front/src/generated/graphql.ts @@ -2565,6 +2565,7 @@ export type User = { supportUserHash?: Maybe; updatedAt: Scalars['DateTime']; userVars?: Maybe; + userWorkspaces: Array; workspaceMember?: Maybe; workspaceMembers?: Maybe>; workspaces: Array; diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/0-54/0-54-lowercase-user-and-invitation-emails.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/0-54/0-54-lowercase-user-and-invitation-emails.command.ts index 6e04d4c0b..a91782840 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/0-54/0-54-lowercase-user-and-invitation-emails.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/0-54/0-54-lowercase-user-and-invitation-emails.command.ts @@ -49,7 +49,7 @@ export class LowercaseUserAndInvitationEmailsCommand extends ActiveOrSuspendedWo private async lowercaseUserEmails(workspaceId: string, dryRun: boolean) { const users = await this.userRepository.find({ where: { - workspaces: { + userWorkspaces: { workspaceId, }, email: Raw((alias) => `LOWER(${alias}) != ${alias}`), diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-config/hooks/use-cached-metadata.ts b/packages/twenty-server/src/engine/api/graphql/graphql-config/hooks/use-cached-metadata.ts index 24695ff3f..8b0296763 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-config/hooks/use-cached-metadata.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-config/hooks/use-cached-metadata.ts @@ -1,7 +1,10 @@ import { createHash } from 'crypto'; -import { isNonEmptyString } from '@sniptt/guards'; +import { Request } from 'express'; import { Plugin } from 'graphql-yoga'; +import { isDefined } from 'twenty-shared/utils'; + +import { InternalServerError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; export type CacheMetadataPluginConfig = { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -12,19 +15,26 @@ export type CacheMetadataPluginConfig = { }; export function useCachedMetadata(config: CacheMetadataPluginConfig): Plugin { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const computeCacheKey = (serverContext: any) => { - const workspaceId = serverContext.req.workspace?.id ?? 'anonymous'; - const workspaceMetadataVersion = - serverContext.req.workspaceMetadataVersion ?? '0'; - const operationName = getOperationName(serverContext); - const locale = serverContext.req.locale; - const localeCacheKey = isNonEmptyString(locale) ? `:${locale}` : ''; + const computeCacheKey = ({ + operationName, + request, + }: { + operationName: string; + request: Pick; + }) => { + const workspace = request.workspace; + + if (!isDefined(workspace)) { + throw new InternalServerError('Workspace is not defined'); + } + + const workspaceMetadataVersion = workspace.metadataVersion ?? '0'; + const locale = request.locale; const queryHash = createHash('sha256') - .update(serverContext.req.body.query) + .update(request.body.query) .digest('hex'); - return `graphql:operations:${operationName}:${workspaceId}:${workspaceMetadataVersion}${localeCacheKey}:${queryHash}`; + return `graphql:operations:${operationName}:${workspace.id}:${workspaceMetadataVersion}:${locale}:${queryHash}`; }; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -37,7 +47,11 @@ export function useCachedMetadata(config: CacheMetadataPluginConfig): Plugin { return; } - const cacheKey = computeCacheKey(serverContext); + const cacheKey = computeCacheKey({ + operationName: getOperationName(serverContext), + // TODO: we should probably override the graphql-yoga request type to include the workspace and locale + request: (serverContext as unknown as { req: Request }).req, + }); const cachedResponse = await config.cacheGetter(cacheKey); if (cachedResponse) { @@ -51,7 +65,10 @@ export function useCachedMetadata(config: CacheMetadataPluginConfig): Plugin { return; } - const cacheKey = computeCacheKey(serverContext); + const cacheKey = computeCacheKey({ + operationName: getOperationName(serverContext), + request: (serverContext as unknown as { req: Request }).req, + }); const cachedResponse = await config.cacheGetter(cacheKey); diff --git a/packages/twenty-server/src/engine/core-modules/admin-panel/__tests__/admin-panel.service.spec.ts b/packages/twenty-server/src/engine/core-modules/admin-panel/__tests__/admin-panel.service.spec.ts index dc12a7c23..ee4303591 100644 --- a/packages/twenty-server/src/engine/core-modules/admin-panel/__tests__/admin-panel.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/admin-panel/__tests__/admin-panel.service.spec.ts @@ -91,7 +91,7 @@ describe('AdminPanelService', () => { const mockUser = { id: 'user-id', email: 'user@example.com', - workspaces: [ + userWorkspaces: [ { workspace: { id: 'workspace-id', @@ -114,12 +114,12 @@ describe('AdminPanelService', () => { expect.objectContaining({ where: expect.objectContaining({ id: 'user-id', - workspaces: { + userWorkspaces: { workspaceId: 'workspace-id', workspace: { allowImpersonation: true }, }, }), - relations: ['workspaces', 'workspaces.workspace'], + relations: { userWorkspaces: { workspace: true } }, }), ); diff --git a/packages/twenty-server/src/engine/core-modules/admin-panel/admin-panel.service.ts b/packages/twenty-server/src/engine/core-modules/admin-panel/admin-panel.service.ts index 9b536cc36..6bbf0ff92 100644 --- a/packages/twenty-server/src/engine/core-modules/admin-panel/admin-panel.service.ts +++ b/packages/twenty-server/src/engine/core-modules/admin-panel/admin-panel.service.ts @@ -39,14 +39,14 @@ export class AdminPanelService { const user = await this.userRepository.findOne({ where: { id: userId, - workspaces: { + userWorkspaces: { workspaceId, workspace: { allowImpersonation: true, }, }, }, - relations: ['workspaces', 'workspaces.workspace'], + relations: { userWorkspaces: { workspace: true } }, }); userValidator.assertIsDefinedOrThrow( @@ -59,14 +59,14 @@ export class AdminPanelService { const loginToken = await this.loginTokenService.generateLoginToken( user.email, - user.workspaces[0].workspace.id, + user.userWorkspaces[0].workspace.id, ); return { workspace: { - id: user.workspaces[0].workspace.id, + id: user.userWorkspaces[0].workspace.id, workspaceUrls: this.domainManagerService.getWorkspaceUrls( - user.workspaces[0].workspace, + user.userWorkspaces[0].workspace, ), }, loginToken, @@ -101,7 +101,7 @@ export class AdminPanelService { firstName: targetUser.firstName, lastName: targetUser.lastName, }, - workspaces: targetUser.workspaces.map((userWorkspace) => ({ + workspaces: targetUser.userWorkspaces.map((userWorkspace) => ({ id: userWorkspace.workspace.id, name: userWorkspace.workspace.displayName ?? '', totalUsers: userWorkspace.workspace.workspaceUsers.length, diff --git a/packages/twenty-server/src/engine/core-modules/approved-access-domain/services/approved-access-domain.service.ts b/packages/twenty-server/src/engine/core-modules/approved-access-domain/services/approved-access-domain.service.ts index 1e2bc929a..0e8297f8d 100644 --- a/packages/twenty-server/src/engine/core-modules/approved-access-domain/services/approved-access-domain.service.ts +++ b/packages/twenty-server/src/engine/core-modules/approved-access-domain/services/approved-access-domain.service.ts @@ -6,7 +6,6 @@ import crypto from 'crypto'; import { t } from '@lingui/core/macro'; import { render } from '@react-email/render'; import { SendApprovedAccessDomainValidation } from 'twenty-emails'; -import { APP_LOCALES } from 'twenty-shared/translations'; import { Repository } from 'typeorm'; import { ApprovedAccessDomain as ApprovedAccessDomainEntity } from 'src/engine/core-modules/approved-access-domain/approved-access-domain.entity'; @@ -78,7 +77,7 @@ export class ApprovedAccessDomainService { lastName: sender.name.lastName, }, serverUrl: this.twentyConfigService.get('SERVER_URL'), - locale: 'en' as keyof typeof APP_LOCALES, + locale: 'en', }); const html = await render(emailTemplate); const text = await render(emailTemplate, { diff --git a/packages/twenty-server/src/engine/core-modules/auth/auth.resolver.ts b/packages/twenty-server/src/engine/core-modules/auth/auth.resolver.ts index b4d43a623..625719bda 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/auth.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/auth.resolver.ts @@ -528,14 +528,13 @@ export class AuthResolver { async updatePasswordViaResetToken( @Args() { passwordResetToken, newPassword }: UpdatePasswordViaResetTokenInput, - @Context() context: I18nContext, ): Promise { const { id } = await this.resetPasswordService.validatePasswordResetToken( passwordResetToken, ); - await this.authService.updatePassword(id, newPassword, context.req.locale); + await this.authService.updatePassword(id, newPassword); return await this.resetPasswordService.invalidatePasswordResetToken(id); } diff --git a/packages/twenty-server/src/engine/core-modules/auth/controllers/sso-auth.controller.ts b/packages/twenty-server/src/engine/core-modules/auth/controllers/sso-auth.controller.ts index 9d4f93b97..bddb41ee2 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/controllers/sso-auth.controller.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/controllers/sso-auth.controller.ts @@ -115,7 +115,7 @@ export class SSOAuthController { const workspaceIdentityProvider = await this.workspaceSSOIdentityProviderRepository.findOne({ where: { id: req.user.identityProviderId }, - relations: ['workspace'], + relations: { workspace: true }, }); try { diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/auth.service.ts b/packages/twenty-server/src/engine/core-modules/auth/services/auth.service.ts index a11e94a19..91484715c 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/services/auth.service.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/services/auth.service.ts @@ -9,7 +9,6 @@ import { render } from '@react-email/render'; import { addMilliseconds } from 'date-fns'; import ms from 'ms'; import { PasswordUpdateNotifyEmail } from 'twenty-emails'; -import { APP_LOCALES } from 'twenty-shared/translations'; import { isDefined } from 'twenty-shared/utils'; import { Repository } from 'typeorm'; @@ -142,7 +141,7 @@ export class AuthService { where: { email: input.email, }, - relations: ['workspaces'], + relations: { userWorkspaces: true }, }); if (!user) { @@ -424,7 +423,6 @@ export class AuthService { async updatePassword( userId: string, newPassword: string, - locale: keyof typeof APP_LOCALES, ): Promise { if (!userId) { throw new AuthException( @@ -433,7 +431,10 @@ export class AuthService { ); } - const user = await this.userRepository.findOneBy({ id: userId }); + const user = await this.userRepository.findOne({ + where: { id: userId }, + relations: { userWorkspaces: true }, + }); if (!user) { throw new AuthException( @@ -442,6 +443,15 @@ export class AuthService { ); } + const [firstUserWorkspace] = user.userWorkspaces; + + if (!firstUserWorkspace) { + throw new AuthException( + 'User does not have a workspace', + AuthExceptionCode.USER_WORKSPACE_NOT_FOUND, + ); + } + const isPasswordValid = PASSWORD_REGEX.test(newPassword); if (!isPasswordValid) { @@ -461,13 +471,13 @@ export class AuthService { userName: `${user.firstName} ${user.lastName}`, email: user.email, link: this.domainManagerService.getBaseUrl().toString(), - locale, + locale: firstUserWorkspace.locale, }); const html = await render(emailTemplate, { pretty: true }); const text = await render(emailTemplate, { plainText: true }); - i18n.activate(locale); + i18n.activate(firstUserWorkspace.locale); this.emailService.send({ from: `${this.twentyConfigService.get( diff --git a/packages/twenty-server/src/engine/core-modules/email-verification/email-verification.resolver.ts b/packages/twenty-server/src/engine/core-modules/email-verification/email-verification.resolver.ts index 86c19cdd9..f5443a8a4 100644 --- a/packages/twenty-server/src/engine/core-modules/email-verification/email-verification.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/email-verification/email-verification.resolver.ts @@ -23,6 +23,7 @@ export class EmailVerificationResolver { private readonly domainManagerService: DomainManagerService, ) {} + // TODO: this should be an authenticated endpoint @Mutation(() => ResendEmailVerificationTokenOutput) @UseGuards(PublicEndpointGuard) async resendEmailVerificationToken( diff --git a/packages/twenty-server/src/engine/core-modules/sso/services/sso.service.ts b/packages/twenty-server/src/engine/core-modules/sso/services/sso.service.ts index 5d38b00fd..eb92c554f 100644 --- a/packages/twenty-server/src/engine/core-modules/sso/services/sso.service.ts +++ b/packages/twenty-server/src/engine/core-modules/sso/services/sso.service.ts @@ -132,7 +132,7 @@ export class SSOService { async findSSOIdentityProviderById(identityProviderId: string) { return (await this.workspaceSSOIdentityProviderRepository.findOne({ where: { id: identityProviderId }, - relations: ['workspace'], + relations: { workspace: true }, })) as (SSOConfiguration & WorkspaceSSOIdentityProvider) | null; } diff --git a/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.entity.ts b/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.entity.ts index 6cda98867..1b6735227 100644 --- a/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.entity.ts +++ b/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.entity.ts @@ -2,6 +2,7 @@ import { Field, ObjectType, registerEnumType } from '@nestjs/graphql'; import { IDField } from '@ptc-org/nestjs-query-graphql'; import { PermissionsOnAllObjectRecords } from 'twenty-shared/constants'; +import { APP_LOCALES } from 'twenty-shared/translations'; import { Column, CreateDateColumn, @@ -46,7 +47,7 @@ export class UserWorkspace { id: string; @Field(() => User) - @ManyToOne(() => User, (user) => user.workspaces, { + @ManyToOne(() => User, (user) => user.userWorkspaces, { onDelete: 'CASCADE', }) @JoinColumn({ name: 'userId' }) @@ -71,8 +72,8 @@ export class UserWorkspace { defaultAvatarUrl: string; @Field(() => String, { nullable: false }) - @Column({ nullable: false, default: 'en' }) - locale: string; + @Column({ nullable: false, default: 'en', type: 'varchar' }) + locale: keyof typeof APP_LOCALES; @Field() @CreateDateColumn({ type: 'timestamptz' }) diff --git a/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.spec.ts b/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.spec.ts index d336d185f..977bd8f66 100644 --- a/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.spec.ts @@ -615,7 +615,7 @@ describe('UserWorkspaceService', () => { } as unknown as Workspace; const user = { email, - workspaces: [ + userWorkspaces: [ { workspaceId: workspace1.id, workspace: workspace1, @@ -645,12 +645,14 @@ describe('UserWorkspaceService', () => { where: { email, }, - relations: [ - 'workspaces', - 'workspaces.workspace', - 'workspaces.workspace.workspaceSSOIdentityProviders', - 'workspaces.workspace.approvedAccessDomains', - ], + relations: { + userWorkspaces: { + workspace: { + workspaceSSOIdentityProviders: true, + approvedAccessDomains: true, + }, + }, + }, }); expect(result).toEqual({ @@ -694,7 +696,7 @@ describe('UserWorkspaceService', () => { const user = { email, - workspaces: [ + userWorkspaces: [ { workspaceId: workspace1.id, workspace: workspace1, @@ -727,12 +729,14 @@ describe('UserWorkspaceService', () => { where: { email, }, - relations: [ - 'workspaces', - 'workspaces.workspace', - 'workspaces.workspace.workspaceSSOIdentityProviders', - 'workspaces.workspace.approvedAccessDomains', - ], + relations: { + userWorkspaces: { + workspace: { + workspaceSSOIdentityProviders: true, + approvedAccessDomains: true, + }, + }, + }, }); expect(result).toEqual({ @@ -792,7 +796,7 @@ describe('UserWorkspaceService', () => { } as unknown as Workspace; const user = { id: userId, - workspaces: [{ workspace: workspace1 }, { workspace: workspace2 }], + userWorkspaces: [{ workspace: workspace1 }, { workspace: workspace2 }], } as unknown as User; jest.spyOn(userRepository, 'findOne').mockResolvedValue(user); @@ -803,9 +807,9 @@ describe('UserWorkspaceService', () => { where: { id: userId, }, - relations: ['workspaces', 'workspaces.workspace'], + relations: { userWorkspaces: { workspace: true } }, order: { - workspaces: { + userWorkspaces: { workspace: { createdAt: 'ASC', }, diff --git a/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.ts b/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.ts index 6eb9609aa..cf870bc4d 100644 --- a/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.ts +++ b/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.ts @@ -222,9 +222,9 @@ export class UserWorkspaceService extends TypeOrmQueryService { where: { id: userId, }, - relations: ['workspaces', 'workspaces.workspace'], + relations: { userWorkspaces: { workspace: true } }, order: { - workspaces: { + userWorkspaces: { workspace: { createdAt: 'ASC', }, @@ -232,7 +232,7 @@ export class UserWorkspaceService extends TypeOrmQueryService { }, }); - const workspace = user?.workspaces?.[0]?.workspace; + const workspace = user?.userWorkspaces?.[0]?.workspace; workspaceValidator.assertIsDefinedOrThrow( workspace, @@ -250,16 +250,18 @@ export class UserWorkspaceService extends TypeOrmQueryService { where: { email, }, - relations: [ - 'workspaces', - 'workspaces.workspace', - 'workspaces.workspace.workspaceSSOIdentityProviders', - 'workspaces.workspace.approvedAccessDomains', - ], + relations: { + userWorkspaces: { + workspace: { + workspaceSSOIdentityProviders: true, + approvedAccessDomains: true, + }, + }, + }, }); const alreadyMemberWorkspaces = user - ? user.workspaces.map(({ workspace }) => ({ workspace })) + ? user.userWorkspaces.map(({ workspace }) => ({ workspace })) : []; const alreadyMemberWorkspacesIds = alreadyMemberWorkspaces.map( diff --git a/packages/twenty-server/src/engine/core-modules/user/services/user.service.ts b/packages/twenty-server/src/engine/core-modules/user/services/user.service.ts index 7e4e92d53..1e2ffc73f 100644 --- a/packages/twenty-server/src/engine/core-modules/user/services/user.service.ts +++ b/packages/twenty-server/src/engine/core-modules/user/services/user.service.ts @@ -97,13 +97,13 @@ export class UserService extends TypeOrmQueryService { where: { id: userId, }, - relations: ['workspaces'], + relations: { userWorkspaces: true }, }); userValidator.assertIsDefinedOrThrow(user); const prepareForUserDeletionInWorkspaces = await Promise.all( - user.workspaces.map(async (userWorkspace) => { + user.userWorkspaces.map(async (userWorkspace) => { const { workspaceId } = userWorkspace; const workspaceMemberRepository = @@ -200,11 +200,11 @@ export class UserService extends TypeOrmQueryService { const user = await this.userRepository.findOne({ where: { id: userId, - workspaces: { + userWorkspaces: { workspaceId, }, }, - relations: ['workspaces'], + relations: { userWorkspaces: true }, }); userValidator.assertIsDefinedOrThrow( diff --git a/packages/twenty-server/src/engine/core-modules/user/user.entity.ts b/packages/twenty-server/src/engine/core-modules/user/user.entity.ts index 1a0f57d9e..32976643b 100644 --- a/packages/twenty-server/src/engine/core-modules/user/user.entity.ts +++ b/packages/twenty-server/src/engine/core-modules/user/user.entity.ts @@ -112,7 +112,7 @@ export class User { @Field(() => [UserWorkspace]) @OneToMany(() => UserWorkspace, (userWorkspace) => userWorkspace.user) - workspaces: Relation; + userWorkspaces: Relation; @Field(() => OnboardingStatus, { nullable: true }) onboardingStatus: OnboardingStatus; diff --git a/packages/twenty-server/src/engine/core-modules/user/user.resolver.ts b/packages/twenty-server/src/engine/core-modules/user/user.resolver.ts index 245760bdc..f30d6d846 100644 --- a/packages/twenty-server/src/engine/core-modules/user/user.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/user/user.resolver.ts @@ -121,7 +121,7 @@ export class UserResolver { where: { id: userId, }, - relations: ['workspaces'], + relations: { userWorkspaces: true }, }); userValidator.assertIsDefinedOrThrow( @@ -133,7 +133,7 @@ export class UserResolver { return user; } - const currentUserWorkspace = user.workspaces.find( + const currentUserWorkspace = user.userWorkspaces.find( (userWorkspace) => userWorkspace.workspaceId === workspace.id, ); @@ -387,6 +387,13 @@ export class UserResolver { return workspace; } + @ResolveField(() => [UserWorkspace], { + nullable: false, + }) + async workspaces(@Parent() user: User) { + return user.userWorkspaces; + } + @ResolveField(() => AvailableWorkspaces) async availableWorkspaces( @AuthUser() user: User, diff --git a/packages/twenty-server/src/engine/core-modules/workspace-invitation/services/workspace-invitation.service.ts b/packages/twenty-server/src/engine/core-modules/workspace-invitation/services/workspace-invitation.service.ts index 6292e09fb..389077596 100644 --- a/packages/twenty-server/src/engine/core-modules/workspace-invitation/services/workspace-invitation.service.ts +++ b/packages/twenty-server/src/engine/core-modules/workspace-invitation/services/workspace-invitation.service.ts @@ -9,7 +9,6 @@ import { render } from '@react-email/render'; import { addMilliseconds } from 'date-fns'; import ms from 'ms'; import { SendInviteLinkEmail } from 'twenty-emails'; -import { APP_LOCALES } from 'twenty-shared/translations'; import { IsNull, Repository } from 'typeorm'; import { @@ -61,7 +60,7 @@ export class WorkspaceInvitationService { value: workspacePersonalInviteToken, type: AppTokenType.InvitationToken, }, - relations: ['workspace'], + relations: { workspace: true }, }); if (!appToken) { @@ -119,7 +118,7 @@ export class WorkspaceInvitationService { value: invitationToken, type: AppTokenType.InvitationToken, }, - relations: ['workspace'], + relations: { workspace: true }, }); if (!appToken) { @@ -298,7 +297,7 @@ export class WorkspaceInvitationService { lastName: sender.name.lastName, }, serverUrl: this.twentyConfigService.get('SERVER_URL'), - locale: sender.locale as keyof typeof APP_LOCALES, + locale: sender.locale, }; const emailTemplate = SendInviteLinkEmail(emailData); diff --git a/packages/twenty-server/src/engine/middlewares/middleware.service.ts b/packages/twenty-server/src/engine/middlewares/middleware.service.ts index 9787064a2..dbac7a338 100644 --- a/packages/twenty-server/src/engine/middlewares/middleware.service.ts +++ b/packages/twenty-server/src/engine/middlewares/middleware.service.ts @@ -161,8 +161,8 @@ export class MiddlewareService { request.authProvider = data.authProvider; request.locale = - ((data.userWorkspace?.locale ?? - request.headers['x-locale']) as keyof typeof APP_LOCALES) ?? + data.userWorkspace?.locale ?? + (request.headers['x-locale'] as keyof typeof APP_LOCALES) ?? SOURCE_LOCALE; }