Pages

Saturday, 1 June 2019

一个基于浏览器端 JS 实现的在线代理程序-jsproxy

 

演示

(目前仍在更新中,如有问题尝试用隐身模式访问)

更新

  • 2019-05-30 更新 cfworker,对 ytb 视频进行了优化(推荐选 1080p+,不会增加服务器压力)
  • 2019-05-29 nginx 增加静态资源服务,可同时支持代理接口和首页访问
  • 2019-05-27 增加 nio.io、sslip.io 后备域名,减少申请失败的几率
  • 2019-05-26 安装时自动申请证书(使用 xip.io 域名),安装后即可预览

安装

curl https://raw.githubusercontent.com/EtherDream/jsproxy/master/i.sh | bash
  • 自动安装目前只支持 Linux x64,并且需要 root 权限
  • 安装过程中 80 端口能被外网访问(申请 HTTPS 证书)
无法满足上述条件,或想了解安装细节,可尝试手动安装
测试: https://服务器IP.xip.io:8443(具体参考脚本输出)

部署

Fork 本项目,进入 gh-pages 分支,编辑 conf.js 文件:
  • 节点列表(node_map 字段,包括节点 id 和节点主机)
  • 默认节点(node_default 字段,指定节点 id)
访问 https://用户名.github.io/jsproxy 预览。
GitHub 支持自定义域名。也可以将文件发布到自己的 Web 服务器上。

维护

# 切换到 jsproxy 用户
su - jsproxy

# 重启服务
./run.sh reload

# 关闭服务(参数和 nginx -s 相同)
./run.sh quit

# 启动服务
./run.sh

# 查看代理日志
tail server/nginx/logs/proxy.log
目前暂未实现开机自启动。

禁止外链

默认情况下,代理接口允许所有 github.io 子站点调用,这可能导致不必要的流量消耗。
如果希望只给自己网站使用,可编辑 allowed-sites.conf。(重启服务生效)

安全策略

如果不希望代理访问内网(避免 SSRF 风险),可执行 setup-ipset.sh
/home/jsproxy/server/setup-ipset.sh
需要 root 权限,依赖 ipset 命令
该脚本可禁止 jsporxy 用户访问保留 IP 段(针对 TCP)。nginx 之外的程序也生效,但不影响其他用户。

项目特点

相比传统在线代理,本项目具有以下特点:

服务端开销低

传统在线代理几乎都是在服务端替换 HTML/JS/CSS 等资源中的 URL。这不仅需要对内容做大量的分析和处理,还需对流量进行解压和再压缩,消耗大量 CPU 资源。并且由于逻辑较复杂,通常使用 Python/PHP 等编程语言自己实现。
为降低服务端开销,本项目使用浏览器的一个黑科技 —— Service Worker。它能让 JS 拦截网页产生的请求,并能自定义返回内容,相当于在浏览器内部实现一个反向代理。这使得绝大部分的内容处理都可以在浏览器上完成,服务器只需纯粹的转发流量。
因此本项目服务端直接使用 nginx,并且转发过程不修改内容(只修改 HTTP 头),避免了内容处理产生的巨大开销。同时得益于 nginx 丰富的功能,很多常用需求无需重新造轮子,通过简单配置即可实现。并且无论性能还是稳定性,都远高于自己实现。

API 虚拟化

传统在线代理大多只针对静态 URL 的替换,忽视了动态 URL 以及和 URL 相关的网页 API。例如 a.com 反向代理 google.com,但页面中 JS 读取 document.domain 得到的仍是 a.com。这可能导致某些业务逻辑出现问题。
为缓解这个问题,本代理在页面头部注入一个 JS,用以重写绝大部分和 URL 相关的 API,使得页面中的 JS 获取到的仍是原始 URL:
对于有些无法重写的 API,例如 location,本代理会将代码中字面出现的 location 替换成 __location,从而将操作转移到自定义对象上。当然对于非字面的情况(例如 this['lo' + 'cation']),目前还无法处理。

类似项目

目前找到的都是传统后端替换 URL 的方案。当然后端替换也有不少优点,例如浏览器兼容性高,甚至低版本的 IE 都可以使用。

zmirror

php-proxy

项目意义

本项目主要用于以下技术的研究:
  • 网站镜像 / 沙盒化
  • 钓鱼网站检测技术
  • 前端资源访问加速
