更新
一次偶遇 昨天在咱的 让图片飞起来 oh-my-webp.sh ! 收到了 ChrAlpha 大佬的回复,咱就拜访了一下大佬的博客😂,无意间发现 Cloudflare Worder 免费搭建镜像站 这篇博客。于是呐,咱也想着能不能玩一玩这个 Workers 。虽然之前听说过有用 Workers 做很多好玩的事儿,比如加速网站、代理 Google 镜像站什么的。不过这些对于咱来说不是很刚需就没有折腾。刚好咱的 telegram 电报频道 RSS Kubernetes 人也比较多了,为了提高一点影响力,咱就想着能不能把频道的预览界面 t.me/s/rss_kubernetes 反代到咱域名上。虽然之前尝试使用 nginx 进行反代,但是效果不尽人意,于是当时就弃坑了。在春节的时候咱也看到过有人反代 2019-nCoV疫情实时播报🅥 ,比如 2019ncov.ga ,不过当时那个项目折腾起来也是很麻烦,咱这种菜鸡还是溜了溜了😂。直到今天看到 Cloudflare Worder 免费搭建镜像站 这篇博客后就心血来潮,就搞一搞吧😂
劝退三一连 首先你要有个 Cloudflare 账户,这是必须的。关于 Cloudflare 的注册咱就不多说啦,不过咱倒是建议大家伙把域名的 DNS 解析放到 Cloudflare 上来,好处多多:有把 https 小绿锁、免费的 加速 减速 CDN (墙内)、域名访问统计等等可玩性比较高😋。需要注意的是 Cloudflare 的 Worker 一天 10 万次免费额度,也够咱喝一壶的啦,不用担心不够用。
新建 Worker 登录到 Cloudflare 的大盘面板,点击左上角的 Menu
—-> Workers
进入到 Workers 页面。新注册的用户会提示设置一个 workers.dev
顶级域名下的二级子域名,这个子域名设置好之后是不可更改的,之后你新创建的 Worker 就会使以这个域名而二级子域名开始的,类似于 WorkerName.yousetdomain.workers.dev
。yousetdomain
就是你要设置的二级子域名,WorkerName
可以自定义,默认是随机生成的。也可以给自己的域名添加一条 CNAME 到 WorkerName.yousetdomain.workers.dev
,这样使用自己的域名就可以访问到 Worker
了。
设置好二级子域名之后选择 free 套餐计划,然后进入到 Worker 管理界面,创建一个新的 Worker
然后在 Script
输入框里填入以下代码。俗话说代码写得好,同行抄到老 ,咱的 worker.js
代码是参考自 Workers-Proxy ,不过咱去掉了一些无关紧要的代码,原代码是加入了辨别移动设备适配域名、屏蔽某些 IP 和国家的功能。对于咱的 telegram 频道镜像站来说,这都是多余的,于是就去掉了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 const upstream_me = 't.me' ;const upstream_org = 'telegram.org' ;const upstream_path = '/s/rss_kubernetes' ;const https = true ;const replace_dict = { $upstream: '$custom_domain' }; addEventListener('fetch' , event => { event.respondWith(fetchAndApply(event.request)); }); async function fetchAndApply (request ) { let response = null ; let url = new URL(request.url); let url_hostname = url.hostname; if (https == true ) { url.protocol = 'https:' ; } else { url.protocol = 'http:' ; } var upstream_domain = upstream_me; let pathname = url.pathname; console .log(pathname); if (pathname.startsWith('/static' )) { console .log('here' ); upstream_domain = upstream_org; url.pathname = pathname.replace('/static' , '' ); } else { if (pathname == '/' ) { url.pathname = upstream_path; } else { url.pathname = upstream_path + url.pathname; } } url.host = upstream_domain; let method = request.method; let request_headers = request.headers; let new_request_headers = new Headers(request_headers); new_request_headers.set('Host' , url.hostname); new_request_headers.set('Referer' , url.hostname); let original_response = await fetch(url.href, { method: method, headers: new_request_headers }); let response_headers = original_response.headers; let status = original_response.status; response = new Response(original_response.body, { status, headers: response_headers }); let text = await response.text(); let modified = text.replace( /telegram.org/g, 'tg.k8s.li/static' ); return new Response(modified, { status: response.status, statusText: response.statusText, headers: response.headers }); }
需要注意的是,像 t.me
域名下的站点,比如我的 https://t.me/s/rss_kubernetes
,它的 js 和 css 样式文件是使用的 telegram.org 域名。所以我们需要在 replace_dict
那里定义好替换的正则表达式,将 https://t.me/s/rss_kubernetes
页面里的 telegram.org
同样进行反代才行,这需要为 telegram 建一个单独的 Worker 😑。这也是评论区 ChrAlpha 小伙伴提到的办法。
1 2 3 4 5 6 7 8 ubuntu@blog:~$ curl https://t.me/s/rss_kubernetes | grep "<script src =" % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 141k 100 141k 0 0 85287 0 0:00:01 0:00:01 --:--:-- 85237 <script src=" //telegram.org /js /jquery.min.js "></script > <script src ="//telegram.org/js/jquery-ui.min.js" > </script > <script src ="//telegram.org/js/widget-frame.js?29" > </script > <script src ="//telegram.org/js/telegram-web.js?8" > </script >
修改下处代码为,将https://t.me/s/rss_kubernetes
页面里的 telegram.org
同样进行一次反代。这样访问到 https://t.me/s/rss_kubernetes
页面时,把的 telegram.org 替换为另一个 Worker 的域名,比如我的 telegram.k8srss.workers.dev
。不过像频道里的图片、文件、视频等资源 telegram 是使用的 CDN ,而且有好几个域名……这点很僵硬,暂时找不到合适的办法。貌似一个 Worker 只能反代一个域名?如果汝有合适的办法,欢迎与咱交流,咱感激不尽😋
1 let modified = text.replace(/telegram.org/g , "telegram.k8srss.workers.dev" )
这样再使用 curl 访问测试一下,原来的 telegram.org 已经全部替换成 telegram.k8srss.workers.dev 了😂。现在墙内用户也可以无痛访问啦。在此感谢 ChrAlpha 小伙伴😂提出宝贵的建议。
1 2 3 4 5 6 7 8 </main > <script src ="//telegram.k8srss.workers.dev/js/jquery.min.js" > </script > <script src ="//telegram.k8srss.workers.dev/js/jquery-ui.min.js" > </script > <script src ="//telegram.k8srss.workers.dev/js/widget-frame.js?29" > </script > <script src ="//telegram.k8srss.workers.dev/js/telegram-web.js?8" > </script >
这个文本替换功能很好玩儿,在 Cloudflare 官方的博客里还有个 demo introducing-cloudflare-workers 。使用这个功能咱有解锁了一个玩具,稍后再讲😂。
Here is a worker which performs a site-wide search-and-replace, replacing the word “Worker” with “Minion”. Try it out on this blog post.
剽窃修改好代码之后点击左下角的 Save and Deploy
然后 Preview 看看页面是否显示正常,如果显示正常恭喜你成功啦。
如果你想使用这种办法反代其他频道,只需要把 const upstream_path = '/s/rss_kubernetes'
后的 rss_kubernetes 替换为你想要代理的 telegram 频道 username 即可。之所以加上 upstream_path
而不反代整个 t.me
是为了防止别人滥用,毕竟 10W 次不经薅😂
改进 周四晚上睡觉前在推特上发了个推文,向大家请教了一下之前一个 Worker 里只能反代一个域名的问题。第二天 @Echowxsy 就回复咱了,而且还特意注册了 CouldFare 账号使用 Workers 帮咱测试了一下。在此非常感谢 @Echowxsy 帮咱。按照 @Echowxsy 小伙伴所说的:
我没用过 CloudFlare,不过我看了一下你的blog,貌似可以用两个upstream实现这个功能。 在modified那里替换为当前worker的地址,然后在后面加上一个不会重复的路径,例如xxx。 然后在fetchAndApply里面判断,如果当前请求的pathname=/xxx,使用upstream2,否则使用upstream1。 理论上是可以实现的。
就是一个 Workers 可以做很多事情,他实际上就是 Node.js 代码。 然后这里是将 http://telegram.org/xxxx 映射到 http://tg.k8s.li/static/xxxx 。 然后在 Workers 里面判断,如果有 /static
则从 http://telegram.org 获取,否则从 http://t.me 获取。
此处引用
@Echowxsy 的 推文
Woerker 首先我们获取完要反代的 https://t.me/s/rss_kubernetes
页面 html 源码,然后使用 replace 函数把 telegram.org 以及cdn[1-5].telesco.pe 等域名进行替换,替换为 /static 、/cdn[1-5] 等不同的 url 路径。然后将修改后的 html 页面返回给客户端。客户端客户端在请求 /static
时 Worker 就会去 http://telegram.org 获取相应的资源返回给用户。这样就实现了一个 Worker 反代多个域名骚操作。修改后的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 const upstream_me = 't.me' ;const upstream_org = 'telegram.org' ;const upstream_path = '/s/rss_kubernetes' ;const https = true ;const replace_dict = { $upstream: '$custom_domain' }; addEventListener('fetch' , event => { event.respondWith(fetchAndApply(event.request)); }); async function fetchAndApply (request ) { let response = null ; let url = new URL(request.url); let url_hostname = url.hostname; if (https == true ) { url.protocol = 'https:' ; } else { url.protocol = 'http:' ; } var upstream_domain = upstream_me; let pathname = url.pathname; console .log(pathname); if (pathname.startsWith('/static' )) { console .log('here' ); upstream_domain = upstream_org; url.pathname = pathname.replace('/static' , '' ); } else { if (pathname == '/' ) { url.pathname = upstream_path; } else { url.pathname = upstream_path + url.pathname; } } url.host = upstream_domain; let method = request.method; let request_headers = request.headers; let new_request_headers = new Headers(request_headers); new_request_headers.set('Host' , url.hostname); new_request_headers.set('Referer' , url.hostname); let original_response = await fetch(url.href, { method: method, headers: new_request_headers }); let original_response_clone = original_response.clone(); let response_headers = original_response.headers; let new_response_headers = new Headers(response_headers); let status = original_response.status; response = new Response(original_response_clone.body, { status, headers: new_response_headers }); let text = await response.text(); let modified = text.replace(/telegram.org/g ,'tg.k8s.li/static' ); return new Response(modified, { status: response.status, statusText: response.statusText, headers: response.headers }); }
自定义域名 回到 Workers 的管理页面,点击 rename
即可修改 Worker 的三级子域名。不过咱还是不太喜欢 WorkerName.yousetdomain.workers.dev
这么长的域名,想使用咱自己的二级子域名访问。
首先回到域名管理的页面,进入到自己域名顶部那一栏里的 Workers
,在那里添加相应的路由即可。
点击 Add Route
,在 Route 那一栏输入好自己的域名,注意最后的 /*
也要加上,然后 Worker 选择刚刚创建的那个即可。接着再添加 CNAME
记录到自己的 WorkerName.yousetdomain.workers.dev
,这样就能使用自己的域名访问啦😋
文本替换 前文提到的文本替换功能帮咱解决了一个小问题。咱友链关注的一个博客 chanshiyu.com 没有提供 RSS ,他是将博客内容放在 GitHub issue 上,所以只能通过 RSSHUB 来订阅 GitHub 的 issue 来获取博客的更新。但 RSSHUB 获取的是 GitHub issue 的链接而非原博客的链接,于是咱想了想就用 Worker
进行替换不得了。
通过 RSSHUB 获取到的 RSS 数据如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <rss xmlns:atom ="http://www.w3.org/2005/Atom" version ="2.0" > <channel > <title > <![CDATA[ chanshiyucx/blog Issues ]]> </title > <link > https://github.com/chanshiyucx/blog/issues</link > <atom:link href ="http://rsshub.app/github/issue/chanshiyucx/blog" rel ="self" type ="application/rss+xml" /> <description > <![CDATA[ chanshiyucx/blog Issues - Made with love by RSSHub(https://github.com/DIYgod/RSSHub) ]]> </description > <generator > RSSHub</generator > <webMaster > i@diygod.me (DIYgod)</webMaster > <language > zh-cn</language > <lastBuildDate > Thu, 26 Mar 2020 11:00:06 GMT</lastBuildDate > <ttl > 60</ttl > <item > <title > <![CDATA[ Telegram 电报机器人 ]]> </title > <description > <![CDATA[ ]]> </description > <pubDate > Wed, 25 Mar 2020 03:54:04 GMT</pubDate > <guid isPermaLink ="false" > https://github.com/chanshiyucx/blog/issues/108</guid > <link > https://github.com/chanshiyucx/blog/issues/108</link >
其中的 <guid isPermaLink="false">
和 <link>
中的链接 github.com/chanshiyucx/blog/issues/ 替换为 chanshiyu.com/#/post/ 即可。于是还是同样的方法新建一个 Worker,然后修改一下 worker.js
的代码就可以啦。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 const upstream = 'rsshub.app' const upstream_path = '/github/issue/chanshiyucx/blog' const https = true addEventListener('fetch' , event => { event.respondWith(fetchAndApply(event.request)); }) async function fetchAndApply (request ) { let response = null ; let url = new URL(request.url); let url_hostname = url.hostname; if (https == true ) { url.protocol = 'https:' ; } else { url.protocol = 'http:' ; } var upstream_domain = upstream; url.host = upstream_domain; if (url.pathname == '/' ) { url.pathname = upstream_path; } else { url.pathname = upstream_path + url.pathname; } let method = request.method; let request_headers = request.headers; let new_request_headers = new Headers(request_headers); new_request_headers.set('Host' , url.hostname); new_request_headers.set('Referer' , url.hostname); let original_response = await fetch(url.href, { method: method, headers: new_request_headers }) let original_response_clone = original_response.clone(); let original_text = null ; let response_headers = original_response.headers; let new_response_headers = new Headers(response_headers); let status = original_response.status; const content_type = new_response_headers.get('content-type' ); if (content_type.includes('text/html' ) && content_type.includes('UTF-8' )) { original_text = await replace_response_text(original_response_clone, upstream_domain, url_hostname); } else { original_text = original_response_clone.body } response = new Response(original_text, { status, headers: new_response_headers }) let text = await response.text() let modified = text.replace(/github.com\/chanshiyucx\/blog\/issues\//g , "chanshiyu.com\/#\/post\/" ) return new Response(modified, { status: response.status, statusText: response.statusText, headers: response.headers }) return response; }
部署好 Worker 之后就可以使用 RSS 来订阅啦😋
解锁更多好玩儿的😋 关于 Cloudflare 的 Workers 还有更多好玩的等待你去发现,咱就推荐一下啦:
最后宣传一下咱的 @rss_kubernetes 频道,国内用户可以访问 tg.k8s.li ,如果你对 docker 、K8s、云原生等感兴趣,就到咱碗里来吧😂。不订阅咱的频道也可以通过咱的 tg.k8s.li 镜像站来查看 RSS 推送信息。
GitHub page 又被墙一波,rsshub.app 也被墙掉了。墙越来越高了,这个社会也来越可笑了……不知道未来的互联网会变成什么样子,但我们作为一只屁民能做的就是不为墙添砖加瓦,不为极权专制独裁暴政唱赞歌 。最后一张自己制作 kindle 电子书时喜欢使用的封面图片送给大家。