avatar
文章
7
标签
15
分类
4
首页
归档
标签
分类
关于
Q's blogHexo 博客文章加密实现 返回首页
搜索
首页
归档
标签
分类
关于

Hexo 博客文章加密实现

发表于2026-06-17|更新于2026-06-17|技术建站
|总字数:7|阅读时长:1分钟|浏览量:
Hexo加密安全
上一篇
GitHub Pages 完全指南:原理、配置与使用限制
约 1.7k 字    6 分钟阅读 什么是 GitHub Pages #GitHub Pages 是 GitHub 提供的静态网站托管服务。它直接从 GitHub 仓库读取 HTML/CSS/JS 文件,通过 CDN 分发到全球,无需自己搭建服务器。 核心定位:为项目文档、个人博客、开源项目主页提供零成本的静态站点托管。 工作原理 #请求流程 #1234567用户访问 https://username.github.io/repo/ ↓GitHub Pages CDN 节点(全球分布) ↓从仓库的特定分支读取静态文件 ↓返回 HTML/CSS/JS 给浏览器 构建方式 #GitHub Pages 支持两种模式: 1. 直接部署模式(Deploy from a branch) 123仓库分支(如 master/main 或 gh-pages) ↓ 直接提供文件GitHub Pages CDN 适用于已经构建好的静态文件(如 Hexo 的 public/ 目录直接推送到仓库)。 ...
下一篇
用 Hexo 搭建认知管理系统
约 1.3k 字    4 分钟阅读 为什么用 Hexo 做认知管理 #知识管理的核心痛点不是”存”,而是”找”。传统的笔记软件(Notion、Obsidian、语雀)各自封闭,数据无法被搜索引擎和 AI 直接检索。而 Hexo 作为一个静态博客生成器,天然具备三个优势: 数据即文件 — 所有内容是本地 Markdown,无平台锁定 结构化索引 — search.json 提供机器可读的全文索引 公开可检索 — 部署到 GitHub Pages 后,内容可被 Google 和 AI 搜索引擎索引 这个项目不是”写博客”,而是构建一个个人知识的外化系统:把学过的知识用结构化的方式记录下来,既便于自己检索,也能被 AI 工具消费。 技术栈 # 组件 选型 说明 框架 Hexo 8.x 静态站点生成器 主题 Butterfly 5.5.5 暗色模式,通过 npm 安装 搜索 hexo-generator-searchdb 生成本地搜索索引 search.json 字数统计 hexo-wordcount 文章阅读时长估...
相关推荐
2026-06-17
Hexo AI 数据通道:让 AI 零噪音读取博客内容
约 2k 字    9 分钟阅读 问题:AI 读到的不是文章,是噪音 #Hexo 生成的 HTML 页面中,正文内容只占约 30%,其余全是导航栏、侧边栏、页脚、JavaScript 脚本、CSS 样式等 UI 元素。当 AI Agent 尝试读取文章时,必须从一堆 HTML 标签中提取正文,效率低且容易丢失格式。 现有数据源的问题 # 数据源 问题 HTML 页面 80% 是 UI 噪音,AI 要从 HTML 中捞正文 search.json 所有文章挤一个文件;内容被截断;混入 UI 噪音;表格/代码格式丢失 atom.xml XML 开销;仅限 20 篇;仍含 HTML 标签 ai-index.json 只有元数据,没有正文 核心矛盾:人类需要丰富的 UI,AI 需要干净的纯内容。两者不能在同一个 HTML 里兼得。 解决方案:平行数据通道 #为 AI 提供一条独立的数据通道——人类看 HTML,AI 读 JSON/MD。两条通道并行,互不干扰。 12345678hexo generate ├── 生成...
2026-06-17
用 Hexo 搭建认知管理系统
约 1.3k 字    4 分钟阅读 为什么用 Hexo 做认知管理 #知识管理的核心痛点不是”存”,而是”找”。传统的笔记软件(Notion、Obsidian、语雀)各自封闭,数据无法被搜索引擎和 AI 直接检索。而 Hexo 作为一个静态博客生成器,天然具备三个优势: 数据即文件 — 所有内容是本地 Markdown,无平台锁定 结构化索引 — search.json 提供机器可读的全文索引 公开可检索 — 部署到 GitHub Pages 后,内容可被 Google 和 AI 搜索引擎索引 这个项目不是”写博客”,而是构建一个个人知识的外化系统:把学过的知识用结构化的方式记录下来,既便于自己检索,也能被 AI 工具消费。 技术栈 # 组件 选型 说明 框架 Hexo 8.x 静态站点生成器 主题 Butterfly 5.5.5 暗色模式,通过 npm 安装 搜索 hexo-generator-searchdb 生成本地搜索索引 search.json 字数统计 hexo-wordcount 文章阅读时长估...
avatar
Bsheepcoder
认知管理系统 · AI 索引友好 · 记录与检索并重
文章
7
标签
15
分类
4
Follow Me
公告
本站支持 AI 数据通道(/llms.txt),内容可通过 API 获取
目录
  1. 1. 为什么静态博客需要加密
  2. 2. 加密流程(构建时)
    1. 2.1. 1. 生成随机盐和随机数
    2. 2.2. 2. 密钥派生(PBKDF2-SHA256)
    3. 2.3. 3. AES-256-GCM 加密
  3. 3. 解密流程(浏览器端)
  4. 4. 安全性分析
    1. 4.1. 局限性
  5. 5. 配置方式
    1. 5.1. 单篇文章
    2. 5.2. 全局默认配置
    3. 5.3. 多密码(标签分组)
  6. 6. 总结
