Skip to content

feat: Cloudflare Workers deployment for Media Kit#487

Open
xiaomoziyi wants to merge 11 commits intomasterfrom
feat/cloudflare-workers-migration
Open

feat: Cloudflare Workers deployment for Media Kit#487
xiaomoziyi wants to merge 11 commits intomasterfrom
feat/cloudflare-workers-migration

Conversation

@xiaomoziyi
Copy link
Copy Markdown

Summary

  • Migrate Media Kit from Blocklet Server to Cloudflare Workers (Hono + D1 + R2)
  • Reuse original frontend source via Vite alias + shim layer (zero changes to blocklets/image-bin/src/)
  • Full feature parity: upload, gallery, folders, AI Image generation, file serving
  • Deployed and verified at https://media-kit.yexiaofang.workers.dev

Changes

New: cloudflare/ directory (Worker backend + frontend build config)

  • Hono API: presigned URL upload, multipart, R2 storage, D1 database
  • AIGNE Hub proxy for AI Image models & generation
  • Shim layer for @blocklet/ui-react, @arcblock/ux, @arcblock/did-connect-react
  • SPA fallback for client-side routing
  • Deploy docs, migration scripts, CI/CD example

Modified: packages/uploader/ (backward compatible)

  • AI Image: support both URL and base64 response formats
  • Add presigned-upload Uppy plugin
  • Add .catch() for AI Image URL fetch

TODO (post-merge)

  • Replace x-user-did header auth with CF auth SDK (shijun)
  • Restrict CORS origin after auth is ready
  • Production optimization: S3 CopyObject for large file dedup

Test plan

  • Upload files (presigned URL flow)
  • View gallery with pagination
  • Folder create/filter
  • Copy URL / Download / Delete
  • AI Image generation via AIGNE Hub
  • SPA routing (refresh on /admin)
  • Verify blocklet version unaffected (lint passes)

🤖 Generated with Claude Code

Migrate Media Kit from Blocklet Server (Express + SQLite + local disk) to
Cloudflare Workers (Hono + D1 + R2), enabling edge deployment with zero
server maintenance.

Backend (cloudflare/src/):
- Hono API server with full upload/folder/serve/status routes
- Presigned URL upload flow with multipart support
- R2 file storage with MD5 content dedup
- D1 database with Drizzle ORM
- AIGNE Hub proxy for AI Image generation
- SPA fallback for client-side routing

