御坂网站建设历史(ing)

本文分两部分:上半是站点架构——博客由哪些版块组成,哪些来自插件、哪些是主题自带的、哪些是自己写的,哪些目录哪些代码控制哪些地方,以及换主题时要搬走什么;下半是改动年表,倒序排列(新的在上,旧的在下)。

博客架构

整体链路

1
2
3
4
5
6
7
8
9
10
11
12
13
本地写作(Markdown)
│ git push
▼
CNB 云原生构建(.cnb.yml · node:22 容器)
│ hexo clean → hexo generate
▼
静态产物 public/
│ hexo deploy
▼
阿里云 OSS(oss-cn-hongkong 香港节点)
│
▼
阿里云 CDN ──► 访客 https://misaka10013.cn

例外:/media/ 与 /map/ 的数据不走这条链路。 这两个页面的 HTML/CSS/JS 仍由上面这条链路发布,但数据的主副本在腾讯云开发:

1
2
3
4
5
6
7
8
9
浏览器(/media/ 或 /map/)
│ auth.js:带 Authorization 头请求
▼
云函数 api(Event 类型 + HTTP 访问服务,挂 /api)
│ 一个集合装所有数据集
▼
云开发数据库 media 集合
├─ _id='anime' / 'movie' / 'book' / 'game' ← 媒体库,一个类型一个文档
└─ _id='map' ← 足迹地图(多带 categories / config)

