feat: IMAP Driver Integration (#12576)
### Added IMAP integration This PR adds support for connecting email accounts via IMAP protocol, allowing users to sync their emails without OAuth. #### DB Changes: - Added customConnectionParams and connectionType fields to ConnectedAccountWorkspaceEntity #### UI: - Added settings pages for creating and editing IMAP connections with proper validation and connection testing. - Implemented reconnection flows for handling permission issues. #### Backend: - Built ImapConnectionModule with corresponding resolver and service for managing IMAP connections. - Created MessagingIMAPDriverModule to handle IMAP client operations, message fetching/parsing, and error handling. #### Dependencies: Integrated `imapflow` and `mailparser` libraries with their type definitions to handle the IMAP protocol communication. --------- Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Co-authored-by: Félix Malfait <felix.malfait@gmail.com> Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
@ -96,6 +96,7 @@ describe('ClientConfigController', () => {
|
||||
isGoogleMessagingEnabled: false,
|
||||
isGoogleCalendarEnabled: false,
|
||||
isConfigVariablesInDbEnabled: false,
|
||||
isIMAPMessagingEnabled: false,
|
||||
calendarBookingPageId: undefined,
|
||||
};
|
||||
|
||||
|
||||
@ -177,6 +177,9 @@ export class ClientConfig {
|
||||
@Field(() => Boolean)
|
||||
isConfigVariablesInDbEnabled: boolean;
|
||||
|
||||
@Field(() => Boolean)
|
||||
isIMAPMessagingEnabled: boolean;
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
calendarBookingPageId?: string;
|
||||
}
|
||||
|
||||
@ -137,6 +137,9 @@ export class ClientConfigService {
|
||||
isConfigVariablesInDbEnabled: this.twentyConfigService.get(
|
||||
'IS_CONFIG_VARIABLES_IN_DB_ENABLED',
|
||||
),
|
||||
isIMAPMessagingEnabled: this.twentyConfigService.get(
|
||||
'MESSAGING_PROVIDER_IMAP_ENABLED',
|
||||
),
|
||||
calendarBookingPageId: this.twentyConfigService.get(
|
||||
'CALENDAR_BOOKING_PAGE_ID',
|
||||
),
|
||||
|
||||
@ -23,6 +23,7 @@ import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-
|
||||
import { FileStorageModule } from 'src/engine/core-modules/file-storage/file-storage.module';
|
||||
import { FileStorageService } from 'src/engine/core-modules/file-storage/file-storage.service';
|
||||
import { HealthModule } from 'src/engine/core-modules/health/health.module';
|
||||
import { ImapSmtpCaldavModule } from 'src/engine/core-modules/imap-smtp-caldav-connection/imap-smtp-caldav-connection.module';
|
||||
import { LabModule } from 'src/engine/core-modules/lab/lab.module';
|
||||
import { LoggerModule } from 'src/engine/core-modules/logger/logger.module';
|
||||
import { loggerModuleFactory } from 'src/engine/core-modules/logger/logger.module-factory';
|
||||
@ -83,6 +84,7 @@ import { FileModule } from './file/file.module';
|
||||
RedisClientModule,
|
||||
WorkspaceQueryRunnerModule,
|
||||
SubscriptionsModule,
|
||||
ImapSmtpCaldavModule,
|
||||
FileStorageModule.forRoot(),
|
||||
LoggerModule.forRootAsync({
|
||||
useFactory: loggerModuleFactory,
|
||||
@ -125,6 +127,7 @@ import { FileModule } from './file/file.module';
|
||||
WorkspaceModule,
|
||||
WorkspaceInvitationModule,
|
||||
WorkspaceSSOModule,
|
||||
ImapSmtpCaldavModule,
|
||||
],
|
||||
})
|
||||
export class CoreEngineModule {}
|
||||
|
||||
@ -12,6 +12,15 @@ export type PublicFeatureFlag = {
|
||||
};
|
||||
|
||||
export const PUBLIC_FEATURE_FLAGS: PublicFeatureFlag[] = [
|
||||
{
|
||||
key: FeatureFlagKey.IS_IMAP_ENABLED,
|
||||
metadata: {
|
||||
label: 'IMAP',
|
||||
description:
|
||||
'Easily add email accounts from any provider that supports IMAP (and soon, send emails with SMTP)',
|
||||
imagePath: 'https://twenty.com/images/lab/is-imap-enabled.png',
|
||||
},
|
||||
},
|
||||
...(process.env.CLOUDFLARE_API_KEY
|
||||
? [
|
||||
// {
|
||||
|
||||
@ -5,4 +5,5 @@ export enum FeatureFlagKey {
|
||||
IS_UNIQUE_INDEXES_ENABLED = 'IS_UNIQUE_INDEXES_ENABLED',
|
||||
IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED',
|
||||
IS_AI_ENABLED = 'IS_AI_ENABLED',
|
||||
IS_IMAP_ENABLED = 'IS_IMAP_ENABLED',
|
||||
}
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import { ConnectedAccountProvider } from 'twenty-shared/types';
|
||||
|
||||
import { ImapSmtpCaldavConnectionParameters } from 'src/engine/core-modules/imap-smtp-caldav-connection/dtos/imap-smtp-caldav-connection.dto';
|
||||
|
||||
@ObjectType()
|
||||
export class ConnectedImapSmtpCaldavAccount {
|
||||
@Field(() => String)
|
||||
id: string;
|
||||
|
||||
@Field(() => String)
|
||||
handle: string;
|
||||
|
||||
@Field(() => String)
|
||||
provider: ConnectedAccountProvider;
|
||||
|
||||
@Field(() => String)
|
||||
accountOwnerId: string;
|
||||
|
||||
@Field(() => ImapSmtpCaldavConnectionParameters, { nullable: true })
|
||||
connectionParameters: ImapSmtpCaldavConnectionParameters | null;
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
@ObjectType()
|
||||
export class ImapSmtpCaldavConnectionSuccess {
|
||||
@Field(() => Boolean)
|
||||
success: boolean;
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
import { Field, InputType, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
@InputType()
|
||||
export class AccountType {
|
||||
@Field(() => String)
|
||||
type: 'IMAP' | 'SMTP' | 'CALDAV';
|
||||
}
|
||||
|
||||
@InputType()
|
||||
export class ConnectionParameters {
|
||||
@Field(() => String)
|
||||
host: string;
|
||||
|
||||
@Field(() => Number)
|
||||
port: number;
|
||||
|
||||
@Field(() => String)
|
||||
username: string;
|
||||
|
||||
/**
|
||||
* Note: This field is stored in plain text in the database.
|
||||
* While encrypting it could provide an extra layer of defense, we have decided not to,
|
||||
* as database access implies a broader compromise. For context, see discussion in PR #12576.
|
||||
*/
|
||||
@Field(() => String)
|
||||
password: string;
|
||||
|
||||
@Field(() => Boolean, { nullable: true })
|
||||
secure?: boolean;
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class ConnectionParametersOutput {
|
||||
@Field(() => String)
|
||||
host: string;
|
||||
|
||||
@Field(() => Number)
|
||||
port: number;
|
||||
|
||||
@Field(() => String)
|
||||
username: string;
|
||||
|
||||
@Field(() => String)
|
||||
password: string;
|
||||
|
||||
@Field(() => Boolean, { nullable: true })
|
||||
secure?: boolean;
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class ImapSmtpCaldavConnectionParameters {
|
||||
@Field(() => ConnectionParametersOutput, { nullable: true })
|
||||
IMAP?: ConnectionParametersOutput;
|
||||
|
||||
@Field(() => ConnectionParametersOutput, { nullable: true })
|
||||
SMTP?: ConnectionParametersOutput;
|
||||
|
||||
@Field(() => ConnectionParametersOutput, { nullable: true })
|
||||
CALDAV?: ConnectionParametersOutput;
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { ImapSmtpCaldavValidatorModule } from 'src/engine/core-modules/imap-smtp-caldav-connection/services/imap-smtp-caldav-connection-validator.module';
|
||||
import { MessageQueueModule } from 'src/engine/core-modules/message-queue/message-queue.module';
|
||||
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
||||
import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module';
|
||||
import { ConnectedAccountModule } from 'src/modules/connected-account/connected-account.module';
|
||||
import { IMAPAPIsModule } from 'src/modules/connected-account/imap-api/imap-apis.module';
|
||||
import { MessagingIMAPDriverModule } from 'src/modules/messaging/message-import-manager/drivers/imap/messaging-imap-driver.module';
|
||||
import { MessagingImportManagerModule } from 'src/modules/messaging/message-import-manager/messaging-import-manager.module';
|
||||
|
||||
import { ImapSmtpCaldavResolver } from './imap-smtp-caldav-connection.resolver';
|
||||
|
||||
import { ImapSmtpCaldavService } from './services/imap-smtp-caldav-connection.service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConnectedAccountModule,
|
||||
MessagingIMAPDriverModule,
|
||||
IMAPAPIsModule,
|
||||
MessagingImportManagerModule,
|
||||
MessageQueueModule,
|
||||
TwentyORMModule,
|
||||
FeatureFlagModule,
|
||||
ImapSmtpCaldavValidatorModule,
|
||||
PermissionsModule,
|
||||
],
|
||||
providers: [ImapSmtpCaldavResolver, ImapSmtpCaldavService],
|
||||
exports: [ImapSmtpCaldavService],
|
||||
})
|
||||
export class ImapSmtpCaldavModule {}
|
||||
@ -0,0 +1,139 @@
|
||||
import { UseFilters, UseGuards, UsePipes } from '@nestjs/common';
|
||||
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
|
||||
|
||||
import { ConnectedAccountProvider } from 'twenty-shared/types';
|
||||
|
||||
import { AuthGraphqlApiExceptionFilter } from 'src/engine/core-modules/auth/filters/auth-graphql-api-exception.filter';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe';
|
||||
import { UserInputError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import { ConnectedImapSmtpCaldavAccount } from 'src/engine/core-modules/imap-smtp-caldav-connection/dtos/imap-smtp-caldav-connected-account.dto';
|
||||
import { ImapSmtpCaldavConnectionSuccess } from 'src/engine/core-modules/imap-smtp-caldav-connection/dtos/imap-smtp-caldav-connection-success.dto';
|
||||
import {
|
||||
AccountType,
|
||||
ConnectionParameters,
|
||||
} from 'src/engine/core-modules/imap-smtp-caldav-connection/dtos/imap-smtp-caldav-connection.dto';
|
||||
import { ImapSmtpCaldavValidatorService } from 'src/engine/core-modules/imap-smtp-caldav-connection/services/imap-smtp-caldav-connection-validator.service';
|
||||
import { ImapSmtpCaldavService } from 'src/engine/core-modules/imap-smtp-caldav-connection/services/imap-smtp-caldav-connection.service';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
||||
import { SettingsPermissionsGuard } from 'src/engine/guards/settings-permissions.guard';
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/constants/setting-permission-type.constants';
|
||||
import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { ImapSmtpCalDavAPIService } from 'src/modules/connected-account/services/imap-smtp-caldav-apis.service';
|
||||
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
|
||||
@Resolver()
|
||||
@UsePipes(ResolverValidationPipe)
|
||||
@UseFilters(AuthGraphqlApiExceptionFilter, PermissionsGraphqlApiExceptionFilter)
|
||||
@UseGuards(SettingsPermissionsGuard(SettingPermissionType.WORKSPACE))
|
||||
export class ImapSmtpCaldavResolver {
|
||||
constructor(
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly ImapSmtpCaldavConnectionService: ImapSmtpCaldavService,
|
||||
private readonly imapSmtpCaldavApisService: ImapSmtpCalDavAPIService,
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
private readonly mailConnectionValidatorService: ImapSmtpCaldavValidatorService,
|
||||
) {}
|
||||
|
||||
private async checkIfFeatureEnabled(
|
||||
workspaceId: string,
|
||||
accountType: AccountType,
|
||||
): Promise<void> {
|
||||
if (accountType.type === 'IMAP') {
|
||||
const isImapEnabled = await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IS_IMAP_ENABLED,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (!isImapEnabled) {
|
||||
throw new UserInputError(
|
||||
'IMAP feature is not enabled for this workspace',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (accountType.type === 'SMTP') {
|
||||
throw new UserInputError(
|
||||
'SMTP feature is not enabled for this workspace',
|
||||
);
|
||||
}
|
||||
|
||||
if (accountType.type === 'CALDAV') {
|
||||
throw new UserInputError(
|
||||
'CALDAV feature is not enabled for this workspace',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Query(() => ConnectedImapSmtpCaldavAccount)
|
||||
@UseGuards(WorkspaceAuthGuard)
|
||||
async getConnectedImapSmtpCaldavAccount(
|
||||
@Args('id') id: string,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
): Promise<ConnectedImapSmtpCaldavAccount> {
|
||||
const connectedAccountRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<ConnectedAccountWorkspaceEntity>(
|
||||
workspace.id,
|
||||
'connectedAccount',
|
||||
);
|
||||
|
||||
const connectedAccount = await connectedAccountRepository.findOne({
|
||||
where: { id, provider: ConnectedAccountProvider.IMAP_SMTP_CALDAV },
|
||||
});
|
||||
|
||||
if (!connectedAccount) {
|
||||
throw new UserInputError(
|
||||
`Connected mail account with ID ${id} not found`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
id: connectedAccount.id,
|
||||
handle: connectedAccount.handle,
|
||||
provider: connectedAccount.provider,
|
||||
connectionParameters: connectedAccount.connectionParameters,
|
||||
accountOwnerId: connectedAccount.accountOwnerId,
|
||||
};
|
||||
}
|
||||
|
||||
@Mutation(() => ImapSmtpCaldavConnectionSuccess)
|
||||
@UseGuards(WorkspaceAuthGuard)
|
||||
async saveImapSmtpCaldav(
|
||||
@Args('accountOwnerId') accountOwnerId: string,
|
||||
@Args('handle') handle: string,
|
||||
@Args('accountType') accountType: AccountType,
|
||||
@Args('connectionParameters')
|
||||
connectionParameters: ConnectionParameters,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
@Args('id', { nullable: true }) id?: string,
|
||||
): Promise<ImapSmtpCaldavConnectionSuccess> {
|
||||
await this.checkIfFeatureEnabled(workspace.id, accountType);
|
||||
|
||||
const validatedParams =
|
||||
this.mailConnectionValidatorService.validateProtocolConnectionParams(
|
||||
connectionParameters,
|
||||
);
|
||||
|
||||
await this.ImapSmtpCaldavConnectionService.testImapSmtpCaldav(
|
||||
validatedParams,
|
||||
accountType.type,
|
||||
);
|
||||
|
||||
await this.imapSmtpCaldavApisService.setupConnectedAccount({
|
||||
handle,
|
||||
workspaceMemberId: accountOwnerId,
|
||||
workspaceId: workspace.id,
|
||||
connectionParams: validatedParams,
|
||||
accountType: accountType.type,
|
||||
connectedAccountId: id,
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { ImapSmtpCaldavValidatorService } from './imap-smtp-caldav-connection-validator.service';
|
||||
|
||||
@Module({
|
||||
providers: [ImapSmtpCaldavValidatorService],
|
||||
exports: [ImapSmtpCaldavValidatorService],
|
||||
})
|
||||
export class ImapSmtpCaldavValidatorModule {}
|
||||
@ -0,0 +1,41 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
import { UserInputError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import { ConnectionParameters } from 'src/engine/core-modules/imap-smtp-caldav-connection/types/imap-smtp-caldav-connection.type';
|
||||
|
||||
@Injectable()
|
||||
export class ImapSmtpCaldavValidatorService {
|
||||
private readonly protocolConnectionSchema = z.object({
|
||||
host: z.string().min(1, 'Host is required'),
|
||||
port: z.number().int().positive('Port must be a positive number'),
|
||||
username: z.string().min(1, 'Username is required'),
|
||||
password: z.string().min(1, 'Password is required'),
|
||||
secure: z.boolean().optional(),
|
||||
});
|
||||
|
||||
validateProtocolConnectionParams(
|
||||
params: ConnectionParameters,
|
||||
): ConnectionParameters {
|
||||
if (!params) {
|
||||
throw new UserInputError('Protocol connection parameters are required');
|
||||
}
|
||||
|
||||
try {
|
||||
return this.protocolConnectionSchema.parse(params);
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
const errorMessages = error.errors
|
||||
.map((err) => `${err.path.join('.')}: ${err.message}`)
|
||||
.join(', ');
|
||||
|
||||
throw new UserInputError(
|
||||
`Protocol connection validation failed: ${errorMessages}`,
|
||||
);
|
||||
}
|
||||
|
||||
throw new UserInputError('Protocol connection validation failed');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,129 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { ImapFlow } from 'imapflow';
|
||||
import { ConnectedAccountProvider } from 'twenty-shared/types';
|
||||
|
||||
import { UserInputError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import {
|
||||
AccountType,
|
||||
ConnectionParameters,
|
||||
} from 'src/engine/core-modules/imap-smtp-caldav-connection/types/imap-smtp-caldav-connection.type';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
export class ImapSmtpCaldavService {
|
||||
private readonly logger = new Logger(ImapSmtpCaldavService.name);
|
||||
|
||||
constructor(
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
) {}
|
||||
|
||||
async testImapConnection(params: ConnectionParameters): Promise<boolean> {
|
||||
if (!params.host || !params.username || !params.password) {
|
||||
throw new UserInputError('Missing required IMAP connection parameters');
|
||||
}
|
||||
|
||||
const client = new ImapFlow({
|
||||
host: params.host,
|
||||
port: params.port,
|
||||
secure: params.secure ?? true,
|
||||
auth: {
|
||||
user: params.username,
|
||||
pass: params.password,
|
||||
},
|
||||
logger: false,
|
||||
tls: {
|
||||
rejectUnauthorized: false,
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
await client.connect();
|
||||
|
||||
const mailboxes = await client.list();
|
||||
|
||||
this.logger.log(
|
||||
`IMAP connection successful. Found ${mailboxes.length} mailboxes.`,
|
||||
);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`IMAP connection failed: ${error.message}`,
|
||||
error.stack,
|
||||
);
|
||||
|
||||
if (error.authenticationFailed) {
|
||||
throw new UserInputError(
|
||||
'IMAP authentication failed. Please check your credentials.',
|
||||
);
|
||||
}
|
||||
|
||||
if (error.code === 'ECONNREFUSED') {
|
||||
throw new UserInputError(
|
||||
`IMAP connection refused. Please verify server and port.`,
|
||||
);
|
||||
}
|
||||
|
||||
throw new UserInputError(`IMAP connection failed: ${error.message}`);
|
||||
} finally {
|
||||
if (client.authenticated) {
|
||||
await client.logout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async testSmtpConnection(params: ConnectionParameters): Promise<boolean> {
|
||||
this.logger.log('SMTP connection testing not yet implemented', params);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async testCaldavConnection(params: ConnectionParameters): Promise<boolean> {
|
||||
this.logger.log('CALDAV connection testing not yet implemented', params);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async testImapSmtpCaldav(
|
||||
params: ConnectionParameters,
|
||||
accountType: AccountType,
|
||||
): Promise<boolean> {
|
||||
if (accountType === 'IMAP') {
|
||||
return this.testImapConnection(params);
|
||||
}
|
||||
|
||||
if (accountType === 'SMTP') {
|
||||
return this.testSmtpConnection(params);
|
||||
}
|
||||
|
||||
if (accountType === 'CALDAV') {
|
||||
return this.testCaldavConnection(params);
|
||||
}
|
||||
|
||||
throw new UserInputError(
|
||||
'Invalid account type. Must be one of: IMAP, SMTP, CALDAV',
|
||||
);
|
||||
}
|
||||
|
||||
async getImapSmtpCaldav(
|
||||
workspaceId: string,
|
||||
connectionId: string,
|
||||
): Promise<ConnectedAccountWorkspaceEntity | null> {
|
||||
const connectedAccountRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<ConnectedAccountWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'connectedAccount',
|
||||
);
|
||||
|
||||
const connectedAccount = await connectedAccountRepository.findOne({
|
||||
where: {
|
||||
id: connectionId,
|
||||
provider: ConnectedAccountProvider.IMAP_SMTP_CALDAV,
|
||||
},
|
||||
});
|
||||
|
||||
return connectedAccount;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
export type ConnectionParameters = {
|
||||
host: string;
|
||||
port: number;
|
||||
username: string;
|
||||
password: string;
|
||||
secure?: boolean;
|
||||
};
|
||||
|
||||
export type AccountType = 'IMAP' | 'SMTP' | 'CALDAV';
|
||||
|
||||
export type ImapSmtpCaldavParams = {
|
||||
handle: string;
|
||||
IMAP?: ConnectionParameters;
|
||||
SMTP?: ConnectionParameters;
|
||||
CALDAV?: ConnectionParameters;
|
||||
};
|
||||
@ -143,6 +143,13 @@ export class ConfigVariables {
|
||||
})
|
||||
MESSAGING_PROVIDER_GMAIL_ENABLED = false;
|
||||
|
||||
@ConfigVariablesMetadata({
|
||||
group: ConfigVariablesGroup.Other,
|
||||
description: 'Enable or disable the IMAP messaging integration',
|
||||
type: ConfigVariableType.BOOLEAN,
|
||||
})
|
||||
MESSAGING_PROVIDER_IMAP_ENABLED = false;
|
||||
|
||||
@ConfigVariablesMetadata({
|
||||
group: ConfigVariablesGroup.MicrosoftAuth,
|
||||
description: 'Enable or disable Microsoft authentication',
|
||||
|
||||
Reference in New Issue
Block a user