2026-11-03 · Projects

One codebase, two homes: dual storage and dual-platform deployment

The same astro-blog-admin code deploys to both Cloudflare Pages and EdgeOne Pages. Its image bed uses each platform's own storage, yet presents an identical interface and link rules.

One codebase, two homes: dual storage and dual-platform deployment

astro-blog-admin has an unusual deployment story: one repository, one branch, deployed at the same time to Cloudflare Pages and Tencent EdgeOne Pages, with feature parity and an identical interface on both. This article covers how its built-in image bed lands on the two platforms, and what dual-platform development gives — and teaches.

A public upload page

The site’s root path is a login-free public upload page: pick an image, upload it, get a link — nothing more. It cannot manage images or touch settings. The in-admin image page adds management: large previews, checkbox batch deletion, and a lightbox for full-size viewing. PNG/JPG uploads are converted to WebP in the browser first to save space, and upload progress is a set of thin bars at the top of the page that fade away when done.

Image URLs are root-relative only: /<prefix>/<filename>, no domain. The prefix is customizable in settings, and it does not matter which domain serves the page — change domains or platforms and every image URL already written into your posts keeps working. Uploading a file whose name already exists auto-renames it with a suffix instead of overwriting.

Dual storage: one interface, two backends

A storage abstraction layer

Upload, read, list and delete all go through one storage layer, dispatched by configuration: on Cloudflare it prefers a bound R2 bucket and falls back to the S3-compatible API; on EdgeOne it uses the platform’s own Blob store. Even the settings label is platform-aware — the second storage tier is called “Cloudflare R2” on Cloudflare and “EdgeOne Blob” on EdgeOne, while the stored configuration value stays the same and neither side affects the other.

Switching does not migrate

The two stores are independent: an image uploaded to R2 is invisible when you switch to Blob, and back again when you switch back. This “no automatic moving” is deliberate — storage is a platform asset, and the code’s job is to do things right on the platform it is currently running on.

Dual-platform deployment

Platform detection at build time

The build command is identical on both sides. At build time the code detects where it is (from marker variables the platform injects), bakes the answer into the output, and at runtime follows platform-specific storage and configuration paths. One codebase, one branch, two deployments — no near-duplicate repositories to maintain.

The lesson: variables first, code second

The biggest dual-platform trap is not in the code but in configuration: any change involving environment variables must update and read-back-verify the variables on the platform side first, and only then push code — because a push is a deployment, and reversing the order leaves production running without the variables it needs for a while. Each platform’s variables are independent, so the discipline has to be performed twice.

That closes this six-article series: the blog itself, multilingual, content shapes, the admin overview, visual editing, and the image bed with deployment. Both projects are open source — come and see what they look like now in their GitHub repositories.

2026-11-03 · プロジェクト紹介

1つのコードベース、2つの家:画像ホスティングの二重ストレージと二重プラットフォーム展開

astro-blog-admin の同じコードが Cloudflare Pages と EdgeOne Pages の両方にデプロイされています。画像ホスティングは各プラットフォーム自身のストレージを使いながら、まったく同じインターフェースとリンク規則を提供します。

1つのコードベース、2つの家:画像ホスティングの二重ストレージと二重プラットフォーム展開

astro-blog-admin には少し珍しいデプロイ方式があります。1 つのリポジトリ、1 つのブランチを、Cloudflare Pages と Tencent EdgeOne Pages の 2 プラットフォームへ同時にデプロイし、両側で機能は対等、インターフェースは同一です。本稿では内蔵の画像ホスティングが 2 プラットフォームにどう着地したか、そして二重プラットフォームが開発にもたらす利点と教訓を扱います。

画像ホスティング:アップロードして、リンクを得る、それだけ

公開アップロードページ

サイトのルートパスはログイン不要の公開アップロードページです。画像を選んで、アップロードして、リンクを得る——それだけ。管理も設定変更もできません。管理画面内の画像ページには管理機能が加わります:大きなプレビュー、チェックボックスでの一括削除、ライトボックスでの原寸表示。アップロード時、PNG/JPG は空間の節約のためにブラウザ側で先に WebP へ変換され、進捗はページ上部の細いバーが複数並ぶ形で示され、完了すると自動で消えます。

リンクの規則

画像の URL はルート相対のみ:/<接頭辞>/<ファイル名>、ドメインを含みません。接頭辞は設定でカスタマイズでき、どのドメインで開くかは関係ありません——ドメインやプラットフォームを変えても、記事に書かれた画像 URL はそのまま動きます。同名ファイルの再アップロードは上書きせず、自動で連番を付けて改名されます。

