Remix Speed Metal Stack

深入了解 Remix Stacks

npx create-remix --template Girish21/speed-metal-stack

Remix 部落格 📖

這個部落格起始範本很大程度地受到 Kent C. Dodds 實作的 kentcdodds.com 啟發。你可以在 How I built a modern website in 2021 閱讀更多關於架構和背後概念的資訊。

架構 💡

website-architecture

重要 🚧

Fly 要求所有應用程式都必須有一個全域唯一的名稱,我們使用目錄名稱和隨機雜湊作為應用程式名稱。當然,你可以在使用 Fly CLI 啟動應用程式之前隨時更改此名稱。不過,這並不是什麼大問題,因為你可以透過將 CNAME 紀錄新增到你的自訂網域,使其指向 Fly 內部 URL,來將 Fly 內部 URL 重新指定到任何自訂網域。我們稍後在將應用程式部署到正式環境時會看到。

快速開始

# run database migrations and set up the initial blog
npm run setup
# run the initial build for the development server
npm run build
# start the development server and run other processes in parallel in watch mode
npm run dev

可用腳本

  • build - 編譯並建立 express 伺服器、Remix 應用程式、Tailwind 在 production 模式中
  • dev - 啟動 express 伺服器、Remix 監看程式、Tawilwind CLI 在監看模式中
  • format - 在程式碼庫上執行 prettier 並修復可修復的問題
  • lint - 在程式碼庫上執行 ESLint
  • new:blog - 從命令列建立新的部落格文章範本
  • start - 啟動 express 伺服器(應僅在執行 npm run build 之後執行)
  • test - 執行 vitest
  • test:e2e:dev - 在開發模式下啟動 cypress 執行器
  • test:e2e:run - 在 CI 模式下啟動 cypress 執行器
  • typecheck - 在程式碼庫上執行類型檢查

Fly 設定 🛠

  1. 安裝 Fly

  2. 註冊並登入 Fly

    flyctl auth signup
    

資料庫 🗃

我們在這個範本中使用 SQLite 作為資料庫。SQLite 是一個快速的資料庫引擎,是持久化資料而不需使用 Postgres 等進階資料庫引擎的絕佳選擇。

安裝 ⚙️

SQLite 已預先安裝在所有 Mac 上。你可以在其他作業系統的官方安裝指南中查看 SQLite 安裝指南

為什麼需要資料庫 ❓

我們使用 MDX-Bundler 來編譯 MDX 檔案,而 MDX-Bundler 在內部使用 esbuild 來編譯 MDX 檔案。雖然 esbuild 速度很快,但在這個過程中會出現明顯的延遲,這不適合網站的效能和使用者體驗。而且,由於當資料沒有變更時,沒有必要在每個請求都編譯 MDX,這似乎是浪費時間和效能。因此,我們可以快取編譯後的 MDX,並且僅在我們知道內容已變更時才重新編譯。

Prisma △

我們在這個範本中使用 Prisma 作為 ORM。若要建立 SQLite 資料庫並初始化資料庫架構,請執行

npx prisma migrate dev

以上命令會提示輸入遷移名稱,你可以將其命名為 initial migration。此命令還會安裝 Prisma Client 以與資料庫互動。

開發 💻

我們可以使用執行遷移和使用初始架構填入的 SQLite 資料庫來啟動我們的開發伺服器。然後,在你的終端機中的新分頁中,執行命令。

npm run dev

此命令會同時啟動四個程序。

  • Remix 開發伺服器在開發模式下啟動,並在檔案變更時重建資產。
  • Tailwind CLI,在樣式變更時重建樣式表
  • 一個 MSW 伺服器,它攔截對 GitHub 的 API 呼叫,並從本機提供內容,而不是呼叫遠端 API
  • 一個檔案監看程式,監看 content 目錄並重建資產。

相關檔案 🔍

部署 🚀

初始設定 👀

在繼續部署我們的應用程式之前,我們有一些步驟需要處理

  • 建立一個 GitHub 帳戶 GitHub
  • 在 Fly 上建立一個新的應用程式
flyctl launch --name YOUR_APP_NAME --copy-config --no-deploy

⚠️ 請記住不要部署,因為我們還有一些設定步驟需要完成!

環境變數和機密 🤫

