<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[月光下的键盘声]]></title><description><![CDATA[深夜加班的浪漫]]></description><link>https://moyefu.cn</link><image><url>https://moyefu.cn/api/v2/objects/icon/0ndpj1sn2lvplli5nh.ico</url><title>月光下的键盘声</title><link>https://moyefu.cn</link></image><generator>Shiro (https://github.com/Innei/Shiro)</generator><lastBuildDate>Thu, 28 May 2026 18:29:08 GMT</lastBuildDate><atom:link href="https://moyefu.cn/feed" rel="self" type="application/rss+xml"/><pubDate>Thu, 28 May 2026 18:29:08 GMT</pubDate><language><![CDATA[zh-CN]]></language><item><title><![CDATA[aircrack-ng抓wifi握手包]]></title><description><![CDATA[<div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://moyefu.cn/posts/default/aircrack-ng">https://moyefu.cn/posts/default/aircrack-ng</a></blockquote><div><h1 id="aircrack-ngwifihttpswwwcnblogscomthammerp5157980html">aircrack-ng抓wifi握手包(<a href="https://www.cnblogs.com/thammer/p/5157980.html">原文</a>)</h1><hr/><h2 id="1-">1. 关闭影响进程</h2><pre class="language-bash lang-bash"><code class="language-bash lang-bash">airmon-ng check kill
</code></pre>
<p>将要进入监听模式的无线网卡断开它已连接的 AP</p><h2 id="2-">2. 查看无线网卡的名字</h2><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ifconfig
</code></pre>
<p>例如 <code>wlan0</code></p><h2 id="3-">3. 进入监听模式</h2><pre class="language-bash lang-bash"><code class="language-bash lang-bash">airmon-ng start wlan0
</code></pre>
<p>启动成功，<code>ifconfig</code> 后发现多了一个 <strong>mon0</strong></p><h2 id="4--wifi">4. 扫描附近 WiFi</h2><pre class="language-bash lang-bash"><code class="language-bash lang-bash">airodump-ng mon0
</code></pre>
<p>扫描后，终端显示为上下 2 个部分，按 <strong>空格键</strong> 可以启动/停止动态刷新，<strong>&#x27;a&#x27;</strong> 键可以调整显示视图，上下方向键可以选定一行方便查看。</p><p>其中默认视图中：</p><ul><li><strong>上半部分</strong>：扫描到的 AP 以及其属性，例如信号强度、通道、BSSID、ESSID 等</li><li><strong>下半部分</strong>：显示有哪些机器（对应于 STATE 栏，就是它的 MAC）连接到这个 AP（对应的就是 ESSID）</li></ul><h2 id="5--ap">5. 监听指定 AP</h2><p>执行下列命令要停止扫描，即停止步骤 4 的命令，因为扫描会不断切换 channel，而监听某个 AP 时，这个 AP 的 channel 是固定的。</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">airodump-ng --bssid AP&#x27;s mac -c channel -w capfile mon0
</code></pre>
<p>参数说明：</p><ul><li><code>--bssid</code>：指定监听哪个 AP（由 AP&#x27;s mac 指定）</li><li><code>-c</code>：指定这个 AP 的通道</li><li><code>-w</code>：捕获的包存入文件 capfile</li></ul><p><strong>注意选定 AP：</strong></p><p>步骤 4 下半部分的 BSSID 和 STATE 要都存在一个 MAC，意思是探测到一个 WiFi 的 BSSID，并且这个 WiFi 被一个或多个机器连接上（STATE 栏指示其 MAC）。</p><h2 id="6-">6. 强制重连，使握手包出现</h2><pre class="language-bash lang-bash"><code class="language-bash lang-bash">aireplay-ng -0 2 -a AP&#x27;s bssid -c phone&#x27;s mac mon0
</code></pre>
<table><thead><tr><th> 参数 </th><th> 说明 </th></tr></thead><tbody><tr><td> <code>-0</code> </td><td> 攻击次数 </td></tr><tr><td> <code>-a</code> </td><td> WiFi 的 BSSID </td></tr><tr><td> <code>-c</code> </td><td> 连上这个 WiFi 的手机的 MAC </td></tr><tr><td> <code>mon0</code> </td><td> 监听接口 </td></tr></tbody></table><p>攻击会导致手机和 WiFi 断开连接。</p><h3 id="">为什么要抓握手包？</h3><p>握手包里包含密码信息</p><h3 id="">握手过程</h3><p>假设是我的手机去连 WiFi，第一次肯定会要你输入密码：</p><ol start="1"><li>手机：我要连接你 WiFi</li><li>WiFi：你的密码</li><li>手机：这是我的密码</li><li>WiFi：检查密码，正确，建立连接；错误，滚。</li></ol><p>对于一个已经过密码验证的手机和 WiFi，它们建立连接后交换的数据只不过是一些上网数据，抓它们没用。要抓就要抓包含密码的握手包，怎么让一个已验证过密码的连接再次出现握手包？最快的方法就是把手机踢下线，然后手机会自动重连，或者用手机的人发现手机断网了，他就会去连一下，这时握手包就出现了。这也就是为什么要选定一个有机器连接上的 WiFi 来监听。</p><p>不久就会在步骤 5 那个终端的第一行的末尾出现&quot;抓到握手包&quot;。若没出现，过几分钟重复攻击。</p><h2 id="7-">7. 跑字典</h2><pre class="language-bash lang-bash"><code class="language-bash lang-bash">aircrack-ng capfile.cap -w passwd.lst
</code></pre>
<hr/></div><p style="text-align:right"><a href="https://moyefu.cn/posts/default/aircrack-ng#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://moyefu.cn/posts/default/aircrack-ng</link><guid isPermaLink="true">https://moyefu.cn/posts/default/aircrack-ng</guid><dc:creator><![CDATA[墨夜富]]></dc:creator><pubDate>Wed, 13 May 2026 03:27:52 GMT</pubDate></item><item><title><![CDATA[OTP/2FA 双重验证码/二步验证码获取工具 基于JS实现 在线工具+源码]]></title><description><![CDATA[<div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://moyefu.cn/posts/default/opt-or-2fa">https://moyefu.cn/posts/default/opt-or-2fa</a></blockquote><div><h1 id="otp2fa--js-">OTP/2FA 双重验证码/二步验证码获取工具 基于JS实现 在线工具+源码</h1><hr/><h2 id="">工具在线使用</h2><p>直接食用地址：<a href="https://2fa.moyefu.cn">https://2fa.moyefu.cn</a> 、 <a href="https://otp.moyefu.cn">https://otp.moyefu.cn</a></p><hr/><h2 id="html--totp">HTML 代码（基于 TOTP）</h2><pre class="language-html lang-html"><code class="language-html lang-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;双重验证码/二步验证码获取工具&lt;/title&gt;
    &lt;script src=&quot;https://cdn.bootcdn.net/ajax/libs/otpauth/9.1.4/otpauth.umd.js&quot;&gt;&lt;/script&gt;
    &lt;style&gt;
        body {
            text-align: center;
            width: 100%;
        }

        #progress {
            color: red;
            background-color: #ff0000;
            width: 100%;
            height: 2px;
            margin: 0;
            position: absolute;
            left: 0;
            top: 0;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;progress id=&quot;progress&quot; max=&quot;0&quot; value=&quot;0&quot; style=&quot;display: none;&quot;&gt;&lt;/progress&gt;
    &lt;p&gt;&lt;input type=&quot;text&quot; id=&quot;secret&quot; placeholder=&quot;请输入秘钥&quot; value=&quot;&quot;&gt;&lt;/p&gt;
    &lt;p&gt;当前验证码：&lt;span id=&quot;code&quot;&gt;请输入秘钥&lt;/span&gt;&lt;/p&gt;
    &lt;p&gt;双重密钥为：&lt;span id=&quot;secret2&quot;&gt;请输入秘钥&lt;/span&gt;&lt;/p&gt;
    &lt;p&gt;剩余的时间：&lt;span id=&quot;remainingTime&quot;&gt;&lt;/span&gt;&lt;/p&gt;
    &lt;p&gt; (请在倒计时结束前输入验证码完成登录或者验证)&lt;/p&gt;
