Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,19 @@ jobs:
env: # Or as an environment variable
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
S3_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY_ID }}
S3_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_ACCESS_KEY }}
JWT_SECRET: ${{ secrets.JWT_SECRET }}

DB_NAME: ${{ vars.DB_NAME }}
WORKER_NAME: ${{ vars.WORKER_NAME }}
FRONTEND_URL: ${{ vars.FRONTEND_URL }}
S3_ACCESS_HOST: ${{ vars.S3_ACCESS_HOST }}
S3_BUCKET: ${{ vars.S3_BUCKET }}
S3_CACHE_FOLDER: ${{ vars.S3_CACHE_FOLDER }}
S3_ENDPOINT: ${{ vars.S3_ENDPOINT }}
S3_FOLDER: ${{ vars.S3_FOLDER }}
S3_REGION: ${{ vars.S3_REGION }}
run: |
cd Rin/
bun i
Expand Down
46 changes: 46 additions & 0 deletions .github/workflows/seo.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: SEO Workflow

on:
schedule:
- cron: "0 * * * *"
workflow_dispatch:


jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Clone Rin repository
uses: actions/checkout@v4
with:
path: Rin

- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: 21

- name: Set up Bun
uses: oven-sh/setup-bun@v1

- name: Set up Puppeteer
run: |
sudo apt-get update
sudo apt-get install -y libgbm-dev

- name: Run script
env: # Or as an environment variable
S3_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY_ID }}
S3_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_ACCESS_KEY }}
S3_REGION: ${{ vars.S3_REGION }}
S3_ENDPOINT: ${{ vars.S3_ENDPOINT }}
SEO_BASE_URL: ${{ vars.SEO_BASE_URL }}
SEO_CONTAINS_KEY: ${{ vars.SEO_CONTAINS_KEY }}
S3_ACCESS_HOST: ${{ vars.S3_ACCESS_HOST }}
S3_BUCKET: ${{ vars.S3_BUCKET }}
S3_CACHE_FOLDER: ${{ vars.S3_CACHE_FOLDER }}
run: |
cd Rin/
bun i
bun scripts/render.ts
50 changes: 34 additions & 16 deletions docs/DEPLOY.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# 部署

## 更新日志
### v0.2.0 2024-06-07 更新
* 新增 `S3_CACHE_FOLDER` 环境变量
* 环境变量加密列表与变量列表更新,仅保留必须加密的环境变量
* 加密变量现在可以通过 Github 直接配置
* Github 变量配置更新,新增必须通过 Github 配置的加密变量(S3 存储,用于 SEO 索引保存)


## 其他文档
[环境变量列表](./ENV.md)

Expand Down Expand Up @@ -51,7 +59,7 @@ NAME=Xeu # 昵称,显示在左上角
DESCRIPTION=杂食动物 # 个人描述,显示在左上角昵称下方
AVATAR=https://avatars.githubusercontent.com/u/36541432 # 头像地址,显示在左上角
API_URL=https://rin.xeu.life # 服务端域名,可以先留空后面再改
PAGE_SIZE=10 # 默认分页大小
PAGE_SIZE=5 # 默认分页大小,推荐 5
SKIP_DEPENDENCY_INSTALL=true
UNSTABLE_PRE_BUILD=asdf install bun latest && asdf global bun latest && bun i
```
Expand Down Expand Up @@ -97,34 +105,44 @@ ID 随意点击一个自己绑定的域名,进入后在右侧(需要向下
CLOUDFLARE_ACCOUNT_ID=<你的用户ID>
CLOUDFLARE_API_TOKEN=<你的令牌>
```

同时你可以在`Actions secrets and variables`的 `Variables` 中创建以下变量:
```ini
DB_NAME=<数据库名称,默认rin>
WORKER_NAME=<Cloudflare Worker 名称,默认rin-server>
FRONTEND_URL=<前端地址,用于Webhook通知时拼接地址,可不填>
S3_FOLDER=<S3 图片资源存储的文件夹,默认为images/>
SEO_BASE_URL=<SEO 基础地址,用于 SEO 索引,默认为 FRONTEND_URL>
SEO_CONTAINS_KEY=<SEO 索引时只索引以 SEO_BASE_URL 开头或包含SEO_CONTAINS_KEY 关键字的链接,默认为空>
S3_FOLDER=<S3 图片资源存储的文件夹,默认为 'images/'>
S3_CACHE_FOLDER=<S3 缓存文件夹(用于 SEO、高频请求缓存),默认为 'cache/'>
S3_BUCKET=<S3 存储桶名称>
S3_REGION=<S3 存储桶所在区域,如使用 Cloudflare R2 填写 auto 即可>
S3_ENDPOINT=<S3 存储桶接入点地址>
S3_ACCESS_HOST=<S3 存储桶访问地址,末尾无'/'>
```
> [!TIP]
> 关于 SEO 工作原理与配置请参考 [SEO 文档](./SEO.md)

