Wenlan
Wenlan is a source-backed AI knowledge base and LLM wiki for AI work: organize documents and decisions into pages you can find, inspect, and review.
翻訳待ち
No updates detected yet. GitHub releases will appear here.
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗Combine multiple iCal calendar feeds into a single subscription URL. Works with Google Calendar, Outlook, iCloud, and any public .ics feed.
Live: https://www.calendar-aggregator.online
When your calendar app fetches the feed, events are pulled from all sources in real time, deduplicated by UID, and returned as a single .ics file. Nothing is cached — only the collection metadata (name, source URLs) is stored.
Collections can be edited later at /manage/{guid} using the management token.
curl -X POST https://www.calendar-aggregator.online/api/collections \
-H "Content-Type: application/json" \
-d '{
"name": "All Calendars",
"customId": "my-cals",
"calendars": [
{ "url": "https://...", "name": "Work" },
{ "url": "webcal://...", "name": "Personal" }
]
}'
customId is optional — omit it to get an auto-generated UUID. webcal:// URLs are converted to https:// automatically.
The response includes a managementToken. It is returned only once — save it. It is required to update or delete the collection and is never included in any later response.
Feed URLs are not authenticated: anyone who has the URL can read the combined feed. This is by design, because calendar apps subscribe to a plain URL and cannot send credentials.
my-cals are short and guessable, and a feed at /api/calendar/my-cals should be assumed readable by anyone. Use a custom ID only for calendars you are happy to make public; leave it blank for anything private.Editing or deleting a collection is a separate matter and does require the management token returned once at creation.
GET /api/calendar/{guid}
Returns text/calendar. Subscribe to this URL in any calendar app.
Optional query parameters:
timeout=5000 — per-source fetch timeout in milliseconds (1000–30000, default 15000)start=YYYY-MM-DD / end=YYYY-MM-DD — only include events overlapping this inclusive date range (UTC days)past=<n><d|w|m|y> / future=<n><d|w|m|y> — rolling window relative to the request time, e.g. past=2w drops events that ended more than two weeks ago, future=3m drops events starting more than three months aheadAny combination works (start/end win over past/future for the same bound).
Examples:
# Last 2 weeks plus everything upcoming
/api/calendar/{guid}?past=2w
# Last 2 weeks and the next 3 months
/api/calendar/{guid}?past=2w&future=3m
# Only the next 30 days
/api/calendar/{guid}?past=1d&future=30d
# A fixed range, e.g. one academic term
/api/calendar/{guid}?start=2026-09-01&end=2026-12-18
# Everything from a fixed date until 1 year from now
/api/calendar/{guid}?start=2026-01-01&future=1y
Invalid values (e.g. past=2x, start=2026-02-30, or start after end) return HTTP 400.
Filtering is approximate by design: recurring series are kept whole whenever they could still occur in the window (the client expands the rule), and timezone-qualified times are compared as UTC.
Response behaviour:
ETag; conditional requests with If-None-Match get 304 Not Modified206 Partial Content400 beyond that); sources are fetched at most 5 at a timecurl -X PUT https://www.calendar-aggregator.online/api/collections/{guid} \
-H "Authorization: Bearer <managementToken>" \
-H "Content-Type: application/json" \
-d '{ "name": "New Name" }'
PUT and DELETE require the collection's management token as a bearer token. Collections created before tokens were introduced can still be modified without one. A web UI for editing lives at /manage/{guid}.
| Method | Path | Description |
|---|---|---|
GET | /api/collections/{guid} | Get a collection |
PUT | /api/collections/{guid} | Update a collection |
DELETE | /api/collections/{guid} | Delete a collection |
HEAD | /api/calendar/{guid} | Check feed exists without downloading |
GET | /api/health | Service health check |
npm install
npm run dev # http://localhost:3000
npm test # run tests
npm run type-check # TypeScript validation
npm run lint # lint
SUPABASE_URL=... # falls back to NEXT_PUBLIC_SUPABASE_URL
SUPABASE_SERVICE_ROLE_KEY=... # server-only secret; never expose to the browser
Without Supabase configured, collections fall back to in-memory storage (lost on restart).
calendar_aggregator)Calendar event data is never stored. See the Privacy Policy for details.
MIT