這個範本隨附 GitHub 動作工作流程,可自動將應用程式部署到 Fly.io。首先,我們需要設定我們的 GitHub 動作和 Fly 應用程式,並設定一些機密。現在開始設定。

若要從 GitHub 動作將組建映像推送至遠端 Fly 登錄檔,我們需要 Fly 的存取權杖。我們可以透過 Fly 命令列產生該權杖,執行

flyctl auth token

該命令會產生一個存取權杖。然後,你可以透過瀏覽你的 GitHub 儲存庫的 settings 頁面 https://github.com/:owner/:repo/settings/secrets/actions,然後按一下 New repository secret,將此權杖新增至你的 GitHub 動作機密。接下來,GitHub 會提示輸入金鑰和值。金鑰應為 FLY_API_TOKEN,而值將是命令列產生的權杖。

我們還需要將 Fly 應用程式名稱設定為機密,金鑰應為 FLY_APP_NAME,而值將是 fly.toml 中指定的應用程式名稱

現在我們需要在我們的 Fly 應用程式中設定機密。

由於我們是按需從 GitHub 擷取內容,而不是預先建立所有頁面,我們需要來自 GitHub 的存取權杖才能呼叫 GitHub API 並擷取內容。此外,GitHub 不會限制應用程式更頻繁地呼叫 GitHub API。因此,你可以在 個人存取權杖 產生存取權杖。然後,你可以複製產生的權杖並將其設定為應用程式的機密。我們可以透過執行以下命令來執行此操作

flyctl secrets set GITHUB_TOKEN={GITHUB_TOKEN}

我們還需要一個機密來簽署我們的會話。我們可以透過執行命令來執行此操作

flyctl secrets set SESSION_SECRETS=$(openssl rand -hex 32)

如果 openssl 不可用,你可以使用密碼產生服務(例如 1Password)來產生安全權杖。

所需的最後一個機密是用於在 GitHub 動作和部署在遠端伺服器上的應用程式之間安全通訊的權杖,因為我們需要一個公開的 API 來進行此通訊。

openssl rand -hex 32

我們必須將此機密設定為 GitHub 動作機密和 Fly 機密的一部分。金鑰應為 REFRESH_TOKEN。你可以在 GitHub 中建立新的動作機密,並透過執行命令為 Fly 應用程式建立新的機密。

flyctl secrets set REFRESH_TOKEN={GENERATED_PASSWORD}

磁碟區 💾

我們還需要在 Fly 中建立磁碟區,以保留我們的應用程式資料 (SQLite DB),以便 Fly 可以保留跨部署和容器重新啟動儲存的資料。同樣,我們可以透過 Fly 命令列來執行此操作。

flyctl volumes create data --region [REGION] --size 1

注意:REGION 應為啟動應用程式時選取的區域。你可以透過執行 flyctl regions list 來查看選取的區域。

重要的是要注意,磁碟區會繫結到區域中的應用程式,並且無法在相同區域或跨多個區域的應用程式之間共用。

你可以在 這裡 深入了解 Fly 磁碟區

推送到正式環境 🥳

我們已準備好進行首次部署。GitHub 動作工作流程已設定為在推送到 main 分支時執行。因此,讓我們將本機分支 main 推送到遠端,觸發工作流程。

通過所有檢查,並且部署完成後,你可以執行

flyctl info

以取得目前的應用程式 URL 和 IP 位址。應用程式 URL 將為 https://YOUR_APP_NAME.fly.dev。你可以瀏覽該 URL,網站應該會上線。就是這樣。你已部署了使用 REMIX 建立的部落格!。

新增自訂網域 🔖

若要將自訂網域新增至應用程式,你必須先從網域名稱註冊商購買網域,你可以選擇你的偏好。一些常見的選項包括 Domain.comGoogleCloudflare

購買網域後,我們可以新增 DNS 記錄,以指向網域,或建立子網域並將其指向 Fly 應用程式 URL。我們可以透過使用 CNAME 選項新增 DNS 記錄並輸入 Fly URL https://YOUR_APP_NAME.fly.dev 來執行此操作。

我們還必須使用網域名稱在 Fly 上建立 SSL 憑證。我們可以透過執行命令來執行此操作

flyctl certs create [DOMAIN]

