File - Migrate avatarUrl > avatarFile on person (data migration + logic) + Attachment data migration (#17752)

- Migration command
    - Check IS_FILES_FIELD_MIGRATED:false
    - Check or create avatarFile field
    - Fetch all people with avatarUrl
           - Move (Copy/move) file in storage
           - Create core.file record
           - Update person record
           
    - bonus : attachment migration  : fullPath > file (same logic)
   
- BE logic
    - Add avatarFile field on person

- FE logic 
   - Adapt logic to upload on/display avatarFile data

The whole imageIdentifier logic will be done later
This commit is contained in:
Etienne
2026-02-11 16:07:05 +01:00
committed by GitHub
parent 5be64bf4be
commit d0c1841f0f
46 changed files with 1704 additions and 737 deletions
@@ -3,19 +3,21 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { getCompanyDomainName } from '@/object-metadata/utils/getCompanyDomainName';
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
import { getImageIdentifierFieldValue } from './getImageIdentifierFieldValue';
import { isNonEmptyString } from '@sniptt/guards';
import {
getImageAbsoluteURI,
getLogoUrlFromDomainName,
isDefined,
} from 'twenty-shared/utils';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
import { getImageIdentifierFieldValue } from './getImageIdentifierFieldValue';
export const getAvatarUrl = (
objectNameSingular: string,
record: ObjectRecord,
imageIdentifierFieldMetadataItem: FieldMetadataItem | undefined,
allowRequestsToTwentyIcons?: boolean | undefined,
isFilesFieldMigrated?: boolean | undefined,
) => {
if (objectNameSingular === CoreObjectNameSingular.WorkspaceMember) {
return record.avatarUrl ?? undefined;
@@ -31,7 +33,11 @@ export const getAvatarUrl = (
}
if (objectNameSingular === CoreObjectNameSingular.Person) {
return isDefined(record.avatarUrl)
if (isFilesFieldMigrated === true) {
return record.avatarFile?.[0]?.url ?? '';
}
return isNonEmptyString(record.avatarUrl)
? getImageAbsoluteURI({
imageUrl: record.avatarUrl,
baseUrl: REACT_APP_SERVER_BASE_URL,
@@ -7,10 +7,12 @@ export const getImageIdentifierFieldMetadataItem = (
ObjectMetadataItem,
'fields' | 'imageIdentifierFieldMetadataId' | 'nameSingular'
>,
isFilesFieldMigrated?: boolean,
): FieldMetadataItem | undefined =>
objectMetadataItem.fields.find((fieldMetadataItem) =>
isImageIdentifierField({
fieldMetadataItem,
objectMetadataItem,
isFilesFieldMigrated,
}),
);
@@ -12,6 +12,7 @@ export const getObjectRecordIdentifier = ({
objectMetadataItem,
record,
allowRequestsToTwentyIcons,
isFilesFieldMigrated,
}: {
objectMetadataItem: Pick<
ObjectMetadataItem,
@@ -22,6 +23,7 @@ export const getObjectRecordIdentifier = ({
>;
record: ObjectRecord;
allowRequestsToTwentyIcons: boolean;
isFilesFieldMigrated?: boolean;
}): ObjectRecordIdentifier => {
const labelIdentifierFieldMetadataItem =
getLabelIdentifierFieldMetadataItem(objectMetadataItem);
@@ -43,6 +45,7 @@ export const getObjectRecordIdentifier = ({
record,
imageIdentifierFieldMetadata,
allowRequestsToTwentyIcons,
isFilesFieldMigrated,
);
const linkToShowPage = getLinkToShowPage(
@@ -5,12 +5,14 @@ import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataI
export const isImageIdentifierField = ({
fieldMetadataItem,
objectMetadataItem,
isFilesFieldMigrated,
}: {
fieldMetadataItem: Pick<FieldMetadataItem, 'id' | 'name'>;
objectMetadataItem: Pick<
ObjectMetadataItem,
'imageIdentifierFieldMetadataId' | 'nameSingular'
>;
isFilesFieldMigrated?: boolean;
}) => {
if (
objectMetadataItem.nameSingular === CoreObjectNameSingular.Company &&
@@ -19,6 +21,13 @@ export const isImageIdentifierField = ({
return true;
}
if (objectMetadataItem.nameSingular === CoreObjectNameSingular.Person) {
if (isFilesFieldMigrated === true) {
return fieldMetadataItem.name === 'avatarFile';
}
return fieldMetadataItem.name === 'avatarUrl';
}
return (
fieldMetadataItem.id === objectMetadataItem.imageIdentifierFieldMetadataId
);