[pending-judge] feat(DC-161): calendar widget + Radicale auto-wiring
- Top-bar calendar widget (status/js/calendar-widget.js): next-appointment
display, settings modal with CalDAV/ICS source management and a
Test-connection probe. Follows the weather-widget pattern.
- Backend routes/calendar.js: sources CRUD (secrets in credential manager,
never in responses), aggregated events across sources with per-source
error isolation, Radicale provision endpoint, CalDAV discover probe.
- src/calendar/ics-parser.js: dependency-free RFC 5545 parser with bounded
RRULE expansion (DAILY/WEEKLY-BYDAY/MONTHLY/YEARLY, COUNT/UNTIL/INTERVAL,
EXDATE consumes COUNT, RECURRENCE-ID overrides), 500-event cap.
- src/calendar/caldav-client.js: CalDAV REPORT time-range queries over
node:https (no new deps), HTTPS-enforced off localhost, 5MiB response cap.
- src/calendar/radicale-provisioner.js: zero-config auto-wiring - generates
a 24-char password, writes Radicale htpasswd config (pure-Node APR1 hash
verified byte-for-byte against openssl passwd -apr1 vectors), stores
credentials, registers the source. Deploy hook in routes/apps/deploy.js
wires any Radicale deployed via the App Selector automatically.
- Radicale app template (tomsquest/docker-radicale) in the catalog.
- Tests: 32 new (parser unit, provisioner unit incl. openssl vectors,
route tests). Full suite 3552 passed; only pre-existing pdf-parse
billing/invoice failures remain (proven red at base 448f535).
- OpenAPI: 5 calendar paths documented. CHANGELOG entry added.
This commit is contained in:
18 files changed
+1845
No files matched your search
@@ -7676,6 +7676,103 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SuccessResponse'
|
||||
|
||||
/api/v1/calendar/sources:
|
||||
get:
|
||||
tags: [Calendar]
|
||||
summary: List calendar sources
|
||||
description: Returns all configured calendar sources (CalDAV/ICS). Secrets are never included.
|
||||
responses:
|
||||
'200':
|
||||
description: Successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SuccessResponse'
|
||||
'401':
|
||||
description: Unauthorized
|
||||
post:
|
||||
tags: [Calendar]
|
||||
summary: Add a calendar source
|
||||
description: Registers a CalDAV or read-only ICS calendar. CalDAV sources require user+pass or a token.
|
||||
responses:
|
||||
'200':
|
||||
description: Successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SuccessResponse'
|
||||
'400':
|
||||
description: Validation error (bad type/URL, plaintext HTTP to a non-local host)
|
||||
|
||||
/api/v1/calendar/sources/{id}:
|
||||
put:
|
||||
tags: [Calendar]
|
||||
summary: Update a calendar source
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema: { type: string }
|
||||
responses:
|
||||
'200':
|
||||
description: Successful operation
|
||||
'404':
|
||||
description: Source not found
|
||||
delete:
|
||||
tags: [Calendar]
|
||||
summary: Remove a calendar source
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema: { type: string }
|
||||
responses:
|
||||
'200':
|
||||
description: Successful operation
|
||||
'404':
|
||||
description: Source not found
|
||||
|
||||
/api/v1/calendar/events:
|
||||
get:
|
||||
tags: [Calendar]
|
||||
summary: Aggregated calendar events
|
||||
description: Fetches and merges events from all enabled sources over a time window. RRULE expansion is applied server-side.
|
||||
parameters:
|
||||
- name: start
|
||||
in: query
|
||||
schema: { type: string, format: date-time }
|
||||
- name: end
|
||||
in: query
|
||||
schema: { type: string, format: date-time }
|
||||
- name: sourceId
|
||||
in: query
|
||||
schema: { type: string }
|
||||
responses:
|
||||
'200':
|
||||
description: Events plus per-source status (a failing source does not fail the request)
|
||||
'400':
|
||||
description: Invalid window or range exceeds 400 days
|
||||
|
||||
/api/v1/calendar/radicale/provision:
|
||||
post:
|
||||
tags: [Calendar]
|
||||
summary: Auto-wire a deployed Radicale instance
|
||||
description: Generates credentials, writes the Radicale htpasswd config, and registers the calendar source for zero-config widget wiring.
|
||||
responses:
|
||||
'200':
|
||||
description: Successful operation
|
||||
'400':
|
||||
description: Invalid subdomain
|
||||
|
||||
/api/v1/calendar/discover:
|
||||
post:
|
||||
tags: [Calendar]
|
||||
summary: Probe a CalDAV URL
|
||||
description: OPTIONS + PROPFIND probe used by the settings modal "Test connection" button.
|
||||
responses:
|
||||
'200':
|
||||
description: Probe result (ok flag inside — transport errors are reported, not thrown)
|
||||
|
||||
components:
|
||||
schemas:
|
||||
SuccessResponse:
|
||||
|
||||
Reference in new issue
Block a user