283 lines
8.3 KiB
Markdown
283 lines
8.3 KiB
Markdown
# Nuxt全栈博客站
|
||
|
||
`Nuxt` `博客` `全栈` `Vue` `MySQL` `Redis`
|
||
|
||
# 博客站
|
||
|
||
一个基于 [Nuxt@4.0.3](https://nuxt.com/)、[NuxtContent@3.4.0](https://content.nuxt.com/)、[shadcn/vue](https://www.shadcn-vue.com/)、[inspira-ui](https://inspira-ui.com/components)、Drizzle、MySQL、Redis 的全栈站点
|
||
|
||
**娱乐性质的个人空间!**
|
||
|
||

|
||

|
||

|
||
|
||
## 功能
|
||
|
||
- 登录注册(用户名+密码)
|
||
- GitHub 登录
|
||
- 点赞
|
||
- 评论
|
||
- MySQL
|
||
- Redis
|
||
- 一键复制 HTML 到公众号(保留所有样式)
|
||
- `$fetch/useFetch` 最佳实践
|
||
- Sitemap
|
||
- RSS
|
||
- robots.txt
|
||
- PAT 认证
|
||
|
||
## 安装依赖
|
||
|
||
`git clone` 克隆或下载本仓库到本地
|
||
|
||
`.nvmrc` 和 `.node-version` 中标识了 Node 版本
|
||
|
||
安装:
|
||
|
||
```bash
|
||
npm i
|
||
|
||
pnpm i
|
||
```
|
||
|
||
视报错信息而定:
|
||
|
||
涉及从 npm 切换到 pnpm 时,可能需要重新 `pnpm add better-sqlite3` 和 `pnpm rebuild better-sqlite3`
|
||
|
||
## 零环境快速开始(本地开发)
|
||
|
||
先决条件:
|
||
- Node.js 20+(建议用 nvm 管理),PNPM 10+
|
||
- Docker 与 Docker Compose(用于本地 MySQL/Redis)
|
||
- macOS/Linux 或 Windows WSL2 环境
|
||
|
||
步骤:
|
||
|
||
1) 启动数据库与缓存(使用内置 compose)
|
||
|
||
```bash
|
||
docker compose -f docker-compose.local.yml up -d
|
||
```
|
||
|
||
默认账号与端口:
|
||
- MySQL: root / 123456,数据库 blog,端口 3306
|
||
- Redis: 无密码,端口 6379
|
||
|
||
2) 创建 .env(项目根目录)
|
||
|
||
```bash
|
||
# 数据库连接(与 compose 保持一致)
|
||
DATABASE_URL=mysql://root:123456@127.0.0.1:3306/blog
|
||
|
||
# Redis(不配置则默认 127.0.0.1:6379)
|
||
REDIS_HOST=127.0.0.1
|
||
REDIS_PORT=6379
|
||
|
||
# 从 GitHub 仓库拉取文章内容所需(如不使用,可留空或注释 content.config.ts 的 source)
|
||
CONTENT_REPO_TOKEN=
|
||
|
||
# 应用密钥(本地可任意随机值,生产需妥善保管)
|
||
NUXT_SESSION_PASSWORD=please_set_a_32_chars_random_string
|
||
NUXT_JWT_SECRET=please_set_a_random_string
|
||
|
||
# 可选:邮件/COS 等第三方配置(本地可留空)
|
||
NUXT_NODEMAILER_HOST=
|
||
NUXT_NODEMAILER_PORT=
|
||
NUXT_NODEMAILER_AUTH_USER=
|
||
NUXT_NODEMAILER_AUTH_PASS=
|
||
NUXT_COS_SECRET_ID=
|
||
NUXT_COS_SECRET_KEY=
|
||
NUXT_COS_BUCKET=
|
||
NUXT_COS_REGION=
|
||
```
|
||
|
||
3) 初始化数据库(二选一)
|
||
|
||
优先使用迁移(如果仓库已包含 `lib/drizzle/migrations`):
|
||
|
||
```bash
|
||
pnpm db:migrate
|
||
```
|
||
|
||
如果没有历史迁移或希望按 `schema.ts` 直接同步结构:
|
||
|
||
```bash
|
||
pnpm db:push
|
||
```
|
||
|
||
4) 启动开发服务器
|
||
|
||
```bash
|
||
pnpm dev
|
||
```
|
||
|
||
访问:http://localhost:4775
|
||
|
||
排错小贴士:
|
||
- 3306/6379 端口被占用:修改 `docker-compose.local.yml` 的端口映射,或停止占用进程。
|
||
- better-sqlite3 报错:执行 `pnpm rebuild better-sqlite3`。
|
||
- 无法拉取 GitHub 文章:确认 `CONTENT_REPO_TOKEN` 有权限,或临时注释 `content.config.ts` 的 `source` 配置。
|
||
|
||
## 运行前配置
|
||
|
||
`content.config.ts`
|
||
|
||
```ts
|
||
// content 可以随意更改,对应的是 queryCollection 的第一个参数
|
||
content: defineCollection({
|
||
// page 表示会一对一生成路由
|
||
type: 'page',
|
||
source: {
|
||
// 过滤 md 文件,因为一个 ob 库里可能什么文件都有
|
||
include: '**/*.md',
|
||
// 过滤掉某些文件,如没写完的或者不想给人看的
|
||
// book 是我的 ob 根目录下的一个文件夹名称,视情况修改
|
||
exclude: ['**/-*.md', 'book/**/*.md'],
|
||
// 路由前缀 /post,因为渲染文章的 vue 文件位于 app/pages/post/[...slug].vue
|
||
// 所以为了匹配这个路径要加这个路由前缀
|
||
prefix: '/post',
|
||
// cwd: process.env.CONTENT_FS_PATH,
|
||
// TODO 替换为你的仓库地址,不能使用组织仓库
|
||
repository: 'https://github.com/aatrooox/Blog',
|
||
// TODO 替换为你的 token,在 github > settings > developers settings > personal access tokens
|
||
authToken: process.env.CONTENT_REPO_TOKEN
|
||
},
|
||
// md 文件的元信息,根据自己的实际情况来,这些字段会用作数据库中的表头
|
||
schema: z.object({
|
||
date: z.date(),
|
||
lastmod: z.date(),
|
||
tags: z.array(z.string()),
|
||
versions: z.array(z.string()),
|
||
})
|
||
}),
|
||
```
|
||
|
||
`env`
|
||
|
||
MySQL、Redis 环境,具体操作可以参考[这篇文章](https://zzao.club/post/nuxt/local-init-mysql-by-docker)
|
||
|
||
Redis 没有配置时,默认连接 localhost 6379 端口,本地同生产(单体数据库服务,不对外开放端口)
|
||
|
||
```
|
||
DATABASE_URL=mysql://root:root@127.0.0.1:3306/blog
|
||
NUXT_FEISHU_WEBHOOK=
|
||
NUXT_FEISHU_USER_ID=
|
||
```
|
||
|
||
先启动 MySQL、Redis
|
||
|
||
## 使用 Drizzle 初始化数据库和表结构
|
||
|
||
```bash
|
||
# 方式一:使用迁移(推荐)
|
||
pnpm db:migrate
|
||
|
||
# 方式二:按 schema 直接推送
|
||
pnpm db:push
|
||
```
|
||
|
||
## 启动项目
|
||
|
||
```bash
|
||
pnpm dev
|
||
```
|
||
|
||
## 生产部署
|
||
|
||
部署链路:GitHub Actions 构建 -> 打包 `.output` + `pm2.config.json` + `pm2.preload.cjs` -> SSH 上传 -> 服务器 `/root/web/blog` 原地解压 -> Drizzle 迁移 -> PM2 启动 `Blog`(端口 4571)。
|
||
|
||
1) 服务器准备
|
||
- 安装 Node.js 20+ 与 PM2(全局)
|
||
- `npm i -g pm2`
|
||
- 创建目录与生产环境变量文件:
|
||
- 应用目录:`/root/web/blog`
|
||
- 环境文件:`/root/envs/blog/.env`
|
||
- 必需变量(示例):
|
||
- `DATABASE_URL=mysql://user:pass@host:3306/blog`
|
||
- `NUXT_SESSION_PASSWORD=<32位随机字符串>`
|
||
- `NUXT_JWT_SECRET=<随机字符串>`
|
||
- 可选:`REDIS_HOST`、`REDIS_PORT`、`CONTENT_REPO_TOKEN`、`NUXT_*`(Nodemailer、OAuth GitHub、COS、飞书等)
|
||
|
||
2) 配置 GitHub Secrets(仓库 Settings -> Secrets and variables -> Actions)
|
||
- SSH 访问:`SSH_PRIVATE_KEY`、`SSH_HOST`、`SSH_USER`、`SSH_PORT`
|
||
- 应用密钥与依赖:`DATABASE_URL`、`NUXT_SESSION_PASSWORD`、`NUXT_JWT_SECRET`
|
||
- 内容拉取:`CONTENT_REPO_TOKEN`(若启用 content 仓库)
|
||
- 可选通知与第三方:`NUXT_FEISHU_WEBHOOK`、`NUXT_FEISHU_USER_ID`、`NUXT_NODEMAILER_*`、`NUXT_OAUTH_GITHUB_CLIENT_ID/SECRET`、`NUXT_COS_*`
|
||
|
||
3) 触发部署
|
||
- Push 到 `main` 且提交信息包含 `chore(release)` 会自动触发(可使用 `pnpm release:patch|minor|major` 脚本生成)
|
||
|
||
4) 运行方式与端口
|
||
- PM2 使用 `pm2.config.json` 启动进程名 `Blog`,监听 `4571`
|
||
- 运行时通过 `node_args: -r ./pm2.preload.cjs` 读取 `/root/envs/blog/.env`,无需安装 `dotenv`
|
||
- 反向代理(示例 Nginx):
|
||
```nginx
|
||
server {
|
||
listen 80;
|
||
server_name your.domain.com;
|
||
location / {
|
||
proxy_pass http://127.0.0.1:4571;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
}
|
||
}
|
||
```
|
||
|
||
5) 数据库迁移
|
||
- 工作流会在服务器上自动执行 Drizzle 迁移(优先 `dotenv-cli`,无则回退为 `source ENVFILE` + `npx drizzle-kit migrate`)
|
||
- 如服务器无公网 npm,可自行预先装好 `dotenv-cli`,或手动执行迁移
|
||
|
||
6) 回滚与灰度(可选)
|
||
- 回滚:直接 `git revert` 上次 release 提交并 push(同样会触发部署)
|
||
- 灰度:仓库包含 `pm2.canary.json`(4572 端口),如需金丝雀发布可另行添加对应 Workflows 与目录(例如 `/root/web/blog-canary`)
|
||
|
||
7) 常见排查
|
||
- 启动异常:`pm2 logs Blog` 查看日志
|
||
- 环境变量未生效:确认 `/root/envs/blog/.env` 存在且键名正确;`pm2 env 0`/`pm2 describe Blog` 检查进程环境
|
||
- 无法连接数据库/Redis:验证 `DATABASE_URL`、`REDIS_HOST/PORT` 与网络策略
|
||
|
||
## 发布
|
||
|
||
> 注意 0.x 版本会有不同
|
||
|
||
```bash
|
||
# 1.0.0 => 1.0.1 0.1.0 => 0.1.1
|
||
pnpm release:patch
|
||
# 1.0.0 => 1.1.0 0.1.0 => 0.1.1
|
||
pnpm release:minor
|
||
# 1.0.0 => 2.0.0 0.1.0 => 0.2.0
|
||
pnpm release:major
|
||
```
|
||
|
||
## 自定义字体
|
||
|
||
使用自定义字体,要注意 format 的值:
|
||
|
||
```
|
||
.woff 文件:使用 format('woff')
|
||
.woff2 文件:使用 format('woff2')
|
||
.ttf 文件:使用 format('truetype')
|
||
.otf 文件:使用 format('opentype')
|
||
.eot 文件:使用 format('embedded-opentype')
|
||
.svg 文件:使用 format('svg')
|
||
```
|
||
|
||
## 关于环境变量
|
||
|
||
`.env` 文件用于开发、构建、生成期间。
|
||
|
||
生产环境如何设置环境变量,取决于你的生产环境是什么。
|
||
|
||
因为有些 `serverless` / `cf worker` 环境下没有传统的文件系统,所以 `nuxt` 选择不维护环境变量的生产环境配置。
|
||
|
||
### Nuxt 交流群
|
||
|
||
- 小群(少于 50 人)
|
||
- 分享 Nuxt 相关高质量资料
|
||
- 探讨 Nuxt 的各种问题
|
||
- 分享自己在做的项目
|
||
|
||
 |