<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Tavric&apos;s Blog</title><description>Research · Engineering · Notes</description><link>https://tavricccc.github.io/</link><language>en-us</language><item><title>Hynan 介紹</title><link>https://tavricccc.github.io/blog/hynan-%E4%BB%8B%E7%B4%B9/</link><guid isPermaLink="true">https://tavricccc.github.io/blog/hynan-%E4%BB%8B%E7%B4%B9/</guid><description>App 介紹</description><pubDate>Sun, 16 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;app-介紹&quot;&gt;App 介紹&lt;/h1&gt;
&lt;p&gt;Hynan 是一個以每日工作流為核心的任務管理 App，適合用來整理待辦、安排日期、撰寫每日筆記，並透過標籤、工作區與 Google Calendar 同步來管理不同情境下的工作內容。&lt;/p&gt;
&lt;p&gt;這個 App 使用 Vue 3、Pinia、Vue Router、Vite、Supabase Auth、Supabase Postgres 與 Supabase Edge Functions 開發。前端支援 PWA，可以安裝到桌面或手機使用；後端透過 Supabase 儲存使用者資料，並使用 RLS 保護每位使用者只能讀寫自己的資料。&lt;/p&gt;
&lt;h1 id=&quot;核心功能&quot;&gt;核心功能&lt;/h1&gt;
&lt;h2 id=&quot;google-登入&quot;&gt;Google 登入&lt;/h2&gt;
&lt;p&gt;使用者可以透過 Google 帳號登入。登入後，任務、標籤、每日筆記與工作區資料會與 Supabase 帳號綁定。&lt;/p&gt;
&lt;h2 id=&quot;demo-模式&quot;&gt;Demo 模式&lt;/h2&gt;
&lt;p&gt;App 提供 Demo 路由，使用者可以在不登入的情況下體驗主要功能。Demo 資料保存在本機，不會寫入正式 Supabase 資料庫。&lt;/p&gt;
&lt;h2 id=&quot;inbox-任務管理&quot;&gt;Inbox 任務管理&lt;/h2&gt;
&lt;p&gt;Inbox 用來集中管理所有尚未完成的任務。任務會依狀態分組，例如無日期、逾期、今天與未來，方便快速判斷接下來要處理什麼。&lt;/p&gt;
&lt;h2 id=&quot;每日任務&quot;&gt;每日任務&lt;/h2&gt;
&lt;p&gt;每日頁面可以查看指定日期的任務。使用者可以替任務設定日期、時間、時區與預估時長，並在同一天的工作流中集中處理。&lt;/p&gt;
&lt;h2 id=&quot;每日筆記&quot;&gt;每日筆記&lt;/h2&gt;
&lt;p&gt;Journal 頁面支援每日筆記。每一天都有自己的筆記內容，適合記錄日誌、會議摘要、工作回顧或臨時想法。&lt;/p&gt;
&lt;h2 id=&quot;標籤分類&quot;&gt;標籤分類&lt;/h2&gt;
&lt;p&gt;任務可以加入多個標籤。使用者可以建立、編輯、刪除標籤，並從標籤頁面查看某個標籤底下的所有任務。&lt;/p&gt;
&lt;h2 id=&quot;多-workspace&quot;&gt;多 Workspace&lt;/h2&gt;
&lt;p&gt;Hynan 支援多 workspace。每位使用者會有一個 main workspace，也可以建立其他 workspace，用來區分不同專案、生活領域或工作情境。&lt;/p&gt;
&lt;h2 id=&quot;任務詳情面板&quot;&gt;任務詳情面板&lt;/h2&gt;
&lt;p&gt;點選任務後可以開啟詳情面板，編輯任務標題、完成狀態、日期、時間、時長與標籤。&lt;/p&gt;
&lt;h2 id=&quot;google-calendar-同步&quot;&gt;Google Calendar 同步&lt;/h2&gt;
&lt;p&gt;Hynan 可以連接 Google Calendar。有日期的任務會同步成 Google Calendar 事件；沒有日期的 Inbox 任務會留在 Hynan，不建立日曆事件。&lt;/p&gt;
&lt;p&gt;主 workspace 會使用名為 Hynan 的 Google Calendar，其他 workspace 會使用 workspace 名稱建立或對應專用行事曆。&lt;/p&gt;
&lt;h2 id=&quot;離線與本機快取&quot;&gt;離線與本機快取&lt;/h2&gt;
&lt;p&gt;App 有本機快取與離線佇列設計。網路不穩時可以先保留本機資料，恢復連線後再同步到 Supabase。&lt;/p&gt;
&lt;h2 id=&quot;即時同步&quot;&gt;即時同步&lt;/h2&gt;
&lt;p&gt;Supabase Realtime 會監聽 workspaces、tasks、tags、daily_entries 等資料表，讓資料更新能更即時反映在 App 中。&lt;/p&gt;
&lt;h2 id=&quot;pwa-安裝&quot;&gt;PWA 安裝&lt;/h2&gt;
&lt;p&gt;Hynan 支援 PWA。正式部署後，使用者可以透過瀏覽器安裝到桌面或手機，獲得接近原生 App 的使用體驗。&lt;/p&gt;
&lt;h1 id=&quot;主要頁面&quot;&gt;主要頁面&lt;/h1&gt;
&lt;h2 id=&quot;login&quot;&gt;Login&lt;/h2&gt;
&lt;p&gt;登入頁，提供 Google 登入與進入 Demo 模式。&lt;/p&gt;
&lt;h2 id=&quot;inbox&quot;&gt;Inbox&lt;/h2&gt;
&lt;p&gt;集中查看尚未完成的任務，包含無日期、逾期、今天與未來任務。&lt;/p&gt;
&lt;h2 id=&quot;daily&quot;&gt;Daily&lt;/h2&gt;
&lt;p&gt;查看某一天的任務，也可以透過日期切換不同日期。&lt;/p&gt;
&lt;h2 id=&quot;journal&quot;&gt;Journal&lt;/h2&gt;
&lt;p&gt;撰寫與查看每日筆記。&lt;/p&gt;
&lt;h2 id=&quot;tags&quot;&gt;Tags&lt;/h2&gt;
&lt;p&gt;查看所有標籤，管理標籤資料。&lt;/p&gt;
&lt;h2 id=&quot;tag-detail&quot;&gt;Tag Detail&lt;/h2&gt;
&lt;p&gt;查看指定標籤底下的任務。&lt;/p&gt;
&lt;h1 id=&quot;資料表&quot;&gt;資料表&lt;/h1&gt;
&lt;h2 id=&quot;workspaces&quot;&gt;workspaces&lt;/h2&gt;
&lt;p&gt;儲存使用者的工作區。每位使用者會有一個 main workspace，也可以新增其他 workspace。&lt;/p&gt;
&lt;h2 id=&quot;tasks&quot;&gt;tasks&lt;/h2&gt;
&lt;p&gt;儲存任務資料，包含標題、完成狀態、日期、時間、標籤、排序、workspace、Google Calendar 同步狀態等。&lt;/p&gt;
&lt;h2 id=&quot;tags-1&quot;&gt;tags&lt;/h2&gt;
&lt;p&gt;儲存標籤資料，包含標籤名稱、顏色與所屬 workspace。&lt;/p&gt;
&lt;h2 id=&quot;daily_entries&quot;&gt;daily_entries&lt;/h2&gt;
&lt;p&gt;儲存每日筆記內容。&lt;/p&gt;
&lt;h2 id=&quot;google_calendar_connections&quot;&gt;google_calendar_connections&lt;/h2&gt;
&lt;p&gt;儲存使用者 Google Calendar 連線狀態與加密後的 refresh token。&lt;/p&gt;
&lt;h2 id=&quot;google_calendar_workspace_calendars&quot;&gt;google_calendar_workspace_calendars&lt;/h2&gt;
&lt;p&gt;儲存每個 workspace 對應的 Google Calendar。&lt;/p&gt;
&lt;h2 id=&quot;google_calendar_sync_queue&quot;&gt;google_calendar_sync_queue&lt;/h2&gt;
&lt;p&gt;儲存待同步到 Google Calendar 的任務變更佇列。&lt;/p&gt;</content:encoded><category>Hynan</category></item><item><title>Wordat 介紹</title><link>https://tavricccc.github.io/blog/wordat-%E4%BB%8B%E7%B4%B9/</link><guid isPermaLink="true">https://tavricccc.github.io/blog/wordat-%E4%BB%8B%E7%B4%B9/</guid><description>Wordat 是一個 Local-first 的英文單字學習本，適合用來建立個人字庫、整理單字意思、例句與分類，並透過搜尋、篩選、排序和統計快速回顧學習狀態。</description><pubDate>Sun, 16 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Wordat 是一個 Local-first 的英文單字學習本，適合用來建立個人字庫、整理單字意思、例句與分類，並透過搜尋、篩選、排序和統計快速回顧學習狀態。&lt;/p&gt;
&lt;p&gt;此 App 以 Vite、React、TypeScript 和 Tailwind CSS 開發，資料預設儲存在瀏覽器的 localStorage，不需要登入或後端資料庫即可使用。專案也具備 PWA 與離線快取能力，部署後可在支援的瀏覽器中安裝成接近原生 App 的使用體驗。&lt;/p&gt;
&lt;h1 id=&quot;核心特色&quot;&gt;核心特色&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Local-first 資料儲存：單字與分類存在使用者本機瀏覽器，操作快速，不依賴伺服器。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;PWA 離線支援：透過 Service Worker 快取 App 資源，已載入後可支援離線使用與更新提示。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;單字 CRUD：可新增、編輯、刪除單字。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;一字多義：同一個英文單字可保存多組詞性、中文意思、例句與分類。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;重複單字處理：新增已存在單字時，可選擇取代既有字義、加入新字義，或只累計重複次數。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;分類管理：可新增、編輯、刪除自訂分類，並為分類配置顏色。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;搜尋與篩選：支援全域搜尋、分類篩選、A-Z 首字母篩選。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;排序功能：可依 A-Z、最近新增、重複次數排序。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;TTS 發音播放：使用瀏覽器語音合成功能播放英文單字發音。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;儀表板統計：顯示總單字量、累計重複次數、最不精熟單字，以及概覽字卡。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;單字清單：以表格化清單呈現單字、詞性、意思、例句、重複次數與操作按鈕。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;備份匯出與匯入：可匯出 JSON 備份檔，也可匯入既有備份合併資料。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;深色 / 淺色模式：支援主題切換，並記住使用者偏好。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;響應式介面：桌面使用側邊欄，手機使用抽屜式導覽。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;主要畫面&quot;&gt;主要畫面&lt;/h1&gt;
&lt;h2 id=&quot;單字清單&quot;&gt;單字清單&lt;/h2&gt;
&lt;p&gt;單字清單是主要工作區，提供新增單字、搜尋、分類篩選、字母篩選、排序、分頁、編輯與刪除操作。每筆單字可包含多個字義，每個字義可獨立設定詞性、意思、例句與分類。&lt;/p&gt;
&lt;h2 id=&quot;儀表板&quot;&gt;儀表板&lt;/h2&gt;
&lt;p&gt;儀表板用於快速掌握目前字庫狀態，包含總單字量、累計重複次數、最不精熟單字，並以字卡方式展示目前篩選條件下的單字概覽。&lt;/p&gt;
&lt;h2 id=&quot;分類管理&quot;&gt;分類管理&lt;/h2&gt;
&lt;p&gt;分類管理可建立自己的學習分類，例如 TOEIC、日常、商務、學校、易混淆字等。分類可被套用到單字或單一字義上，方便後續篩選與複習。&lt;/p&gt;
&lt;h2 id=&quot;備份與還原&quot;&gt;備份與還原&lt;/h2&gt;
&lt;p&gt;Wordat 的資料存在瀏覽器本機，因此建議定期使用匯出功能保存 JSON 備份。更換瀏覽器、清除網站資料或換裝置時，可使用匯入功能恢復或合併資料。&lt;/p&gt;
&lt;h1 id=&quot;技術架構&quot;&gt;技術架構&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;前端框架：React&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;開發工具：Vite&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;語言：TypeScript&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;樣式：Tailwind CSS&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;PWA：vite-plugin-pwa&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;資料儲存：localStorage&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;語音播放：Web Speech API&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;使用限制&quot;&gt;使用限制&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;資料預設只存在目前瀏覽器與目前網域，不會自動同步到其他裝置。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果清除瀏覽器網站資料，localStorage 內的單字資料可能會被刪除。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;TTS 發音品質與可用語音取決於使用者的瀏覽器與作業系統。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;離線功能需先成功開啟過 App，讓瀏覽器完成必要資源快取。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Wordat</category></item><item><title>Tabin 介紹</title><link>https://tavricccc.github.io/blog/tabin-%E4%BB%8B%E7%B4%B9/</link><guid isPermaLink="true">https://tavricccc.github.io/blog/tabin-%E4%BB%8B%E7%B4%B9/</guid><description>Tabin 是一個以新分頁為使用情境設計的書籤管理 App。它把常用網站整理成資料夾和圖示網格，讓使用者可以用更接近桌面啟動器的方式管理連結。</description><pubDate>Sun, 16 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Tabin 是一個以新分頁為使用情境設計的書籤管理 App。它把常用網站整理成資料夾和圖示網格，讓使用者可以用更接近桌面啟動器的方式管理連結。&lt;/p&gt;
&lt;h1 id=&quot;主要用途&quot;&gt;主要用途&lt;/h1&gt;
&lt;p&gt;Tabin 適合用來整理日常工作、學習、專案或個人常用連結。使用者可以建立資料夾，把書籤分門別類，並在瀏覽器新分頁中快速開啟常用網站。&lt;/p&gt;
&lt;h1 id=&quot;核心功能&quot;&gt;核心功能&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;書籤管理：新增、編輯、刪除書籤。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;資料夾管理：新增、編輯、刪除資料夾。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;分類檢視：可切換「所有」或指定資料夾查看書籤。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;圖示網格：書籤以 favicon 圖示和名稱呈現，方便快速辨識。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;favicon 來源 fallback：會依序嘗試 Apple touch icon、SVG favicon、ICO favicon、DuckDuckGo 與 Google favicon 來源。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;拖曳排序：書籤可以在目前檢視中拖曳排序。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;匯入/匯出備份：可匯出 JSON 備份，也可匯入備份恢復資料。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;本機資料保存：App 資料儲存在瀏覽器本機，不需要後端服務。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Chrome 新分頁導向：&lt;code&gt;extension&lt;/code&gt; 資料夾提供一個簡單 Chrome extension，可把新分頁導向部署後的 Tabin 網址。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;技術架構&quot;&gt;技術架構&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;前端框架：React&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;建置工具：Vite&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;語言：TypeScript&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;圖示：lucide-react&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;資料儲存：瀏覽器 localStorage&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;部署目標：Vercel 或其他靜態網站平台&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;資料與隱私&quot;&gt;資料與隱私&lt;/h1&gt;
&lt;p&gt;Tabin 預設不需要登入，也不會把書籤資料送到伺服器。書籤、資料夾與設定會存在使用者目前瀏覽器的 localStorage。若更換裝置或清除瀏覽器資料，請先使用匯出功能備份。&lt;/p&gt;
&lt;h1 id=&quot;適合的使用方式&quot;&gt;適合的使用方式&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;作為個人的瀏覽器新分頁首頁。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;管理工作常用工具、文件、服務後台和專案連結。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;透過 JSON 備份在不同環境間搬移書籤資料。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;部署成公開或私人 Vercel 網址，再搭配 Chrome extension 使用。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tabin</category></item><item><title>部署教學</title><link>https://tavricccc.github.io/blog/%E9%83%A8%E7%BD%B2%E6%95%99%E5%AD%B8/</link><guid isPermaLink="true">https://tavricccc.github.io/blog/%E9%83%A8%E7%BD%B2%E6%95%99%E5%AD%B8/</guid><description>Fork GitHub repo</description><pubDate>Sun, 16 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;1-fork-github-repo&quot;&gt;1. Fork GitHub repo&lt;/h1&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;步驟&lt;/th&gt;&lt;th&gt;操作&lt;/th&gt;&lt;th&gt;推薦&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;Fork 這個 repo 到自己的 GitHub&lt;/td&gt;&lt;td&gt;用要部署的 GitHub 帳號&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;確認 repo 有 &lt;code&gt;main&lt;/code&gt; / &lt;code&gt;dev&lt;/code&gt; 分支&lt;/td&gt;&lt;td&gt;之後 push 會觸發 GitHub Actions&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;先不要急著 deploy&lt;/td&gt;&lt;td&gt;先把 Firebase、GCP、IAM、Secrets 設好&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h1 id=&quot;2-建-firebase--google-cloud-project&quot;&gt;2. 建 Firebase / Google Cloud project&lt;/h1&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;步驟&lt;/th&gt;&lt;th&gt;操作&lt;/th&gt;&lt;th&gt;推薦&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;在 Google Cloud Console 建 project&lt;/td&gt;&lt;td&gt;Project ID 用正式、短、好辨識的名字&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;綁定 Billing&lt;/td&gt;&lt;td&gt;Functions / Cloud Build / Artifact Registry 會需要&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;到 Firebase Console，把 Firebase 加到同一個 project&lt;/td&gt;&lt;td&gt;不要另開不同 project&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;建立 Firebase Web App&lt;/td&gt;&lt;td&gt;之後要複製 config 到 GitHub Secrets&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h1 id=&quot;3-firebase-console-要自己建立或啟用的項目&quot;&gt;3. Firebase Console 要自己建立或啟用的項目&lt;/h1&gt;



































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;項目&lt;/th&gt;&lt;th&gt;操作&lt;/th&gt;&lt;th&gt;推薦&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Authentication&lt;/td&gt;&lt;td&gt;啟用 Google provider&lt;/td&gt;&lt;td&gt;Authorized domains 加上正式 Hosting 網域&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Firestore Database&lt;/td&gt;&lt;td&gt;建 Native mode database&lt;/td&gt;&lt;td&gt;台灣使用者建議 &lt;code&gt;asia-east1&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cloud Storage for Firebase&lt;/td&gt;&lt;td&gt;建預設 bucket&lt;/td&gt;&lt;td&gt;要 Always Free 選 &lt;code&gt;us-central1&lt;/code&gt;、&lt;code&gt;us-east1&lt;/code&gt;、&lt;code&gt;us-west1&lt;/code&gt;；要台灣延遲才選亞洲&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Hosting&lt;/td&gt;&lt;td&gt;啟用 Firebase Hosting&lt;/td&gt;&lt;td&gt;用 default site 即可&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Functions&lt;/td&gt;&lt;td&gt;啟用 Cloud Functions for Firebase&lt;/td&gt;&lt;td&gt;跟同一個 Firebase project 綁定&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h1 id=&quot;4-google-cloud-console-要啟用的-api&quot;&gt;4. Google Cloud Console 要啟用的 API&lt;/h1&gt;
&lt;p&gt;API&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Firebase Management API&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Firebase Hosting API&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Firebase Rules API&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Identity Toolkit API&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Cloud Firestore API&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Cloud Storage API&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Cloud Functions API&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Cloud Build API&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Artifact Registry API&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Cloud Run Admin API&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Eventarc API&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Pub/Sub API&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Cloud Scheduler API&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Cloud Tasks API&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Secret Manager API&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;IAM Credentials API&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Service Usage API&lt;/p&gt;
&lt;hr&gt;
&lt;h1 id=&quot;5-github-actions-deploy-account&quot;&gt;5. GitHub Actions deploy account&lt;/h1&gt;
&lt;p&gt;Firebase Hosting GitHub integration 會建立 GitHub Actions 用的 service account，也可以自己手動建一個。這個專案建議直接用同一個 deploy account，然後補下面權限。&lt;/p&gt;





































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;權限&lt;/th&gt;&lt;th&gt;角色&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Firebase 管理&lt;/td&gt;&lt;td&gt;&lt;code&gt;roles/firebase.admin&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Hosting&lt;/td&gt;&lt;td&gt;&lt;code&gt;roles/firebasehosting.admin&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Firestore rules&lt;/td&gt;&lt;td&gt;&lt;code&gt;roles/firebaserules.admin&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Firestore indexes&lt;/td&gt;&lt;td&gt;&lt;code&gt;roles/datastore.indexAdmin&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Storage&lt;/td&gt;&lt;td&gt;&lt;code&gt;roles/storage.admin&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Functions&lt;/td&gt;&lt;td&gt;&lt;code&gt;roles/cloudfunctions.admin&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cloud Run&lt;/td&gt;&lt;td&gt;&lt;code&gt;roles/run.admin&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cloud Build&lt;/td&gt;&lt;td&gt;&lt;code&gt;roles/cloudbuild.builds.editor&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Artifact Registry&lt;/td&gt;&lt;td&gt;&lt;code&gt;roles/artifactregistry.admin&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Eventarc&lt;/td&gt;&lt;td&gt;&lt;code&gt;roles/eventarc.admin&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Pub/Sub&lt;/td&gt;&lt;td&gt;&lt;code&gt;roles/pubsub.admin&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Scheduler&lt;/td&gt;&lt;td&gt;&lt;code&gt;roles/cloudscheduler.admin&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Tasks&lt;/td&gt;&lt;td&gt;&lt;code&gt;roles/cloudtasks.admin&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Secret Manager&lt;/td&gt;&lt;td&gt;&lt;code&gt;roles/secretmanager.admin&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;使用 runtime service account&lt;/td&gt;&lt;td&gt;&lt;code&gt;roles/iam.serviceAccountUser&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h1 id=&quot;6-functions-runtime-account&quot;&gt;6. Functions runtime account&lt;/h1&gt;
&lt;p&gt;到 Cloud Functions 或 Cloud Run 詳情頁確認 runtime service account，然後給它下面權限。&lt;/p&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;權限&lt;/th&gt;&lt;th&gt;角色&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Firestore&lt;/td&gt;&lt;td&gt;&lt;code&gt;roles/datastore.user&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Storage object&lt;/td&gt;&lt;td&gt;&lt;code&gt;roles/storage.objectAdmin&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Secret 讀取&lt;/td&gt;&lt;td&gt;&lt;code&gt;roles/secretmanager.secretAccessor&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cloud Tasks enqueue&lt;/td&gt;&lt;td&gt;&lt;code&gt;roles/cloudtasks.enqueuer&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;清舊 Secret versions&lt;/td&gt;&lt;td&gt;&lt;code&gt;roles/secretmanager.admin&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h1 id=&quot;7-github-secrets&quot;&gt;7. GitHub Secrets&lt;/h1&gt;
&lt;p&gt;到 GitHub repo 的 Settings -&gt; Secrets and variables -&gt; Actions 新增：&lt;/p&gt;





























































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Secret&lt;/th&gt;&lt;th&gt;值&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;FIREBASE_PROJECT_ID&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Firebase / GCP project id&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;FIREBASE_SERVICE_ACCOUNT&lt;/code&gt;&lt;/td&gt;&lt;td&gt;GitHub Actions deploy service account JSON&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;VITE_APP_TITLE&lt;/code&gt;&lt;/td&gt;&lt;td&gt;App 標題&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;VITE_ALLOWED_DOMAIN&lt;/code&gt;&lt;/td&gt;&lt;td&gt;校內 email 網域&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;VITE_ADMIN_EMAILS&lt;/code&gt;&lt;/td&gt;&lt;td&gt;管理員 email，逗號分隔&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;VITE_FIREBASE_API_KEY&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Firebase Web App config&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;VITE_FIREBASE_AUTH_DOMAIN&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Firebase Web App config&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;VITE_FIREBASE_PROJECT_ID&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Firebase Web App config&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;VITE_FIREBASE_APP_ID&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Firebase Web App config&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;VITE_FIREBASE_STORAGE_BUCKET&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Firebase Web App config&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;VITE_FIREBASE_MESSAGING_SENDER_ID&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Firebase Web App config&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;NOTION_DATABASE_ID&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Notion database id&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;NOTION_TOKEN&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Notion integration token&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h1 id=&quot;8-部署順序&quot;&gt;8. 部署順序&lt;/h1&gt;

















































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;順序&lt;/th&gt;&lt;th&gt;操作&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;Fork repo&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;建 Google Cloud project 並綁 Billing&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;加 Firebase project&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;建 Firebase Web App&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;啟用 Auth、Firestore、Storage、Hosting、Functions&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;啟用 Google Cloud API&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;7&lt;/td&gt;&lt;td&gt;建或確認 GitHub Actions deploy service account&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;設 IAM&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;9&lt;/td&gt;&lt;td&gt;設 GitHub Secrets&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;push 到 &lt;code&gt;dev&lt;/code&gt; 或 &lt;code&gt;main&lt;/code&gt;，讓 GitHub Actions 部署&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h1 id=&quot;官方參考&quot;&gt;官方參考&lt;/h1&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;主題&lt;/th&gt;&lt;th&gt;連結&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Firebase CLI&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://firebase.google.com/docs/cli&quot;&gt;firebase.google.com/docs/cli&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Hosting GitHub integration&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://firebase.google.com/docs/hosting/github-integration&quot;&gt;firebase.google.com/docs/hosting/github-integration&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cloud Storage Always Free&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://cloud.google.com/free/docs/free-cloud-features#cloud-storage&quot;&gt;cloud.google.com/free/docs/free-cloud-features#cloud-storage&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cloud Functions IAM&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://cloud.google.com/functions/docs/concepts/iam&quot;&gt;cloud.google.com/functions/docs/concepts/iam&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;GitHub Actions Secrets&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets&quot;&gt;docs.github.com/…/use-secrets-in-github-actions&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;</content:encoded><category>SRP</category></item><item><title>Wordmem 介紹</title><link>https://tavricccc.github.io/blog/wordmem-%E4%BB%8B%E7%B4%B9/</link><guid isPermaLink="true">https://tavricccc.github.io/blog/wordmem-%E4%BB%8B%E7%B4%B9/</guid><description>App 介紹</description><pubDate>Sun, 16 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;app-介紹&quot;&gt;App 介紹&lt;/h1&gt;
&lt;p&gt;Wordmem 是一個以單字集為核心的英文單字練習 App，適合把課本、考試範圍、自訂生字或 AI 產生的單字資料整理成可反覆練習的題庫。&lt;/p&gt;
&lt;p&gt;App 採用 Vue 3、Pinia、Vite 與 Tailwind CSS 建置，資料主要儲存在瀏覽器本機，並提供 ZIP 匯出匯入與 Google Drive 手動備份。使用者不需要登入帳號也能建立與練習單字集；只有使用 Google Drive 備份功能時，才需要授權 Google 帳號。&lt;/p&gt;
&lt;h1 id=&quot;核心特色&quot;&gt;核心特色&lt;/h1&gt;
&lt;h2 id=&quot;單字集管理&quot;&gt;單字集管理&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;建立多個單字集，依照課程、考試、主題或難度分類。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;每個單字項目可包含：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;英文單字&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;詞性&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;中文字義&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;英文例句&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;選擇題題幹&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;選項與正確答案&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可編輯既有單字集與單字內容。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可刪除不再需要的單字集。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;首頁會顯示目前擁有的單字集數量與總單字數。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;ai-匯入單字集&quot;&gt;AI 匯入單字集&lt;/h2&gt;
&lt;p&gt;Wordmem 內建 AI 匯入流程，協助使用者把單字清單轉成 App 可讀取的 JSON 格式。&lt;/p&gt;
&lt;p&gt;流程如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;在 App 內輸入想學習的單字。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;選擇難度。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;複製 App 產生的 AI 指令。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;貼到 ChatGPT、Claude 或其他 AI 平台。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;將 AI 回傳的 JSON 貼回 Wordmem。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;預覽並建立新的單字集。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;難度目前分為：&lt;/p&gt;





















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;難度&lt;/th&gt;&lt;th&gt;適用情境&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;簡單&lt;/td&gt;&lt;td&gt;基礎單字、初學複習&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;中等&lt;/td&gt;&lt;td&gt;段考、一般測驗&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;困難&lt;/td&gt;&lt;td&gt;大考、進階練習&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;單字卡模式&quot;&gt;單字卡模式&lt;/h2&gt;
&lt;p&gt;單字卡模式適合快速瀏覽與記憶。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;逐張查看單字。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;顯示字義、詞性與例句。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;支援接續上次未完成的進度。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;適合正式測驗前快速複習。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;選擇題練習&quot;&gt;選擇題練習&lt;/h2&gt;
&lt;p&gt;選擇題練習會根據單字集內的題目與選項進行測驗。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;可自訂本輪練習題數。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;題目順序會重新排列。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;每題可選擇答案，也可略過。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;完成後產生測驗結果。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;錯題可再次複習。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;拼字測試&quot;&gt;拼字測試&lt;/h2&gt;
&lt;p&gt;拼字測試用來訓練英文單字輸出能力。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;根據提示輸入完整英文單字。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;判定時會忽略大小寫與前後空白。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;留空送出會被視為略過。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;完成後顯示正確答案與作答狀態。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可針對錯題再次練習。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;測驗結果與錯題複習&quot;&gt;測驗結果與錯題複習&lt;/h2&gt;
&lt;p&gt;每輪練習完成後，Wordmem 會整理結果。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;顯示總題數、答對題數、錯題數與分數。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;標示正確、錯誤與略過題目。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;顯示使用者答案與正確答案。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可針對錯題重新練習。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可在選擇題與拼字模式之間切換。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可複製錯題解析指令，貼到 AI 平台取得補充說明。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;本機資料保存&quot;&gt;本機資料保存&lt;/h2&gt;
&lt;p&gt;Wordmem 主要以瀏覽器本機儲存資料。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;單字集會保存在本機瀏覽器。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;練習進度可保存並接續。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;主題偏好會保留。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;不需要後端伺服器或資料庫。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：清除瀏覽器資料、換瀏覽器、換裝置或使用無痕模式，都可能導致本機資料消失。重要資料建議定期匯出 ZIP 或備份到 Google Drive。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;zip-匯出與匯入&quot;&gt;ZIP 匯出與匯入&lt;/h2&gt;
&lt;p&gt;Wordmem 支援離線備份。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;可選擇要匯出的單字集。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;下載 Wordmem 專用 ZIP 備份檔。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可從 ZIP 備份匯入資料。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;匯入時可選擇追加或覆蓋本機資料。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;同名單字集可選擇保留本機版本或使用匯入版本。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;匯入模式比較：&lt;/p&gt;




















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;模式&lt;/th&gt;&lt;th&gt;說明&lt;/th&gt;&lt;th&gt;適合情境&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;追加到本機&lt;/td&gt;&lt;td&gt;保留現有資料，將備份內容加入本機&lt;/td&gt;&lt;td&gt;合併不同來源的單字集&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;覆蓋本機&lt;/td&gt;&lt;td&gt;用備份完整取代目前資料並清除練習進度&lt;/td&gt;&lt;td&gt;換裝置或完整還原&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;google-drive-手動備份&quot;&gt;Google Drive 手動備份&lt;/h2&gt;
&lt;p&gt;設定 Google OAuth Client ID 後，Wordmem 可以手動備份到 Google Drive。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;使用 Google Identity Services 進行授權。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用 Google Drive API 儲存備份檔。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;備份檔會放在 Google Drive 的 &lt;code&gt;Wordmem&lt;/code&gt; 資料夾。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;備份格式為 ZIP。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;App 會保留最近的備份，並清理過舊備份。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可從 Google Drive 選擇備份檔並匯入。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Wordmem 使用的 Google Drive 權限範圍為：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;https://www.googleapis.com/auth/drive.file&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;這代表 App 只能存取由 Wordmem 建立或由使用者透過授權流程選用的檔案，並非完整讀取整個 Google Drive。&lt;/p&gt;
&lt;h1 id=&quot;使用流程&quot;&gt;使用流程&lt;/h1&gt;
&lt;h2 id=&quot;第一次使用&quot;&gt;第一次使用&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;開啟 Wordmem。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;點選新增單字集或 AI 匯入。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;建立第一個單字集。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;從首頁選擇單字卡、選擇練習或拼字測試。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;完成練習後查看結果。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;定期使用 ZIP 或 Google Drive 備份。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;日常複習&quot;&gt;日常複習&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;選擇今天要練習的單字集。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;先用單字卡快速看過。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用選擇題測驗理解程度。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用拼字測試檢查是否真的記住英文拼法。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;對錯題進行複習。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;必要時複製錯題解析指令到 AI 平台。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&quot;技術概覽&quot;&gt;技術概覽&lt;/h1&gt;
&lt;h2 id=&quot;前端架構&quot;&gt;前端架構&lt;/h2&gt;









































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;項目&lt;/th&gt;&lt;th&gt;技術&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;框架&lt;/td&gt;&lt;td&gt;Vue 3&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;語法&lt;/td&gt;&lt;td&gt;&lt;code&gt;&amp;#x3C;script setup lang=&quot;ts&quot;&gt;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;狀態管理&lt;/td&gt;&lt;td&gt;Pinia&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;路由&lt;/td&gt;&lt;td&gt;Vue Router&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;樣式&lt;/td&gt;&lt;td&gt;Tailwind CSS v4&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;建置工具&lt;/td&gt;&lt;td&gt;Vite&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;測試&lt;/td&gt;&lt;td&gt;Vitest&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;PWA&lt;/td&gt;&lt;td&gt;vite-plugin-pwa&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;資料與備份&quot;&gt;資料與備份&lt;/h2&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;功能&lt;/th&gt;&lt;th&gt;實作&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;本機儲存&lt;/td&gt;&lt;td&gt;IndexedDB、localStorage&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ZIP 備份&lt;/td&gt;&lt;td&gt;fflate、Web Worker&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Google 登入&lt;/td&gt;&lt;td&gt;Google Identity Services&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;雲端備份&lt;/td&gt;&lt;td&gt;Google Drive API&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;匯入驗證&lt;/td&gt;&lt;td&gt;TypeScript 型別與驗證邏輯&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h1 id=&quot;適用對象&quot;&gt;適用對象&lt;/h1&gt;
&lt;h2 id=&quot;學生&quot;&gt;學生&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;整理課本單字。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;準備段考、會考、學測、分科測驗或英文檢定。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;針對錯題反覆練習。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;老師與家教&quot;&gt;老師與家教&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;建立課程單字集。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;產生可分享的 ZIP 備份。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;搭配 AI 快速建立題庫。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;自學者&quot;&gt;自學者&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;建立個人英文詞庫。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;用拼字測試訓練輸出能力。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;將錯題交給 AI 補充語法、用法與例句。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Wordmem</category></item><item><title>邏輯介紹</title><link>https://tavricccc.github.io/blog/%E9%82%8F%E8%BC%AF%E4%BB%8B%E7%B4%B9/</link><guid isPermaLink="true">https://tavricccc.github.io/blog/%E9%82%8F%E8%BC%AF%E4%BB%8B%E7%B4%B9/</guid><description>角色與使用情境</description><pubDate>Sun, 16 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;角色與使用情境&quot;&gt;角色與使用情境&lt;/h1&gt;









































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;角色&lt;/th&gt;&lt;th&gt;可以做什麼&lt;/th&gt;&lt;th&gt;看不到什麼&lt;/th&gt;&lt;th&gt;主要入口&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;未登入訪客&lt;/td&gt;&lt;td&gt;看到登入提示&lt;/td&gt;&lt;td&gt;所有提案、公告、通知、留言&lt;/td&gt;&lt;td&gt;登入面板&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;校內一般使用者&lt;/td&gt;&lt;td&gt;看公共議題、設備提案、公告；建立提案；留言；附議公共議題&lt;/td&gt;&lt;td&gt;其他人的學生權益維護提案、其他人的通知、其他人的私有作者資料&lt;/td&gt;&lt;td&gt;提案看板、公告頁&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;提案作者&lt;/td&gt;&lt;td&gt;管理自己的提案；在「我的提案」看到自己發過的內容&lt;/td&gt;&lt;td&gt;其他人的私密提案&lt;/td&gt;&lt;td&gt;我的提案分頁、提案詳情&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;公共議題附議者&lt;/td&gt;&lt;td&gt;對公共議題附議或取消附議&lt;/td&gt;&lt;td&gt;其他使用者是否附議&lt;/td&gt;&lt;td&gt;公共議題卡片、提案詳情&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;管理員&lt;/td&gt;&lt;td&gt;調整提案狀態、建立公告、刪除不適合內容、看平台統計&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;管理員狀態選單、公告管理、Dashboard&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h1 id=&quot;使用者功能總覽&quot;&gt;使用者功能總覽&lt;/h1&gt;




























































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;功能&lt;/th&gt;&lt;th&gt;使用者會看到的行為&lt;/th&gt;&lt;th&gt;背後邏輯&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Google 校內登入&lt;/td&gt;&lt;td&gt;只能用已驗證的校內 Google 帳號登入&lt;/td&gt;&lt;td&gt;前端與 Cloud Functions 都檢查 Google provider、email verified、指定網域&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;提案分類分頁&lt;/td&gt;&lt;td&gt;公共議題、學生權益維護、設備、我的提案&lt;/td&gt;&lt;td&gt;路由 &lt;code&gt;/issues/:filter&lt;/code&gt; 控制目前分頁，不靠單一畫面內部狀態硬切&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;提案看板&lt;/td&gt;&lt;td&gt;卡片與列表模式切換、搜尋、分頁&lt;/td&gt;&lt;td&gt;Firestore 即時訂閱加上前端分頁與搜尋狀態&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;新增提案&lt;/td&gt;&lt;td&gt;填標題、內容、分類，可插入圖片&lt;/td&gt;&lt;td&gt;送出時統一呼叫 &lt;code&gt;backendAction&lt;/code&gt;，由後端驗證與寫入&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;公共議題附議&lt;/td&gt;&lt;td&gt;按下附議後數字立即更新，失敗會回復&lt;/td&gt;&lt;td&gt;前端 optimistic UI，後端 transaction 防止重複附議&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;提案留言&lt;/td&gt;&lt;td&gt;在提案詳情內留言，可附圖片&lt;/td&gt;&lt;td&gt;留言讀寫都經 Cloud Functions，Firestore client 不直接讀寫留言集合&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;狀態通知&lt;/td&gt;&lt;td&gt;狀態變更、留言、附議達標會通知&lt;/td&gt;&lt;td&gt;&lt;code&gt;user_notifications&lt;/code&gt; 只讓收件人讀取，最多保留最新 15 則&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;公告&lt;/td&gt;&lt;td&gt;校內使用者可看公告、留言、按讚&lt;/td&gt;&lt;td&gt;公告由管理員建立，互動也走 callable&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;管理員 Dashboard&lt;/td&gt;&lt;td&gt;看使用者、提案、留言、附議與分類統計&lt;/td&gt;&lt;td&gt;後端累加平台統計，Dashboard callable 僅 admin 可取&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Notion 備份&lt;/td&gt;&lt;td&gt;提案、公告與互動事件可同步到 Notion&lt;/td&gt;&lt;td&gt;Cloud Tasks 排隊，失敗可重試並記錄狀態&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h1 id=&quot;提案分類與差異&quot;&gt;提案分類與差異&lt;/h1&gt;













































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;分類&lt;/th&gt;&lt;th&gt;適合內容&lt;/th&gt;&lt;th&gt;誰看得到&lt;/th&gt;&lt;th&gt;是否可附議&lt;/th&gt;&lt;th&gt;作者顯示&lt;/th&gt;&lt;th&gt;期限邏輯&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;公共議題&lt;/td&gt;&lt;td&gt;影響多數學生的制度、環境、流程問題&lt;/td&gt;&lt;td&gt;校內使用者都可看&lt;/td&gt;&lt;td&gt;是，目標 50 人&lt;/td&gt;&lt;td&gt;一般使用者看不到作者真實資訊；本人與管理員可看私有作者資料&lt;/td&gt;&lt;td&gt;14 天內未達 50 人會自動轉為「未通過」；達標後有 7 天回覆期限&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;學生權益維護&lt;/td&gt;&lt;td&gt;個別權益、敏感事件、需要私下處理的問題&lt;/td&gt;&lt;td&gt;只有作者本人與管理員可看&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;只在本人與管理員情境可辨識&lt;/td&gt;&lt;td&gt;建立後直接進入待回覆流程，回覆期限 7 天&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;設備&lt;/td&gt;&lt;td&gt;設備損壞、空間問題、修繕需求&lt;/td&gt;&lt;td&gt;校內使用者都可看&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;可顯示校內作者資訊&lt;/td&gt;&lt;td&gt;建立後直接進入待回覆流程，回覆期限 7 天&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;我的提案&lt;/td&gt;&lt;td&gt;使用者自己發出的全部提案&lt;/td&gt;&lt;td&gt;只列出目前登入者自己的提案&lt;/td&gt;&lt;td&gt;依原分類規則&lt;/td&gt;&lt;td&gt;使用者可以知道哪些是自己送出的&lt;/td&gt;&lt;td&gt;用路由 filter 顯示，不是獨立分類&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h1 id=&quot;提案狀態&quot;&gt;提案狀態&lt;/h1&gt;









































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;狀態代碼&lt;/th&gt;&lt;th&gt;顯示文字&lt;/th&gt;&lt;th&gt;代表意義&lt;/th&gt;&lt;th&gt;誰能造成狀態變化&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;pending&lt;/code&gt;&lt;/td&gt;&lt;td&gt;未回覆&lt;/td&gt;&lt;td&gt;新提案等待管理方處理；公共議題可能仍在附議期&lt;/td&gt;&lt;td&gt;使用者建立提案後產生&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;processing&lt;/code&gt;&lt;/td&gt;&lt;td&gt;處理中&lt;/td&gt;&lt;td&gt;管理方已接手處理&lt;/td&gt;&lt;td&gt;管理員&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;auto-rejected&lt;/code&gt;&lt;/td&gt;&lt;td&gt;未通過&lt;/td&gt;&lt;td&gt;公共議題附議期到期但未達門檻&lt;/td&gt;&lt;td&gt;排程函式或維護 callable&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;infeasible&lt;/code&gt;&lt;/td&gt;&lt;td&gt;無法實行&lt;/td&gt;&lt;td&gt;管理方判定目前無法執行&lt;/td&gt;&lt;td&gt;管理員&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;completed&lt;/code&gt;&lt;/td&gt;&lt;td&gt;已完成&lt;/td&gt;&lt;td&gt;已處理或完成回覆&lt;/td&gt;&lt;td&gt;管理員&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h1 id=&quot;提案生命週期&quot;&gt;提案生命週期&lt;/h1&gt;



























































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;階段&lt;/th&gt;&lt;th&gt;公共議題&lt;/th&gt;&lt;th&gt;學生權益維護&lt;/th&gt;&lt;th&gt;設備&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;建立&lt;/td&gt;&lt;td&gt;檢查標題、內容、分類、圖片；產生附議期限&lt;/td&gt;&lt;td&gt;檢查標題、內容、分類、圖片；標記私密可見性&lt;/td&gt;&lt;td&gt;檢查標題、內容、分類、圖片&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;初始狀態&lt;/td&gt;&lt;td&gt;&lt;code&gt;pending&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;pending&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;pending&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;附議&lt;/td&gt;&lt;td&gt;可附議，可取消，重複附議被後端拒絕&lt;/td&gt;&lt;td&gt;不開放&lt;/td&gt;&lt;td&gt;不開放&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;達標&lt;/td&gt;&lt;td&gt;達 50 人後記錄 &lt;code&gt;support_met_at&lt;/code&gt;，設定回覆期限&lt;/td&gt;&lt;td&gt;不適用&lt;/td&gt;&lt;td&gt;不適用&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;未達標&lt;/td&gt;&lt;td&gt;14 天後自動轉為 &lt;code&gt;auto-rejected&lt;/code&gt;&lt;/td&gt;&lt;td&gt;不適用&lt;/td&gt;&lt;td&gt;不適用&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;管理員處理&lt;/td&gt;&lt;td&gt;可改為處理中、無法實行、已完成&lt;/td&gt;&lt;td&gt;可改為處理中、無法實行、已完成&lt;/td&gt;&lt;td&gt;可改為處理中、無法實行、已完成&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;通知&lt;/td&gt;&lt;td&gt;留言、狀態更新、附議達標都可通知&lt;/td&gt;&lt;td&gt;留言與狀態更新通知作者&lt;/td&gt;&lt;td&gt;留言與狀態更新通知作者&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;備份&lt;/td&gt;&lt;td&gt;Notion 事件同步&lt;/td&gt;&lt;td&gt;Notion 事件同步，但依資料設計保護私密資訊&lt;/td&gt;&lt;td&gt;Notion 事件同步&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h1 id=&quot;權限模型總表&quot;&gt;權限模型總表&lt;/h1&gt;







































































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;動作&lt;/th&gt;&lt;th&gt;未登入&lt;/th&gt;&lt;th&gt;校內使用者&lt;/th&gt;&lt;th&gt;作者本人&lt;/th&gt;&lt;th&gt;管理員&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;讀取公共議題&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;讀取學生權益維護提案&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;讀取設備提案&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;新增提案&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;刪除自己的提案&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;調整提案狀態&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;附議公共議題&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;附議學生權益維護或設備&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;讀取留言&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;透過 callable 依提案權限讀取&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;新增留言&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;透過 callable 依提案權限新增&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;讀取通知&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;只能讀自己的&lt;/td&gt;&lt;td&gt;只能讀自己的&lt;/td&gt;&lt;td&gt;只能讀自己的&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;建立公告&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;讀取 Dashboard&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h1 id=&quot;登入與校內身分驗證&quot;&gt;登入與校內身分驗證&lt;/h1&gt;









































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;檢查項目&lt;/th&gt;&lt;th&gt;前端用途&lt;/th&gt;&lt;th&gt;後端用途&lt;/th&gt;&lt;th&gt;原因&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Google 登入 provider&lt;/td&gt;&lt;td&gt;引導使用者用正確方式登入&lt;/td&gt;&lt;td&gt;&lt;code&gt;validateAuthAndDomain&lt;/code&gt; 拒絕非 Google provider&lt;/td&gt;&lt;td&gt;避免用其他 provider 繞過校內帳號政策&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Email 已驗證&lt;/td&gt;&lt;td&gt;顯示登入狀態前先驗證&lt;/td&gt;&lt;td&gt;callable 每次操作都驗證 token&lt;/td&gt;&lt;td&gt;避免未驗證 email 被當成有效校內身分&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Email 網域&lt;/td&gt;&lt;td&gt;顯示校內網域限制&lt;/td&gt;&lt;td&gt;比對 &lt;code&gt;ALLOWED_DOMAIN&lt;/code&gt;&lt;/td&gt;&lt;td&gt;平台只服務指定學校網域&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;管理員 email&lt;/td&gt;&lt;td&gt;顯示管理 UI&lt;/td&gt;&lt;td&gt;比對 &lt;code&gt;ADMIN_EMAILS&lt;/code&gt;&lt;/td&gt;&lt;td&gt;管理員身份不靠前端判斷&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;UID&lt;/td&gt;&lt;td&gt;訂閱自己的通知與提案&lt;/td&gt;&lt;td&gt;寫入作者、附議、留言紀錄&lt;/td&gt;&lt;td&gt;保持每個操作可追蹤&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h1 id=&quot;資料讀寫邊界&quot;&gt;資料讀寫邊界&lt;/h1&gt;







































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;資料&lt;/th&gt;&lt;th&gt;前端可直接讀嗎&lt;/th&gt;&lt;th&gt;前端可直接寫嗎&lt;/th&gt;&lt;th&gt;實際寫入方式&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;issues&lt;/code&gt;&lt;/td&gt;&lt;td&gt;可讀，但學生權益維護只限作者與管理員&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;&lt;code&gt;backendAction.createIssue&lt;/code&gt;、&lt;code&gt;deleteIssue&lt;/code&gt;、&lt;code&gt;moderateIssueStatus&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;issues/{issueId}/votes/{uid}&lt;/code&gt;&lt;/td&gt;&lt;td&gt;只可讀自己的投票狀態或管理員讀取&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;&lt;code&gt;backendAction.toggleSupport&lt;/code&gt;、&lt;code&gt;removeSupport&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;private_issue_authors&lt;/code&gt;&lt;/td&gt;&lt;td&gt;只限作者與管理員&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;建立公共議題時由後端同步寫入&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;issue_comments&lt;/code&gt;&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;&lt;code&gt;backendAction.listComments&lt;/code&gt;、&lt;code&gt;createComment&lt;/code&gt;、&lt;code&gt;deleteComment&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;announcements&lt;/code&gt;&lt;/td&gt;&lt;td&gt;校內使用者可讀&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;管理員 callable 建立、更新、刪除&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;announcements/{id}/likes/{uid}&lt;/code&gt;&lt;/td&gt;&lt;td&gt;只可讀自己的讚狀態或管理員讀取&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;&lt;code&gt;backendAction.toggleAnnouncementLike&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;announcement_comments&lt;/code&gt;&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;公告留言 callable&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;uploads&lt;/code&gt;&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;圖片上傳、解析、刪除 callable&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;user_notifications&lt;/code&gt;&lt;/td&gt;&lt;td&gt;只可讀自己的通知&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;後端事件建立通知&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;platform_stats&lt;/code&gt;&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;否&lt;/td&gt;&lt;td&gt;管理員 Dashboard callable&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h1 id=&quot;為什麼所有寫入都走-cloud-functions&quot;&gt;為什麼所有寫入都走 Cloud Functions&lt;/h1&gt;








































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;風險&lt;/th&gt;&lt;th&gt;如果讓前端直接寫入會怎樣&lt;/th&gt;&lt;th&gt;現在的做法&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;偽造作者&lt;/td&gt;&lt;td&gt;使用者可能改掉 &lt;code&gt;author_uid&lt;/code&gt; 或管理欄位&lt;/td&gt;&lt;td&gt;後端從 Firebase Auth token 取 uid/email/name&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;偽造管理員&lt;/td&gt;&lt;td&gt;前端 UI 隱藏不代表安全&lt;/td&gt;&lt;td&gt;後端每次 callable 都比對 &lt;code&gt;ADMIN_EMAILS&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;重複附議&lt;/td&gt;&lt;td&gt;同一人可能送出多次寫入&lt;/td&gt;&lt;td&gt;Firestore transaction 與 votes 子集合防重&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;私密提案外洩&lt;/td&gt;&lt;td&gt;client query 寫錯就可能列出敏感資料&lt;/td&gt;&lt;td&gt;Firestore Rules 先擋，再由查詢服務按分類取資料&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;圖片任意讀寫&lt;/td&gt;&lt;td&gt;Storage URL 若公開會擴散&lt;/td&gt;&lt;td&gt;Storage 規則拒絕 uploads 直接讀寫，只用短效 signed URL&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Notion 同步失敗&lt;/td&gt;&lt;td&gt;直接同步容易讓使用者操作卡住&lt;/td&gt;&lt;td&gt;Cloud Tasks 排隊、重試、限速&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h1 id=&quot;圖片與-markdown&quot;&gt;圖片與 Markdown&lt;/h1&gt;













































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;項目&lt;/th&gt;&lt;th&gt;設計&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;圖片格式&lt;/td&gt;&lt;td&gt;上傳前端先壓縮，後端接受 WebP&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;單張大小&lt;/td&gt;&lt;td&gt;後端限制最大 2 MB&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;預覽圖&lt;/td&gt;&lt;td&gt;後端用 &lt;code&gt;sharp&lt;/code&gt; 產生最大 480px preview&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Markdown 語法&lt;/td&gt;&lt;td&gt;支援 `![alt&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Storage 權限&lt;/td&gt;&lt;td&gt;&lt;code&gt;/uploads/**&lt;/code&gt; 不允許前端直接 read/write/delete&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;圖片讀取&lt;/td&gt;&lt;td&gt;前端解析 &lt;code&gt;srp-upload://&lt;/code&gt; 後，透過 callable 換短效 signed URL&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;signed URL 有效期&lt;/td&gt;&lt;td&gt;15 分鐘&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;可見性&lt;/td&gt;&lt;td&gt;&lt;code&gt;school&lt;/code&gt; 或 &lt;code&gt;private&lt;/code&gt;，由 registry 與權限 helper 判斷&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;刪除&lt;/td&gt;&lt;td&gt;刪提案、留言或公告時，後端清理關聯圖片&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h1 id=&quot;公共議題附議邏輯&quot;&gt;公共議題附議邏輯&lt;/h1&gt;





































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;規則&lt;/th&gt;&lt;th&gt;說明&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;只限公共議題&lt;/td&gt;&lt;td&gt;學生權益維護與設備不需要附議流程&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;目標人數&lt;/td&gt;&lt;td&gt;&lt;code&gt;PUBLIC_SUPPORT_GOAL = 50&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;附議期限&lt;/td&gt;&lt;td&gt;建立後 14 天&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;達標效果&lt;/td&gt;&lt;td&gt;寫入 &lt;code&gt;support_met_at&lt;/code&gt;，設定 7 天回覆期限，通知作者&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;未達標效果&lt;/td&gt;&lt;td&gt;排程每 60 分鐘掃描，到期且未達標轉成 &lt;code&gt;auto-rejected&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;防止重複&lt;/td&gt;&lt;td&gt;votes 子集合以 uid 當文件 ID，交易內同步 &lt;code&gt;support_count&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;使用者狀態&lt;/td&gt;&lt;td&gt;&lt;code&gt;users.supported_issue_ids&lt;/code&gt; 保留使用者目前附議過的提案 ID&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h1 id=&quot;通知與公告&quot;&gt;通知與公告&lt;/h1&gt;








































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;模組&lt;/th&gt;&lt;th&gt;功能&lt;/th&gt;&lt;th&gt;權限&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;通知鈴鐺&lt;/td&gt;&lt;td&gt;顯示最新通知與未讀數&lt;/td&gt;&lt;td&gt;只讀目前登入者的通知&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;提案留言通知&lt;/td&gt;&lt;td&gt;有人留言時通知相關使用者&lt;/td&gt;&lt;td&gt;後端決定收件人&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;狀態變更通知&lt;/td&gt;&lt;td&gt;管理員改狀態後通知作者&lt;/td&gt;&lt;td&gt;後端建立&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;附議達標通知&lt;/td&gt;&lt;td&gt;公共議題達標時通知作者&lt;/td&gt;&lt;td&gt;後端建立&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;公告列表&lt;/td&gt;&lt;td&gt;校內使用者可讀全站公告&lt;/td&gt;&lt;td&gt;管理員才可新增、編輯、刪除&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;公告留言與讚&lt;/td&gt;&lt;td&gt;校內使用者互動&lt;/td&gt;&lt;td&gt;透過 callable 寫入，非 client 直寫&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h1 id=&quot;notion-備份邏輯&quot;&gt;Notion 備份邏輯&lt;/h1&gt;








































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;事件類型&lt;/th&gt;&lt;th&gt;備份內容&lt;/th&gt;&lt;th&gt;同步方式&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;提案建立&lt;/td&gt;&lt;td&gt;標題、分類、狀態、作者資訊、內容&lt;/td&gt;&lt;td&gt;建立或更新 Notion page&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;提案狀態變更&lt;/td&gt;&lt;td&gt;舊狀態、新狀態、時間&lt;/td&gt;&lt;td&gt;追加事件紀錄&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;提案留言&lt;/td&gt;&lt;td&gt;留言摘要、圖片 block&lt;/td&gt;&lt;td&gt;追加到 Notion&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;公告建立或更新&lt;/td&gt;&lt;td&gt;公告標題、內容、互動狀態&lt;/td&gt;&lt;td&gt;建立或更新 Notion page&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;公告留言或讚&lt;/td&gt;&lt;td&gt;互動事件&lt;/td&gt;&lt;td&gt;追加到 Notion&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;刪除事件&lt;/td&gt;&lt;td&gt;將 Notion 對應頁或事件標記處理&lt;/td&gt;&lt;td&gt;透過 event handler&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Notion 同步不直接卡住使用者操作。後端會先寫入 Firestore，再把同步事件丟進 Cloud Tasks。任務最多重試 5 次，限速為每秒 1 個、同時最多 2 個，避免打爆 Notion API 或因短暫失敗造成資料遺失。&lt;/p&gt;
&lt;h1 id=&quot;主要資料集合&quot;&gt;主要資料集合&lt;/h1&gt;



















































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Collection&lt;/th&gt;&lt;th&gt;主要用途&lt;/th&gt;&lt;th&gt;讀取者&lt;/th&gt;&lt;th&gt;寫入者&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;issues&lt;/code&gt;&lt;/td&gt;&lt;td&gt;提案主資料&lt;/td&gt;&lt;td&gt;校內使用者依分類與作者權限讀取&lt;/td&gt;&lt;td&gt;Cloud Functions&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;private_issue_authors&lt;/code&gt;&lt;/td&gt;&lt;td&gt;公共議題私有作者資訊&lt;/td&gt;&lt;td&gt;作者本人、管理員&lt;/td&gt;&lt;td&gt;Cloud Functions&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;issue_comments&lt;/code&gt;&lt;/td&gt;&lt;td&gt;提案留言&lt;/td&gt;&lt;td&gt;只能透過 callable 取得&lt;/td&gt;&lt;td&gt;Cloud Functions&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;announcements&lt;/code&gt;&lt;/td&gt;&lt;td&gt;公告&lt;/td&gt;&lt;td&gt;校內使用者&lt;/td&gt;&lt;td&gt;Cloud Functions&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;announcement_comments&lt;/code&gt;&lt;/td&gt;&lt;td&gt;公告留言&lt;/td&gt;&lt;td&gt;只能透過 callable 取得&lt;/td&gt;&lt;td&gt;Cloud Functions&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;users&lt;/code&gt;&lt;/td&gt;&lt;td&gt;使用者附議 ID、頭像快取等&lt;/td&gt;&lt;td&gt;本人&lt;/td&gt;&lt;td&gt;Cloud Functions&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;uploads&lt;/code&gt;&lt;/td&gt;&lt;td&gt;圖片 registry&lt;/td&gt;&lt;td&gt;不開放 client 直接讀&lt;/td&gt;&lt;td&gt;Cloud Functions&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;user_notifications&lt;/code&gt;&lt;/td&gt;&lt;td&gt;站內通知&lt;/td&gt;&lt;td&gt;收件人本人&lt;/td&gt;&lt;td&gt;Cloud Functions&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;platform_stats&lt;/code&gt;&lt;/td&gt;&lt;td&gt;平台成果統計&lt;/td&gt;&lt;td&gt;管理員 callable&lt;/td&gt;&lt;td&gt;Cloud Functions&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;notion_pages&lt;/code&gt; / &lt;code&gt;notion_issue_pages&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Firestore 與 Notion 頁面對應&lt;/td&gt;&lt;td&gt;後端內部&lt;/td&gt;&lt;td&gt;Cloud Functions&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;notion_sync_jobs&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Notion 同步任務狀態&lt;/td&gt;&lt;td&gt;後端內部&lt;/td&gt;&lt;td&gt;Cloud Functions&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;rate_limits&lt;/code&gt;&lt;/td&gt;&lt;td&gt;發文與留言限流 bucket&lt;/td&gt;&lt;td&gt;後端內部&lt;/td&gt;&lt;td&gt;Cloud Functions&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h1 id=&quot;使用者常見問題&quot;&gt;使用者常見問題&lt;/h1&gt;





































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;問題&lt;/th&gt;&lt;th&gt;回答&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;為什麼我看不到別人的學生權益維護提案？&lt;/td&gt;&lt;td&gt;這個分類是私密用途，只能看到自己發出的提案；如果目前是空的，代表你還沒有在這個分類送出提案。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;為什麼公共議題看不到作者？&lt;/td&gt;&lt;td&gt;公共議題希望降低提案者被針對的風險，因此一般使用者看議題內容與附議狀態即可，作者資訊由後端保存給本人與管理員使用。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;為什麼設備提案不能附議？&lt;/td&gt;&lt;td&gt;設備問題通常是明確修繕或回報流程，不需要用附議達標來判斷是否處理。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;為什麼圖片有時需要重新載入？&lt;/td&gt;&lt;td&gt;圖片不是永久公開 URL，而是短效 signed URL；過期後前端會再向後端解析。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;為什麼送出失敗時圖片不會留下公開連結？&lt;/td&gt;&lt;td&gt;圖片先進 registry，送出失敗或刪除內容時後端會清理，降低孤兒檔案與外洩風險。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;管理員是不是可以在前端直接改資料？&lt;/td&gt;&lt;td&gt;不行。前端只能呼叫後端 action，真正的 admin 判斷在 Cloud Functions。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;提案內容支援 Markdown 嗎？&lt;/td&gt;&lt;td&gt;支援，並會用 DOMPurify 過濾渲染後 HTML，降低 XSS 風險。&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h1 id=&quot;前端模組責任&quot;&gt;前端模組責任&lt;/h1&gt;













































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;模組&lt;/th&gt;&lt;th&gt;責任&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;src/router&lt;/code&gt;&lt;/td&gt;&lt;td&gt;將提案、公告、管理員頁面路由化；提案 filter 由 URL 控制&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;src/views&lt;/code&gt;&lt;/td&gt;&lt;td&gt;頁面入口，只組合資料流程與主要元件&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;src/components&lt;/code&gt;&lt;/td&gt;&lt;td&gt;UI 呈現與互動元件，避免直接寫 Firestore 邏輯&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;src/components/ui&lt;/code&gt;&lt;/td&gt;&lt;td&gt;純展示共用元件，不 import composable 或 service&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;src/composables&lt;/code&gt;&lt;/td&gt;&lt;td&gt;狀態、表單、訂閱、分頁、搜尋、對話框、通知等邏輯&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;src/services&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Firestore 讀取與 Cloud Functions callable 包裝&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;src/lib&lt;/code&gt;&lt;/td&gt;&lt;td&gt;無 Vue 相依的純函式，例如 Markdown 圖片解析、日期格式化、搜尋正規化&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;src/constants&lt;/code&gt;&lt;/td&gt;&lt;td&gt;分類、狀態、路由選項&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;src/types&lt;/code&gt;&lt;/td&gt;&lt;td&gt;TypeScript 型別，作為資料模型文件&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h1 id=&quot;後端模組責任&quot;&gt;後端模組責任&lt;/h1&gt;

















































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;模組&lt;/th&gt;&lt;th&gt;責任&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;functions/src/index.ts&lt;/code&gt;&lt;/td&gt;&lt;td&gt;只匯出部署入口&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;functions/src/entrypoints/backend-action.ts&lt;/code&gt;&lt;/td&gt;&lt;td&gt;單一 callable router，依 action 分派 handler&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;functions/src/core&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Firebase Admin 初始化、環境變數、身分驗證、限流&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;functions/src/issues&lt;/code&gt;&lt;/td&gt;&lt;td&gt;提案建立、刪除、留言、附議、狀態管理、過期同步&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;functions/src/announcements&lt;/code&gt;&lt;/td&gt;&lt;td&gt;公告 CRUD、公告留言、讚、Notion retry&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;functions/src/uploads&lt;/code&gt;&lt;/td&gt;&lt;td&gt;圖片建立、解析、刪除、Storage 與 registry 權限&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;functions/src/notifications&lt;/code&gt;&lt;/td&gt;&lt;td&gt;通知建立與已讀時間&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;functions/src/stats&lt;/code&gt;&lt;/td&gt;&lt;td&gt;平台統計與 Dashboard&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;functions/src/notion&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Notion client、Markdown 轉 block、事件同步與 page mapping&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;functions/src/scheduled&lt;/code&gt;&lt;/td&gt;&lt;td&gt;每小時自動駁回、每日清理 Secret Manager 舊版本&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h1 id=&quot;一句話流程圖&quot;&gt;一句話流程圖&lt;/h1&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;flowchart TD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  A[&quot;校內 Google 登入&quot;] --&gt; B[&quot;選擇提案分類&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  B --&gt; C[&quot;送出提案與圖片&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  C --&gt; D[&quot;Cloud Functions 驗證與寫入&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  D --&gt; E{&quot;公共議題？&quot;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  E --&gt;|是| F[&quot;14 天附議期&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  F --&gt; G{&quot;達 50 人？&quot;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  G --&gt;|是| H[&quot;設定 7 天回覆期限並通知&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  G --&gt;|否| I[&quot;排程自動轉未通過&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  E --&gt;|否| J[&quot;直接等待管理員回覆&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  H --&gt; K[&quot;管理員處理狀態&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  I --&gt; K&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  J --&gt; K&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  K --&gt; L[&quot;通知使用者並同步 Notion&quot;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content:encoded><category>SRP</category></item><item><title>部署</title><link>https://tavricccc.github.io/blog/%E9%83%A8%E7%BD%B2/</link><guid isPermaLink="true">https://tavricccc.github.io/blog/%E9%83%A8%E7%BD%B2/</guid><description>準備工作</description><pubDate>Sun, 16 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;準備工作&quot;&gt;準備工作&lt;/h1&gt;
&lt;p&gt;在開始之前，請確保您已擁有以下平台的帳號：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;：託管原始碼&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Supabase&lt;/strong&gt;：後端服務（認證、資料庫、Edge Functions）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Vercel&lt;/strong&gt;：前端網站部署&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Google Cloud Console&lt;/strong&gt;：設定 Google 登入與 Google Calendar API&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;step-1-本機環境驗證&quot;&gt;Step 1. 本機環境驗證&lt;/h1&gt;
&lt;p&gt;請確保本機已安裝 &lt;strong&gt;Node.js 18+ (建議24 LTS)&lt;/strong&gt;。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;安裝依賴&lt;/strong&gt;：Bash&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm install&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;設定環境變數&lt;/strong&gt;：程式碼片段&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;複製 &lt;code&gt;.env.example&lt;/code&gt; 並命名為 &lt;code&gt;.env&lt;/code&gt;：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;VITE_SUPABASE_URL=https://你的-project-ref.supabase.co&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;VITE_SUPABASE_ANON_KEY=你的-supabase-publishable-anon-key&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;啟動本地開發伺服器&lt;/strong&gt;：Bash&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm run dev&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：預設網址通常為 &lt;code&gt;http://localhost:5173&lt;/code&gt;。確認畫面可正常開啟後再進行下一步。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;step-2-建立-github-repository&quot;&gt;Step 2. 建立 GitHub Repository&lt;/h1&gt;
&lt;p&gt;將專案推送到您的 GitHub 儲存庫：&lt;/p&gt;
&lt;p&gt;Bash&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git init&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git add .&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git commit -m &quot;Initial commit&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git branch -M main&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git remote add origin https://github.com/你的帳號/hynan.git&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git push -u origin main&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：請務必確認 &lt;code&gt;.env&lt;/code&gt; 已列入 &lt;code&gt;.gitignore&lt;/code&gt; 中，切勿推送到 GitHub 公開儲存庫。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;step-3-supabase-基礎設定&quot;&gt;Step 3. Supabase 基礎設定&lt;/h1&gt;
&lt;h2 id=&quot;1-建立專案&quot;&gt;1. 建立專案&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;前往 &lt;strong&gt;Supabase Dashboard&lt;/strong&gt; 建立新專案。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;設定 Project name（如 &lt;code&gt;hynan&lt;/code&gt;）、強密碼，並選擇合適的 Region。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;2-獲取-api-金鑰&quot;&gt;2. 獲取 API 金鑰&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;前往 &lt;strong&gt;Project Settings &gt; API&lt;/strong&gt;，複製 &lt;strong&gt;Project URL&lt;/strong&gt; 與 &lt;strong&gt;publishable anon key&lt;/strong&gt;，更新至本機 &lt;code&gt;.env&lt;/code&gt; 及保留給後續 Vercel 使用。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;3-初始化資料庫結構&quot;&gt;3. 初始化資料庫結構&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;前往 &lt;strong&gt;SQL Editor&lt;/strong&gt;，開啟新查詢。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;複製專案中 &lt;code&gt;supabase/schema.sql&lt;/code&gt; 的完整內容，貼入並執行。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;執行後，至 &lt;strong&gt;Table Editor&lt;/strong&gt; 確認以下資料表已成功建立並啟動 &lt;strong&gt;RLS (Row Level Security)&lt;/strong&gt;：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;workspaces&lt;/code&gt;, &lt;code&gt;tasks&lt;/code&gt;, &lt;code&gt;tags&lt;/code&gt;, &lt;code&gt;daily_entries&lt;/code&gt;, &lt;code&gt;google_calendar_connections&lt;/code&gt;, &lt;code&gt;google_calendar_workspace_calendars&lt;/code&gt;, &lt;code&gt;google_calendar_sync_queue&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;step-4-google-cloud-console-設定&quot;&gt;Step 4. Google Cloud Console 設定&lt;/h1&gt;
&lt;h2 id=&quot;1-建立專案與啟用-api&quot;&gt;1. 建立專案與啟用 API&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;前往 Google Cloud Console 建立名為 &lt;code&gt;Hynan&lt;/code&gt; 的專案。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;搜尋 &lt;strong&gt;Google Calendar API&lt;/strong&gt; 並點擊 &lt;strong&gt;啟用 (Enable)&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;2-設定-oauth-同意畫面-consent-screen&quot;&gt;2. 設定 OAuth 同意畫面 (Consent Screen)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;進入 &lt;strong&gt;APIs &amp;#x26; Services &gt; OAuth consent screen&lt;/strong&gt;，User Type 選擇 &lt;strong&gt;External&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;填寫 App name (&lt;code&gt;Hynan&lt;/code&gt;)、支援與開發者 Email。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;測試使用者 (Test users)&lt;/strong&gt;：若 App 處於 Testing 狀態，請務必將您要用來登入測試的 Google 帳號加入列表中，否則將無法通過認證。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;3-建立-oauth-憑證&quot;&gt;3. 建立 OAuth 憑證&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;進入 &lt;strong&gt;APIs &amp;#x26; Services &gt; Credentials&lt;/strong&gt;，點擊 &lt;strong&gt;Create Credentials &gt; OAuth client ID&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Application type&lt;/strong&gt;：選擇 &lt;code&gt;Web application&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Authorized JavaScript origins&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;本地：&lt;code&gt;http://localhost:5173&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;正式：&lt;code&gt;https://你的-vercel-app.vercel.app&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Authorized redirect URIs&lt;/strong&gt;（注意結尾必須帶 &lt;code&gt;/&lt;/code&gt;）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;本地：&lt;code&gt;http://localhost:5173/&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;正式：&lt;code&gt;https://你的-vercel-app.vercel.app/&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;建立完成後，&lt;strong&gt;複製 Client ID 與 Client Secret&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;step-5-對接-supabase-與-google-auth&quot;&gt;Step 5. 對接 Supabase 與 Google Auth&lt;/h1&gt;
&lt;h2 id=&quot;1-啟用-google-provider&quot;&gt;1. 啟用 Google Provider&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;回到 &lt;strong&gt;Supabase Dashboard &gt; Authentication &gt; Providers &gt; Google&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;啟用開關，填入剛剛獲取的 &lt;strong&gt;Google Client ID&lt;/strong&gt; 與 &lt;strong&gt;Client Secret&lt;/strong&gt; 並儲存。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;2-設定重導向網址-url-configuration&quot;&gt;2. 設定重導向網址 (URL Configuration)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;前往 &lt;strong&gt;Authentication &gt; URL Configuration&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Site URL&lt;/strong&gt;：&lt;code&gt;https://你的-vercel-app.vercel.app&lt;/code&gt; (本機測試時可先填 &lt;code&gt;http://localhost:5173&lt;/code&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Redirect URLs&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;http://localhost:5173/&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;https://你的-vercel-app.vercel.app/&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;step-6-部署-supabase-edge-function&quot;&gt;Step 6. 部署 Supabase Edge Function&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;全域安裝 Supabase CLI&lt;/strong&gt; 并登入：Bash&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm install -g supabase&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;supabase login&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;連結專案&lt;/strong&gt;（&lt;code&gt;project-ref&lt;/code&gt; 可從 Supabase URL 中取得）：Bash&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;supabase link --project-ref 你的-project-ref&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;設定 Edge Function 密鑰&lt;/strong&gt;：Bash&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;supabase secrets set GOOGLE_CLIENT_ID=&quot;你的-google-client-id&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;supabase secrets set GOOGLE_CLIENT_SECRET=&quot;你的-google-client-secret&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;supabase secrets set GOOGLE_TOKEN_ENCRYPTION_KEY=&quot;請換成一組32字元以上的隨機長字串&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：&lt;code&gt;GOOGLE_TOKEN_ENCRYPTION_KEY&lt;/code&gt; 用於加密 Google Token，請妥善保管，勿用範例弱密碼。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;部署 Function&lt;/strong&gt;（專案路徑需為 &lt;code&gt;supabase/functions/google-calendar/index.ts&lt;/code&gt;）：Bash&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;supabase functions deploy google-calendar&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;step-7-vercel-前端部署&quot;&gt;Step 7. Vercel 前端部署&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;登入 &lt;strong&gt;Vercel&lt;/strong&gt;，點擊 &lt;strong&gt;Add New &gt; Project&lt;/strong&gt;，導入您的 GitHub &lt;code&gt;hynan&lt;/code&gt; 儲存庫。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Framework Preset&lt;/strong&gt; 選擇 &lt;code&gt;Vite&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;設定環境變數 (Environment Variables)&lt;/strong&gt;，務必勾選 Production, Preview, Development：&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;VITE_SUPABASE_URL&lt;/code&gt; = &lt;code&gt;你的 Supabase URL&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;VITE_SUPABASE_ANON_KEY&lt;/code&gt; = &lt;code&gt;你的 Supabase Anon Key&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;點擊 &lt;strong&gt;Deploy&lt;/strong&gt;。部署成功後複製 Vercel 提供的正式網址。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;注意事項&lt;/strong&gt;：請將此正式網址同步更新回 &lt;strong&gt;Supabase Auth URL&lt;/strong&gt; 與 &lt;strong&gt;Google OAuth Credentials&lt;/strong&gt; 的允許清單中（參見 Step 4 與 Step 5）。如有變更環境變數，記得在 Vercel 點選 &lt;strong&gt;Redeploy&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&quot;驗證與自我測試&quot;&gt;驗證與自我測試&lt;/h1&gt;
&lt;p&gt;部署完成後，請依序執行以下測試以確保功能完整：&lt;/p&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;
&lt;p&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; [ ] &lt;strong&gt;Google 登入&lt;/strong&gt;：是否能正常跳轉、成功登入，且 Supabase 後台有產生對應 User。&lt;/p&gt;
&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;
&lt;p&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; [ ] &lt;strong&gt;資料庫讀寫&lt;/strong&gt;：新增任務、標籤、每日筆記，檢查 Supabase Table Editor 是否有即時寫入資料。&lt;/p&gt;
&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;
&lt;p&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; [ ] &lt;strong&gt;行事曆連動&lt;/strong&gt;：在設定中點擊「連接 Google Calendar」，確認是否成功跳出授權並於 Google Calendar 中建立 &lt;code&gt;Hynan&lt;/code&gt; 行事曆。&lt;/p&gt;
&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;
&lt;p&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; [ ] &lt;strong&gt;雙向同步&lt;/strong&gt;：建立有日期的任務，點擊立即同步，檢查 Google Calendar 是否出現事件；反之於 Google 修改，確認能同步回 App。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;常見問題排查-faq&quot;&gt;常見問題排查 (FAQ)&lt;/h1&gt;
&lt;h2 id=&quot;google-登入失敗&quot;&gt;Google 登入失敗？&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;檢查 Google Cloud 的 &lt;strong&gt;Authorized redirect URIs&lt;/strong&gt; 結尾是否有加 &lt;code&gt;/&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;檢查 Supabase 的 &lt;strong&gt;Site URL&lt;/strong&gt; 與 &lt;strong&gt;Redirect URLs&lt;/strong&gt; 是否包含完整的部署網址與 &lt;code&gt;/&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;確認測試帳號是否有加入 Google OAuth Consent Screen 的 &lt;strong&gt;Test users&lt;/strong&gt; 中。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;google-calendar-連接或同步失敗&quot;&gt;Google Calendar 連接或同步失敗？&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;確認 Google Cloud 專案中已啟動 &lt;strong&gt;Google Calendar API&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;檢查 Supabase Edge Function 是否成功部署，且名稱精確為 &lt;code&gt;google-calendar&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;前往 &lt;strong&gt;Supabase Dashboard &gt; Edge Functions &gt; google-calendar &gt; Logs&lt;/strong&gt; 查看具體報錯訊息（如：&lt;code&gt;Missing secret&lt;/code&gt; 代表環境變數未設定成功）。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;重新整理頁面後出現-vercel-404-錯誤&quot;&gt;重新整理頁面後出現 Vercel 404 錯誤？&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;專案根目錄必須包含 &lt;code&gt;vercel.json&lt;/code&gt; 檔案。其內容需設定將所有路由導回 &lt;code&gt;/index.html&lt;/code&gt;（SPA 路由支援）。請確認該檔案是否有被推送到 Git 並成功部署。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;資料看得見但無法新增或修改&quot;&gt;資料看得見，但無法新增或修改？&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;這通常是資料庫 schema 沒有完整建立，或是 RLS Policy 阻擋了寫入。請前往 Supabase &lt;strong&gt;SQL Editor&lt;/strong&gt; 重新完整執行一遍 &lt;code&gt;schema.sql&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Hynan</category></item><item><title>部署</title><link>https://tavricccc.github.io/blog/%E9%83%A8%E7%BD%B2-wordat/</link><guid isPermaLink="true">https://tavricccc.github.io/blog/%E9%83%A8%E7%BD%B2-wordat/</guid><description>本文件說明如何將 Wordat 部署到 Vercel。專案是 Vite React 靜態前端 App，部署時不需要後端伺服器或資料庫。</description><pubDate>Sun, 16 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;本文件說明如何將 Wordat 部署到 Vercel。專案是 Vite React 靜態前端 App，部署時不需要後端伺服器或資料庫。&lt;/p&gt;
&lt;h1 id=&quot;部署前準備&quot;&gt;部署前準備&lt;/h1&gt;
&lt;p&gt;請先確認本機已安裝：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Node.js&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Git&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;GitHub 帳號&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Vercel 帳號&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;專案主要指令：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm install&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm run dev&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm run build&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm run lint&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vercel 部署設定：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Framework Preset：Vite&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Build Command：&lt;code&gt;npm run build&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Output Directory：&lt;code&gt;dist&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install Command：&lt;code&gt;npm install&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;1-本機確認專案可建置&quot;&gt;1. 本機確認專案可建置&lt;/h1&gt;
&lt;p&gt;在專案根目錄執行：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm install&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm run lint&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm run build&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果 &lt;code&gt;npm run build&lt;/code&gt; 成功，專案根目錄會產生 &lt;code&gt;dist&lt;/code&gt; 資料夾，Vercel 會使用這個資料夾作為正式網站內容。&lt;/p&gt;
&lt;h1 id=&quot;2-建立-github-repository&quot;&gt;2. 建立 GitHub Repository&lt;/h1&gt;
&lt;p&gt;到 GitHub 建立一個新的 repository，例如：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;wordat&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以選擇 Public 或 Private。Vercel 兩者都支援，但 Private repository 需要授權 Vercel 存取。&lt;/p&gt;
&lt;h1 id=&quot;3-將專案推送到-github&quot;&gt;3. 將專案推送到 GitHub&lt;/h1&gt;
&lt;p&gt;如果專案尚未初始化 Git：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git init&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git add .&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git commit -m &quot;Initial commit&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git branch -M main&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git remote add origin &amp;#x3C;https://github.com/&gt;&amp;#x3C;你的帳號&gt;/wordat.git&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git push -u origin main&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果專案已經有 Git，只需要確認 remote 後推送：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git remote -v&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git add .&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git commit -m &quot;Update Wordat&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git push&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;請將 &lt;code&gt;&amp;#x3C;你的帳號&gt;&lt;/code&gt; 換成自己的 GitHub username。&lt;/p&gt;
&lt;h1 id=&quot;4-在-vercel-匯入專案&quot;&gt;4. 在 Vercel 匯入專案&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;前往 Vercel Dashboard。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;點選 Add New Project。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;選擇剛剛建立的 GitHub repository。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;確認 Framework Preset 為 Vite。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;確認設定：&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Build Command：&lt;code&gt;npm run build&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Output Directory：&lt;code&gt;dist&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install Command：&lt;code&gt;npm install&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;點選 Deploy。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;部署完成後，Vercel 會提供一個網址，例如：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;https://wordat.vercel.app&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;5-後續更新部署&quot;&gt;5. 後續更新部署&lt;/h1&gt;
&lt;p&gt;之後只要將更新推送到 GitHub，Vercel 就會自動重新部署：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git add .&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git commit -m &quot;Update app&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git push&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vercel 會針對 production branch 自動建立正式部署。通常 production branch 是 &lt;code&gt;main&lt;/code&gt;。&lt;/p&gt;
&lt;h1 id=&quot;6-自訂網域&quot;&gt;6. 自訂網域&lt;/h1&gt;
&lt;p&gt;如果要使用自己的網域：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;進入 Vercel 專案頁面。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;開啟 Settings。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;進入 Domains。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;輸入自訂網域。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;依照 Vercel 顯示的 DNS 指示，到網域商後台設定 DNS。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;常見設定方式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;根網域使用 A record 指向 Vercel 提供的 IP。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;子網域使用 CNAME 指向 Vercel 提供的目標網域。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;DNS 生效可能需要數分鐘到數小時。&lt;/p&gt;
&lt;h1 id=&quot;7-pwa-與離線功能注意事項&quot;&gt;7. PWA 與離線功能注意事項&lt;/h1&gt;
&lt;p&gt;Wordat 使用 PWA 快取機制。部署到 Vercel 後，正式環境會以 HTTPS 提供網站，符合 Service Worker 與 PWA 安裝需求。&lt;/p&gt;
&lt;p&gt;注意事項：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;第一次開啟網站時需要網路連線。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;App 資源快取完成後，瀏覽器才可支援離線開啟。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果發布新版本，使用者可能會看到更新提示。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;PWA 快取與 localStorage 都是瀏覽器端資料，不等於雲端同步。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;8-資料備份建議&quot;&gt;8. 資料備份建議&lt;/h1&gt;
&lt;p&gt;Wordat 的單字資料存在使用者瀏覽器 localStorage。部署到 Vercel 後，資料仍然不會上傳到 Vercel 或 GitHub。&lt;/p&gt;
&lt;p&gt;建議使用者定期在 App 內點選匯出，下載 JSON 備份檔。更換裝置、換瀏覽器或清除網站資料前，請先匯出備份。&lt;/p&gt;
&lt;h1 id=&quot;9-常見問題&quot;&gt;9. 常見問題&lt;/h1&gt;
&lt;h2 id=&quot;部署後畫面空白&quot;&gt;部署後畫面空白&lt;/h2&gt;
&lt;p&gt;先確認本機 &lt;code&gt;npm run build&lt;/code&gt; 是否成功。如果本機成功但 Vercel 失敗，請到 Vercel 的 Deployment Logs 查看錯誤訊息。&lt;/p&gt;
&lt;h2 id=&quot;vercel-找不到-build-output&quot;&gt;Vercel 找不到 build output&lt;/h2&gt;
&lt;p&gt;確認 Output Directory 是：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;dist&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;typescript-錯誤導致部署失敗&quot;&gt;TypeScript 錯誤導致部署失敗&lt;/h2&gt;
&lt;p&gt;先在本機執行：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm run lint&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;依照錯誤訊息修正後再推送。&lt;/p&gt;
&lt;h2 id=&quot;使用者資料沒有同步到另一台裝置&quot;&gt;使用者資料沒有同步到另一台裝置&lt;/h2&gt;
&lt;p&gt;這是目前設計。Wordat 採 Local-first 架構，資料存在本機瀏覽器。若要移動資料，請使用匯出 / 匯入 JSON 備份。&lt;/p&gt;</content:encoded><category>Wordat</category></item><item><title>部署</title><link>https://tavricccc.github.io/blog/%E9%83%A8%E7%BD%B2-tabin/</link><guid isPermaLink="true">https://tavricccc.github.io/blog/%E9%83%A8%E7%BD%B2-tabin/</guid><description>這份文件說明如何把 Tabin 推到 GitHub，並使用 Vercel 部署成可公開存取的網站。</description><pubDate>Sun, 16 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;這份文件說明如何把 Tabin 推到 GitHub，並使用 Vercel 部署成可公開存取的網站。&lt;/p&gt;
&lt;h1 id=&quot;前置需求&quot;&gt;前置需求&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;已安裝 Node.js。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;已有 GitHub 帳號。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;已有 Vercel 帳號。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;本機可以執行 &lt;code&gt;npm install&lt;/code&gt; 和 &lt;code&gt;npm run build&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;1-確認專案可以建置&quot;&gt;1. 確認專案可以建置&lt;/h1&gt;
&lt;p&gt;在專案根目錄執行：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm install&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm run build&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;建置成功後，Vite 會產生 &lt;code&gt;dist&lt;/code&gt; 資料夾。這個資料夾是部署後的靜態網站輸出。&lt;/p&gt;
&lt;h1 id=&quot;2-建立-github-repository&quot;&gt;2. 建立 GitHub Repository&lt;/h1&gt;
&lt;p&gt;在 GitHub 建立一個新的 repository，例如：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;tabin&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;建立時可以不要勾選 README、.gitignore 或 license，避免和本機專案內容衝突。&lt;/p&gt;
&lt;h1 id=&quot;3-推送專案到-github&quot;&gt;3. 推送專案到 GitHub&lt;/h1&gt;
&lt;p&gt;如果本機尚未設定 remote：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git remote add origin &amp;#x3C;https://github.com/YOUR_USERNAME/tabin.git&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;提交並推送：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git add .&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git commit -m &quot;Initial Tabin deployment&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git branch -M main&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git push -u origin main&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之後每次修改完成，都可以用：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git add .&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git commit -m &quot;Update Tabin&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git push&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;4-在-vercel-匯入-github-專案&quot;&gt;4. 在 Vercel 匯入 GitHub 專案&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;前往 &lt;a href=&quot;https://vercel.com/&quot;&gt;Vercel&lt;/a&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;登入後選擇 &lt;code&gt;Add New Project&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;選擇剛剛建立的 GitHub repository。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Vercel 通常會自動偵測這是 Vite 專案。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;設定值建議如下：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Framework Preset: Vite&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Build Command: npm run build&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Output Directory: dist&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Install Command: npm install&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;確認後按下 Deploy。&lt;/p&gt;
&lt;h1 id=&quot;5-取得部署網址&quot;&gt;5. 取得部署網址&lt;/h1&gt;
&lt;p&gt;部署完成後，Vercel 會提供一個網址，例如：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;https://tabin-yourname.vercel.app&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之後 GitHub 的 &lt;code&gt;main&lt;/code&gt; 分支有新 commit 時，Vercel 會自動重新部署。&lt;/p&gt;
&lt;h1 id=&quot;6-設定-chrome-新分頁-extension&quot;&gt;6. 設定 Chrome 新分頁 Extension&lt;/h1&gt;
&lt;p&gt;如果要讓 Chrome 新分頁自動打開 Tabin，請修改：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;extension/newtab.html&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;把裡面的網址：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt; TABIN_URL&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt; &quot;&amp;#x3C;https://your-tabin-vercel-url.vercel.app&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;改成你的 Vercel 部署網址，例如：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt; TABIN_URL&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt; &quot;&amp;#x3C;https://tabin-yourname.vercel.app&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;noscript&lt;/code&gt; 裡的連結也可以同步改成相同網址。&lt;/p&gt;
&lt;h1 id=&quot;7-載入-chrome-extension&quot;&gt;7. 載入 Chrome Extension&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;開啟 Chrome。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;前往 &lt;code&gt;chrome://extensions/&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;開啟右上角 &lt;code&gt;Developer mode&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;點選 &lt;code&gt;Load unpacked&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;選擇專案內的 &lt;code&gt;extension&lt;/code&gt; 資料夾。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;載入後，Chrome 的新分頁會導向你部署好的 Tabin 網站。&lt;/p&gt;
&lt;h1 id=&quot;8-更新部署&quot;&gt;8. 更新部署&lt;/h1&gt;
&lt;p&gt;修改 App 後，先在本機確認：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm run build&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;確認成功後推送到 GitHub：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git add .&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git commit -m &quot;Update layout&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git push&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vercel 會自動偵測 GitHub 更新並重新部署。&lt;/p&gt;
&lt;h1 id=&quot;注意事項&quot;&gt;注意事項&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Tabin 的資料存在瀏覽器 localStorage，不會因為重新部署而自動同步到其他裝置。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果清除瀏覽器資料，書籤也可能被清掉，建議定期使用匯出功能備份。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果 Vercel 部署網址改變，記得同步更新 &lt;code&gt;extension/newtab.html&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果 Chrome extension 修改後沒有生效，回到 &lt;code&gt;chrome://extensions/&lt;/code&gt; 點擊重新載入 extension。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Tabin</category></item><item><title>部署</title><link>https://tavricccc.github.io/blog/%E9%83%A8%E7%BD%B2-wordmem/</link><guid isPermaLink="true">https://tavricccc.github.io/blog/%E9%83%A8%E7%BD%B2-wordmem/</guid><description>部署前準備</description><pubDate>Sun, 16 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;部署前準備&quot;&gt;部署前準備&lt;/h1&gt;
&lt;p&gt;這份教學會帶你把 Wordmem 放到 GitHub，並部署到 Vercel，同時設定 Google Cloud，讓 Google Drive 備份功能可以正常使用。&lt;/p&gt;
&lt;p&gt;你需要準備：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;一個 GitHub 帳號&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;一個 Vercel 帳號&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;一個 Google 帳號&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;本機已安裝 Git&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;本機已安裝 Node.js 與 npm&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;專案原始碼&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;建議版本：&lt;/p&gt;





















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;工具&lt;/th&gt;&lt;th&gt;建議&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Node.js&lt;/td&gt;&lt;td&gt;使用目前 LTS 版本&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;npm&lt;/td&gt;&lt;td&gt;隨 Node.js 安裝即可&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Git&lt;/td&gt;&lt;td&gt;使用最新版&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;檢查本機工具：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;node -v&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm -v&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git --version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;本機專案檢查&quot;&gt;本機專案檢查&lt;/h1&gt;
&lt;p&gt;進入專案資料夾：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cd C:\\Users\\Tavric\\Documents\\projects\\Wordmem&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;安裝套件：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm install&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;建立本機環境變數檔：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Copy-Item .env.example .env.local&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;.env.local&lt;/code&gt; 先保留範例值也可以，等 Google Cloud 設定完成後再回來填入。&lt;/p&gt;
&lt;p&gt;啟動本機開發伺服器：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm run dev&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;瀏覽器開啟 Vite 顯示的網址，通常是：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;http://localhost:5173&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;正式部署前，建議先跑一次檢查：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm run typecheck&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm run lint&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm run test&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm run build&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果以上指令都成功，代表專案基本狀態正常。&lt;/p&gt;
&lt;h1 id=&quot;建立-github-repository&quot;&gt;建立 GitHub Repository&lt;/h1&gt;
&lt;h2 id=&quot;在-github-建立空-repository&quot;&gt;在 GitHub 建立空 Repository&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;開啟 GitHub。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;點右上角 &lt;code&gt;+&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;選擇 &lt;code&gt;New repository&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Repository name 可填：&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Wordmem&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Visibility 可選 &lt;code&gt;Public&lt;/code&gt; 或 &lt;code&gt;Private&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;不要勾選 &lt;code&gt;Add a README file&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;不要勾選 &lt;code&gt;.gitignore&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;不要勾選 &lt;code&gt;Choose a license&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;點 &lt;code&gt;Create repository&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;這樣 GitHub 會建立一個空的遠端 Repository。&lt;/p&gt;
&lt;h2 id=&quot;將本機專案推上-github&quot;&gt;將本機專案推上 GitHub&lt;/h2&gt;
&lt;p&gt;回到 PowerShell，確認目前在專案根目錄：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cd C:\\Users\\Tavric\\Documents\\projects\\Wordmem&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;確認 Git 狀態：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git status&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果專案還沒有初始化 Git：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git init&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;加入檔案：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git add .&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;建立第一次 commit：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git commit -m &quot;Initial commit&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;把遠端 Repository 加進來。請把網址換成你的 GitHub Repository URL：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git remote add origin &amp;#x3C;https://github.com/你的帳號/Wordmem.git&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;設定主要分支名稱：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git branch -M main&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;推送到 GitHub：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git push -u origin main&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;推送完成後，重新整理 GitHub Repository 頁面，應該會看到專案檔案。&lt;/p&gt;
&lt;h1 id=&quot;google-cloud-設定&quot;&gt;Google Cloud 設定&lt;/h1&gt;
&lt;p&gt;Wordmem 的 Google Drive 備份功能需要 Google OAuth 2.0 Client ID。這個 Client ID 會放在 Vercel 的環境變數 &lt;code&gt;VITE_GOOGLE_CLIENT_ID&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id=&quot;建立-google-cloud-project&quot;&gt;建立 Google Cloud Project&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;開啟 Google Cloud Console：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;https://console.cloud.google.com/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;登入 Google 帳號。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;點上方專案選擇器。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;點 &lt;code&gt;New Project&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Project name 可填：&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Wordmem&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Organization 沒有也沒關係。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;點 &lt;code&gt;Create&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;等待專案建立完成。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;確認目前選到剛建立的 &lt;code&gt;Wordmem&lt;/code&gt; 專案。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;啟用-google-drive-api&quot;&gt;啟用 Google Drive API&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;在 Google Cloud Console 左上角選單打開 &lt;code&gt;APIs &amp;#x26; Services&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;進入 &lt;code&gt;Library&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;搜尋：&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Google Drive API&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;點進 &lt;code&gt;Google Drive API&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;點 &lt;code&gt;Enable&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;啟用完成後，Wordmem 才能透過 Drive API 建立資料夾、上傳備份、列出備份與下載備份。&lt;/p&gt;
&lt;h2 id=&quot;設定-oauth-consent-screen&quot;&gt;設定 OAuth Consent Screen&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;到 &lt;code&gt;APIs &amp;#x26; Services&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;進入 &lt;code&gt;OAuth consent screen&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果看到使用者類型，選擇 &lt;code&gt;External&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;點 &lt;code&gt;Create&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;填寫 App 資訊：&lt;/p&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;欄位&lt;/th&gt;&lt;th&gt;建議填寫&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;App name&lt;/td&gt;&lt;td&gt;Wordmem&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;User support email&lt;/td&gt;&lt;td&gt;你的 email&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;App logo&lt;/td&gt;&lt;td&gt;可先不填&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;App domain&lt;/td&gt;&lt;td&gt;部署完成前可先空著&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Developer contact information&lt;/td&gt;&lt;td&gt;你的 email&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;儲存後繼續。&lt;/p&gt;
&lt;h2 id=&quot;設定-scopes&quot;&gt;設定 Scopes&lt;/h2&gt;
&lt;p&gt;Wordmem 使用的 Google Drive scope 是：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;https://www.googleapis.com/auth/drive.file&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;設定方式：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;在 OAuth consent screen 的 Scopes 步驟點 &lt;code&gt;Add or Remove Scopes&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;搜尋 &lt;code&gt;drive.file&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;勾選 Google Drive API 的 &lt;code&gt;.../auth/drive.file&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;點 &lt;code&gt;Update&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;儲存並繼續。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;drive.file&lt;/code&gt; 的用途是讓 Wordmem 建立與管理自己的備份檔，不需要完整讀取使用者整個 Google Drive。&lt;/p&gt;
&lt;h2 id=&quot;加入測試使用者&quot;&gt;加入測試使用者&lt;/h2&gt;
&lt;p&gt;如果 OAuth App 還在 Testing 狀態，只有測試使用者能登入。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;在 OAuth consent screen 找到 &lt;code&gt;Test users&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;點 &lt;code&gt;Add users&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;輸入你的 Google 帳號 email。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;儲存。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果你要給其他人測試，也要把對方的 Google 帳號加入測試使用者。&lt;/p&gt;
&lt;h2 id=&quot;建立-oauth-client-id&quot;&gt;建立 OAuth Client ID&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;到 &lt;code&gt;APIs &amp;#x26; Services&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;進入 &lt;code&gt;Credentials&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;點 &lt;code&gt;Create Credentials&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;選擇 &lt;code&gt;OAuth client ID&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Application type 選：&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Web application&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Name 可填：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Wordmem Web&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;設定-authorized-javascript-origins&quot;&gt;設定 Authorized JavaScript Origins&lt;/h2&gt;
&lt;p&gt;這裡要填允許載入 Google 登入服務的網站來源。&lt;/p&gt;
&lt;p&gt;本機開發先加入：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;http://localhost:5173&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vercel 正式網址稍後部署完成才會知道，之後要回來加入，例如：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;https://wordmem.vercel.app&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果 Vercel 給你的網址不同，請填你的實際網址。&lt;/p&gt;
&lt;h2 id=&quot;設定-authorized-redirect-uris&quot;&gt;設定 Authorized Redirect URIs&lt;/h2&gt;
&lt;p&gt;Wordmem 目前使用 Google Identity Services 的 token client，不需要傳統 redirect callback 頁面。&lt;/p&gt;
&lt;p&gt;一般情況下，&lt;code&gt;Authorized redirect URIs&lt;/code&gt; 可以不填。&lt;/p&gt;
&lt;p&gt;如果 Google Cloud 介面要求你填寫，或你之後改成 redirect flow，才需要額外設定 callback URL。&lt;/p&gt;
&lt;h2 id=&quot;複製-client-id&quot;&gt;複製 Client ID&lt;/h2&gt;
&lt;p&gt;建立完成後，Google Cloud 會顯示 OAuth Client。&lt;/p&gt;
&lt;p&gt;複製 &lt;code&gt;Client ID&lt;/code&gt;，格式大約像：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;1234567890-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;這個值等等會填到：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;VITE_GOOGLE_CLIENT_ID&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;本機設定-google-client-id&quot;&gt;本機設定 Google Client ID&lt;/h1&gt;
&lt;p&gt;打開 &lt;code&gt;.env.local&lt;/code&gt;，填入剛剛複製的 Client ID：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;VITE_GOOGLE_CLIENT_ID=你的-client-id.apps.googleusercontent.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;重新啟動開發伺服器：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm run dev&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;測試流程：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;開啟 Wordmem。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;建立至少一個單字集。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;開啟備份與匯入。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;點登入 Google。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;完成授權。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;點備份到 Drive。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;到 Google Drive 確認是否出現 &lt;code&gt;Wordmem&lt;/code&gt; 資料夾與 ZIP 備份檔。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果看到 &lt;code&gt;尚未設定 VITE_GOOGLE_CLIENT_ID&lt;/code&gt;，代表 &lt;code&gt;.env.local&lt;/code&gt; 沒有設定成功或開發伺服器沒有重新啟動。&lt;/p&gt;
&lt;h1 id=&quot;vercel-部署&quot;&gt;Vercel 部署&lt;/h1&gt;
&lt;h2 id=&quot;匯入-github-專案&quot;&gt;匯入 GitHub 專案&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;開啟 Vercel：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;https://vercel.com/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;使用 GitHub 帳號登入。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;點 &lt;code&gt;Add New&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;選擇 &lt;code&gt;Project&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;找到 &lt;code&gt;Wordmem&lt;/code&gt; Repository。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;點 &lt;code&gt;Import&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;設定-framework-與-build&quot;&gt;設定 Framework 與 Build&lt;/h2&gt;
&lt;p&gt;Vercel 通常會自動偵測為 Vite 專案。&lt;/p&gt;
&lt;p&gt;確認設定如下：&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;項目&lt;/th&gt;&lt;th&gt;值&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Framework Preset&lt;/td&gt;&lt;td&gt;Vite&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Build Command&lt;/td&gt;&lt;td&gt;&lt;code&gt;npm run build&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Output Directory&lt;/td&gt;&lt;td&gt;&lt;code&gt;dist&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Install Command&lt;/td&gt;&lt;td&gt;&lt;code&gt;npm install&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Root Directory 如果專案就在 Repository 根目錄，保持預設即可。&lt;/p&gt;
&lt;h2 id=&quot;設定-vercel-環境變數&quot;&gt;設定 Vercel 環境變數&lt;/h2&gt;
&lt;p&gt;在部署前，找到 Environment Variables 區塊。&lt;/p&gt;
&lt;p&gt;新增：&lt;/p&gt;













&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Name&lt;/th&gt;&lt;th&gt;Value&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;VITE_GOOGLE_CLIENT_ID&lt;/code&gt;&lt;/td&gt;&lt;td&gt;你的 Google OAuth Client ID&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Environment 建議全部勾選：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Production&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Preview&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Development&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;填好後再部署。&lt;/p&gt;
&lt;h2 id=&quot;開始部署&quot;&gt;開始部署&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;點 &lt;code&gt;Deploy&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;等待 Vercel 安裝套件與執行 build。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;部署成功後，Vercel 會提供網址，例如：&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;https://wordmem.vercel.app&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;請複製你的實際網址。&lt;/p&gt;
&lt;h1 id=&quot;回到-google-cloud-加入正式網域&quot;&gt;回到 Google Cloud 加入正式網域&lt;/h1&gt;
&lt;p&gt;Vercel 部署完成後，必須把正式網址加入 Google OAuth Client 的 Authorized JavaScript Origins。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;回到 Google Cloud Console。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;進入你的 &lt;code&gt;Wordmem&lt;/code&gt; Project。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;到 &lt;code&gt;APIs &amp;#x26; Services&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;進入 &lt;code&gt;Credentials&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;點剛剛建立的 OAuth 2.0 Client ID。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在 &lt;code&gt;Authorized JavaScript origins&lt;/code&gt; 加入 Vercel 網址，例如：&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;https://wordmem.vercel.app&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;儲存。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;注意：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;不要在網址最後加 &lt;code&gt;/&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;必須使用 &lt;code&gt;https://&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;必須填實際部署網址。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果你使用自訂網域，也要把自訂網域加入。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Google Cloud 設定更新後可能需要幾分鐘才完全生效。&lt;/p&gt;
&lt;h1 id=&quot;驗證正式站&quot;&gt;驗證正式站&lt;/h1&gt;
&lt;p&gt;打開 Vercel 正式網址，逐項確認。&lt;/p&gt;
&lt;h2 id=&quot;基本功能&quot;&gt;基本功能&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;首頁可正常載入。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可新增單字集。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可編輯單字集。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可進入單字卡。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可進入選擇題練習。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可進入拼字測試。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;測驗完成後可看到結果頁。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;路由重新整理&quot;&gt;路由重新整理&lt;/h2&gt;
&lt;p&gt;Wordmem 使用 Vue Router，&lt;code&gt;vercel.json&lt;/code&gt; 已設定 rewrite：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt;  &quot;rewrites&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;: [{ &lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt;&quot;source&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt;&quot;/(.*)&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt;&quot;destination&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt;&quot;/index.html&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; }]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;請測試：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;進入任一練習頁。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;重新整理瀏覽器。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;確認不會出現 404。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;pwa-與快取&quot;&gt;PWA 與快取&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;vercel.json&lt;/code&gt; 也針對以下檔案設定重新驗證：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;sw.js&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;version.json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;index.html&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;這能降低使用者卡在舊版本的機率。&lt;/p&gt;
&lt;p&gt;部署新版本後，如果畫面沒有更新，可以嘗試：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;重新整理頁面。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;關閉再打開瀏覽器分頁。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;清除該網站快取。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;google-drive-備份&quot;&gt;Google Drive 備份&lt;/h2&gt;
&lt;p&gt;正式站測試：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;開啟 Wordmem 正式網址。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;建立一個測試單字集。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;開啟備份與匯入。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;點登入 Google。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;選擇已加入測試使用者的 Google 帳號。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;完成授權。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;點備份到 Drive。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;到 Google Drive 檢查是否有 &lt;code&gt;Wordmem&lt;/code&gt; 資料夾與 ZIP 檔。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;回到 App，點讀取 Drive 備份。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;確認可以列出備份並選擇匯入。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&quot;常見問題&quot;&gt;常見問題&lt;/h1&gt;
&lt;h2 id=&quot;google-登入顯示-access-blocked&quot;&gt;Google 登入顯示 access blocked&lt;/h2&gt;
&lt;p&gt;可能原因：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;OAuth consent screen 還在 Testing，但你的 Google 帳號沒有加入 Test users。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;OAuth App 設定不完整。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用了未授權的網域。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;處理方式：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;到 Google Cloud 的 OAuth consent screen。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;確認你的 email 已加入 Test users。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;確認 Authorized JavaScript origins 有正式 Vercel 網址。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;等幾分鐘後再重試。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;google-登入顯示-origin-mismatch&quot;&gt;Google 登入顯示 origin mismatch&lt;/h2&gt;
&lt;p&gt;代表目前網站網址沒有被加入 Authorized JavaScript origins。&lt;/p&gt;
&lt;p&gt;請檢查：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;本機開發是否加入 &lt;code&gt;http://localhost:5173&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;正式站是否加入你的 Vercel 網址&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;網址是否多了結尾 &lt;code&gt;/&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;http&lt;/code&gt; 與 &lt;code&gt;https&lt;/code&gt; 是否填錯&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;app-顯示尚未設定-vite_google_client_id&quot;&gt;App 顯示尚未設定 VITE_GOOGLE_CLIENT_ID&lt;/h2&gt;
&lt;p&gt;代表環境變數沒有正確注入。&lt;/p&gt;
&lt;p&gt;本機請檢查：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;.env.local&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vercel 請檢查：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Project Settings -&gt; Environment Variables&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;修改 Vercel 環境變數後，需要重新部署。&lt;/p&gt;
&lt;h2 id=&quot;vercel-部署成功但-google-drive-仍不能用&quot;&gt;Vercel 部署成功但 Google Drive 仍不能用&lt;/h2&gt;
&lt;p&gt;請確認：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Vercel 有設定 &lt;code&gt;VITE_GOOGLE_CLIENT_ID&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;修改環境變數後已重新部署。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Google Cloud 已啟用 Google Drive API。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;OAuth Client ID 的 Authorized JavaScript origins 有正式網址。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用的 Google 帳號已加入 Test users，或 OAuth App 已正式發布。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;重新整理練習頁出現-404&quot;&gt;重新整理練習頁出現 404&lt;/h2&gt;
&lt;p&gt;請確認 Repository 內有 &lt;code&gt;vercel.json&lt;/code&gt;，且內容包含：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt;  &quot;rewrites&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;: [{ &lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt;&quot;source&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt;&quot;/(.*)&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt;&quot;destination&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt;&quot;/index.html&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; }]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你剛新增或修改 &lt;code&gt;vercel.json&lt;/code&gt;，請 commit、push，並讓 Vercel 重新部署。&lt;/p&gt;
&lt;h2 id=&quot;npm-run-build-在-vercel-失敗&quot;&gt;npm run build 在 Vercel 失敗&lt;/h2&gt;
&lt;p&gt;先在本機執行：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm run typecheck&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm run lint&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm run test&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm run build&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;本機如果也失敗，先修本機錯誤。&lt;/p&gt;
&lt;p&gt;本機成功但 Vercel 失敗時，檢查：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Node.js 版本是否差太多。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;package-lock.json 是否已 commit。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;是否有缺少環境變數。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Vercel Build Logs 的第一個錯誤訊息。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;更新部署&quot;&gt;更新部署&lt;/h1&gt;
&lt;p&gt;日後修改程式後，照以下流程更新正式站。&lt;/p&gt;
&lt;p&gt;檢查狀態：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git status&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;加入變更：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git add .&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;建立 commit：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git commit -m &quot;Update Wordmem&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;推送到 GitHub：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git push&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vercel 會自動偵測 GitHub 的新 commit，並開始重新部署。&lt;/p&gt;
&lt;h1 id=&quot;自訂網域&quot;&gt;自訂網域&lt;/h1&gt;
&lt;p&gt;如果你不想使用 Vercel 預設網址，可以設定自訂網域。&lt;/p&gt;
&lt;h2 id=&quot;在-vercel-加入網域&quot;&gt;在 Vercel 加入網域&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;到 Vercel 專案。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;進入 &lt;code&gt;Settings&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;進入 &lt;code&gt;Domains&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;輸入你的網域，例如：&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;wordmem.example.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;依照 Vercel 顯示的 DNS 指示設定。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;在-google-cloud-加入自訂網域&quot;&gt;在 Google Cloud 加入自訂網域&lt;/h2&gt;
&lt;p&gt;自訂網域啟用後，回到 Google Cloud OAuth Client。&lt;/p&gt;
&lt;p&gt;在 Authorized JavaScript origins 加入：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;https://wordmem.example.com&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果使用裸網域，也加入：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;https://example.com&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;只加入你實際會使用的網址即可。&lt;/p&gt;
&lt;h1 id=&quot;上線前檢查清單&quot;&gt;上線前檢查清單&lt;/h1&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;
&lt;p&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; GitHub Repository 已建立。&lt;/p&gt;
&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;
&lt;p&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; 本機專案已 push 到 GitHub。&lt;/p&gt;
&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;
&lt;p&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Google Cloud Project 已建立。&lt;/p&gt;
&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;
&lt;p&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Google Drive API 已啟用。&lt;/p&gt;
&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;
&lt;p&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; OAuth consent screen 已設定。&lt;/p&gt;
&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;
&lt;p&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Scope 已加入 &lt;code&gt;drive.file&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;
&lt;p&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; 測試使用者已加入。&lt;/p&gt;
&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;
&lt;p&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; OAuth Client ID 已建立。&lt;/p&gt;
&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;
&lt;p&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; 本機 &lt;code&gt;http://localhost:5173&lt;/code&gt; 已加入 Authorized JavaScript origins。&lt;/p&gt;
&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;
&lt;p&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Vercel 專案已連接 GitHub。&lt;/p&gt;
&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;
&lt;p&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Vercel Build Command 是 &lt;code&gt;npm run build&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;
&lt;p&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Vercel Output Directory 是 &lt;code&gt;dist&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;
&lt;p&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Vercel 已設定 &lt;code&gt;VITE_GOOGLE_CLIENT_ID&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;
&lt;p&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Vercel 正式網址已加入 Google Cloud Authorized JavaScript origins。&lt;/p&gt;
&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;
&lt;p&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; 正式站可建立單字集。&lt;/p&gt;
&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;
&lt;p&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; 正式站可完成測驗。&lt;/p&gt;
&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;
&lt;p&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; ZIP 匯出匯入正常。&lt;/p&gt;
&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;
&lt;p&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Google Drive 備份與匯入正常。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;建議維護方式&quot;&gt;建議維護方式&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;每次功能修改後先跑 &lt;code&gt;npm run typecheck&lt;/code&gt;、&lt;code&gt;npm run lint&lt;/code&gt;、&lt;code&gt;npm run test&lt;/code&gt;、&lt;code&gt;npm run build&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;每次正式部署後測一次 Google Drive 登入與備份。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;定期確認 Google Cloud OAuth 設定沒有被改動。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果要開放給更多使用者，需評估是否把 OAuth App 從 Testing 發布到 Production。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;重要單字資料請定期匯出 ZIP，避免只依賴瀏覽器本機資料。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Wordmem</category></item></channel></rss>