306 lines
9.5 KiB
JavaScript
306 lines
9.5 KiB
JavaScript
// Calendar Service for Nextcloud CalDAV integration
|
|
// This is a basic implementation that can be enhanced with the @nextcloud/cdav-library
|
|
const crypto = require('crypto');
|
|
const https = require('https');
|
|
const http = require('http');
|
|
const { URL } = require('url');
|
|
|
|
class CalendarService {
|
|
constructor() {
|
|
this.events = new Map(); // In-memory storage for demo purposes
|
|
// TODO: Replace with actual CalDAV operations when library is available
|
|
}
|
|
|
|
/**
|
|
* Create a calendar event
|
|
* @param {Object} userConfig - Nextcloud user configuration
|
|
* @param {string} date - Date in YYYY-MM-DD format
|
|
* @param {string} text - Event description
|
|
* @returns {Promise<Object>} Created event
|
|
*/
|
|
async createEvent(userConfig, date, text) {
|
|
try {
|
|
// TODO: Implement actual CalDAV event creation
|
|
// For now, simulate the operation with local storage
|
|
|
|
const eventId = crypto.randomUUID();
|
|
const event = {
|
|
id: eventId,
|
|
date: date,
|
|
text: text,
|
|
user: userConfig.name,
|
|
summary: text,
|
|
dtstart: this._formatDateForCalDAV(date),
|
|
dtend: this._formatDateForCalDAV(date),
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString()
|
|
};
|
|
|
|
this.events.set(eventId, event);
|
|
|
|
console.log(`Calendar event created for ${userConfig.name}: ${text} on ${date}`);
|
|
|
|
// TODO: When CalDAV library is available, replace with:
|
|
// const caldavEvent = await this._createCalDAVEvent(userConfig, event);
|
|
|
|
return event;
|
|
} catch (error) {
|
|
console.error('Error creating calendar event:', error);
|
|
throw new Error('Failed to create calendar event');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get calendar events for a specific month
|
|
* @param {Object} userConfig - Nextcloud user configuration
|
|
* @param {string} year - Year (YYYY)
|
|
* @param {string} month - Month (MM)
|
|
* @returns {Promise<Array>} Array of events
|
|
*/
|
|
async getEventsForMonth(userConfig, year, month) {
|
|
try {
|
|
// TODO: Implement actual CalDAV event retrieval
|
|
// For now, filter local events
|
|
|
|
const events = Array.from(this.events.values());
|
|
const filteredEvents = events.filter(event => {
|
|
if (event.user !== userConfig.name) return false;
|
|
const eventDate = new Date(event.date);
|
|
return eventDate.getFullYear() === parseInt(year) &&
|
|
(eventDate.getMonth() + 1) === parseInt(month);
|
|
});
|
|
|
|
console.log(`Retrieved ${filteredEvents.length} events for ${userConfig.name} in ${year}-${month}`);
|
|
|
|
// TODO: When CalDAV library is available, replace with:
|
|
// const caldavEvents = await this._fetchCalDAVEvents(userConfig, year, month);
|
|
|
|
return filteredEvents;
|
|
} catch (error) {
|
|
console.error('Error retrieving calendar events:', error);
|
|
throw new Error('Failed to retrieve calendar events');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get calendar events for all users (family view)
|
|
* @param {Array} userConfigs - Array of user configurations
|
|
* @param {string} year - Year (YYYY)
|
|
* @param {string} month - Month (MM)
|
|
* @returns {Promise<Array>} Array of events from all users
|
|
*/
|
|
async getFamilyEventsForMonth(userConfigs, year, month) {
|
|
try {
|
|
const allEvents = [];
|
|
|
|
for (const userConfig of userConfigs) {
|
|
const userEvents = await this.getEventsForMonth(userConfig, year, month);
|
|
allEvents.push(...userEvents);
|
|
}
|
|
|
|
// Sort events by date
|
|
allEvents.sort((a, b) => new Date(a.date) - new Date(b.date));
|
|
|
|
console.log(`Retrieved ${allEvents.length} family events for ${year}-${month}`);
|
|
return allEvents;
|
|
} catch (error) {
|
|
console.error('Error retrieving family calendar events:', error);
|
|
throw new Error('Failed to retrieve family calendar events');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update a calendar event
|
|
* @param {Object} userConfig - Nextcloud user configuration
|
|
* @param {string} eventId - Event ID
|
|
* @param {string} date - New date
|
|
* @param {string} text - New text
|
|
* @returns {Promise<Object>} Updated event
|
|
*/
|
|
async updateEvent(userConfig, eventId, date, text) {
|
|
try {
|
|
const event = this.events.get(eventId);
|
|
if (!event) {
|
|
throw new Error('Event not found');
|
|
}
|
|
|
|
if (event.user !== userConfig.name) {
|
|
throw new Error('Permission denied: You can only edit your own events');
|
|
}
|
|
|
|
event.date = date;
|
|
event.text = text;
|
|
event.summary = text;
|
|
event.dtstart = this._formatDateForCalDAV(date);
|
|
event.dtend = this._formatDateForCalDAV(date);
|
|
event.updatedAt = new Date().toISOString();
|
|
|
|
this.events.set(eventId, event);
|
|
|
|
console.log(`Calendar event updated for ${userConfig.name}: ${eventId}`);
|
|
|
|
// TODO: When CalDAV library is available, replace with:
|
|
// await this._updateCalDAVEvent(userConfig, event);
|
|
|
|
return event;
|
|
} catch (error) {
|
|
console.error('Error updating calendar event:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete a calendar event
|
|
* @param {Object} userConfig - Nextcloud user configuration
|
|
* @param {string} eventId - Event ID
|
|
* @returns {Promise<boolean>} Success status
|
|
*/
|
|
async deleteEvent(userConfig, eventId) {
|
|
try {
|
|
const event = this.events.get(eventId);
|
|
if (!event) {
|
|
throw new Error('Event not found');
|
|
}
|
|
|
|
if (event.user !== userConfig.name) {
|
|
throw new Error('Permission denied: You can only delete your own events');
|
|
}
|
|
|
|
this.events.delete(eventId);
|
|
|
|
console.log(`Calendar event deleted for ${userConfig.name}: ${eventId}`);
|
|
|
|
// TODO: When CalDAV library is available, replace with:
|
|
// await this._deleteCalDAVEvent(userConfig, eventId);
|
|
|
|
return true;
|
|
} catch (error) {
|
|
console.error('Error deleting calendar event:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test connection to Nextcloud instance
|
|
* @param {Object} userConfig - Nextcloud user configuration
|
|
* @returns {Promise<boolean>} Connection status
|
|
*/
|
|
async testConnection(userConfig) {
|
|
try {
|
|
// Basic HTTP test to verify Nextcloud instance is reachable
|
|
const url = new URL(userConfig.nextcloudUrl);
|
|
const options = {
|
|
hostname: url.hostname,
|
|
port: url.port || (url.protocol === 'https:' ? 443 : 80),
|
|
path: '/status.php',
|
|
method: 'GET',
|
|
timeout: 5000,
|
|
headers: {
|
|
'User-Agent': 'OpenWall/1.0'
|
|
}
|
|
};
|
|
|
|
const protocol = url.protocol === 'https:' ? https : http;
|
|
|
|
return new Promise((resolve) => {
|
|
const req = protocol.request(options, (res) => {
|
|
let data = '';
|
|
res.on('data', chunk => data += chunk);
|
|
res.on('end', () => {
|
|
try {
|
|
const status = JSON.parse(data);
|
|
const isNextcloud = status.productname && status.productname.toLowerCase().includes('nextcloud');
|
|
console.log(`Connection test for ${userConfig.name}: ${isNextcloud ? 'SUCCESS' : 'NOT_NEXTCLOUD'}`);
|
|
resolve(isNextcloud);
|
|
} catch (e) {
|
|
console.log(`Connection test for ${userConfig.name}: INVALID_RESPONSE`);
|
|
resolve(false);
|
|
}
|
|
});
|
|
});
|
|
|
|
req.on('error', (error) => {
|
|
console.log(`Connection test for ${userConfig.name}: ERROR - ${error.message}`);
|
|
resolve(false);
|
|
});
|
|
|
|
req.on('timeout', () => {
|
|
console.log(`Connection test for ${userConfig.name}: TIMEOUT`);
|
|
req.destroy();
|
|
resolve(false);
|
|
});
|
|
|
|
req.end();
|
|
});
|
|
} catch (error) {
|
|
console.error('Error testing connection:', error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Format date for CalDAV (YYYYMMDD format for all-day events)
|
|
* @private
|
|
*/
|
|
_formatDateForCalDAV(dateString) {
|
|
const date = new Date(dateString);
|
|
return date.toISOString().slice(0, 10).replace(/-/g, '');
|
|
}
|
|
|
|
// TODO: Implement these methods when @nextcloud/cdav-library is available
|
|
/*
|
|
async _createCalDAVEvent(userConfig, event) {
|
|
const { createClient } = require('@nextcloud/cdav-library');
|
|
const client = createClient({
|
|
url: userConfig.nextcloudUrl,
|
|
username: userConfig.username,
|
|
password: userConfig.password
|
|
});
|
|
|
|
// Create ICS event content
|
|
const icsContent = this._generateICSEvent(event);
|
|
|
|
// Create event on Nextcloud
|
|
const result = await client.createEvent(icsContent);
|
|
return result;
|
|
}
|
|
|
|
async _fetchCalDAVEvents(userConfig, year, month) {
|
|
const { createClient } = require('@nextcloud/cdav-library');
|
|
const client = createClient({
|
|
url: userConfig.nextcloudUrl,
|
|
username: userConfig.username,
|
|
password: userConfig.password
|
|
});
|
|
|
|
const startDate = new Date(year, month - 1, 1);
|
|
const endDate = new Date(year, month, 0);
|
|
|
|
const events = await client.fetchCalendarEvents({
|
|
start: startDate,
|
|
end: endDate
|
|
});
|
|
|
|
return events;
|
|
}
|
|
|
|
_generateICSEvent(event) {
|
|
return `BEGIN:VCALENDAR
|
|
VERSION:2.0
|
|
PRODID:-//OpenWall//Calendar//EN
|
|
BEGIN:VEVENT
|
|
UID:${event.id}
|
|
DTSTART;VALUE=DATE:${event.dtstart}
|
|
DTEND;VALUE=DATE:${event.dtend}
|
|
SUMMARY:${event.summary}
|
|
DESCRIPTION:${event.text}
|
|
CREATED:${new Date(event.createdAt).toISOString().replace(/[-:]/g, '').split('.')[0]}Z
|
|
LAST-MODIFIED:${new Date(event.updatedAt).toISOString().replace(/[-:]/g, '').split('.')[0]}Z
|
|
END:VEVENT
|
|
END:VCALENDAR`;
|
|
}
|
|
*/
|
|
}
|
|
|
|
module.exports = new CalendarService();
|