辅助开发
使用项目脚本、Prisma Studio、Claude Code 和 CI/CD 工具
项目除了 dev、build 和 preview 等基础命令,还提供了用于媒体处理、部署配置、数据库查看、AI 辅助开发、持续集成和 IP 查询的脚本,本节介绍这些开发过程中经常使用的工具。
安装脚本依赖
运维脚本的依赖单独放在 scripts/package.json 中,不会随根目录的 bun install 一起安装。第一次使用实况照片压缩、COS 上传、服务器上传或密码重置等脚本前,先在项目根目录执行:
Bashbun run scripts:install执行后会在 scripts/ 下安装 sharp、ffmpeg-static、ssh2-sftp-client、cos-nodejs-sdk-v5 和 tsx 等脚本依赖;根目录的 ipdb、pg 等共享依赖仍由主项目安装。
压缩实况照片
安卓手机中的“实况照片”通常叫“动态照片”“动态影像”或 Motion Photo。按照 Android Motion Photo 格式说明,它本质上是在一张静态照片后面追加一段短视频,不是一个单独的 .live 文件,也不是一张 JPG 和一个 MP4 分开保存的文件;静态部分可以是 JPEG、HEIC 或 AVIF,视频部分通常是 MP4;文件还可以通过 XMP 或容器元数据记录视频的位置。不同厂商和相册应用的导出方式并不完全一致,因此安卓手机拍出的“动态照片”不一定都能直接用于本项目。
本项目当前处理的是安卓手机最常见的 JPEG Motion Photo 文件:文件扩展名为 .jpg 或 .jpeg,文件开头是一份完整的 JPEG 图片,JPEG 数据后面紧接着追加完整的 MP4 视频数据,整体结构可以理解为 JPEG 数据 + MP4 数据。它仍然使用 .jpg 文件名,所以普通图片查看器会显示前面的静态照片;支持动态照片的程序则会继续查找后面的 MP4 并播放动态内容。这里的 MP4 直接追加在同一个文件中,不是通过文件名关联的外部视频,也不是写入 JPEG EXIF 缩略图中的一小段预览。
在安卓手机上使用时,应先在相机中开启“动态照片”功能,再导出原始 .jpg 或 .jpeg 文件。不要选择“仅保存照片”“导出静态图片”或“导出视频”,也不要经过会重新编码图片的应用后再使用;这些操作可能会丢掉追加在 JPEG 后面的 MP4。将文件复制到项目根目录的 .live-photos 后运行压缩命令即可。压缩脚本的处理结果会分别显示 JPEG 和 MP4 的大小;如果日志只显示一个总大小,通常说明输入文件中没有找到可识别的内嵌视频。
压缩时,脚本会先把 JPEG 部分交给 sharp 和 mozjpeg,再把 MP4 部分交给 ffmpeg 使用 H.264 重新编码,最后按照 JPEG 数据 + MP4 数据 的顺序重新拼接。默认将视频高度缩小到 1080 像素以内、使用 CRF 28 和 veryfast 预设,并移除音轨;压缩后的视频会启用 faststart,便于网页边下载边播放。如果动态照片中的声音也需要保留,添加 --keep-audio;如果只需要压缩画面而不希望放大原视频,脚本会自动取目标高度和原始高度中的较小值。
将待处理的 .jpg 或 .jpeg 文件放入项目根目录的 .live-photos,执行:
Bashbun run compress:livephoto默认模式会扫描 .live-photos 目录,并把结果写入 .compressed-live-photos,不会覆盖原文件。也可以直接指定单个文件或目录,例如:
Bashbun scripts/compress-livephoto.ts photo.jpg --overwrite --video-height 720脚本支持使用 -o 或 --output 指定输出目录,使用 -j 或 --jpeg-quality 调整图片质量,使用 --video-height 调整视频高度,使用 -c 或 --crf 调整视频压缩率,使用 --preset 调整编码预设;添加 --keep-audio 保留音轨,添加 --dry-run 只打印处理结果而不写入文件。脚本会按 FFMPEG_PATH、ffmpeg-static、系统 PATH 的顺序寻找可用的 ffmpeg,三者都不可用时会停止并提示安装。
生成 Nginx 配置
nginx:generate 会读取 .env 中的部署变量,在项目根目录生成 nginx.conf。运行前需要填写 DEPLOY_PORT、DEPLOY_PROJECT_ROOT_DIR、DEPLOY_SITE_DOMAIN、DEPLOY_CDN_DOMAIN 和 DEPLOY_ENABLE_CDN_REDIRECT;如果站点前面还有 CDN 或负载均衡,可以再配置 DEPLOY_TRUSTED_PROXY 和 DEPLOY_REAL_IP_HEADER,用于恢复访客真实 IP。
生成配置:
Bashbun run nginx:generate生成的片段包含 /sw.js、robots.txt、RSS、API、静态资源和 Nuxt 页面反向代理规则,也会根据 DEPLOY_ENABLE_CDN_REDIRECT 输出 CDN 重定向规则。裸机部署时,DEPLOY_PORT 要与运行环境的 PORT 保持一致;Docker 部署时,DEPLOY_PORT 是宿主机端口。生成完成后,将 nginx.conf 中的内容填入 Nginx 或宝塔 Node 项目的伪静态配置,并根据实际域名和目录检查一次。
示例:
Nginxset_real_ip_from 0.0.0.0/0;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
# Service Worker(必须从主域名提供,不允许 CDN 跨域)
location = /sw.js {
root /path/to/imqi1-cms;
add_header Cache-Control "no-cache" always;
add_header Service-Worker-Allowed "/" always;
}
# robots.txt(必须从主域名提供,不允许 CDN)
location = /robots.txt {
root /path/to/imqi1-cms;
add_header Cache-Control "public, max-age=3600" always;
}
# RSS 订阅
location = /feed {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Host $host:$server_port;
proxy_set_header X-Scheme $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 关键:绕过缓存
proxy_no_cache 1;
proxy_cache_bypass 1;
add_header Cache-Control "no-cache, no-store, must-revalidate" always;
}
# CDN 重定向规则(静态资源)
# 单文件重定向
location = /favicon.ico {
return 301 https://cdn.imqi1.com/favicon.ico;
}
location = /manifest.webmanifest {
return 301 https://cdn.imqi1.com/manifest.webmanifest;
}
# 静态资源目录重定向
location ~ ^/(emojis|fonts|icons|imgs|skills)/ {
return 301 https://cdn.imqi1.com$request_uri;
}
# 上传文件重定向
location ~ ^/(uploads)/ {
return 301 https://cdn.imqi1.com$request_uri;
}
# ============================================
# 反向代理到 Node.js 服务
# ============================================
location / {
proxy_pass http://127.0.0.1:3000;
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;
proxy_http_version 1.1;
proxy_set_header Connection '';
proxy_buffering on;
proxy_buffer_size 64k;
proxy_buffers 32 64k;
proxy_busy_buffers_size 128k;
proxy_no_cache 1;
proxy_cache_bypass 1;
proxy_cache off;
proxy_hide_header Cache-Control;
add_header Cache-Control "no-cache, no-store, must-revalidate" always;
}使用 Prisma Studio
Prisma Studio 用于在浏览器中查看和编辑 PostgreSQL 数据库中的表数据。确认 .env 中的数据库连接信息可用后,在项目根目录执行:
Bashbun run prisma:studio命令会启动 Prisma Studio,默认在 http://localhost:5555 打开数据库管理界面,可以查看文章、评论、分类、标签、友链、订阅和站点设置等数据。修改 prisma/schema.prisma 后,先执行 bun run prisma:generate 重新生成 Prisma Client,再启动 Studio 查看新的模型。

