mirror of
https://codeberg.org/mayx/pages
synced 2026-01-01 23:43:41 +08:00
update
This commit is contained in:
401
2025/08/01/sw-proxy.html
Normal file
401
2025/08/01/sw-proxy.html
Normal file
@@ -0,0 +1,401 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
|
||||
<!-- Begin Jekyll SEO tag v2.8.0 -->
|
||||
<title>用Service Worker实现一个反向代理 | Mayx的博客</title>
|
||||
<meta name="generator" content="Jekyll v3.9.5" />
|
||||
<meta property="og:title" content="用Service Worker实现一个反向代理" />
|
||||
<meta name="author" content="mayx" />
|
||||
<meta property="og:locale" content="zh_CN" />
|
||||
<meta name="description" content="现代浏览器真是强大,可以替代一些服务器的功能了!" />
|
||||
<meta property="og:description" content="现代浏览器真是强大,可以替代一些服务器的功能了!" />
|
||||
<meta property="og:site_name" content="Mayx的博客" />
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="article:published_time" content="2025-08-01T00:00:00+08:00" />
|
||||
<meta name="twitter:card" content="summary" />
|
||||
<meta property="twitter:title" content="用Service Worker实现一个反向代理" />
|
||||
<meta name="google-site-verification" content="huTYdEesm8NaFymixMNqflyCp6Jfvd615j5Wq1i2PHc" />
|
||||
<meta name="msvalidate.01" content="0ADFCE64B3557DC4DC5F2DC224C5FDDD" />
|
||||
<meta name="yandex-verification" content="fc0e535abed800be" />
|
||||
<script type="application/ld+json">
|
||||
{"@context":"https://schema.org","@type":"BlogPosting","author":{"@type":"Person","name":"mayx"},"dateModified":"2025-08-01T00:00:00+08:00","datePublished":"2025-08-01T00:00:00+08:00","description":"现代浏览器真是强大,可以替代一些服务器的功能了!","headline":"用Service Worker实现一个反向代理","mainEntityOfPage":{"@type":"WebPage","@id":"/2025/08/01/sw-proxy.html"},"publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"https://avatars0.githubusercontent.com/u/17966333"},"name":"mayx"},"url":"/2025/08/01/sw-proxy.html"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
||||
<link rel="canonical" href="https://mabbs.github.io/2025/08/01/sw-proxy.html" />
|
||||
<link type="application/atom+xml" rel="alternate" href="/atom.xml" title="Mayx的博客" />
|
||||
<link rel="alternate" type="application/rss+xml" title="Mayx的博客(RSS)" href="/rss.xml" />
|
||||
<link rel="alternate" type="application/json" title="Mayx的博客(JSON Feed)" href="/feed.json" />
|
||||
<link rel="stylesheet" href="/assets/css/style.css?v=1767196818" />
|
||||
<!--[if !IE]> -->
|
||||
<link rel="stylesheet" href="/Live2dHistoire/live2d/css/live2d.css" />
|
||||
<!-- <![endif]-->
|
||||
<link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="Mayx的博客" />
|
||||
<link rel="webmention" href="https://webmention.io/mabbs.github.io/webmention" />
|
||||
<link rel="pingback" href="https://webmention.io/mabbs.github.io/xmlrpc" />
|
||||
<link rel="preconnect" href="https://summary.mayx.eu.org" crossorigin="anonymous" />
|
||||
<link rel="prefetch" href="https://www.blogsclub.org/badge/mabbs.github.io" as="image" />
|
||||
<link rel="blogroll" type="text/xml" href="/blogroll.opml" />
|
||||
<link rel="me" href="https://github.com/Mabbs" />
|
||||
<script src="/assets/js/jquery.min.js"></script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-ajaxtransport-xdomainrequest/1.0.3/jquery.xdomainrequest.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
<script>
|
||||
var lastUpdated = new Date("Thu, 01 Jan 2026 00:00:18 +0800");
|
||||
var BlogAPI = "https://summary.mayx.eu.org";
|
||||
</script>
|
||||
<script src="/assets/js/main.js"></script>
|
||||
<!--[if !IE]> -->
|
||||
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
<script async="async" src="https://www.googletagmanager.com/gtag/js?id=UA-137710294-1"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', 'UA-137710294-1');
|
||||
</script>
|
||||
|
||||
<script src="/assets/js/instant.page.js" type="module"></script>
|
||||
<!-- <![endif]-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!--[if !IE]> --><noscript><marquee style="top: -15px; position: relative;"><small>发现当前浏览器没有启用JavaScript,这不影响你的浏览,但可能会有一些功能无法使用……</small></marquee></noscript><!-- <![endif]-->
|
||||
<!--[if IE]><marquee style="top: -15px; position: relative;"><small>发现当前浏览器为Internet Explorer,这不影响你的浏览,但可能会有一些功能无法使用……</small></marquee><![endif]-->
|
||||
<div class="wrapper">
|
||||
<header class="h-card">
|
||||
<h1><a class="u-url u-uid p-name" rel="me" href="/">Mayx的博客</a></h1>
|
||||
|
||||
|
||||
<img src="https://avatars0.githubusercontent.com/u/17966333" fetchpriority="high" class="u-photo" alt="Logo" style="width: 90%; max-width: 300px; max-height: 300px;" />
|
||||
|
||||
|
||||
<p class="p-note">Mayx's Home Page</p>
|
||||
|
||||
<form action="/search.html">
|
||||
<input type="text" name="keyword" id="search-input-all" placeholder="Search blog posts.." /> <input type="submit" value="搜索" />
|
||||
</form>
|
||||
<br />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p class="view"><a class="u-url" href="/Mabbs/">About me</a></p>
|
||||
|
||||
<ul class="downloads">
|
||||
|
||||
<li style="width: 270px; border-right: none;"><a href="/MayxBlog.tgz">Download <strong>TGZ File</strong></a></li>
|
||||
|
||||
</ul>
|
||||
</header>
|
||||
<section class="h-entry">
|
||||
|
||||
<small><time class="date dt-published" datetime="2025-08-01T00:00:00+08:00">1 August 2025</time> - 字数统计:4085 - 阅读大约需要14分钟 - Hits: <span id="/2025/08/01/sw-proxy.html" class="visitors">Loading...</span></small>
|
||||
<h1 class="p-name">用Service Worker实现一个反向代理</h1>
|
||||
|
||||
<p class="view">by <a class="p-author h-card" href="//github.com/Mabbs">mayx</a></p>
|
||||
<div id="outdate" style="display:none;">
|
||||
<hr /><p>
|
||||
这是一篇创建于 <span id="outime"></span> 天前的文章,其中的信息可能已经有所发展或是发生改变。
|
||||
</p>
|
||||
</div>
|
||||
<script>
|
||||
daysold = Math.floor((new Date().getTime() - new Date("Fri, 01 Aug 2025 00:00:00 +0800").getTime()) / (24 * 60 * 60 * 1000));
|
||||
if (daysold > 90) {
|
||||
document.getElementById("outdate").style.display = "block";
|
||||
document.getElementById("outime").innerHTML = daysold;
|
||||
}
|
||||
</script>
|
||||
|
||||
<hr />
|
||||
|
||||
<b>AI摘要</b>
|
||||
<p id="ai-output">这篇文章介绍了作者如何利用Service Worker在现代浏览器中实现一个简单的反向代理功能,以提供博客的备份和离线访问。作者原本希望通过Service Worker在用户浏览器中运行一个Web服务器来存储博客副本,但发现 tar.gz 文件的处理需要第三方库且难以找到适用的解决方案,尤其是对于tar文件的处理。作者最终选择使用Service Worker作为反向代理,将请求转发到其他网站,如GeoCities风格的静态网站托管平台,实现了类似的效果。这个过程中,作者体验到了浏览器功能的强大,并认为Service Worker在离线场景中有更大的潜力,尽管在他的例子中并没有充分展示这种优势。</p>
|
||||
|
||||
<hr />
|
||||
|
||||
|
||||
|
||||
<ul><li><a href="#起因">起因</a></li><li><a href="#研究实现方案">研究实现方案</a></li><li><a href="#使用service-worker进行反向代理">使用Service Worker进行反向代理</a></li><li><a href="#感想">感想</a></li></ul>
|
||||
<hr />
|
||||
|
||||
|
||||
<main class="post-content e-content" role="main"><p>现代浏览器真是强大,可以替代一些服务器的功能了!<!--more--></p>
|
||||
<h1 id="起因">
|
||||
|
||||
|
||||
<a href="#起因"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 起因
|
||||
|
||||
|
||||
</h1>
|
||||
|
||||
<p>前段时间在和群友聊天的时候,提到了我博客的<a href="/2022/02/14/move.html">分发方案</a>,这么多年过去之后我已经在很多平台上<a href="/proxylist.html">分发</a>了我的博客,不过这只是多重冗余,并不算去中心化(虽然我也有向IPFS同步,不过IPFS还得pin,也不太可靠)……所以这么看来,我的博客似乎还不算极其可靠😂?但其实不完全是这样。因为除了向不同平台的分发,我的博客还有一个全文搜索的功能。更重要的是,之前做<a href="/2024/10/01/suggest.html">文章推荐功能</a>时,会把整个博客所有文章的文字存到访客浏览器的localStorage中。这么说来,只要有人访问了我博客的文章,他们的浏览器中就会保存一份我博客文章的完整文本副本。从这个角度看,可靠性应该算是相当高了吧? </p><p>
|
||||
不过我之前的分发方案里还记录了一点,在GitHub Pages以外的平台我还打包了一份全站生成后的代码,之所以要全站打包,也是希望我的博客能尽可能的分发,考虑到几乎所有的Linux发行版一定有tar,而不一定有zip,所以我最终打包成了tgz格式。如果能让访客下载这个全站打包好的副本,相比于浏览器里只存储了文章文字的全文数据,这应该是一个更好的备份方式吧?毕竟我的博客本身也是我的作品……所以这个压缩包到底有什么地方可以用到呢? </p><p>
|
||||
这时候我想起来,现代的浏览器功能已经非常强大了,甚至在浏览器里直接运行一个Web服务器也完全没问题。如果能让访客在浏览器里下载那个压缩包并运行一个Web服务器,那就相当于在他们本地设备上部署了一份我的博客副本。这样一来,除了我自己搭建的网站之外,这些访客的本地也运行着一个我的博客实例😆(当然,这份副本只有访客自己能看到)。</p>
|
||||
<h1 id="研究实现方案">
|
||||
|
||||
|
||||
<a href="#研究实现方案"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 研究实现方案
|
||||
|
||||
|
||||
</h1>
|
||||
|
||||
<p>想要在浏览器上运行Web服务器其实很简单,那就是使用Service Worker,它可以完全离线在浏览器上工作。格式的话和以前写过的Cloudflare Worker非常相似,毕竟Cloudflare Worker就是模仿Service Worker的方式运行啊😂,所以我要是想写Service Worker应该很简单。 </p><p>
|
||||
有了执行的东西之后就是存储,在Service Worker上存储可以用Cache Storage,用它的话不仅可以保存文件的内容,还可以保存响应头之类的东西,用来和Service Worker配合使用非常的方便,不过既然是Cache,它的可靠性就不能保证了,浏览器很可能在需要的时候清除缓存内容,所以相比之下用IndexedDB应该会更可靠一些。 </p><p>
|
||||
那么接下来就该处理我的tgz文件了,tgz的本质是tar文件被gzip压缩之后的东西。浏览器解压gzip倒是简单,可以用Compression Stream API,但它也只能处理gzip了……对于tar的处理似乎就必须用第三方库。而tar的库在网上搜了搜似乎很少,网上找了个<a href="https://github.com/gera2ld/tarjs">tarjs</a>库,文档写的也看不懂,⭐️也很少,看来是有这个需求的人很少啊,而且还要用现代JS那种开发方式,要用什么npm之类的。在<a href="/2025/07/24/screenshot.html">上一篇文章</a>我就说过我不是专门写前端的,对在自己电脑上安装Node.js之类的东西很反感。后来问AI也完全写不出能用的代码,估计这个功能还是太小众了……另外又想到除了这个问题之外还要处理网站更新的时候该怎么通知Service Worker之类乱七八糟的事情……所以只好作罢😅。</p>
|
||||
<h1 id="使用service-worker进行反向代理">
|
||||
|
||||
|
||||
<a href="#使用service-worker进行反向代理"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 使用Service Worker进行反向代理
|
||||
|
||||
|
||||
</h1>
|
||||
|
||||
<p>这么看来离线运行我的博客似乎有点麻烦,不过既然都研究了一下Service Worker,不如想想其他能做的事情……比如当作反向代理?虽然在浏览器上搞反向代理好像意义不是很大……但值得一试。我之前见过一个项目叫做<a href="https://github.com/EtherDream/jsproxy">jsproxy</a>,它是用Service Worker实现的正向代理,这给了我一些启发。我在之前研究分发方案的时候发现了一些模仿GeoCities的复古静态网站托管平台,比如<a href="https://neocities.org">Neocities</a>和<a href="https://nekoweb.org">Nekoweb</a>。它们需要通过网页或API才能上传网站,不太方便使用CI/CD的方式部署。但是我又觉得它们的社区很有意思,所以想用Service Worker的方式反代到我的网站,显得我的网站是部署在它们上面一样。 </p><p>
|
||||
这个做起来非常简单,其实就和我以前用<a href="/2021/03/02/workers.html#%E9%A6%96%E5%85%88%E7%BB%99%E8%87%AA%E5%B7%B1%E6%90%AD%E4%B8%AA%E5%8F%8D%E4%BB%A3">Cloudflare Worker搭建反代</a>几乎完全一样,遇到请求之后直接通过Fetch获取内容然后再返回就行,唯一不同的就是浏览器存在跨域策略,在跨域时只有对应网站存在合适的响应头才可以成功请求,还好我用的Pages服务大多都允许跨域。但是在我实际测试的时候发现这个允许跨域的等级不太一样,比如GitHub Pages的响应头里包含<code class="language-plaintext highlighter-rouge">Access-Control-Allow-Origin: *</code>,但是不允许OPTIONS方式请求,另外如果要修改请求头,在响应头里还要一一允许相应的请求头才行……当然对于这种问题解决起来很简单,就和我之前写的<a href="/2025/04/08/feed.html">订阅源预览</a>一样,用<a href="https://github.com/Zibri/cloudflare-cors-anywhere">cloudflare-cors-anywhere</a>搭建的CORS代理就可以,有了这个就可以轻松使用Service Worker反代其他网站了。 </p><p>
|
||||
当然对我来说其实有<code class="language-plaintext highlighter-rouge">Access-Control-Allow-Origin: *</code>就够了,我也不需要花里胡哨的请求方式,也不需要在请求头和请求体里加什么莫名其妙的东西,所以对我来说直接请求我的某一个镜像站就可以,于是代码如下: </p><p>
|
||||
<strong>index.html</strong></p>
|
||||
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><!DOCTYPE html></span>
|
||||
<span class="nt"><html></span>
|
||||
|
||||
<span class="nt"><head></span>
|
||||
<span class="nt"><meta</span> <span class="na">charset=</span><span class="s">"UTF-8"</span> <span class="nt">/></span>
|
||||
<span class="nt"><title></span>Mayx的博客<span class="nt"></title></span>
|
||||
<span class="nt"></head></span>
|
||||
|
||||
<span class="nt"><body></span>
|
||||
<span class="nt"><script></span>
|
||||
<span class="c1">// 注册 Service Worker</span>
|
||||
<span class="k">if</span> <span class="p">(</span><span class="dl">'</span><span class="s1">serviceWorker</span><span class="dl">'</span> <span class="k">in</span> <span class="nb">navigator</span><span class="p">)</span> <span class="p">{</span>
|
||||
<span class="nb">navigator</span><span class="p">.</span><span class="nx">serviceWorker</span><span class="p">.</span><span class="nx">register</span><span class="p">(</span><span class="dl">'</span><span class="s1">/sw.js</span><span class="dl">'</span><span class="p">)</span>
|
||||
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">registration</span> <span class="o">=></span> <span class="p">{</span>
|
||||
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">Service Worker 注册成功:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">registration</span><span class="p">.</span><span class="nx">scope</span><span class="p">);</span>
|
||||
<span class="c1">// 刷新网页</span>
|
||||
<span class="nx">location</span><span class="p">.</span><span class="nx">reload</span><span class="p">();</span>
|
||||
<span class="p">})</span>
|
||||
<span class="p">.</span><span class="k">catch</span><span class="p">(</span><span class="nx">error</span> <span class="o">=></span> <span class="p">{</span>
|
||||
<span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="dl">'</span><span class="s1">Service Worker 注册失败:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span>
|
||||
<span class="nx">location</span><span class="o">=</span><span class="dl">"</span><span class="s2">https://mabbs.github.io</span><span class="dl">"</span><span class="p">;</span>
|
||||
<span class="p">});</span>
|
||||
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
|
||||
<span class="nx">location</span><span class="o">=</span><span class="dl">"</span><span class="s2">https://mabbs.github.io</span><span class="dl">"</span><span class="p">;</span>
|
||||
<span class="p">}</span>
|
||||
<span class="nt"></script></span>
|
||||
<span class="nt"><h1></span>Redirecting<span class="ni">&hellip;</span><span class="nt"></h1></span>
|
||||
<span class="nt"><a</span> <span class="na">href=</span><span class="s">"https://mabbs.github.io"</span><span class="nt">></span>Click here if you are not redirected.<span class="nt"></a></span>
|
||||
<span class="nt"></body></span>
|
||||
|
||||
<span class="nt"></html></span>
|
||||
</code></pre></div></div>
|
||||
<p><strong>sw.js</strong></p>
|
||||
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">TARGET_SITE</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">被反代的网站</span><span class="dl">'</span><span class="p">;</span> <span class="c1">//也可以用CORS代理</span>
|
||||
|
||||
<span class="nb">self</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">install</span><span class="dl">'</span><span class="p">,</span> <span class="nx">event</span> <span class="o">=></span> <span class="p">{</span>
|
||||
<span class="c1">// 强制立即激活新 Service Worker</span>
|
||||
<span class="nx">event</span><span class="p">.</span><span class="nx">waitUntil</span><span class="p">(</span><span class="nb">self</span><span class="p">.</span><span class="nx">skipWaiting</span><span class="p">());</span>
|
||||
<span class="p">});</span>
|
||||
|
||||
<span class="nb">self</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">activate</span><span class="dl">'</span><span class="p">,</span> <span class="nx">event</span> <span class="o">=></span> <span class="p">{</span>
|
||||
<span class="c1">// 立即控制所有客户端</span>
|
||||
<span class="nx">event</span><span class="p">.</span><span class="nx">waitUntil</span><span class="p">(</span><span class="nb">self</span><span class="p">.</span><span class="nx">clients</span><span class="p">.</span><span class="nx">claim</span><span class="p">());</span>
|
||||
<span class="p">});</span>
|
||||
|
||||
<span class="nb">self</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">fetch</span><span class="dl">'</span><span class="p">,</span> <span class="nx">event</span> <span class="o">=></span> <span class="p">{</span>
|
||||
<span class="k">if</span> <span class="p">(</span><span class="k">new</span> <span class="nx">URL</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">.</span><span class="nx">url</span><span class="p">).</span><span class="nx">origin</span> <span class="o">==</span> <span class="nb">self</span><span class="p">.</span><span class="nx">location</span><span class="p">.</span><span class="nx">origin</span><span class="p">)</span> <span class="p">{</span>
|
||||
<span class="nx">event</span><span class="p">.</span><span class="nx">respondWith</span><span class="p">(</span><span class="nx">handleProxyRequest</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">));</span>
|
||||
<span class="p">}</span>
|
||||
<span class="p">});</span>
|
||||
|
||||
<span class="k">async</span> <span class="kd">function</span> <span class="nx">handleProxyRequest</span><span class="p">(</span><span class="nx">request</span><span class="p">)</span> <span class="p">{</span>
|
||||
<span class="k">try</span> <span class="p">{</span>
|
||||
<span class="c1">// 构建目标 URL</span>
|
||||
<span class="kd">const</span> <span class="nx">targetUrl</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">URL</span><span class="p">(</span><span class="nx">request</span><span class="p">.</span><span class="nx">url</span><span class="p">);</span>
|
||||
<span class="kd">const</span> <span class="nx">proxyUrl</span> <span class="o">=</span> <span class="nx">TARGET_SITE</span> <span class="o">+</span> <span class="nx">targetUrl</span><span class="p">.</span><span class="nx">pathname</span> <span class="o">+</span> <span class="nx">targetUrl</span><span class="p">.</span><span class="nx">search</span><span class="p">;</span>
|
||||
|
||||
<span class="c1">// 创建新请求(复制原请求属性)</span>
|
||||
<span class="kd">const</span> <span class="nx">proxyRequest</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Request</span><span class="p">(</span><span class="nx">proxyUrl</span><span class="p">,</span> <span class="p">{</span>
|
||||
<span class="na">method</span><span class="p">:</span> <span class="nx">request</span><span class="p">.</span><span class="nx">method</span><span class="p">,</span>
|
||||
<span class="c1">// headers: request.headers,</span>
|
||||
<span class="c1">// body: request.body</span>
|
||||
<span class="p">});</span>
|
||||
|
||||
<span class="c1">// 发送代理请求</span>
|
||||
<span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">proxyRequest</span><span class="p">);</span>
|
||||
|
||||
<span class="c1">// 返回修改后的响应</span>
|
||||
<span class="k">return</span> <span class="k">new</span> <span class="nx">Response</span><span class="p">(</span><span class="nx">response</span><span class="p">.</span><span class="nx">body</span><span class="p">,</span> <span class="p">{</span>
|
||||
<span class="na">status</span><span class="p">:</span> <span class="nx">response</span><span class="p">.</span><span class="nx">status</span><span class="p">,</span>
|
||||
<span class="na">statusText</span><span class="p">:</span> <span class="nx">response</span><span class="p">.</span><span class="nx">statusText</span><span class="p">,</span>
|
||||
<span class="na">headers</span><span class="p">:</span> <span class="nx">response</span><span class="p">.</span><span class="nx">headers</span>
|
||||
<span class="p">});</span>
|
||||
|
||||
<span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
|
||||
<span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="dl">'</span><span class="s1">Proxy error:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span>
|
||||
<span class="k">return</span> <span class="k">new</span> <span class="nx">Response</span><span class="p">(</span><span class="dl">'</span><span class="s1">Proxy failed</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span> <span class="na">status</span><span class="p">:</span> <span class="mi">500</span> <span class="p">});</span>
|
||||
<span class="p">}</span>
|
||||
<span class="p">}</span>
|
||||
</code></pre></div></div>
|
||||
<p>最终的实际效果: <a href="https://mayx.nekoweb.org">https://mayx.nekoweb.org</a></p>
|
||||
<h1 id="感想">
|
||||
|
||||
|
||||
<a href="#感想"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 感想
|
||||
|
||||
|
||||
</h1>
|
||||
|
||||
<p>虽然折腾了半天没能增强我博客的可靠性……但是体会到了现代浏览器的强大之处,难怪前几年会提出ChromeOS和PWA之类的东西,原来浏览器功能还是相当强大的,用了Service Worker以后即使是纯前端也可以有和使用服务器一样的体验,在过去的浏览器中要是想实现这样的功能……好像也不是不可能😂,用AJAX加服务器使用伪静态策略其实是可以做到的……其实Service Worker的功能更多还是在离线时使用的,我这个例子好像没体现它的优势😆。 </p><p>
|
||||
但总的来说相比以前想要实现这种反代的功能代码还是更清晰,也更简单了,也许以后如果有机会我又有心思让博客在访客浏览器上离线运行,那就可以体现Service Worker真正的优势了🤣。</p></main>
|
||||
|
||||
|
||||
<small style="display: block">tags: <a rel="category tag" class="p-category" href="/search.html?keyword=%E6%B5%8F%E8%A7%88%E5%99%A8"><em>浏览器</em></a> - <a rel="category tag" class="p-category" href="/search.html?keyword=Service%20Worker"><em>Service Worker</em></a> - <a rel="category tag" class="p-category" href="/search.html?keyword=Worker"><em>Worker</em></a> - <a rel="category tag" class="p-category" href="/search.html?keyword=%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86"><em>反向代理</em></a> <span style="float: right;"><a href="https://gitlab.com/mayx/mayx.gitlab.io/tree/master/_posts/2025-08-01-sw-proxy.md">查看原始文件</a></span></small>
|
||||
|
||||
|
||||
<h4 style="border-bottom: 1px solid #e5e5e5;margin: 2em 0 5px;">推荐文章</h4>
|
||||
<p id="suggest-container">Loading...</p>
|
||||
<script>
|
||||
var suggest = $("#suggest-container");
|
||||
$.get(BlogAPI + "/suggest?id=/2025/08/01/sw-proxy.html&update=" + lastUpdated.valueOf(), function (data) {
|
||||
if (data.length) {
|
||||
getSearchJSON(function (search) {
|
||||
suggest.empty();
|
||||
var searchMap = {};
|
||||
for (var i = 0; i < search.length; i++) {
|
||||
searchMap[search[i].url] = search[i];
|
||||
}
|
||||
|
||||
var tooltip = $('<div class="content-tooltip"></div>').appendTo('body').hide();
|
||||
for (var j = 0; j < data.length; j++) {
|
||||
var item = searchMap[data[j].id];
|
||||
if (item) {
|
||||
var link = $('<a href="' + item.url + '">' + item.title + '</a>');
|
||||
var contentPreview = item.content.substring(0, 100);
|
||||
if (item.content.length > 100) {
|
||||
contentPreview += "……";
|
||||
}
|
||||
link.hover(
|
||||
function(e) {
|
||||
tooltip.text($(this).data('content'))
|
||||
.css({
|
||||
top: e.pageY + 10,
|
||||
left: e.pageX + 10
|
||||
})
|
||||
.show();
|
||||
},
|
||||
function() {
|
||||
tooltip.hide();
|
||||
}
|
||||
).mousemove(function(e) {
|
||||
tooltip.css({
|
||||
top: e.pageY + 10,
|
||||
left: e.pageX + 10
|
||||
});
|
||||
}).data('content', contentPreview);
|
||||
|
||||
suggest.append(link);
|
||||
suggest.append(' - ' + item.date + '<br />');
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
suggest.html("暂无推荐文章……");
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<br />
|
||||
<div class="pagination">
|
||||
|
||||
<span class="prev">
|
||||
<a href="/2025/07/24/screenshot.html">
|
||||
上一篇:使用Cloudflare制作自动更新的网站预览图
|
||||
</a>
|
||||
</span>
|
||||
|
||||
<br />
|
||||
|
||||
<span class="next">
|
||||
<a href="/2025/08/10/tilde.html">
|
||||
下一篇:在Tilde社区的游玩体验
|
||||
</a>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
|
||||
<!--[if !IE]> -->
|
||||
<link rel="stylesheet" href="/assets/css/gitalk.css">
|
||||
<script src="/assets/js/gitalk.min.js"></script>
|
||||
|
||||
<div id="gitalk-container"></div>
|
||||
|
||||
<script>
|
||||
var gitalk = new Gitalk({
|
||||
clientID: '36557aec4c3cb04f7ac6',
|
||||
clientSecret: 'ac32993299751cb5a9ba81cf2b171cca65879cdb',
|
||||
repo: 'mabbs.github.io',
|
||||
owner: 'Mabbs',
|
||||
admin: ['Mabbs'],
|
||||
id: '/2025/08/01/sw-proxy', // Ensure uniqueness and length less than 50
|
||||
distractionFreeMode: false, // Facebook-like distraction free mode
|
||||
proxy: "https://cors-anywhere.mayx.eu.org/?https://github.com/login/oauth/access_token"
|
||||
})
|
||||
gitalk.render('gitalk-container')
|
||||
</script>
|
||||
<!-- <![endif]-->
|
||||
|
||||
</section>
|
||||
<!--[if !IE]> -->
|
||||
<div id="landlord" style="left:5px;bottom:0px;">
|
||||
<div class="message" style="opacity:0"></div>
|
||||
<canvas id="live2d" width="500" height="560" class="live2d"></canvas>
|
||||
<div class="live_talk_input_body">
|
||||
<form id="live_talk_input_form">
|
||||
<div class="live_talk_input_name_body" >
|
||||
<input type="checkbox" id="load_this" />
|
||||
<input type="hidden" id="post_id" value="/2025/08/01/sw-proxy.html" />
|
||||
<label for="load_this">
|
||||
<span style="font-size: 11px; color: #fff;"> 想问这篇文章</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="live_talk_input_text_body">
|
||||
<input name="talk" type="text" class="live_talk_talk white_input" id="AIuserText" autocomplete="off" placeholder="要和我聊什么呀?" />
|
||||
<button type="submit" class="live_talk_send_btn" id="talk_send">发送</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<input name="live_talk" id="live_talk" value="1" type="hidden" />
|
||||
<div class="live_ico_box" style="display:none;">
|
||||
<div class="live_ico_item type_info" id="showInfoBtn"></div>
|
||||
<div class="live_ico_item type_talk" id="showTalkBtn"></div>
|
||||
<div class="live_ico_item type_music" id="musicButton"></div>
|
||||
<div class="live_ico_item type_youdu" id="youduButton"></div>
|
||||
<div class="live_ico_item type_quit" id="hideButton"></div>
|
||||
<input name="live_statu_val" id="live_statu_val" value="0" type="hidden" />
|
||||
<audio src="" style="display:none;" id="live2d_bgm" data-bgm="0" preload="none"></audio>
|
||||
<input id="duType" value="douqilai" type="hidden" />
|
||||
</div>
|
||||
</div>
|
||||
<div id="open_live2d">召唤伊斯特瓦尔</div>
|
||||
<!-- <![endif]-->
|
||||
<footer>
|
||||
<p>
|
||||
<small>Made with ❤ by Mayx<br />Last updated at 2026-01-01 00:00:18<br /> 总字数:614622 - 文章数:178 - <a href="/atom.xml" >Atom</a> - <a href="/README.html" >About</a></small>
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
<script src="/assets/js/scale.fix.js"></script>
|
||||
<!--[if !IE]> -->
|
||||
<script src="/assets/js/main_new.js"></script>
|
||||
<script src="/Live2dHistoire/live2d/js/live2d.js"></script>
|
||||
<script src="/Live2dHistoire/live2d/js/message.js"></script>
|
||||
<!-- <![endif]-->
|
||||
</body>
|
||||
</html>
|
||||
365
2025/08/10/tilde.html
Normal file
365
2025/08/10/tilde.html
Normal file
@@ -0,0 +1,365 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
|
||||
<!-- Begin Jekyll SEO tag v2.8.0 -->
|
||||
<title>在Tilde社区的游玩体验 | Mayx的博客</title>
|
||||
<meta name="generator" content="Jekyll v3.9.5" />
|
||||
<meta property="og:title" content="在Tilde社区的游玩体验" />
|
||||
<meta name="author" content="mayx" />
|
||||
<meta property="og:locale" content="zh_CN" />
|
||||
<meta name="description" content="Tilde社区,如“家”一般的感受😝" />
|
||||
<meta property="og:description" content="Tilde社区,如“家”一般的感受😝" />
|
||||
<meta property="og:site_name" content="Mayx的博客" />
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="article:published_time" content="2025-08-10T00:00:00+08:00" />
|
||||
<meta name="twitter:card" content="summary" />
|
||||
<meta property="twitter:title" content="在Tilde社区的游玩体验" />
|
||||
<meta name="google-site-verification" content="huTYdEesm8NaFymixMNqflyCp6Jfvd615j5Wq1i2PHc" />
|
||||
<meta name="msvalidate.01" content="0ADFCE64B3557DC4DC5F2DC224C5FDDD" />
|
||||
<meta name="yandex-verification" content="fc0e535abed800be" />
|
||||
<script type="application/ld+json">
|
||||
{"@context":"https://schema.org","@type":"BlogPosting","author":{"@type":"Person","name":"mayx"},"dateModified":"2025-08-10T00:00:00+08:00","datePublished":"2025-08-10T00:00:00+08:00","description":"Tilde社区,如“家”一般的感受😝","headline":"在Tilde社区的游玩体验","mainEntityOfPage":{"@type":"WebPage","@id":"/2025/08/10/tilde.html"},"publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"https://avatars0.githubusercontent.com/u/17966333"},"name":"mayx"},"url":"/2025/08/10/tilde.html"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
||||
<link rel="canonical" href="https://mabbs.github.io/2025/08/10/tilde.html" />
|
||||
<link type="application/atom+xml" rel="alternate" href="/atom.xml" title="Mayx的博客" />
|
||||
<link rel="alternate" type="application/rss+xml" title="Mayx的博客(RSS)" href="/rss.xml" />
|
||||
<link rel="alternate" type="application/json" title="Mayx的博客(JSON Feed)" href="/feed.json" />
|
||||
<link rel="stylesheet" href="/assets/css/style.css?v=1767196818" />
|
||||
<!--[if !IE]> -->
|
||||
<link rel="stylesheet" href="/Live2dHistoire/live2d/css/live2d.css" />
|
||||
<!-- <![endif]-->
|
||||
<link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="Mayx的博客" />
|
||||
<link rel="webmention" href="https://webmention.io/mabbs.github.io/webmention" />
|
||||
<link rel="pingback" href="https://webmention.io/mabbs.github.io/xmlrpc" />
|
||||
<link rel="preconnect" href="https://summary.mayx.eu.org" crossorigin="anonymous" />
|
||||
<link rel="prefetch" href="https://www.blogsclub.org/badge/mabbs.github.io" as="image" />
|
||||
<link rel="blogroll" type="text/xml" href="/blogroll.opml" />
|
||||
<link rel="me" href="https://github.com/Mabbs" />
|
||||
<script src="/assets/js/jquery.min.js"></script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-ajaxtransport-xdomainrequest/1.0.3/jquery.xdomainrequest.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
<script>
|
||||
var lastUpdated = new Date("Thu, 01 Jan 2026 00:00:18 +0800");
|
||||
var BlogAPI = "https://summary.mayx.eu.org";
|
||||
</script>
|
||||
<script src="/assets/js/main.js"></script>
|
||||
<!--[if !IE]> -->
|
||||
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
<script async="async" src="https://www.googletagmanager.com/gtag/js?id=UA-137710294-1"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', 'UA-137710294-1');
|
||||
</script>
|
||||
|
||||
<script src="/assets/js/instant.page.js" type="module"></script>
|
||||
<!-- <![endif]-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!--[if !IE]> --><noscript><marquee style="top: -15px; position: relative;"><small>发现当前浏览器没有启用JavaScript,这不影响你的浏览,但可能会有一些功能无法使用……</small></marquee></noscript><!-- <![endif]-->
|
||||
<!--[if IE]><marquee style="top: -15px; position: relative;"><small>发现当前浏览器为Internet Explorer,这不影响你的浏览,但可能会有一些功能无法使用……</small></marquee><![endif]-->
|
||||
<div class="wrapper">
|
||||
<header class="h-card">
|
||||
<h1><a class="u-url u-uid p-name" rel="me" href="/">Mayx的博客</a></h1>
|
||||
|
||||
|
||||
<img src="https://avatars0.githubusercontent.com/u/17966333" fetchpriority="high" class="u-photo" alt="Logo" style="width: 90%; max-width: 300px; max-height: 300px;" />
|
||||
|
||||
|
||||
<p class="p-note">Mayx's Home Page</p>
|
||||
|
||||
<form action="/search.html">
|
||||
<input type="text" name="keyword" id="search-input-all" placeholder="Search blog posts.." /> <input type="submit" value="搜索" />
|
||||
</form>
|
||||
<br />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p class="view"><a class="u-url" href="/Mabbs/">About me</a></p>
|
||||
|
||||
<ul class="downloads">
|
||||
|
||||
<li style="width: 270px; border-right: none;"><a href="/MayxBlog.tgz">Download <strong>TGZ File</strong></a></li>
|
||||
|
||||
</ul>
|
||||
</header>
|
||||
<section class="h-entry">
|
||||
|
||||
<small><time class="date dt-published" datetime="2025-08-10T00:00:00+08:00">10 August 2025</time> - 字数统计:4432 - 阅读大约需要14分钟 - Hits: <span id="/2025/08/10/tilde.html" class="visitors">Loading...</span></small>
|
||||
<h1 class="p-name">在Tilde社区的游玩体验</h1>
|
||||
|
||||
<p class="view">by <a class="p-author h-card" href="//github.com/Mabbs">mayx</a></p>
|
||||
<div id="outdate" style="display:none;">
|
||||
<hr /><p>
|
||||
这是一篇创建于 <span id="outime"></span> 天前的文章,其中的信息可能已经有所发展或是发生改变。
|
||||
</p>
|
||||
</div>
|
||||
<script>
|
||||
daysold = Math.floor((new Date().getTime() - new Date("Sun, 10 Aug 2025 00:00:00 +0800").getTime()) / (24 * 60 * 60 * 1000));
|
||||
if (daysold > 90) {
|
||||
document.getElementById("outdate").style.display = "block";
|
||||
document.getElementById("outime").innerHTML = daysold;
|
||||
}
|
||||
</script>
|
||||
|
||||
<hr />
|
||||
|
||||
<b>AI摘要</b>
|
||||
<p id="ai-output">这篇文章介绍了作者在Tilde社区的体验,这是一类基于类Unix环境的公共服务器社区,类似于家目录,提供预装的软件、开发环境和公共服务,如聊天室、邮件、BBS论坛等,强调了社区的互动性和共享精神。作者通过申请、审核过程加入了几个社区,并详细描述了在这些社区中的个人主页、编程支持(如Gemini和Gopher协议)、博客发布、代码托管(Git支持)、CI/CD部署以及使用Git hooks自动化博客更新等功能。尽管作者受限于语言和工具使用体验,未能充分参与社区交流,但对社区学习新知识和丰富博客内容印象深刻。</p>
|
||||
|
||||
<hr />
|
||||
|
||||
|
||||
|
||||
<ul><li><a href="#起因">起因</a></li><li><a href="#什么是tilde社区">什么是Tilde社区</a></li><li><a href="#tilde社区的体验">Tilde社区的体验</a></li><li><a href="#使用git-hooks自动部署博客">使用Git hooks自动部署博客</a></li><li><a href="#感想">感想</a></li></ul>
|
||||
<hr />
|
||||
|
||||
|
||||
<main class="post-content e-content" role="main"><p>Tilde社区,如“家”一般的感受😝<!--more--></p>
|
||||
<h1 id="起因">
|
||||
|
||||
|
||||
<a href="#起因"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 起因
|
||||
|
||||
|
||||
</h1>
|
||||
|
||||
<p>在<a href="/2025/08/01/sw-proxy.html">上一篇文章</a>里,我说到给我的博客增加了不少网站<a href="/proxylist.html">镜像</a>,也在这个过程中发现了不少Git平台实例。顺便一提,我找到了个不错的<a href="https://github.com/ecosyste-ms/repos">仓库</a>,可以全网搜索各种Git平台实例。在这探索的过程中,我发现了一种神奇的社区——Tilde社区,体验之后感觉非常有意思,所以来分享一下。</p>
|
||||
<h1 id="什么是tilde社区">
|
||||
|
||||
|
||||
<a href="#什么是tilde社区"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 什么是Tilde社区
|
||||
|
||||
|
||||
</h1>
|
||||
|
||||
<p>Tilde社区之所以叫Tilde,是因为在类Unix系统(如Linux、BSD)中,波浪号(Tilde)“~”代表家目录。因此,Tilde社区就是基于类Unix系统环境,并且可以公共登录的服务器,又被称为<abbr title="public access unix systems">pubnixes</abbr>。一般这些社区的管理员会预装很多软件、开发环境以及一些公共服务,比如聊天室、邮件、BBS论坛等,这些构成了社区互动的基础。不过并不是所有类似这样提供Shell访问的公共服务器都可以被称作社区,比如知名的免费网站托管商<a href="https://www.serv00.com">Serv00</a>虽然也提供可以登录的FreeBSD服务器,并且在服务器上安装了非常多的工具和环境,从表面来看和Tilde社区提供的服务几乎一模一样,但是它少了一个很重要的东西,那就是社区,它的权限管理非常严格,不允许服务器的用户互相串门,也没有互相交流的平台,而且它的本质是商业服务(尽管是免费的),所以它不算Tilde社区。 </p><p>
|
||||
至于Tilde社区的加入方式,一般可以通过填写在线申请表、私信或发送邮件申请,有些比较有特色的社区会用SSH交互等方式。审核通过后,管理员就会在服务器上为你创建账户,即可获得属于自己的“家”,一般的Tilde社区在这个过程中不需要付一分钱,因为他们通常都是反商业化的,如果遇到了需要付钱才能激活账户的公共服务器,那就不是Tilde社区,即使它历史悠久,可能是别的什么东西😆。 </p><p>
|
||||
那么在哪里可以找到它们呢?有一个不错的网站,叫做<a href="https://tildeverse.org">tildeverse</a>,这不仅是一个Tilde社区的集合,它自身也提供了很多服务。不过总的来说各个社区之间也是互相独立的,tildeverse只是提供了一个平台让大家可以互相沟通,所以这个网站叫做“loose association”,就相当于博客中的博客圈一样。 </p><p>
|
||||
于是我在tildeverse的成员列表中随便挑选了几个Tilde社区提交了注册申请,过了一段时间申请通过了,那么接下来就来说说我在Tilde社区的体验吧。</p>
|
||||
<h1 id="tilde社区的体验">
|
||||
|
||||
|
||||
<a href="#tilde社区的体验"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> Tilde社区的体验
|
||||
|
||||
|
||||
</h1>
|
||||
|
||||
<p>虽然我加入了不少Tilde社区,不过各个社区提供的服务都差不多,首先最重要的就是个人主页,一般Tilde社区基本上都会提供一个像<code class="language-plaintext highlighter-rouge">~/public_html</code>这样的目录存放个人主页的网页文件,并且可以通过类似<code class="language-plaintext highlighter-rouge">example.com/~username</code>这样的地址访问,还有些社区会允许通过二级域名的方式访问,类似<code class="language-plaintext highlighter-rouge">username.example.com</code>这样,像我博客好多地方写的都是从根路径开始,就很适合用二级域名的方式。这些主页大多也支持使用PHP之类的网页,不过不像虚拟主机那样有个面板可以轻松安装扩展和切换版本,有些可能要自己写配置文件,有些可能要管理员才可以操作,毕竟是社区,所以不太注重用户体验。 </p><p>
|
||||
当然除了HTTP协议的个人主页,通常他们还可以创建一些Gemini协议和Gopher协议的个人主页,这些协议不支持普通浏览器访问,需要用<a href="https://github.com/rkd77/elinks">ELinks</a>之类的文本浏览器才能打开,这个浏览器甚至可以在终端里用鼠标操作😆。不过因为协议非常简单,所以内容也就只能整些文本内容了。 </p><p>
|
||||
除了个人主页外,一般还会提供编写博客的程序,比如<a href="https://github.com/cfenollosa/bashblog">bashblog</a>,用这个编写好之后就可以直接生成HTML网站,能直接发布到自己的主页上让别人访问。这个脚本还是纯Bash的,就和我当年的<a href="https://github.com/Mabbs/Mabbs.Project">Mabbs</a>一样,看起来还挺酷,当然功能上肯定比不上正经的静态博客生成器😆。 </p><p>
|
||||
当然博客是一方面,还可以写微博,他们一般提供一款叫<a href="https://github.com/buckket/twtxt">twtxt</a>的软件,用这个软件可以使用命令发微博,还能关注其他人,查看时间线,而且这还是去中心化的,可以跨服务器进行关注,感觉就和<a href="https://github.com/mastodon/mastodon">Mastodon</a>一样。 </p><p>
|
||||
除此之外作为社区当然就会有聊天室和论坛了,不过这些聊天室和BBS论坛通常不会像大多数人使用的那种通过Web或者图形界面来查看,而是纯文本的那种,比如论坛通常会用<a href="https://github.com/bbj-dev/bbj">Bulletin Butter & Jelly</a>,聊天室会用IRC,可以使用<a href="https://github.com/weechat/weechat">WeeChat</a>,只是我对IRC的印象不太好,在终端使用的IRC客户端没有一个使用体验好的😅,相比于其他在终端使用的软件,操作通常只需要一些快捷键,而且界面上通常会有提示,而IRC客户端就只能敲命令,而且还担心敲错了当成普通内容发出去……所以尽管我加入了Tilde社区,受限于聊天软件的使用体验以及我的英文水平,所以并不能和在服务器上的其他人聊天,没法参与到社区中,这么来看似乎我只能把Tilde社区当作普通的共享服务器来看待了😭。 </p><p>
|
||||
在Tilde社区中既然都是用类Unix系统,自然大都是会写程序的人,所以托管代码也很重要,不过因为大多Tilde社区的主机性能很垃圾,所以很多都不会提供Git平台服务,即使有可能也只会提供Gitea,像GitLab这种对服务器要求比较高的基本上就不会有了。但很多人可能对Git有误解,其实绝大多数情况下都不需要Git平台来托管代码,之所以用Gitea、GitLab的工具是因为它们有比较完整的用户管理以及代码协作能力,比如Issue和Wiki之类的,但是大多数人其实根本没必要用到这些功能,有问题发邮件就好了,像Linux的开发就完全没有用Gitea、GitLab之类的平台。所以在Tilde社区中托管代码非常简单,直接新建个文件夹,执行<code class="language-plaintext highlighter-rouge">git init --bare</code>,那就是个仓库,另外很多Tilde社区提供<a href="https://git.zx2c4.com/cgit/about/">cgit</a>方便让公众在网页上查看和克隆自己的仓库,一般只要放到<code class="language-plaintext highlighter-rouge">~/public_git</code>目录下就可以。至于自己如果想要提交代码,可以用<code class="language-plaintext highlighter-rouge">git remote add tilde ssh://example.com/~/public_git/repo.git</code>添加远程仓库,本地改完之后push上去就可以。 </p><p>
|
||||
不过用那些Git平台还有一个地方可能会用到,那就是CI/CD,直接用命令创建的仓库它可以做到CI/CD吗?其实是可以的,Git有hooks功能,如果想要类似CI/CD的功能就可以直接用post-receive这个钩子,提交完成之后就会执行这个脚本,所以接下来就讲讲我是如何用Git hooks在服务器上自动部署我的博客吧。</p>
|
||||
<h1 id="使用git-hooks自动部署博客">
|
||||
|
||||
|
||||
<a href="#使用git-hooks自动部署博客"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 使用Git hooks自动部署博客
|
||||
|
||||
|
||||
</h1>
|
||||
|
||||
<p>我的博客使用的是<a href="https://github.com/jekyll/jekyll">Jekyll</a>框架,这是一个使用Ruby编写的静态博客生成器。所以要想构建我的博客至少要有Ruby的环境,还好几乎所有的Tilde社区都预装了,不用担心环境的问题。 </p><p>
|
||||
不过Tilde社区一般不提供root权限,所以Ruby的包需要放到自己的目录下,比如可以执行这样的命令:</p>
|
||||
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bundle2.7 config <span class="nb">set</span> <span class="nt">--local</span> path <span class="s1">'/home/mayx/blog-env'</span>
|
||||
</code></pre></div></div>
|
||||
<p>然后再在我的仓库下执行<code class="language-plaintext highlighter-rouge">bundle2.7 install</code>就可以了。 </p><p>
|
||||
接下来就需要编写构建的脚本,这个倒是简单,直接用我的<a href="/deploy.sh">部署脚本</a>改改就行:</p>
|
||||
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
|
||||
<span class="nb">cd</span> /home/mayx/
|
||||
<span class="nb">rm</span> <span class="nt">-rf</span> public_html
|
||||
git <span class="nt">--work-tree</span><span class="o">=</span>/home/mayx/blog <span class="nt">--git-dir</span><span class="o">=</span>/home/mayx/blog.git checkout <span class="nt">-f</span>
|
||||
<span class="nb">cd </span>blog
|
||||
<span class="nb">mkdir </span>Mabbs
|
||||
curl <span class="nt">-L</span> <span class="nt">-o</span> Mabbs/README.md https://github.com/Mabbs/Mabbs/raw/main/README.md
|
||||
bundle2.7 <span class="nb">exec </span>jekyll build <span class="nt">-d</span> ../public_html
|
||||
<span class="nb">tar </span>czvf MayxBlog.tgz <span class="nt">--exclude-vcs</span> ../public_html/
|
||||
<span class="nb">mv </span>MayxBlog.tgz ../public_html/
|
||||
</code></pre></div></div>
|
||||
<p>写完之后把这个脚本放到仓库的<code class="language-plaintext highlighter-rouge">hooks/post-receive</code>下,然后加上执行权限就可以用了,以后每次push之后都会直接更新我在Tilde社区的主页,也就是我的镜像站。这样部署不像一般CI/CD还要额外装环境,直接使用提前装好的环境,构建速度会快不少。 </p><p>
|
||||
不过既然有机会构建了,我就可以把一些不支持构建的Pages用起来了,有些Forgejo实例支持Pages功能,但是仓库里只能包含构建后的代码,还有Bitbucket Cloud也是一样的问题,所以我可以把构建后的文件夹转为仓库,然后推送到这些Git平台上。 </p><p>
|
||||
考虑到我的网站每次构建基本上所有的页面都有改动,因此我不打算保留提交记录,所以我每次都会重新初始化git仓库,不过在我实际测试的时候,发现钩子触发的脚本执行<code class="language-plaintext highlighter-rouge">git init</code>的时候创建的是裸仓库……查了一下貌似是环境变量的问题,只要把<code class="language-plaintext highlighter-rouge">GIT_DIR</code>变量删掉就没问题了,以下是实际的代码:</p>
|
||||
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd</span> ../public_html/
|
||||
<span class="nb">unset </span>GIT_DIR
|
||||
git init
|
||||
git add <span class="nb">.</span>
|
||||
git commit <span class="nt">-m</span> <span class="s2">"update"</span>
|
||||
git remote add codeberg ssh://git@codeberg.org/mayx/pages.git
|
||||
git remote add gitgay ssh://git@git.gay/mayx/pages.git
|
||||
git remote add bitbucket ssh://git@bitbucket.org/unmayx/unmayx.bitbucket.io.git
|
||||
git push <span class="nt">-f</span> codeberg master
|
||||
git push <span class="nt">-f</span> gitgay master
|
||||
git push <span class="nt">-f</span> bitbucket master
|
||||
</code></pre></div></div>
|
||||
<p>除了这些Pages之外,还有一些平台只支持使用他们自己的软件上传网站代码,比如surge,既然我可以在构建的时候执行命令,那就顺带一起上传吧,比如我可以这样执行:</p>
|
||||
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/home/mayx/blog-env/node_modules/surge/bin/surge /home/mayx/public_html/ mayx.surge.sh
|
||||
</code></pre></div></div>
|
||||
<p>其实除了这个之外我还想上传到sourcehut pages,这个也需要用他们自己的软件上传,但是sourcehut pages的CSP太严格了,居然禁止脚本访问其他网站😭,这样我的文章点击计数、文章推荐、AI摘要之类乱七八糟的功能就全用不了了,所以只好作罢……</p>
|
||||
<h1 id="感想">
|
||||
|
||||
|
||||
<a href="#感想"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 感想
|
||||
|
||||
|
||||
</h1>
|
||||
|
||||
<p>总的来说,这次在Tilde社区的各种体验还挺有意思,虽然没能和各个社区的成员进行对话,但是在探索的过程中,也了解到了不少新知识,而且也给我的博客增加了不少镜像。不知道会不会有哪个社区成员在闲逛的时候看到我的博客然后对里面的内容感兴趣😝……要是有哪个成员看到然后给我评论,那也算是社区互动吧😋。虽然我的文章内容都是中文,但现在翻译软件也足够强大了,应该不至于拦住外国人。只是在国内似乎没有见过类似的社区,在国内也有的话,那就可以用中文和大家对话了吧。</p></main>
|
||||
|
||||
|
||||
<small style="display: block">tags: <a rel="category tag" class="p-category" href="/search.html?keyword=tilde"><em>tilde</em></a> - <a rel="category tag" class="p-category" href="/search.html?keyword=%E6%9C%8D%E5%8A%A1%E5%99%A8"><em>服务器</em></a> - <a rel="category tag" class="p-category" href="/search.html?keyword=git"><em>git</em></a> - <a rel="category tag" class="p-category" href="/search.html?keyword=%E4%BD%93%E9%AA%8C"><em>体验</em></a> <span style="float: right;"><a href="https://gitlab.com/mayx/mayx.gitlab.io/tree/master/_posts/2025-08-10-tilde.md">查看原始文件</a></span></small>
|
||||
|
||||
|
||||
<h4 style="border-bottom: 1px solid #e5e5e5;margin: 2em 0 5px;">推荐文章</h4>
|
||||
<p id="suggest-container">Loading...</p>
|
||||
<script>
|
||||
var suggest = $("#suggest-container");
|
||||
$.get(BlogAPI + "/suggest?id=/2025/08/10/tilde.html&update=" + lastUpdated.valueOf(), function (data) {
|
||||
if (data.length) {
|
||||
getSearchJSON(function (search) {
|
||||
suggest.empty();
|
||||
var searchMap = {};
|
||||
for (var i = 0; i < search.length; i++) {
|
||||
searchMap[search[i].url] = search[i];
|
||||
}
|
||||
|
||||
var tooltip = $('<div class="content-tooltip"></div>').appendTo('body').hide();
|
||||
for (var j = 0; j < data.length; j++) {
|
||||
var item = searchMap[data[j].id];
|
||||
if (item) {
|
||||
var link = $('<a href="' + item.url + '">' + item.title + '</a>');
|
||||
var contentPreview = item.content.substring(0, 100);
|
||||
if (item.content.length > 100) {
|
||||
contentPreview += "……";
|
||||
}
|
||||
link.hover(
|
||||
function(e) {
|
||||
tooltip.text($(this).data('content'))
|
||||
.css({
|
||||
top: e.pageY + 10,
|
||||
left: e.pageX + 10
|
||||
})
|
||||
.show();
|
||||
},
|
||||
function() {
|
||||
tooltip.hide();
|
||||
}
|
||||
).mousemove(function(e) {
|
||||
tooltip.css({
|
||||
top: e.pageY + 10,
|
||||
left: e.pageX + 10
|
||||
});
|
||||
}).data('content', contentPreview);
|
||||
|
||||
suggest.append(link);
|
||||
suggest.append(' - ' + item.date + '<br />');
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
suggest.html("暂无推荐文章……");
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<br />
|
||||
<div class="pagination">
|
||||
|
||||
<span class="prev">
|
||||
<a href="/2025/08/01/sw-proxy.html">
|
||||
上一篇:用Service Worker实现一个反向代理
|
||||
</a>
|
||||
</span>
|
||||
|
||||
<br />
|
||||
|
||||
<span class="next">
|
||||
<a href="/2025/09/01/quine.html">
|
||||
下一篇:关于ZIP Quine与自产生程序的探索
|
||||
</a>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
|
||||
<!--[if !IE]> -->
|
||||
<link rel="stylesheet" href="/assets/css/gitalk.css">
|
||||
<script src="/assets/js/gitalk.min.js"></script>
|
||||
|
||||
<div id="gitalk-container"></div>
|
||||
|
||||
<script>
|
||||
var gitalk = new Gitalk({
|
||||
clientID: '36557aec4c3cb04f7ac6',
|
||||
clientSecret: 'ac32993299751cb5a9ba81cf2b171cca65879cdb',
|
||||
repo: 'mabbs.github.io',
|
||||
owner: 'Mabbs',
|
||||
admin: ['Mabbs'],
|
||||
id: '/2025/08/10/tilde', // Ensure uniqueness and length less than 50
|
||||
distractionFreeMode: false, // Facebook-like distraction free mode
|
||||
proxy: "https://cors-anywhere.mayx.eu.org/?https://github.com/login/oauth/access_token"
|
||||
})
|
||||
gitalk.render('gitalk-container')
|
||||
</script>
|
||||
<!-- <![endif]-->
|
||||
|
||||
</section>
|
||||
<!--[if !IE]> -->
|
||||
<div id="landlord" style="left:5px;bottom:0px;">
|
||||
<div class="message" style="opacity:0"></div>
|
||||
<canvas id="live2d" width="500" height="560" class="live2d"></canvas>
|
||||
<div class="live_talk_input_body">
|
||||
<form id="live_talk_input_form">
|
||||
<div class="live_talk_input_name_body" >
|
||||
<input type="checkbox" id="load_this" />
|
||||
<input type="hidden" id="post_id" value="/2025/08/10/tilde.html" />
|
||||
<label for="load_this">
|
||||
<span style="font-size: 11px; color: #fff;"> 想问这篇文章</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="live_talk_input_text_body">
|
||||
<input name="talk" type="text" class="live_talk_talk white_input" id="AIuserText" autocomplete="off" placeholder="要和我聊什么呀?" />
|
||||
<button type="submit" class="live_talk_send_btn" id="talk_send">发送</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<input name="live_talk" id="live_talk" value="1" type="hidden" />
|
||||
<div class="live_ico_box" style="display:none;">
|
||||
<div class="live_ico_item type_info" id="showInfoBtn"></div>
|
||||
<div class="live_ico_item type_talk" id="showTalkBtn"></div>
|
||||
<div class="live_ico_item type_music" id="musicButton"></div>
|
||||
<div class="live_ico_item type_youdu" id="youduButton"></div>
|
||||
<div class="live_ico_item type_quit" id="hideButton"></div>
|
||||
<input name="live_statu_val" id="live_statu_val" value="0" type="hidden" />
|
||||
<audio src="" style="display:none;" id="live2d_bgm" data-bgm="0" preload="none"></audio>
|
||||
<input id="duType" value="douqilai" type="hidden" />
|
||||
</div>
|
||||
</div>
|
||||
<div id="open_live2d">召唤伊斯特瓦尔</div>
|
||||
<!-- <![endif]-->
|
||||
<footer>
|
||||
<p>
|
||||
<small>Made with ❤ by Mayx<br />Last updated at 2026-01-01 00:00:18<br /> 总字数:614622 - 文章数:178 - <a href="/atom.xml" >Atom</a> - <a href="/README.html" >About</a></small>
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
<script src="/assets/js/scale.fix.js"></script>
|
||||
<!--[if !IE]> -->
|
||||
<script src="/assets/js/main_new.js"></script>
|
||||
<script src="/Live2dHistoire/live2d/js/live2d.js"></script>
|
||||
<script src="/Live2dHistoire/live2d/js/message.js"></script>
|
||||
<!-- <![endif]-->
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user