Frontend (cloudflare/frontend/):
- Reuses original blocklets/image-bin/src/ via Vite aliases
- Shim layer replaces @blocklet/* and @arcblock/* SDK dependencies
- window.blocklet injected in index.html for module-level access
- path-browserify polyfill for browser compatibility

Shared code changes (backward compatible):
- uploader: support both URL and base64 for AI Image results
- uploader: add presigned-upload Uppy plugin
- uploader: add .catch() for AI Image URL fetch

Deployed at: https://media-kit.yexiaofang.workers.dev

TODO:
- Replace x-user-did auth with CF auth SDK (shijun)
- Restrict CORS origin after auth is ready
- Production: use S3 CopyObject instead of R2 binding get+put for large files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 17, 2026

Image description AIGNE CodeSmith

Copy link
Copy Markdown

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description AIGNE CodeSmith

Commits Files that changed from the base of the PR and between 279ab7d and 236dcd8 commits.
Files selected (49)
  • cloudflare/.gitignore (1)
  • cloudflare/README.md (1)
  • cloudflare/docs/CHANGES.md (1)
  • cloudflare/docs/MIGRATION-PLAN.md (1)
  • cloudflare/docs/MIGRATION-REPORT.md (1)
  • cloudflare/drizzle.config.ts (1)
  • cloudflare/frontend/index.html (1)
  • cloudflare/frontend/src/App.tsx (1)
  • cloudflare/frontend/src/api.ts (1)
  • cloudflare/frontend/src/components/FolderList.tsx (1)
  • cloudflare/frontend/src/components/ImageGrid.tsx (1)
  • cloudflare/frontend/src/components/UploadButton.tsx (1)
  • cloudflare/frontend/src/main.tsx (1)
  • cloudflare/frontend/src/plugins/presigned-upload.ts (1)
  • cloudflare/frontend/src/shims/arcblock-did.ts (1)
  • cloudflare/frontend/src/shims/arcblock-ux.tsx (1)
  • cloudflare/frontend/src/shims/blocklet-js-sdk.ts (1)
  • cloudflare/frontend/src/shims/blocklet-ui-react-dashboard.tsx (1)
  • cloudflare/frontend/src/shims/blocklet-ui-react-footer.tsx (1)
  • cloudflare/frontend/src/shims/blocklet-ui-react-header.tsx (1)
  • cloudflare/frontend/src/shims/blocklet-ui-react.tsx (1)
  • cloudflare/frontend/src/shims/did-connect-react-button.tsx (1)
  • cloudflare/frontend/src/shims/did-connect-react-session.tsx (1)
  • cloudflare/frontend/tsconfig.json (1)
  • cloudflare/frontend/vite.config.ts (1)
  • cloudflare/migrations/0001_initial.sql (1)
  • cloudflare/pnpm-lock.yaml (1)
  • cloudflare/scripts/migrate-data.ts (1)
  • cloudflare/src/tests/utils.test.ts (1)
  • cloudflare/src/tests/worker.integration.ts (1)
  • cloudflare/src/db/schema.ts (1)
  • cloudflare/src/middleware/auth.ts (1)
  • cloudflare/src/routes/cleanup.ts (1)
  • cloudflare/src/routes/folders.ts (1)
  • cloudflare/src/routes/serve.ts (1)
  • cloudflare/src/routes/status.ts (1)
  • cloudflare/src/routes/unsplash.ts (1)
  • cloudflare/src/routes/upload.ts (1)
  • cloudflare/src/types.ts (1)
  • cloudflare/src/utils/hash.ts (1)
  • cloudflare/src/utils/s3.ts (1)
  • cloudflare/src/worker.ts (1)
  • cloudflare/tsconfig.json (1)
  • cloudflare/vitest.config.ts (1)
  • cloudflare/wrangler.toml (1)
  • packages/uploader/src/react/plugins/ai-image/show-panel/output/index.tsx (1)
  • packages/uploader/src/react/plugins/presigned-upload.ts (1)
  • packages/uploader/src/react/uploader.tsx (8)
  • pnpm-lock.yaml (21)
Files ignored due to filter (2)
  • cloudflare/frontend/package.json
  • cloudflare/package.json
Files not summarized due to errors (49)
  • cloudflare/drizzle.config.ts (nothing obtained from anthropic)
  • cloudflare/README.md (nothing obtained from anthropic)
  • cloudflare/docs/MIGRATION-PLAN.md (nothing obtained from anthropic)
  • cloudflare/.gitignore (nothing obtained from anthropic)
  • cloudflare/docs/CHANGES.md (nothing obtained from anthropic)
  • cloudflare/docs/MIGRATION-REPORT.md (nothing obtained from anthropic)
  • cloudflare/frontend/index.html (nothing obtained from anthropic)
  • cloudflare/frontend/src/App.tsx (nothing obtained from anthropic)
  • cloudflare/frontend/src/components/FolderList.tsx (nothing obtained from anthropic)
  • cloudflare/frontend/src/components/ImageGrid.tsx (nothing obtained from anthropic)
  • cloudflare/frontend/src/components/UploadButton.tsx (nothing obtained from anthropic)
  • cloudflare/frontend/src/api.ts (nothing obtained from anthropic)
  • cloudflare/frontend/src/main.tsx (nothing obtained from anthropic)
  • cloudflare/frontend/src/shims/arcblock-did.ts (nothing obtained from anthropic)
  • cloudflare/frontend/src/plugins/presigned-upload.ts (nothing obtained from anthropic)
  • cloudflare/frontend/src/shims/blocklet-ui-react-dashboard.tsx (nothing obtained from anthropic)
  • cloudflare/frontend/src/shims/arcblock-ux.tsx (nothing obtained from anthropic)
  • cloudflare/frontend/src/shims/blocklet-js-sdk.ts (nothing obtained from anthropic)
  • cloudflare/frontend/src/shims/blocklet-ui-react.tsx (nothing obtained from anthropic)
  • cloudflare/frontend/src/shims/blocklet-ui-react-header.tsx (nothing obtained from anthropic)
  • cloudflare/frontend/src/shims/did-connect-react-button.tsx (nothing obtained from anthropic)
  • cloudflare/frontend/tsconfig.json (nothing obtained from anthropic)
  • cloudflare/frontend/src/shims/did-connect-react-session.tsx (nothing obtained from anthropic)
  • cloudflare/frontend/src/shims/blocklet-ui-react-footer.tsx (nothing obtained from anthropic)
  • cloudflare/frontend/vite.config.ts (nothing obtained from anthropic)
  • cloudflare/migrations/0001_initial.sql (nothing obtained from anthropic)
  • cloudflare/src/tests/utils.test.ts (nothing obtained from anthropic)
  • cloudflare/pnpm-lock.yaml (nothing obtained from anthropic)
  • cloudflare/scripts/migrate-data.ts (nothing obtained from anthropic)
  • cloudflare/src/tests/worker.integration.ts (nothing obtained from anthropic)
  • cloudflare/src/db/schema.ts (nothing obtained from anthropic)
  • cloudflare/src/routes/cleanup.ts (nothing obtained from anthropic)
  • cloudflare/src/middleware/auth.ts (nothing obtained from anthropic)
  • cloudflare/src/routes/status.ts (nothing obtained from anthropic)
  • cloudflare/src/routes/folders.ts (nothing obtained from anthropic)
  • cloudflare/src/routes/serve.ts (nothing obtained from anthropic)
  • cloudflare/src/routes/unsplash.ts (nothing obtained from anthropic)
  • cloudflare/src/routes/upload.ts (nothing obtained from anthropic)
  • cloudflare/src/utils/hash.ts (nothing obtained from anthropic)
  • cloudflare/src/types.ts (nothing obtained from anthropic)
  • cloudflare/src/utils/s3.ts (nothing obtained from anthropic)
  • cloudflare/src/worker.ts (nothing obtained from anthropic)
  • cloudflare/wrangler.toml (nothing obtained from anthropic)
  • cloudflare/tsconfig.json (nothing obtained from anthropic)
  • cloudflare/vitest.config.ts (nothing obtained from anthropic)
  • packages/uploader/src/react/plugins/ai-image/show-panel/output/index.tsx (nothing obtained from anthropic)
  • packages/uploader/src/react/plugins/presigned-upload.ts (nothing obtained from anthropic)
  • packages/uploader/src/react/uploader.tsx (nothing obtained from anthropic)
  • pnpm-lock.yaml (nothing obtained from anthropic)
Files not reviewed due to errors (49)
  • cloudflare/README.md (no response)
  • cloudflare/docs/MIGRATION-PLAN.md (no response)
  • cloudflare/drizzle.config.ts (no response)
  • cloudflare/docs/MIGRATION-REPORT.md (no response)
  • cloudflare/.gitignore (no response)
  • cloudflare/docs/CHANGES.md (no response)
  • cloudflare/frontend/src/App.tsx (no response)
  • cloudflare/frontend/src/api.ts (no response)
  • cloudflare/frontend/index.html (no response)
  • cloudflare/frontend/src/components/FolderList.tsx (no response)
  • cloudflare/frontend/src/components/ImageGrid.tsx (no response)
  • cloudflare/frontend/src/components/UploadButton.tsx (no response)
  • cloudflare/frontend/src/plugins/presigned-upload.ts (no response)
  • cloudflare/frontend/src/shims/arcblock-did.ts (no response)
  • cloudflare/frontend/src/main.tsx (no response)
  • cloudflare/frontend/src/shims/arcblock-ux.tsx (no response)
  • cloudflare/frontend/src/shims/blocklet-js-sdk.ts (no response)
  • cloudflare/frontend/src/shims/blocklet-ui-react-dashboard.tsx (no response)
  • cloudflare/frontend/src/shims/blocklet-ui-react-header.tsx (no response)
  • cloudflare/frontend/src/shims/blocklet-ui-react.tsx (no response)
  • cloudflare/frontend/src/shims/did-connect-react-button.tsx (no response)
  • cloudflare/frontend/src/shims/blocklet-ui-react-footer.tsx (no response)
  • cloudflare/frontend/src/shims/did-connect-react-session.tsx (no response)
  • cloudflare/frontend/tsconfig.json (no response)
  • cloudflare/frontend/vite.config.ts (no response)
  • cloudflare/migrations/0001_initial.sql (no response)
  • cloudflare/pnpm-lock.yaml (no response)
  • cloudflare/scripts/migrate-data.ts (no response)
  • cloudflare/src/tests/utils.test.ts (no response)
  • cloudflare/src/tests/worker.integration.ts (no response)
  • cloudflare/src/db/schema.ts (no response)
  • cloudflare/src/middleware/auth.ts (no response)
  • cloudflare/src/routes/cleanup.ts (no response)
  • cloudflare/src/routes/folders.ts (no response)
  • cloudflare/src/routes/serve.ts (no response)
  • cloudflare/src/routes/status.ts (no response)
  • cloudflare/src/routes/unsplash.ts (no response)
  • cloudflare/src/routes/upload.ts (no response)
  • cloudflare/src/types.ts (no response)
  • cloudflare/src/utils/hash.ts (no response)
  • cloudflare/src/worker.ts (no response)
  • cloudflare/src/utils/s3.ts (no response)
  • cloudflare/tsconfig.json (no response)
  • cloudflare/vitest.config.ts (no response)
  • packages/uploader/src/react/plugins/ai-image/show-panel/output/index.tsx (no response)
  • cloudflare/wrangler.toml (no response)
  • packages/uploader/src/react/plugins/presigned-upload.ts (no response)
  • packages/uploader/src/react/uploader.tsx (no response)
  • pnpm-lock.yaml (no response)
Review comments generated (0)
  • Review: 0
  • LGTM: 0

提示

Image description AIGNE CodeSmith (@codesmith) 聊天

  • 回复此机器人留下的审查评论以提出后续问题。审查评论是对差异或文件的评论。
  • 通过在回复中标记 @codesmith 邀请机器人加入审查评论链。

代码建议

  • 机器人可能会提出代码建议,但在提交前请仔细审查它们,因为行号范围可能会不对齐。
  • 你可以编辑机器人做出的评论,并在建议稍有偏差时手动调整。

暂停增量审查

  • 在 PR 描述中的任何位置添加 @codesmith: ignore 以暂停机器人的进一步审查。

Code improvements:
- Multipart: use R2 binding for create/complete/abort instead of S3 API
  (zero latency, no credential dependency, only listParts still uses S3)
- proxy-put: stream request body directly to R2 instead of arrayBuffer()
  (avoids 128MB memory ceiling for large files in dev mode)
- Cleanup: delete temp R2 objects for expired single-file sessions
  (previously only multipart sessions were cleaned up)
- SVG sanitization: add <style> block stripping and xlink:href data: fix
- D1: add TODO for withSession("first-primary") write consistency
- Fix TypeScript error in direct upload FormData casting

Documentation updates:
- README: fix wrangler.toml line number reference, clarify deploy paths
- MIGRATION-REPORT: Cloudflare Pages → Workers Static Assets
- MIGRATION-PLAN: mark /api/url/import as unimplemented, add GET /api/folders
- All design docs: add "historical reference" note at top

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 17, 2026

Image description AIGNE Framework

xiaomoziyi and others added 9 commits March 17, 2026 22:16
Hub image URLs (hub.aigne.io/image-bin/uploads/...) fail to load directly
from the browser due to connection issues. Rewrite URLs in the generation
response to go through /api/image/proxy, which fetches from Hub server-side
and streams to the client. Only allows proxying URLs from the configured
AIGNE_HUB_URL to prevent open proxy abuse.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
AI generated images from AIGNE Hub are temporary and should not be cached.
Previous 24h browser cache caused stale images to appear when selecting
generated results. Also disable CF edge cache for the proxy fetch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Hub image URLs are served directly to the frontend now. The proxy endpoint
caused slow loading and stale image issues. The /api/image/proxy endpoint
is kept available but not auto-applied — can be used manually if needed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
AI Image generation:
- Download hub images to tmp/ai/ in R2 during generation
- Return local /uploads/tmp/ai/xxx.png URLs (fast, no CORS)
- Cron cleanup deletes tmp/ai/ files older than 24 hours
- User clicks "Use selected" → fetches from local R2 (instant)

Toast shim:
- Fix default export: Toast object (with success/error methods)
  was incorrectly exporting ToastProvider as default

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Changed /uploads/:filename to /uploads/* wildcard match so that
AI-generated temp images at /uploads/tmp/ai/xxx.png can be served.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ages

These packages are pure UI components with no Blocklet Server dependency,
so shims were unnecessary. Removed 13 shim files, keeping only 7 shims
for packages that truly depend on Blocklet Server runtime:

- @blocklet/js-sdk (createAxios)
- @blocklet/ui-react (Dashboard/Header/Footer/ComponentInstaller)
- @arcblock/did-connect-react (SessionProvider/ConnectButton)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Shims: clarify only @blocklet/* and did-connect-react need shims,
  @arcblock/ux and @arcblock/did use real packages
- Add AIGNE_HUB_URL to secrets documentation
- Add AI temp image cleanup (tmp/ai/, 24h) to architecture description
- Update environment differences table

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Resource tab is a Blocklet Server feature (browse installed component
resources) that has no equivalent in CF Workers. Added window.blocklet.
inCFWorkers flag to conditionally hide it. Blocklet version unaffected
(flag is undefined by default).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Updated the PresignedUploadPlugin to construct an absolute upload URL when the URL starts with a slash. This change ensures compatibility with cross-origin requests, particularly when using Cloudflare Workers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants