Export calenders with iCal

It would be nice to be able to export calendars as iCal so that they can be included in other programs/calendar tools/websites.

Especially as Fibery still doesn’t properly support mobile it would be ideal if that could be included in a phone’s calender.

CalDAV would be great as well. With both iCal and CalDAV pretty much any 3rd party calendar App should be supported. I’ld really love seing it being supported in the future :slight_smile:

1 Like

I created a basic version of this feature using a cloudflare worker and the graphql API!

It doesn’t currently support recurring events, but otherwise, it works perfectly! You will need to edit the graphql query to work with your calendar database (my timed events are “meetings” and my all-day events are “timelines”). Serve the worker from a custom domain, and then you can have a live link to an ICS file!

import ical from 'ical-generator';

const ENDPOINT = ''; // graphql endpoint and token
const TOKEN = '';

export default {
	async fetch(request) {
		try {
			const query = `
	  {
		findMeetings {
		  id,
		  name,
		  description {text},
		  dates {start, end},
		  location,
		  repeatSettings
		},
		findTimelines {
		  id,
		  name,
		  description {text},
		  date {start, end}
		}
	  }`;

			const response = await fetch(ENDPOINT, {
				method: 'POST',
				body: JSON.stringify({ query }),
				headers: {
					'Content-Type': 'application/json',
					Authorization: `Token ${TOKEN}`,
				},
			});
			const data = await response.json();

			const cal = ical({ name: 'Meeting and Timeline Schedule' });

			// Handle findMeetings
			data.data.findMeetings.forEach((meeting) => {
				cal.createEvent({
					id: `meeting-${meeting.id}`,
					start: new Date(meeting.dates.start),
					end: new Date(meeting.dates.end),
					summary: meeting.name,
					description: meeting.description.text,
					location: meeting.location || '',
				});
			});

			// Handle findTimelines as all-day events
			data.data.findTimelines.forEach((timeline) => {
				cal.createEvent({
					id: `timeline-${timeline.id}`,
					start: new Date(timeline.date.start),
					end: new Date(timeline.date.end),
					summary: timeline.name,
					description: timeline.description.text,
					allDay: true,
				});
			});

			const icsFile = cal.toString();
			return new Response(icsFile, {
				headers: {
					'Content-Type': 'text/calendar',
					'Content-Disposition': 'attachment; filename="meetings-and-timelines.ics"',
				},
			});
		} catch (error) {
			console.error(error);
			return new Response(JSON.stringify({ message: 'Internal Server Error' }), {
				status: 500,
				headers: {
					'Content-Type': 'application/json',
				},
			});
		}
	},
};
3 Likes