完成准备工作以后即可在 Github Action 中手动触发一次 Workflow,一切正常的话很快就能部署完成

这样服务端就部署好了,但是我们还需要配置 Github OAuth用于登录和 S3 存储用于存储图片

这样服务端就部署好了,但是目前仍然不能运行,我们还需要配置 Github OAuth用于登录和 S3 存储用于存储图片

回到 Cloudflare 面板配置后端域名与一些敏感的环境变量

在 `设置` > `触发器` > `自定义域` 处可以自定义后端的域名,默认也有分配一个`workers.dev`的域名
> [!TIP]
> 在 v0.2.0 版本后,不再需要回到 Cloudflare 面板配置后端域名与一些敏感的环境变量,所有环境变量都可以通过 Github 创建对应的密钥来添加,如果你在更早的版本中部署过,需要将环境变量迁移到 Github 中

在 `设置` > `变量` > `环境变量` 处编辑变量,点击添加变量,复制粘贴以下内容至变量名处即可自动添加上所有环境变量,之后再根据自己的具体配置修改变量值:
> ~~回到 Cloudflare 面板配置后端域名与一些敏感的环境变量~~
>
> ~~在 `设置` > `触发器` > `自定义域` 处可以自定义后端的域名,默认也有分配一个`workers.dev`的域名~~
>
> ~~在 `设置` > `变量` > `环境变量` 处编辑变量,点击添加变量,复制粘贴以下内容至变量名处即可自动添加上所有环境变量,之后再根据自己的具体配置修改变量值:~~
在 v0.2.0 版本后,以下所有环境变量都建议通过在 Github 创建对应的密钥来添加,添加方式与上文添加 `CLOUDFLARE_ACCOUNT_ID` 与 `CLOUDFLARE_API_TOKEN` 相同,以下是环境变量列表:
```
GITHUB_CLIENT_ID=YourGithubClientID
GITHUB_CLIENT_SECRET=YourGithubClientSecret
JWT_SECRET=YourJWTSecret
S3_BUCKET=YourBucketName
S3_REGION=YourRegion
S3_ENDPOINT=YourEndpoint
S3_ACCESS_HOST=YourAccessHost
S3_ACCESS_KEY_ID=YourAccessKeyID
S3_SECRET_ACCESS_KEY=YourSecretAccessKey
GITHUB_CLIENT_ID=<你的GithubClientID>
GITHUB_CLIENT_SECRET=<你的GithubClientSecret>
JWT_SECRET=<JWT密钥>
S3_ACCESS_KEY_ID=<你的S3AccessKeyID>
S3_SECRET_ACCESS_KEY=<你的S3SecretAccessKey>
```

## 接入 Github OAuth
Expand Down
21 changes: 11 additions & 10 deletions docs/ENV.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
| AVATAR | 是 | 网站左上角头像地址 | 无 | https://avatars.githubusercontent.com/u/36541432 |
| NAME | 是 | 网站左上角名称 & 标题 | 无 | Xeu |
| DESCRIPTION | 否 | 网站左上角描述 | 无 | 杂食动物 |
| PAGE_SIZE | 否 | 默认分页限制 | 10 | 10 |
| PAGE_SIZE | 否 | 默认分页限制 | 5 | 5 |

**部署环境变量列表**

Expand All @@ -28,10 +28,16 @@
> [!NOTE]
> 以下变量在 Cloudflare Workers 中保持不加密即可

| 名称 | 是否必须 | 描述 | 默认值 | 示例值 |
| ------------ | -------- | ------------------------------------------------- | ------ | ---------------- |
| FRONTEND_URL | 暂时必须 | 评论通知 Webhook 时包含评论文章链接时所需,可留空 | 无 | https://xeu.life |
| S3_FOLDER | 必须 | 上传保存图片时资源存放的文件路径 | 无 | images/ |
| 名称 | 是否必须 | 描述 | 默认值 | 示例值 |
| --------------- | -------- | ------------------------------------------------------ | ----------- | --------------------------------------------------------------- |
| FRONTEND_URL | 暂时必须 | 评论通知 Webhook 时包含评论文章链接时所需,可留空 | 无 | https://xeu.life |
| S3_FOLDER | 是 | 上传保存图片时资源存放的文件路径 | 无 | images/ |
| S3_BUCKET | 是 | S3 存储桶名称 | 无 | images |
| S3_REGION | 是 | S3 存储桶所在区域,如使用 Cloudflare R2 填写 auto 即可 | 无 | auto |
| S3_ENDPOINT | 是 | S3 存储桶接入点地址 | 无 | https://1234567890abcdef1234567890abcd.r2.cloudflarestorage.com |
| WEBHOOK_URL | 否 | 新增评论时发送 Webhook 通知目标地址 | 无 | https://webhook.example.com/webhook |
| S3_ACCESS_HOST | 否 | S3 存储桶访问地址 | S3_ENDPOINT | https://image.xeu.life |
| S3_CACHE_FOLDER | 否 | S3 缓存文件夹(用于 SEO、高频请求缓存) | cache/ | cache/ |