这么改的直接收益:加一条记录不用重新部署整站。仓库里的 data.json / data/*.json 退化为「云端不可达时的降级快照」,只在云挂掉时才会被读到 —— 所以只改这些文件访客是看不到的,必须走页面上的「☁️ 提交」。

项 值
生成器 Hexo 8.1.2
主题 安知鱼 anzhiyu 1.7.1(themes/anzhiyu/)
构建 CNB 云原生,推送 master 自动触发(.cnb.yml)
托管 阿里云 OSS 香港节点 + CDN(境外节点,无需 ICP 备案)
文章链接 p/:abbrlink.html(crc32 → 十进制,短且稳定)
内容规模 175 篇文章

三层归属

站点由三层叠成。改动之前先判断目标属于哪一层,这决定了「会不会因为换主题而失效」。

层 内容 位置 换主题时
① Hexo 与插件 Hexo 本体 + 二十余个 npm 插件 package.json / node_modules/ 不受影响
② 主题 安知鱼 1.7.1,页面骨架、样式、交互 themes/anzhiyu/ 整层被替换
③ 自定义 页面、脚本、样式、数据 source/、scripts/、两个配置文件 需手动迁移(见下文清单)

主题层在 2026-09-01 被整体替换过一次(NexT → 安知鱼),当时主题内的 4 处改动需要重打补丁。这正是后来把「能拿出来的东西」尽量都挪到第 ③ 层的原因。

功能版块清单

一、主题自带(换主题即失效,需依赖新主题的同类能力)

功能 由谁提供 启用位置
评论区(Twikoo)接入 themes/anzhiyu/layout/includes/third-party/comments/twikoo.pug _config.anzhiyu.yml → comments.use: Twikoo + twikoo.envId
音乐馆 /music/ 主题 type: music 页型 + 黑胶播放器 source/music/index.md
关于页 /about/ 主题页型 + 数据文件 source/about/index.md + source/_data/about.yml
友链 /link/ 主题 type: link source/link/index.md + source/_data/link.yml
归档 / 分类 / 标签页 Hexo 生成器 + 主题模板 hexo-generator-archive/category/tag
本地搜索 主题搜索 UI + hexo-generator-searchdb _config.yml 的 search 段 + 主题 local_search
代码块一键复制 主题自带 highlight_copy: true
暗色模式 / 阅读模式 / 中控台 主题自带 darkmode、readmode、centerConsole
侧栏 / 悬浮按钮 / 头图 / 首页 主题自带 sidebar、rightside_item_order、home_top 等
404 页 主题自带 source/404.html

二、Hexo 插件(package.json,独立于主题,换主题不受影响)

插件 作用
hexo-abbrlink 文章短链(p/:abbrlink.html)
hexo-generator-searchdb 生成 search.xml 供本地搜索
hexo-generator-sitemap / hexo-generator-feed SEO:站点地图、RSS
hexo-generator-archive / category / tag / index 归档、分类、标签、首页列表
hexo-excerpt 首页自动摘要
hexo-deployer-ali-oss 部署到阿里云 OSS(锁定 1.0.0)
hexo-symbols-count-time / hexo-wordcount 字数与阅读时长
hexo-renderer-pug / marked / stylus / ejs 模板与正文渲染
hexo-lazyload-image 已关闭(与主题 lazyload 协议冲突)

三、自定义功能(source/ + scripts/,换主题要迁的就是这些)

功能 类型 代码位置 引用 / 入口 迁移动作
足迹地图 /map/ 独立静态应用 + 云端后端 前端 source/map/:index.html + app.js + style.css + data.json(静态快照)+ vendor/(Leaflet、marked);后端同媒体库共用 cloudfunctions/api/(路由 /map/list、/map/ops) 菜单项;skip_render 直出,不走 Hexo 渲染。数据以云端为准(media 集合 _id='map'),data.json 退化为「云端不可达时的降级快照」,只含公开点位。详见 source/map/README.md 前端整目录复制 + 后端云函数另建。⚠️ 改 app.js / style.css / vendor / js/lib/* 后必须同步 bump index.html 里的 ?v=(当前 20260929a,六处),否则访客吃旧缓存
地图数据工具链 Node 离线脚本 _tools/:poi-build.js、poi-price-fix.js、poi-query-fix.js、interest-build.js、map-migrate.js、verify-map-cloud.js、set-main-pass.js 等 手动运行,不参与构建 整目录复制。migrate-private.js / verify-v19.js 是 v19 整块密文时代的遗留(已退役、留档不删)
媒体档案库 /media/ 独立静态应用 + 云端后端 前端 source/media/:index.html + app.js + style.css + data/(静态快照 JSON)+ vendor/;后端 cloudfunctions/api/(云开发 CloudBase) 菜单项;skip_render。数据以云端为准,静态 data/*.json 退化为「云端不可达时的降级快照」。详见 source/media/README.md 前端整目录复制;后端云函数需在目标环境另建(见下)。⚠️ 与地图同一约定:改 app.js / style.css / vendor / js/lib/* 后必须同步 bump index.html 里五处 ?v=(当前 20260929a),否则访客吃旧缓存
自研后端 api 云函数 云开发函数(Event 类型 + HTTP 访问服务) cloudfunctions/api/:index.js(路由分发)+ package.json;线上函数名 api,服务路径 /api,/health 版本 5 前端经 source/js/lib/auth.js 里的 DEFAULT_API_BASE 调用 代码随仓库走,但函数本身要在目标环境重新部署(tcb CLI 在 Windows 下部署是坏的,走 MCP manageFunctions)。Twikoo 是独立函数、不并入此处
前端公共件 js/lib/ 独立页面共用脚本 source/js/lib/secrets.js(PBKDF2/AES-GCM 加解密)、auth.js(登录换 token、请求封装、密钥缓存) 由需要的独立页面用相对路径 <script> 自行引入(独立页不走主题 inject) 整目录复制。媒体库与地图都已接入,共用同一个主密码与 salt misaka-main-v1、密钥槽位 main(同标签页跨模块免二次解锁)
留言板 /comments/ Hexo 渲染页 source/comments/index.md(页面壳,样式与逻辑全用主题自带) 菜单项(_config.anzhiyu.yml 的 menu) 一个 md + 菜单一行。评论走主题评论位(Twikoo),无需额外代码
同好接口 /interests/ Hexo 渲染页 source/interests/index.md(页面壳)+ style.css(样式)+ scripts/interests.js(渲染器)+ source/_data/interests.yml(唯一数据源) 菜单项;_config.yml 的 skip_render 两项排除 README 与样式对照页 四处一起搬(_data/ 与 scripts/ 是 Hexo 约定位置,不能塞进 source/interests/)
阅读榜单 /rank/ Hexo 渲染页 + 运行时取后端 source/rank/index.md(页面壳)+ style.css(样式)+ scripts/rank.js(渲染器)+ source/_data/rank.yml(文案与条数);阅读排行由后端 cloudfunctions/api/ 的 GET /stat/top 提供(读 Twikoo 的 counter 集合) 菜单项(「文章」组下);skip_render 一项排除 README 四处一起搬。⚠️ 依赖 _config.yml 的 updated_option: date(否则「最近更新」排序失真,且 sitemap 的 lastmod 全变成构建时间);⚠️ 行链接必须带主题的 no-text-decoration 类。详见 source/rank/README.md
Live2D 看板娘 第三方组件(自包含) source/live2d/:autoload.js、waifu-tips.js、waifu-tips.json、waifu.css、模型资源 inject.bottom 一行 整目录 + 一行引用
多语言(中英日) 构建脚本 + 前端脚本 scripts/i18n-blog.js(译文路由、列表过滤、生成 /js/i18n-map.js)+ source/js/i18n-switcher.js(右侧栏切换按钮) _config.yml 的 language 与 i18n_blog 段;inject.bottom 两行 脚本 + 配置段 + 注入行
日文注音(振り仮名) 前端脚本 source/js/furigana-app.js + source/js/furigana/(kuromoji + 词典) inject.bottom 一行 脚本 + 目录 + 一行
Twikoo 管理面板补丁 前端补丁(临时) source/js/twikoo-admin-fix.js inject.bottom 一行 一行 + 文件;上游修复后可整段删除
自定义样式 CSS source/css/anzhiyu-custom.css(播放器定位、语言按钮状态) inject.head 一行 一行 + 文件
脚注 构建脚本 scripts/footnotes-simple.js 无(构建期自动生效) 搬 scripts/
特殊博文集 /special/ Hexo 渲染页 source/special/index.md(页面壳)+ style.css(样式)+ scripts/special.js(渲染器)+ source/_data/special.yml(篇目与文案) 菜单项(「关于」组下);skip_render 一项排除 README 四处一起搬。⚠️ 篇目里的 file 必须与 _posts/ 下的文件名逐字一致(含中文标点),匹配不上会在页面上显式标红。链接与「最后更新」时间由构建期从文章本身取,改标题、abbrlink 漂移都不会失效。详见 source/special/README.md
资源下载 /res/ 独立静态应用 + 云端后端 前端 source/res/(2026-09-30 起为占地页,正式页:index.html + style.css + app.js + data.json + vendor/);后端复用 cloudfunctions/api/ 的 /res/list、/res/ops、/res/sign、/res/uploadurl、/res/filemeta、/res/probe 六条路由 + 云存储前缀 res/ 菜单项;skip_render: res/** 前端整目录复制 + 后端云函数另建。⚠️ 下载链接由后端签临时 URL(10 分钟),无签名直连对象一律 403;同一路径覆盖上传必须改版本号,否则命中 CDN 旧缓存
照片墙 /photos/ 旧独立页面 source/photos/:index.html + css/ + js/ + images/ 菜单项;skip_render 整目录

四、已清理的残留

文件 状态
source/js/copy-button.js NexT 时代的代码块复制脚本。安知鱼自带 highlight_copy: true 已取代它,全仓库零引用 —— 已于 2026-09-28 删除
_config.next.yml、_config.next_test.yml、_config.yml.bak2 等 Hexo 升级与主题切换留下的配置备份,待清理(_config.yml.bak2 可确认后删)

目录职责地图

路径 管什么
source/_posts/ 文章本体(Markdown),175 篇
source/_data/ 数据文件:about.yml、link.yml、interests.yml、rank.yml
source/js/、source/css/ 自定义脚本与样式,经 inject 注入页面(source/js/lib/ 是独立页面用的公共件,不注入)
source/map/、source/media/、source/res/、source/photos/ 独立静态页面(skip_render 直出)
source/interests/、source/rank/、source/special/ 自定义渲染页的页面壳与样式(样式只在本页加载,由各自的 scripts/*.js 注入)
source/live2d/ 看板娘组件全量资源
scripts/ 构建期脚本插件(Hexo 启动自动加载):脚注、多语言、同好接口与阅读榜单渲染
_tools/ 离线数据工具(POI 导入、加密迁移、改主密码、端到端校验 verify-*.js),不参与构建。_tools/vendor/ 放验证脚本的夹具(如 pjax 库——本机 CDN 不可达时靠它喂给浏览器,否则验证会假绿)
cloudfunctions/api/ 自研云函数源码(评论区的 Twikoo 是第三方成品,代码不入本仓库)
themes/anzhiyu/ 主题:页面骨架、样式、交互
_config.yml 站点配置:链接规则、多语言、搜索、部署目标
_config.anzhiyu.yml 主题配置:菜单、评论、inject 注入清单、CDN
.cnb.yml 云端构建流水线
public/、db.json、node_modules/ 构建产物与缓存(已在 .gitignore 中,不进仓库)

换主题要做什么

按上面的清单,迁移动作收敛为四步:

  1. 搬目录:source/ 下的 map、media、interests、rank、live2d、other、photos、js、css 九个目录,加 scripts/ 四个脚本、_tools/ 工具链、cloudfunctions/
  2. 搬配置:_config.yml 的 permalink/abbrlink、i18n_blog、search、symbols_count_time、excerpt、skip_render、updated_option、deploy 八段
  3. 搬注入:inject 段共 8 行(head 2 行 + bottom 6 行),改用新主题的注入机制
  4. 重接评论 + 后端:评论区接入代码是主题提供的,新主题若支持 Twikoo,填上 envId 即可——评论数据在云端 CloudBase,与主题无关,不会丢;媒体库的自研 api 云函数同样与主题无关,只要 auth.js 里的 DEFAULT_API_BASE 指向同一个环境就能接着用

内容组织

  • 主分类:生活随笔 / 技术笔记 / 知识库 / 收集整理 / 项目作品(另有若干早期分类仍在使用)
  • 导航分四组:文章(归档 · 分类 · 标签 · 知识库 · 阅读榜单)、友链、我的(同好接口 · 音乐馆 · 媒体库 · 照片墙 · 资源下载 · 资源集 · 足迹地图)、关于
  • ⚠️ 「资源集」是全站唯一的外链菜单项(指向第三方效率集页面,未自建)—— menu 的值写完整 https:// 地址即可,url_for() 原样放行,_config.yml 的 external_link 会自动补 target="_blank" rel="noopener"
  • 文章目录 source/_posts/,文件名即中文标题

改动年表

倒序排列:新的在上,旧的在下。

2026年

背景:2025 年腾讯云 Coding 停服,博客一度无法更新。2026 年年中迁到腾讯 CNB 云原生构建,随后展开了一轮大规模现代化改造——Hexo 4→8、主题 NexT→安知鱼、单语言→中英日三语,以及多个自研功能模块。

9月28-29日 · 媒体库云化(数据搬上 CloudBase + 真加密)

  • 动机:媒体库此前把数据写死在静态 data/*.json,改一条要本地跑脚本再 push 等构建;且「仅有自己可见」条目靠前端口令遮挡(哈希就写在公开可下载的 config.json 里),形同虚设
  • 后端:新建自研云函数 api(Event 类型 + HTTP 访问服务,挂 /api),按路径路由分发 /auth/login、/auth/ping、/auth/passwd、/media/list、/media/ops。Twikoo 保持独立函数不并入——它是第三方成品,升级要手工 merge,与自研逻辑纠缠;「共用后端」在云开发里指的是共用同一个环境(库/白名单/额度/日志),函数只是计算单元
  • 数据:一个类型一个文档(media 集合,_id = 类型 key,2400 条:anime 299 / movie 1868 / book 232 / game 1);静态 data/*.json 退化为「云端不可达时的降级快照」
  • 写入:整份覆盖写撞上 HTTP 访问服务 100 KB 请求体上限(实测 96 KB 过 / 100 KB 返回 EXCEED_MAX_PAYLOAD_SIZE,而响应体 6 MB 正常)→ 改为 op 级增量写入(pub/pubdel/priv/privdel 四种 op),前端按 ~64 KB 分批提交;写操作带 rev 防两台设备互相覆盖,撞 409 自动重拉重放
  • 编辑节奏:单条编辑与批量导入都只落在本地草稿(mediaDraft_<type> 的 op 变更日志),只有点「☁️ 提交」才写云——上云动作唯一,从入口上杜绝”无授权写入”
  • 权限:密码哈希只存云端(数据库 auth_guard/main 优先、环境变量兜底),前端永不可见;登录换 HMAC 签名带过期的 token(签名密钥混入密码哈希 → 改密码即让所有旧 token 失效);同 IP 连错 5 次锁 10 分钟。⚠️ HTTP 访问服务的 CORS 由平台自动回显任意 Origin(连 evil.example.com 都放通)→ token 必须走 Authorization 请求头,绝不能改用 Cookie
  • 私密条目真加密:PBKDF2-SHA256 × 600,000 → AES-256-GCM,逐条独立加密,密文块与公开数据同文档一起下发(访客拿到也解不开);salt 固定为 bWlzYWthLW1lZGlhLXYx,换来「跨设备、跨类型同一把钥匙」。密钥只进 sessionStorage(刷新不丢、关标签页失效),刷新页面不必重跑 60 万次 PBKDF2
  • 改密码:node _tools/set-main-pass.js 一条命令搞定——旧密码解密私密块 → 新密码重新加密上云 → 改后端主密码 → 验证新旧密码
  • 教训:KDF.salt 一开始写成裸字符串 'misaka-media-v1',而前端用 atob 解析 → 输对密码也进不去管理模式;改成 base64 并在 secrets.js 里加了校验与人话报错
  • 其他配套:抽出前端公共件 source/js/lib/secrets.js + auth.js(后续任何「需要密码」的模块直接复用);媒体库本地 38 项端到端断言全过(访客只读 / 口令校验 / 编辑提交 / 私密加密 / 云端降级);config.json 删掉形同虚设的 pinHash

9月29日 · 导航加「资源集」外链(评估后决定不自建)

  • 背景:原本想在导航「我的」组做一个自己的 web 导航页 —— 加 web、给 web 分类、统计点击量、每个分类内按点击量自动往前/往左排(但不超过置顶的)、标记「已失效」的加删除线,对标第三方站点 效率集(该站 2015 年用到现在)
  • 评估结论:不自建。 地基其实够(source/media/ 那套「云端 + 本地草稿 + op 增量 + 编辑 UI + 导入导出」能复用六成;数据落点也只是往现有 media 集合加一个 _id:'res' 文档,连新集合都不用建),但编辑体验恰恰是效率集最值钱、也最难追平的部分,为它重造一套不划算。最终只加一个外链菜单项
  • 改法:_config.anzhiyu.yml 的 menu.我的 组加一行,值直接写完整 URL:
    1
    资源集: "https://www.xiaolvji.com/u/misaka10013 || fa fa-bookmark"
  • ⚠️ 这是全站唯一的外链菜单项,两个现成机制正好配合:① url_for() 对绝对 URL 原样放行(实测:函数在最开头就 return,压根不碰 config、不会加站点前缀);② _config.yml 的 external_link.enable: true + field: site 会给全站外链自动补 target="_blank" rel="noopener",而站内项(如 /map/)不受影响 —— 于是点「资源集」是新标签页打开、博客还留着。零代码改动
  • 评估时实测的结论留档(将来真要自建可省一轮调研):
    • 自动图标不可靠:本机对 api.iowen.cn / favicon.im / favicon.cccyun.cc / Google s2 / DuckDuckGo 全部不可达(但百度、必应、npmmirror 都通,所以不是白名单问题);站点自身 /favicon.ico 则有 200 有 301。→ 真要自建得走「云函数代抓 HTML 里的 <link rel="icon">」,纯前端直连覆盖率不够
    • 点击量是这个需求唯一的架构难点:它与现有数据性质不同 —— 点击是高频动作,而现有 op 模式是「改一次提一次」。三条路:纯本地(换设备归零)、本地累加 + 进页面合并到云端(推荐)、每次点击立刻写云端(最费)
    • 自动排序会吃掉手动排序:按点击量排之后「左移/右移」就没意义了,只剩「置顶」还有用

9月29日 · 阅读榜单 /rank/

  • 入口:导航「文章」组下新增一项(PC 在顶部导航下拉、手机在侧边栏抽屉——两者共用 _config.anzhiyu.yml 的 menu 这一份配置,menu_item.pug 渲染两处)。页面两个 Tab:阅读排行 与 最近更新
  • 阅读量数据源:全站没有任何第三方统计(busuanzi 三个开关全 false,百度 / GA / CNZZ / Cloudflare / Clarity / Umami 全是空值),唯一的阅读量来源是 Twikoo 的 counter 集合——就是文章页顶上「阅读量: N」那个数。它的结构是「一个 url 一条文档,time = 该页累计访问次数」,所以排行直接倒序即可:没有新建埋点、没有新建集合
  • 这个数准不准取决于主题的 comments.lazyload:本站是 false → Twikoo 在文章页一打开就 init() 并 +1 → 等于文章页 PV(主题注释也写了”设为 true 会让评论数失效”)。⚠️ 数据从 2026-09-28 起才有,历史阅读量从未被统计过、任何地方都不存在、补不回来,页面底部有注脚如实说明
  • 后端:api 云函数加只读路由 GET /stat/top(routeStatTop)——只读 counter(从不写它)、只保留 /p/ 开头的文章页、裁掉标题里的站点后缀、带 5 分钟实例级内存缓存、读库失败返回空列表 + degraded 而不是整页报错。/health 版本 4 → 5
  • 两个榜单的数据来源不同(本页最要紧的一点):最近更新在构建期就渲染进 HTML(0 请求);阅读排行只能运行时 fetch —— Hexo 静态生成拿不到云端计数
  • 页面里埋了一份文章索引(url → [标题, 分类],175 篇约 15 KB):既给前端把排行的 url 换回标题与分类,也顺手挡掉非文章页——counter 会逐渐混进首页、留言板、独立页的记录,光靠后端前缀过滤不够稳
  • 踩坑一 · updated_option:Hexo 默认 mtime,而 CI 构建是全新 clone → 175 篇里 150 篇没手写 updated 的文章,其 updated 全等于构建时间:榜单排不出意义,且 sitemap 的 lastmod 与文章页 JSON-LD 的 dateModified 全体失真(每次部署都在说”全站刚改过”)。改成 updated_option: date(没手写时退回发布日期)—— 顺带修掉一个既有的 SEO 不准确问题
  • 踩坑二 · 主题的链接下划线:Anzhiyu 有一条 #article-container a:not(…):not(.no-text-decoration) { border-bottom: solid 2px var(--anzhiyu-lighttext); padding: 0 .2em },把本页的行分隔线覆盖成了主色实线。修法是用主题自带的逃生口类 no-text-decoration(就是那串 :not() 里的最后一个),没有用 !important 或加权重去硬压 —— 那样主题升级后会变隐患
  • 踩坑三 · pjax:主题的 pjax 只替换 ['head > title','#config-diff','#body-wrap',…],</head> 里除 <title> 外的标签不参与替换 → 从站内点菜单进来时正文换新了、按页注入的 <link> 却从没进过 DOM,页面成无样式纯文本。两条路缺一不可:after_render:html 往 </head> 前注入(管首访/刷新)+ tag 输出里带 <script data-pjax> 自愈脚本(管 pjax 跳转)。交互脚本同样带 data-pjax,否则 pjax 进来时脚本不执行、阅读排行永远停在”正在读取…”
  • 踩坑四 · 验证脚本自己制造了”假绿”(性质最坏的一个):脚本为绕开同步 CDN 阻塞,把外部请求全 abort 了 —— 但 pjax 库 pjax@0.2.8/pjax.min.js 也在 CDN 上,被一起掐掉 → window.Pjax undefined → 主题 new Pjax() 抛错 → pjax 根本没建起来,点菜单退化成普通整页跳转。而整页跳转本来就让 head 里的 <link> 正常加载、样式自然生效 → “进到了 /rank/、样式进了 DOM、样式生效、Tab 能切”全部通过。测试全绿,但 A′ 那个 data-pjax 自愈脚本一次都没被触发,等于没测。修法两条:① 用本地夹具 _tools/vendor/pjax-0.2.8.min.js(本机 jsdelivr / unpkg / staticfile / bootcdn 全不可达,只有 npmmirror 通)在 route.fulfill 里喂给浏览器,且必须排在 abort 判断之前;② 加存活标记断言 —— 点击前往 window 上写标记,点击后还在才算真 pjax(整页跳转会清空 window,pjax 不会)。教训推广:凡是用”屏蔽外部依赖”来稳定测试的写法,都要问一句「被我屏蔽的东西里,有没有本来就是被测对象的一部分」
  • 排查手段留档:临时探针页 public/_rkprobe.html 把 /rank/ 装进同源 iframe,用 ?p=style 打印关键元素的计算样式、?p=rules 遍历 document.styleSheets 列出命中该元素且含 border-bottom / text-decoration 的规则(直接拿到选择器全名)—— 比对着 671 KB 编译后 CSS 盲搜快得多
  • 验证:新增 _tools/verify-rank.js,23 项断言(a 直接访问 11 项 / b 从首页点菜单的 pjax 场景 11 项 / c 本页报错),本地与线上同一份脚本(BASE=https://misaka10013.cn)。⚠️ 脚本必须拦截并 abort 外部请求:主题 head 里挂的同步 CDN 脚本在受限网络下会阻塞 HTML 解析,导致 Playwright 的 domcontentloaded 和 load 全都不触发(现象是 30~45 秒超时,看着像”页面打不开”,其实本站在毫秒级就返回了 200)。线上实测 23 通过 / 0 失败
  • 不动主题文件:样式走 scripts/rank.js 的 after_render:html 按页注入(版本号取 style.css 内容 sha1 前 8 位),没有加进 inject 全站清单;no-text-decoration 是主题自己提供的类,不算改主题

9月29日 · 足迹地图云化(数据后端平移到 CloudBase)

  • 动机:媒体库云化后,地图仍是”浏览器改 → 导出 JSON → 替换 data.json → push 等构建”,加一个点要重新部署整站;且地图的私密数据是把 324 条打成一整块 84 KB 密文塞在公开可下载的 data.json 里 —— 改一条要整块重传,”未登录不给密文”也就无从谈起
  • 复用而非新建:地图没写第二套后端,直接复用媒体库那套基建。media 集合里多一个 _id='map' 的文档,路由加 /map/list、/map/ops,云函数 /health 版本升到 4。地图文档比媒体库文档多带 categories、config 两个字段,为此新增第 5 种 op:meta
  • 分两类而不是三类:点位只有公开和私密两种。此前把”个人点位 / 公共 POI / 私密点位”当三类是错的 —— 公开点位里有个人手写的、也有 poi-build.js 批量导入的景点,它们在数据上没区别,页面靠 poi: true 区分的是”参不参与时间筛选”,不是可见性
  • 访客可写,但只写浏览器:这是与媒体库刻意不同的一点 —— 媒体库访客只读,地图的访客可以随手加点、改点、删点、拖点。这些操作全部只落本地草稿(mapDraft_v1 的 op 变更日志,同一 id 同一种操作只留最后一个,拖动 100 次也只算一笔),一个字节都不碰云端。真正上云只有一条路:解锁后工具栏才出现的「☁️ 提交」
  • 私密点改逐条加密:旧的整块密文用旧随机 salt 解开 → 用固定 salt misaka-main-v1 逐条重新加密 → 324 个独立密文块上云,与媒体库完全同构。同步把媒体库的 salt 也改成同一值、密钥槽位统一成 main —— 于是同一标签页里两个模块解锁过一次就都不必再输密码
  • 静态文件里彻底不留密文:data.json 从 269 KB 瘦身到 185 KB(删掉 private 块,只留 443 个公开点)。这是云化的关键一步而不是顺带清理:data.json 人人可下载,留着密文就等于绕过”未登录不下发 priv”,攻击者不走 API 直接下文件照样能离线爆破
  • 顺手堵上一个洞:后端 /media/list、/map/list 改成无有效 token 一律不下发 priv(只返回 privCount 条数)。此前访客也能整包下载密文,等于把离线字典爆破的门票免费发出去;现在访客连密文都拿不到,媒体库省 97 KB、地图省 104 KB。privCount 仍返回是因为界面要显示”共 N 个私密点位”,这个数字确认为可公开
  • 三级兜底:云端优先 → 不可达时读 data.json 静态快照(弹提示”此时改动无法提交”)→ 再不行用 app.js 里的内置默认。另外云端尚无地图数据集时(首次上云之前的空档)会拿仓库快照当初始内容,解锁后一点提交就建好文档 —— 页面不会白屏
  • 登录态与密钥分两处存:token 进 localStorage(180 天,关标签页不丢)、解密密钥进 sessionStorage(关标签页失效)。所以刷新页面不用重输密码,关标签页才要重新解锁。另外解锁后必须带 token 重拉一次 /map/list —— 密文只有在登录后才下发
  • 删除是逻辑删除:公开点删掉后在云端留一条 deleted 标记的记录(页面不再显示,没做回收站 UI);私密点删除只撤密文块,绝不写公开明文墓碑(否则等于把私密内容公开上云)
  • 迁移脚本:_tools/map-migrate.js 一条命令搬完 443 公开 + 324 私密(分 10 批、rev 从 0 涨到 10)。它是回读校验通过之后才动本地 data.json 的 —— 中途失败云端和本地都不会被改;重复执行也安全(本地已无 private 块时跳过私密迁移,不会误删云端私密数据)
  • 验证:新增 _tools/verify-map-cloud.js,50 项端到端断言(访客态 / 解锁 / 公开点提交 / 私密点提交 / 云端降级 / 自动清理),本机与线上各跑一次全过;解锁后标记数 443 → 767 说明 324 条私密全部解密正确;媒体库复验 38/38 无回归
  • 顺手修的测试债:两个验证脚本里”检查云端密文”的断言此前用的是匿名请求,自”无 token 不下发 priv”上线后恒为空、属于静默失效(媒体库那条还因此漏删了一个测试密文块)。已改为带 token 请求,并把清理逻辑改成只删自己记录下来的 id,不按名字或数量猜
  • 其他配套:清掉 verify-v19.js 里硬编码的旧密码(改成从环境变量读);旧整块时代的 migrate-private.js 留档不删;index.html 引入 js/lib/secrets.js + auth.js,六处 ?v= bump 到 20260929a

9月28日 · Twikoo 评论系统适配与修复

  • 前端 Twikoo 升级到 2.0.11,以适配 CloudBase 新版身份认证(v2)——旧版 1.6.44 只走 v1 认证,报 100007
  • 开启匿名评论
  • 修复「管理面板输密码点登录没反应」。排查下来与密码无关,是 Twikoo 2.0.11 自身的两个上游缺陷:
    1. 后端从不签发 CUSTOM ticket(@twikoojs/common 里 ticket 出现 0 次,私钥错误码 1022 / 1025 只有枚举定义、零返回点)→ 前端 getCurrentUser().loginType 恒为匿名 → 面板永远停在登录框
    2. 前端在 CloudBase 路径下调云函数不携带 accessToken(HTTP 兜底路径会带,两条路径行为不一致)→ 即便面板能打开,管理操作也会被后端以 1024 请先登录 拒绝
  • 解法:新增 source/js/twikoo-admin-fix.js,打三处 hook(getCurrentUser / app.callFunction / signOut),判定权仍在后端(GET_CONFIG 返回 IS_ADMIN),前端只做转发。经 inject.bottom 注入,不动主题文件,上游修复后可整段删除
  • 顺带确认:官方 FAQ 里「粘贴自定义登录私钥」的流程在 2.0.11 根本没实现,私钥传了后端也不收

9月22-24日 · 同好接口上线 + 足迹地图加密

  • 新增 /interests/ 同好接口页面:统一卡片结构(name / state / level / one / now / links),数据集中在 source/_data/interests.yml,渲染由 scripts/interests.js 完成
  • 足迹地图私密点加密升级:PBKDF2-SHA256 × 600,000 → AES-256-GCM,公开点明文 + 私密点密文块;密钥仅存内存,刷新后需重新解锁
  • 地图缓存机制优化(确立 ?v= 版本号约定),补「返回博客」入口

9月15-17日 · SEO 专项 + 导航修整

  • 修复 robots.txt 里模板残留的错误 Sitemap 域名;注入 JSON-LD 结构化数据;强化品牌词
  • 填入 Google / 百度站长验证码,清理主题默认的 msvalidate 占位符
  • 全站切换 https
  • 修复多语言标签页 / 归档空页 / robots 三类问题
  • 导航与首页分类入口修正:关闭 4 个无对应页面的导航项,按钮改指实际分类

9月7-8日 · 日文注音 / 媒体库 / 音乐馆

  • 新增日文文章自动注音(振り仮名):source/js/furigana-app.js + 自托管 kuromoji 分词库,仅 /ja/ 页面激活
  • 媒体档案库 /media/ 上线并迭代,加入「仅自己可见」条目与主人口令锁
  • 音乐馆修复:Meting 默认 API 失效,改用公益镜像(153 首可播)

9月4日 · 图片加载与访问速度优化

  • 修复图片加载慢:关闭 hexo-lazyload-image(与主题 lazyload 协议冲突,它只认 no-lazy、不认主题的 nolazyload)
  • CDN 优化:第三方资源 provider 从 cbd 换成 jsdelivr(影响 17 个资源)
  • 修复多语言 bug、关于页排版

9月1日 · 主题正式切换为安知鱼

  • 主配置 theme 从 NexT 切换为 anzhiyu(NexT 配置保留在 _config.next_test.yml,可随时切回)
  • NexT → 安知鱼配置迁移:社交、友链、搜索、字数统计、页脚年份、深色按钮
  • 删除 NexT 遗留的注入文件(copy-button.js、崩溃欺骗、本文结束 paw)
  • 新增《Hexo升级记:跨越6年与增加多语言支持》及英日译文

8月27-28日 · Hexo 4→8 大版本升级 + 多语言

  • Hexo 4 → 8、NexT 6 → 8(改 npm 主题 + Alternate Config 迁移定制项,保留 live2d / 复制按钮 / gitalk / 本地搜索)
  • 补装 hexo-excerpt,恢复 NexT 6 的首页自动摘要行为
  • 仓库卫生:.gitignore 并停止跟踪 node_modules / public / db.json / debug.log
  • 多语言 i18n 三步落地:译文语言前缀路由(/en/p/、/ja/p/)+ 列表按语言过滤 + 文章页切换器(有译文可点、无译文置灰)
  • CNB 构建镜像改 node:22(Hexo 8 要求 ≥ 20.19)

8月17-19日 · 代码块复制 + 足迹地图上线

  • 为代码块添加一键复制按钮(注入 JS 方案,不升级主题)
  • 足迹地图 /map/ 上线,并配置 skip_render 避免被 Hexo 渲染

8月7日 · 脚注方案重写

  • 弃用 hexo-reference(它在表格单元格里会被 marked 转义成纯文本),改用自研 scripts/footnotes-simple.js,生成干净的 sup + a 结构

7月10日 · 博客分类重构 + 访问速度优化

  • 分类重构:新增「知识库」分类,151 篇文章更新 categories,10 篇知识库文章重命名并注入知识地图,侧边栏新增知识库入口
  • 优化访问速度:本地化外部依赖
  • 修复 hexo-util 1.9.0 生成 0kb 文件的问题

6月12日 · 迁移到 CNB 云原生构建

  • 添加 .cnb.yml,博客从腾讯云 Coding 迁移到 CNB,推送即构建
  • 部署配置修正:分支名 main → master、hexo-deployer-ali-oss 版本回退到 1.0.0
  • 新博文《hexo博客从Coding迁移到CNB云原生构建实践》

2023年

因为腾讯云 COS 加强对静态页面的审核,促使我将博客 hexo 静态页面的生成地址迁移到阿里云香港 OSS 上。

2021年

4月22日

由于文章有点多,有时候自己找起来不方便,在网上找了个搜索组件。

npm install hexo-generator-searchdb --save

持续集成现目中去添加一下组件。
在站点配置文件更新内容

1
2
3
4
5
search:
path: search.xml
field: post
format: html
limit: 10000

在样式文件中修改设置。

1
2
local_search:
enable: true

3月29日

修复博客自动部署后 live2D 的 js 文件拉取不到的情况。

<script src="https://soyawww.github.io/live2d/autoload.js"></script> 索引到自己的 js 文件。

2月9日

构建失败,测试

2020年

9月26日

  • 利用 coding 的 ci 自动集成部署实现 hexo 博客的云端编译自动化。将原本部署在 surface 上面的 hexo 源码上传到 coding 仓库中,利用持续集成部署 Jenkinsfile 编辑以下脚本内容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    pipeline {
    agent any
    stages {
    stage('构建') {
    steps {
    echo '构建中...'
    sh 'go version'
    sh 'node -v'
    sh 'git clone https://XXX:YYY@e.coding.net/项目名称.git .' //XXX是coding免密访问用户名,YYY是免密秘钥,在hexo中的git内容中也需要配置
    sh 'ls -a'
    sh 'npm install -g hexo-cli'
    sh 'npm install hexo --save' //hexo依赖
    sh 'npm install hexo-generator-archive --save' //坚持安装其他依赖
    sh 'npm install hexo-generator-sitemap' //站点优化seo依赖
    sh 'npm install hexo-generator-baidu-sitemap'
    sh 'npm install hexo-abbrlink --save' //文章访问链接优化依赖
    echo '构建完成.'
    }
    }
    stage('测试') {
    steps {
    echo '单元测试中...'
    sh 'hexo clean'
    sh 'hexo g'
    echo '单元测试完成.'
    }
    }
    stage('部署') {
    steps {
    echo '部署中...'
    sh 'hexo deploy'
    echo '部署完成'
    }
    }
    }

    部署实现详情需要参考文章:

  • 需要注意目前中文名称文章优化遇到未知问题失效,后期需要再次优化。目前修改配置的文章访问链接优化依赖

    1
    npm install hexo-abbrlink --save

    需要修改博客根目录配置文件 _config.yml 的 permalink:

    1
    2
    3
    4
    5
    # permalink: :year/:month/:day/:title/
    permalink: p/:abbrlink.html # p 是自定义的前缀
    abbrlink:
    alg: crc32 #算法: crc16(default) and crc32
    rep: hex #进制: dec(default) and hex

8月22日

  • coding解析的境外线路,突然被墙了。目前正在反馈核实中。

  • 针对皮皮明反馈的文章页面连接过长的问题,进行了优化。hexo中特别是文章中文标题在复制链接时每个汉字都会转换为4个16进制字符,导致访问链接会非常长。
    目前解决方案:
    对node_modules\hexo\lib\plugins\filter\post_permalink.js 文件postPermalinkFilter函数中关于meta变量定义中加入一行date_abbr: data.date.unix().toString(36),或将date_abbr: 定义为
    Math.floor((data.date.unix()-1377964800)/3600).toString(36)。之后修改站点文件_config.yml的permalink属性值为从:year/:month/:day/:title/修改为:date_abbr/。可以实现将原来链接中名称部分精简到4个字符。

  • 针对站点进行SEO优化。
    添加SEO插件:

1
2
npm install hexo-generator-sitemap
npm install hexo-generator-baidu-sitemap

在站点_config.yml文件中设置SEO:

1
2
3
4
sitemap:  
path: sitemap.xml
baidusitemap:
path: baidusitemap.xml #注意此行参数不能tab空格。

8月15日

  • 为了解决页面因图片加载访问过多导致部分图片无法访问的问题,使用网上的懒加载方式。

Hexo-lazy-image 使用

1
npm install hexo-lazyload-image --save

安装插件之后修改_config.yml 文件添加以下内容

1
2
3
4
lazyload:
enable: true # 开启图片懒加载
onlypost: false # 当为 true 时只针对 post 做图片懒加载,为 false 则应用到所有页面
# loadingImg: # 不设置使用默认图片作为懒加载时的替代图片,设置后则使用该图片作为懒加载时显示的图片

8月7日

  • 为了加快访问,将对象存储图片文件使用CDN加速。
  • 修复了部分文章的图片
  • 为了应对对象存储中大量的图片文件管理进行进一步文件分类和标签化

5月6日

  • 更新coding、GitHub双平台博客

5月5日

  • 更新部分live2d人物话语

5月3日

  • 更新live2d

下载大佬的live2d源码,放置于 themes\next\source 下,在 \themes\next\layout\_layout.swig 文件的head标签下添加依赖。

1
2
<script src="https://cdn.jsdelivr.net/npm/jquery/dist/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/font-awesome/css/font-awesome.min.css">

在footer标签下添加调用
<script src="https://soyawww.github.io/live2d/autoload.js"></script>
修改live2d包文件下的autoloads.js文件路径为绝对路径
const live2d_path = "/live2d/";
其他如在waifu.css修改看板的大小和位置,在waifu-tips.json修改对话。

5月2日

  • 去掉背景音乐,加快访问速度

4月21日

  • 更新瀑布式相册,使用腾讯云对象存储。

  • 御坂特殊博文页面:用于放置长时间持续更新的特殊的博客文章。

3月12日

  • 更新评论系统

  • 更新音乐

  • 更新相册

2020年2月27日 GitHub+hexo搭建静态博客

为逃避国内和谐,使用hexo搭建静态博客,简化维护,便于上传文章。GitHub成为博客数据中心。

2019年3月11日 上传部分文章制作御坂博客页面

主要为引导页面,指向简书、新浪博客以及自定义页面。

2017年3月22日 使用zblog在阿里服务器建立网站

首次进行备案、域名解析等等。但后因域名被抢注不再使用。