当然请勿将本项目用于非法用途,否则后果自负。
Demo 页面文明使用,不要进行登陆等涉及隐私的操作。
------------

手动安装

创建用户

新建一个名为 jsproxy 用户(nobody 组),并切换:
groupadd nobody
useradd jsproxy -g nobody --create-home

su - jsproxy
非 Linux 系统,或者无 root 权限的设备,可忽略。
为什么要创建用户?因为使用低权限运行服务可减少风险。另外在防 SSRF 脚本 setup-ipset.sh 中,是通过 iptalbes 的 uid-owner 策略阻止 jsprxoy 这个特定用户访问内网的。

安装 nginx

本项目使用 OpenResty。编译前需确保 make、gcc 等工具存在。
cd $(mktemp -d)

curl -O https://www.openssl.org/source/openssl-1.1.1b.tar.gz
tar zxf openssl-*

curl -O https://ftp.pcre.org/pub/pcre/pcre-8.43.tar.gz
tar zxf pcre-*

curl -O https://zlib.net/zlib-1.2.11.tar.gz
tar zxf zlib-*

curl -O https://openresty.org/download/openresty-1.15.8.1.tar.gz
tar zxf openresty-*
cd openresty-*

export PATH=$PATH:/sbin

./configure \
  --with-openssl=../openssl-1.1.1b \
  --with-pcre=../pcre-8.43 \
  --with-zlib=../zlib-1.2.11 \
  --with-http_v2_module \
  --with-http_ssl_module \
  --with-pcre-jit \
  --prefix=$HOME/openresty

make
make install
其中 configure 的参数 --prefix 指定 nginx 安装路径,这里为方便设为用户主目录。
注意编译后的 nginx 程序不能改变位置,否则会启动失败
测试能否执行:
~/openresty/nginx/sbin/nginx -h

安装代理程序

下载本项目,其本质就是一堆 nginx 配置。推荐放在 jsproxy 用户的主目录:
cd ~
git clone --depth=1 https://github.com/EtherDream/jsproxy.git server
下载静态资源文件到 www 目录:
cd server
rm -rf www
git clone -b gh-pages --depth=1 https://github.com/EtherDream/jsproxy.git www
开启服务:
./run.sh
更新使用 git 即可。

申请域名

类似的还有 nip.iosslip.io,自动安装脚本默认使用 xip.io

申请证书

可通过 Let's Encrypt 申请免费的 HTTPS 证书。
也可以不申请证书,使用免费的 HTTPS 反向代理,例如 CloudFlare
[浏览器] --- https ---> [CloudFlare] --- http ---> [服务器]
这种方案不仅能节省系统资源,还能减少流量开销(静态资源可被 CloudFlare 缓存)。当然延时可能较高,并且安全性略低。
为什么一定要用 HTTPS?因为本项目使用了浏览器 Service Worker 技术,该 API 只能在安全环境使用,除了 localhost、127.0.0.0/8 站点可以使用 HTTP,其他必须 HTTPS。

支持系统

目前测试了 OSX 系统,其他还在测试中。。。

-------

--------

使用 CloudFlare Worker 免费部署jsproxy

cf worker 无服务器版 发布,长期使用演示服务的请使用该版本。

简介

CloudFlare Worker 是 CloudFlare 的边缘计算服务。开发者可通过 JavaScript 对 CDN 进行编程,从而能灵活处理 HTTP 请求。这使得很多任务可在 CDN 上完成,无需自己的服务器参与。

部署

注册,登陆,Start building,取一个子域名,Create a Worker
复制 index.js 到左侧代码框,Save and deploy。如果正常,右侧应显示首页。
收藏地址框中的 https://xxxx.子域名.workers.dev,以后可直接访问。

计费

后退到 overview 页面可参看使用情况。免费版每天有 10 万次免费请求,对于个人通常足够。
如果不够用,可注册多个 Worker,在 conf.js 中配置多线路负载均衡。或者升级到 $5 的高级版本,每月可用 1000 万次请求(超出部分 $0.5/百万次请求)。
如果远不够用,建议和服务器组合使用。因为 cfworker 是按请求次数计费的,所以小文件更适合通过服务器代理,大文件走 cfworker 才合算。可参考下面的 加速功能

修改配置

默认情况下,静态资源从 https://zjcqoo.github.io 反向代理,可通过代码中 ASSET_URL 配置,从而可使用自定义的 conf.js 配置。

