<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Nginx on aiken&#39;s blog</title>
    <link>https://aikenh.cn/tags/nginx/</link>
    <description>Recent content in Nginx on aiken&#39;s blog</description>
    <generator>Hugo -- 0.137.0</generator>
    <language>en-us</language>
    <lastBuildDate>Sun, 30 Mar 2025 14:16:22 +0000</lastBuildDate>
    <follow_challenge>
      <feedId>90080597450814464</feedId>
      <userId>72439538841050112</userId>
    </follow_challenge>
    <atom:link href="https://aikenh.cn/tags/nginx/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>使用 cloudflare 转发 ipv6 网页至 ipv4</title>
      <link>https://aikenh.cn/posts/%E4%BD%BF%E7%94%A8cloudflare%E8%BD%AC%E5%8F%91ipv6%E7%BD%91%E9%A1%B5%E8%87%B3ipv4/</link>
      <pubDate>Sun, 30 Mar 2025 14:16:22 +0000</pubDate>
      <guid>https://aikenh.cn/posts/%E4%BD%BF%E7%94%A8cloudflare%E8%BD%AC%E5%8F%91ipv6%E7%BD%91%E9%A1%B5%E8%87%B3ipv4/</guid>
      <description>Descript for artical</description>
      <content:encoded><![CDATA[<h2 id="intro-起因为-follow-添加小红书全文获取">Intro 起因：为 follow 添加小红书全文获取</h2>
<p>事情的起因是为了部署 Rsshub 服务为自己的 folo 提供小红书的全文解析，而 folo 是不支持本地/ipv6 的 rsshub ，因此需要将对应设置好小红书 cookie 的 rsshub 服务部署到 ipv4 的公网环境之下，权衡之下采取了该方式进行。</p>
<p>以下简单介绍一下自己的尝试过程：</p>
<ul>
<li>首先尝试利用 vercel 部署：由于部署 <a href="https://docs.rsshub.app/zh/deploy/" target="_blank" rel="noopener">rsshub</a>
 到 vercel 的方式目前还没修复，如果<a href="https://github.com/DIYgod/RSSHub/issues/14622" target="_blank" rel="noopener">使用 legacy 分支部署</a>
，也无法支持小红书的全文获取的需求，对应的环境变量可以直接在 github  rsshub 仓库的根目录直接添加 <code>.env</code> 文件即可。</li>
<li>其余的部署方式部分是需要收费的，或者说注册起来比较麻烦就没有再去尝试；</li>
</ul>
<p>本地尝试全文获取主要感谢以下作者的探索和分享，这里不再重复分享，通过添加 Cookie 的方式获取了小红书的 全文 Rss 源：</p>
<ul>
<li><a href="https://blog.gujiakai.top/2024/09/xiaohongshu-rss-tips/" target="_blank" rel="noopener">顾佳凯的网络日志 | 小红书RSS的解决方案</a>
</li>
</ul>
<p>但是由于小红书反扒严重，因此使用该方案还是要小心被封或者稳定性等问题，故而暂时没有将该 rsshub 源公开出来。</p>
<p>rsshub 的其余部署细节和配置可以参考：<a href="https://docs.rsshub.app/zh/" target="_blank" rel="noopener">RSSHub</a>
 官网的文档，这里不在赘述，</p>
<h2 id="转换-ipv6-服务到-ipv4-环境">转换 ipv6 服务到 ipv4 环境</h2>
<p>我们也知道获取公网的 ipv6 地址是比较容易的，通常普通家庭或者宽带都不会有公网的 ipv4 ，在此情况下 ipv6 的访问支持也并没有十分普及，在一些公共网络环境或者一些公司网络中，很多部署在 ipv6 环境下的网页实际上是没法被访问到的，而通过流量访问，还是有其局限性（流量收费限制）；</p>
<p>因此这里就尝试通过免费的 CloudFlare 的 CDN 服务来将 ipv6 服务代理到 ipv4 环境中，以下是一些相关的资料：</p>
<ul>
<li><a href="https://www.ztianzeng.com/posts/Cloudflare%E8%AE%A9IPV6%E4%B8%8D%E5%9C%A8%E9%B8%A1%E8%82%8B/" target="_blank" rel="noopener">Cloudflare让IPV6不在鸡肋 | 天增的博客</a>
</li>
<li><a href="https://post.smzdm.com/p/avxzg2gm/" target="_blank" rel="noopener">利用cloudflare让ipv4与ipv6互通_NAS存储_什么值得买</a>
</li>
<li><a href="https://www.bilibili.com/opus/969223623124451365" target="_blank" rel="noopener">使用Cloudflare代理为纯IPv6站点添加IPv4访问 - 哔哩哔哩</a>
</li>
</ul>
<h3 id="转换腾讯云解析到-cloudflare">转换腾讯云解析到 Cloudflare</h3>
<blockquote>
<p>使用 CF 的 CDN 的方式的先决条件是已经有了自己的域名，同时已经通过/可以通过公网 ipv6 发布自己的服务</p>
</blockquote>
<p>Cloudflare 官网： <a href="https://dash.cloudflare.com/" target="_blank" rel="noopener">https://dash.cloudflare.com/</a>
</p>
<p>主要参考<a href="https://blog.csdn.net/qq_38894585/article/details/131054885" target="_blank" rel="noopener">开启 Cloudflare CDN 代理，实现 IPv4 to IPv6 转换_ipv4转发ipv6-CSDN博客</a>
 一文，在已经基于 ipv6 部署了服务的情况下，参考该博主的文章将域名的 DNS 解析服务转换到 CF 即可，但是后续可能会遇到访问服务的时候反复重定向的问题，这里可以参考[TroubleShooting]( ### CloudFlare 网站【重定向次数过多】) 部分的解决方案；</p>
<p>P.S. 备注</p>
<ul>
<li>后续还是可以在腾讯云的控制台对子域名申请免费证书，对应的解析在 CF 中进行即可，</li>
<li>后续对应子域名的添加可以借助 DDNS-GO 直接更新到 CF 中即可</li>
<li>CF 对于服务的端口是有要求的，笔者本地是通过 Nginx 进行反向代理各项服务的，因此只需要处理好域名的对应关系即可。</li>
<li>通过这种方式，可以使用一个备用域名来将自己的一些服务部署到 ipv4 上。</li>
</ul>
<h2 id="troubleshooting-一些问题解决">Troubleshooting 一些问题解决</h2>
<h3 id="为-cloudflare-配置-ddns-go-动态更新-ipv6-地址">为 CloudFlare 配置 DDNS-Go 动态更新 ipv6 地址</h3>
<p>这里 DDNS-Go 的配置没有什么特别的，主要就是要获取 CloudFlare 的密钥，填入对应的配置中即可，具体的密钥获取位置如下：</p>
<p>首先进入 Profile 页面：</p>
<p>
<div class="post-img-view">
  <a data-fancybox="gallery" href="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20250331001823.png">
    <img alt="image.png" loading="lazy" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20250331001823.png"class="responsive-image" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20250331001823.png" style="display: block; margin: 0 auto;"
      alt="image.png"  />
  </a>
</div>


<script>
  document.addEventListener("DOMContentLoaded", function() {
      var images = document.querySelectorAll(".responsive-image");
      var maxHeight = window.innerHeight / 2.5;
      images.forEach(function(image) {
          image.style.maxHeight = maxHeight + "px";
      });
  });
</script>
</p>
<p>选择 API Tokens 中创建一个新的 Token</p>
<p>
<div class="post-img-view">
  <a data-fancybox="gallery" href="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20250331001907.png">
    <img alt="image.png" loading="lazy" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20250331001907.png"class="responsive-image" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20250331001907.png" style="display: block; margin: 0 auto;"
      alt="image.png"  />
  </a>
</div>


<script>
  document.addEventListener("DOMContentLoaded", function() {
      var images = document.querySelectorAll(".responsive-image");
      var maxHeight = window.innerHeight / 2.5;
      images.forEach(function(image) {
          image.style.maxHeight = maxHeight + "px";
      });
  });
</script>
</p>
<p>选择 Edit zone DNS 模板后设置自己要操作的域名如下，最后点击确定即可获取需要的 Token</p>
<p>
<div class="post-img-view">
  <a data-fancybox="gallery" href="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20250331002118.png">
    <img alt="image.png" loading="lazy" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20250331002118.png"class="responsive-image" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20250331002118.png" style="display: block; margin: 0 auto;"
      alt="image.png"  />
  </a>
</div>


<script>
  document.addEventListener("DOMContentLoaded", function() {
      var images = document.querySelectorAll(".responsive-image");
      var maxHeight = window.innerHeight / 2.5;
      images.forEach(function(image) {
          image.style.maxHeight = maxHeight + "px";
      });
  });
</script>
</p>
<p>DDNS-Go 的话只需要新增一个 Cloudflare 的配置，填入对应的 token 后，在 Ipv6 的部分添加你要跟踪的子域名即可，如果域名不存在 Cloudflare 中，其会自动完成对应记录的添加：</p>
<p>
<div class="post-img-view">
  <a data-fancybox="gallery" href="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20250331002310.png">
    <img alt="image.png" loading="lazy" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20250331002310.png"class="responsive-image" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20250331002310.png" style="display: block; margin: 0 auto;"
      alt="image.png"  />
  </a>
</div>


<script>
  document.addEventListener("DOMContentLoaded", function() {
      var images = document.querySelectorAll(".responsive-image");
      var maxHeight = window.innerHeight / 2.5;
      images.forEach(function(image) {
          image.style.maxHeight = maxHeight + "px";
      });
  });
</script>
</p>
<p><strong>Ref</strong>: <a href="https://zhuanlan.zhihu.com/p/581967733" target="_blank" rel="noopener">3、使用DDNS-GO配置DDNS，通过cloudflare api修改指定域名对应的动态ip - 知乎</a>
</p>
<h3 id="cloudflare-网站重定向次数过多">CloudFlare 网站【重定向次数过多】</h3>
<p>通常都是由于 SSL/TLS 的设置冲突导致的，这里只需要去 CloudFlare 管理面板的 SSL/TLS 菜单中将安全策略改为 Full（Strict）即可解决。</p>
<p>具体操作如下图：</p>
<p>
<div class="post-img-view">
  <a data-fancybox="gallery" href="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20250331001459.png">
    <img alt="image.png" loading="lazy" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20250331001459.png"class="responsive-image" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20250331001459.png" style="display: block; margin: 0 auto;"
      alt="image.png"  />
  </a>
</div>


<script>
  document.addEventListener("DOMContentLoaded", function() {
      var images = document.querySelectorAll(".responsive-image");
      var maxHeight = window.innerHeight / 2.5;
      images.forEach(function(image) {
          image.style.maxHeight = maxHeight + "px";
      });
  });
</script>
</p>
<p>
<div class="post-img-view">
  <a data-fancybox="gallery" href="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20250331001546.png">
    <img alt="image.png" loading="lazy" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20250331001546.png"class="responsive-image" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20250331001546.png" style="display: block; margin: 0 auto;"
      alt="image.png"  />
  </a>
</div>


<script>
  document.addEventListener("DOMContentLoaded", function() {
      var images = document.querySelectorAll(".responsive-image");
      var maxHeight = window.innerHeight / 2.5;
      images.forEach(function(image) {
          image.style.maxHeight = maxHeight + "px";
      });
  });
</script>
</p>
<p>ref：<a href="https://blog.csdn.net/qq_37126941/article/details/122540553" target="_blank" rel="noopener">使用 CloudFlare 导致网站「重定向的次数过多」解决方案_wordpess 使用 cloudfare 重定向次数-CSDN博客</a>
</p>
<h2 id="fi">Fi</h2>
]]></content:encoded>
    </item>
    <item>
      <title>使用Flask和Nginx为StableDiffusion添加登录鉴权</title>
      <link>https://aikenh.cn/posts/stablediffusionwebui%E9%89%B4%E6%9D%83%E8%AE%BE%E8%AE%A1/</link>
      <pubDate>Sat, 27 Jan 2024 15:42:36 +0000</pubDate>
      <guid>https://aikenh.cn/posts/stablediffusionwebui%E9%89%B4%E6%9D%83%E8%AE%BE%E8%AE%A1/</guid>
      <description>Secure and Personalize Your Web Services: A Guide to Nginx Authentication and Engaging Frontend Design</description>
      <content:encoded><![CDATA[<p>[&gt; [!summary]+</p>
<blockquote>
<p>this article purpose is to build an authority page for stable diffusion webui using nginx &amp; python/js. Which can publish my personal stable diffusion server. Wrote by GPT(try).</p>
</blockquote>
<p>
<div class="post-img-view">
  <a data-fancybox="gallery" href="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20240127161828.png">
    <img alt="image.png" loading="lazy" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20240127161828.png"class="responsive-image" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20240127161828.png" style="display: block; margin: 0 auto;"
      alt="image.png"  />
  </a>
</div>


<script>
  document.addEventListener("DOMContentLoaded", function() {
      var images = document.querySelectorAll(".responsive-image");
      var maxHeight = window.innerHeight / 2.5;
      images.forEach(function(image) {
          image.style.maxHeight = maxHeight + "px";
      });
  });
</script>
</p>
<h3 id="introduction">Introduction</h3>
<p>In the digital age, the security and user-friendliness of web services are not just conveniences; they are necessities. Balancing robust security protocols with an engaging user experience is key to maintaining both the integrity and popularity of any online service. This blog post dives into the intricacies of securing web services using Nginx for authentication, coupled with designing an appealing frontend. Our journey begins with a practical scenario:</p>
<p><strong>publishing a stable diffusion webUI service, accessible only to an authenticated audience.</strong></p>
<h3 id="setting-up-nginx-for-secure-authentication">Setting Up Nginx for Secure Authentication</h3>
<p>Nginx excels in serving web pages and as a reverse proxy, providing enhanced security through authentication mechanisms. Let’s explore a typical Nginx configuration for secure authentication:</p>
<ul>
<li><strong>/verify_token</strong>: This block forwards authentication requests to a dedicated server. By excluding the request body and focusing on essential headers, it ensures that only valid, authenticated requests proceed.</li>
</ul>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>conf</span>
    </div>
    <div class="code-content">
        
        <pre tabindex="0"><code class="language-conf" data-lang="conf">location = /verify_token {
    proxy_pass http://{your_auth_server}:2424;
    proxy_pass_request_body off;
    proxy_set_header Content-Length &#34;&#34;;
    proxy_set_header X-Original-URI $request_uri;
    proxy_set_header X-Original-Remote-Addr $remote_addr;
    proxy_set_header X-Original-Host $host;
}</code></pre>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><ul>
<li><strong>/login</strong>: Catering to login requests, this configuration forwards the necessary details to the authentication server, preserving crucial information about the request&rsquo;s origin.</li>
</ul>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>conf</span>
    </div>
    <div class="code-content">
        
        <pre tabindex="0"><code class="language-conf" data-lang="conf">location /login {
    proxy_pass http://{your_auth_server}:2424;
    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;
}</code></pre>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><ul>
<li><strong>Error Handling (@error401)</strong>: A clever redirect mechanism that guides unauthenticated users to the login page, keeping the original URL intact.</li>
</ul>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>conf</span>
    </div>
    <div class="code-content">
        
        <pre tabindex="0"><code class="language-conf" data-lang="conf">location @error401 {
    return 302 {your_domain}/login;
}</code></pre>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><ul>
<li><strong>Root Location (/)</strong>: The gateway to your service, which rigorously checks each request for authentication, granting access only to verified users.</li>
</ul>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>conf</span>
    </div>
    <div class="code-content">
        
        <pre tabindex="0"><code class="language-conf" data-lang="conf">location / {
    auth_request /verify_token;
    error_page 401 = @error401;
    proxy_pass http://{your_server}:2323/;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection &#39;upgrade&#39;;
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
}</code></pre>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><p>This setup not only fortifies your service against unauthorized access but also maintains a seamless user experience, redirecting unauthenticated users without hassle.</p>
<h3 id="implementing-jwt-for-robust-backend-authentication">Implementing JWT for Robust Backend Authentication</h3>
<p>Implementing JWT (JSON Web Tokens) in a Flask web application provides a secure way of handling authentication. The provided Flask code demonstrates how JWT can be integrated for a robust backend authentication system:</p>
<ol>
<li><strong>Setup and Environment Variables</strong>:
<ul>
<li>The Flask app is configured with a secret key, essential for JWT encoding and decoding.</li>
</ul>
</li>
</ol>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>python</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">os</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">dotenv</span> <span class="kn">import</span> <span class="n">load_dotenv</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">load_dotenv</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="vm">__name__</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">app</span><span class="o">.</span><span class="n">secret_key</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s1">&#39;SECRET_KEY&#39;</span><span class="p">)</span></span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><ol>
<li><strong>Login Route</strong>:
<ul>
<li>The <code>/login</code> route handles user authentication. Upon successful login, a JWT is encoded with the user&rsquo;s information and expiration time, then sent as a cookie.</li>
</ul>
</li>
</ol>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>python</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="nd">@app.route</span><span class="p">(</span><span class="s1">&#39;/login&#39;</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s1">&#39;POST&#39;</span><span class="p">,</span> <span class="s1">&#39;GET&#39;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">login</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">count</span> <span class="o">=</span> <span class="n">update_visit_count</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s1">&#39;POST&#39;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">username</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">form</span><span class="p">[</span><span class="s1">&#39;username&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">        <span class="n">password</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">form</span><span class="p">[</span><span class="s1">&#39;password&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1"># Validate credentials (use a secure method in production)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">username</span> <span class="o">==</span> <span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s1">&#39;chat_username&#39;</span><span class="p">)</span> <span class="ow">and</span> <span class="n">password</span> <span class="o">==</span> <span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s1">&#39;password&#39;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="n">token</span> <span class="o">=</span> <span class="n">jwt</span><span class="o">.</span><span class="n">encode</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">                <span class="s1">&#39;user&#39;</span><span class="p">:</span> <span class="n">username</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="s1">&#39;exp&#39;</span><span class="p">:</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">utcnow</span><span class="p">()</span> <span class="o">+</span> <span class="n">datetime</span><span class="o">.</span><span class="n">timedelta</span><span class="p">(</span><span class="n">minutes</span><span class="o">=</span><span class="mi">30</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span> <span class="n">app</span><span class="o">.</span><span class="n">secret_key</span><span class="p">,</span> <span class="n">algorithm</span><span class="o">=</span><span class="s1">&#39;HS256&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="n">response</span> <span class="o">=</span> <span class="n">make_response</span><span class="p">(</span><span class="n">redirect</span><span class="p">(</span><span class="s1">&#39;/&#39;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">            <span class="n">response</span><span class="o">.</span><span class="n">set_cookie</span><span class="p">(</span><span class="s1">&#39;token&#39;</span><span class="p">,</span> <span class="n">token</span><span class="p">,</span> <span class="n">max_age</span><span class="o">=</span><span class="mi">1800</span><span class="p">)</span>  <span class="c1"># Keep login status for 30 mins</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">response</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="n">error</span> <span class="o">=</span> <span class="s1">&#39;Invalid credentials&#39;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s1">&#39;login.html&#39;</span><span class="p">,</span> <span class="n">error</span><span class="o">=</span><span class="n">error</span><span class="p">,</span> <span class="n">count</span><span class="o">=</span><span class="n">count</span><span class="p">),</span> <span class="mi">401</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1"># Show login page</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s1">&#39;login.html&#39;</span><span class="p">,</span><span class="n">count</span><span class="o">=</span><span class="n">count</span><span class="p">)</span></span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><ol>
<li><strong>Token Verification</strong>:
<ul>
<li>A decorator <code>token_required</code> is used to verify the JWT token in subsequent requests. This ensures that only authenticated users can access certain routes.</li>
</ul>
</li>
</ol>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>python</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">token_required</span><span class="p">(</span><span class="n">f</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">decorator</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">token</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">cookies</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;token&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="n">token</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;token is none&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">redirect</span><span class="p">(</span><span class="s1">&#39;/login&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">jwt</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="n">token</span><span class="p">,</span> <span class="n">app</span><span class="o">.</span><span class="n">secret_key</span><span class="p">,</span> <span class="n">algorithms</span><span class="o">=</span><span class="p">[</span><span class="s1">&#39;HS256&#39;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">        <span class="k">except</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;token is invalid&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">redirect</span><span class="p">(</span><span class="s1">&#39;/login&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">f</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">decorator</span><span class="o">.</span><span class="vm">__name__</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="vm">__name__</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">decorator</span></span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><ol>
<li><strong>Token Validation Endpoint</strong>:
<ul>
<li>The <code>/verify_token</code> endpoint checks the validity of the token, which is essential for integrating with the Nginx authentication mechanism.</li>
</ul>
</li>
</ol>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>python</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="nd">@app.route</span><span class="p">(</span><span class="s1">&#39;/verify_token&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">verify_token</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">token</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">cookies</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;token&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="ow">not</span> <span class="n">token</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s1">&#39;Access Denied&#39;</span><span class="p">,</span> <span class="mi">401</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">jwt</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="n">token</span><span class="p">,</span> <span class="n">app</span><span class="o">.</span><span class="n">secret_key</span><span class="p">,</span> <span class="n">algorithms</span><span class="o">=</span><span class="p">[</span><span class="s1">&#39;HS256&#39;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s1">&#39;Authorized&#39;</span><span class="p">,</span> <span class="mi">200</span>
</span></span><span class="line"><span class="cl">    <span class="k">except</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s1">&#39;Access Denied&#39;</span><span class="p">,</span> <span class="mi">401</span></span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><ol>
<li><strong>Dynamic Secret Key Update</strong>:
<ul>
<li>A function <code>update_secret_key_in_env</code> is included to update the secret key daily, enhancing security by invalidating old tokens.</li>
</ul>
</li>
</ol>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>python</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># Function to generate a new secret key and write it to the .env file</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">update_secret_key_in_env</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">env_file_path</span> <span class="o">=</span> <span class="s1">&#39;.env&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">secret_key</span> <span class="o">=</span> <span class="sa">f</span><span class="s1">&#39;SECRET_KEY=</span><span class="si">{</span><span class="n">secrets</span><span class="o">.</span><span class="n">token_urlsafe</span><span class="p">(</span><span class="mi">32</span><span class="p">)</span><span class="si">}</span><span class="s1">&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">last_updated</span> <span class="o">=</span> <span class="sa">f</span><span class="s1">&#39;LAST_UPDATED=</span><span class="si">{</span><span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">&#34;%Y-%m-</span><span class="si">%d</span><span class="s2">&#34;</span><span class="p">)</span><span class="si">}</span><span class="s1">&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">key_updated</span> <span class="o">=</span> <span class="kc">False</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Check if .env file exists and read the content</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">exists</span><span class="p">(</span><span class="n">env_file_path</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">env_file_path</span><span class="p">,</span> <span class="s1">&#39;r&#39;</span><span class="p">)</span> <span class="k">as</span> <span class="n">file</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">lines</span> <span class="o">=</span> <span class="n">file</span><span class="o">.</span><span class="n">readlines</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1"># Update or add SECRET_KEY and LAST_UPDATED</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">line</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">lines</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="s1">&#39;LAST_UPDATED&#39;</span> <span class="ow">in</span> <span class="n">line</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">last_updated_date</span> <span class="o">=</span> <span class="n">line</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">&#39;=&#39;</span><span class="p">)[</span><span class="mi">1</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="n">last_updated_date</span> <span class="o">!=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">&#34;%Y-%m-</span><span class="si">%d</span><span class="s2">&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">                    <span class="n">lines</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">last_updated</span> <span class="o">+</span> <span class="s1">&#39;</span><span class="se">\n</span><span class="s1">&#39;</span>
</span></span><span class="line"><span class="cl">                    <span class="n">key_updated</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">            <span class="k">elif</span> <span class="s1">&#39;SECRET_KEY&#39;</span> <span class="ow">in</span> <span class="n">line</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="n">key_updated</span> <span class="ow">is</span> <span class="kc">True</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                    <span class="n">lines</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">secret_key</span> <span class="o">+</span> <span class="s1">&#39;</span><span class="se">\n</span><span class="s1">&#39;</span>
</span></span><span class="line"><span class="cl">                <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;no need to update the secret key today&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="n">key_updated</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1"># If SECRET_KEY or LAST_UPDATED are not found, add them</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="n">key_updated</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">lines</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">last_updated</span> <span class="o">+</span> <span class="s1">&#39;</span><span class="se">\n</span><span class="s1">&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">lines</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">secret_key</span> <span class="o">+</span> <span class="s1">&#39;</span><span class="se">\n</span><span class="s1">&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">key_updated</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1"># Write back to .env file</span>
</span></span><span class="line"><span class="cl">        <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">env_file_path</span><span class="p">,</span> <span class="s1">&#39;w&#39;</span><span class="p">)</span> <span class="k">as</span> <span class="n">file</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">file</span><span class="o">.</span><span class="n">writelines</span><span class="p">(</span><span class="n">lines</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">else</span><span class="p">:</span> <span class="c1"># If .env file doesn&#39;t exist, create one</span>
</span></span><span class="line"><span class="cl">        <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">env_file_path</span><span class="p">,</span> <span class="s1">&#39;w&#39;</span><span class="p">)</span> <span class="k">as</span> <span class="n">file</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">file</span><span class="o">.</span><span class="n">writelines</span><span class="p">([</span><span class="n">last_updated</span> <span class="o">+</span> <span class="s1">&#39;</span><span class="se">\n</span><span class="s1">&#39;</span><span class="p">,</span> <span class="n">secret_key</span> <span class="o">+</span> <span class="s1">&#39;</span><span class="se">\n</span><span class="s1">&#39;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">        <span class="n">key_updated</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">key_updated</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Secret key and last updated date added/updated in .env file.&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;No update required for today.&#34;</span><span class="p">)</span></span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><ol>
<li><strong>Visitor Count Feature</strong>:
<ul>
<li>As part of the user experience, a visitor count is maintained, which is updated with each visit.</li>
</ul>
</li>
</ol>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>python</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">update_visit_count</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s2">&#34;visit_count.txt&#34;</span><span class="p">,</span> <span class="s2">&#34;r&#34;</span><span class="p">)</span> <span class="k">as</span> <span class="n">file</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">count</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">file</span><span class="o">.</span><span class="n">read</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">        <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s2">&#34;visit_count.txt&#34;</span><span class="p">,</span> <span class="s2">&#34;w&#34;</span><span class="p">)</span> <span class="k">as</span> <span class="n">file</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">            <span class="n">file</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">count</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">count</span>
</span></span><span class="line"><span class="cl">    <span class="k">except</span> <span class="ne">IOError</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s2">&#34;Error&#34;</span></span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><p>This implementation of JWT in a Flask application exemplifies a secure and efficient way of handling user authentication, ensuring that only authorized users can access protected services.</p>
<p>
<div class="post-img-view">
  <a data-fancybox="gallery" href="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20240127161720.png">
    <img alt="image.png" loading="lazy" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20240127161720.png"class="responsive-image" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20240127161720.png" style="display: block; margin: 0 auto;"
      alt="image.png"  />
  </a>
</div>


<script>
  document.addEventListener("DOMContentLoaded", function() {
      var images = document.querySelectorAll(".responsive-image");
      var maxHeight = window.innerHeight / 2.5;
      images.forEach(function(image) {
          image.style.maxHeight = maxHeight + "px";
      });
  });
</script>
</p>
<h3 id="designing-a-user-friendly-frontend">Designing a User-Friendly Frontend</h3>
<p>In tandem with robust backend security, the frontend of your web service plays a crucial role in user engagement and satisfaction. Let&rsquo;s delve into the key features of our project&rsquo;s frontend design:</p>
<p><strong>Theme Toggle and Slogan</strong>:</p>
<ul>
<li>The theme switcher allows users to choose between dark and light modes.</li>
</ul>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>html</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;theme-switch-wrapper&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">button</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;theme-toggle&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;theme-toggle&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;moon&#34;</span><span class="p">&gt;</span><span class="ni">&amp;#9790;</span><span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;sun&#34;</span><span class="p">&gt;</span><span class="ni">&amp;#9728;</span><span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span></span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><ul>
<li>A catchy slogan is displayed, setting a creative and visionary tone.</li>
</ul>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>html</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;slogan&#34;</span><span class="p">&gt;</span>Crafting Tomorrow&#39;s Narratives, Today.<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span></span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><p><strong>Login Interface</strong>:
The entry point of any web service, the login page, is where security meets user experience. Our Flask-based web application presents a clean and intuitive login interface. It includes fields for username and password and a login button. This simplicity ensures ease of use while maintaining a professional appearance.</p>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>html</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">form</span> <span class="na">action</span><span class="o">=</span><span class="s">&#34;/login&#34;</span> <span class="na">method</span><span class="o">=</span><span class="s">&#34;post&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;email&#34;</span> <span class="na">placeholder</span><span class="o">=</span><span class="s">&#34;Username&#34;</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;username&#34;</span> <span class="na">required</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;password&#34;</span> <span class="na">placeholder</span><span class="o">=</span><span class="s">&#34;Password&#34;</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;password&#34;</span> <span class="na">required</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">button</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;submit&#34;</span><span class="p">&gt;</span>Login<span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">form</span><span class="p">&gt;</span></span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><ul>
<li>Additional subtitle for contextual information.</li>
</ul>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>html</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">p</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;subtitle&#34;</span><span class="p">&gt;</span>If you want to reach this server, please contact Aiken or Metis.<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span></span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><p><strong>Interactive Error Handling</strong>:</p>
<ul>
<li>A unique error message display using a cat image, adding a touch of humor.</li>
</ul>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>html</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">{% if error %}
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;error-cat&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">img</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;{{ url_for(&#39;static&#39;, filename=&#39;images/cat.webp&#39;) }}&#34;</span> <span class="na">alt</span><span class="o">=</span><span class="s">&#34;Cat&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;speech-bubble&#34;</span><span class="p">&gt;</span>{{ error }}<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">{% endif %}</span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><p><strong>Footer with Visitor Count</strong>:</p>
<ul>
<li>Displaying the visitor count adds an interactive and transparent element.</li>
</ul>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>html</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">footer</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    This page has been visited {{ count }} times.
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">footer</span><span class="p">&gt;</span></span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><p>
<div class="post-img-view">
  <a data-fancybox="gallery" href="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20240127161058.png">
    <img alt="image.png" loading="lazy" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20240127161058.png"class="responsive-image" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20240127161058.png" style="display: block; margin: 0 auto;"
      alt="image.png"  />
  </a>
</div>


<script>
  document.addEventListener("DOMContentLoaded", function() {
      var images = document.querySelectorAll(".responsive-image");
      var maxHeight = window.innerHeight / 2.5;
      images.forEach(function(image) {
          image.style.maxHeight = maxHeight + "px";
      });
  });
</script>
</p>
<h3 id="comparing-authentication-methods-jwt-oauth-and-basic-authentication">Comparing Authentication Methods: JWT, OAuth, and Basic Authentication</h3>
<p>In the realm of web security, selecting the right authentication method is crucial. Let&rsquo;s compare JWT (JSON Web Tokens), OAuth, and Basic Authentication to understand their unique features and use cases:</p>
<ol>
<li>
<p><strong>JWT (JSON Web Tokens)</strong>:</p>
<ul>
<li><strong>Mechanism</strong>: Encoded tokens that carry claims and are used for secure data exchange.</li>
<li><strong>Use Cases</strong>: Ideal for single sign-on (SSO) and stateless applications.</li>
<li><strong>Pros</strong>: Highly flexible, supports cross-domain requests, and is self-contained with payload data.</li>
<li><strong>Cons</strong>: Requires careful management of secret keys and token expiration.</li>
</ul>
</li>
<li>
<p><strong>OAuth</strong>:</p>
<ul>
<li><strong>Mechanism</strong>: An authorization framework allowing applications to secure designated access without revealing user credentials.</li>
<li><strong>Use Cases</strong>: Best for third-party access (like social logins) and granting limited access to user data.</li>
<li><strong>Pros</strong>: Enhanced security as user credentials are not exposed, and allows token-based access control.</li>
<li><strong>Cons</strong>: Complex implementation and requires understanding of tokens and scopes.</li>
</ul>
</li>
<li>
<p><strong>Basic Authentication</strong>:</p>
<ul>
<li><strong>Mechanism</strong>: A simple authentication scheme built into the HTTP protocol using username and password.</li>
<li><strong>Use Cases</strong>: Suitable for simple login needs, especially when accessing APIs for internal use.</li>
<li><strong>Pros</strong>: Easy to implement and understand.</li>
<li><strong>Cons</strong>: Less secure as it sends credentials in base64 encoded format, vulnerable to interception.</li>
</ul>
</li>
</ol>
<p>Each of these methods has its strengths and weaknesses, and the choice largely depends on the specific requirements of your web application.</p>
<h3 id="conclusion-balancing-security-and-user-experience-in-web-services">Conclusion: Balancing Security and User Experience in Web Services</h3>
<p>As we conclude this journey through securing and personalizing web services, it&rsquo;s clear that the right blend of backend security and frontend design can significantly enhance both the security and user experience of a web application.</p>
<p>From implementing robust JWT authentication in a Flask application to designing an engaging and intuitive frontend, each aspect plays a pivotal role in delivering a seamless and secure user experience. Whether it&rsquo;s the compact and scalable nature of JWTs, the interactive and user-friendly design elements on the frontend, or the comparative analysis of different authentication methods, each component contributes to a comprehensive web service solution.</p>
<p>For small projects, especially, the choice of JWT stands out for its efficiency, scalability, and ease of implementation, making it a wise choice for developers looking to secure their applications without compromising on performance or user experience.</p>
<p>In the end, the key takeaway is that security and usability do not have to be at odds; with the right tools and approaches, they can complement each other to create web services that are not only secure but also enjoyable to use.</p>
<p>We encourage our readers to explore the code and concepts discussed in this blog post and to visit the GitHub repository for the original code at <a href="https://github.com/AikenH/ezEncryptYourService?tab=readme-ov-file" target="_blank" rel="noopener">ezEncryptYourService</a>
. Dive in, experiment, and elevate your web services to new heights of security and user engagement.</p>
]]></content:encoded>
    </item>
    <item>
      <title>搭建家庭服务器1 整体方案设计</title>
      <link>https://aikenh.cn/posts/%E5%AE%B6%E5%BA%AD%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%95%B4%E4%BD%93%E6%96%B9%E6%A1%88/</link>
      <pubDate>Wed, 20 Dec 2023 08:24:36 +0000</pubDate>
      <guid>https://aikenh.cn/posts/%E5%AE%B6%E5%BA%AD%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%95%B4%E4%BD%93%E6%96%B9%E6%A1%88/</guid>
      <description>&lt;head&gt;
    
    &lt;script src=&#34;https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/crypto-js/3.1.9-1/crypto-js.js&#34;&gt;&lt;/script&gt;
&lt;/head&gt;





&lt;div class=&#34;hugo-encryptor-container&#34;&gt;
  &lt;div class=&#34;hugo-encryptor-prompt&#34;&gt;
    
      &lt;p&gt;文章的部分内容被密码保护：&lt;/p&gt;
    
  &lt;/div&gt;
  &lt;div class=&#34;hugo-encryptor-form&#34;&gt;
    &lt;input
      class=&#34;hugo-encryptor-input&#34;
      placeholder=&#39;请输入密码&#39;
    /&gt;
    &lt;input
      class=&#34;hugo-encryptor-button&#34;
      type=&#34;button&#34;
      value=&#39;CLICK&#39;
      onclick=&#34;_click_handler(this)&#34;
    /&gt;
  &lt;/div&gt;
  &lt;div
    class=&#34;hugo-encryptor-cipher-text&#34;
    data-password=&#34;aikenhong_blog&#34;
    style=&#34;display: none;&#34;
  &gt;
    &lt;span style=&#34;display: none;&#34;&gt;--- DON&#39;T MODIFY THIS LINE ---&lt;/span&gt;
    


  
  

&lt;blockquote class=&#34;alert-blockquote alert-summary&#34;&gt;
  &lt;p class=&#34;alert-heading&#34;&gt;
    &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 16 16&#34; width=&#34;16&#34; height=&#34;16&#34;&gt;
      &lt;path d=&#34;M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z&#34;&gt;&lt;/path&gt;
    &lt;/svg&gt;
    &lt;span&gt;Summary&lt;/span&gt;
  &lt;/p&gt;</description>
      <content:encoded><![CDATA[<head>
    
    <script src="https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/crypto-js/3.1.9-1/crypto-js.js"></script>
</head>





<div class="hugo-encryptor-container">
  <div class="hugo-encryptor-prompt">
    
      <p>文章的部分内容被密码保护：</p>
    
  </div>
  <div class="hugo-encryptor-form">
    <input
      class="hugo-encryptor-input"
      placeholder='请输入密码'
    />
    <input
      class="hugo-encryptor-button"
      type="button"
      value='CLICK'
      onclick="_click_handler(this)"
    />
  </div>
  <div
    class="hugo-encryptor-cipher-text"
    data-password="aikenhong_blog"
    style="display: none;"
  >
    <span style="display: none;">--- DON'T MODIFY THIS LINE ---</span>
    


  
  

<blockquote class="alert-blockquote alert-summary">
  <p class="alert-heading">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">
      <path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path>
    </svg>
    <span>Summary</span>
  </p>
  <p>本文主要介绍笔者的家庭服务器设计方案 V1。该方案中中涵盖影视、图像、图书&amp;文献、博客、智能家居管理、密码管理服务，以及公网上内容分发的初步设计和选择的考量。</p>

</blockquote>
<h2 id="整体架构">整体架构</h2>
<p>整个服务中心在设计的时候主要考虑以下的几个方面：</p>
<ul>
<li>💻操作设备&amp;系统(OS)和内容存储方案：如何<strong>经济实惠</strong>的选择合适的设备和系统</li>
<li>🌦️服务需求：需要搭建<strong>哪些服务</strong>，应该选择<strong>哪个服务</strong>进行自托管</li>
<li>🌍部署方案：如何保证在局域网+<strong>公网</strong>都能顺利且<strong>安全的访问</strong></li>
<li>📱访问策略：各个平台上的访问方式的选择</li>
</ul>
<p>此外，并非所有服务都需要在公网上进行部署，这里的部分服务可以仅用于局域网，由于仅开放了 Nginx 端口，因此从公网中访问不到未分发的内容，因此，部分存储与敏感信息相关的服务可以仅保留在局域网中进行访问。</p>



  
  

<blockquote class="alert-blockquote alert-note">
  <p class="alert-heading">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">
      <path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path>
    </svg>
    <span>Note</span>
  </p>
  <p>在这种情况下如果希望能够使用一致的域名访问方式，是否可以考虑使用 Nginx 鉴权限制访问 ip 为内网 ip，其他公网访问直接丢弃？</p>

</blockquote>
<p>初版家庭服务中心的架构图如下，(这里并非列出所有的服务组件)，该架构图中所有的应用都是为了方便日常生活中的各项活动，下面会对架构图自底向上的做一些简单的说明。</p>
<p>
<div class="post-img-view">
  <a data-fancybox="gallery" href="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231209082421.png">
    <img alt="image.png" loading="lazy" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231209082421.png"class="responsive-image" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231209082421.png" style="display: block; margin: 0 auto;"
      alt="image.png"  />
  </a>
</div>


<script>
  document.addEventListener("DOMContentLoaded", function() {
      var images = document.querySelectorAll(".responsive-image");
      var maxHeight = window.innerHeight / 2.5;
      images.forEach(function(image) {
          image.style.maxHeight = maxHeight + "px";
      });
  });
</script>
</p>
<h3 id="设备和存储选择">设备和存储选择</h3>
<p>存储和设备的选择主要来自以下的两个需求，首先是能够用作<strong>机顶盒</strong>，其次是尽量<strong>能省则省</strong>，综合考虑上述的两个需求，树莓派+网络云盘的策略有如下的几个优势，也是选择它的原因：</p>
<ul>
<li>设备和存储价格相对便宜，长时间在线电费消耗也几乎可以忽略不计</li>
<li>体积较小，作为机顶盒放置的方式较为简单</li>
<li>云盘的转存和资源收集的速度快，且可结合小雅和 Alist 进行网盘的集成，以及线上资源的整合。</li>
</ul>
<p>劣势就在于非本地存储和对网络的要求相对较高一些，且在系统上需要折腾的时间会比成熟的 NAS 厂家的开箱即用要折腾一些，最后就是一些对性能要求较高的服务树莓派可能无法承担这个职责，因此这里使用自己的个人电脑用来部署一些对性能要求较高的服务。</p>
<h3 id="服务部署策略">服务部署策略</h3>
<p>docker 在服务部署的优势有：独立互不影响的环境和一键部署方便性，因此我们使用 docker 进行几乎所有的服务的部署，并使用 portainer 对其进行管理。</p>
<ul>
<li>图书&amp;漫画：使用 Kavita+Calibre（进行资源的转换和元信息的不全）+ （Zlibray&amp;漫自由）获取资源。</li>
<li>照片：使用 Immich 满足整个家庭备份手机照片并进行统一管理的需求，可完全代替 icloud 且只需要本地磁盘。</li>
<li>影视：使用 Alist 整合自己的网盘 + 线上的 xiaoya 的 alist 资源 + emby 做一个影视墙（可选）+ Kodi 进行电视端的播放（KORE，kodi 手机端的官方遥控器）</li>
<li>智能家居：使用 HomeAssistant 管理各种不同品牌的智能家居设备</li>
</ul>
<h3 id="服务分发和服务访问">服务分发和服务访问</h3>
<p>这里使用 Nginx+腾讯云进行服务的分发，并通过 DDNSgo 动态更新域名解析的 IPV6 地址，之后就可以通过域名对相应服务进行访问。</p>
<blockquote>
<p>可用 Caddy 这个现在的后期之秀来代替 Nginx，Nginx 也可以安装对应的可视化设置界面。</p>
</blockquote>
<p>上述提到的是基于 ipv6 部署到公网后的分发情况，如果没有或者不想部署公网的话就使用如 Zerotier 和 Tailscale 的内网穿透服务来实现虚拟内网 ip+端口去访问服务。</p>
<p>在各个终端上的服务访问可以通过浏览器的<strong>保存为应用</strong>/<strong>固定到桌面</strong>，以及各自对应的官方 APP 为主要的访问手段。</p>
<h2 id="后续计划">后续计划</h2>



  
  

<blockquote class="alert-blockquote alert-note">
  <p class="alert-heading">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">
      <path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path>
    </svg>
    <span>Note</span>
  </p>
  <p>这里记录后续希望进行补充的服务和设计，最终希望能够作为一个比较全面且完整的方案，覆盖到生活的方方面面，为自己和朋友的生活提供便利。</p>

</blockquote>
<ul>
<li><input checked="" disabled="" type="checkbox"> 设计 StableDiffusionWEBui 的<strong>鉴权</strong>页面，方便部署到公网上使用，方便作图需求</li>
<li><input checked="" disabled="" type="checkbox"> 现已涵盖视频图片和图书等服务，后续考虑是否有必要针对<strong>音乐</strong>添加相关页面</li>
<li><input checked="" disabled="" type="checkbox"> (eval) 菜谱管理中心，收藏各个不同的平台的菜谱</li>
<li><input checked="" disabled="" type="checkbox"> (eval) RSS Center 可能需要 RSS-HUB 创建自己关心的所有 RSS 源，然后通过</li>
<li><input checked="" disabled="" type="checkbox"> (eval) 使用 Ghost 尝试更新自己的 Blog</li>
<li><input checked="" disabled="" type="checkbox"> (eval) 更新 Homepage</li>
<li><input checked="" disabled="" type="checkbox"> 更新 CodeServer，是否要在网页上部署Vscode</li>
</ul>
<h2 id="fi">FI</h2>

  </div>
</div>

<script>
    


function sanitizeContent(content) {
    
    return content.replace(/[\x00-\x1F\x7F]/g, '').trim(); 
}

function encryptContent(password, content) {
    const key = CryptoJS.MD5(password).toString();
    const iv = key.substring(16); 
    const paddedContent = padContent(content);
    const encrypted = CryptoJS.AES.encrypt(paddedContent, CryptoJS.enc.Utf8.parse(key), {
        iv: CryptoJS.enc.Utf8.parse(iv),
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    });
    return CryptoJS.enc.Base64.stringify(encrypted.ciphertext);
}

function padContent(content) {
    const blockSize = 32; 
    const padlen = blockSize - (content.length % blockSize);
    
    
    return content;
}

function processEncryptedBlocks() {
    const blocks = document.querySelectorAll('.hugo-encryptor-cipher-text');
    blocks.forEach(block => {
        const password = block.getAttribute('data-password');
        const content = block.innerHTML.trim(); 
        const sanitizedContent = sanitizeContent(content); 
        const encryptedContent = encryptContent(password, sanitizedContent);
        block.innerHTML = encryptedContent;
        block.removeAttribute('data-password');
    });

    
    const script = document.createElement('script');
    script.src = '/js/decrypt.js';
    document.body.appendChild(script);
}


document.addEventListener('DOMContentLoaded', processEncryptedBlocks);
</script>
]]></content:encoded>
    </item>
    <item>
      <title>破解本地自托管Emby服务</title>
      <link>https://aikenh.cn/posts/emby_localhost_crack_by_nginx/</link>
      <pubDate>Fri, 10 Nov 2023 14:05:15 +0000</pubDate>
      <guid>https://aikenh.cn/posts/emby_localhost_crack_by_nginx/</guid>
      <description>&lt;h2 id=&#34;intro-问题描述&#34;&gt;Intro 问题描述&lt;/h2&gt;
&lt;p&gt;以 emby 为例，学习如何对这种自托管的服务，请求验证网站的软件进行破解，破解的整体思路分为以下的两类：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一种是改客户端，一种是改服务端。两种方式的最终原理一样，都是搭建仿冒认证服务器，客户端访问伪服务器拿到授权信息；【3】&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;因此无论是那种方法我们都需要搭建伪验证服务器：所以以下的操作是必须的：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;抓取定位到验证服务器的地址，并获取验证服务器返回的验证信息格式；&lt;/li&gt;
&lt;li&gt;使用 Nginx 搭建虚拟的验证服务器，使其返回激活/验证信息；&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果是&lt;strong&gt;修改客户端&lt;/strong&gt;，则在搭建完验证站后需要：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;修改 Host 文件，将请求验证服务器的请求劫持到本地搭建的服务上；&lt;/li&gt;
&lt;li&gt;需要伪服务器和客户端都要安装自签名证书&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果是&lt;strong&gt;修改服务端&lt;/strong&gt;，则后续需要：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;修改服务端源文件，将默认的认证服务器 mb3admin.com 地址直接改成伪服务器&lt;/li&gt;
&lt;li&gt;这样就&lt;strong&gt;不需要安装自签名证书&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果是使用 Docker 进行部署的 emby 可以使用别人修改好的服务端的镜像进行部署安装（like 开心版）&lt;/p&gt;
&lt;p&gt;本文主要介绍验证服务器的搭建、修改客户端的方法，以及一些其他的注意事项；&lt;/p&gt;
&lt;h2 id=&#34;server-伪验证服务器搭建&#34;&gt;Server 伪验证服务器搭建&lt;/h2&gt;
&lt;h3 id=&#34;ca-证书申请&#34;&gt;CA 证书申请&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;什么是证书：&lt;a href=&#34;https://aws.amazon.com/cn/what-is/ssl-certificate/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;什么是 SSL 认证？- SSL/TLS 认证简介 - AWS (amazon.com)&lt;/a&gt;
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这里由于我们并没有真正拥有认证网站 md3admin 的域名故而没有对应的证书文件，因此需要为自己的服务自签发一个假的证书文件，并手动让浏览器信任该证书，保证在将该域名通过 hosts 修改转移到自己的服务上时，浏览器能正常请求该页面。&lt;/p&gt;
&lt;p&gt;下面介绍证书的签发过程（转载自参考文献:【2】)：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;div class=&#34;code-title&#34; onclick=&#34;toggleCode(this)&#34;&gt;
        
        &lt;span class=&#34;code-block-open&#34;&gt;&lt;ion-icon name=&#34;chevron-down-outline&#34;&gt;&lt;/ion-icon&gt;&lt;/span&gt;
        &lt;span class=&#34;code-block-close&#34;&gt;&lt;ion-icon name=&#34;chevron-forward-outline&#34;&gt;&lt;/ion-icon&gt;&lt;/ion-icon&gt;&lt;/span&gt;
        &lt;span&gt;shell&lt;/span&gt;
    &lt;/div&gt;
    &lt;div class=&#34;code-content&#34;&gt;
        
        &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir -p &amp;lt;username&amp;gt;/&amp;lt;docker-file-path&amp;gt;/nginx/cert/mb3admin.com &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; &amp;lt;username&amp;gt;/&amp;lt;docker-file-path&amp;gt;/nginx/cert/mb3admin.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 生成 CA 密钥&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;openssl genrsa -out ca.key &lt;span class=&#34;m&#34;&gt;2048&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 生成 CA 证书&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;openssl req -x509 -new -nodes -key ca.key -subj &lt;span class=&#34;s2&#34;&gt;&amp;#34;/C=CN/ST=Beijing/L=Beijing/O=&amp;lt;username&amp;gt;/OU=&amp;lt;username&amp;gt;/CN=&amp;lt;username&amp;gt;/emailAddress=&amp;lt;user-email&amp;gt;&amp;#34;&lt;/span&gt; -days &lt;span class=&#34;m&#34;&gt;36500&lt;/span&gt; -out ca.crt
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 将 CA 转换成 p12 格式，并指定密码 （&amp;lt;username&amp;gt;）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;openssl pkcs12 -export -clcerts -in ./ca.crt -inkey ca.key -out ca.p12 -password pass:&amp;lt;username&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 将 p12 格式的证书 Base64 编码&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;base64 ca.p12
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Base64 一行不能超过 76 字符，超过则添加回车换行符。如果因为换行的原因，不能安装证书，可以使用 -w 参数&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;base64 -w &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; ca.p12
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 将 CA 转换成 pem 格式&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;openssl x509 -outform pem -in ca.crt -out ca.pem
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 生成服务端私钥 server.key&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;openssl genrsa -out server.key &lt;span class=&#34;m&#34;&gt;2048&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 生成服务端证书请求 server.csr&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;openssl req -new -sha256 -key server.key -out server.csr -subj &lt;span class=&#34;s2&#34;&gt;&amp;#34;/C=CN/L=Beijing/O=&amp;lt;username&amp;gt;/OU=&amp;lt;username&amp;gt;/CN=mb3admin.com/CN=*.mb3admin.com&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 生成服务端证书 server.crt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;openssl x509 -req -extfile &amp;lt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;subjectAltName=DNS:mb3admin.com,DNS:*.mb3admin.com&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; -days &lt;span class=&#34;m&#34;&gt;3650&lt;/span&gt; -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;script&gt;
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === &#34;none&#34; || codeContent.style.display === &#34;&#34;) {
        codeContent.style.display = &#34;block&#34;; 
        codeContent.parentNode.classList.remove(&#34;code-has-hidden-child&#34;);
    } else {
        codeContent.style.display = &#34;none&#34;; 
        codeContent.parentNode.classList.add(&#34;code-has-hidden-child&#34;);
    }
}
&lt;/script&gt;&lt;p&gt;上述代码中替换一下自己的相关信息即可（也可以直接去参考资源中获取直接执行的代码）：&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="intro-问题描述">Intro 问题描述</h2>
<p>以 emby 为例，学习如何对这种自托管的服务，请求验证网站的软件进行破解，破解的整体思路分为以下的两类：</p>
<blockquote>
<p>一种是改客户端，一种是改服务端。两种方式的最终原理一样，都是搭建仿冒认证服务器，客户端访问伪服务器拿到授权信息；【3】</p>
</blockquote>
<p>因此无论是那种方法我们都需要搭建伪验证服务器：所以以下的操作是必须的：</p>
<ol>
<li>抓取定位到验证服务器的地址，并获取验证服务器返回的验证信息格式；</li>
<li>使用 Nginx 搭建虚拟的验证服务器，使其返回激活/验证信息；</li>
</ol>
<p>如果是<strong>修改客户端</strong>，则在搭建完验证站后需要：</p>
<ul>
<li>修改 Host 文件，将请求验证服务器的请求劫持到本地搭建的服务上；</li>
<li>需要伪服务器和客户端都要安装自签名证书</li>
</ul>
<p>如果是<strong>修改服务端</strong>，则后续需要：</p>
<ul>
<li>修改服务端源文件，将默认的认证服务器 mb3admin.com 地址直接改成伪服务器</li>
<li>这样就<strong>不需要安装自签名证书</strong></li>
</ul>
<p>如果是使用 Docker 进行部署的 emby 可以使用别人修改好的服务端的镜像进行部署安装（like 开心版）</p>
<p>本文主要介绍验证服务器的搭建、修改客户端的方法，以及一些其他的注意事项；</p>
<h2 id="server-伪验证服务器搭建">Server 伪验证服务器搭建</h2>
<h3 id="ca-证书申请">CA 证书申请</h3>
<blockquote>
<p>什么是证书：<a href="https://aws.amazon.com/cn/what-is/ssl-certificate/" target="_blank" rel="noopener">什么是 SSL 认证？- SSL/TLS 认证简介 - AWS (amazon.com)</a>
</p>
</blockquote>
<p>这里由于我们并没有真正拥有认证网站 md3admin 的域名故而没有对应的证书文件，因此需要为自己的服务自签发一个假的证书文件，并手动让浏览器信任该证书，保证在将该域名通过 hosts 修改转移到自己的服务上时，浏览器能正常请求该页面。</p>
<p>下面介绍证书的签发过程（转载自参考文献:【2】)：</p>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>shell</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">mkdir -p &lt;username&gt;/&lt;docker-file-path&gt;/nginx/cert/mb3admin.com <span class="o">&amp;&amp;</span> <span class="nb">cd</span> &lt;username&gt;/&lt;docker-file-path&gt;/nginx/cert/mb3admin.com
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 生成 CA 密钥</span>
</span></span><span class="line"><span class="cl">openssl genrsa -out ca.key <span class="m">2048</span> 
</span></span><span class="line"><span class="cl"><span class="c1"># 生成 CA 证书</span>
</span></span><span class="line"><span class="cl">openssl req -x509 -new -nodes -key ca.key -subj <span class="s2">&#34;/C=CN/ST=Beijing/L=Beijing/O=&lt;username&gt;/OU=&lt;username&gt;/CN=&lt;username&gt;/emailAddress=&lt;user-email&gt;&#34;</span> -days <span class="m">36500</span> -out ca.crt
</span></span><span class="line"><span class="cl"><span class="c1"># 将 CA 转换成 p12 格式，并指定密码 （&lt;username&gt;）</span>
</span></span><span class="line"><span class="cl">openssl pkcs12 -export -clcerts -in ./ca.crt -inkey ca.key -out ca.p12 -password pass:&lt;username&gt;
</span></span><span class="line"><span class="cl"><span class="c1"># 将 p12 格式的证书 Base64 编码</span>
</span></span><span class="line"><span class="cl">base64 ca.p12
</span></span><span class="line"><span class="cl"><span class="c1"># Base64 一行不能超过 76 字符，超过则添加回车换行符。如果因为换行的原因，不能安装证书，可以使用 -w 参数</span>
</span></span><span class="line"><span class="cl">base64 -w <span class="m">0</span> ca.p12
</span></span><span class="line"><span class="cl"><span class="c1"># 将 CA 转换成 pem 格式</span>
</span></span><span class="line"><span class="cl">openssl x509 -outform pem -in ca.crt -out ca.pem
</span></span><span class="line"><span class="cl"><span class="c1"># 生成服务端私钥 server.key</span>
</span></span><span class="line"><span class="cl">openssl genrsa -out server.key <span class="m">2048</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 生成服务端证书请求 server.csr</span>
</span></span><span class="line"><span class="cl">openssl req -new -sha256 -key server.key -out server.csr -subj <span class="s2">&#34;/C=CN/L=Beijing/O=&lt;username&gt;/OU=&lt;username&gt;/CN=mb3admin.com/CN=*.mb3admin.com&#34;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 生成服务端证书 server.crt</span>
</span></span><span class="line"><span class="cl">openssl x509 -req -extfile &lt;<span class="o">(</span><span class="nb">printf</span> <span class="s2">&#34;subjectAltName=DNS:mb3admin.com,DNS:*.mb3admin.com&#34;</span><span class="o">)</span> -days <span class="m">3650</span> -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt</span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><p>上述代码中替换一下自己的相关信息即可（也可以直接去参考资源中获取直接执行的代码）：</p>
<ul>
<li><code>\&lt;username\&gt;</code></li>
<li><code>\&lt;user-email\&gt;</code></li>
<li><code>\&lt;docker-file-path\&gt;</code></li>
</ul>
<p>生成对应证书后，对证书的操作还有以下的两步，我们首先介绍证书安装，在对 Nginx 进行配置。</p>
<ul>
<li>Nginx 配置：将 server.crt 和 server.key 放到 nginx 存放的地方，方便后面配置 Nginx</li>
<li>证书安装：便于浏览器后续正常访问</li>
</ul>
<h3 id="证书安装">证书安装</h3>
<p>双击打开证书目录中的 crt 进行证书安装，会显示“无法验证到一个受信任的证书颁发机构”，这是因为我们只是自签发并没有找第三方机构认证，不要紧直接安装即可。</p>
<blockquote>
<p>没有安装证书的情况下也可能会发现验证信息正确但是激活失败的情况</p>
</blockquote>
<p>这里简单介绍以下 ubuntu 信任证书的方法【2】（nas 和 ios 可参考该文）</p>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>shell</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo cp ca.crt /usr/local/share/ca-certificates/zhuangzhuang.crt <span class="o">&amp;&amp;</span> sudo update-ca-certificates</span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><h3 id="nginx-设置">Nginx 设置</h3>
<p>Nginx 可以直接在 conf.d/default.conf 中新增以下代码片段</p>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>json</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="err">server</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="err">listen</span> <span class="err">443</span> <span class="err">ssl;</span>
</span></span><span class="line"><span class="cl">    <span class="err">listen</span> <span class="err">[::]:443</span> <span class="err">ssl;</span>
</span></span><span class="line"><span class="cl">    <span class="err">server_name</span> <span class="err">mb3admin.com;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="err">ssl_certificate</span> <span class="err">/etc/nginx/cert/mb3admin.com/server.crt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">ssl_certificate_key</span> <span class="err">/etc/nginx/cert/mb3admin.com/server.key;</span>
</span></span><span class="line"><span class="cl">	<span class="err">ssl_session_timeout</span> <span class="err">5m;</span>
</span></span><span class="line"><span class="cl">    <span class="err">ssl_protocols</span> <span class="err">TLSv1</span> <span class="err">TLSv1.1</span> <span class="err">TLSv1.2;</span>
</span></span><span class="line"><span class="cl">    <span class="err">ssl_ciphers</span> <span class="err">ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;</span>
</span></span><span class="line"><span class="cl">    <span class="err">ssl_prefer_server_ciphers</span> <span class="err">on;</span>
</span></span><span class="line"><span class="cl">    <span class="err">add_header</span> <span class="err">Access-Control-Allow-Origin</span> <span class="err">*;</span>
</span></span><span class="line"><span class="cl">    <span class="err">add_header</span> <span class="err">Access-Control-Allow-Headers</span> <span class="err">*;</span>
</span></span><span class="line"><span class="cl">    <span class="err">add_header</span> <span class="err">Access-Control-Allow-Method</span> <span class="err">*;</span>
</span></span><span class="line"><span class="cl">    <span class="err">add_header</span> <span class="err">Access-Control-Allow-Credentials</span> <span class="err">true;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="err">location</span> <span class="err">/admin/service/registration/validateDevice</span> <span class="err">{</span>
</span></span><span class="line"><span class="cl">        <span class="err">default_type</span> <span class="err">application/json;</span>
</span></span><span class="line"><span class="cl">        <span class="err">return</span> <span class="err">200</span> <span class="err">&#39;{</span><span class="nt">&#34;cacheExpirationDays&#34;</span><span class="p">:</span> <span class="mi">365</span><span class="p">,</span><span class="nt">&#34;message&#34;</span><span class="p">:</span> <span class="s2">&#34;Device Valid&#34;</span><span class="p">,</span><span class="nt">&#34;resultCode&#34;</span><span class="p">:</span> <span class="s2">&#34;GOOD&#34;</span><span class="p">}</span><span class="err">&#39;;</span>
</span></span><span class="line"><span class="cl">    <span class="err">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="err">location</span> <span class="err">/admin/service/registration/validate</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="err">default_type</span> <span class="err">application/json;</span>
</span></span><span class="line"><span class="cl">        <span class="err">return</span> <span class="err">200</span> <span class="err">&#39;{</span><span class="nt">&#34;featId&#34;</span><span class="p">:</span><span class="s2">&#34;&#34;</span><span class="p">,</span><span class="nt">&#34;registered&#34;</span><span class="p">:</span><span class="kc">true</span><span class="p">,</span><span class="nt">&#34;expDate&#34;</span><span class="p">:</span><span class="s2">&#34;2099-01-01&#34;</span><span class="p">,</span><span class="nt">&#34;key&#34;</span><span class="p">:</span><span class="s2">&#34;&#34;</span><span class="p">}</span><span class="err">&#39;;</span>
</span></span><span class="line"><span class="cl">    <span class="err">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="err">location</span> <span class="err">/admin/service/registration/getStatus</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="err">default_type</span> <span class="err">application/json;</span>
</span></span><span class="line"><span class="cl">        <span class="err">return</span> <span class="err">200</span> <span class="err">&#39;{</span><span class="nt">&#34;deviceStatus&#34;</span><span class="p">:</span><span class="s2">&#34;0&#34;</span><span class="p">,</span><span class="nt">&#34;planType&#34;</span><span class="p">:</span><span class="s2">&#34;Lifetime&#34;</span><span class="p">,</span><span class="nt">&#34;subscriptions&#34;</span><span class="p">:{}}</span><span class="err">&#39;;</span>
</span></span><span class="line"><span class="cl">    <span class="err">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="err">location</span> <span class="err">/emby/Plugins/SecurityInfo</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="err">default_type</span> <span class="err">application/json;</span>
</span></span><span class="line"><span class="cl">        <span class="err">return</span> <span class="err">200</span> <span class="err">&#39;{</span><span class="nt">&#34;SupporterKey&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="nt">&#34;IsMBSupporter&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">}</span><span class="err">&#39;;</span>
</span></span><span class="line"><span class="cl">    <span class="err">}</span>
</span></span><span class="line"><span class="cl"><span class="err">}</span></span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><p>可以看到其中最为关键的是以下的几个点：</p>
<ol>
<li>设置好 servername 为验证网址的域名 mb3admin.com;</li>
<li>将以下的域名子路径都设置为返回对应的验证信息</li>
</ol>
<ul>
<li><code>/admin/service/registration/validateDevice</code></li>
<li><code>/admin/service/registration/validate</code></li>
<li><code>/admin/service/registration/getStatus</code></li>
<li><code>/emby/Plugins/SecurityInfo</code></li>
</ul>
<ol start="3">
<li>ssl_certificate 和 ssl_certificate_key 填写好对应的证书文件地址存放地址</li>
</ol>
<h3 id="hosts-文件修改">Hosts 文件修改</h3>
<p>linux 修改 host 文件地址：<code>/etc/hosts</code></p>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>bash</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo <span class="nb">echo</span> <span class="s2">&#34;&lt;server_ip&gt;    &lt;domain_address&gt;&#34;</span> &gt;&gt; /etc/hosts</span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><p>Windows 修改 host 文件地址：<code>C:\Windows\System32\drivers\etc\hosts</code></p>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>txt</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">....
</span></span><span class="line"><span class="cl">&lt;server_ip&gt; &lt;domain_address&gt;</span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><p>修改完立即刷新 dns 不需重启立即生效的方法,在 powershell 执行：</p>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>powershell</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="n">ipconfig</span> <span class="p">/</span><span class="n">flushdns</span></span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><h2 id="test-服务测试">Test 服务测试</h2>
<p>完成上面的服务和 Host 修改后，可以登录对应的网站进行测试或者使用 curl 进行测试：</p>
<p>可以通过浏览器访问： <a href="https://mb3admin.com/admin/service/registration/validateDevice/" target="_blank" rel="noopener">https://mb3admin.com/admin/service/registration/validateDevice/</a>
 或者 <a href="https://mb3admin.com/admin/service/registration/validate/" target="_blank" rel="noopener">https://mb3admin.com/admin/service/registration/validate/</a>
 查看返回信息是否正确；</p>
<p>
<div class="post-img-view">
  <a data-fancybox="gallery" href="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231112095033.png">
    <img alt="image.png" loading="lazy" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231112095033.png"class="responsive-image" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231112095033.png" style="display: block; margin: 0 auto;"
      alt="image.png"  />
  </a>
</div>


<script>
  document.addEventListener("DOMContentLoaded", function() {
      var images = document.querySelectorAll(".responsive-image");
      var maxHeight = window.innerHeight / 2.5;
      images.forEach(function(image) {
          image.style.maxHeight = maxHeight + "px";
      });
  });
</script>
</p>
<p>或者在 <strong>powershell</strong> 中执行以下命令查看返回信息是否正确，这里需要注意的是可能会与 WSL2 部署的 docker 混淆，但是该指令是否生效我们还是要在宿主机上的 powershell 查看。</p>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>powershell</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="nb">curl </span><span class="n">-ik</span> <span class="n">https</span><span class="err">:</span><span class="p">//</span><span class="n">mb3admin</span><span class="p">.</span><span class="n">com</span><span class="p">/</span><span class="n">admin</span><span class="p">/</span><span class="n">service</span><span class="p">/</span><span class="n">registration</span><span class="p">/</span><span class="n">validateDevice</span><span class="p">/</span></span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><p>
<div class="post-img-view">
  <a data-fancybox="gallery" href="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231112095026.png">
    <img alt="image.png" loading="lazy" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231112095026.png"class="responsive-image" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231112095026.png" style="display: block; margin: 0 auto;"
      alt="image.png"  />
  </a>
</div>


<script>
  document.addEventListener("DOMContentLoaded", function() {
      var images = document.querySelectorAll(".responsive-image");
      var maxHeight = window.innerHeight / 2.5;
      images.forEach(function(image) {
          image.style.maxHeight = maxHeight + "px";
      });
  });
</script>
</p>
<p><code>-k</code> 参数是为了忽略<strong>证书不受信任的 https 访问安全限制</strong>导致访问失败的问题。例如如下相关报错</p>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>txt</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">&gt; Curl(60) issuer certificate is invalid </span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><p>如果使用 curl 能够正常返回，但是网页的返回信息有误，或者无法打开网页，可能就要考虑如下的原因：</p>
<ul>
<li><strong>证书信任问题</strong>，可能要看是否正确配置和安装相关证书</li>
<li><strong>浏览器缓存问题</strong>，可以使用隐私(InPrivate)模式再次访问,或者清楚缓存后再次访问；</li>
<li><strong>代理的问题</strong>，具体的配置如下</li>
</ul>
<h3 id="clash-代理配置">Clash 代理配置</h3>
<p>若是由于设置了代理导致网站测试失败，这种情况下可以关掉代理再做测试，或者新增规则，配置验证网址不走代理。</p>
<p>配置规则的方法以 Clash 为例如下：</p>
<p>
<div class="post-img-view">
  <a data-fancybox="gallery" href="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231111161114.png">
    <img alt="image.png" loading="lazy" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231111161114.png"class="responsive-image" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231111161114.png" style="display: block; margin: 0 auto;"
      alt="image.png"  />
  </a>
</div>


<script>
  document.addEventListener("DOMContentLoaded", function() {
      var images = document.querySelectorAll(".responsive-image");
      var maxHeight = window.innerHeight / 2.5;
      images.forEach(function(image) {
          image.style.maxHeight = maxHeight + "px";
      });
  });
</script>
</p>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>txt</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">parsers: # array
</span></span><span class="line"><span class="cl">    - url: &lt;你的订阅地址&gt;
</span></span><span class="line"><span class="cl">      yaml:
</span></span><span class="line"><span class="cl">        prepend-rules:
</span></span><span class="line"><span class="cl">            - DOMAIN-SUFFIX,&lt;直连的域名&gt;,DIRECT</span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><p>添加 DOMAIN-SUFFIX 设置对应的域名为直链即可；</p>
<h3 id="activate-激活">Activate 激活</h3>
<p>访问 emby 服务页面，在 <code>emby premiere</code> 选单中随便选取密钥存储即可，这里会显示无效的密钥但是不要紧，可以切换主题看看是否已经激活成功。</p>
<p>
<div class="post-img-view">
  <a data-fancybox="gallery" href="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231112100556.png">
    <img alt="image.png" loading="lazy" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231112100556.png"class="responsive-image" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231112100556.png" style="display: block; margin: 0 auto;"
      alt="image.png"  />
  </a>
</div>


<script>
  document.addEventListener("DOMContentLoaded", function() {
      var images = document.querySelectorAll(".responsive-image");
      var maxHeight = window.innerHeight / 2.5;
      images.forEach(function(image) {
          image.style.maxHeight = maxHeight + "px";
      });
  });
</script>
</p>
<p>或者在控制台界面看是否已经有金标即可：</p>
<p>
<div class="post-img-view">
  <a data-fancybox="gallery" href="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231112100635.png">
    <img alt="image.png" loading="lazy" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231112100635.png"class="responsive-image" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231112100635.png" style="display: block; margin: 0 auto;"
      alt="image.png"  />
  </a>
</div>


<script>
  document.addEventListener("DOMContentLoaded", function() {
      var images = document.querySelectorAll(".responsive-image");
      var maxHeight = window.innerHeight / 2.5;
      images.forEach(function(image) {
          image.style.maxHeight = maxHeight + "px";
      });
  });
</script>
</p>
<h2 id="ios-使用小火箭解锁-emby">IOS 使用小火箭解锁 emby</h2>
<p>详细图文请访问参考资料【5】，下面是简单的摘要：</p>
<p>第一步 default.conf（长按） -&gt; 编辑纯文本，将以下代码贴到底部：</p>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>shell</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="o">[</span>Script<span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="nv">EmbyPremiere</span> <span class="o">=</span> <span class="nv">type</span><span class="o">=</span>http-response,script-path<span class="o">=</span>https://raw.githubusercontent.com/rartv/SurgeScript/main/EmbyPremiere/EmbyPremiere.js,pattern<span class="o">=</span>^https?:<span class="se">\/\/</span>mb3admin.com<span class="se">\/</span>admin<span class="se">\/</span>service<span class="se">\/</span>registration<span class="se">\/</span>validateDevice,max-size<span class="o">=</span>131072,requires-body<span class="o">=</span>true,timeout<span class="o">=</span>10,enable<span class="o">=</span><span class="nb">true</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span>MITM<span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="nv">hostname</span> <span class="o">=</span> mb3admin.com</span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><p>第二步 default.conf（右侧的 i 按钮） -&gt; <code>https解密</code> （界面中应该会有 mb3admin.com 的域名信息）-&gt; 证书-&gt; 生成新的 CA 证书 -&gt; 安装证书 -&gt; 系统中信任证书</p>
<p>第三步打开 vpn，打开 emby，信任证书选择 ok，激活完成后可以关闭小火箭，后续失效的时候再打开一次即可。</p>
<h2 id="ref-参考资料">Ref 参考资料</h2>
<ol>
<li><a href="https://lengyue524.github.io/emby/2021/10/19/%E7%BE%A4%E6%99%96%E4%B8%8A%E5%88%A9%E7%94%A8%E7%8E%B0%E6%9C%89nginx%E6%90%AD%E5%BB%BAemby%E7%A0%B4%E8%A7%A3%E6%9C%8D%E5%8A%A1.html" target="_blank" rel="noopener">群晖上利用现有nginx搭建emby破解服务 (lengyue524)</a>
</li>
<li><a href="https://zhuangzhuang.io/2022/01/08/emby-carck.html" target="_blank" rel="noopener">Nas Emby 伪验证服务站搭建 - ZHUANGZHUANG</a>
</li>
<li><a href="https://hgl2.com/2023/unlock-emby/" target="_blank" rel="noopener">学习 Emby 解锁及优化 - Gallen&rsquo;s Blog (hgl2.com)</a>
</li>
<li><a href="https://velaciela.ms/emby-free-premier" target="_blank" rel="noopener">Emby全平台开心版、认证服务器设置方法 | Velaciela</a>
</li>
<li><a href="https://www.huluohu.com/posts/186/" target="_blank" rel="noopener">iOS利用小火箭解锁Emby | 胡萝虎的博客 (huluohu.com)</a>
</li>
</ol>
<h2 id="fi">Fi</h2>
]]></content:encoded>
    </item>
    <item>
      <title>使用Ipv6部署服务02 Nginx和Https</title>
      <link>https://aikenh.cn/posts/deploy_server_byipv6_02_nginx/</link>
      <pubDate>Mon, 06 Nov 2023 20:49:16 +0000</pubDate>
      <guid>https://aikenh.cn/posts/deploy_server_byipv6_02_nginx/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本系列主要介绍通过公网的 ipv6 访问局域网设备并部署自己的服务供公网访问；本篇是其中的第二篇，以腾讯云和 Nginx 为例，介绍如何将服务部署到 https://域名 。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;需要以下的事前准备：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;腾讯云/阿里云/Cloudflare 等随便一个地方购买一个自己的域名&lt;/li&gt;
&lt;li&gt;公网 ipv4/ipv6 地址&lt;/li&gt;
&lt;li&gt;本地部署一个服务以供测试&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;nginxdocker-安装&#34;&gt;Nginx(Docker) 安装&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;这里选择 Docker 来分离安装 Nginx 服务，Docker 部署的优势这里就不再赘述，这里建议使用 portainer 进行容器管理和运行状态查看。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;首先创建存放 Nginx 配置文件和证书文件的目录，后续挂载给 Nginx Contrainer 使用：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;div class=&#34;code-title&#34; onclick=&#34;toggleCode(this)&#34;&gt;
        
        &lt;span class=&#34;code-block-open&#34;&gt;&lt;ion-icon name=&#34;chevron-down-outline&#34;&gt;&lt;/ion-icon&gt;&lt;/span&gt;
        &lt;span class=&#34;code-block-close&#34;&gt;&lt;ion-icon name=&#34;chevron-forward-outline&#34;&gt;&lt;/ion-icon&gt;&lt;/ion-icon&gt;&lt;/span&gt;
        &lt;span&gt;shell&lt;/span&gt;
    &lt;/div&gt;
    &lt;div class=&#34;code-content&#34;&gt;
        
        &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir -p /home/username/docker-file/nginx/conf.d
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir -p /home/username/docker-file/nginx/cert&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;script&gt;
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === &#34;none&#34; || codeContent.style.display === &#34;&#34;) {
        codeContent.style.display = &#34;block&#34;; 
        codeContent.parentNode.classList.remove(&#34;code-has-hidden-child&#34;);
    } else {
        codeContent.style.display = &#34;none&#34;; 
        codeContent.parentNode.classList.add(&#34;code-has-hidden-child&#34;);
    }
}
&lt;/script&gt;&lt;blockquote&gt;
&lt;p&gt;配置文件存放到 &lt;code&gt;conf.d&lt;/code&gt; 中，&lt;strong&gt;各种 SSL 证书则存放到&lt;/strong&gt; &lt;code&gt;cert&lt;/code&gt; 文件夹中；&lt;/p&gt;</description>
      <content:encoded><![CDATA[<blockquote>
<p>本系列主要介绍通过公网的 ipv6 访问局域网设备并部署自己的服务供公网访问；本篇是其中的第二篇，以腾讯云和 Nginx 为例，介绍如何将服务部署到 https://域名 。</p>
</blockquote>
<p>需要以下的事前准备：</p>
<ul>
<li>腾讯云/阿里云/Cloudflare 等随便一个地方购买一个自己的域名</li>
<li>公网 ipv4/ipv6 地址</li>
<li>本地部署一个服务以供测试</li>
</ul>
<h2 id="nginxdocker-安装">Nginx(Docker) 安装</h2>
<blockquote>
<p>这里选择 Docker 来分离安装 Nginx 服务，Docker 部署的优势这里就不再赘述，这里建议使用 portainer 进行容器管理和运行状态查看。</p>
</blockquote>
<p>首先创建存放 Nginx 配置文件和证书文件的目录，后续挂载给 Nginx Contrainer 使用：</p>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>shell</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">mkdir -p /home/username/docker-file/nginx/conf.d
</span></span><span class="line"><span class="cl">mkdir -p /home/username/docker-file/nginx/cert</span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><blockquote>
<p>配置文件存放到 <code>conf.d</code> 中，<strong>各种 SSL 证书则存放到</strong> <code>cert</code> 文件夹中；</p>
</blockquote>
<p>接着部署 Nginx，这次先不挂载目录，将 Nginx 中一些默认配置拷贝出来，以供后续使用和修改：</p>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>shell</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># run会自己执行镜像拉取，pull可以不执行</span>
</span></span><span class="line"><span class="cl"><span class="c1"># docker pull nginx</span>
</span></span><span class="line"><span class="cl">sudo docker run --name nginx -p 80:80 -d nginx </span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><p>将配置文件复制出来：</p>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>shell</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo docker cp nginx:/etc/nginx/conf.d/default.conf /home/username/docker-file/nginx/conf.d/default.conf
</span></span><span class="line"><span class="cl">sudo docker cp nginx:/etc/nginx/nginx.conf /home/username/docker-file/nginx/conf.d/nginx.conf
</span></span><span class="line"><span class="cl">sudo docker cp nginx:/etc/nginx/mime.types /home/username/docker-file/nginx/conf.d/mime.types</span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><p>基于原始的配置文件挂载后查看 nginx 是否正常运行：</p>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>shell</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo docker rm -f nginx
</span></span><span class="line"><span class="cl">sudo docker run --name nginx -p 80:80 -p 443:443 -v /home/username/docker-file/nginx/conf.d/nginx.conf:/etc/nginx/nginx.conf -v /home/username/docker-file/nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf -v /home/username/docker-file/nginx/conf.d/mime.types:/etc/nginx/mime.types -v /home/username/docker-file/nginx/cert:/etc/nginx/cert -d nginx</span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><p>参考资料：<a href="https://www.cnblogs.com/Aamir-Ye/p/16823739.html" target="_blank" rel="noopener">使用docker部署nginx并配置https - 逊老头 - 博客园 (cnblogs.com)</a>
</p>
<h2 id="域名解析及申请免费-ssl-证书">域名解析及申请免费 SSL 证书</h2>
<blockquote>
<p>账号应该是有50个 SSL 证书限额，但是注意每个二级域名都需要单独申请 SSL 证书，例如 aiken.Com 和 videos.Aiken.Com 需要分别申请 SSL 证书。</p>
</blockquote>
<h3 id="域名解析">域名解析</h3>
<p>注册完域名后到域名控制台，选择特定的域名进行解析：</p>
<p>
<div class="post-img-view">
  <a data-fancybox="gallery" href="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231107004308.png">
    <img alt="image.png" loading="lazy" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231107004308.png"class="responsive-image" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231107004308.png" style="display: block; margin: 0 auto;"
      alt="image.png"  />
  </a>
</div>


<script>
  document.addEventListener("DOMContentLoaded", function() {
      var images = document.querySelectorAll(".responsive-image");
      var maxHeight = window.innerHeight / 2.5;
      images.forEach(function(image) {
          image.style.maxHeight = maxHeight + "px";
      });
  });
</script>
</p>
<p>主机记录可以设置 <code>www</code> 等二级域名，@既是基础域名（没有二级域名），IPV6的话设置记录类型为 AAAA，记录值这里填入公网 ipv6 地址（基于安全性考虑 Ipv6 地址大多会动态变化，包括 windows 会提供临时 ipv6 地址等等，后续会使用 DDNS-go 动态更新该地址）</p>
<p>
<div class="post-img-view">
  <a data-fancybox="gallery" href="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/1699289330400.png">
    <img alt="1699289330400.png" loading="lazy" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/1699289330400.png"class="responsive-image" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/1699289330400.png" style="display: block; margin: 0 auto;"
      alt="1699289330400.png"  />
  </a>
</div>


<script>
  document.addEventListener("DOMContentLoaded", function() {
      var images = document.querySelectorAll(".responsive-image");
      var maxHeight = window.innerHeight / 2.5;
      images.forEach(function(image) {
          image.style.maxHeight = maxHeight + "px";
      });
  });
</script>
</p>
<h3 id="ssl-证书申请">SSL 证书申请</h3>
<p>进入 SSL 证书管理界面 -&gt; 申请免费证书 -&gt; 提交证书申请 -&gt;</p>
<p>
<div class="post-img-view">
  <a data-fancybox="gallery" href="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231107005919.png">
    <img alt="image.png" loading="lazy" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231107005919.png"class="responsive-image" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231107005919.png" style="display: block; margin: 0 auto;"
      alt="image.png"  />
  </a>
</div>


<script>
  document.addEventListener("DOMContentLoaded", function() {
      var images = document.querySelectorAll(".responsive-image");
      var maxHeight = window.innerHeight / 2.5;
      images.forEach(function(image) {
          image.style.maxHeight = maxHeight + "px";
      });
  });
</script>
</p>
<p>提交证书申请界面按照指引填写即可，域名部分记得二级域名也要单独申请即可。</p>
<p>
<div class="post-img-view">
  <a data-fancybox="gallery" href="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231107010036.png">
    <img alt="image.png" loading="lazy" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231107010036.png"class="responsive-image" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231107010036.png" style="display: block; margin: 0 auto;"
      alt="image.png"  />
  </a>
</div>


<script>
  document.addEventListener("DOMContentLoaded", function() {
      var images = document.querySelectorAll(".responsive-image");
      var maxHeight = window.innerHeight / 2.5;
      images.forEach(function(image) {
          image.style.maxHeight = maxHeight + "px";
      });
  });
</script>
</p>
<p>验证域名部分按照要求添加解析信息后点击验证域名即可，下载证书文件</p>
<p>
<div class="post-img-view">
  <a data-fancybox="gallery" href="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231107011115.png">
    <img alt="image.png" loading="lazy" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231107011115.png"class="responsive-image" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231107011115.png" style="display: block; margin: 0 auto;"
      alt="image.png"  />
  </a>
</div>


<script>
  document.addEventListener("DOMContentLoaded", function() {
      var images = document.querySelectorAll(".responsive-image");
      var maxHeight = window.innerHeight / 2.5;
      images.forEach(function(image) {
          image.style.maxHeight = maxHeight + "px";
      });
  });
</script>
</p>
<p>耐心等待证书签发后，在我的证书界面下载证书，并将证书文件（解压后）放到挂载的 nginx 的 cert 目录中</p>
<p>
<div class="post-img-view">
  <a data-fancybox="gallery" href="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231107011305.png">
    <img alt="image.png" loading="lazy" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231107011305.png"class="responsive-image" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231107011305.png" style="display: block; margin: 0 auto;"
      alt="image.png"  />
  </a>
</div>


<script>
  document.addEventListener("DOMContentLoaded", function() {
      var images = document.querySelectorAll(".responsive-image");
      var maxHeight = window.innerHeight / 2.5;
      images.forEach(function(image) {
          image.style.maxHeight = maxHeight + "px";
      });
  });
</script>
</p>
<h2 id="nginx-配置部署服务">Nginx 配置部署服务</h2>
<blockquote>
<p>不同服务的 Nginx 配置在细节上会有一些差别，如果基础的设置不对，可以 Google 以下 Server+Nginx 查看是否有相关的文档说明，该部分简单介绍以下基本的一些写法。</p>
</blockquote>
<p>为了最大化的利用域名和单机服务，通常而言我们希望仅仅使用一个域名（包括其二级域名和二级目录）能够分别部署单机上分布于多个端口的多个服务，这里简单分别介绍如何为不同服务部署二级域名和二级目录。</p>
<p>需要注意的是，不同服务在部署的时候有不同的要求，<strong>有些服务涉及到一些 url 的跳转等问题，不支持二级目录的域名反代</strong>，会导致功能异常，具体需要参见相关文档，这种时候就推荐使用二级域名进行服务代理。</p>
<p>同时配置过程中<strong>需要注意反斜杠不要缺失，使用正确</strong>，不同的反斜杠最终会导致转向地址的不一致，参考：<a href="https://blog.csdn.net/clonetx/article/details/122305089" target="_blank" rel="noopener">nginx 反向代理斜杠问题_clonetx的博客-CSDN博客</a>
</p>
<h3 id="证书配置部分">证书配置部分</h3>
<p>证书配置部分主要按照签发处的指引，会给一个基础的模板，比如腾讯云：<a href="https://cloud.tencent.com/document/product/400/35244?from_cn_redirect=1" target="_blank" rel="noopener">SSL 证书 Nginx 服务器 SSL 证书安装部署-证书安装-文档中心-腾讯云 (tencent.com)</a>
</p>
<h3 id="二级地址反代">二级地址反代：</h3>
<p>在挂载的 conf.d 目录下的 default.conf 中新增 server 如下：</p>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>conf</span>
    </div>
    <div class="code-content">
        
        <pre tabindex="0"><code class="language-conf" data-lang="conf">server {
     listen 443 ssl ;
     listen [::]:443 ssl;
     expires 12h;
     server_name metisy.cool;
     ssl_certificate /etc/nginx/cert/&lt;your domain&gt;.crt;
     ssl_certificate_key /etc/nginx/cert/&lt;your domain&gt;.key;
     ssl_session_timeout 5m;
     ssl_prefer_server_ciphers on;
    
    # FIXME: for safe reason disable this ---below---.
     location /dashboard/ {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Range $http_range;
        proxy_set_header If-Range $http_if_range;
        proxy_redirect off;
        proxy_pass &lt;url_local_server_address&gt;:&lt;port&gt;/;
        # the max size of file to upload
     }
}</code></pre>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><p>其中</p>
<ul>
<li>&lt;url_local_server_address&gt;填写本地的局域网 ip 例如192.168.11.23</li>
<li>port 则填写该 ip 对应设备上提供服务的端口，例如1234</li>
<li>ssl_certificate 和 ssl_certificate_key 填写 ssl 证书对应的 crt 和 key 文件的路径</li>
</ul>
<h3 id="二级域名反代">二级域名反代</h3>
<p>在挂载的 conf.D 目录下的 default.Conf 中新增 server 如下：</p>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>conf</span>
    </div>
    <div class="code-content">
        
        <pre tabindex="0"><code class="language-conf" data-lang="conf">server {
	listen 443 ssl ; 
	listen [::]:443 ssl;
	server_name {prefix_your domain}; 
	client_max_body_size 50000M;
	ssl_certificate /etc/nginx/cert/{prefix_your domain}.crt; 
	ssl_certificate_key /etc/nginx/cert/{prefix_your domain}.key; 
	ssl_session_timeout 5m;
	ssl_prefer_server_ciphers on;
	
	location / {
		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_set_header X-Forwarded-Protocol $scheme;
		proxy_set_header X-Forwarded-Host $http_host;
		proxy_buffering off;
		proxy_pass &lt;url_local_server_address&gt;:&lt;port&gt;/;
		#root /usr/share/nginx/html;
		#index  index.html index.htm;
	}
}</code></pre>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><p>因为二级域名可以相当于一个新的域名，所以只需要把证书和 server_name 修改为新域名即可</p>
<h3 id="http-跳转https">Http 跳转Https</h3>
<p>在挂载的 conf.D 目录下的 default.Conf 中修改最上面的 server 为如下：</p>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>conf</span>
    </div>
    <div class="code-content">
        
        <pre tabindex="0"><code class="language-conf" data-lang="conf">server {
    listen       80 default_server;
    listen  [::]:80 default_server;
    server_name  _;
    # rewrite ^(.*)$ https://metisy.cool:443/$1 permanent;
	return       301 https://$host$request_uri;
    #access_log  /var/log/nginx/host.access.log  main;

    # location / {
    #     root   /usr/share/nginx/html;
    #     index  index.html index.htm;
    # }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}</code></pre>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><p>使用 return(新) 或者 rewrite(旧) 命令，将针对该域名的 http 请求都重定向到 https 中。</p>
<h3 id="alist-的反向代理">Alist 的反向代理</h3>
<p>Alist 的反向代理参考如下文章：<a href="https://alist.nn.ci/zh/guide/install/reverse-proxy.html" target="_blank" rel="noopener">反向代理 | AList文档 (nn.ci)</a>
，如果不修改配置的话需要将其挂载在/根路径下，否则会导致出错，二级路径会导致无法正常运行，如果要挂载到子级目录/alist/下需要按照该文修改配置文件：<a href="https://alist.nn.ci/zh/faq/howto.html#%E5%A6%82%E4%BD%95%E5%AF%B9%E5%AD%90%E7%9B%AE%E5%BD%95%E8%BF%9B%E8%A1%8C%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86" target="_blank" rel="noopener">怎么做 | AList文档 (nn.ci)</a>
</p>
<p>此外，挂载完成后会发现由于 Nginx 缓存，会导致卡在登录界面无法进到主页面的问题，该问题可以通过在配置中取消缓存修复，修改 nginx 配置，添加该行：</p>
<p>
<div class="post-img-view">
  <a data-fancybox="gallery" href="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231107133129.png">
    <img alt="image.png" loading="lazy" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231107133129.png"class="responsive-image" src="https://picture-bed-001-1310572365.cos.ap-guangzhou.myqcloud.com/3070PC/20231107133129.png" style="display: block; margin: 0 auto;"
      alt="image.png"  />
  </a>
</div>


<script>
  document.addEventListener("DOMContentLoaded", function() {
      var images = document.querySelectorAll(".responsive-image");
      var maxHeight = window.innerHeight / 2.5;
      images.forEach(function(image) {
          image.style.maxHeight = maxHeight + "px";
      });
  });
</script>
</p>
<h3 id="immich-的反向代理">Immich 的反向代理</h3>
<p>为了安全性和诸多其他考虑，不支持二级目录，建议使用二级域名挂载到根目录，具体的配置参考：</p>
<ul>
<li><a href="https://immich.app/docs/administration/reverse-proxy" target="_blank" rel="noopener">Reverse Proxy | Immich</a>
</li>
</ul>
<h3 id="xiaoya-alist-的反向代理">xiaoya Alist 的反向代理</h3>
<p>这里提一嘴，xiaoya 的 alist 的挂载目前也仅支持挂载到根路径，因此使用一个二级域名，个人暂时没找到如何挂载到子路径下，欢迎指正，万分感谢。</p>
<p>而如果是 xiaoya 的 emby 的反向代理，则需要手动重新映射一下 strm 资源库目录下的播放直链，将其改为挂载小雅的公网地址，可以使用以下脚本批量修改：</p>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>shell</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">find /home/user-dockerfile-dir/emby_res/xiaoya -name <span class="s2">&#34;*.strm&#34;</span> -exec sed <span class="se">\-</span>i <span class="s2">&#34;s#http://{locahost}:{port}#https://{domain}/#g; s# #%20#g;     s#|#%7C#g&#34;</span> <span class="o">{}</span> <span class="se">\;</span></span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><p>这里的{localhost} {port} {domain}请自行修改成自己的对应地址，这样挂载出来的 emby 就可以在公网进行播放了。</p>
<h3 id="重启-nginx">重启 Nginx</h3>
<blockquote>
<p>完成上述所有对应的配置后，启动 Nginx，即可实现 https 访问对应的网站了。</p>
</blockquote>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>shell</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># 可以删除当前contrainer重新部署</span>
</span></span><span class="line"><span class="cl">docker rm -f nginx
</span></span><span class="line"><span class="cl">sudo docker run --name nginx -p 80:80 -p 443:443 -v /home/username/docker-file/nginx/conf.d/nginx.conf:/etc/nginx/nginx.conf -v /home/username/docker-file/nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf -v /home/username/docker-file/nginx/conf.d/mime.types:/etc/nginx/mime.types -v /home/username/docker-file/nginx/cert:/etc/nginx/cert -d nginx</span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><p>但是实际没必要那么麻烦，直接执行：</p>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>shell</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">docker restart nginx</span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><p>即可，观察容器是否正常运行，如果运行失败查看 log 看是否哪里配置出错。</p>
<p>参考资料：</p>
<ul>
<li><a href="https://zhang.ge/5054.html" target="_blank" rel="noopener">Nginx通过二级目录（路径）映射不同的反向代理，规避IP+端口访问 | 张戈博客 (zhang.ge)</a>
</li>
<li><a href="https://www.quanxiaoha.com/docker/docker-nginx-install-ssl.html" target="_blank" rel="noopener">Docker Nginx 配置安装 SSL 证书（支持 Https 访问） - 犬小哈教程 (quanxiaoha.com)</a>
</li>
<li><a href="https://syq.pub/archives/27/?scroll=comment-4" target="_blank" rel="noopener">基于网盘挂载的emby服务端并实现直链播放 - syqman折腾笔记</a>
 基于小雅的方式的话无需参考该文，仅需将 address 文档替换成公网连接即可。</li>
<li><a href="https://note.youdao.com/ynoteshare/index.html?id=34790ec69ccaf38687664e96e0d8e46d&amp;type=note&amp;_time=1699094094800" target="_blank" rel="noopener">小雅Emby反代</a>
</li>
<li><a href="https://baijiahao.baidu.com/s?id=1630860688840962591" target="_blank" rel="noopener">nginx反向代理关闭缓存 (baidu.com)</a>
</li>
<li><a href="https://community.home-assistant.io/t/reverse-proxy-using-nginx/196954" target="_blank" rel="noopener">Reverse proxy using NGINX - Community Guides - Home Assistant Community (home-assistant.io)</a>
</li>
</ul>
<h2 id="troubleshooting-问题解决">TroubleShooting 问题解决</h2>



  
  

<blockquote class="alert-blockquote alert-bug">
  <p class="alert-heading">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">
      <path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path>
    </svg>
    <span>Bug</span>
  </p>
  <p>访问域名 Nginx 显示 504 超时 error，直接使用局域网 ip 访问各个服务并无超时现象，各个服务运行正常，Nginx 使用默认页面也能正常访问。</p>

</blockquote>
<p>怀疑是由于 Docker、Windows、WSL2 更新之后网络的问题，导致 wsl2 可以访问各个局域网，但是 nginx 的 container 无法对各个局域网上的服务进行访问，因此采用 windows 宿主机直接安装 nginx 来避免此类网络问题，将 Nginx 添加到启动服务中，并将 WSL2 中的 nginx 配置迁移过来即可。</p>
<blockquote>
<p>WIN+R shell:starup 可打开启动文件夹，建立 nginx 的快捷方式后就可以在任务管理器中设置为启动项了。</p>
</blockquote>
<h2 id="fi">Fi</h2>
<p>本章就到这边，下一章节讲解由于 Ipv6 的动态特性，需要部署动态域名解析服务，这里以 ddns-go 为例，自动更新对应域名解析中的主机记录值。</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
