@@ -145,6 +145,11 @@ import { JwtAuthStrategy } from './strategies/jwt.auth.strategy';
|
||||
ApiKeyService,
|
||||
AuthSsoService,
|
||||
],
|
||||
exports: [AccessTokenService, LoginTokenService, RefreshTokenService],
|
||||
exports: [
|
||||
AccessTokenService,
|
||||
LoginTokenService,
|
||||
RefreshTokenService,
|
||||
CreateMessageFolderService,
|
||||
],
|
||||
})
|
||||
export class AuthModule {}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { AuthModule } from 'src/engine/core-modules/auth/auth.module';
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { MessageQueueModule } from 'src/engine/core-modules/message-queue/message-queue.module';
|
||||
import { TwentyConfigModule } from 'src/engine/core-modules/twenty-config/twenty-config.module';
|
||||
@@ -17,6 +18,7 @@ import { ImapSmtpCalDavAPIService } from 'src/modules/connected-account/services
|
||||
TwentyConfigModule,
|
||||
TwentyORMModule,
|
||||
FeatureFlagModule,
|
||||
AuthModule,
|
||||
],
|
||||
providers: [ImapSmtpCalDavAPIService],
|
||||
exports: [ImapSmtpCalDavAPIService],
|
||||
|
||||
+30
-17
@@ -3,10 +3,12 @@ import { Injectable } from '@nestjs/common';
|
||||
import { ConnectedAccountProvider } from 'twenty-shared/types';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { CreateMessageFolderService } from 'src/engine/core-modules/auth/services/create-message-folder.service';
|
||||
import { EmailAccountConnectionParameters } from 'src/engine/core-modules/imap-smtp-caldav-connection/dtos/imap-smtp-caldav-connection.dto';
|
||||
import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decorators/message-queue.decorator';
|
||||
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
||||
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
|
||||
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
|
||||
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import {
|
||||
@@ -38,6 +40,7 @@ export class ImapSmtpCalDavAPIService {
|
||||
private readonly messageQueueService: MessageQueueService,
|
||||
@InjectMessageQueue(MessageQueue.calendarQueue)
|
||||
private readonly calendarQueueService: MessageQueueService,
|
||||
private readonly createMessageFolderService: CreateMessageFolderService,
|
||||
) {}
|
||||
|
||||
async setupCompleteAccount(input: {
|
||||
@@ -86,25 +89,28 @@ export class ImapSmtpCalDavAPIService {
|
||||
let createdMessageChannel: MessageChannelWorkspaceEntity | null = null;
|
||||
let createdCalendarChannel: CalendarChannelWorkspaceEntity | null = null;
|
||||
|
||||
await workspaceDataSource.transaction(async () => {
|
||||
await this.upsertConnectedAccount(
|
||||
input,
|
||||
accountId,
|
||||
connectedAccountRepository,
|
||||
);
|
||||
await workspaceDataSource.transaction(
|
||||
async (manager: WorkspaceEntityManager) => {
|
||||
await this.upsertConnectedAccount(
|
||||
input,
|
||||
accountId,
|
||||
connectedAccountRepository,
|
||||
);
|
||||
|
||||
createdMessageChannel = await this.setupMessageChannels(
|
||||
input,
|
||||
accountId,
|
||||
messageChannelRepository,
|
||||
);
|
||||
createdMessageChannel = await this.setupMessageChannels(
|
||||
input,
|
||||
accountId,
|
||||
messageChannelRepository,
|
||||
manager,
|
||||
);
|
||||
|
||||
createdCalendarChannel = await this.setupCalendarChannels(
|
||||
input,
|
||||
accountId,
|
||||
calendarChannelRepository,
|
||||
);
|
||||
});
|
||||
createdCalendarChannel = await this.setupCalendarChannels(
|
||||
input,
|
||||
accountId,
|
||||
calendarChannelRepository,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
await this.enqueueSyncJobs(
|
||||
input,
|
||||
@@ -143,6 +149,7 @@ export class ImapSmtpCalDavAPIService {
|
||||
},
|
||||
accountId: string,
|
||||
messageChannelRepository: WorkspaceRepository<MessageChannelWorkspaceEntity>,
|
||||
manager: WorkspaceEntityManager,
|
||||
): Promise<MessageChannelWorkspaceEntity | null> {
|
||||
const existingChannels = await messageChannelRepository.find({
|
||||
where: { connectedAccountId: accountId },
|
||||
@@ -175,6 +182,12 @@ export class ImapSmtpCalDavAPIService {
|
||||
{},
|
||||
);
|
||||
|
||||
await this.createMessageFolderService.createMessageFolders({
|
||||
workspaceId: input.workspaceId,
|
||||
messageChannelId: newMessageChannel.id,
|
||||
manager,
|
||||
});
|
||||
|
||||
return shouldEnableSync ? newMessageChannel : null;
|
||||
}
|
||||
|
||||
|
||||
+69
-38
@@ -4,9 +4,13 @@ import { ImapFlow } from 'imapflow';
|
||||
|
||||
import { ImapClientProvider } from 'src/modules/messaging/message-import-manager/drivers/imap/providers/imap-client.provider';
|
||||
import { ImapHandleErrorService } from 'src/modules/messaging/message-import-manager/drivers/imap/services/imap-handle-error.service';
|
||||
import { MessageFolderName } from 'src/modules/messaging/message-import-manager/drivers/imap/types/folders';
|
||||
import { findSentMailbox } from 'src/modules/messaging/message-import-manager/drivers/imap/utils/find-sent-mailbox.util';
|
||||
import { GetMessageListsArgs } from 'src/modules/messaging/message-import-manager/types/get-message-lists-args.type';
|
||||
import { GetMessageListsResponse } from 'src/modules/messaging/message-import-manager/types/get-message-lists-response.type';
|
||||
import {
|
||||
GetMessageListsResponse,
|
||||
GetOneMessageListResponse,
|
||||
} from 'src/modules/messaging/message-import-manager/types/get-message-lists-response.type';
|
||||
|
||||
@Injectable()
|
||||
export class ImapGetMessageListService {
|
||||
@@ -20,59 +24,38 @@ export class ImapGetMessageListService {
|
||||
async getMessageLists({
|
||||
messageChannel,
|
||||
connectedAccount,
|
||||
messageFolders,
|
||||
}: GetMessageListsArgs): Promise<GetMessageListsResponse> {
|
||||
try {
|
||||
const client = await this.imapClientProvider.getClient(connectedAccount);
|
||||
const result: GetMessageListsResponse = [];
|
||||
|
||||
const mailboxes = ['INBOX'];
|
||||
for (const folder of messageFolders) {
|
||||
const mailboxName = await this.getMailboxName(client, folder.name);
|
||||
|
||||
const sentFolder = await findSentMailbox(client, this.logger);
|
||||
if (!mailboxName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sentFolder) {
|
||||
mailboxes.push(sentFolder);
|
||||
}
|
||||
|
||||
let allMessages: { id: string; date: string }[] = [];
|
||||
|
||||
for (const mailbox of mailboxes) {
|
||||
try {
|
||||
const messages = await this.getMessagesFromMailbox(
|
||||
const response = await this.getMessageListForMailbox(
|
||||
client,
|
||||
mailbox,
|
||||
messageChannel.syncCursor,
|
||||
mailboxName,
|
||||
folder.syncCursor,
|
||||
);
|
||||
|
||||
allMessages = [...allMessages, ...messages];
|
||||
this.logger.log(
|
||||
`Fetched ${messages.length} messages from ${mailbox}`,
|
||||
);
|
||||
result.push({
|
||||
...response,
|
||||
folderId: folder.id,
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.warn(
|
||||
`Error fetching from mailbox ${mailbox}: ${error.message}. Continuing with other mailboxes.`,
|
||||
`Error fetching from folder ${folder.name} (${mailboxName}): ${error.message}. Continuing with other folders.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
allMessages.sort(
|
||||
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime(),
|
||||
);
|
||||
|
||||
const messageExternalIds = allMessages.map((message) => message.id);
|
||||
|
||||
const nextSyncCursor =
|
||||
allMessages.length > 0
|
||||
? allMessages[allMessages.length - 1].date
|
||||
: messageChannel.syncCursor || '';
|
||||
|
||||
return [
|
||||
{
|
||||
messageExternalIds,
|
||||
nextSyncCursor,
|
||||
previousSyncCursor: messageChannel.syncCursor,
|
||||
messageExternalIdsToDelete: [],
|
||||
folderId: undefined,
|
||||
},
|
||||
];
|
||||
return result;
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Error getting message list: ${error.message}`,
|
||||
@@ -95,6 +78,54 @@ export class ImapGetMessageListService {
|
||||
}
|
||||
}
|
||||
|
||||
private async getMailboxName(
|
||||
client: ImapFlow,
|
||||
folderName: string,
|
||||
): Promise<string | null> {
|
||||
if (folderName === MessageFolderName.INBOX) {
|
||||
return 'INBOX';
|
||||
}
|
||||
|
||||
if (folderName === MessageFolderName.SENT_ITEMS) {
|
||||
const sentMailbox = await findSentMailbox(client, this.logger);
|
||||
|
||||
if (!sentMailbox) {
|
||||
this.logger.warn('SENT folder not found, skipping');
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return sentMailbox;
|
||||
}
|
||||
|
||||
return folderName;
|
||||
}
|
||||
|
||||
private async getMessageListForMailbox(
|
||||
client: ImapFlow,
|
||||
mailbox: string,
|
||||
cursor?: string,
|
||||
): Promise<GetOneMessageListResponse> {
|
||||
const messages = await this.getMessagesFromMailbox(client, mailbox, cursor);
|
||||
|
||||
messages.sort(
|
||||
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime(),
|
||||
);
|
||||
|
||||
const messageExternalIds = messages.map((message) => message.id);
|
||||
|
||||
const nextSyncCursor =
|
||||
messages.length > 0 ? messages[messages.length - 1].date : cursor || '';
|
||||
|
||||
return {
|
||||
messageExternalIds,
|
||||
nextSyncCursor,
|
||||
previousSyncCursor: cursor || '',
|
||||
messageExternalIdsToDelete: [],
|
||||
folderId: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
private async getMessagesFromMailbox(
|
||||
client: ImapFlow,
|
||||
mailbox: string,
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
export enum MessageFolderName {
|
||||
INBOX = 'inbox',
|
||||
SENT_ITEMS = 'sentItems',
|
||||
}
|
||||
Reference in New Issue
Block a user