7c8d362772
### 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>
107 lines
3.5 KiB
TypeScript
107 lines
3.5 KiB
TypeScript
import { Injectable } from '@nestjs/common';
|
|
|
|
import { ConnectedAccountProvider } from 'twenty-shared/types';
|
|
import { assertUnreachable, isDefined } from 'twenty-shared/utils';
|
|
import { z } from 'zod';
|
|
|
|
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
|
import { GmailClientProvider } from 'src/modules/messaging/message-import-manager/drivers/gmail/providers/gmail-client.provider';
|
|
import { OAuth2ClientProvider } from 'src/modules/messaging/message-import-manager/drivers/gmail/providers/oauth2-client.provider';
|
|
import { MicrosoftClientProvider } from 'src/modules/messaging/message-import-manager/drivers/microsoft/providers/microsoft-client.provider';
|
|
import { mimeEncode } from 'src/modules/messaging/message-import-manager/utils/mime-encode.util';
|
|
|
|
interface SendMessageInput {
|
|
body: string;
|
|
subject: string;
|
|
to: string;
|
|
}
|
|
|
|
@Injectable()
|
|
export class MessagingSendMessageService {
|
|
constructor(
|
|
private readonly gmailClientProvider: GmailClientProvider,
|
|
private readonly oAuth2ClientProvider: OAuth2ClientProvider,
|
|
private readonly microsoftClientProvider: MicrosoftClientProvider,
|
|
) {}
|
|
|
|
public async sendMessage(
|
|
sendMessageInput: SendMessageInput,
|
|
connectedAccount: ConnectedAccountWorkspaceEntity,
|
|
): Promise<void> {
|
|
switch (connectedAccount.provider) {
|
|
case ConnectedAccountProvider.GOOGLE: {
|
|
const gmailClient =
|
|
await this.gmailClientProvider.getGmailClient(connectedAccount);
|
|
|
|
const oAuth2Client =
|
|
await this.oAuth2ClientProvider.getOAuth2Client(connectedAccount);
|
|
|
|
const { data } = await oAuth2Client.userinfo.get();
|
|
|
|
const fromEmail = data.email;
|
|
|
|
const fromName = data.name;
|
|
|
|
const headers: string[] = [];
|
|
|
|
if (isDefined(fromName)) {
|
|
headers.push(`From: "${mimeEncode(fromName)}" <${fromEmail}>`);
|
|
}
|
|
|
|
headers.push(
|
|
`To: ${sendMessageInput.to}`,
|
|
`Subject: ${mimeEncode(sendMessageInput.subject)}`,
|
|
'MIME-Version: 1.0',
|
|
'Content-Type: text/plain; charset="UTF-8"',
|
|
'',
|
|
sendMessageInput.body,
|
|
);
|
|
|
|
const message = headers.join('\n');
|
|
|
|
const encodedMessage = Buffer.from(message).toString('base64');
|
|
|
|
await gmailClient.users.messages.send({
|
|
userId: 'me',
|
|
requestBody: {
|
|
raw: encodedMessage,
|
|
},
|
|
});
|
|
break;
|
|
}
|
|
case ConnectedAccountProvider.MICROSOFT: {
|
|
const microsoftClient =
|
|
await this.microsoftClientProvider.getMicrosoftClient(
|
|
connectedAccount,
|
|
);
|
|
|
|
const message = {
|
|
subject: sendMessageInput.subject,
|
|
body: {
|
|
contentType: 'Text',
|
|
content: sendMessageInput.body,
|
|
},
|
|
toRecipients: [{ emailAddress: { address: sendMessageInput.to } }],
|
|
};
|
|
|
|
const response = await microsoftClient
|
|
.api(`/me/messages`)
|
|
.post(message);
|
|
|
|
z.string().parse(response.id);
|
|
|
|
await microsoftClient.api(`/me/messages/${response.id}/send`).post({});
|
|
break;
|
|
}
|
|
case ConnectedAccountProvider.IMAP_SMTP_CALDAV: {
|
|
throw new Error('IMAP provider does not support sending messages');
|
|
}
|
|
default:
|
|
assertUnreachable(
|
|
connectedAccount.provider,
|
|
`Provider ${connectedAccount.provider} not supported for sending messages`,
|
|
);
|
|
}
|
|
}
|
|
}
|