- Add getCalendars() and updateCalendarSelection() to caldav-sync.js
- Add sync() function for bidirectional CalDAV synchronization
- Add getStatus() to report on all accounts and enabled calendars
- Add 8 new API routes to calendar.js for CalDAV account and calendar management
- All routes require admin role for security
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Null/undefined items in the Google Calendar API response caused a
TypeError on `item.status` access, silently aborting the sync while
the OAuth callback had already redirected to the success page.
- Filter null items in `upsertGoogleEvents` with an early `continue`
- Await `sync()` in the OAuth callback so errors surface as
`sync_error=google` redirects instead of false success
Resolves#92
Co-authored-by: Ulas Kalayci <ulas.kalayci@googlemail.com>
Google and Apple sync services now fetch calendar metadata and persist it via
upsertExternalCalendar(). The /calendar and /upcoming endpoints JOIN on
external_calendars to return cal_name and cal_color with every event.
- Add CRUD routes for /subscriptions (GET, POST, PATCH, DELETE)
- Add manual sync trigger: POST /subscriptions/:id/sync
- Add ICS visibility filter to GET /calendar (private vs. shared)
- Set user_modified=1 on PUT /:id for ICS events
- Add POST /:id/reset to clear user_modified on ICS events
- Wire icsSubscription.sync() into runSync() in server/index.js
Convert all server/, test, and setup files from require()/module.exports
to import/export syntax. Activate ESM globally via "type": "module" in
package.json and load dotenv via --import dotenv/config in npm scripts.
Add server/logger.js - zero-dependency, level-based logger that outputs
JSON in production and human-readable format in development. Controlled
via LOG_LEVEL env var (debug/info/warn/error, default: info).
Replaces all 100 console.log/warn/error calls in 14 server files.
Fix stored XSS in tasks (titles/subtasks) and settings (member list)
by applying escHtml(). Harden trust proxy to loopback default, add
OAuth state parameter for Google Calendar CSRF protection, sanitize
CSV export against formula injection, invalidate sessions on user
deletion, restrict usernames to alphanumeric chars, and require admin
role for calendar sync triggers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace all — with - in all source files (JS, CSS, HTML, JSON,
Markdown) for consistency and readability.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Root causes:
1. parseRRule did not strip the "RRULE:" prefix stored by the ICS parser,
causing all recurrence rules from CalDAV sync to silently fail parsing
2. YEARLY frequency (used by birthday events) was not supported
3. expandRecurringEvents filtered instances only by start date, missing
multi-day events that start before the view window but span into it
4. All-day recurring instances got datetime end values instead of date-only
Fixes#5 (follow-up from @tschig)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Admin can now enter CalDAV URL, Apple-ID and app-specific password
directly in Settings; credentials are tested live before saving and
stored in sync_config (take precedence over .env); disconnect clears
DB-stored credentials without server restart. Auto-sync interval
(15 min, configurable via SYNC_INTERVAL_MINUTES) was already in place.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
expandRecurringEvents() iterates from the event's original start date,
generating all occurrences within the requested window using the existing
nextOccurrence() service (max 1000 iterations). The SQL query is extended
to also fetch recurring events that started before the window. Event
duration is preserved across instances. Virtual instances carry
is_recurring_instance=1 and are shown with a repeat icon in the agenda
view. /upcoming expands across a 90-day forward window.
Closes BL-01.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>