[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
@@ -94,6 +94,8 @@ const sitesRoutes = require('../routes/sites');
|
||||
const credentialsRoutes = require('../routes/credentials');
|
||||
const arrRoutes = require('../routes/arr');
|
||||
const appsRoutes = require('../routes/apps');
|
||||
// DC-161: calendar widget backend (CalDAV/ICS sources + Radicale auto-wiring)
|
||||
const calendarRoutes = require('../routes/calendar');
|
||||
const logsRoutes = require('../routes/logs');
|
||||
const backupsRoutes = require('../routes/backups');
|
||||
const caRoutes = require('../routes/ca');
|
||||
@@ -159,6 +161,10 @@ async function createApp() {
|
||||
// Create state managers
|
||||
const servicesStateManager = new StateManager(config.SERVICES_FILE);
|
||||
const configStateManager = new StateManager(config.CONFIG_FILE);
|
||||
// DC-161: calendar sources live next to services.json (same dataDir guarantees)
|
||||
const calendarStateManager = new StateManager(
|
||||
require('path').join(require('path').dirname(config.SERVICES_FILE), 'calendar-sources.json')
|
||||
);
|
||||
|
||||
// DC-053: share-store. Single shared instance, lazy file creation on first
|
||||
// write. Lives alongside user-store/invite-store semantics (defensive
|
||||
@@ -917,6 +923,18 @@ async function createApp() {
|
||||
}));
|
||||
apiRouter.use(arrRoutes(ctx));
|
||||
apiRouter.use(appsRoutes(ctx));
|
||||
// DC-161: calendar — sources CRUD, aggregated events, Radicale auto-provision
|
||||
apiRouter.use('/calendar', calendarRoutes({
|
||||
calendarStateManager,
|
||||
credentialManager: ctx.credentialManager,
|
||||
docker: ctx.docker,
|
||||
asyncHandler: ctx.asyncHandler,
|
||||
ok: ctx.ok,
|
||||
errorResponse: ctx.errorResponse,
|
||||
log: ctx.log,
|
||||
fetchT: ctx.fetchT,
|
||||
buildServiceUrl: ctx.buildServiceUrl,
|
||||
}));
|
||||
apiRouter.use('/openclaw', openClawRoutes(ctx));
|
||||
apiRouter.use(logsRoutes({
|
||||
asyncHandler: ctx.asyncHandler,
|
||||
|
||||
Reference in new issue
Block a user