feat: basic caldav support (#14372)

fix #13531 

#### PR Dependency Tree


* **PR #14372** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* CalDAV calendar integration: link and sync CalDAV-compatible calendars
(discovery, listing, event sync).
* New UI flow and dialog to link CalDAV accounts with provider
selection, credentials, and display name.

* **API / Config**
* Server exposes CalDAV provider presets in config and new GraphQL
mutation to link CalDAV accounts.
  * New calendar config section for CalDAV with validation and defaults.

* **Tests**
  * Comprehensive CalDAV integration test suite added.

* **Chores**
* Removed analytics tokens from build configuration and reduced Cloud
E2E test shards.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
DarkSky
2026-02-05 03:04:21 +08:00
committed by GitHub
parent 403f16b404
commit a655b79166
34 changed files with 2995 additions and 217 deletions
@@ -1,4 +1,5 @@
import { Inject, Injectable, Logger } from '@nestjs/common';
import type { CalendarAccount } from '@prisma/client';
import { CalendarProviderRequestError, Config, OnEvent } from '../../../base';
import { CalendarProviderFactory } from './factory';
@@ -54,11 +55,17 @@ export interface CalendarProviderEvent {
export interface CalendarProviderListEventsParams {
accessToken: string;
calendarId: string;
account?: CalendarAccount;
timeMin?: string;
timeMax?: string;
syncToken?: string;
}
export interface CalendarProviderListCalendarsParams {
accessToken: string;
account?: CalendarAccount;
}
export interface CalendarProviderListEventsResult {
events: CalendarProviderEvent[];
nextSyncToken?: string;
@@ -97,7 +104,7 @@ export abstract class CalendarProvider {
accessToken: string
): Promise<CalendarAccountProfile>;
abstract listCalendars(
accessToken: string
params: CalendarProviderListCalendarsParams
): Promise<CalendarProviderCalendar[]>;
abstract listEvents(
params: CalendarProviderListEventsParams
@@ -117,12 +124,17 @@ export abstract class CalendarProvider {
}
get configured() {
return (
!!this.config &&
!!this.config.enabled &&
!!this.config.clientId &&
!!this.config.clientSecret
);
if (!this.config || !this.config.enabled) {
return false;
}
if ('clientId' in this.config || 'clientSecret' in this.config) {
return Boolean(this.config.clientId && this.config.clientSecret);
}
return true;
}
get supportsOAuth() {
return true;
}
@OnEvent('config.init')