加速功能

如果你已有服务器,也可通过 CloudFlare Worker 分担大文件的代理。
前端修改:conf.js 的 cfworker 节点 lines 配置。
后端修改:lua/http-enc-res-hdr.lua 的 114-116 行 注释打开,重启服务生效。
可在 84行 处修改大于多少字节的静态资源走加速。
该功能目前还在实验中,有问题或者更好的思路可交流。
(推荐下行流量免费且不限速的服务器,可节省大量费用)

存在问题

  • WebSocket 代理尚未实现
  • 外链限制尚未实现
  • 未充分测试,以后再完善
----------------------------------------------------------------------------------------------------------------------

利用CloudFlare Workers搭建JSproxy


最近CloudFlare出了个Worker服务,这个东西可以开发出很多的玩法:
  • 搭建博客(就是本博客了)
  • 搭建网页代理(就是我接下去要讲的。)
  • and so on…… (等待大佬开发了。)
废话不多说,我们开始整:
第一步:创建一个Gthub储存库
  • 登录https://github.com
  • 去https://github.com/EtherDream/jsproxy 这里Fork大佬的项目
第二步:创建一个CloudFlare Workers项目
  • 打开https://workers.cloudflare.com/ ,登陆后如果没玩过worker的会让你创建一个域名类似:xxx.workers.dev,想好这个“xxx”因为无法修改
  • 创建一个项目,复制https://github.com/XXX/jsproxy/blob/master/cf-worker/index.js的代码到Workers工作台
  • 注意修改链接XXX为自己的Github用户名
  • 进入gh-pages分支,编辑conf.js文件然后修改默认那个cfworkerurlWorkers应用的链接
  • 再去https://github.com/XXX/jsproxy/blob/master/lua/http-enc-res-hdr.lua#L114打开上下三行的注释
  • 同样注意修改链接XXX为自己的Github用户名
第三步:访问查看效果
  • 访问访问https://XXX.github.io/jsproxy/index.html预览。
from https://blog.qixiv.me/8
------

Cloudflare Workers 初探——以 G2WW 作为例子转发 Grafana 报警到企业微信

Cloudflare Blog 发布了一篇文章:「Everyone can now run JavaScript on Cloudflare with Workers」,标志着 Cloudflare 的 “Serverless” 平台的 “GA”,作为一个标榜 “Edge computing for everyone” 的产品,Cloudflare Workers 或许是我们能接触到的最 “亲民” 的 Serverless 产品。

什么是 Serverless

Wiki 对于 Serverless Computing 的解释是:

Serverless computing is a cloud computing execution model in which the cloud provider runs the server, and dynamically manages the allocation of machine resources. Pricing is based on the actual amount of resources consumed by an application, rather than on pre-purchased units of capacity.

我对于 Serverless 的理解是:

  • 尽可能使用云服务商的资源(不一定是服务器,主要是云函数和对象存储)
  • 编写业务逻辑与云服务商的服务(例如数据库)交互.

对于静态内容来说,可以直接通过 S3 (搭配 Cloudfront)进行输出,对于动态的请求来说,只需要自己写好业务逻辑,去请求后端的服务即可,然后,无需考虑自己服务器部署,维护等内容,且真正按量计费。

什么是 Cloudflare Workers

相比较传统的 Serverless 框架来说,Cloudflare Workers 更近了一步,传统的 Serverless 结构还是有一个中心化的部署(比如应用就部署在 us-west 区域),甚至某些厂商认为自己的 Managed Docker 也是 Serverless,非常迷惑,前面是服务商的 CDN,而 Cloudflare 的函数部分是全球化的,计算逻辑直接跑在边缘节点上,依托他们的全球 Anycast 网络。这一点类似 Amazon 的 Lambda@Edge

Cloudflare 对于自己的 Workers 是这么介绍的:

You write code. We handle the rest.

以及:

Deploy serverless code to data centers across 200 cities in 95 countries to give it exceptional performance and reliability.

相对比传统的业务逻辑,我们将应用部署在自己的服务器上,并使用 Cloudflare 进行 CDN 加速(或者减速),终端用户的所有请求(一般来说就是 HTTP 请求啦)会先通过 Cloudflare 的网络,然后再由 Cloudflare 走公网反向代理到自己的应用服务器上,等应用服务器完成处理后请求再通过 Cloudflare 的边缘节点重新返回给用户(这个过程称为:回源)。

