个人博客搭建全记录03-更优雅的更新和部署

个人博客搭建全记录03-更优雅的更新和部署
SEAlencehe写在前面
前文已经介绍过,笔者使用 webhooks 的方式更新博客,本地 push 仓库,Gitee 会通知云服务器,云服务器运行脚本自动 pull 仓库打包,但是这个方法遇到各种各样的问题需要手动解决,实在不够优雅,为了能一键更(tou)新(lan),找到了一个目前为止还算简洁的方法。
懒惰是人类第一生产力
一、遇到的问题
1.git 部署
在云服务器需要安装 git,手动配置密钥,不够轻量化,
2.新项目部署
要部署新项目,在服务器还得手动 clone 一遍,仓库和服务器也要再手动设置设置一遍 webhooks
3.错误不可见
笔者遇到过本地 push 新代码,项目站始终没有更新,手动查看日志和 build 产物,发现 pull 的仓库不完整,检查发现主题是项目的子仓库,需要合并到主仓库。
如果遇到其他错误依然要手动到服务器检查,项目构建带来诸多不便
二、新的方案
因为目前 webhooks 方案出现的问题都是在云端构建的过程中,所以直接在本地构建,再上传构建产物到服务器,这里就要使用 ssh2-sftp-client 这个包了,可以直接使用 js 脚本 ssh 链接服务器
1.安装包
pnpm i ssh2-sftp-client
2.ssh 配置
在根目录新建deploy.config.jsssh 配置文件
export default {
host: 'xxx.xxx.xxx.xxx', // 服务器公网地址
port: 22, // 默认SSH端口
username: 'root', // 服务器用户名
// password: 'xxx', // 服务器密码
privateKey: fs.readFileSync('/path/to/key'), // 使用密钥
// 本地打包后的目录
localPath: './dist',
// 服务器上的部署目录 (请确保该目录已存在)
remotePath: '/www/wwwroot/my_blog',
}
为了安全起见,我的服务器关闭了密码登录,所以使用密钥登录
3.编写脚本
在项目根目录新建scripts文件夹,该文件夹下新建deploy.js
引入配置文件和包
const path = require('path')
const Client = require('ssh2-sftp-client')
const config = require('../deploy.config.js')
实例化
const sftp = new Client()
// 获取 Hexo 生成的静态文件目录(默认 public)
const localBuildDir = path.resolve(__dirname, '../public')
上传的函数
async function main() {
try {
console.log('⏳ 正在连接服务器...')
await sftp.connect(config)
console.log('✅ 服务器连接成功')
console.log(`🧹 清理远程目录: ${config.remotePath}`)
const remoteExists = await sftp.exists(config.remotePath)
if (remoteExists) {
// 递归删除远程目录后再重新创建,确保干净上传
await sftp.rmdir(config.remotePath, true)
}
await sftp.mkdir(config.remotePath, true)
console.log(`📂 准备上传: ${localBuildDir} -> ${config.remotePath}`)
// 开始上传目录
sftp.on('upload', (info) => {
console.log(`Uploading: ${info.source}`)
})
// uploadDir 会自动覆盖同名文件
await sftp.uploadDir(localBuildDir, config.remotePath)
console.log('🎉 发布成功!')
} catch (err) {
console.error('❌ 发布失败:', err.message)
} finally {
sftp.end()
}
}
⚠⚠⚠注意:
在链接服务器之后先清除原有的目录,避免奇奇怪怪的问题
完整代码
const path = require('path')
const Client = require('ssh2-sftp-client')
const config = require('../deploy.config.js')
const sftp = new Client()
// 获取 Hexo 生成的静态文件目录(默认 public)
const localBuildDir = path.resolve(__dirname, '../public')
async function main() {
try {
console.log('⏳ 正在连接服务器...')
await sftp.connect(config)
console.log('✅ 服务器连接成功')
console.log(`🧹 清理远程目录: ${config.remotePath}`)
const remoteExists = await sftp.exists(config.remotePath)
if (remoteExists) {
// 递归删除远程目录后再重新创建,确保干净上传
await sftp.rmdir(config.remotePath, true)
}
await sftp.mkdir(config.remotePath, true)
console.log(`📂 准备上传: ${localBuildDir} -> ${config.remotePath}`)
// 开始上传目录
sftp.on('upload', (info) => {
console.log(`Uploading: ${info.source}`)
})
// uploadDir 会自动覆盖同名文件
await sftp.uploadDir(localBuildDir, config.remotePath)
console.log('🎉 发布成功!')
} catch (err) {
console.error('❌ 发布失败:', err.message)
} finally {
sftp.end()
}
}
main()
package.json 下新增
"deploy:sftp": "hexo clean && hexo generate && node scripts/deploy.js",
"fastcommit": "git add . && git commit -m 'update' && git pull --rebase && git push origin master",
"fastdeploy": "pnpm fastcommit && pnpm deploy:sftp"
这样直接运行就可以实现一键上传 build 产物和提交远程仓库了
⚠⚠⚠注意:
如果项目要提交远程仓库,记得把 deploy.config.js 忽略
4.测试
pnpm fastdeploy
三、其他杂项
文章模板修改
为了新建文章方便,可以修改 Hexo 文章模板,位置在/scaffolds/post.md
Front-matter 选项中的所有内容均为非必填的。至少填写 title 和 date 的值。
| 配置选项 | 默认值 | 描述 |
|---|---|---|
| title | Markdown 的文件标题 |
文章标题,强烈建议填写此选项 |
| date | 文件创建时的日期时间 | 发布时间,强烈建议填写此选项,且最好保证全局唯一 |
| author | 根 _config.yml 中的 author |
文章作者 |
| img | featureImages 中的某个值 |
文章特征图,推荐使用图床(腾讯云、七牛云、又拍云等)来做图片的路径.如: http://xxx.com/xxx.jpg |
| top | true |
推荐文章(文章是否置顶),如果 top 值为 true,则会作为首页推荐文章 |
| hide | false |
隐藏文章,如果hide值为true,则文章不会在首页显示 |
| cover | false |
v1.0.2版本新增,表示该文章是否需要加入到首页轮播封面中 |
| coverImg | 无 | v1.0.2版本新增,表示该文章在首页轮播封面需要显示的图片路径,如果没有,则默认使用文章的特色图片 |
| password | 无 | 文章阅读密码,如果要对文章设置阅读验证密码的话,就可以设置 password 的值,该值必须是用 SHA256 加密后的密码,防止被他人识破。前提是在主题的 config.yml 中激活了 verifyPassword 选项 |
| toc | true |
是否开启 TOC,可以针对某篇文章单独关闭 TOC 的功能。前提是在主题的 config.yml 中激活了 toc 选项 |
| mathjax | false |
是否开启数学公式支持 ,本文章是否开启 mathjax,且需要在主题的 _config.yml 文件中也需要开启才行 |
| summary | 无 | 文章摘要,自定义的文章摘要内容,如果这个属性有值,文章卡片摘要就显示这段文字,否则程序会自动截取文章的部分内容作为摘要 |
| categories | 无 | 文章分类,本主题的分类表示宏观上大的分类,只建议一篇文章一个分类 |
| tags | 无 | 文章标签,一篇文章可以多个标签 |
| keywords | 文章标题 | 文章关键字,SEO 时需要 |
| reprintPolicy | cc_by | 文章转载规则, 可以是 cc_by, cc_by_nd, cc_by_sa, cc_by_nc, cc_by_nc_nd, cc_by_nc_sa, cc0, noreprint 或 pay 中的一个 |
注意:
- 如果
img属性不填写的话,文章特色图会根据文章标题的hashcode的值取余,然后选取主题中对应的特色图片,从而达到让所有文章的特色图各有特色。date的值尽量保证每篇文章是唯一的,因为本主题中Gitalk和Gitment识别id是通过date的值来作为唯一标识的。- 如果要对文章设置阅读验证密码的功能,不仅要在 Front-matter 中设置采用了 SHA256 加密的 password 的值,还需要在主题的
_config.yml中激活了配置。有些在线的 SHA256 加密的地址,可供你使用:开源中国在线工具、chahuo、站长工具。- 您可以在文章 md 文件的 front-matter 中指定 reprintPolicy 来给单个文章配置转载规则
最全示例
---
title: typora-vue-theme主题介绍
date: 2018-09-07 09:25:00
author: 赵奇
img: /source/images/xxx.jpg
top: true
hide: false
cover: true
coverImg: /images/1.jpg
password: 8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92
toc: false
mathjax: false
summary: 这是你自定义的文章摘要内容,如果这个属性有值,文章卡片摘要就显示这段文字,否则程序会自动截取文章的部分内容作为摘要
categories: Markdown
tags:
- Typora
- Markdown
---

.png)