二重ストレージ:1 つのインターフェース、2 つのバックエンド

ストレージ抽象化レイヤー

アップロード、読み取り、一覧、削除はすべて統一されたストレージ層を通り、設定に応じて振り分けられます。Cloudflare 側ではバインドされた R2 バケットを優先し、なければ S3 互換 API を使い、EdgeOne 側ではプラットフォーム標準の Blob ストレージを使います。設定ページの第 2 ティアの呼び名までプラットフォーム対応で、Cloudflare では「Cloudflare R2」、EdgeOne では「EdgeOne Blob」と表示されますが、保存される設定値は同じで、両側は互いに影響しません。

切替で移行はしない

2 つのストレージは独立しています。R2 に上げた画像は Blob に切り替えると見えず、戻すとまた見えます。この「自動で引っ越さない」設計は意図的です——ストレージはプラットフォームの資産であり、コードの仕事は今動いているプラットフォームで物事を正しく行うことだからです。

二重プラットフォーム展開

ビルド時のプラットフォーム判定

ビルドコマンドは両側で同一です。コードはビルド時に、プラットフォームが注入するマーカー変数から「自分がどこにいるか」を判定し、その結果を成果物に焼き込んで、実行時にプラットフォーム別のストレージと設定読み込みへ分岐します。1 つのコードベース、1 つのブランチ、2 回のデプロイ。似たようなリポジトリを 2 つ保守する必要はありません。

教訓:変数が先、コードは後

二重プラットフォーム最大の落とし穴はコードではなく設定にあります。環境変数に関わる変更は、まずプラットフォーム側で変数を変更し、読み戻して検証してから、コードをプッシュしなければなりません——プッシュ即デプロイなので、順序が逆だと本番がしばらく変数不足の状態で動くことになります。各プラットフォームの変数は独立しているため、この規律は 2 回実行する必要があります。

この 6 本のシリーズはここまでです:ブログ本体、多言語、コンテンツの形、管理画面の概要、ビジュアル編集、画像ホスティングとデプロイ。どちらのプロジェクトもオープンソースです。GitHub リポジトリで今の姿をぜひ見てみてください。

2026-11-03 · 프로젝트 소개

코드베이스 하나, 집 둘: 이미지 호스팅 이중 스토리지와 이중 플랫폼 배포

astro-blog-admin의 같은 코드가 Cloudflare Pages와 EdgeOne Pages 양쪽에 배포됩니다. 이미지 호스팅은 각 플랫폼의 자체 스토리지를 쓰면서도 완전히 같은 인터페이스와 링크 규칙을 제공합니다.

코드베이스 하나, 집 둘: 이미지 호스팅 이중 스토리지와 이중 플랫폼 배포

astro-blog-admin에는 조금 특이한 배포 방식이 있습니다. 저장소 하나, 브랜치 하나를 Cloudflare Pages와 Tencent EdgeOne Pages 두 플랫폼에 동시에 배포해, 양쪽의 기능이 대등하고 인터페이스가 동일합니다. 이 글에서는 내장 이미지 호스팅이 두 플랫폼에 어떻게 자리 잡았는지, 그리고 이중 플랫폼이 개발에 주는 이점과 교훈을 다룹니다.

이미지 호스팅: 올리고, 링크 받고, 그게 다

공개 업로드 페이지

사이트의 루트 경로는 로그인 없는 공개 업로드 페이지입니다. 이미지를 고르고, 올리고, 링크를 받는다——그뿐입니다. 관리를 보거나 설정을 만질 수는 없습니다. 관리 화면 안의 이미지 페이지에는 관리 기능이 더해집니다. 큰 미리보기, 체크박스 일괄 삭제, 라이트박스로 원본 크기 보기. 업로드할 때 PNG/JPG는 공간 절약을 위해 브라우저에서 먼저 WebP로 변환되고, 업로드 진행 상황은 페이지 위쪽의 가는 진행 바 여러 개로 표시되며 끝나면 자동으로 사라집니다.

링크 규칙

이미지 주소는 루트 상대 경로만 씁니다. /<접두사>/<파일 이름>, 도메인은 붙지 않습니다. 접두사는 설정에서 바꿀 수 있고 어떤 도메인으로 열든 상관없습니다——도메인이나 플랫폼을 바꿔도 글에 이미 적어 둔 이미지 주소는 그대로 동작합니다. 같은 이름의 파일을 다시 올리면 덮어쓰지 않고 자동으로 번호를 붙여 이름을 바꿉니다.