**加密环境变量,以下所有内容均为必须(Webhook 除外)**

Expand All @@ -44,10 +50,5 @@
| GITHUB_CLIENT_ID | Github OAuth 的客户端 ID | Ux66poMrKi1k11M1Q1b2 |
| GITHUB_CLIENT_SECRET | Github OAuth 的客户端密钥 | 1234567890abcdef1234567890abcdef12345678 |
| JWT_SECRET | JWT 认证所需密钥,可为常规格式的任意密码 | J0sT%Ch@nge#Me1 |
| S3_BUCKET | S3 存储桶名称 | images |
| S3_REGION | S3 存储桶所在区域,如使用 Cloudflare R2 填写 auto 即可 | auto |
| S3_ENDPOINT | S3 存储桶接入点地址 | https://1234567890abcdef1234567890abcd.r2.cloudflarestorage.com |
| S3_ACCESS_HOST | S3 存储桶访问地址 | https://image.xeu.life |
| S3_ACCESS_KEY_ID | S3 存储桶访问所需的 KEY ID,使用 Cloudflare R2 时为拥有 R2 编辑权限的 API 令牌 ID | 1234567890abcdef1234567890abcd |
| S3_SECRET_ACCESS_KEY | S3 存储桶访问所需的 Secret,使用 Cloudflare R2 时为拥有 R2 编辑权限的 API 令牌 | 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef |
| WEBHOOK_URL | 唯一非必须环境变量 | https://webhook.example.com/webhook |
56 changes: 56 additions & 0 deletions docs/SEO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# SEO 工作原理介绍与配置指南
## 前言
由于采用前后端分离的技术,导致搜索引擎无法直接获取到页面内容,因此需要通过 SEO 优化来提高搜索引擎的收录效果。本文将介绍本项目中 SEO 实现的工作原理与配置指南。

## 工作原理
本项目采用的 SEO 优化方案是通过 Github Action进行预渲染,将预渲染后的页面上传到 S3 存储桶,通过 Cloudflare Workers 代理请求,实现 SEO 优化。

预渲染是一个简单的爬虫。从提供的 SEO_BASE_URL 开始,每次请求一个页面,将渲染完成后的 html 内容上传至 S3 存储桶缓存。同时提取出页面中的所有链接,判断是否以 SEO_BASE_URL 开头或包含 SEO_CONTAINS_KEY 关键字,如果是则请求该链接并预渲染,直到没有新的链接为止。

## 配置指南
### 环境变量
在部署后端时,需要在 Github 配置以下环境变量(明文):
```ini
SEO_BASE_URL=<SEO 基础地址,用于 SEO 索引,默认为 FRONTEND_URL>
SEO_CONTAINS_KEY=<SEO 索引时只索引以 SEO_BASE_URL 开头或包含SEO_CONTAINS_KEY 关键字的链接,默认为空>
S3_FOLDER=<S3 图片资源存储的文件夹,默认为 'images/'>
S3_CACHE_FOLDER=<S3 缓存文件夹(用于 SEO、高频请求缓存),默认为 'cache/'>
S3_BUCKET=<S3 存储桶名称>
S3_REGION=<S3 存储桶所在区域,如使用 Cloudflare R2 填写 auto 即可>
S3_ENDPOINT=<S3 存储桶接入点地址>
S3_ACCESS_HOST=<S3 存储桶访问地址>
```

以及以下环境变量(加密):
```ini
S3_ACCESS_KEY_ID=<你的S3AccessKeyID>
S3_SECRET_ACCESS_KEY=<你的S3SecretAccessKey>
```

由于这些环境变量数量庞大且覆盖了相当一部分环境变量全列表,因此在 `v0.2.0` 及之后都建议在部署时直接在 Github 中添加这些环境变量,而不是通过 Cloudflare 面板添加。这样能够一定程度上减少配置的时间成本。

### 部署
在配置好环境变量后,即可在 Github Action 中手动触发一次 Workflow,一切正常的话很快就能部署完成。

### 配置 Workers 路由
在 Cloudflare Workers 面板中打开自己的域名详情页,点击 `Workers 路由`,添加一个新路由,路由填写为:
```
<前端域名>/seo/*
```
如:
```
xeu.life/seo/*
```
Worker 选择为部署的 Worker,点击保存。

