Merge branch 'main' into feat/api-keys
commit
d158c18b9d
|
@ -1,6 +1,7 @@
|
||||||
/// <reference path="../types/ical.d.ts"/>
|
/// <reference path="../types/ical.d.ts"/>
|
||||||
import { Credential, Prisma } from "@prisma/client";
|
import { Credential, Prisma } from "@prisma/client";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
import isBetween from "dayjs/plugin/isBetween";
|
||||||
import timezone from "dayjs/plugin/timezone";
|
import timezone from "dayjs/plugin/timezone";
|
||||||
import utc from "dayjs/plugin/utc";
|
import utc from "dayjs/plugin/utc";
|
||||||
import ICAL from "ical.js";
|
import ICAL from "ical.js";
|
||||||
|
@ -36,6 +37,7 @@ const DEFAULT_CALENDAR_TYPE = "caldav";
|
||||||
|
|
||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
dayjs.extend(timezone);
|
dayjs.extend(timezone);
|
||||||
|
dayjs.extend(isBetween);
|
||||||
|
|
||||||
const CALENDSO_ENCRYPTION_KEY = process.env.CALENDSO_ENCRYPTION_KEY || "";
|
const CALENDSO_ENCRYPTION_KEY = process.env.CALENDSO_ENCRYPTION_KEY || "";
|
||||||
|
|
||||||
|
@ -241,24 +243,63 @@ export default abstract class BaseCalendarService implements Calendar {
|
||||||
)
|
)
|
||||||
).flat();
|
).flat();
|
||||||
|
|
||||||
const events = objects
|
const events: { start: string; end: string }[] = [];
|
||||||
.filter((e) => !!e.data)
|
|
||||||
.map((object) => {
|
objects.forEach((object) => {
|
||||||
|
if (object.data == null) return;
|
||||||
|
|
||||||
const jcalData = ICAL.parse(object.data);
|
const jcalData = ICAL.parse(object.data);
|
||||||
const vcalendar = new ICAL.Component(jcalData);
|
const vcalendar = new ICAL.Component(jcalData);
|
||||||
const vevent = vcalendar.getFirstSubcomponent("vevent");
|
const vevent = vcalendar.getFirstSubcomponent("vevent");
|
||||||
const event = new ICAL.Event(vevent);
|
const event = new ICAL.Event(vevent);
|
||||||
const vtimezone = vcalendar.getFirstSubcomponent("vtimezone");
|
const vtimezone = vcalendar.getFirstSubcomponent("vtimezone");
|
||||||
|
|
||||||
|
if (event.isRecurring()) {
|
||||||
|
let maxIterations = 365;
|
||||||
|
if (["HOURLY", "SECONDLY", "MINUTELY"].includes(event.getRecurrenceTypes())) {
|
||||||
|
console.error(`Won't handle [${event.getRecurrenceTypes()}] recurrence`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const start = dayjs(dateFrom);
|
||||||
|
const end = dayjs(dateTo);
|
||||||
|
|
||||||
|
const iterator = event.iterator();
|
||||||
|
let current;
|
||||||
|
let currentEvent;
|
||||||
|
let currentStart;
|
||||||
|
|
||||||
|
do {
|
||||||
|
maxIterations -= 1;
|
||||||
|
current = iterator.next();
|
||||||
|
currentEvent = event.getOccurrenceDetails(current);
|
||||||
|
// as pointed out in https://datatracker.ietf.org/doc/html/rfc4791#section-9.6.5
|
||||||
|
// recurring events are always in utc
|
||||||
|
currentStart = dayjs(currentEvent.startDate.toJSDate());
|
||||||
|
|
||||||
|
if (currentStart.isBetween(start, end) === true) {
|
||||||
|
return events.push({
|
||||||
|
start: currentStart.toISOString(),
|
||||||
|
end: dayjs(currentEvent.endDate.toJSDate()).toISOString(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} while (maxIterations > 0 && currentStart.isAfter(end) === false);
|
||||||
|
if (maxIterations <= 0) {
|
||||||
|
console.warn("could not find any occurrence for recurring event in 365 iterations");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (vtimezone) {
|
if (vtimezone) {
|
||||||
const zone = new ICAL.Timezone(vtimezone);
|
const zone = new ICAL.Timezone(vtimezone);
|
||||||
event.startDate = event.startDate.convertToZone(zone);
|
event.startDate = event.startDate.convertToZone(zone);
|
||||||
event.endDate = event.endDate.convertToZone(zone);
|
event.endDate = event.endDate.convertToZone(zone);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return events.push({
|
||||||
start: dayjs(event.startDate.toJSDate()).toISOString(),
|
start: dayjs(event.startDate.toJSDate()).toISOString(),
|
||||||
end: dayjs(event.endDate.toJSDate()).toISOString(),
|
end: dayjs(event.endDate.toJSDate()).toISOString(),
|
||||||
};
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return Promise.resolve(events);
|
return Promise.resolve(events);
|
||||||
|
|
|
@ -80,6 +80,16 @@ declare module "ical.js" {
|
||||||
|
|
||||||
public isRecurring(): boolean;
|
public isRecurring(): boolean;
|
||||||
public iterator(startTime?: Time): RecurExpansion;
|
public iterator(startTime?: Time): RecurExpansion;
|
||||||
|
public getOccurrenceDetails(occurrence: Time): OccurrenceDetails;
|
||||||
|
public getRecurrenceTypes(): FrequencyValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://mozilla-comm.github.io/ical.js/api/ICAL.Event.html#.occurrenceDetails
|
||||||
|
interface OccurrenceDetails {
|
||||||
|
recurrenceId: Time;
|
||||||
|
item: Event;
|
||||||
|
startDate: Time;
|
||||||
|
endDate: Time;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Property {
|
export class Property {
|
||||||
|
@ -108,9 +118,9 @@ declare module "ical.js" {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Time {
|
export class Time {
|
||||||
public fromString(str: string): Time;
|
public static fromString(str: string): Time;
|
||||||
public fromJSDate(aDate: Date | null, useUTC: boolean): Time;
|
public static fromJSDate(aDate: Date | null, useUTC: boolean): Time;
|
||||||
public fromData(aData: TimeJsonData): Time;
|
public static fromData(aData: TimeJsonData): Time;
|
||||||
|
|
||||||
public now(): Time;
|
public now(): Time;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue