Koofr WebDAV 初尝试

随橙想,开了 Tampermonkey WebDAV 同步把坚果云 1G 流量造完了,只好另谋生路——这一谋就搜到了这篇文章《Alist 事件后,我如何用 Deno 解决 Koofr WebDAV 在国内的访问难题

然而然而,我以为很简单的一个复制粘贴先后经历了:

  1. Deno Deploy 无法注册(第二天好了
  2. 链接代码无法部署;
  3. 让 ChatGPT 改,纯属添乱。

只好自己动手,注意这个代码对应的服务器地址应为 https://APP.ORG.deno.net/koofr

改完之后发现 Deno Deploy 早在上文发布之前就被墙了这是怎么回事呢!

const mapping: Record<string, string> = {
  "/anthropic": "api.anthropic.com",
  "/cohere": "api.cohere.ai",
  "/discord": "discord.com/api",
  "/fireworks": "api.fireworks.ai/inference",
  "/gemini": "generativelanguage.googleapis.com",
  "/groq": "api.groq.com/openai",
  "/huggingface": "api-inference.huggingface.co",
  "/koofr": "app.koofr.net/dav/Koofr",
  "/novita": "api.novita.ai",
  "/openai": "api.openai.com",
  "/openrouter": "openrouter.ai/api",
  "/portkey": "api.portkey.ai",
  "/telegram": "api.telegram.org",
  "/together": "api.together.xyz",
  "/xai": "api.x.ai",
};

Deno.serve(async (req: Request) => {
  const url = new URL(req.url);
  const hostname = url.hostname;
  const pathname = url.pathname;

  if (pathname === "/") {
    return new Response(null, { status: 404 });
  }
  if (pathname === "/robots.txt") {
    return new Response("User-agent: *\nDisallow: /", {
      headers: { "content-type": "text/plain" },
    });
  }

  let href = "";

  for (const key in mapping) {
    if (pathname.startsWith(key)) {
      href = req.url.replace(hostname + key, mapping[key]);
      break;
    }
  }

  if (href === req.url) {
    return new Response("Not Found", { status: 404 });
  }

  const reqHeaders = new Headers(req.headers);
  reqHeaders.set("host", new URL(href).host);
  reqHeaders.delete("x-forwarded-for");
  reqHeaders.delete("x-real-ip");
  reqHeaders.delete("forwarded");
  reqHeaders.delete("via");

  try {
    const res = await fetch(href, {
      headers: reqHeaders,
      method: req.method,
      body: req.body,
      redirect: "manual"
    });

    const resHeaders = new Headers(res.headers);
    resHeaders.set("x-content-type-options", "nosniff");
    resHeaders.delete("connection");
    resHeaders.delete("keep-alive");
    resHeaders.delete("proxy-authenticate");
    resHeaders.delete("proxy-authorization");
    resHeaders.delete("te");
    resHeaders.delete("trailers");
    resHeaders.delete("transfer-encoding");
    resHeaders.delete("upgrade");

    if (pathname.startsWith("/koofr") && req.method === "PROPFIND") {
      const type = res.headers.get("Content-Type") || "";
      if (res.ok && (type.includes("application/xml") || type.includes("text/xml"))) {
        resHeaders.delete("Content-Length");
        const text = await res.text();
        const body = text.replaceAll("/dav/Koofr/", "/koofr/");
        return new Response(body, {
          status: res.status,
          headers: resHeaders
        });
      }
    }

    return new Response(res.body, {
      status: res.status,
      headers: resHeaders
    });
  } catch (err) {
    return new Response("Upstream error", { status: 502 });
  }

});