feat: CalDav Driver (#13170)
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
@ -14,6 +14,9 @@ export class ConnectionParameters {
|
||||
@Field(() => Number)
|
||||
port: number;
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
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,
|
||||
@ -26,6 +29,18 @@ export class ConnectionParameters {
|
||||
secure?: boolean;
|
||||
}
|
||||
|
||||
@InputType()
|
||||
export class EmailAccountConnectionParameters {
|
||||
@Field(() => ConnectionParameters, { nullable: true })
|
||||
IMAP?: ConnectionParameters;
|
||||
|
||||
@Field(() => ConnectionParameters, { nullable: true })
|
||||
SMTP?: ConnectionParameters;
|
||||
|
||||
@Field(() => ConnectionParameters, { nullable: true })
|
||||
CALDAV?: ConnectionParameters;
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class ConnectionParametersOutput {
|
||||
@Field(() => String)
|
||||
@ -34,6 +49,9 @@ export class ConnectionParametersOutput {
|
||||
@Field(() => Number)
|
||||
port: number;
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
username?: string;
|
||||
|
||||
@Field(() => String)
|
||||
password: string;
|
||||
|
||||
|
||||
@ -16,10 +16,7 @@ import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/re
|
||||
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 { EmailAccountConnectionParameters } 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';
|
||||
@ -78,12 +75,11 @@ export class ImapSmtpCaldavResolver {
|
||||
|
||||
@Mutation(() => ImapSmtpCaldavConnectionSuccess)
|
||||
@UseGuards(WorkspaceAuthGuard)
|
||||
async saveImapSmtpCaldav(
|
||||
async saveImapSmtpCaldavAccount(
|
||||
@Args('accountOwnerId') accountOwnerId: string,
|
||||
@Args('handle') handle: string,
|
||||
@Args('accountType') accountType: AccountType,
|
||||
@Args('connectionParameters')
|
||||
connectionParameters: ConnectionParameters,
|
||||
connectionParameters: EmailAccountConnectionParameters,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
@Args('id', { nullable: true }) id?: string,
|
||||
): Promise<ImapSmtpCaldavConnectionSuccess> {
|
||||
@ -100,23 +96,16 @@ export class ImapSmtpCaldavResolver {
|
||||
);
|
||||
}
|
||||
|
||||
const validatedParams =
|
||||
this.mailConnectionValidatorService.validateProtocolConnectionParams(
|
||||
connectionParameters,
|
||||
);
|
||||
|
||||
await this.ImapSmtpCaldavConnectionService.testImapSmtpCaldav(
|
||||
const validatedParams = await this.validateAndTestConnectionParameters(
|
||||
connectionParameters,
|
||||
handle,
|
||||
validatedParams,
|
||||
accountType.type,
|
||||
);
|
||||
|
||||
await this.imapSmtpCaldavApisService.setupConnectedAccount({
|
||||
await this.imapSmtpCaldavApisService.setupCompleteAccount({
|
||||
handle,
|
||||
workspaceMemberId: accountOwnerId,
|
||||
workspaceId: workspace.id,
|
||||
connectionParams: validatedParams,
|
||||
accountType: accountType.type,
|
||||
connectionParameters: validatedParams,
|
||||
connectedAccountId: id,
|
||||
});
|
||||
|
||||
@ -124,4 +113,34 @@ export class ImapSmtpCaldavResolver {
|
||||
success: true,
|
||||
};
|
||||
}
|
||||
|
||||
private async validateAndTestConnectionParameters(
|
||||
connectionParameters: EmailAccountConnectionParameters,
|
||||
handle: string,
|
||||
): Promise<EmailAccountConnectionParameters> {
|
||||
const validatedParams: EmailAccountConnectionParameters = {};
|
||||
const protocols = ['IMAP', 'SMTP', 'CALDAV'] as const;
|
||||
|
||||
for (const protocol of protocols) {
|
||||
const params = connectionParameters[protocol];
|
||||
|
||||
if (params) {
|
||||
validatedParams[protocol] =
|
||||
this.mailConnectionValidatorService.validateProtocolConnectionParams(
|
||||
params,
|
||||
);
|
||||
const validatedProtocolParams = validatedParams[protocol];
|
||||
|
||||
if (validatedProtocolParams) {
|
||||
await this.ImapSmtpCaldavConnectionService.testImapSmtpCaldav(
|
||||
handle,
|
||||
validatedProtocolParams,
|
||||
protocol,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validatedParams;
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ 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().optional(),
|
||||
password: z.string().min(1, 'Password is required'),
|
||||
secure: z.boolean().optional(),
|
||||
});
|
||||
|
||||
@ -10,6 +10,7 @@ import {
|
||||
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 { CalDAVClient } from 'src/modules/calendar/calendar-event-import-manager/drivers/caldav/lib/caldav.client';
|
||||
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
@ -121,7 +122,31 @@ export class ImapSmtpCaldavService {
|
||||
handle: string,
|
||||
params: ConnectionParameters,
|
||||
): Promise<boolean> {
|
||||
this.logger.log('CALDAV connection testing not yet implemented', params);
|
||||
const client = new CalDAVClient({
|
||||
serverUrl: params.host,
|
||||
username: params.username ?? handle,
|
||||
password: params.password,
|
||||
});
|
||||
|
||||
try {
|
||||
await client.listCalendars();
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`CALDAV connection failed: ${error.message}`,
|
||||
error.stack,
|
||||
);
|
||||
if (error.code === 'FailedToOpenSocket') {
|
||||
throw new UserInputError(`CALDAV connection failed: ${error.message}`, {
|
||||
userFriendlyMessage:
|
||||
"We couldn't connect to your CalDAV server. Please check your server settings and try again.",
|
||||
});
|
||||
}
|
||||
|
||||
throw new UserInputError(`CALDAV connection failed: ${error.message}`, {
|
||||
userFriendlyMessage:
|
||||
'Invalid credentials. Please check your username and password.',
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
export type ConnectionParameters = {
|
||||
host: string;
|
||||
port: number;
|
||||
username?: string;
|
||||
password: string;
|
||||
secure?: boolean;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user