给 QNAP 文件分享外链套个壳

This entry is part 4 of 7 in the series homelab 历险记

QNAP 提供的在线服务 MyQNAPCloud 的下载速度有时真的非常令人捉急。为了方便他人下载我分享的文件,得提供一个允许公网访问 NAS 共享链接的方式。

基础设施改动

考虑到我现在 Homelab 的基础设施中已经有了 cloudflared,所以可以用它设置一个 tunnel,但是为了避免将 NAS 其他功能暴露到公网,还需要再用 Nginx 设置一个代理,拒绝所有访问非共享链接页面的请求:

location ^~ /share.cgi {
  proxy_pass https://qnap.lan:5001;
}

location ~* \.(js|svg|gif|png)$ {
  proxy_pass https://qnap.lan:5001;
}


location ^~ /shareLink/ {
  proxy_pass https://qnap.lan:5001;
}

location / {
  return 302 "https://zeeko.dev";
}

接下来,设置 Cloudflared 的 tunnel,将 Local Service 的地址改成这台 nginx 服务器。

上面之所以还配置了 Host Header 是因为我的 nginx 上配置的域名是给局域网专用的,跟公网域名不同,为了让 Nginx 能够正常识别请求,就需要让 Cloudflared 设置转发 Host Header。

UI 体验优化

设置好网络功能后,还需要优化一下 QNAP 管理页面的 UI,方便我复制公网链接。这里我选择使用油猴脚本来实现我的目的。

// ==UserScript==
// @name         My QNAP
// @namespace    https://qnap.lan/
// @version      2025-05-31
// @description  try to take over the QNAP!
// @author       You
// @match        https://qnap.lan/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=tampermonkey.net
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // Configuration for the observer
    const observerConfig = {
        childList: true,
        subtree: true,
        attributes: false,
        characterData: false
    };

    // Create an observer instance
    const observer = new MutationObserver(mutations => {
        requestIdleCallback(() => {
            // DEBUG: Get all label elements
            const allLabels = Array.from(document.querySelectorAll('label.x-form-item-label'));
            // DEBUG: Find Local IP label
            const localIPLabel = allLabels
            .find(it => it.innerText === 'Local IP');
            console.debug('[DEBUG] localIPLabel:', localIPLabel);
            if (localIPLabel == null) {
                console.warn('[DEBUG] Local IP label not found');
                return;
            }
            const localIP = localIPLabel.nextElementSibling.innerText.trim();
            console.debug('[DEBUG] localIP:', localIP);

            if(localIP == null || localIP === '') {
                console.warn('[DEBUG] localIP is null or empty');
                return;
            }

            const query = new URL(localIP).search;
            console.debug('[DEBUG] query:', query);

            // DEBUG: Find SmartShare label
            const smartShareLabel = allLabels
            .find(it => it.innerText === 'SmartShare');
            if (smartShareLabel == null) {
                console.warn('[DEBUG] SmartShare label not found');
                return;
            }
            const copyButton = smartShareLabel.nextElementSibling.querySelector('button');
            if (copyButton == null) {
                console.warn('[DEBUG] Copy button not found');
                return;
            }

            copyButton.addEventListener('click', () => {
                const shareUrl = 'https://smartshare.zeeko.dev/share.cgi' + query;
                navigator.clipboard.writeText(shareUrl);
            });

        });
    });

    // Start observing the document body
    observer.observe(document.body, observerConfig);

    // Cleanup observer when page unloads
    window.addEventListener('unload', () => {
        observer.disconnect();
    });
})();

启用上面的脚本后,就可以在 UI 上直接复制文件共享的公网链接:

最终效果

相比通过 QNAP 的云下载,速度要快了 10 倍。

homelab 历险记

Zerotier: No route to host OpenWRT 上的 zerotier 突然无法启动了

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

Index