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 e7af3a44c..2de14a4ca 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 @@ -27,7 +27,7 @@ export class FileService { }); } - async encodeFileToken(payloadToEncode: Record) { + encodeFileToken(payloadToEncode: Record) { const fileTokenExpiresIn = this.twentyConfigService.get( 'FILE_TOKEN_EXPIRES_IN', ); diff --git a/packages/twenty-server/src/engine/core-modules/search/__tests__/search.service.spec.ts b/packages/twenty-server/src/engine/core-modules/search/__tests__/search.service.spec.ts index fe2b63db2..f2067bb75 100644 --- a/packages/twenty-server/src/engine/core-modules/search/__tests__/search.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/search/__tests__/search.service.spec.ts @@ -1,5 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; +import { FileService } from 'src/engine/core-modules/file/services/file.service'; import { mockObjectMetadataItemsWithFieldMaps } from 'src/engine/core-modules/search/__mocks__/mockObjectMetadataItemsWithFieldMaps'; import { SearchService } from 'src/engine/core-modules/search/services/search.service'; @@ -8,7 +9,7 @@ describe('SearchService', () => { beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - providers: [SearchService], + providers: [SearchService, { provide: FileService, useValue: {} }], }).compile(); service = module.get(SearchService); diff --git a/packages/twenty-server/src/engine/core-modules/search/search.module.ts b/packages/twenty-server/src/engine/core-modules/search/search.module.ts index 4557f2fc4..121951034 100644 --- a/packages/twenty-server/src/engine/core-modules/search/search.module.ts +++ b/packages/twenty-server/src/engine/core-modules/search/search.module.ts @@ -1,12 +1,13 @@ import { Module } from '@nestjs/common'; import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module'; +import { FileModule } from 'src/engine/core-modules/file/file.module'; import { SearchResolver } from 'src/engine/core-modules/search/search.resolver'; import { SearchService } from 'src/engine/core-modules/search/services/search.service'; import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module'; @Module({ - imports: [WorkspaceCacheStorageModule, FeatureFlagModule], + imports: [WorkspaceCacheStorageModule, FeatureFlagModule, FileModule], providers: [SearchResolver, SearchService], }) export class SearchModule {} diff --git a/packages/twenty-server/src/engine/core-modules/search/search.resolver.ts b/packages/twenty-server/src/engine/core-modules/search/search.resolver.ts index e138d51d5..29c65f060 100644 --- a/packages/twenty-server/src/engine/core-modules/search/search.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/search/search.resolver.ts @@ -118,6 +118,7 @@ export class SearchResolver { return this.searchService.computeSearchObjectResults( allRecordsWithObjectMetadataItems, limit, + workspace.id, ); } } diff --git a/packages/twenty-server/src/engine/core-modules/search/services/search.service.ts b/packages/twenty-server/src/engine/core-modules/search/services/search.service.ts index 3f6554108..0ebb5fcb2 100644 --- a/packages/twenty-server/src/engine/core-modules/search/services/search.service.ts +++ b/packages/twenty-server/src/engine/core-modules/search/services/search.service.ts @@ -1,13 +1,14 @@ import { Injectable } from '@nestjs/common'; -import { Brackets, ObjectLiteral } from 'typeorm'; import { FieldMetadataType } from 'twenty-shared/types'; import { getLogoUrlFromDomainName } from 'twenty-shared/utils'; +import { Brackets, ObjectLiteral } from 'typeorm'; import { ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface'; import { FeatureFlagMap } from 'src/engine/core-modules/feature-flag/interfaces/feature-flag-map.interface'; import { GraphqlQueryParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser'; +import { FileService } from 'src/engine/core-modules/file/services/file.service'; import { RESULTS_LIMIT_BY_OBJECT_WITHOUT_SEARCH_TERMS } from 'src/engine/core-modules/search/constants/results-limit-by-object-without-search-terms'; import { STANDARD_OBJECTS_BY_PRIORITY_RANK } from 'src/engine/core-modules/search/constants/standard-objects-by-priority-rank'; import { ObjectRecordFilterInput } from 'src/engine/core-modules/search/dtos/object-record-filter-input'; @@ -24,6 +25,8 @@ import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace. @Injectable() export class SearchService { + constructor(private readonly fileService: FileService) {} + filterObjectMetadataItems({ objectMetadataItemWithFieldMaps, includedObjectNameSingulars, @@ -194,9 +197,18 @@ export class SearchService { ].name; } + private getImageUrlWithToken(avatarUrl: string, workspaceId: string): string { + const avatarUrlToken = this.fileService.encodeFileToken({ + workspaceId, + }); + + return `${avatarUrl}?token=${avatarUrlToken}`; + } + getImageIdentifierValue( record: ObjectRecord, objectMetadataItem: ObjectMetadataItemWithFieldMaps, + workspaceId: string, ): string { const imageIdentifierField = this.getImageIdentifierColumn(objectMetadataItem); @@ -205,12 +217,15 @@ export class SearchService { return getLogoUrlFromDomainName(record.domainNamePrimaryLinkUrl) || ''; } - return imageIdentifierField ? record[imageIdentifierField] : ''; + return imageIdentifierField + ? this.getImageUrlWithToken(record[imageIdentifierField], workspaceId) + : ''; } computeSearchObjectResults( recordsWithObjectMetadataItems: RecordsWithObjectMetadataItem[], limit: number, + workspaceId: string, ) { const searchRecords = recordsWithObjectMetadataItems.flatMap( ({ objectMetadataItem, records }) => { @@ -219,7 +234,11 @@ export class SearchService { recordId: record.id, objectNameSingular: objectMetadataItem.nameSingular, label: this.getLabelIdentifierValue(record, objectMetadataItem), - imageUrl: this.getImageIdentifierValue(record, objectMetadataItem), + imageUrl: this.getImageIdentifierValue( + record, + objectMetadataItem, + workspaceId, + ), tsRankCD: record.tsRankCD, tsRank: record.tsRank, }; diff --git a/packages/twenty-server/src/modules/workspace-member/standard-objects/workspace-member.workspace-entity.ts b/packages/twenty-server/src/modules/workspace-member/standard-objects/workspace-member.workspace-entity.ts index 3a078b41a..6de04496c 100644 --- a/packages/twenty-server/src/modules/workspace-member/standard-objects/workspace-member.workspace-entity.ts +++ b/packages/twenty-server/src/modules/workspace-member/standard-objects/workspace-member.workspace-entity.ts @@ -80,6 +80,7 @@ export const SEARCH_FIELDS_FOR_WORKSPACE_MEMBER: FieldTypeAndNameMetadata[] = [ description: msg`A workspace member`, icon: STANDARD_OBJECT_ICONS.workspaceMember, labelIdentifierStandardId: WORKSPACE_MEMBER_STANDARD_FIELD_IDS.name, + imageIdentifierStandardId: WORKSPACE_MEMBER_STANDARD_FIELD_IDS.avatarUrl, }) @WorkspaceIsSystem() @WorkspaceIsNotAuditLogged()