最新文章
RSS 日报 · 2026-06-182026-06-18
RSS 日报生成工作流2026-06-18
Hexo AI 数据通道:让 AI 零噪音读取博客内容2026-06-17
RSS 源大全:可信信息获取的数据源清单2026-06-17
GitHub Pages 完全指南:原理、配置与使用限制2026-06-17
© 2021 - 2026 By Bsheepcoder框架 Hexo 8.1.2|主题 Butterfly 5.5.5
` const blob = new Blob([htmlSource], { type: 'text/html;charset=utf-8' }) const url = URL.createObjectURL(blob) window.open(url, '_blank', 'noopener') setTimeout(() => URL.revokeObjectURL(url), 30000) } const attachMermaidViewerButton = wrap => { let btn = wrap.querySelector('.mermaid-open-btn') if (!btn) { btn = document.createElement('button') btn.type = 'button' btn.className = 'mermaid-open-btn' wrap.appendChild(btn) } btn.innerHTML = '' if (!btn.__mermaidViewerBound) { btn.addEventListener('click', e => { e.preventDefault() e.stopPropagation() const svg = wrap.__mermaidOriginalSvg || wrap.querySelector('svg') if (!svg) return const initViewBox = wrap.__mermaidInitViewBox if (typeof svg === 'string') { openSvgInNewTab({ source: svg, initViewBox }) return } openSvgInNewTab({ source: svg, initViewBox }) }) btn.__mermaidViewerBound = true } } // Zoom around a point (px, py) in the SVG viewport (in viewBox coordinates) const zoomAtPoint = (vb, factor, px, py) => { const w = vb[2] * factor const h = vb[3] * factor const nx = px - (px - vb[0]) * factor const ny = py - (py - vb[1]) * factor return [nx, ny, w, h] } const initMermaidGestures = wrap => { const svg = wrap.querySelector('svg') if (!svg) return // Ensure viewBox exists so gestures always work const initVb = getSvgViewBox(svg) wrap.__mermaidInitViewBox = initVb wrap.__mermaidCurViewBox = initVb.slice() setSvgViewBox(svg, initVb) // Avoid binding multiple times on themeChange/pjax if (wrap.__mermaidGestureBound) return wrap.__mermaidGestureBound = true // Helper: map client (viewport) coordinate -> viewBox coordinate const clientToViewBox = (clientX, clientY) => { const rect = svg.getBoundingClientRect() const vb = wrap.__mermaidCurViewBox || getSvgViewBox(svg) const x = vb[0] + (clientX - rect.left) * (vb[2] / rect.width) const y = vb[1] + (clientY - rect.top) * (vb[3] / rect.height) return { x, y, rect, vb } } const state = { pointers: new Map(), startVb: null, startDist: 0, startCenter: null } const clampVb = vb => { const init = wrap.__mermaidInitViewBox || vb const minW = init[2] * 0.1 const maxW = init[2] * 10 const minH = init[3] * 0.1 const maxH = init[3] * 10 vb[2] = clamp(vb[2], minW, maxW) vb[3] = clamp(vb[3], minH, maxH) return vb } const setCurVb = vb => { vb = clampVb(vb) wrap.__mermaidCurViewBox = vb setSvgViewBox(svg, vb) } const onPointerDown = e => { // Allow only primary button for mouse if (e.pointerType === 'mouse' && e.button !== 0) return svg.setPointerCapture(e.pointerId) state.pointers.set(e.pointerId, { x: e.clientX, y: e.clientY }) if (state.pointers.size === 1) { state.startVb = (wrap.__mermaidCurViewBox || getSvgViewBox(svg)).slice() } else if (state.pointers.size === 2) { const pts = [...state.pointers.values()] const dx = pts[0].x - pts[1].x const dy = pts[0].y - pts[1].y state.startDist = Math.hypot(dx, dy) state.startVb = (wrap.__mermaidCurViewBox || getSvgViewBox(svg)).slice() state.startCenter = { x: (pts[0].x + pts[1].x) / 2, y: (pts[0].y + pts[1].y) / 2 } } } const onPointerMove = e => { if (!state.pointers.has(e.pointerId)) return state.pointers.set(e.pointerId, { x: e.clientX, y: e.clientY }) // Pan with 1 pointer if (state.pointers.size === 1 && state.startVb) { const p = [...state.pointers.values()][0] const prev = { x: e.clientX - e.movementX, y: e.clientY - e.movementY } // movementX/Y unreliable on touch, compute from stored last position const last = wrap.__mermaidLastSinglePointer || p const dxClient = p.x - last.x const dyClient = p.y - last.y wrap.__mermaidLastSinglePointer = p const { rect } = clientToViewBox(p.x, p.y) const vb = (wrap.__mermaidCurViewBox || getSvgViewBox(svg)).slice() const dx = dxClient * (vb[2] / rect.width) const dy = dyClient * (vb[3] / rect.height) setCurVb([vb[0] - dx, vb[1] - dy, vb[2], vb[3]]) return } // Pinch zoom with 2 pointers if (state.pointers.size === 2 && state.startVb && state.startDist > 0) { const pts = [...state.pointers.values()] const dx = pts[0].x - pts[1].x const dy = pts[0].y - pts[1].y const dist = Math.hypot(dx, dy) if (!dist) return const factor = state.startDist / dist // dist bigger => zoom in (viewBox smaller) const cx = (pts[0].x + pts[1].x) / 2 const cy = (pts[0].y + pts[1].y) / 2 const centerClient = { x: cx, y: cy } const pxy = clientToViewBox(centerClient.x, centerClient.y) const cpx = pxy.x const cpy = pxy.y const vb = zoomAtPoint(state.startVb, factor, cpx, cpy) setCurVb(vb) } } const onPointerUpOrCancel = e => { state.pointers.delete(e.pointerId) if (state.pointers.size === 0) { state.startVb = null state.startDist = 0 state.startCenter = null wrap.__mermaidLastSinglePointer = null } else if (state.pointers.size === 1) { // reset single pointer baseline to avoid jump wrap.__mermaidLastSinglePointer = [...state.pointers.values()][0] } } // Wheel zoom (mouse/trackpad) const onWheel = e => { // ctrlKey on mac trackpad pinch; we treat both as zoom e.preventDefault() const delta = e.deltaY const zoomFactor = delta > 0 ? 1.1 : 0.9 const { x, y } = clientToViewBox(e.clientX, e.clientY) const vb = (wrap.__mermaidCurViewBox || getSvgViewBox(svg)).slice() setCurVb(zoomAtPoint(vb, zoomFactor, x, y)) } const onDblClick = () => { const init = wrap.__mermaidInitViewBox if (!init) return wrap.__mermaidCurViewBox = init.slice() setSvgViewBox(svg, init) } svg.addEventListener('pointerdown', onPointerDown) svg.addEventListener('pointermove', onPointerMove) svg.addEventListener('pointerup', onPointerUpOrCancel) svg.addEventListener('pointercancel', onPointerUpOrCancel) svg.addEventListener('wheel', onWheel, { passive: false }) svg.addEventListener('dblclick', onDblClick) } const runMermaid = ele => { window.loadMermaid = true const theme = document.documentElement.getAttribute('data-theme') === 'dark' ? '' : '' ele.forEach((item, index) => { const mermaidSrc = item.firstElementChild // Clear old render (themeChange/pjax will rerun) const oldSvg = item.querySelector('svg') if (oldSvg) oldSvg.remove() item.__mermaidGestureBound = false const config = mermaidSrc.dataset.config ? JSON.parse(mermaidSrc.dataset.config) : {} if (!config.theme) { config.theme = theme } const mermaidThemeConfig = `%%{init: ${JSON.stringify(config)}}%%\n` const mermaidID = `mermaid-${index}` const mermaidDefinition = mermaidThemeConfig + mermaidSrc.textContent const renderFn = mermaid.render(mermaidID, mermaidDefinition) const renderMermaid = svg => { mermaidSrc.insertAdjacentHTML('afterend', svg) if (true) initMermaidGestures(item) item.__mermaidOriginalSvg = svg if (true) attachMermaidViewerButton(item) } // mermaid v9 and v10 compatibility typeof renderFn === 'string' ? renderMermaid(renderFn) : renderFn.then(({ svg }) => renderMermaid(svg)) }) } const codeToMermaid = () => { const codeMermaidEle = document.querySelectorAll('pre > code.mermaid') if (codeMermaidEle.length === 0) return codeMermaidEle.forEach(ele => { const preEle = document.createElement('pre') preEle.className = 'mermaid-src' preEle.hidden = true preEle.textContent = ele.textContent const newEle = document.createElement('div') newEle.className = 'mermaid-wrap' newEle.appendChild(preEle) ele.parentNode.replaceWith(newEle) }) } const loadMermaid = () => { if (false) codeToMermaid() const $mermaid = document.querySelectorAll('#article-container .mermaid-wrap') if ($mermaid.length === 0) return const runMermaidFn = () => runMermaid($mermaid) btf.addGlobalFn('themeChange', runMermaidFn, 'mermaid') window.loadMermaid ? runMermaidFn() : btf.getScript('https://cdn.jsdelivr.net/npm/mermaid@11.15.0/dist/mermaid.min.js').then(runMermaidFn) } btf.addGlobalFn('encrypt', loadMermaid, 'mermaid') window.pjax ? loadMermaid() : document.addEventListener('DOMContentLoaded', loadMermaid) })()
搜索
数据加载中