diff --git a/packages/twenty-front/src/modules/settings/profile/components/ProfilePictureUploader.tsx b/packages/twenty-front/src/modules/settings/profile/components/ProfilePictureUploader.tsx
index cef17405c..dcfcfaf5e 100644
--- a/packages/twenty-front/src/modules/settings/profile/components/ProfilePictureUploader.tsx
+++ b/packages/twenty-front/src/modules/settings/profile/components/ProfilePictureUploader.tsx
@@ -51,7 +51,7 @@ export const ProfilePictureUploader = () => {
setUploadController(null);
setErrorMessage(null);
- const avatarUrl = result?.data?.uploadProfilePicture;
+ const avatarUrl = result?.data?.uploadProfilePicture.split('?')[0];
if (!avatarUrl) {
throw new Error('Avatar URL not found');
@@ -64,7 +64,10 @@ export const ProfilePictureUploader = () => {
},
});
- setCurrentWorkspaceMember({ ...currentWorkspaceMember, avatarUrl });
+ setCurrentWorkspaceMember({
+ ...currentWorkspaceMember,
+ avatarUrl: result?.data?.uploadProfilePicture,
+ });
return result;
} catch (error) {
diff --git a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerHeader.tsx b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerHeader.tsx
index ddea26d1a..d6bbf8100 100644
--- a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerHeader.tsx
+++ b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerHeader.tsx
@@ -7,6 +7,7 @@ import { DEFAULT_WORKSPACE_LOGO } from '@/ui/navigation/navigation-drawer/consta
import { DEFAULT_WORKSPACE_NAME } from '@/ui/navigation/navigation-drawer/constants/DefaultWorkspaceName';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
+import { isNonEmptyString } from '@sniptt/guards';
import { NavigationDrawerCollapseButton } from './NavigationDrawerCollapseButton';
const StyledContainer = styled.div<{ isMultiWorkspace: boolean }>`
@@ -65,7 +66,9 @@ export const NavigationDrawerHeader = ({
) : (
<>
-
+
{name}
>
)}
diff --git a/packages/twenty-server/src/engine/core-modules/auth/auth.module.ts b/packages/twenty-server/src/engine/core-modules/auth/auth.module.ts
index 2a090c79e..064cb7aad 100644
--- a/packages/twenty-server/src/engine/core-modules/auth/auth.module.ts
+++ b/packages/twenty-server/src/engine/core-modules/auth/auth.module.ts
@@ -1,7 +1,6 @@
/* eslint-disable no-restricted-imports */
import { HttpModule } from '@nestjs/axios';
import { forwardRef, Module } from '@nestjs/common';
-import { JwtModule } from '@nestjs/jwt';
import { TypeOrmModule } from '@nestjs/typeorm';
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
@@ -16,13 +15,13 @@ import { SignInUpService } from 'src/engine/core-modules/auth/services/sign-in-u
import { TokenService } from 'src/engine/core-modules/auth/services/token.service';
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
import { FileUploadModule } from 'src/engine/core-modules/file/file-upload/file-upload.module';
+import { JwtModule } from 'src/engine/core-modules/jwt/jwt.module';
import { OnboardingModule } from 'src/engine/core-modules/onboarding/onboarding.module';
import { UserWorkspaceModule } from 'src/engine/core-modules/user-workspace/user-workspace.module';
import { User } from 'src/engine/core-modules/user/user.entity';
import { UserModule } from 'src/engine/core-modules/user/user.module';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { WorkspaceModule } from 'src/engine/core-modules/workspace/workspace.module';
-import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module';
@@ -32,27 +31,15 @@ import { CalendarChannelWorkspaceEntity } from 'src/modules/calendar/common/stan
import { ConnectedAccountModule } from 'src/modules/connected-account/connected-account.module';
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
import { MessageChannelWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
-import { UserVarsModule } from 'src/engine/core-modules/user/user-vars/user-vars.module';
import { AuthResolver } from './auth.resolver';
import { AuthService } from './services/auth.service';
import { JwtAuthStrategy } from './strategies/jwt.auth.strategy';
-const jwtModule = JwtModule.registerAsync({
- useFactory: async (environmentService: EnvironmentService) => {
- return {
- secret: environmentService.get('ACCESS_TOKEN_SECRET'),
- signOptions: {
- expiresIn: environmentService.get('ACCESS_TOKEN_EXPIRES_IN'),
- },
- };
- },
- inject: [EnvironmentService],
-});
@Module({
imports: [
- jwtModule,
+ JwtModule,
FileUploadModule,
DataSourceModule,
forwardRef(() => UserModule),
@@ -89,6 +76,6 @@ const jwtModule = JwtModule.registerAsync({
GoogleAPIsService,
AppTokenService,
],
- exports: [jwtModule, TokenService],
+ exports: [TokenService],
})
export class AuthModule {}
diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/token.service.spec.ts b/packages/twenty-server/src/engine/core-modules/auth/services/token.service.spec.ts
index 1e5d63996..28122edc0 100644
--- a/packages/twenty-server/src/engine/core-modules/auth/services/token.service.spec.ts
+++ b/packages/twenty-server/src/engine/core-modules/auth/services/token.service.spec.ts
@@ -1,25 +1,25 @@
-import { Test, TestingModule } from '@nestjs/testing';
-import { JwtService } from '@nestjs/jwt';
-import { getRepositoryToken } from '@nestjs/typeorm';
import {
BadRequestException,
InternalServerErrorException,
NotFoundException,
} from '@nestjs/common';
+import { JwtService } from '@nestjs/jwt';
+import { Test, TestingModule } from '@nestjs/testing';
+import { getRepositoryToken } from '@nestjs/typeorm';
import crypto from 'crypto';
import { IsNull, MoreThan, Repository } from 'typeorm';
-import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
import {
AppToken,
AppTokenType,
} from 'src/engine/core-modules/app-token/app-token.entity';
-import { User } from 'src/engine/core-modules/user/user.entity';
import { JwtAuthStrategy } from 'src/engine/core-modules/auth/strategies/jwt.auth.strategy';
-import { EmailService } from 'src/engine/integrations/email/email.service';
+import { User } from 'src/engine/core-modules/user/user.entity';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
+import { EmailService } from 'src/engine/integrations/email/email.service';
+import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
import { TokenService } from './token.service';
diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/token.service.ts b/packages/twenty-server/src/engine/core-modules/auth/services/token.service.ts
index 0216f9686..88cb7e088 100644
--- a/packages/twenty-server/src/engine/core-modules/auth/services/token.service.ts
+++ b/packages/twenty-server/src/engine/core-modules/auth/services/token.service.ts
@@ -7,50 +7,50 @@ import {
UnauthorizedException,
UnprocessableEntityException,
} from '@nestjs/common';
-import { JwtService } from '@nestjs/jwt';
import { InjectRepository } from '@nestjs/typeorm';
import crypto from 'crypto';
-import { addMilliseconds, differenceInMilliseconds } from 'date-fns';
-import ms from 'ms';
-import { JsonWebTokenError, TokenExpiredError } from 'jsonwebtoken';
-import { IsNull, MoreThan, Repository } from 'typeorm';
-import { Request } from 'express';
-import { ExtractJwt } from 'passport-jwt';
import { render } from '@react-email/render';
+import { addMilliseconds, differenceInMilliseconds } from 'date-fns';
+import { Request } from 'express';
+import { JsonWebTokenError, TokenExpiredError } from 'jsonwebtoken';
+import ms from 'ms';
+import { ExtractJwt } from 'passport-jwt';
import { PasswordResetLinkEmail } from 'twenty-emails';
+import { IsNull, MoreThan, Repository } from 'typeorm';
import {
- JwtAuthStrategy,
- JwtPayload,
-} from 'src/engine/core-modules/auth/strategies/jwt.auth.strategy';
-import { assert } from 'src/utils/assert';
+ AppToken,
+ AppTokenType,
+} from 'src/engine/core-modules/app-token/app-token.entity';
+import { EmailPasswordResetLink } from 'src/engine/core-modules/auth/dto/email-password-reset-link.entity';
+import { ExchangeAuthCode } from 'src/engine/core-modules/auth/dto/exchange-auth-code.entity';
+import { ExchangeAuthCodeInput } from 'src/engine/core-modules/auth/dto/exchange-auth-code.input';
+import { InvalidatePassword } from 'src/engine/core-modules/auth/dto/invalidate-password.entity';
import {
ApiKeyToken,
AuthToken,
AuthTokens,
PasswordResetToken,
} from 'src/engine/core-modules/auth/dto/token.entity';
-import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
-import { User } from 'src/engine/core-modules/user/user.entity';
-import {
- AppToken,
- AppTokenType,
-} from 'src/engine/core-modules/app-token/app-token.entity';
import { ValidatePasswordResetToken } from 'src/engine/core-modules/auth/dto/validate-password-reset-token.entity';
-import { EmailService } from 'src/engine/integrations/email/email.service';
-import { InvalidatePassword } from 'src/engine/core-modules/auth/dto/invalidate-password.entity';
-import { EmailPasswordResetLink } from 'src/engine/core-modules/auth/dto/email-password-reset-link.entity';
+import {
+ JwtAuthStrategy,
+ JwtPayload,
+} from 'src/engine/core-modules/auth/strategies/jwt.auth.strategy';
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
+import { JwtWrapperService } from 'src/engine/core-modules/jwt/services/jwt-wrapper.service';
+import { User } from 'src/engine/core-modules/user/user.entity';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
-import { ExchangeAuthCodeInput } from 'src/engine/core-modules/auth/dto/exchange-auth-code.input';
-import { ExchangeAuthCode } from 'src/engine/core-modules/auth/dto/exchange-auth-code.entity';
+import { EmailService } from 'src/engine/integrations/email/email.service';
+import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
+import { assert } from 'src/utils/assert';
@Injectable()
export class TokenService {
constructor(
- private readonly jwtService: JwtService,
+ private readonly jwtWrapperService: JwtWrapperService,
private readonly jwtStrategy: JwtAuthStrategy,
private readonly environmentService: EnvironmentService,
@InjectRepository(User, 'core')
@@ -90,7 +90,7 @@ export class TokenService {
};
return {
- token: this.jwtService.sign(jwtPayload),
+ token: this.jwtWrapperService.sign(jwtPayload),
expiresAt,
};
}
@@ -116,7 +116,7 @@ export class TokenService {
await this.appTokenRepository.save(refreshToken);
return {
- token: this.jwtService.sign(jwtPayload, {
+ token: this.jwtWrapperService.sign(jwtPayload, {
secret,
expiresIn,
// Jwtid will be used to link RefreshToken entity to this token
@@ -137,7 +137,7 @@ export class TokenService {
};
return {
- token: this.jwtService.sign(jwtPayload, {
+ token: this.jwtWrapperService.sign(jwtPayload, {
secret,
expiresIn,
}),
@@ -164,7 +164,7 @@ export class TokenService {
};
return {
- token: this.jwtService.sign(jwtPayload, {
+ token: this.jwtWrapperService.sign(jwtPayload, {
secret,
expiresIn,
}),
@@ -193,7 +193,7 @@ export class TokenService {
} else {
expiresIn = this.environmentService.get('API_TOKEN_EXPIRES_IN');
}
- const token = this.jwtService.sign(jwtPayload, {
+ const token = this.jwtWrapperService.sign(jwtPayload, {
secret,
expiresIn,
jwtid: apiKeyId,
@@ -496,7 +496,10 @@ export class TokenService {
async verifyJwt(token: string, secret?: string) {
try {
- return this.jwtService.verify(token, secret ? { secret } : undefined);
+ return this.jwtWrapperService.verify(
+ token,
+ secret ? { secret } : undefined,
+ );
} catch (error) {
if (error instanceof TokenExpiredError) {
throw new UnauthorizedException('Token has expired.');
@@ -668,12 +671,4 @@ export class TokenService {
return { success: true };
}
-
- async encodePayload(payload: any, options?: any): Promise {
- return this.jwtService.sign(payload, options);
- }
-
- async decodePayload(payload: any, options?: any): Promise {
- return this.jwtService.decode(payload, options);
- }
}
diff --git a/packages/twenty-server/src/engine/core-modules/file/file.module.ts b/packages/twenty-server/src/engine/core-modules/file/file.module.ts
index 0169906f0..dc9de78fd 100644
--- a/packages/twenty-server/src/engine/core-modules/file/file.module.ts
+++ b/packages/twenty-server/src/engine/core-modules/file/file.module.ts
@@ -1,15 +1,15 @@
-import { forwardRef, Module } from '@nestjs/common';
+import { Module } from '@nestjs/common';
-import { AuthModule } from 'src/engine/core-modules/auth/auth.module';
import { FileUploadModule } from 'src/engine/core-modules/file/file-upload/file-upload.module';
import { FilePathGuard } from 'src/engine/core-modules/file/guards/file-path-guard';
+import { JwtModule } from 'src/engine/core-modules/jwt/jwt.module';
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
import { FileController } from './controllers/file.controller';
import { FileService } from './services/file.service';
@Module({
- imports: [FileUploadModule, forwardRef(() => AuthModule)],
+ imports: [FileUploadModule, JwtModule],
providers: [FileService, EnvironmentService, FilePathGuard],
exports: [FileService],
controllers: [FileController],
diff --git a/packages/twenty-server/src/engine/core-modules/file/guards/file-path-guard.ts b/packages/twenty-server/src/engine/core-modules/file/guards/file-path-guard.ts
index 082d6383c..db9f466c5 100644
--- a/packages/twenty-server/src/engine/core-modules/file/guards/file-path-guard.ts
+++ b/packages/twenty-server/src/engine/core-modules/file/guards/file-path-guard.ts
@@ -6,13 +6,13 @@ import {
Injectable,
} from '@nestjs/common';
-import { TokenService } from 'src/engine/core-modules/auth/services/token.service';
+import { JwtWrapperService } from 'src/engine/core-modules/jwt/services/jwt-wrapper.service';
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
@Injectable()
export class FilePathGuard implements CanActivate {
constructor(
- private readonly tokenService: TokenService,
+ private readonly jwtWrapperService: JwtWrapperService,
private readonly environmentService: EnvironmentService,
) {}
@@ -22,11 +22,11 @@ export class FilePathGuard implements CanActivate {
if (query && query['token']) {
const payloadToDecode = query['token'];
- const decodedPayload = await this.tokenService.decodePayload(
+ const decodedPayload = await this.jwtWrapperService.decode(
payloadToDecode,
{
secret: this.environmentService.get('FILE_TOKEN_SECRET'),
- },
+ } as any,
);
const expirationDate = decodedPayload?.['expiration_date'];
diff --git a/packages/twenty-server/src/engine/core-modules/file/services/file.service.ts b/packages/twenty-server/src/engine/core-modules/file/services/file.service.ts
index dcce3f7d4..7b71fc59b 100644
--- a/packages/twenty-server/src/engine/core-modules/file/services/file.service.ts
+++ b/packages/twenty-server/src/engine/core-modules/file/services/file.service.ts
@@ -10,16 +10,16 @@ import {
FileStorageExceptionCode,
} from 'src/engine/integrations/file-storage/interfaces/file-storage-exception';
-import { TokenService } from 'src/engine/core-modules/auth/services/token.service';
+import { JwtWrapperService } from 'src/engine/core-modules/jwt/services/jwt-wrapper.service';
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
import { FileStorageService } from 'src/engine/integrations/file-storage/file-storage.service';
@Injectable()
export class FileService {
constructor(
+ private readonly jwtWrapperService: JwtWrapperService,
private readonly fileStorageService: FileStorageService,
private readonly environmentService: EnvironmentService,
- private readonly tokenService: TokenService,
) {}
async getFileStream(
@@ -57,7 +57,7 @@ export class FileService {
const expirationDate = addMilliseconds(new Date(), ms(fileTokenExpiresIn));
- const signedPayload = await this.tokenService.encodePayload(
+ const signedPayload = await this.jwtWrapperService.sign(
{
expiration_date: expirationDate,
...payloadToEncode,
diff --git a/packages/twenty-server/src/engine/core-modules/jwt/jwt.module.ts b/packages/twenty-server/src/engine/core-modules/jwt/jwt.module.ts
new file mode 100644
index 000000000..6acbd76a0
--- /dev/null
+++ b/packages/twenty-server/src/engine/core-modules/jwt/jwt.module.ts
@@ -0,0 +1,27 @@
+/* eslint-disable no-restricted-imports */
+import { Module } from '@nestjs/common';
+import { JwtModule as NestJwtModule } from '@nestjs/jwt';
+
+import { JwtWrapperService } from 'src/engine/core-modules/jwt/services/jwt-wrapper.service';
+import { EnvironmentModule } from 'src/engine/integrations/environment/environment.module';
+import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
+
+const InternalJwtModule = NestJwtModule.registerAsync({
+ useFactory: async (environmentService: EnvironmentService) => {
+ return {
+ secret: environmentService.get('ACCESS_TOKEN_SECRET'),
+ signOptions: {
+ expiresIn: environmentService.get('ACCESS_TOKEN_EXPIRES_IN'),
+ },
+ };
+ },
+ inject: [EnvironmentService],
+});
+
+@Module({
+ imports: [InternalJwtModule, EnvironmentModule],
+ controllers: [],
+ providers: [JwtWrapperService],
+ exports: [JwtWrapperService],
+})
+export class JwtModule {}
diff --git a/packages/twenty-server/src/engine/core-modules/jwt/services/jwt-wrapper.service.ts b/packages/twenty-server/src/engine/core-modules/jwt/services/jwt-wrapper.service.ts
new file mode 100644
index 000000000..43a4ea2cb
--- /dev/null
+++ b/packages/twenty-server/src/engine/core-modules/jwt/services/jwt-wrapper.service.ts
@@ -0,0 +1,21 @@
+import { Injectable } from '@nestjs/common';
+import { JwtService, JwtSignOptions, JwtVerifyOptions } from '@nestjs/jwt';
+
+import * as jwt from 'jsonwebtoken';
+
+@Injectable()
+export class JwtWrapperService {
+ constructor(private readonly jwtService: JwtService) {}
+
+ sign(payload: string, options?: JwtSignOptions): string {
+ return this.jwtService.sign(payload, options);
+ }
+
+ verify(token: string, options?: JwtVerifyOptions): T {
+ return this.jwtService.verify(token, options);
+ }
+
+ decode(payload: string, options: jwt.DecodeOptions): T {
+ return this.jwtService.decode(payload, options);
+ }
+}
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 4a95a9588..a311201fd 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
@@ -70,20 +70,6 @@ export class UserResolver {
assert(user, 'User not found');
- user.workspaces = await Promise.all(
- user.workspaces.map(async (userWorkspace) => {
- if (userWorkspace.workspace.logo) {
- const workspaceLogoToken = await this.fileService.encodeFileToken({
- workspace_id: userWorkspace.workspace.id,
- });
-
- userWorkspace.workspace.logo = `${userWorkspace.workspace.logo}?token=${workspaceLogoToken}`;
- }
-
- return userWorkspace;
- }),
- );
-
return user;
}
@@ -186,7 +172,11 @@ export class UserResolver {
workspaceId,
});
- return paths[0];
+ const fileToken = await this.fileService.encodeFileToken({
+ workspace_id: workspaceId,
+ });
+
+ return `${paths[0]}?token=${fileToken}`;
}
@UseGuards(DemoEnvGuard)
diff --git a/packages/twenty-server/src/engine/core-modules/workspace/workspace.module.ts b/packages/twenty-server/src/engine/core-modules/workspace/workspace.module.ts
index 213427eaf..a8255d899 100644
--- a/packages/twenty-server/src/engine/core-modules/workspace/workspace.module.ts
+++ b/packages/twenty-server/src/engine/core-modules/workspace/workspace.module.ts
@@ -7,6 +7,7 @@ import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
import { BillingModule } from 'src/engine/core-modules/billing/billing.module';
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
import { FileUploadModule } from 'src/engine/core-modules/file/file-upload/file-upload.module';
+import { FileModule } from 'src/engine/core-modules/file/file.module';
import { OnboardingModule } from 'src/engine/core-modules/onboarding/onboarding.module';
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
import { UserWorkspaceModule } from 'src/engine/core-modules/user-workspace/user-workspace.module';
@@ -17,7 +18,6 @@ import { WorkspaceResolver } from 'src/engine/core-modules/workspace/workspace.r
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
import { WorkspaceCacheVersionModule } from 'src/engine/metadata-modules/workspace-cache-version/workspace-cache-version.module';
import { WorkspaceManagerModule } from 'src/engine/workspace-manager/workspace-manager.module';
-import { UserVarsModule } from 'src/engine/core-modules/user/user-vars/user-vars.module';
import { workspaceAutoResolverOpts } from './workspace.auto-resolver-opts';
import { Workspace } from './workspace.entity';
@@ -30,6 +30,7 @@ import { WorkspaceService } from './services/workspace.service';
NestjsQueryGraphQLModule.forFeature({
imports: [
BillingModule,
+ FileModule,
FileUploadModule,
WorkspaceCacheVersionModule,
NestjsQueryTypeOrmModule.forFeature(
diff --git a/packages/twenty-server/src/engine/core-modules/workspace/workspace.resolver.ts b/packages/twenty-server/src/engine/core-modules/workspace/workspace.resolver.ts
index 3722d5457..cc390dfa9 100644
--- a/packages/twenty-server/src/engine/core-modules/workspace/workspace.resolver.ts
+++ b/packages/twenty-server/src/engine/core-modules/workspace/workspace.resolver.ts
@@ -15,6 +15,7 @@ import { FileFolder } from 'src/engine/core-modules/file/interfaces/file-folder.
import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity';
import { BillingSubscriptionService } from 'src/engine/core-modules/billing/services/billing-subscription.service';
import { FileUploadService } from 'src/engine/core-modules/file/file-upload/services/file-upload.service';
+import { FileService } from 'src/engine/core-modules/file/services/file.service';
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
import { User } from 'src/engine/core-modules/user/user.entity';
import { ActivateWorkspaceInput } from 'src/engine/core-modules/workspace/dtos/activate-workspace-input';
@@ -41,6 +42,7 @@ export class WorkspaceResolver {
private readonly workspaceCacheVersionService: WorkspaceCacheVersionService,
private readonly userWorkspaceService: UserWorkspaceService,
private readonly fileUploadService: FileUploadService,
+ private readonly fileService: FileService,
private readonly billingSubscriptionService: BillingSubscriptionService,
) {}
@@ -92,7 +94,11 @@ export class WorkspaceResolver {
logo: paths[0],
});
- return paths[0];
+ const workspaceLogoToken = await this.fileService.encodeFileToken({
+ workspace_id: id,
+ });
+
+ return `${paths[0]}?token=${workspaceLogoToken}`;
}
@UseGuards(DemoEnvGuard)
@@ -124,6 +130,19 @@ export class WorkspaceResolver {
return await this.userWorkspaceService.getUserCount(workspace.id);
}
+ @ResolveField(() => String)
+ async logo(@Parent() workspace: Workspace): Promise {
+ if (workspace.logo) {
+ const workspaceLogoToken = await this.fileService.encodeFileToken({
+ workspace_id: workspace.id,
+ });
+
+ return `${workspace.logo}?token=${workspaceLogoToken}`;
+ }
+
+ return workspace.logo ?? '';
+ }
+
@Mutation(() => SendInviteLink)
async sendInviteLink(
@Args() sendInviteLinkInput: SendInviteLinkInput,
diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-object-metadata-identifiers.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-object-metadata-identifiers.service.ts
index 54c781fcb..159db8ec9 100644
--- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-object-metadata-identifiers.service.ts
+++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-object-metadata-identifiers.service.ts
@@ -114,6 +114,8 @@ export class WorkspaceSyncObjectMetadataIdentifiersService {
...objectMetadata,
labelIdentifierFieldMetadataId:
labelIdentifierFieldMetadata?.id ?? null,
+ imageIdentifierFieldMetadataId:
+ imageIdentifierFieldMetadata?.id ?? null,
});
}
}
@@ -161,9 +163,12 @@ export class WorkspaceSyncObjectMetadataIdentifiersService {
);
}
- if (imageIdentifierFieldMetadata) {
+ if (
+ imageIdentifierFieldMetadata &&
+ imageIdentifierFieldMetadata.type !== FieldMetadataType.TEXT
+ ) {
throw new Error(
- `Image identifier field for object ${objectMetadata.nameSingular} are not supported yet.`,
+ `Image identifier field for object ${objectMetadata.nameSingular} has invalid type ${imageIdentifierFieldMetadata.type}`,
);
}
}
diff --git a/packages/twenty-server/src/modules/person/standard-objects/person.workspace-entity.ts b/packages/twenty-server/src/modules/person/standard-objects/person.workspace-entity.ts
index 891b453ac..46300a145 100644
--- a/packages/twenty-server/src/modules/person/standard-objects/person.workspace-entity.ts
+++ b/packages/twenty-server/src/modules/person/standard-objects/person.workspace-entity.ts
@@ -39,6 +39,7 @@ import { TimelineActivityWorkspaceEntity } from 'src/modules/timeline/standard-o
description: 'A person',
icon: 'IconUser',
labelIdentifierStandardId: PERSON_STANDARD_FIELD_IDS.name,
+ imageIdentifierStandardId: PERSON_STANDARD_FIELD_IDS.avatarUrl,
})
export class PersonWorkspaceEntity extends BaseWorkspaceEntity {
@WorkspaceField({