이중 스토리지: 인터페이스 하나, 백엔드 둘

스토리지 추상화 계층

업로드, 읽기, 목록, 삭제는 모두 통일된 스토리지 계층을 거치며 설정에 따라 분기됩니다. Cloudflare 쪽에서는 바인딩된 R2 버킷을 우선 쓰고, 없으면 S3 호환 인터페이스를 쓰고, EdgeOne 쪽에서는 플랫폼 자체의 Blob 스토리지를 씁니다. 설정 페이지의 두 번째 스토리지 이름까지 플랫폼을 따릅니다. Cloudflare에서는 “Cloudflare R2”, EdgeOne에서는 “EdgeOne Blob”로 표시되지만 저장되는 설정 값은 같고, 양쪽은 서로 영향을 주지 않습니다.

전환해도 마이그레이션은 없다

두 스토리지는 독립적입니다. R2에 올린 이미지는 Blob으로 전환하면 보이지 않고, 되돌리면 다시 보입니다. 이 “자동으로 이사하지 않는” 설계는 의도적입니다——스토리지는 플랫폼의 자산이고, 코드의 일은 지금 돌아가고 있는 플랫폼에서 일을 제대로 처리하는 것이기 때문입니다.

이중 플랫폼 배포

빌드 시점의 플랫폼 판별

빌드 명령은 양쪽이 같습니다. 코드는 빌드 시점에 플랫폼이 주입하는 표식 변수로 “자기가 어디에 있는지”를 판별해 그 결과를 결과물에 새기고, 실행 시에는 플랫폼별 스토리지와 설정 읽기 경로를 탑니다. 코드베이스 하나, 브랜치 하나, 배포 두 번. 비슷한 저장소 두 개를 유지할 필요가 없습니다.

교훈: 변수 먼저, 코드는 나중

이중 플랫폼의 가장 큰 함정은 코드가 아니라 설정에 있습니다. 환경 변수와 관련된 변경은 반드시 플랫폼 쪽에서 변수를 먼저 바꾸고 되읽어 검증한 다음 코드를 푸시해야 합니다——푸시가 곧 배포라, 순서가 반대면 운영 환경이 한동안 변수가 부족한 채로 돌게 되기 때문입니다. 각 플랫폼의 변수는 독립적이라 이 규율을 두 번 실행해야 합니다.

여섯 편의 시리즈는 여기까지입니다. 블로그 본체, 다국어, 콘텐츠의 형태, 관리 화면 개요, 시각적 편집, 이미지 호스팅과 배포. 두 프로젝트 모두 오픈소스이니, GitHub 저장소에서 지금의 모습을 구경해 보세요.

2026-11-03 · 项目介绍

一套代码两个家:图床双存储与双平台部署

astro-blog-admin 同一份代码同时部署在 Cloudflare Pages 和 EdgeOne Pages 上;图床在两边各用自家存储,却给出一模一样的界面和链接规则。

一套代码两个家:图床双存储与双平台部署

astro-blog-admin 有一个不常见的部署方式:同一个仓库、同一个分支,同时部署在 Cloudflare Pages 和腾讯 EdgeOne Pages 两个平台上,两边功能对等、界面一致。这一篇聊它自带的图床是怎么在两个平台上落地的,以及双平台给开发带来的好处和教训。

图床:上传,拿链接,就这么简单

公开上传页

站点根路径就是免登录的公开上传页:选图、上传、拿到链接,仅此而已,不能看管理、不能改设置。后台里的图床页则多了图片管理:大图预览、批量勾选删除、点开灯箱看大图。上传时 PNG/JPG 会在浏览器里先转成 WebP 再传,省空间;上传进度是页面顶部的多条细进度条,传完自动消失。

链接规则

图片地址只用根路径形式:/<前缀>/<文件名>,不带域名。前缀可以在设置里自定义,跟用哪个域名打开无关——换域名、换平台,文章里写好的图片地址都不用动。同名文件再传会自动改名加序号,不覆盖旧图。

双存储:一套界面,两种后端

存储层抽象