你可以在 適用於自訂網域的 SSL 深入了解此內容

就這樣,我們已準備好與世界分享我們的部落格!但在分享之前,還有一個步驟需要處理。

擴展 ⚖️

擴展應用程式的方式有兩種,垂直和水平。

在垂直擴展中,系統會透過將更多運算資源新增至伺服器(增加 CPU/RAM)來擴展。Fly 支援垂直擴展,你可以在這裡查看文件 擴展虛擬機器

水平擴展是透過新增相同應用程式的更多複本來實現,無論是在相同區域還是全球其他區域。Fly 支援全球許多區域,你可以在這裡深入了解它們 Fly 區域

我們的應用程式目前僅部署在我們執行 flyctl launch 時選擇的單一區域。在原型設計和開發階段這沒有問題,但當推向生產環境時,我們會希望我們的應用程式能從全球各區域存取,並為全球使用者提供相似的效能。在這種情況下,我們可以增加應用程式在全球的複本,至少在目標使用者眾多的區域,讓應用程式在更靠近使用者的伺服器上執行,並且所有使用者都能有相當的效能。

由於 Fly 會根據建立的磁碟區錨定區域,我們可以透過在新區域建立磁碟區或在相同區域新增複本來增加更多區域。例如,我們可以透過以下方式達成:

flyctl volumes create data --region [NEW_REGION] --size 1

您可以在Fly 區域查看可用的區域列表

然後,執行以下命令來增加 Fly 應用程式的規模計數

flyctl scale count 2

上述命令會將規模計數設定為 2,表示我們的應用程式的兩個實例將會在建立磁碟區的指定區域上執行。

您可以在Fly 擴展閱讀更多關於擴展的資訊

API 模擬 🔸

我們的架構是從 GitHub 儲存庫按需獲取部落格內容,而不是將內容作為建置的一部分進行捆綁。因此,我們將會向 GitHub 發出大量的 API 呼叫。而任何 API 都會有限制,例如速率限制、每分鐘的呼叫次數等。當我們正在撰寫文章時,由於我們正在呼叫 GitHub API,這個過程會變得繁瑣;文章必須在 GitHub 上,API 才能返回內容。這個過程並不理想。我們可以做得更好。

相反地,我們可以模擬對 GitHub 的 API 請求,並在本地提供文章,提供更好的體驗。我們不呼叫 GitHub API,而是使用 MSW 攔截請求,並返回一個模擬的回應,該回應會從本地檔案系統提供內容。

程式碼檢查 ⬡

此範本已預先設定了 ESLintPrettier 規則,並作為建置步驟整合到工作流程中。例如,您可以執行 npm run lint 在專案上執行 ESLint,以及執行 npm run format 來執行 Prettier。

樣式設定 💅🏻

此範本已預先設定了 Tailwindcss,並包含開發和生產期間所需的所有腳本。

此範本還預先設定了一個主題切換器,可以偵測合適的主題並防止 FART

測試 🔬

此範本已預先設定了 JestReact testing library 用於單元測試,以及 Cypress 用於 e2e 測試,並設定為作為 GitHub Actions 的一部分執行。您可以執行 npm run test 命令來執行 Jest 測試套件。以及 npm run test:e2e:run 在無頭模式下執行 Cypress 測試。您可以查看 package.json 以取得可用的命令。

型別檢查 ʦ

您可以執行 npm run typecheck 在程式碼庫上執行 tsc。型別檢查也包含在部署工作流程中。

除錯

一些有用的命令,可以使用命令列在 Fly 上除錯應用程式

日誌

您可以使用專案目錄中包含 fly.toml 檔案的 flyctl logs 命令來檢查日誌。您也可以造訪 fly.io/apps 從主控台檢查日誌。

主控台

您也可以使用 flyctl ssh console 命令登入遠端主控台。

資料庫

登入主控台後,您也可以檢查 SQLite 資料庫。但首先,我們必須在遠端機器上安裝 SQLite。我們可以使用 apt-get install sqlite3 命令來完成。然後,使用 cd data 命令 cd 到磁碟區中 (注意:data 是指從命令列建立的磁碟區名稱)。然後執行 sqlite3 sqlite.db 命令以開啟資料庫的命令列介面。