2025-03-17 13:25:46 +00:00

320 lines
27 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html><html lang="zh-CN" data-theme="dark"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0,viewport-fit=cover"><title>基于 IPv6 公网地址、NAS 和 MacMini 的私有部署博客方案 | 時痕</title><meta name="author" content="Linloir"><meta name="copyright" content="Linloir"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#0d0d0d"><meta name="description" content="方案速览简单来说,方案分为了几个主要的部分: 根据 在 NAS 上部署自己的 Gitea 服务,无需公网服务器 方案打通外网到家用 NAS &#x2F; MacMini 的链路 采用 Git 仓库 main 分支存放源码 + Gitea Actions 编译至 publish 分支实现源码及制品存储 使用 caddy-git 插件实现拉取 Git 仓库 publish 分支并作为 fileser">
<meta property="og:type" content="article">
<meta property="og:title" content="基于 IPv6 公网地址、NAS 和 MacMini 的私有部署博客方案">
<meta property="og:url" content="https://blog.linloir.cn/2024/10/12/blog-from-scratch/">
<meta property="og:site_name" content="時痕">
<meta property="og:description" content="方案速览简单来说,方案分为了几个主要的部分: 根据 在 NAS 上部署自己的 Gitea 服务,无需公网服务器 方案打通外网到家用 NAS &#x2F; MacMini 的链路 采用 Git 仓库 main 分支存放源码 + Gitea Actions 编译至 publish 分支实现源码及制品存储 使用 caddy-git 插件实现拉取 Git 仓库 publish 分支并作为 fileser">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://blog.linloir.cn/img/cover.jpg">
<meta property="article:published_time" content="2024-10-12T14:21:13.000Z">
<meta property="article:modified_time" content="2025-03-17T13:25:21.597Z">
<meta property="article:author" content="Linloir">
<meta property="article:tag" content="瞎捣鼓">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://blog.linloir.cn/img/cover.jpg"><link rel="shortcut icon" href="/img/avatar.png"><link rel="canonical" href="https://blog.linloir.cn/2024/10/12/blog-from-scratch/"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//busuanzi.ibruce.info"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox/fancybox.min.css" media="print" onload="this.media='all'"><script>
(() => {
const saveToLocal = {
set: (key, value, ttl) => {
if (!ttl) return
const expiry = Date.now() + ttl * 86400000
localStorage.setItem(key, JSON.stringify({ value, expiry }))
},
get: key => {
const itemStr = localStorage.getItem(key)
if (!itemStr) return undefined
const { value, expiry } = JSON.parse(itemStr)
if (Date.now() > expiry) {
localStorage.removeItem(key)
return undefined
}
return value
}
}
window.btf = {
saveToLocal,
getScript: (url, attr = {}) => new Promise((resolve, reject) => {
const script = document.createElement('script')
script.src = url
script.async = true
Object.entries(attr).forEach(([key, val]) => script.setAttribute(key, val))
script.onload = script.onreadystatechange = () => {
if (!script.readyState || /loaded|complete/.test(script.readyState)) resolve()
}
script.onerror = reject
document.head.appendChild(script)
}),
getCSS: (url, id) => new Promise((resolve, reject) => {
const link = document.createElement('link')
link.rel = 'stylesheet'
link.href = url
if (id) link.id = id
link.onload = link.onreadystatechange = () => {
if (!link.readyState || /loaded|complete/.test(link.readyState)) resolve()
}
link.onerror = reject
document.head.appendChild(link)
}),
addGlobalFn: (key, fn, name = false, parent = window) => {
if (!true && key.startsWith('pjax')) return
const globalFn = parent.globalFn || {}
globalFn[key] = globalFn[key] || {}
if (name && globalFn[key][name]) return
globalFn[key][name || Object.keys(globalFn[key]).length] = fn
parent.globalFn = globalFn
}
}
const activateDarkMode = () => {
document.documentElement.setAttribute('data-theme', 'dark')
if (document.querySelector('meta[name="theme-color"]') !== null) {
document.querySelector('meta[name="theme-color"]').setAttribute('content', 'undefined')
}
}
const activateLightMode = () => {
document.documentElement.setAttribute('data-theme', 'light')
if (document.querySelector('meta[name="theme-color"]') !== null) {
document.querySelector('meta[name="theme-color"]').setAttribute('content', 'undefined')
}
}
btf.activateDarkMode = activateDarkMode
btf.activateLightMode = activateLightMode
const theme = saveToLocal.get('theme')
const mediaQueryDark = window.matchMedia('(prefers-color-scheme: dark)')
const mediaQueryLight = window.matchMedia('(prefers-color-scheme: light)')
if (theme === undefined) {
if (mediaQueryLight.matches) activateLightMode()
else if (mediaQueryDark.matches) activateDarkMode()
else {
const hour = new Date().getHours()
const isNight = hour <= 6 || hour >= 18
isNight ? activateDarkMode() : activateLightMode()
}
mediaQueryDark.addEventListener('change', () => {
if (saveToLocal.get('theme') === undefined) {
e.matches ? activateDarkMode() : activateLightMode()
}
})
} else {
theme === 'light' ? activateLightMode() : activateDarkMode()
}
const asideStatus = saveToLocal.get('aside-status')
if (asideStatus !== undefined) {
document.documentElement.classList.toggle('hide-aside', asideStatus === 'hide')
}
const detectApple = () => {
if (/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)) {
document.documentElement.classList.add('apple')
}
}
detectApple()
})()
</script><script>const GLOBAL_CONFIG = {
root: '/',
algolia: undefined,
localSearch: undefined,
translate: {"defaultEncoding":2,"translateDelay":0,"msgToTraditionalChinese":"繁","msgToSimplifiedChinese":"简"},
noticeOutdate: undefined,
highlight: {"plugin":"highlight.js","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":false,"highlightFullpage":false,"highlightMacStyle":false},
copy: {
success: '复制成功',
error: '复制失败',
noSupport: '浏览器不支持'
},
relativeDate: {
homepage: false,
post: false
},
runtime: '',
dateSuffix: {
just: '刚刚',
min: '分钟前',
hour: '小时前',
day: '天前',
month: '个月前'
},
copyright: undefined,
lightbox: 'fancybox',
Snackbar: {"chs_to_cht":"已切换为繁体中文","cht_to_chs":"已切换为简体中文","day_to_night":"已切换为深色模式","night_to_day":"已切换为浅色模式","bgLight":"#49b1f5","bgDark":"#1f1f1f","position":"top-center"},
infinitegrid: {
js: 'https://cdn.jsdelivr.net/npm/@egjs/infinitegrid/dist/infinitegrid.min.js',
buttonText: '加载更多'
},
isPhotoFigcaption: false,
islazyload: false,
isAnchor: true,
percent: {
toc: false,
rightside: false,
},
autoDarkmode: true
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
title: '基于 IPv6 公网地址、NAS 和 MacMini 的私有部署博客方案',
isPost: true,
isHome: false,
isHighlightShrink: false,
isToc: true,
postUpdate: '2025-03-17 21:25:21'
}</script><meta name="generator" content="Hexo 7.3.0"></head><body><script>window.paceOptions = {
restartOnPushState: false
}
btf.addGlobalFn('pjaxSend', () => {
Pace.restart()
}, 'pace_restart')
</script><link rel="stylesheet" href="/css/minimal.css"/><script src="https://cdn.jsdelivr.net/npm/pace-js/pace.min.js"></script><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src="/img/avatar.png" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="site-data is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">21</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">18</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">2</div></a></div><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 主页</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-th"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 归档</span></a></div></div></div></div><div class="post" id="body-wrap"><header class="post-bg" id="page-header" style="background-image: url(/img/cover.jpg);"><nav id="nav"><span id="blog-info"><a class="nav-site-title" href="/"><span class="site-name">時痕</span></a><a class="nav-page-title" href="/"><span class="site-name">基于 IPv6 公网地址、NAS 和 MacMini 的私有部署博客方案</span></a></span><div id="menus"><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 主页</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-th"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 归档</span></a></div></div><div id="toggle-menu"><span class="site-page"><i class="fas fa-bars fa-fw"></i></span></div></div></nav><div id="post-info"><h1 class="post-title">基于 IPv6 公网地址、NAS 和 MacMini 的私有部署博客方案</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2024-10-12T14:21:13.000Z" title="发表于 2024-10-12 22:21:13">2024-10-12</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2025-03-17T13:25:21.597Z" title="更新于 2025-03-17 21:25:21">2025-03-17</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/%E6%8A%80%E6%9C%AF/">技术</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-wordcount"><i class="far fa-file-word fa-fw post-meta-icon"></i><span class="post-meta-label">总字数:</span><span class="word-count">302</span><span class="post-meta-separator">|</span><i class="far fa-clock fa-fw post-meta-icon"></i><span class="post-meta-label">阅读时长:</span><span>1分钟</span></span><span class="post-meta-separator">|</span><span class="post-meta-pv-cv" id="" data-flag-title=""><i class="far fa-eye fa-fw post-meta-icon"></i><span class="post-meta-label">浏览量:</span><span id="busuanzi_value_page_pv"><i class="fa-solid fa-spinner fa-spin"></i></span></span></div></div></div></header><main class="layout" id="content-inner"><div id="post"><article class="post-content" id="article-container"><h2 id="方案速览"><a href="#方案速览" class="headerlink" title="方案速览"></a>方案速览</h2><p>简单来说,方案分为了几个主要的部分:</p>
<ol>
<li>根据 <a href="https://blog.linloir.cn/2024/10/13/host-git-at-home/">在 NAS 上部署自己的 Gitea 服务,无需公网服务器</a> 方案打通外网到家用 NAS &#x2F; MacMini 的链路</li>
<li>采用 Git 仓库 main 分支存放源码 + Gitea Actions 编译至 publish 分支实现源码及制品存储</li>
<li>使用 <a target="_blank" rel="noopener" href="https://github.com/greenpau/caddy-git">caddy-git</a> 插件实现拉取 Git 仓库 publish 分支并作为 fileserver 由 Caddy 反向代理</li>
</ol>
<p>全方案的拓扑图如下</p>
<p>其中红色线条为 HTTP 流量,蓝色线条为 DDNS-GO 流量,紫色线条为本地或 v6 直连的 ssh TCP 流量</p>
<p><img src="/img/blog-from-scratch/blog_topology.png" alt="blog_topology"></p>
<hr>
<h2 id="环境准备"><a href="#环境准备" class="headerlink" title="环境准备"></a>环境准备</h2><p>在配博客之前,我是先配好了 Nas 上的 Gitea 服务,可以参考 <a href="https://blog.linloir.cn/2024/10/13/host-git-at-home/">在 NAS 上部署自己的 Gitea 服务,无需公网服务器</a> 这一篇博客来准备基本的网络环境和 Gitea 服务。</p>
<p>(也就是说,我是先搭好了 Gitea然后实在不知道能拿干点什么才决定把博客迁移回来的。有点为了醋包饺子的感觉哈哈不过现在博客全部内容都运行在自己本地感觉还是颇有成就感的)</p>
<h2 id="仓库配置"><a href="#仓库配置" class="headerlink" title="仓库配置"></a>仓库配置</h2><p>待后面补充~</p>
</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta"><i class="fas fa-circle-user fa-fw"></i>文章作者: </span><span class="post-copyright-info"><a href="https://blog.linloir.cn">Linloir</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta"><i class="fas fa-square-arrow-up-right fa-fw"></i>文章链接: </span><span class="post-copyright-info"><a href="https://blog.linloir.cn/2024/10/12/blog-from-scratch/">https://blog.linloir.cn/2024/10/12/blog-from-scratch/</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta"><i class="fas fa-circle-exclamation fa-fw"></i>版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外,均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来源 <a href="https://blog.linloir.cn" target="_blank">時痕</a></span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/tags/%E7%9E%8E%E6%8D%A3%E9%BC%93/">瞎捣鼓</a></div><div class="post-share"><div class="social-share" data-image="/img/cover.jpg" data-sites="facebook,twitter,wechat,weibo,qq"></div><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/css/share.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/js/social-share.min.js" defer></script></div></div><nav class="pagination-post" id="pagination"><a class="prev-post pull-left" href="/2024/10/13/host-git-at-home/" title="在 NAS 上部署自己的 Gitea 服务,无需公网服务器"><img class="cover" src="/img/cover.jpg" onerror="onerror=null;src='/img/404.jpg'" alt="cover of previous post"><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">在 NAS 上部署自己的 Gitea 服务,无需公网服务器</div></div></a><a class="next-post pull-right" href="/2024/10/11/reborn/" title="重生"><img class="cover" src="/img/cover.jpg" onerror="onerror=null;src='/img/404.jpg'" alt="cover of next post"><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">重生</div></div></a></nav><div class="relatedPosts"><div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span>相关推荐</span></div><div class="relatedPosts-list"><a href="/2024/10/13/host-git-at-home/" title="在 NAS 上部署自己的 Gitea 服务,无需公网服务器"><img class="cover" src="/img/cover.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2024-10-13</div><div class="title">在 NAS 上部署自己的 Gitea 服务,无需公网服务器</div></div></a></div></div></div><div class="aside-content" id="aside-content"><div class="card-widget card-info is-center"><div class="avatar-img"><img src="/img/avatar.png" onerror="this.onerror=null;this.src='/img/friend_404.gif'" alt="avatar"/></div><div class="author-info-name">Linloir</div><div class="author-info-description">我、技术、生活与值得分享的一切</div><div class="site-data"><a href="/archives/"><div class="headline">文章</div><div class="length-num">21</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">18</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">2</div></a></div><a id="card-info-btn" target="_blank" rel="noopener" href="https://github.com/xxxxxx"><i class="fab fa-github"></i><span>Follow Me</span></a><div class="card-info-social-icons"><a class="social-icon" href="https://github.com/Linloir" target="_blank" title="GitHub"><i class="fab fa-github"></i></a><a class="social-icon" href="mailto:jonathanzhang.st@gmail.com" target="_blank" title="Email"><i class="fas fa-envelope"></i></a></div></div><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span><span class="toc-percentage"></span></div><div class="toc-content"><ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%96%B9%E6%A1%88%E9%80%9F%E8%A7%88"><span class="toc-number">1.</span> <span class="toc-text">方案速览</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%8E%AF%E5%A2%83%E5%87%86%E5%A4%87"><span class="toc-number">2.</span> <span class="toc-text">环境准备</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E4%BB%93%E5%BA%93%E9%85%8D%E7%BD%AE"><span class="toc-number">3.</span> <span class="toc-text">仓库配置</span></a></li></ol></div></div><div class="card-widget card-recent-post"><div class="item-headline"><i class="fas fa-history"></i><span>最新文章</span></div><div class="aside-list"><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/2025/03/17/coroutine-llm-qa/" title="Coroutine 相关疑惑大模型问答记录">Coroutine 相关疑惑大模型问答记录</a><time datetime="2025-03-17T13:09:21.000Z" title="发表于 2025-03-17 21:09:21">2025-03-17</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/2024/12/31/summary-2023-2024/" title="年终总结 - 2023 至 2024">年终总结 - 2023 至 2024</a><time datetime="2024-12-31T17:15:49.000Z" title="发表于 2025-01-01 01:15:49">2025-01-01</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/2024/11/22/debug-windows-socket-drain/" title="问题定位回顾 - Windows 上发起 tcp 连接时提示 Only one usage of each socket address (protocol/network address/port) is normally permitted">问题定位回顾 - Windows 上发起 tcp 连接时提示 Only one usage of each socket address (protocol/network address/port) is normally permitted</a><time datetime="2024-11-22T03:52:37.000Z" title="发表于 2024-11-22 11:52:37">2024-11-22</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/2024/11/19/listary-quick-clone-command/" title="Listary 命令分享 - 快捷 clone 仓库并使用 VSCode 打开">Listary 命令分享 - 快捷 clone 仓库并使用 VSCode 打开</a><time datetime="2024-11-19T06:08:32.000Z" title="发表于 2024-11-19 14:08:32">2024-11-19</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/2024/11/08/linux-commands/" title="长更 - Linux 常用指令">长更 - Linux 常用指令</a><time datetime="2024-11-08T11:07:08.000Z" title="发表于 2024-11-08 19:07:08">2024-11-08</time></div></div></div></div></div></div></main><footer id="footer" style="background: transparent;"><div id="footer-wrap"><div class="copyright">&copy;2022 - 2025 By Linloir</div><div class="framework-info"><span>框架 </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div><div class="footer_custom_text">Wirtten with Love ❤</div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="translateLink" type="button" title="简繁转换"></button><button id="darkmode" type="button" title="日间和夜间模式切换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside-config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><button id="go-up" type="button" title="回到顶部"><span class="scroll-percent"></span><i class="fas fa-arrow-up"></i></button></div></div><div><script src="/js/utils.js"></script><script src="/js/main.js"></script><script src="/js/tw_cn.js"></script><script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox/fancybox.umd.min.js"></script><script src="https://cdn.jsdelivr.net/npm/instant.page/instantpage.min.js" type="module"></script><script src="https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.js"></script><script>(() => {
const panguFn = () => {
if (typeof pangu === 'object') pangu.autoSpacingPage()
else {
btf.getScript('https://cdn.jsdelivr.net/npm/pangu/dist/browser/pangu.min.js')
.then(() => {
pangu.autoSpacingPage()
})
}
}
const panguInit = () => {
if (true){
GLOBAL_CONFIG_SITE.isPost && panguFn()
} else {
panguFn()
}
}
btf.addGlobalFn('pjaxComplete', panguInit, 'pangu')
document.addEventListener('DOMContentLoaded', panguInit)
})()</script><div class="js-pjax"><script>(async () => {
const showKatex = () => {
document.querySelectorAll('#article-container .katex').forEach(el => el.classList.add('katex-show'))
}
if (!window.katex_js_css) {
window.katex_js_css = true
await btf.getCSS('https://cdn.jsdelivr.net/npm/katex/dist/katex.min.css')
if (false) {
await btf.getScript('https://cdn.jsdelivr.net/npm/katex/dist/contrib/copy-tex.min.js')
}
}
showKatex()
})()</script><script>(() => {
const runMermaid = ele => {
window.loadMermaid = true
const theme = document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'default'
ele.forEach((item, index) => {
const mermaidSrc = item.firstElementChild
const mermaidThemeConfig = `%%{init:{ 'theme':'${theme}'}}%%\n`
const mermaidID = `mermaid-${index}`
const mermaidDefinition = mermaidThemeConfig + mermaidSrc.textContent
const renderFn = mermaid.render(mermaidID, mermaidDefinition)
const renderMermaid = svg => {
mermaidSrc.insertAdjacentHTML('afterend', svg)
}
// 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 (true) 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/dist/mermaid.min.js').then(runMermaidFn)
}
btf.addGlobalFn('encrypt', loadMermaid, 'mermaid')
window.pjax ? loadMermaid() : document.addEventListener('DOMContentLoaded', loadMermaid)
})()</script></div><script id="canvas_nest" defer="defer" color="165,165,165" opacity="0.8" zIndex="-1" count="99" mobile="false" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/dist/canvas-nest.min.js"></script><script src="https://cdn.jsdelivr.net/npm/pjax/pjax.min.js"></script><script>(() => {
const pjaxSelectors = ["head > title","#config-diff","#body-wrap","#rightside-config-hide","#rightside-config-show",".js-pjax"]
window.pjax = new Pjax({
elements: 'a:not([target="_blank"])',
selectors: pjaxSelectors,
cacheBust: false,
analytics: false,
scrollRestoration: false
})
const triggerPjaxFn = (val) => {
if (!val) return
Object.values(val).forEach(fn => fn())
}
document.addEventListener('pjax:send', () => {
// removeEventListener
btf.removeGlobalFnEvent('pjaxSendOnce')
btf.removeGlobalFnEvent('themeChange')
// reset readmode
const $bodyClassList = document.body.classList
if ($bodyClassList.contains('read-mode')) $bodyClassList.remove('read-mode')
triggerPjaxFn(window.globalFn.pjaxSend)
})
document.addEventListener('pjax:complete', () => {
btf.removeGlobalFnEvent('pjaxCompleteOnce')
document.querySelectorAll('script[data-pjax]').forEach(item => {
const newScript = document.createElement('script')
const content = item.text || item.textContent || item.innerHTML || ""
Array.from(item.attributes).forEach(attr => newScript.setAttribute(attr.name, attr.value))
newScript.appendChild(document.createTextNode(content))
item.parentNode.replaceChild(newScript, item)
})
triggerPjaxFn(window.globalFn.pjaxComplete)
})
document.addEventListener('pjax:error', e => {
if (e.request.status === 404) {
pjax.loadUrl('/404')
}
})
})()</script><script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script></div></body></html>