Remix Speed Metal Stack

了解更多关于 Remix Stacks 的信息。

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

Remix 博客 📖

这个博客启动模板的灵感很大程度上来自于 Kent C. Dodds 对 kentcdodds.com 的实现。您可以在 我如何在 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 actions 工作流,可自动将应用程序部署到 Fly.io。首先,我们需要使用一些密钥设置 GitHub actions 和 Fly 应用程序。现在开始执行此操作。

要从 GitHub action 将构建映像推送到远程 Fly 注册表,我们需要 Fly 的访问令牌。我们可以使用 Fly 命令行生成该令牌,运行

flyctl auth token

该命令将生成一个访问令牌。然后,您可以访问 GitHub 存储库的 settings 页面 https://github.com/:owner/:repo/settings/secrets/actions,然后单击 New repository secret,将此令牌添加到 GitHub actions 密钥。接下来,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 action 和部署在远程服务器上的应用程序之间进行安全通信的令牌,因为我们需要一个面向公众的 API 来进行此通信。

openssl rand -hex 32

我们必须将此密钥设置为 GitHub actions 密钥和 Fly 密钥的一部分。键应为 REFRESH_TOKEN。您可以在 GitHub 中创建一个新的 actions 密钥,并通过运行该命令为 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 actions 工作流配置为在推送到 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 测试库,以及用于 e2e 测试的 Cypress,并配置为作为 GitHub 操作的一部分运行。您可以运行 npm run test 命令来运行 Jest 测试套件。并运行 npm run test:e2e:run 以在无头模式下运行 Cypress 测试。您可以查看 package.json 以获取可用命令。

类型检查 ʦ

您可以运行 npm run typecheck 以在代码库上运行 tsc。类型检查也包含在部署工作流程中。

调试

一些有用的命令,用于在 Fly 上使用命令行调试应用程序

日志

您可以使用项目目录中的命令 flyctl logs 查看日志,其中包含项目根目录中的 fly.toml 文件。您还可以通过访问 fly.io/apps 从控制台查看日志。

控制台

您还可以使用 flyctl ssh console 命令登录到远程控制台。

数据库

登录到控制台后,您还可以检查 SQLite 数据库。但是首先,我们必须在远程机器上安装 SQLite。我们可以使用 apt-get install sqlite3 命令来完成。然后,使用 cd data 命令进入卷(注意:data 是指从命令行创建的卷的名称)。然后运行命令 sqlite3 sqlite.db 以打开数据库的命令行界面。