随后点击侧边栏菜单 > `规则` > `转换规则` > `重写 URL` > `创建规则`,规则名称随意,自定义筛选表达式为:
```
(http.host eq "<前端域名,如xeu.life>" and http.user_agent contains "Googlebot")
```
重写路径设置为 `Dynamic`,值为:
```
concat("/seo",http.request.uri.path)
```
`保留查询`

点击部署,即可完成 SEO 配置。
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"devDependencies": {
"autoprefixer": "^10.4.19",
"postcss": "^8.4.38",
"puppeteer": "^22.10.0",
"tailwindcss": "^3.4.3",
"turbo": "^1.13.3",
"vitest": "1.3.0"
Expand Down
62 changes: 54 additions & 8 deletions scripts/migrator.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,35 @@
import { $ } from "bun";
import stripIndent from 'strip-indent';
import { readdir } from "node:fs/promises";
import { $ } from "bun"
import { readdir } from "node:fs/promises"
import stripIndent from 'strip-indent'

const DB_NAME = process.env.DB_NAME || 'rin'
const WORKER_NAME = process.env.WORKER_NAME || 'rin-server'
const FRONTEND_URL = process.env.FRONTEND_URL || ""
const S3_FOLDER = process.env.S3_FOLDER || 'images/'

function env(name: string, defaultValue?: string, required = false) {
const env = process.env
const value = env[name] || defaultValue
if (required && !value) {
throw new Error(`${name} is not defined`)
}
return value
}
const renv = (name: string, defaultValue?: string) => env(name, defaultValue, true)

const DB_NAME = renv("DB_NAME", 'rin')
const WORKER_NAME = renv("WORKER_NAME", 'rin-server')
const FRONTEND_URL = env("FRONTEND_URL", "")

const S3_ENDPOINT = renv("S3_ENDPOINT")
const S3_ACCESS_HOST = renv("S3_ACCESS_HOST", S3_ENDPOINT)
const S3_BUCKET = renv("S3_BUCKET")
const S3_CACHE_FOLDER = renv("S3_CACHE_FOLDER", 'cache/')
const S3_FOLDER = renv("S3_FOLDER", 'images/')
const S3_REGION = renv("S3_REGION")

// Secrets
const accessKeyId = env("S3_ACCESS_KEY_ID")
const secretAccessKey = env("S3_SECRET_ACCESS_KEY")
const jwtSecret = env("JWT_SECRET")
const githubClientId = env("GITHUB_CLIENT_ID")
const githubClientSecret = env("GITHUB_CLIENT_SECRET")

Bun.write('wrangler.toml', stripIndent(`
#:schema node_modules/wrangler/config-schema.json
Expand All @@ -21,6 +45,11 @@ crons = ["*/20 * * * *"]
[vars]
FRONTEND_URL = "${FRONTEND_URL}"
S3_FOLDER = "${S3_FOLDER}"
S3_CACHE_FOLDER="${S3_CACHE_FOLDER}"
S3_REGION = "${S3_REGION}"
S3_ENDPOINT = "${S3_ENDPOINT}"
S3_ACCESS_HOST = "${S3_ACCESS_HOST}"
S3_BUCKET = "${S3_BUCKET}"

[placement]
mode = "smart"
Expand Down Expand Up @@ -65,7 +94,7 @@ console.log(`----------------------------`)

console.log(`Migrating D1 "${DB_NAME}"`)
try {
const files = await readdir("./server/sql", { recursive: false });
const files = await readdir("./server/sql", { recursive: false })
for (const file of files) {
await $`bunx wrangler d1 execute ${DB_NAME} --remote --file ./server/sql/${file} -y`
console.log(`Migrated ${file}`)
Expand All @@ -77,6 +106,23 @@ try {

console.log(`Migrated D1 "${DB_NAME}"`)
console.log(`----------------------------`)
console.log(`Put secrets`)

async function putSecret(name: string, value?: string) {
if (value) {
console.log(`Put ${name}`)
await $`echo "${value}" | bun wrangler secret put ${name}`
}
}

await putSecret('S3_ACCESS_KEY_ID', accessKeyId)
await putSecret('S3_SECRET_ACCESS_KEY', secretAccessKey)
await putSecret('GITHUB_CLIENT_ID', githubClientId)
await putSecret('GITHUB_CLIENT_SECRET', githubClientSecret)
await putSecret('JWT_SECRET', jwtSecret)

console.log(`Put Done.`)
console.log(`----------------------------`)
console.log(`Deploying`)
await $`echo -e "n\ny\n" | bunx wrangler deploy`
console.log(`Deployed`)
Expand Down
Loading