香雲寺官網改版
海外實習的委託案。舊官網是一個寄放在共享主機上的 PHP 站,改一則公告都得找工程師,而多年的新聞與相簿正在默默流失。我沒有選一套方案請對方接受——兩條技術路線都蓋成可上線的完整站,讓道場實際操作後自己決定。
先把舊站救下來,才談重建
舊官網是中英雙語的 PHP 站,寄放在共享主機上。三個結構性問題:維運脆弱(任何修改都得找專業人士)、同仁無法自理(更新一則公告卡在工程師身上)、資料正在流失——多年的新聞、相簿、影片與 PDF 散在主機上,沒有一份乾淨可攜的備份。
所以第一件事不是開新專案,是先讓舊資料安全落地。自建爬取管線把全站保存成帶 frontmatter 的 Markdown;舊主機脆弱(曾出現 409),爬取用併發 1、間隔 2–5 秒、離峰執行的禮貌參數進行。
救資料不能把對方的主機救掛。過程中探測挖回 13 篇被原站移出索引、但頁面其實還在的 2021 年文章(中英共 26 頁)。那 26 頁如果沒被挖出來,就會在下一次主機出事時永遠消失。
約 4.6 GB 媒體
從索引外挖回
EXIF 方向修正
抽成獨立欄位重建
不替道場選型,把兩條路都蓋出來讓他們選
技術選型最大的風險,是幫使用者決定他們沒體驗過的東西。所以兩條首選路線都建成完整、可操作的正式站,交給道場實際摸過後台之後再定案。
Astro + TinaCMS + Cloudflare
零維運、成本趨近零;代價是前台要自建自維護。
完成到可上線 · 未被選用
Umbraco 17(.NET 10 / C#)
後台成熟、編輯體驗完整;代價是需要一台常開的伺服器與資料庫。
已部署上線於 Azure
兩軌共用同一份內容模型(content_model.md)。這是刻意設計:確保道場比較的是「後台好不好用」而不是「內容結構長怎樣」,也保證無論選哪一軌,另一軌的資料設計都不浪費。最後道場選了 B,A 軌的內容模型、爬取成果與雙語結構完全共用——沒有一份工作是白做的。
另一個決策是「會手刻,所以更知道不該手刻」:我自己刻過 CMS(Walk to Awareness 的後台就是),也因此清楚官網這種會不斷長大的內容站不該再手刻——真正的坑不是增刪改查,是富文本編輯器、多語系路由與 SEO。力氣留給版型與客製,內容層交給成熟方案。
目標是「我離開之後這個網站還能活下去」
整個網站都能在後台編輯
不只是文章——導覽列、頁尾、聯絡資訊、首頁輪播與公告全部模組化進後台,可增刪拖曳排序。新聞 231 篇「一篇一檔、中英一起編」;活動 37 篇特別處理「中英是兩張不同海報」的實況,英文頁不會跑出中文海報。
為不會寫程式的人設計
後台欄位內建操作說明、網址依內容日期自動產生(編輯者不必自己想,且改標題不會變網址)、前後台互跳按鈕(前台右下 ✎ 直接編這一頁)、一鍵啟動器、每軌一份操作手冊,並附角色分權(可設「只能編新聞活動」的帳號)。
舊連結不斷、無障礙達標
舊站 519 個 .php 網址全數 301 轉址——改版最容易被忽略、但直接決定舊連結會不會全變 404 的一件事。無障礙以自家檢測器全站掃描,WCAG 2.0 AA 修至 0 違規;相依套件一併資安升級。
結構即程式碼
語言與內容類型以程式建立、冪等、可進版控,而不是在後台點出來——這對交接很重要:接手的人看得到結構是怎麼來的,也重建得回去。密碼不留在 repo,並明確列出「還沒做的事」。
每一項規格背後都是一個判斷
| 資源 | 為什麼是這個 | 月費 |
|---|---|---|
| App Service Linux B1 |
同規格 Windows 要 $54.8,價差 4 倍;本站是 .NET 10,跨平台沒有理由不選 Linux。 | $13.1 |
| Azure SQL Basic 2 GB |
不用 SQLite:App Service 的檔案系統是 SMB 網路磁碟,SQLite 的檔案鎖在上面不可靠。這不是效能取捨,是正確性問題。 | $4.9 |
| Blob Storage 容器 media |
媒體不能放 wwwroot:每次重新部署都會覆蓋它,編輯者上傳的圖會不見。 |
~$0.1 |
| 合計 | 已備妥微軟非營利額度(每年 $2,000)申請指引,核准後實質零費用。 | ≈ $18 |
媒體同時壓成網頁用版本:4.92 GB → 1.19 GB(24%)(1920px 長邊 / JPEG q82 / 去 EXIF,2,180 張重壓、51 張原樣複製、0 失敗)。原檔另存不動——壓過的是可隨時重產的衍生物,原檔才是唯一保存檔。
上線後用自寫的巡檢腳本從中英首頁走遍所有站內連結逐一實測:584 頁、2,080 張不重複圖片,0 異常;中英各 292 頁,雙語對稱、沒有一邊漏掉。
本機沒事、上雲爆炸
後台有個「存檔後自動把同資料夾節點依日期重排」的功能。匯入是一筆一筆新增的,新的那筆幾乎必然不在正確位置——於是每存一篇就重寫整個資料夾的所有兄弟節點。231 篇新聞約等於 26,000 次額外寫入。
症狀是速度隨資料變多而遞減(13.7 張/分 → 5 張/分,而且只會愈來愈慢)。本機 SQLite 完全看不出來(單趟不到 1 ms),一搬到雲端資料庫(單趟 42 ms)就放大 40 倍以上。
修法是匯入期間暫停逐篇重排、結束後整批排一次,5 張/分回到 20 張/分。延伸判斷同樣重要:實測資料庫 DTU 只用到 10–15%,升級規格沒有用——瓶頸是「來回次數 × 網路延遲」,不是硬體。看數據而不是憑直覺加錢。
反向檢查才是關鍵
比對資料庫參照(2,258 張)與 Blob 實際檔案(2,644 張),找出 386 張孤兒媒體並在確認「同一張照片另有一份在使用中」後清除,誤刪 0 張。
但同一次比對的反方向更有價值:17 張首頁輪播大圖是「資料庫有、Blob 沒有」——線上正在破圖,而從後台看完全正常。比對要兩邊都看,只找孤兒會漏掉真正影響訪客的那一半。
前台正常、後台壞掉
自建的圖片欄位少設了 EditorUiAlias,後台顯示「找不到編輯器 UI」而無法編輯,前台卻完全正常——只從前台驗收永遠不會發現。上線後才被抓到,也因此學到一件事:驗收清單必須包含「用編輯者的帳號實際編一次」。
這個網站現在就在服務道場
雙語站台部署於 Azure,中英各 292 頁。進站是一頁語言選擇,中英兩邊各自完整——目前仍走 Azure 預設網域,自訂網域(ibps-austin.org)的 DNS 切換由道場安排中,尚未完成。
↗ 看線上網站搬遷完成後,我把整套 Python 爬取與配對管線逐函式移植、產品化為 dotnet tool —— Cornhsu.PolyMigrate,並以這次搬遷的全量真實資料當 golden 基準逐頁對答案。案子驗證了工具,工具反過來把案子踩過的每一個坑,變成有測試背書的功能。