Monorepo for Aesthetic.Computer aesthetic.computer
at main 686 lines 18 kB view raw view rendered
1# Grab Worker - Browser Rendering Service Plan 2 3## 📋 Overview 4 5Replace the current Netlify screenshot.js function with a Cloudflare Worker that uses Cloudflare's Browser Rendering API with Durable Objects for screenshot generation and caching. 6 7**Deployment Target:** `grab.aesthetic.computer` 8 9**Status:****DEPLOYED & WORKING!** 10- Worker URL: https://aesthetic-grab.aesthetic-computer.workers.dev 11- Version: 511d31bc-c2e8-4f00-aea7-9b9d95e08733 12- Screenshots generating successfully with query parameter support 13 14**Tutorial Reference:** https://developers.cloudflare.com/browser-rendering/get-started/ 15 16--- 17 18## ✅ Completed Tasks 19 20### Phase 1: Setup & Implementation ✅ 21- [x] Created `/grab` directory structure 22- [x] Set up vault directory for secrets at `/aesthetic-computer-vault/grab/` 23- [x] Created comprehensive documentation (PLAN.md, README.md, DEPLOYMENT.md, MIGRATION.md) 24- [x] Implemented TypeScript worker following Cloudflare's official example 25- [x] Configured wrangler.toml with Browser binding and Durable Objects 26- [x] Installed dependencies (wrangler 4.42.2, @cloudflare/puppeteer latest) 27- [x] Created deployment scripts (deploy.fish) 28 29### Phase 2: Deployment & Debugging ✅ 30- [x] Fixed TypeScript compilation errors 31- [x] Added Buffer polyfill for Puppeteer compatibility 32- [x] Updated @cloudflare/puppeteer from 0.0.1 to latest (critical fix!) 33- [x] Replaced `page.waitForTimeout()` with standard `setTimeout()` 34- [x] Successfully deployed to Cloudflare Workers 35- [x] Fixed URL construction to use query parameters (`?icon=WxH`, `?preview=WxH`) 36- [x] Verified screenshots generating correctly (128x128, 256x256, 1200x630 tested) 37- [x] **Added video recording endpoint** using CDP screencast API 38 - `/video/WxH/piece.mp4?duration=N` endpoint 39 - Captures frames over 1-30 seconds 40 - Returns last frame + metadata (frame count, duration, resolution) 41 - POC working: 162 frames in 5s (~32 fps) 42 - Full video encoding requires future infrastructure 43 44### Key Fixes Applied 451. **Browser Binding Syntax**: Changed from `[[browser]]` to `[browser]` in wrangler.toml 462. **Durable Object Migration**: Added v1→v2 migration for ScreenshotDO→Browser rename 473. **Puppeteer Update**: Version 0.0.1 was returning empty screenshots; latest version works 484. **Query Parameters**: Now properly uses `?icon=128x128` or `?preview=1200x630` like original 495. **Timeout Replacement**: Modern Puppeteer doesn't have waitForTimeout, using Promise setTimeout 50 51--- 52 53## 🚧 Remaining Tasks 54 55### Phase 3: Custom Domain & DNS 🔄 56- [ ] **Configure Custom Domain** (Choose one method) 57 58 **Method 1: DNS Settings (Recommended - Faster!)** 59 - Navigate to: https://dash.cloudflare.com/a23b54e8877a833a1cf8db7765bce3ca/aesthetic.computer/dns/records 60 - Add CNAME: `grab``aesthetic-grab.aesthetic-computer.workers.dev` (Proxied) 61 - Wait: ~30 seconds 62 - See: `DNS-SETUP.md` for details 63 64 **Method 2: Workers Dashboard (Alternative)** 65 - Navigate to: Workers & Pages > aesthetic-grab > Settings > Domains & Routes 66 - Click: "Add Custom Domain" 67 - Enter: `grab.aesthetic.computer` 68 - Wait: 2-5 minutes 69 70 **Verification:** 71 - Test: `curl -I "https://grab.aesthetic.computer/icon/128x128/prompt.png"` 72 - Should return: HTTP/2 200 73 74### Phase 4: Integration & Migration ✅ 75- [x] **Update parse.mjs** to use grab worker URLs 76 - File: `/workspaces/aesthetic-computer/system/public/aesthetic.computer/lib/parse.mjs` 77 - Lines 487-494: Updated to use `https://grab.aesthetic.computer/icon/...` and `https://grab.aesthetic.computer/preview/...` 78 79- [x] **Update netlify.toml** redirects 80 - File: `/workspaces/aesthetic-computer/system/netlify.toml` 81 - Lines 285-290 & 306-310: Redirects now point to grab.aesthetic.computer 82 - Backwards compatibility maintained for existing paths 83 84- [x] **Create DEV-SETUP.md** with development workflow 85 - Local testing with `wrangler dev` 86 - Integration with netlify dev 87 - Emacs web panels setup instructions 88 - Debugging and monitoring guides 89 90- [ ] **Production Validation** 🔄 91 - Test og:image meta tags on live site 92 - Test favicon generation 93 - Monitor for errors in Cloudflare dashboard 94 - Validate across different pieces 95 96- [ ] **Cleanup** (after 1 week validation period) 97 - Remove `/system/netlify/functions/screenshot.js` 98 - Remove Puppeteer dependencies from Netlify 99 - Update documentation 100 101### Phase 5: Development Integration ✅ 102- [x] **Create Development Documentation** 103 - DEV-SETUP.md: Local testing with wrangler dev 104 - Netlify dev integration patterns 105 - Emacs web panel configuration 106 - Debugging and monitoring guides 107 108- [x] **Create Integration Documentation** 109 - INTEGRATION-SUMMARY.md: Complete overview of changes 110 - CODE-CHANGES.md: Detailed diff of modified files 111 - CUSTOM-DOMAIN-SETUP.md: Quick reference for domain setup 112 113- [ ] **Add to Dev Stack** (Optional) 114 - Create Emacs web panel configuration for grab worker 115 - Add wrangler dev command to startup scripts 116 - Test localhost screenshot generation 117 118--- 119 120## 📚 Documentation Index 121 122All documentation lives in `/workspaces/aesthetic-computer/grab/`: 123 124- **PLAN.md** (this file) - Project roadmap and progress tracking 125- **README.md** - Architecture, API reference, and technical overview 126- **DEPLOYMENT.md** - Deployment guide with custom domain instructions 127- **DEV-SETUP.md** - Local development workflow and debugging 128- **MIGRATION.md** - Migration strategy from Netlify to Cloudflare 129- **INTEGRATION-SUMMARY.md** - What's complete, what's pending, next steps 130- **CODE-CHANGES.md** - Detailed file-by-file change documentation 131- **CUSTOM-DOMAIN-SETUP.md** - Quick reference for domain configuration 132 133--- 134 135## 🎯 Current Status 136 137**Worker:** ✅ Deployed and functional 138**Integration:** ✅ Code updated, redirects configured 139**Custom Domain:** ⚠️ Pending manual setup via Dashboard 140**Testing:** ⚠️ Awaiting custom domain for full validation 141**Production:** 🕐 Ready to go live after custom domain setup 142 143--- 144 145## 🚀 Immediate Next Steps 146 1471. **Get Cloudflare API Token** (1 minute) 148 - Go to: https://dash.cloudflare.com/profile/api-tokens 149 - Create token with "Edit zone DNS" template 150 - Add to `/aesthetic-computer-vault/grab/.env` as `CLOUDFLARE_API_TOKEN` 151 1522. **Deploy with Automatic DNS** (2 minutes) 153 ```fish 154 cd /workspaces/aesthetic-computer/grab 155 ./scripts/deploy-with-dns.fish 156 ``` 157 This one command handles: 158 - Worker deployment 159 - DNS CNAME record creation 160 - DNS propagation wait 161 - Deployment verification 162 1633. **Test Production Integration** (10 minutes) 164 - Test og:images on live site 165 - Verify social media previews 166 - Check favicon loading 167 - Monitor Cloudflare dashboard 168 1694. **Begin Validation Period** (1 week) 170 - Monitor error rates 171 - Compare performance with old implementation 172 - Test with various pieces 173 - Collect user feedback 174 1755. **Cleanup** (after validation) 176 - Remove `/system/netlify/functions/screenshot.js` 177 - Remove Puppeteer dependencies 178 - Update final documentation 179 180--- 181 182## 📁 Directory Structure 183 184``` 185/workspaces/aesthetic-computer/grab/ 186├── PLAN.md # This file 187├── README.md # Documentation and usage 188├── DEPLOYMENT.md # Deployment instructions 189├── package.json # Dependencies 190├── tsconfig.json # TypeScript config 191├── wrangler.toml # Cloudflare config (gitignored) 192├── wrangler.production.toml # Production config template 193├── src/ 194│ ├── worker.ts # Main worker entry point 195│ ├── screenshot-do.ts # Durable Object for screenshot coordination 196│ ├── browser.ts # Browser rendering logic 197│ ├── cache.ts # Cache management utilities 198│ └── types.ts # TypeScript types 199├── scripts/ 200│ ├── deploy.fish # Deployment script 201│ └── setup-browser-binding.fish # Initial setup script 202└── test/ 203 └── worker.test.ts # Tests 204``` 205 206--- 207 208## 🔧 Architecture 209 210### Current Implementation (Netlify) 211- Serverless function using Puppeteer Core 212- Chrome browser via `@sparticuz/chromium` 213- Local file cache in dev mode 214- ETag-based HTTP caching 215- Accepts: `128x128` (icon), `1200x630` (og:image), `1800x900` (twitter:image) 216 217### New Implementation (Cloudflare) 218- **Worker**: Handle incoming requests, parse parameters 219- **Durable Object**: Coordinate screenshot generation per URL 220- **Browser Rendering API**: Puppeteer-compatible interface 221- **R2 Storage** (optional): Long-term screenshot cache 222- **Cache API**: Fast edge caching with ETags 223- **Browser Binding**: Native Cloudflare browser sessions 224 225### Request Flow 226``` 227Client Request 228229Worker (grab.aesthetic.computer) 230231Parse /icon/ or /preview/ path 232233Check Cache API (ETag) 234 ↓ (miss) 235Durable Object for URL 236237Browser Rendering API 238239Take Screenshot 240241Store in Cache + R2 242243Return PNG 244``` 245 246--- 247 248## 🔐 Environment Variables & Secrets 249 250### Stored in `/aesthetic-computer-vault/grab/.env` 251 252```bash 253# Cloudflare Configuration 254CLOUDFLARE_ACCOUNT_ID=<account-id> 255CLOUDFLARE_API_KEY=<api-key> 256CLOUDFLARE_EMAIL=me@jas.life 257 258# Worker Configuration 259GRAB_WORKER_NAME=aesthetic-grab 260GRAB_DOMAIN=grab.aesthetic.computer 261 262# Browser Binding Configuration 263BROWSER_BINDING_NAME=BROWSER 264BROWSER_BINDING_ID=<browser-binding-id> 265 266# Durable Object Configuration 267DO_NAMESPACE_NAME=SCREENSHOT_DO 268DO_NAMESPACE_ID=<do-namespace-id> 269 270# R2 Bucket (optional) 271R2_BUCKET_NAME=aesthetic-screenshots 272R2_BUCKET_ID=<r2-bucket-id> 273 274# Cache Configuration 275CACHE_TTL_SECONDS=3600 # 1 hour for browser cache 276CDN_CACHE_TTL_SECONDS=86400 # 24 hours for CDN cache 277DEV_CACHE_TTL_SECONDS=60 # 1 minute for dev 278 279# Screenshot Configuration 280MAX_SCREENSHOT_AGE_MS=604800000 # 7 days 281BROWSER_TIMEOUT_MS=30000 # 30 seconds 282``` 283 284### Secrets (set via `wrangler secret put`) 285- `BROWSER_API_KEY` - If needed for additional auth 286 287--- 288 289## 📦 Dependencies 290 291```json 292{ 293 "name": "@aesthetic-computer/grab", 294 "version": "1.0.0", 295 "type": "module", 296 "scripts": { 297 "dev": "wrangler dev", 298 "deploy": "./scripts/deploy.fish", 299 "deploy:production": "./scripts/deploy.fish production", 300 "type-check": "tsc --noEmit", 301 "test": "vitest" 302 }, 303 "dependencies": { 304 "@cloudflare/puppeteer": "^0.0.1", 305 "@cloudflare/workers-types": "^4.20231218.0" 306 }, 307 "devDependencies": { 308 "typescript": "^5.3.3", 309 "wrangler": "^3.80.0", 310 "vitest": "^1.0.4" 311 } 312} 313``` 314 315--- 316 317## 🚀 Deployment Process 318 319### 1. Initial Setup (One-time) 320 321```fish 322cd /workspaces/aesthetic-computer/grab 323 324# Install dependencies 325npm install 326 327# Create Browser Rendering binding 328./scripts/setup-browser-binding.fish 329 330# Create Durable Object namespace 331wrangler dispatch-namespace create SCREENSHOT_DO 332 333# Create R2 bucket (optional) 334wrangler r2 bucket create aesthetic-screenshots 335 336# Set secrets 337wrangler secret put BROWSER_API_KEY 338``` 339 340### 2. Development 341 342```fish 343# Copy secrets from vault 344cd /workspaces/aesthetic-computer/aesthetic-computer-vault 345./devault.fish 346 347# Start dev server 348cd /workspaces/aesthetic-computer/grab 349npm run dev 350``` 351 352### 3. Production Deployment 353 354```fish 355# Deploy to production 356cd /workspaces/aesthetic-computer/grab 357npm run deploy:production 358 359# Configure custom domain (one-time) 360# Via Cloudflare Dashboard: 361# Workers & Pages > aesthetic-grab > Settings > Domains & Routes 362# Add custom domain: grab.aesthetic.computer 363``` 364 365--- 366 367## 🔄 Migration from Netlify 368 369### Current Endpoint Usage 370 371**Icon Generation:** 372``` 373https://aesthetic.computer/icon/128x128/prompt~wipe.png 374``` 375 376**Preview Generation (OG Image):** 377``` 378https://aesthetic.computer/preview/1200x630/prompt~wipe.png 379``` 380 381**Preview Generation (Twitter):** 382``` 383https://aesthetic.computer/preview/1800x900/prompt~wipe.png 384``` 385 386### Migration Steps 387 3881. **Phase 1: Deploy new worker** 389 - Deploy to `grab.aesthetic.computer` 390 - Test all resolutions 391 - Verify caching behavior 392 3932. **Phase 2: Update references** 394 - Update `/system/netlify/functions/index.mjs` (main HTML generator) 395 - Update any other references to screenshot.js 396 - Add redirects in `netlify.toml` 397 3983. **Phase 3: Remove old function** 399 - Remove `/system/netlify/functions/screenshot.js` 400 - Remove Puppeteer dependencies from package.json 401 - Clean up old dev cache directory 402 403### Files to Update 404 405**Primary:** 406- `/workspaces/aesthetic-computer/system/netlify/functions/index.mjs` 407 - Lines 367-391 (icon and og:image generation) 408 409**Search for references:** 410```fish 411grep -r "screenshot\.js" /workspaces/aesthetic-computer/ 412grep -r "/icon/" /workspaces/aesthetic-computer/system/ 413grep -r "/preview/" /workspaces/aesthetic-computer/system/ 414``` 415 416**Add to netlify.toml:** 417```toml 418[[redirects]] 419 from = "/icon/*" 420 to = "https://grab.aesthetic.computer/icon/:splat" 421 status = 200 422 force = true 423 424[[redirects]] 425 from = "/preview/*" 426 to = "https://grab.aesthetic.computer/preview/:splat" 427 status = 200 428 force = true 429``` 430 431--- 432 433## 💾 Caching Strategy 434 435### Three-tier Caching 436 4371. **Browser Cache** (Client-side) 438 - `Cache-Control: public, max-age=3600` 439 - ETag-based revalidation 440 - 1 hour default 441 4422. **Cloudflare CDN Cache** 443 - `Cloudflare-CDN-Cache-Control: public, durable, max-age=86400` 444 - 24 hours for icon/preview images 445 - Edge caching globally 446 4473. **R2 Storage** (Optional long-term) 448 - Store screenshots for 7+ days 449 - Metadata: URL, resolution, timestamp 450 - Eviction policy: LRU or time-based 451 452### ETag Generation 453```typescript 454// Same as current implementation 455const etagSource = `${resolution}-${filepath}`; 456const etag = `"${crypto.subtle.digestSync('MD5', 457 new TextEncoder().encode(etagSource) 458).toString('hex')}"`; 459``` 460 461--- 462 463## 🧪 Testing Plan 464 465### Unit Tests 466- URL parsing 467- Resolution validation 468- ETag generation 469- Cache key generation 470 471### Integration Tests 472- Browser rendering 473- Screenshot capture 474- Durable Object coordination 475- Cache retrieval 476 477### E2E Tests 478- Full request/response cycle 479- Multiple resolutions 480- Cache hit/miss scenarios 481- Error handling 482 483### Load Testing 484- Concurrent requests 485- Browser session limits 486- Memory usage 487- Response times 488 489--- 490 491## 📊 Monitoring 492 493### Metrics to Track 494- Request count per resolution 495- Cache hit rate 496- Browser session duration 497- Screenshot generation time 498- Error rate 499- P50/P95/P99 latency 500 501### Logging 502- Request details (resolution, URL) 503- Cache hits/misses 504- Browser errors 505- Durable Object coordination 506 507### Alerts 508- Error rate > 5% 509- P95 latency > 10s 510- Browser session failures 511- Cache errors 512 513--- 514 515## 🔒 Security Considerations 516 5171. **Rate Limiting** 518 - Limit requests per IP 519 - Limit browser sessions per DO 520 - Prevent DoS attacks 521 5222. **URL Validation** 523 - Only allow aesthetic.computer URLs 524 - Sanitize paths 525 - Prevent SSRF 526 5273. **Resource Limits** 528 - Max screenshot size 529 - Browser timeout 530 - Memory limits per session 531 5324. **Secrets Management** 533 - Store in vault 534 - Use wrangler secrets 535 - Never commit to repo 536 537--- 538 539## 🎨 API Design 540 541### Endpoints 542 543**Icon Endpoint:** 544``` 545GET /icon/{resolution}/{piece-path}.png 546``` 547 548**Preview Endpoint:** 549``` 550GET /preview/{resolution}/{piece-path}.png 551``` 552 553### Supported Resolutions 554- `128x128` - Favicon/icon 555- `1200x630` - Open Graph image 556- `1800x900` - Twitter card image 557 558### Response Headers 559``` 560Content-Type: image/png 561Content-Length: {size} 562ETag: "{hash}" 563Cache-Control: public, max-age=3600 564Cloudflare-CDN-Cache-Control: public, durable, max-age=86400 565X-Cache-Status: HIT | MISS 566X-Screenshot-Time: {ms} 567``` 568 569### Error Responses 570 571**400 Bad Request** 572```json 573{ 574 "error": "Invalid resolution", 575 "accepted": ["128x128", "1200x630", "1800x900"] 576} 577``` 578 579**500 Internal Server Error** 580```json 581{ 582 "error": "Screenshot generation failed", 583 "message": "Browser timeout" 584} 585``` 586 587**503 Service Unavailable** 588```json 589{ 590 "error": "Browser unavailable", 591 "message": "All browser sessions in use" 592} 593``` 594 595--- 596 597## 🔮 Future Enhancements 598 5991. **Dynamic Resolution Support** 600 - Allow any resolution within limits 601 - Maintain aspect ratio 602 6032. **Video Thumbnails** 604 - Capture frames from videos 605 - Generate GIF previews 606 6073. **PDF Generation** 608 - Full-page PDFs 609 - Print stylesheets 610 6114. **Advanced Caching** 612 - Predictive pre-rendering 613 - Smart eviction policies 614 6155. **A/B Testing** 616 - Multiple screenshot variants 617 - Performance comparison 618 6196. **Analytics** 620 - Most requested pieces 621 - Popular resolutions 622 - Geographic distribution 623 624--- 625 626## 📚 References 627 628- [Cloudflare Browser Rendering](https://developers.cloudflare.com/browser-rendering/) 629- [Browser Rendering with Durable Objects](https://developers.cloudflare.com/browser-rendering/workers-bindings/browser-rendering-with-do/) 630- [Puppeteer Documentation](https://pptr.dev/) 631- [Cloudflare Workers Docs](https://developers.cloudflare.com/workers/) 632- [Durable Objects](https://developers.cloudflare.com/durable-objects/) 633- [R2 Storage](https://developers.cloudflare.com/r2/) 634 635--- 636 637## ✅ Checklist 638 639### Setup Phase 640- [ ] Create `/grab` directory structure 641- [ ] Set up TypeScript configuration 642- [ ] Install dependencies 643- [ ] Create wrangler.toml from template 644- [ ] Set up vault directory for secrets 645 646### Development Phase 647- [ ] Implement worker entry point 648- [ ] Implement Durable Object 649- [ ] Implement browser rendering logic 650- [ ] Implement caching layer 651- [ ] Add ETag support 652- [ ] Add error handling 653- [ ] Write unit tests 654- [ ] Write integration tests 655 656### Deployment Phase 657- [ ] Create Browser Rendering binding 658- [ ] Create Durable Object namespace 659- [ ] Create R2 bucket (optional) 660- [ ] Set secrets via wrangler 661- [ ] Deploy to dev environment 662- [ ] Test in dev 663- [ ] Deploy to production 664- [ ] Configure custom domain 665- [ ] Test in production 666 667### Migration Phase 668- [ ] Update index.mjs references 669- [ ] Add netlify.toml redirects 670- [ ] Test icon generation 671- [ ] Test preview generation 672- [ ] Verify caching works 673- [ ] Monitor for errors 674- [ ] Compare performance metrics 675 676### Cleanup Phase 677- [ ] Remove screenshot.js 678- [ ] Remove Puppeteer dependencies 679- [ ] Clean up dev cache directory 680- [ ] Update documentation 681- [ ] Archive old implementation 682 683--- 684 685**Status:** 📝 Planning Complete 686**Next Steps:** Begin implementation of worker.ts and screenshot-do.ts