Add automatic company logo fetching during workspace creation (#9158)
Closes #9151 ## Description This PR automatically sets a workspace's logo based on the user's work email domain during signup. When a user creates a new workspace using their work email (e.g., @airbnb.com), the system will fetch and set their company logo from twenty-icons.com as the default workspace logo. ## Implementation Details - Added a new `CompanyEnrichmentService` to handle company-related data enrichment - Created a modular architecture that supports future enrichment features (e.g., company name, details) - Integrated with existing work email detection - Maintains user ability to override the logo later ## Testing https://github.com/user-attachments/assets/f7855c99-462a-4053-9e52-29649e954275 I tested the following scenarios: - Signing up with a work email (e.g., @company.com) → Logo is automatically set - Signing up with a personal email (e.g., @gmail.com) → No logo is set - User can still upload a custom logo after automatic setting ## Technical Notes - Uses existing `isWorkEmail` utility - Structured for future extensibility (additional company data enrichment) - No breaking changes to existing functionality --------- Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
4
.vscode/twenty.code-workspace
vendored
4
.vscode/twenty.code-workspace
vendored
@ -24,6 +24,10 @@
|
|||||||
"name": "packages/twenty-emails",
|
"name": "packages/twenty-emails",
|
||||||
"path": "../packages/twenty-emails"
|
"path": "../packages/twenty-emails"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "packages/twenty-shared",
|
||||||
|
"path": "../packages/twenty-shared"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "packages/twenty-server",
|
"name": "packages/twenty-server",
|
||||||
"path": "../packages/twenty-server"
|
"path": "../packages/twenty-server"
|
||||||
|
|||||||
@ -21,6 +21,7 @@ const jestConfig: JestConfigWithTsJest = {
|
|||||||
}),
|
}),
|
||||||
'^test/(.*)$': '<rootDir>/test/$1',
|
'^test/(.*)$': '<rootDir>/test/$1',
|
||||||
'twenty-emails': '<rootDir>/../twenty-emails/dist/index.js',
|
'twenty-emails': '<rootDir>/../twenty-emails/dist/index.js',
|
||||||
|
'twenty-shared': '<rootDir>/../twenty-shared/dist/index.js',
|
||||||
},
|
},
|
||||||
fakeTimers: {
|
fakeTimers: {
|
||||||
enableGlobally: true,
|
enableGlobally: true,
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { InjectRepository } from '@nestjs/typeorm';
|
|||||||
|
|
||||||
import { isDefined } from 'class-validator';
|
import { isDefined } from 'class-validator';
|
||||||
import FileType from 'file-type';
|
import FileType from 'file-type';
|
||||||
|
import { TWENTY_ICONS_BASE_URL } from 'twenty-shared';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
@ -23,17 +24,19 @@ import { EnvironmentService } from 'src/engine/core-modules/environment/environm
|
|||||||
import { FileUploadService } from 'src/engine/core-modules/file/file-upload/services/file-upload.service';
|
import { FileUploadService } from 'src/engine/core-modules/file/file-upload/services/file-upload.service';
|
||||||
import { OnboardingService } from 'src/engine/core-modules/onboarding/onboarding.service';
|
import { OnboardingService } from 'src/engine/core-modules/onboarding/onboarding.service';
|
||||||
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
|
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
|
||||||
|
import { UserService } from 'src/engine/core-modules/user/services/user.service';
|
||||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||||
import { userValidator } from 'src/engine/core-modules/user/user.validate';
|
import { userValidator } from 'src/engine/core-modules/user/user.validate';
|
||||||
import { WorkspaceInvitationService } from 'src/engine/core-modules/workspace-invitation/services/workspace-invitation.service';
|
import { WorkspaceInvitationService } from 'src/engine/core-modules/workspace-invitation/services/workspace-invitation.service';
|
||||||
|
import { WorkspaceAuthProvider } from 'src/engine/core-modules/workspace/types/workspace.type';
|
||||||
import {
|
import {
|
||||||
Workspace,
|
Workspace,
|
||||||
WorkspaceActivationStatus,
|
WorkspaceActivationStatus,
|
||||||
} from 'src/engine/core-modules/workspace/workspace.entity';
|
} from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate';
|
import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate';
|
||||||
|
import { getDomainNameByEmail } from 'src/utils/get-domain-name-by-email';
|
||||||
import { getImageBufferFromUrl } from 'src/utils/image';
|
import { getImageBufferFromUrl } from 'src/utils/image';
|
||||||
import { WorkspaceAuthProvider } from 'src/engine/core-modules/workspace/types/workspace.type';
|
import { isWorkEmail } from 'src/utils/is-work-email';
|
||||||
import { UserService } from 'src/engine/core-modules/user/services/user.service';
|
|
||||||
|
|
||||||
export type SignInUpServiceInput = {
|
export type SignInUpServiceInput = {
|
||||||
email: string;
|
email: string;
|
||||||
@ -333,12 +336,17 @@ export class SignInUpService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const logo = isWorkEmail(email)
|
||||||
|
? `${TWENTY_ICONS_BASE_URL}/${getDomainNameByEmail(email)}`
|
||||||
|
: undefined;
|
||||||
|
|
||||||
const workspaceToCreate = this.workspaceRepository.create({
|
const workspaceToCreate = this.workspaceRepository.create({
|
||||||
subdomain: await this.domainManagerService.generateSubdomain(),
|
subdomain: await this.domainManagerService.generateSubdomain(),
|
||||||
displayName: '',
|
displayName: '',
|
||||||
domainName: '',
|
domainName: '',
|
||||||
inviteHash: v4(),
|
inviteHash: v4(),
|
||||||
activationStatus: WorkspaceActivationStatus.PENDING_CREATION,
|
activationStatus: WorkspaceActivationStatus.PENDING_CREATION,
|
||||||
|
logo,
|
||||||
});
|
});
|
||||||
|
|
||||||
const workspace = await this.workspaceRepository.save(workspaceToCreate);
|
const workspace = await this.workspaceRepository.save(workspaceToCreate);
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common';
|
|||||||
|
|
||||||
import axios, { AxiosInstance } from 'axios';
|
import axios, { AxiosInstance } from 'axios';
|
||||||
import uniqBy from 'lodash.uniqby';
|
import uniqBy from 'lodash.uniqby';
|
||||||
|
import { TWENTY_COMPANIES_BASE_URL } from 'twenty-shared';
|
||||||
import { DeepPartial, EntityManager, ILike } from 'typeorm';
|
import { DeepPartial, EntityManager, ILike } from 'typeorm';
|
||||||
|
|
||||||
import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type';
|
import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type';
|
||||||
@ -25,7 +26,7 @@ export class CreateCompanyService {
|
|||||||
|
|
||||||
constructor(private readonly twentyORMGlobalManager: TwentyORMGlobalManager) {
|
constructor(private readonly twentyORMGlobalManager: TwentyORMGlobalManager) {
|
||||||
this.httpService = axios.create({
|
this.httpService = axios.create({
|
||||||
baseURL: 'https://twenty-companies.com',
|
baseURL: TWENTY_COMPANIES_BASE_URL,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -27,7 +27,8 @@
|
|||||||
"paths": {
|
"paths": {
|
||||||
"src/*": ["packages/twenty-server/src/*"],
|
"src/*": ["packages/twenty-server/src/*"],
|
||||||
"test/*": ["packages/twenty-server/test/*"],
|
"test/*": ["packages/twenty-server/test/*"],
|
||||||
"twenty-emails": ["packages/twenty-emails/dist"]
|
"twenty-emails": ["packages/twenty-emails/dist"],
|
||||||
|
"twenty-shared": ["packages/twenty-shared/dist"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ts-node": {
|
"ts-node": {
|
||||||
|
|||||||
@ -0,0 +1 @@
|
|||||||
|
export const TWENTY_COMPANIES_BASE_URL = 'https://twenty-companies.com';
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export const TWENTY_ICONS_BASE_URL = 'https://twenty-icons.com';
|
||||||
@ -1 +1,3 @@
|
|||||||
|
export * from './constants/TwentyCompaniesBaseUrl';
|
||||||
|
export * from './constants/TwentyIconsBaseUrl';
|
||||||
export * from './utils/image/getImageAbsoluteURI';
|
export * from './utils/image/getImageAbsoluteURI';
|
||||||
|
|||||||
Reference in New Issue
Block a user