@dytsou/calendar-build
v2.4.0
Published
Build script for calendar HTML from template and environment variables
Readme
Calendar App
A simple calendar application that displays multiple calendar feeds using Open Web Calendar.
Features
- Display multiple calendar feeds simultaneously
- Dark theme with customizable styling
- Full-height responsive design
- URL parameters for view mode and date selection
- Liquid-glass query panel (hover the top-right corner to reveal) for changing
modeanddatewithout editing the URL manually - Clean interface without menu buttons or navigation controls
Setup
1. Configure Cloudflare Worker Secrets
Calendar URLs are now managed via Cloudflare Worker secrets for better security and centralized management.
Set up your Cloudflare Worker secrets:
# Set calendar URLs (comma-separated, can be plain or fernet:// encrypted)
wrangler secret put CALENDAR_URL
# Enter: https://calendar.google.com/calendar/ical/example%40gmail.com/public/basic.ics,https://calendar.google.com/calendar/ical/another%40gmail.com/public/basic.ics
# Set encryption key (if using fernet:// encrypted URLs)
wrangler secret put ENCRYPTION_KEY
# Enter your Fernet encryption key (base64url-encoded 32-byte key)
# Generate one with: node -e "console.log(require('crypto').randomBytes(32).toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, ''))"
# Enable the privacy-safe final-view cache with an opaque HMAC identity key
wrangler secret put CACHE_IDENTITY_SECRET
# Use a separate random secret; do not reuse a calendar URL or encryption keyGetting Google Calendar iCal URLs:
- Go to your Google Calendar settings
- Find the calendar you want to share
- Click "Integrate calendar" or "Get shareable link"
- Copy the "Public URL to iCal format" link
- URL-encode special characters (e.g.,
@becomes%40)
2. Configure Local Development (Optional)
Copy
.env.exampleto.env:cp .env.example .envEdit
.envand set your Cloudflare Worker URL (optional):WORKER_URL=https://cal-proxy.yourdomain.comIf not set, defaults to
https://open-web-calendar.hosted.quelltext.eu
3. Build the HTML File
# Option 1: Using npm/pnpm (after installing)
pnpm install
pnpm run build
# or use the global command if installed globally
calendar-build
# Option 2: Direct execution
node scripts/build.js4. Deploy and Open
- Deploy
index.htmlto your hosting service (GitHub Pages, etc.) - Open the deployed page in your browser
- The Cloudflare Worker will automatically add calendar URLs from secrets to all requests
URL Parameters
The calendar supports URL parameters for navigation:
?mode=month- Show month view?mode=week- Show week view (default)?mode=day- Show day view?date=YYYYMMDD- Navigate to a specific date (e.g.,?date=20250115)?theme=dark- Force dark theme?theme=light- Force light theme
Theme: The calendar uses your browser's color scheme preference by default. You can override it using the ?theme= URL parameter.
Query panel: A round, translucent glass Query button sits in the top-right corner. It stays invisible until you move your mouse into that corner, at which point it fades in. Click it to open a floating panel where you can set mode and date; click Apply to update the URL and reload the calendar. Other query parameters (such as theme) are preserved.
Examples:
index.html?mode=month- Month view (uses browser theme preference)index.html?mode=week&date=20250115- Week view for the week containing January 15, 2025index.html?mode=day&date=20250320- Day view for March 20, 2025index.html?theme=light- Light theme with default week viewindex.html?mode=month&theme=dark- Month view with dark theme
Final-view cache
When CACHE_IDENTITY_SECRET is configured, the Worker caches only successful, sanitized calendar pages and explicit calendar-data responses. The cache identity is an opaque HMAC digest of the configured source/filtering material and safe view parameters; viewer-provided url parameters never select a source or enter the identity.
The normal freshness window is 10 minutes. A stale hit is served immediately, refreshed in the background, and can automatically revalidate in the calendar frame. A successful representation is retained for up to 24 hours as a last-known-good fallback when an upstream refresh fails. Viewer responses remain Cache-Control: no-store and do not expose cache timestamps.
The Cache API is local to the data center handling the request, so the first request in a new location can still be a cold miss. Static assets, calendar-file downloads, authenticated/cookie-bearing requests, and unrecognized JSON resources bypass this final-view cache. If CACHE_IDENTITY_SECRET is missing, the Worker safely falls back to the live path.
Changes to CALENDAR_URL, ENCRYPTION_KEY, or USER_EMAILS select new cache identities. Bump FINAL_VIEW_CACHE_VERSION in worker.js whenever privacy filtering or response-shape rules change. Existing entries then become unreachable without exposing or purging their contents.
Set CACHE_BYPASS=1 as a temporary incident or rollback control to disable cache reads, writes, and refreshes. Rotate CACHE_IDENTITY_SECRET to move to a new opaque namespace; keep it high-entropy and separate from the calendar or encryption secrets.
Install Package
This package is published to both registries:
- npmjs.com: https://www.npmjs.com/package/@dytsou/calendar-build
- GitHub Packages: https://github.com/dytsou/cal/packages
Installation from npmjs (Default - Recommended)
Global installation:
npm install -g @dytsou/calendar-build
# or
pnpm install -g @dytsou/calendar-buildThen use anywhere:
calendar-buildLocal installation:
npm install @dytsou/calendar-build
# or
pnpm install @dytsou/calendar-buildThen use:
npx calendar-build
# or
pnpm run buildInstallation from GitHub Packages
If you prefer to install from GitHub Packages:
1. Setup GitHub Packages Authentication
Create or edit .npmrc file in your home directory:
@dytsou:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=YOUR_GITHUB_TOKEN2. Get your GitHub token:
- Go to https://github.com/settings/tokens
- Click "Generate new token" → "Generate new token (classic)"
- Select
read:packagespermission - Copy the token and replace
YOUR_GITHUB_TOKENin.npmrc
3. Install:
npm install -g @dytsou/calendar-build
# or
pnpm install -g @dytsou/calendar-buildDevelopment
Project Structure
index.html.template- Template file with placeholders for calendar URLsscripts/build.js- Build script that injects calendar URLs from.envand updates year in LICENSEscripts/encrypt-urls.js- Helper script to encrypt calendar URLs using Fernet.env- Local environment file (not committed to git).env.example- Example environment file template
Scripts
pnpm run build- Build the HTML file from templatepnpm test- Run the Worker cache and route testspnpm format- Format code with Prettierpnpm format:check- Check code formatting
Build Process
The build script:
- Reads
WORKER_URLfrom.env(optional, defaults to open-web-calendar) - Replaces
{{WORKER_URL}}placeholder in the template - Updates
{{YEAR}}placeholder with current year in LICENSE and HTML - Generates
index.htmlready for deployment
Note: Calendar URLs are now managed via Cloudflare Worker secrets (CALENDAR_URL), not in .env or the HTML file. The worker automatically adds them to all calendar requests.
Cloudflare Worker Setup
This project uses a Cloudflare Worker to manage calendar URLs securely. Calendar URLs are stored as Cloudflare Worker secrets, keeping them out of the HTML and GitHub secrets.
Setup Instructions
Copy wrangler.toml.example to wrangler.toml:
cp wrangler.toml.example wrangler.tomlThen edit
wrangler.tomland customize it for your environment (e.g., add custom domain routes).Install Wrangler CLI:
npm install -g wrangler # or pnpm add -g wranglerLogin to Cloudflare:
wrangler loginSet Calendar URLs Secret:
wrangler secret put CALENDAR_URLWhen prompted, enter your calendar URLs (comma-separated):
https://calendar.google.com/calendar/ical/example%40gmail.com/public/basic.ics,https://calendar.google.com/calendar/ical/another%40gmail.com/public/basic.icsNote: URLs can be plain or
fernet://encrypted. If using encrypted URLs, you'll also need to setENCRYPTION_KEY.Set Encryption Key Secret (if using fernet:// encrypted URLs):
wrangler secret put ENCRYPTION_KEYWhen prompted, enter your Fernet encryption key (base64url-encoded 32-byte key).
Generate one with:
node -e "console.log(require('crypto').randomBytes(32).toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, ''))"Set the Cache Identity Secret:
wrangler secret put CACHE_IDENTITY_SECRETUse a separate random value for this HMAC secret. If it is not configured, the Worker continues to serve live sanitized responses without using the shared final-view cache.
Deploy the Worker:
wrangler deployUpdate your .env file: After deployment, update the
WORKER_URLin your.envfile with your worker URL:WORKER_URL=https://your-worker.your-subdomain.workers.dev # or if using custom domain: WORKER_URL=https://your-domain.comRebuild your project:
pnpm run build
License
This project is licensed under the MIT License - see the LICENSE file for details.