项目校验
代码改动完成后,在项目根目录执行三项校验,全部通过后再进行提交或构建;三项也可以用一条 bun run check 依次执行。
检查 JavaScript、TypeScript 和 Vue 文件,警告同样视为失败,拥有独立 ESLint 配置的 mini/ 子项目不在检查范围内,需要自动修复时改用 bun run lint:fix:
Bashbun run lint检查 Nuxt 项目的端到端类型:
Bashbun run typecheck检查 Tailwind 类名、@apply 指令和类名写法:
Bashbun run tailwindcss:lint单元、集成与端到端测试的运行方式见 测试。
使用 Claude Code 开发
在项目根目录启动 Claude Code 后,它会读取根目录的 CLAUDE.md,了解项目目录、Bun 版本、数据库约定、接口安全规则和常用校验命令。仓库中的 .claude/memory/ 保存跨会话共享的项目记忆,.claude/skills/ 保存项目专用的操作流程,修改代码前可以让 Claude Code 先读取对应的记忆条目。
Bashclaude开发完成后可以使用 /check 执行 ESLint、Nuxt 类型检查和 Tailwind 校验,使用 /review 检查改动风险,使用 /audit 按项目约定审计代码,使用 /compress-livephoto 处理实况照片,使用 /deploy 执行构建和上传流程。提交完成后使用 /changelog 根据实际 diff 生成 changelogs.json,再到后台导入更新日志;涉及线上上传的技能会在执行前要求确认,不要把本地开发和正式部署混在一次操作中。
构建 CI/CD
仓库的 GitHub Actions 配置位于 .github/workflows/ci.yml。向 master 推送代码或创建目标为 master 的 Pull Request 时会触发 CI,任务在 ubuntu-latest 上使用 Bun 1.4.0,执行 bun install --frozen-lockfile、bunx eslint . 和 bun run tailwindcss:lint;同一分支的新任务会取消仍在运行的旧任务。
当前 CI 只负责代码规范和 Tailwind 类名检查,bunx nuxi typecheck 没有放进 GitHub Actions,而是按照项目约定在本地执行,避免 Nuxt InternalApi 在干净 CI 环境中产生误报。提交前可以直接运行:
Bashbunx nuxi typecheck生产发布由构建和上传脚本组成。先执行 bun run build 生成 .output/,再用 bun run upload:server 上传 Nitro 服务端,用 bun run upload:cos 上传带构建哈希的静态资源;上传完成后可以使用 bun run restart:server 通过宝塔面板 API 重启线上 Node 项目。upload:cos 会询问是否清空本次远程目录,upload:server 默认跳过 node_modules 和 runtime-assets,首次部署或运行时资源有更新时使用 bun run upload:server -- --node-modules。仓库当前没有把这组上传命令直接放进 GitHub Actions,CD 仍由部署者在确认环境变量和上传范围后执行。
查询 IP 地址库
项目使用 server/runtime-assets/qqwry.ipdb 查询 IP 的国家、地区、城市和运营商信息,评论归属地、访客分布、博客网络和后台信任设备都会使用这套本地地址库。服务端会把查询结果在内存中缓存一天,最多保留 20000 个地址,无法识别的地址不会返回虚假的位置。
需要核对某个地址时,可以执行:
Bashbun run get:ip 1.2.3.4 1.2.3.5命令会同时查询本地 qqwry.ipdb 和 ip.zxinc.org,并把两个结果并排输出,便于检查地址库是否命中以及结果是否一致。未提供 IP 参数时,命令只显示用法;传入无效地址时会标记为无效 IP。
构建时,qqwry.ipdb 会由 copy-data.ts 复制到 .output/server/runtime-assets/。使用 bun run upload:server 部署时,默认会跳过 runtime-assets,首次部署新服务器或更新地址库后需要追加 --node-modules,否则线上服务找不到地址库,评论和访客位置查询会失效。