图床的上传、读取、列表、删除都走统一的存储层,按配置分发:Cloudflare 侧优先用绑定的 R2 存储桶,没有绑定就走 S3 兼容接口;EdgeOne 侧用平台自带的 Blob 存储。设置页里第二档存储的叫法会按平台显示——在 Cloudflare 上叫 Cloudflare R2,在 EdgeOne 上叫 EdgeOne Blob,存的配置值不变,两边互不影响。

切换不迁移

两边存储各自独立:在 R2 里传的图,切到 Blob 是看不见的,切回来又在。这种“不自动搬家”的设计是故意的——存储是平台资产,代码只负责在当前平台把事办对。

双平台部署

构建时识别平台

构建命令两边相同,代码在构建时自己判断身在何处(靠平台注入的标志变量),把结果烧进产物,运行时再按平台走不同的存储与配置读取。一份代码、一个分支、两次部署,不用维护两套近似的仓库。

教训:先改变量,再推代码

双平台最大的坑不在代码,在配置:涉及环境变量的改动,必须先在平台侧改好变量、回读验证,再推代码——因为推送即部署,顺序反了,线上就会有一段时间在缺变量的状态下运行。两个平台的环境变量各自独立,这条纪律要执行两遍。

这个系列的六篇文章到这里就结束了:博客本体、多语言、内容形态、后台总览、可视化编辑、图床与部署。两个项目都是开源的,欢迎到 GitHub 仓库里看看它们现在的样子。

2026-11-03 · 項目介紹

一套程式碼兩個家:圖床雙儲存與雙平台部署

astro-blog-admin 同一份程式碼同時部署在 Cloudflare Pages 和 EdgeOne Pages 上;圖床在兩邊各用自家儲存,卻給出一模一樣的介面和連結規則。

一套程式碼兩個家:圖床雙儲存與雙平台部署

astro-blog-admin 有一個不常見的部署方式:同一個倉庫、同一個分支,同時部署在 Cloudflare Pages 和騰訊 EdgeOne Pages 兩個平台上,兩邊功能對等、介面一致。這一篇聊它自帶的圖床是怎麼在兩個平台上落地的,以及雙平台給開發帶來的好處和教訓。

圖床:上傳,拿連結,就這麼簡單

公開上傳頁

站點根路徑就是免登入的公開上傳頁:選圖、上傳、拿到連結,僅此而已,不能看管理、不能改設定。後台裡的圖床頁則多了圖片管理:大圖預覽、批量勾選刪除、點開燈箱看大圖。上傳時 PNG/JPG 會在瀏覽器裡先轉成 WebP 再傳,省空間;上傳進度是頁面頂部的多條細進度條,傳完自動消失。

連結規則

圖片地址只用根路徑形式:/<前綴>/<檔案名>,不帶域名。前綴可以在設定裡自訂,跟用哪個域名打開無關——換域名、換平台,文章裡寫好的圖片地址都不用動。同名檔案再傳會自動改名加序號,不覆蓋舊圖。

雙儲存:一套介面,兩種後端

儲存層抽象

圖床的上傳、讀取、列表、刪除都走統一的儲存層,按配置分發:Cloudflare 側優先用綁定的 R2 儲存桶,沒有綁定就走 S3 相容介面;EdgeOne 側用平台自帶的 Blob 儲存。設定頁裡第二檔儲存的叫法會按平台顯示——在 Cloudflare 上叫 Cloudflare R2,在 EdgeOne 上叫 EdgeOne Blob,存的配置值不變,兩邊互不影響。

切換不遷移

兩邊儲存各自獨立:在 R2 裡傳的圖,切到 Blob 是看不見的,切回來又在。這種「不自動搬家」的設計是故意的——儲存是平台資產,程式碼只負責在目前平台把事辦對。

雙平台部署

建構時識別平台

建構命令兩邊相同,程式碼在建構時自己判斷身在何處(靠平台注入的標誌變量),把結果燒進產物,執行時再按平台走不同的儲存與配置讀取。一份程式碼、一個分支、兩次部署,不用維護兩套近似的倉庫。

教訓:先改變量,再推程式碼

雙平台最大的坑不在程式碼,在配置:涉及環境變量的改動,必須先在平台側改好變量、回讀驗證,再推程式碼——因為推送即部署,順序反了,線上就會有一段時間在缺變量的狀態下執行。兩個平台的環境變量各自獨立,這條紀律要執行兩遍。

這個系列的六篇文章到這裡就結束了:部落格本體、多語言、內容形態、後台總覽、可視化編輯、圖床與部署。兩個項目都是開源的,歡迎到 GitHub 倉庫裡看看它們現在的樣子。