这么一来有两个问题:

  1. 回源需要时间

对于一个源站在日本的服务器来说,如果一个欧洲用户访问,那么用户的访问请求会通过 Cloudflare 位于欧洲的服务器返回到日本的源站上,期间直接走公网,如果你对 Cloudflare 回源机制还不熟悉的话,可以参考我的博文「搭建 Cloudflare 背后的 IPv6 AnyCast 网络」,内部讲解了 Cloudflare 回源机制,以及 Cloudflare 的增值服务——Argo。

  1. 计算的压力全部落在了源站上

由于是一个中心化的源站(当然,如果你使用了负载均衡器等另说),来自不同地区的访客的计算压力会全部落在自己的架构上。

Cloudflare Workers 的出现可以解决以上两个问题,Workers 的工作模式是在 Cloudflare 的 200+ 个数据中心(边缘节点)上直接运行用户的 Javascript 代码,所有的计算全部在边缘进行,此外,为了保证 Stateful 的服务,Workers 还提供一个全球低延迟(利用 Cloudflare 内网)的 KV 键值对数据库用于边缘应用的读写。

除了这些以外,还有一些小特性值得关注:

  • 小于 5ms 的代码冷启动时间
  • 15 秒内推送到 Cloudflare 的所有数据中心(边缘节点)
  • 毫秒级的运行速度

Cloudflare Workers 效果

别的不多说,我们先给一个对比图,以下是 webp-sh/webp_server_go,的官网: https://webp.sh 的内容,我们观察两种不同的使用 Cloudflare 的方式:

Cloudflare 直接反向代理

结构如下,网站的静态内容放置在 GCP 的日本 Osaka,使用的 Premium 网络,即使用了「冷土豆路由」,保证服务器到访客之间尽可能多的在 Google 内网内,此外,前端加入 Cloudflare 反向代理。

我们从 ping 值会发现,由于 Cloudflare 有 Anycast,在海外部分 ping 的延迟符合 Cloudflare 的预期.

这是因为我们的 ICMP 包在 Cloudflare 的边缘节点就已经有了 echo,然而对于网页访问来说是 HTTP 的 GET 请求,这里评判用户看到网页速度的要素不只是 ping 值,还有 TTFB:

TTFB measures the duration from the user or client making an HTTP request to the first byte of the page being received by the client’s browser. This time is made up of the socket connection time, the time taken to send the HTTP request, and the time taken to get the first byte of the page. Although sometimes misunderstood as a post-DNS calculation, the original calculation of TTFB in networking always includes network latency in measuring the time it takes for a resource to begin loading.

  • 可以看到,在日本地区 TTFB 最小(因为距离最短,日本访客 -> Cloudflare 日本节点 -> 日本源站),而在德国,TTFB 就大的多了。

    Cloudflare Workers Site

    这里直接使用了 Cloudflare Workers Site 的功能,将 webp.sh 的网页直接推到 Workers 的 KV 存储中,并使用一个临时域名进行展示,可以看到,由于直接从 Cloudflare 边缘节点进行输出(没有回源的开销),这个时候的 TTFB 就已经大幅度减少.

    Cloudflare Workers 怎么用

    相信看到上面的例子你应该已经大概明白了 Cloudflare Workers 是啥,以及它所能带来的好处了,现在我们看看 Cloudflare Workers 怎么用,可惜的是,目前在 Google 上搜索「Cloudflare Workers」相关除了看到 Sukka 的一篇「将 Hexo 部署到 Cloudflare Workers Site 上的趟坑记录」以外就只能看到大家都在用 EtherDream 写的 JSProxy 做代理翻墙…


    我们来看看 Cloudflare Workers 怎么用吧。

    安装 Wrangler

    这一步其实比较简单,在安装好 NPM 后直接按照 Quick Start 安装就好:

    npm install -g @cloudflare/wrangler
    

    然后就可以 wrangler generate my-router-app 创建一个 APP 了,这里建议先通篇阅读一下上述的 Quick Start.

    index.js

    在安装好 APP 之后,你就有 开局一个 index.js 可以用了,内容一般是这样的:

    addEventListener('fetch', event => {
      event.respondWith(handleRequest(event.request))
    })
    /**
     * Respond with hello worker text
     * @param {Request} request
     */
    async function handleRequest(request) {
      return new Response('Hello worker!', {
        headers: { 'content-type': 'text/plain' },
      })
    }
    

    警告:由于我没有学过 Javascript ,以下纯属瞎掰,仅供娱乐使用

    这里的 addEventListener 是整个程序的入口,目前 Cloudflare Workers 只支持 FetchEvent,这一种。

    G2WW

    好了,标题中我们提到了 G2WW,这里就以 G2WW 作为例子进行演示 Worker 的一个用法,我们知道 Grafana 的报警 Channel 中是没有「企业微信」的(但是有 DingDing,很奇怪),所以为了保证企业微信用户也可以通过 Grafana 进行报警,我们需要做一点改装。

    企业微信的一个"接口"就是「机器人」,它支持 Webhook 调用,只需要按照他们的规范 POST 一个数据即可,他们的规范如下:

    {
      "msgtype": "news",
      "news": {
        "articles": [
          {
            "title": "这里是标题",
            "description": "你的服务掉线了",
            "url": "https://status.xxx.xxx/grafana/xxx",
            "picurl": "https://kongbu.de.diaoxian.tupian/1.jpg"
          }
        ]
      }
    }
    

    Grafana 支持一个 Webhook 报警,Example 如下:

    {
      "dashboardId":1,
      "evalMatches":[
        {
          "value":1,
          "metric":"Count",
          "tags":{}
        }
      ],
      "imageUrl":"https://grafana.com/static/assets/img/blog/mixed_styles.png",
      "message":"Notification Message",
      "orgId":1,
      "panelId":2,
      "ruleId":1,
      "ruleName":"Panel Title alert",
      "ruleUrl":"http://localhost:3000/d/hZ7BuVbWz/test-dashboard?fullscreen\u0026edit\u0026tab=alert\u0026panelId=2\u0026orgId=1",
      "state":"alerting",
      "tags":{
        "tag name":"tag value"
      },
      "title":"[Alerting] Panel Title alert"
    }
    

    那么我们的需求就很明确了,就是需要将 Grafana Webhook 的报警内容提取需要的字段并转发到企业微信的接口上即可,该怎么做呢?

    首先我们需要区分请求是 GET 请求还是 POST 请求:

    addEventListener('fetch', event => {
      const { request } = event
     if (request.method === 'POST') {
        return event.respondWith(postWeChatUrl(request))
      } else if (request.method === 'GET') {
        return event.respondWith(new Response(banner_content, {
          headers: { 'content-type': 'text/html' },
        }))
      }
    })
    

    对于 POST 请求,我们对内容进行处理,并转发到企业微信的 API 地址上:

    async function postWeChatUrl(request) {
      var key = request.url.replace(/^https:\/\/.*?\//gi, "")
    
      // 这里我们拼接一下,对于访问 https://g2ww-serverless.nova.moe/xxxxxx-xxxxxx 的请求就自动发到 https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxxxx-xxxxxx 下
      var wechat_work_url = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=" + key
    
      var json_obj = await request.json()
    
      // 重新构造 JSON
      var template = 
      {
    		"msgtype": "news",
    		"news": {
    		  "articles": [
    			  {
    				"title": json_obj['title'],
    				"description": json_obj['message'],
    				"url": json_obj['ruleUrl'],
    				"picurl": json_obj['imageUrl']
    			  }
    		  ]
    		}
      }
    
      const init = {
        body: JSON.stringify(template),
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
      }
    
      // 发出请求
      const response = await fetch(wechat_work_url, init)
    
      // 准备结果
      const results = await gatherResponse(response)
      return new Response(results, init)
    
    }
    

    最后处理一下返回值并返回给客户端就可以了:

    async function gatherResponse(response) {
      const { headers } = response
      const contentType = headers.get('content-type') || ''
      if (contentType.includes('application/json')) {
        return JSON.stringify(await response.json())
      } else {
        return await response.text()
      }
    }
    

    然后一个 Serverless 的 G2WW 就这么做好了,非常简单是不是?

    我们把代码推到 Cloudflare 上:

    wrangler publish
    

    然后配置一下 Grafana 的报警 Channel 并看看效果如何.

    配置个地址,图片中使用了 https://g2ww.nova.moe/<key>,其实可以使用 https://g2ww-serverless.nova.moe/<key>,然后点一下 「Send Test」。


    一个可用的成品 Demo + 使用方法: https://g2ww-serverless.nova.moe

    当然,这个只是一个 Hack,最终还是希望 Grafana 可以直接原生支持企业微信,我已经给他们提了一个 PR Add a new notifier : WeChat Work(企業微信) #26109,希望他们能快点合并吧。

    尾声

    以上通过一个简单的例子大致走了一遍 Cloudflare Workers 的基本用法,当然,Cloudflare Workers 的玩法绝对不止这么点,尤其是在 KV 的加持下,一定会有更多的有意思的项目兴起,比如他们的「Built with Workers」就有很多的优秀的项目,从各个角度发觉 Cloudflare Workers 的用法。

    想到之前在某论坛上看到的一句话:

    想法太多,我们却很少停下来看看真正的需求是什么,并对自己的技能进行提升和学习,一直忙于低头走路,以至于越走越累了。

    除了已有的项目以外,我们还能利用它来做点什么呢,我想这是一个值得思考的问题。

    References

    1. Alert notifications | Grafana Labs
    2. Everyone can now run JavaScript on Cloudflare with Workers
    3. Cloudflare Workers
    4. FetchEvent
    5. Time to first byte
from https://nova.moe/first-touch-with-cf-workers/
------------
 

将博客部署到CF Workers Site

前几天Cloudflare将Workers KV增加了免费额度,还不得搞起来?

利用Workers KV存储网页内容,通过Workers将内容返回给用户,就等于将自己的网站直接部署到CF成千上万的边缘节点当中,全球访问速度和TTFB都应该不错

https://blog.cloudflare.com/workers-sites/

官方文档

Wrangler有两种安装方式,通过NPM或者Cargo安装都可以,任选其一即可

准备好NodeJS和NPM环境,然后执行下面命令,NPM方式是下载预编译好的二进制程序,安装速度比较快.

npm i @cloudflare/wrangler -g
或者准备好Rust环境,然后执行下面命令,Cargo方式是在本机从源码编译,安装速度比较慢:
cargo install wrangler

使用系统OpenSSL库,生成的二进制会小一些:
cargo install wrangler --features sys-openssl

我自己博客使用的是Hugo,下面所有内容都是按照Hugo的方式来,其他静态站点生成器方法类似.

wrangler login

# 手动设置token
wrangler config 

进入自己站点的目录,执行下面命令进行初始化。这里Wrangler会自动安装cargo-generate工具,在本目录下创建一个workers-site项目目录,然后生成一个wrangler.toml配置文件.

wrangler init --site

打开wrangler.toml文件,按照自己的信息进行修改.

account_idzone_id都可以从Cloudflare官网上找到,route是路由到Workers的规则,这里写你需要绑定的域名,不要忘记后面的/*

bucket是网站的目录,因为我用的是Hugo,所以这个目录默认是public.

entry-point是部署到Workers的js代码目录,这里不需要修改,因为刚刚初始化的时候生成的项目目录名已经自动填写上了.

name = "blog"
type = "webpack"
account_id = "eu5d123456789987456321aabcddgeh"
workers_dev = true
route = "mydomain.com/*"
zone_id = "fhidag8u98f43h93fhiohr929c8d59efhauh"

[site]
bucket = "public"
entry-point = "workers-site"
wrangler preview --watch

在Cloudflare中增加一条DNS记录,需要打开CF代理:


 执行下面命令进行部署:

wrangler publish

Cloudflare提供了官方的Wrangler GitHub Action,可以直接用Github Actions将博客内容部署到CF Workers Site.

在github仓库设置一个secret,名字为CF_API_TOKEN,值为Wrangler的token:


 

name: hugo

on:
  push:
    branches:
      - master

jobs:
  deploy:
    runs-on: ubuntu-18.04
    steps:
      - uses: actions/checkout@v2
        with:
          submodules: true
          fetch-depth: 0

      - name: Setup Hugo
        uses: peaceiris/actions-hugo@v2
        with:
          hugo-version: '0.78.2'
          extended: true

      - name: Build
        run: hugo --minify

      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./public

      - name: Publish
        uses: cloudflare/wrangler-action@1.3.0
        with:
          apiToken: ${{ secrets.CF_API_TOKEN }}


 

 
 

 

 
 
  

 

 

 

 
 
 

No comments:

Post a Comment