&lt;/body&gt;
&lt;script&gt;
    const refreshTime = 30; // 刷新时间

    // 监听 #secret 变化
    document.getElementById(&#x27;secret&#x27;).addEventListener(&#x27;input&#x27;, function (e) {
        let secret = e.target.value;
        refreshCode(secret);
    });

    // 定时器 每秒触发
    setInterval(function () {
        let secret = document.getElementById(&#x27;secret&#x27;).value;
        if (!secret) {
            document.getElementById(&#x27;code&#x27;).innerText = &#x27;请输入秘钥&#x27;;
            document.getElementById(&#x27;secret2&#x27;).innerText = &#x27;请输入秘钥&#x27;;
            document.getElementById(&#x27;remainingTime&#x27;).innerText = &#x27;请输入秘钥&#x27;;
            document.getElementById(&#x27;code&#x27;).style.color = &#x27;black&#x27;;
            document.getElementById(&#x27;remainingTime&#x27;).style.color = &#x27;black&#x27;;
            document.getElementById(&#x27;progress&#x27;).style.display = &#x27;none&#x27;;
            return;
        }
        document.getElementById(&#x27;progress&#x27;).style.display = &#x27;inline-block&#x27;;
        
        // 获取当前的秒数 与 refreshTime 取余数
        let seconds = Math.floor(Date.now() / 1000) % refreshTime;
        seconds = refreshTime - seconds;
        document.getElementById(&#x27;remainingTime&#x27;).innerText = seconds;
        
        if (seconds &gt; 15) {
            // 设置 code 的颜色
            document.getElementById(&#x27;code&#x27;).style.color = &#x27;green&#x27;;
        } else if (seconds &gt; 10) {
            // 设置 code 的颜色
            document.getElementById(&#x27;code&#x27;).style.color = &#x27;blue&#x27;;
        } else {
            if (seconds % 2 === 0) {
                // 设置 code 的颜色
                document.getElementById(&#x27;code&#x27;).style.color = &#x27;red&#x27;;
                document.getElementById(&#x27;remainingTime&#x27;).style.color = &#x27;red&#x27;;
            } else {
                // 设置 code 的颜色
                document.getElementById(&#x27;code&#x27;).style.color = &#x27;black&#x27;;
                document.getElementById(&#x27;remainingTime&#x27;).style.color = &#x27;black&#x27;;
            }
        }
        
        if (seconds === refreshTime) {
            document.getElementById(&#x27;code&#x27;).innerText = &#x27;&#x27;;
            refreshCode(secret);
            return;
        }
    }, 1000)

    setInterval(function () {
        let flo = Date.now()
        // 提取后三位
        flo = flo.toString().substring(flo.toString().length - 3) / 1000;
        let seconds = Math.floor(Date.now() / 1000) % refreshTime;
        document.getElementById(&#x27;progress&#x27;).value = seconds + flo
    }, 50);

    /**
     * 刷新验证码
     */
    function refreshCode(secret) {
        // 如果 secret 为空
        if (!secret) return;
        try {
            var otpauth = new OTPAuth.TOTP({ secret: secret, encoding: &#x27;base32&#x27; });
        } catch (error) {
            document.getElementById(&#x27;code&#x27;).innerText = &#x27;无效的秘钥&#x27;;
            document.getElementById(&#x27;secret2&#x27;).innerText = &#x27;无效的秘钥&#x27;;
            return;
        }
        let code = otpauth.generate();
        document.getElementById(&#x27;code&#x27;).innerText = code;
        document.getElementById(&#x27;secret2&#x27;).innerText = secret;
        document.cookie = secret;
    }

    // 网页加载完成
    window.onload = function () {
        let secret = document.cookie;
        if (secret) {
            document.getElementById(&#x27;secret&#x27;).value = secret;
            // 设置 progress 的 max
            document.getElementById(&#x27;progress&#x27;).max = refreshTime;
            refreshCode(secret)
        }
    }
&lt;/script&gt;
&lt;/html&gt;
</code></pre>
<hr/><h2 id="">说明</h2><ul><li>一个文件即可搞定（此方法使用的是外部链接 JS，需要联网）</li></ul><h2 id="-js-">离线 JS 下载</h2><ul><li><a href="https://github.com/hectorm/otpauth">官网 Github</a> 丶<a href="https://www.bootcdn.cn/otpauth/">CDN 加速</a></li></ul><hr/><h2 id="ps">PS</h2><p>内容没有美化，只能简单使用。而且刷新周期是 30s，如果需要，自己在代码中修改 <code>refreshTime</code>。</p></div><p style="text-align:right"><a href="https://moyefu.cn/posts/default/opt-or-2fa#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://moyefu.cn/posts/default/opt-or-2fa</link><guid isPermaLink="true">https://moyefu.cn/posts/default/opt-or-2fa</guid><dc:creator><![CDATA[墨夜富]]></dc:creator><pubDate>Wed, 13 May 2026 01:42:22 GMT</pubDate></item><item><title><![CDATA[我的世界  Minecraft Java版 使用樱花穿透（SakuraFrp）联机教程]]></title><description><![CDATA[<div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://moyefu.cn/posts/default/MC-SakuraFrp">https://moyefu.cn/posts/default/MC-SakuraFrp</a></blockquote><div><h2 id="--minecraft-java---">我的世界  Minecraft Java 版 · 局域网联机完全指南</h2><hr/><h2 id="">一、什么是樱花穿透？</h2><p>樱花穿透（SakuraFrp）是一款免费的内网穿透工具，可以将您电脑上的 Minecraft 局域网服务器映射到公网，让外网的朋友也能通过 IP 地址直接连接进来。</p><h3 id="">核心优势</h3><ul><li><strong>免费额度</strong>：每日签到送流量，免费用户可用 2 条隧道</li><li><strong>操作简单</strong>：图形界面，无需命令行</li><li><strong>支持全版本</strong>：覆盖绝大部分局域网联机游戏</li><li><strong>多节点选择</strong>：国内多节点可选，延迟低</li><li><strong>稳定性好</strong>：比 P2P 方案更稳定，适合多人联机</li></ul><hr/><h2 id="">二、准备工作</h2><h3 id="21-">2.1 账号注册与实名认证</h3><p>访问樱花穿透官网：https://www.natfrp.com/</p><ol start="1"><li>点击右上角「注册账号」</li><li>填写用户名、密码、邮箱完成注册</li><li>登录后进入「用户」→「实名认证」</li><li>完成实名认证（需支付 1 元认证费）</li></ol><blockquote><p>⚠️ <strong>注意</strong>：未满 18 岁的用户无法使用樱花穿透服务。实名认证是必须的，否则无法创建隧道。</p></blockquote>
<h3 id="22--sakurafrp-">2.2 下载并安装 SakuraFrp 客户端</h3><ol start="1"><li>进入官网，点击顶部菜单「服务」→「软件下载」</li><li>根据您的操作系统（Windows/macOS/Linux）下载对应版本</li><li>运行安装程序，使用默认设置完成安装</li></ol><h3 id="23-">2.3 登录客户端</h3><ul><li>打开 SakuraFrp 软件</li><li>进入「设置」→「账户」→「访问密钥」</li><li>在官网「用户」→「用户信息」→「账户信息」中复制访问密钥</li><li>将密钥粘贴到客户端，点击「登录」</li></ul><hr/><h2 id="-mod-">三、联机详细步骤（无 Mod 方案）</h2><p>适用于所有玩家都是正版账号或皮肤站用户的情况。如果有离线玩家，请查看第四章安装辅助 Mod。</p><h3 id="31--minecraft-">3.1 开启 Minecraft 局域网世界</h3><ol start="1"><li>启动 HMCL 或其他启动器，进入游戏</li><li>进入要联机的存档</li><li>按 ESC 键 → 点击「对局域网开放」</li><li>设置「其他玩家」是否允许后，点击「创造一个局域网世界」</li><li>聊天框会显示「本地游戏已在端口 XXXXX 上开启」，记住这个端口号</li></ol><h3 id="32-">3.2 创建樱花穿透隧道</h3><ol start="1"><li>在 SakuraFrp 客户端中点击左上角的「+」按钮</li><li>选择「节点」：推荐选择物理距离最近的节点</li><li>隧道类型：选择「TCP 隧道」</li><li>本地 IP：填写「127.0.0.1」</li><li>本地端口：填写游戏聊天框显示的端口号（如果忘记，可在启动器日志查看）</li><li>隧道名称：随便填，如「Minecraft联机」</li><li>点击「创建」</li></ol><h3 id="33-">3.3 启动隧道并分享连接地址</h3><ul><li>在隧道列表中找到刚创建的隧道，点击「启动」按钮</li><li>打开「日志」页面，等待出现连接成功信息</li><li>日志中会显示类似「使用 &gt;&gt;frp-xxx.com:xxxxx&lt;&lt; 连接你的隧道」</li><li>复制该地址，发送给要一起玩的朋友</li></ul><blockquote><p>⚠️ <strong>重要提醒</strong>：发送地址时务必使用英文输入法！中文的标点符号（冒号、句号）会导致连接失败。</p></blockquote>
<hr/><h2 id="">四、联机详细步骤（有非正版玩家）</h2><p>如果联机的玩家中有使用离线账号（非正版）的朋友，需要安装辅助 Mod 来关闭正版验证，否则会出现「登录失败：无效会话」的错误。</p><h3 id="41--mod">4.1 安装辅助 Mod</h3><p>推荐使用 <strong>LAN World Plug-n-Play（mcwifipnp）</strong>，兼容性最好</p><ul><li>下载 Mod（支持 Forge/Fabric/Quilt/NeoForge 1.15.2 ~ 1.21.11）</li><li>将下载的 .jar 文件放入游戏 mods 文件夹</li><li>启动游戏，进入要联机的存档</li><li>按 ESC → 找到「对广域网开放」按钮并点击</li><li>在设置界面中找到「在线模式」选项，关闭它（允许离线玩家进入）</li><li>点击「对广域网开放」，聊天框会显示固定端口号</li></ul><h3 id="42-">4.2 创建隧道（步骤同上）</h3><p>将上一步获得的端口号填入樱花穿透客户端的「本地端口」字段，其他步骤与第三章完全相同。</p><blockquote><p>⚠️ <strong>警告</strong>：切换正版验证模式会改变玩家的 UUID，可能导致背包物品和玩家数据丢失！请务必先备份存档。</p></blockquote>
<hr/><h2 id="">五、朋友如何加入游戏</h2><ol start="1"><li>朋友也需要下载并启动 Minecraft Java 版</li><li>进入游戏后，点击「多人游戏」→「添加服务器」</li><li>在服务器地址栏粘贴您发送的 frp-xxx.com:xxxxx 地址</li><li>点击「完成」，然后选择该服务器加入</li><li>成功连接后，朋友就能看到您的世界了！</li></ol><hr/><h2 id="">六、常见问题解答</h2><h3 id="q1">Q1：提示「请检查本地服务是否可访问」怎么办？</h3><ul><li>确认您已开启局域网世界，游戏内能看到端口号提示</li><li>检查 SakuraFrp 中本地 IP 是否填写为 127.0.0.1</li><li>尝试将本地端口改为 25565（MC 默认端口）</li><li>确保您和朋友不在同一个局域网内（否则直接用局域网 IP 就行）</li></ul><h3 id="q2">Q2：连接成功后很卡/延迟高怎么解决？</h3><ul><li>尝试更换节点：选择和朋友同一运营商或更近的节点</li><li>检查主机性能：调试屏幕（F3）查看 ms ticks 是否超过 50ms</li><li>降低游戏设置：减少渲染距离和模拟距离</li><li>检查网络带宽：确保上行带宽充足（建议每人至少 1Mbps）</li><li>高峰期可能有限速，可尝试非高峰期联机</li></ul><h3 id="q3">Q3：樱花穿透免费版有什么限制？</h3><ul><li>限速 10 Mibps（带宽较小）</li><li>最多 2 条隧道</li><li>每月 5GiB 流量（每日签到可获得额外流量）</li><li>对于轻度联机（3-5人）基本够用</li></ul><h3 id="q4">Q4：联机时出现「登录失败：无效会话」？</h3><p>这是因为有玩家使用非正版（离线）账号，但服务器开启了正版验证。需要安装辅助 Mod 并关闭「在线模式」（详见第四章）。</p><h3 id="q5-srv-">Q5：如何设置 SRV 解析让地址更美观？</h3><p>SRV 解析可以让您使用类似 mc.example.nyat.app 的域名连接，无需输入端口号。具体设置方法可参考樱花穿透官方文档。</p><hr/><h2 id="">七、快速回顾</h2><p><strong>总流程：</strong></p><ol start="1"><li>注册樱花穿透账号 → 完成实名认证（1元）</li><li>下载安装 SakuraFrp 客户端 → 登录</li><li>进入 MC 存档 → ESC → 对局域网开放 → 记住端口号</li><li>SakuraFrp 创建 TCP 隧道 → 本地端口填上一步的端口号</li><li>启动隧道 → 复制日志中的 frp 地址 → 发给朋友</li><li>朋友在 MC 中添加服务器 → 粘贴地址 → 加入游戏</li></ol><hr/><p><strong>祝您和朋友联机愉快！</strong></p><p><em>教程整理自 SakuraFrp 官方文档 · <a href="https://www.natfrp.com/">https://www.natfrp.com/</a></em></p></div><p style="text-align:right"><a href="https://moyefu.cn/posts/default/MC-SakuraFrp#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://moyefu.cn/posts/default/MC-SakuraFrp</link><guid isPermaLink="true">https://moyefu.cn/posts/default/MC-SakuraFrp</guid><dc:creator><![CDATA[墨夜富]]></dc:creator><pubDate>Tue, 12 May 2026 08:50:35 GMT</pubDate></item><item><title><![CDATA[Mix Space + Shiro 博客容器部署指南-没有配置反代]]></title><description><![CDATA[<link rel="preload" as="image" href="https://my.ippure.com/v1/card"/><div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://moyefu.cn/posts/default/mix-space">https://moyefu.cn/posts/default/mix-space</a></blockquote><div><p>本文记录了使用 Docker Compose 部署 Mix Space 后端与 Shiro 前端的完整配置。</p>
<p><img src="https://my.ippure.com/v1/card" alt="访客IP信息卡片"/></p><h2 id="">项目概述</h2><ul><li><strong>后端</strong>: Mix Space Core v10.1.5</li><li><strong>前端</strong>: Shiro (latest)</li><li><strong>数据库</strong>: MongoDB 7</li><li><strong>缓存</strong>: Redis</li></ul><h2 id="">服务架构</h2><pre class=""><code class="">┌─────────────────────────────────────────────────────────┐
│                     Docker Network                      │
│                      (mx-space)                         │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐     │
│  │  Shiro  │  │mx-server│  │  Mongo  │  │  Redis  │     │
│  │  :2323  │  │  :2333  │  │  :27017 │  │  :6379  │     │
│  └─────────┘  └─────────┘  └─────────┘  └─────────┘     │
└─────────────────────────────────────────────────────────┘
</code></pre>
<h2 id="">配置文件</h2><h3 id="docker-composeyml">docker-compose.yml</h3><pre class="language-yaml lang-yaml"><code class="language-yaml lang-yaml">
services:
  app:
    container_name: mx-server
    image: innei/mx-server:10.1.5
    environment:
      - TZ=Asia/Shanghai
      - NODE_ENV=production
      - DB_HOST=mongo
      - REDIS_HOST=redis
      - ALLOWED_ORIGINS=*
      - JWT_SECRET=KdtosY6MGBMhPEBscTlkgFWQfJ8PZ4Mc4X6riEYrfDT
      - BETTER_AUTH_URL=http://192.168.1.100:2333
    volumes:
      - ./data/mx-space:/root/.mx-space
    ports:
      - &#x27;2333:2333&#x27;
    depends_on:
      - mongo
      - redis
    networks:
      - mx-space
    restart: unless-stopped
    healthcheck:
      test: [CMD, curl, -f, &#x27;http://127.0.0.1:2333/api/v2/ping&#x27;]
      interval: 1m30s
      timeout: 30s
      retries: 5
      start_period: 30s

  mongo:
    container_name: mongo
    image: mongo:7
    volumes:
      - ./data/db:/data/db
    networks:
      - mx-space
    restart: unless-stopped

  redis:
    image: redis:alpine
    container_name: redis
    volumes:
      - ./data/redis:/data
    healthcheck:
      test: [CMD-SHELL, &#x27;redis-cli ping | grep PONG&#x27;]
      start_period: 20s
      interval: 30s
      retries: 5
      timeout: 3s
    networks:
      - mx-space
    restart: unless-stopped

  shiro:
    container_name: shiro
    image: innei/shiro:latest
    environment:
      NEXT_PUBLIC_API_URL: http://192.168.1.100:2333/api/v2
      NEXT_PUBLIC_GATEWAY_URL: http://192.168.1.100:2333
      NEXT_PUBLIC_CLIENT_API_URL: http://192.168.1.100:2333/api/v2
      NEXT_SHARP_PATH: /usr/local/lib/node_modules/sharp
    volumes:
      - type: bind
        source: ./.env
        target: /app/.env
      - ./public:/app/public
    networks:
      - mx-space
    restart: always
    ports:
      - 2323:2323

networks:
  mx-space:
    driver: bridge
</code></pre>
<h3 id="env">.env</h3><pre class="language-env lang-env"><code class="language-env lang-env">NEXT_PUBLIC_API_URL=http://192.168.1.100:2333/api/v2
NEXT_PUBLIC_GATEWAY_URL=http://192.168.1.100:2333

TMDB_API_KEY=
GH_TOKEN=
</code></pre>
<h2 id="">服务说明</h2><table><thead><tr><th> 服务 </th><th> 容器名 </th><th> 镜像 </th><th> 端口 </th><th> 说明 </th></tr></thead><tbody><tr><td> mx-server </td><td> mx-server </td><td> innei/mx-server:10.1.5 </td><td> 2333 </td><td> 后端 API 服务 </td></tr><tr><td> shiro </td><td> shiro </td><td> innei/shiro:latest </td><td> 2323 </td><td> 前端博客页面 </td></tr><tr><td> mongo </td><td> mongo </td><td> mongo:7 </td><td> 27017 </td><td> 数据库 </td></tr><tr><td> redis </td><td> redis </td><td> redis:alpine </td><td> 6379 </td><td> 缓存服务 </td></tr></tbody></table><h2 id="">环境变量说明</h2><h3 id="">后端环境变量</h3><table><thead><tr><th> 变量名 </th><th> 说明 </th><th> 示例值 </th></tr></thead><tbody><tr><td> <code>DB_HOST</code> </td><td> MongoDB 主机地址 </td><td> mongo </td></tr><tr><td> <code>REDIS_HOST</code> </td><td> Redis 主机地址 </td><td> redis </td></tr><tr><td> <code>ALLOWED_ORIGINS</code> </td><td> 允许的跨域来源 </td><td> * </td></tr><tr><td> <code>JWT_SECRET</code> </td><td> JWT 密钥 </td><td> 随机字符串 </td></tr><tr><td> <code>BETTER_AUTH_URL</code> </td><td> 认证服务 URL </td><td> <a href="http://192.168.1.100:2333">http://192.168.1.100:2333</a> </td></tr></tbody></table><h3 id="">前端环境变量</h3><table><thead><tr><th> 变量名 </th><th> 说明 </th><th> 示例值 </th></tr></thead><tbody><tr><td> <code>NEXT_PUBLIC_API_URL</code> </td><td> API 地址（浏览器访问） </td><td> <a href="http://192.168.1.100:2333/api/v2">http://192.168.1.100:2333/api/v2</a> </td></tr><tr><td> <code>NEXT_PUBLIC_GATEWAY_URL</code> </td><td> 网关地址 </td><td> <a href="http://192.168.1.100:2333">http://192.168.1.100:2333</a> </td></tr><tr><td> <code>NEXT_PUBLIC_CLIENT_API_URL</code> </td><td> API 地址（服务端访问） </td><td> <a href="http://192.168.1.100:2333/api/v2">http://192.168.1.100:2333/api/v2</a> </td></tr></tbody></table><h2 id="">部署命令</h2><pre class="language-bash lang-bash"><code class="language-bash lang-bash"># 启动所有服务
docker compose up -d

# 查看服务状态
docker compose ps

# 查看日志
docker compose logs -f

# 停止服务
docker compose down
</code></pre>
<h2 id="">访问地址</h2><ul><li><strong>博客首页</strong>: <a href="http://192.168.1.100:2323">http://192.168.1.100:2323</a></li><li><strong>后端 API</strong>: <a href="http://192.168.1.100:2333/api/v2">http://192.168.1.100:2333/api/v2</a></li><li><strong>管理面板</strong>: <a href="http://192.168.1.100:2333/proxy/qaqdmin">http://192.168.1.100:2333/proxy/qaqdmin</a></li></ul><h2 id="">注意事项</h2><ol start="1"><li><strong>版本兼容性</strong>: Shiro 前端仅兼容 Mix Space Core 10.x 版本，不兼容 11.x/12.x</li><li><strong>数据库</strong>: 10.x 版本使用 MongoDB，11.x/12.x 版本使用 PostgreSQL</li><li><strong>环境变量</strong>: 前端需要同时配置 <code>NEXT_PUBLIC_API_URL</code> 和 <code>NEXT_PUBLIC_CLIENT_API_URL</code></li><li><strong>跨域配置</strong>: <code>ALLOWED_ORIGINS</code> 需要包含所有访问来源</li><li><strong>ip</strong>: 把<code>192.168.1.100</code>改成你的ip或者改回127.0.0.1 修改时注意<code>BETTER_AUTH_URL</code>环境变量需要改成你的后端地址+端口</li></ol><h2 id="">常见问题</h2><h3 id="typeerror-cannot-read-properties-of-undefined-reading-config">TypeError: Cannot read properties of undefined (reading &#x27;config&#x27;)</h3><p>这是版本不兼容导致的错误，请确保：</p><ul><li>后端版本为 10.x</li><li>数据库使用 MongoDB 而非 PostgreSQL</li></ul><h3 id="invalid-origin-">Invalid origin 错误</h3><p>--- 需要在 <code>ALLOWED_ORIGINS</code> 中添加访问的域名或 IP 地址。测试时推荐直接使用*允许全部 还有必须要端口号 127.0.0.1是不行的127.0.0.1:2333</p><hr/><p><em>最后更新: 2026-05-09</em></p></div><p style="text-align:right"><a href="https://moyefu.cn/posts/default/mix-space#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://moyefu.cn/posts/default/mix-space</link><guid isPermaLink="true">https://moyefu.cn/posts/default/mix-space</guid><dc:creator><![CDATA[墨夜富]]></dc:creator><pubDate>Sat, 09 May 2026 08:49:59 GMT</pubDate></item></channel></rss>