Forráskód Böngészése

Update 4 files

- /_posts/2026-02-08-xslt.md
- /_data/other_repo_list.csv
- /_tools/ai-summary.js
- /_tools/envs_post-receive
mayx 4 hete
szülő
commit
172882a99e
4 módosított fájl, 399 hozzáadás és 406 törlés
  1. 12 46
      _data/other_repo_list.csv
  2. 34 0
      _posts/2026-02-08-xslt.md
  3. 352 359
      _tools/ai-summary.js
  4. 1 1
      _tools/envs_post-receive

+ 12 - 46
_data/other_repo_list.csv

@@ -18,7 +18,6 @@ http://116.62.245.34:3000/mayx/blog
 http://1.6.141.109:3000/mayx/blog
 http://52.28.156.42/mayx/blog
 http://164.92.160.200:3000/mayx/blog
-https://git.louhau.edu.mo/mayx/blog
 https://git.tux.pizza/mayx/blog
 https://code.dsconce.space/mayx/blog
 https://git.anibilag.ru/mayx/blog
@@ -83,7 +82,6 @@ http://106.15.78.64:3000/mayx/blog
 http://hoenking.cn:3000/mayx/blog
 https://gitea.anessen.xyz/mayx/blog
 https://git.cjcrace.io/mayx/blog
-https://git.inkcore.cn/mayx/blog
 https://git.wisder.net/mayx/blog
 http://8.138.187.97:3000/mayx/blog
 https://git2.ujin.tech/mayx/blog
@@ -94,15 +92,12 @@ https://git.tomlab.dev/mayx/blog
 http://182.92.109.202:8070/mayx/blog
 http://94.130.182.154:3000/mayx/blog
 https://git.unicom.studio/mayx/blog
-http://47.122.132.187:3000/mayx/blog
 http://43.138.173.153:8804/mayx/blog
 http://frktedu.ru:3000/mayx/blog
 https://git.lokalix.de/mayx/blog
 http://8.141.14.13:3000/mayx/blog
 http://139.196.201.231:3000/mayx/blog
-http://8.155.23.172:3000/mayx/blog
 https://git.influxfin.com/mayx/blog
-http://47.100.177.77:3000/mayx/blog
 https://git.programar.io/mayx/blog
 http://219.157.255.213:25311/mayx/blog
 https://git.melvincarvalho.com/mayx/blog
@@ -122,7 +117,6 @@ https://git.ultra.pub/mayx/blog
 http://27.124.12.222:3000/mayx/blog
 https://saga.iao.ru:3043/mayx/blog
 https://git.sayndone.ru/mayx/blog
-http://47.113.118.108:3000/mayx/blog
 https://gitea.hintsight.com/mayx/blog
 https://anzsco.co.nz/mayx/blog
 https://git.valami.giize.com/mayx/blog
@@ -133,9 +127,6 @@ https://git.dushes.keenetic.pro/mayx/blog
 http://47.129.22.53:22345/mayx/blog
 https://git.tech.troyrc.com/mayx/blog
 http://git.zkyspace.top/mayx/blog
-http://47.107.62.152:3000/mayx/blog
-http://47.99.119.173:13000/mayx/blog
-https://git.outsidecontext.solutions/mayx/blog
 http://149.88.87.128:3000/mayx/blog
 http://124.70.167.140:3000/mayx/blog
 http://8.137.38.60:8201/mayx/blog
@@ -157,10 +148,8 @@ http://123.56.193.182:3000/mayx/blog
 http://60.204.131.99:3000/mayx/blog
 http://femail.email:8418/mayx/blog
 http://47.93.56.66:8080/mayx/blog
-http://47.92.218.215:3000/mayx/blog
 http://167.172.7.198:8081/mayx/blog
 https://gitea.meetgu.ru/mayx/blog
-http://8.134.253.108:3000/mayx/blog
 https://git.patrich.se/mayx/blog
 http://116.236.50.103:8789/mayx/blog
 https://git.paulll.cc/mayx/blog
@@ -194,25 +183,20 @@ http://47.110.224.240:13000/mayx/blog
 http://194.67.86.160:3100/mayx/blog
 http://47.101.58.33:3000/mayx/blog
 https://gitea.synapsetec.cn/mayx/blog
-http://220.132.16.87:3000/mayx/blog
 http://gitea.yunshanghub.com:8081/mayx/blog
 https://repo.c-software.id/mayx/blog
 http://113.177.27.200:2033/mayx/blog
 http://152.69.204.151:3000/mayx/blog
 http://207.180.229.193:3001/mayx/blog
 https://g2.m8il.in/mayx/blog
-http://8.129.84.221:10800/mayx/blog
 http://34.81.52.16/mayx/blog
 http://120.78.74.94:3000/mayx/blog
 https://git.genowisdom.cn/mayx/blog
 http://120.26.116.243:3000/mayx/blog
-http://120.26.46.180:3000/mayx/blog
 http://123.57.16.111:3000/mayx/blog
 http://115.159.107.117:3000/mayx/blog
-http://47.99.98.98:9000/mayx/blog
 http://116.62.115.84:3000/mayx/blog
 https://Cagit.Cacode.net/mayx/blog
-https://gitea.rodaw.net/mayx/blog
 http://wingsing.net:3000/mayx/blog
 http://hgngit.ipdz.me/mayx/blog
 http://185.163.116.39:49153/mayx/blog
@@ -223,7 +207,6 @@ https://gitea.questline.coop/mayx/blog
 https://git.poggerer.xyz/mayx/blog
 https://git.wisptales.org/mayx/blog
 http://git.chaojing-film.com:3000/mayx/blog
-http://8.142.81.167:1080/mayx/blog
 https://git.nusaerp.com/mayx/blog
 http://35.207.205.18:3000/mayx/blog
 http://git.zjsciot.com:3000/mayx/blog
@@ -241,7 +224,6 @@ http://git.yinas.cn/mayx/blog
 http://git.chilidoginteractive.com:3000/mayx/blog
 http://121.40.40.177:3000/mayx/blog
 http://118.31.223.224:3000/mayx/blog
-http://47.109.67.128:3000/mayx/blog
 http://voicebot.digitalakademie-bw.de:3000/mayx/blog
 http://178.254.35.219:3000/mayx/blog
 http://114.215.207.150:3000/mayx/blog
@@ -250,18 +232,14 @@ http://newslabx.csie.ntu.edu.tw:3000/mayx/blog
 http://81.71.148.57:8080/mayx/blog
 https://git.lmskaran.com/mayx/blog
 http://sdgit.zfmgr.top/mayx/blog
-http://47.122.26.54:3000/mayx/blog
 http://154.86.0.30:3000/mayx/blog
 http://139.224.196.148:3000/mayx/blog
-http://47.95.167.249:3000/mayx/blog
 https://omegat.dmu-medical.de/mayx/blog
 http://52.23.128.62:3000/mayx/blog
 http://85.214.41.219:49153/mayx/blog
 http://6068688.xyz:3000/mayx/blog
-http://8.137.105.8:5530/mayx/blog
 https://git.881221.xyz/mayx/blog
 http://120.46.222.128:10021/mayx/blog
-http://47.92.23.211:3000/mayx/blog
 http://120.26.108.239:9188/mayx/blog
 https://git.van-peeren.de/mayx/blog
 http://git.gkcorp.com.vn:16000/mayx/blog
@@ -270,7 +248,6 @@ https://k0ki-dev.com/mayx/blog
 http://110.42.45.89:2052/mayx/blog
 https://git.furcom.org/mayx/blog
 http://47.109.95.73:3000/mayx/blog
-http://47.104.196.63:8418/mayx/blog
 http://8.133.240.249:3456/mayx/blog
 http://34.102.70.200:3000/mayx/blog
 http://175.178.219.170:3000/mayx/blog
@@ -286,7 +263,6 @@ https://git.liliyamol.cn/mayx/blog
 http://8.138.90.28:3000/mayx/blog
 https://gitea.theaken.com/mayx/blog
 https://git.ja-schwarz.de/mayx/blog
-https://shanewan.cn:30000/mayx/blog
 https://git.xemo-net.de/mayx/blog
 https://git.23cm.cn/mayx/blog
 https://gitea.oio.cat/mayx/blog
@@ -299,8 +275,6 @@ https://git.sduonline.cn/mayx/blog
 https://git.7o9o.net/mayx/blog
 https://dev.init.zone/mayx/blog
 https://git.baneynet.net/mayx/blog
-http://47.97.225.73:3000/mayx/blog
-http://1.15.11.52:13000/mayx/blog
 https://git.gupaoedu.cn/mayx/blog
 http://60.205.233.184:3010/mayx/blog
 https://git.ricecakecat.com/mayx/blog
@@ -315,13 +289,10 @@ http://suncheng.asia:14200/mayx/blog
 http://1.94.13.224:9080/mayx/blog
 https://auric-org.org/mayx/blog
 https://git.git-happens.de/mayx/blog
-http://47.104.241.192:19999/mayx/blog
-http://47.105.61.58:3000/mayx/blog
 http://101.35.183.241/mayx/blog
 https://gitea.quiztimes.nl/mayx/blog
 http://106.14.189.125:3000/mayx/blog
 https://git.hundseth.com/mayx/blog
-https://git.xming.cloud/mayx/blog
 https://travgit.guillorystack.com/mayx/blog
 http://gitea.coderpath.com/mayx/blog
 https://gitea.thanh0x.com/mayx/blog
@@ -329,7 +300,6 @@ http://175.198.180.19:3000/mayx/blog
 https://gitea.albanmary.com/mayx/blog
 http://ngtools.cn:53000/mayx/blog
 https://git.hantify.ru/mayx/blog
-https://git.unpas.dev/mayx/blog
 http://43.136.169.169:3000/mayx/blog
 http://121.196.213.68:3000/mayx/blog
 https://git.vereint-digital.de/mayx/blog
@@ -344,7 +314,6 @@ https://git.anatid.net/mayx/blog
 https://git.lekai.info/mayx/blog
 https://git.avclick.ru/mayx/blog
 http://135.235.225.198:3000/mayx/blog
-https://git.zimerguz.net/mayx/blog
 https://git.sick.earth/mayx/blog
 https://git.prayujt.com/mayx/blog
 https://git.galaxylabs.ca/mayx/blog
@@ -366,13 +335,10 @@ https://gitea.sciotech.cn/mayx/blog
 http://8.148.220.5:8089/mayx/blog
 http://8.140.250.85:3000/mayx/blog
 http://8.134.11.35:3000/mayx/blog
-https://dev.ilink-app.com/mayx/blog
 https://git.ctx.dev/mayx/blog
-http://82.157.205.190:3000/mayx/blog
 https://nas.szwyll.com:3000/mayx/blog
 https://git.koppa.pro/mayx/blog
 http://202.65.194.19:3000/mayx/blog
-http://47.101.60.131:10082/mayx/blog
 http://8.148.234.146:3001/mayx/blog
 http://www.bzturbo.com.br/mayx/blog
 https://git.maiasoft.jp/mayx/blog
@@ -396,7 +362,6 @@ http://111.231.240.168:9082/mayx/blog
 https://git.afonsosoares.com/mayx/blog
 http://209.141.47.52:3000/mayx/blog
 https://git.apfern.com/mayx/blog
-http://47.103.155.22:3000/mayx/blog
 http://dapainas.asia:5990/mayx/blog
 http://60.204.158.188:3000/mayx/blog
 https://zqz5.com/mayx/blog
@@ -436,10 +401,8 @@ http://57.129.94.190:3000/mayx/blog
 https://gitea.micro-stack.org/mayx/blog
 http://101.132.160.67:3000/mayx/blog
 https://gitea.temp.brentgruber.com/mayx/blog
-https://www.know-how.store/mayx/blog
 http://47.102.147.170:3050/mayx/blog
 http://209.38.235.254:3000/mayx/blog
-http://52.66.204.217:3001/mayx/blog
 http://172.236.250.154:3000/mayx/blog
 http://54.198.134.152:3000/mayx/blog
 http://121.41.35.226:11011/mayx/blog
@@ -448,10 +411,8 @@ https://nelgit.nelpi.co.uk/mayx/blog
 http://47.108.156.251:3000/mayx/blog
 http://8.141.91.86:3000/mayx/blog
 http://8.138.187.132:3000/mayx/blog
-https://gitea.codeathome.ovh/mayx/blog
 http://117.72.15.187:3000/mayx/blog
 http://81.70.30.91:3000/mayx/blog
-http://47.98.105.248:3000/mayx/blog
 https://gitea.kisechan.space/mayx/blog
 http://140.238.9.133:3007/mayx/blog
 http://8.138.100.115:3000/mayx/blog
@@ -468,7 +429,6 @@ http://39.96.195.72:10082/mayx/blog
 https://gitea.klopfenstein.org/mayx/blog
 http://8.148.24.160:3000/mayx/blog
 http://221.219.181.35:30000/mayx/blog
-http://47.108.27.137:3000/mayx/blog
 http://172.172.102.93:3000/mayx/blog
 http://139.196.179.195:3000/mayx/blog
 http://115.120.245.164:3000/mayx/blog
@@ -506,7 +466,6 @@ http://116.205.97.109:3000/mayx/blog
 https://gitea.ysme.top/mayx/blog
 http://8.140.242.230:3000/mayx/blog
 http://8.138.142.17:3000/mayx/blog
-https://git.babalish.com/mayx/blog
 http://47.95.31.56:20080/mayx/blog
 http://123.56.231.200:3000/mayx/blog
 http://113.46.142.74:3000/mayx/blog
@@ -517,13 +476,11 @@ https://git.bigdalang.com/mayx/blog
 https://gitea.augeo.dev/mayx/blog
 http://120.46.23.215:3000/mayx/blog
 http://121.41.2.71:3000/mayx/blog
-https://sithcom.de/mayx/blog
 http://8.138.242.82:8888/mayx/blog
 http://110.41.177.80:5000/mayx/blog
 https://gitea.dsmaster.myds.me/mayx/blog
 http://intechrnd.iptime.org:3000/mayx/blog
 https://gitea.primecontrols-dev.com/mayx/blog
-http://47.104.160.69:3000/mayx/blog
 http://39.104.61.219:3000/mayx/blog
 http://www.scserverddns.top:13000/mayx/blog
 http://156.255.1.229:3000/mayx/blog
@@ -556,7 +513,6 @@ http://115.190.107.87:3000/mayx/blog
 https://git.hast.one/mayx/blog
 http://106.12.50.144:8081/mayx/blog
 http://43.192.90.133/mayx/blog
-http://47.120.16.137:8889/mayx/blog
 http://120.77.94.227:9999/mayx/blog
 https://code.wxk8.com/mayx/blog
 http://www.befitsoft.com/mayx/blog
@@ -569,7 +525,6 @@ http://www.dothing.com:6999/mayx/blog
 http://8.217.32.95:3000/mayx/blog
 https://git.temporaryname.org/mayx/blog
 https://git.loli.surf/mayx/blog
-http://121.135.143.139:5830/mayx/blog
 https://Repo.gusdya.net/mayx/blog
 https://git.m.ctf.arrobe.fr/mayx/blog
 http://120.27.238.24:3000/mayx/blog
@@ -588,7 +543,6 @@ http://106.14.138.181:3000/mayx/blog
 https://git.adalspace.com/mayx/blog
 https://git.yyuu.xyz/mayx/blog
 http://39.107.70.124:3000/mayx/blog
-http://8.142.36.79:3006/mayx/blog
 http://huanghomenas2.myqnapcloud.com:4000/mayx/blog
 http://218.237.212.51:30003/mayx/blog
 http://gitea.huangyanjie.com/mayx/blog
@@ -610,3 +564,15 @@ https://git.nihil.foo/mayx/blog
 https://scm.bcorex.e3labs.net/mayx/blog
 http://167.172.88.190:3000/mayx/blog
 http://58.17.14.95:8001/mayx/blog
+https://hero-cloud-stg-code.cnbita.com/mayx/blog
+https://git.darkmattergame.net/mayx/blog
+http://101.37.69.204:3000/mayx/blog
+http://jacksonhampton.com:3000/mayx/blog
+https://git.jaronnie.com/mayx/blog
+https://gitea.tecamino.com/mayx/blog
+http://39.99.175.172:8000/mayx/blog
+http://8.138.13.251:3000/mayx/blog
+http://47.115.223.229:8888/mayx/blog
+http://47.105.59.0:5132/mayx/blog
+https://git.veran.link/mayx/blog
+http://104.254.131.244:3000/mayx/blog

+ 34 - 0
_posts/2026-02-08-xslt.md

@@ -0,0 +1,34 @@
+---
+layout: post
+title: 在Google杀死XSLT之后的XML美化方案
+tags: [XML, Feed, XSLT, 美化]
+---
+
+  即使没有了XSLT,也不能让读者看到光秃秃的XML!<!--more-->   
+
+# 起因
+  在半年前,我写了一篇[用XSLT美化博客XML文件](/2025/07/01/xslt.html)的文章,自从那以后,每次我在浏览其他人博客的时候,都会看一眼对方博客有没有给自己的订阅文件做美化。不过就在前段时间,我在浏览某个博客的时候,发现他博客的订阅文件,甚至连最基本的XML文档树都没有显示出来。这时候我打开开发者工具看了一眼源代码,发现他也并没有使用`xml-stylesheet`之类的指令……而且控制台貌似报了些错,好像是出现了什么CSP错误……于是我就想,浏览器显示XML文档树的本质,会不会其实也是一种XSLT?之所以报错也有可能是浏览器在自动引用内置的XSLT时违反了CSP。所以我就问了问谷歌AI,结果似乎真的是这样,比如火狐浏览器就内置了一份[XSLT文件](https://github.com/mozilla-firefox/firefox/blob/main/dom/xml/resources/XMLPrettyPrint.xsl),IE浏览器也有。正当我为XSLT的功能感到强大时,谷歌AI随后提到,[Chrome浏览器决定弃用XSLT](https://developer.chrome.com/docs/web-platform/deprecating-xslt),所以以后不要再用XSLT了😰……   
+  我给我的订阅文件加美化功能才半年,怎么就要不能用了?XSLT出现这么多年都还能用,结果等我加上就要废弃了?当时为了增加这个功能,还是费了不少劲的,怎么能让谷歌说没就没?于是我就开始对这件事进行了调查。   
+
+# Google杀死了XSLT
+  从上面Chrome的弃用XSLT文档中,可以发现,这件事的始作俑者是[Mason Freed](https://github.com/mfreed7),他在WHATWG中发起了一个[Issue](https://github.com/whatwg/html/issues/11523),因为XSLT用的人很少,以及实现XSLT的库很老而且容易出漏洞,所以建议把XSLT从Web标准中删除。在这个Issue中可以发现,有很多人表示不满,毕竟这个功能对想要给自己订阅做美化的博主来说还是很有用的。为了对抗谷歌,还有人做了个网站: <https://xslt.rip> 。   
+  而且XSLT虽然用的人占比也许不高,但从总量上应该还是挺多的,除了用XSLT美化博客订阅的,甚至还有用[XSLT作为博客框架的](https://github.com/vgr-land/vgr-xslt-blog-framework),另外还有一些人提出[一部分政府网站也有使用XSLT](https://github.com/whatwg/html/issues/11582)。   
+  不过Freed看起来对这件事早有准备,他做了一个[Polyfill库](https://github.com/mfreed7/xslt_polyfill),通过WASM的方式让XSLT可以正常工作,为了方便大家使用这个库,我顺手给CDNJS发了个[PR](https://github.com/cdnjs/packages/pull/2118),以后可以用CDN引用它了。不过使用这个库的前提是需要在订阅中加一段引用JS的代码,像我博客中的Atom订阅,用的是[jekyll-feed](https://github.com/jekyll/jekyll-feed)插件,里面的格式都是写死的,就用不了了……   
+  只不过现在已经没办法阻止谷歌了……而且其他浏览器也表示会跟进,看来我们唯一能做的就是去适应了。   
+  
+# 没有XSLT之后的美化方案
+## 纯CSS
+  虽然XSLT不能用,但不代表`xml-stylesheet`指令就不能用了,除了XSLT之外,`xml-stylesheet`同样可以引用CSS。只是似乎完全没见过用CSS美化订阅源的,也许是因为光用CSS能做到的事比较少吧,想用CSS给XML文档加链接之类的估计就做不到了。   
+  但目前能选择的也不多了,既然大家都没写过用CSS美化订阅源,那就让我来写一个吧!然而我并不会写😅……那就只好让AI来写了,我把需求说清楚之后,AI就写出来了:[feed.css](/assets/css/feed.css)。试了一下效果还挺不错的,我让AI写的这个版本无论是RSS还是Atom都可以使用,如果有人感兴趣可以拿去用。可惜我的Atom订阅因为用的是插件的原因用不了😭,只能加到用纯Liquid实现的RSS订阅上了。   
+  但用纯CSS的缺点也很明显,没办法操作文档的内容,像修改日期格式的就做不了了,而且也不能添加超链接……XML的标签本身对浏览器来说并没有内建的语义,正常情况下也没法让浏览器把某个标签当作超链接。那难道就没办法了吗?   
+## 混合XHTML
+  如果完全不能修改XML内容,那确实就没有办法了,但如果能修改XML的内容那还是有办法的,简单来说就是混入XHTML,事实上Freed编写的Polyfill库原理上也是利用了XHTML,只要在能作为XHTML的标签中添加XHTML的命名空间,那么浏览器就可以理解它的语义并渲染,像刚刚用纯CSS美化的订阅没有链接,那就可以在根元素中添加命名空间:`xmlns:xhtml="http://www.w3.org/1999/xhtml"`,然后在合适的位置写:   
+```xml
+<xhtml:a href="https://example.com">Read more -&gt;</xhtml:a>
+```
+  就可以了。只是这样有个缺点,这样写的订阅文件不够“纯粹”,用验证器验证会显示“[Misplaced XHTML content](https://validator.w3.org/feed/docs/warning/MisplacedXHTMLContent.html)”警告。对有洁癖的人来说可能会有点难受😆。   
+  不过如果能接受这种“不纯粹”,那么其实`xml-stylesheet`指令也没必要了,`link`标签一样可以用,包括`script`也是,所以有人写了一个[不使用XSLT美化XML](https://github.com/dfabulich/style-xml-feeds-without-xslt)的库。   
+  只不过这种方法和XSLT相比还是有一些缺陷,要知道XSLT的本质是转换,是把XML转换为HTML,也就是说转出来的文档本质是HTML,所有的DOM操作都和操作HTML是完全相同的,但是在XML里混入XHTML标签就不一样了,它的本质依然是XML文档,只是嵌入了XHTML命名空间下的元素,所以相应的DOM操作会有一些不同。如果是自己写的纯JS可能还好,如果是用了jQuery之类假定DOM为HTML的库就会出现问题了,因此这也就是那个Polyfill库的局限性,用正常的XSLT执行`document.constructor`会显示`HTMLDocument`,而用这个Polyfill库执行完则是显示`XMLDocument`。因此,直接套用为浏览器原生XSLT编写的旧样式文件,就有可能会出问题,但如果要考虑改XSLT的话那还不如重新写JS,然后用XHTML引入呢。   
+
+# 感想
+  虽然有一些技术会因为各种各样的原因消失,但这不代表我们就要妥协一些东西,总有一些不同的技术可以解决相同的问题,所以我们只需要用其他的技术去实现就好了。不过这也是没办法的事情,毕竟没人能改变浏览器厂商们的决策啊😂。

+ 352 - 359
_tools/ai-summary.js

@@ -1,388 +1,381 @@
 async function sha(str) {
-    const encoder = new TextEncoder();
-    const data = encoder.encode(str);
-    const hashBuffer = await crypto.subtle.digest("SHA-256", data);
-    const hashArray = Array.from(new Uint8Array(hashBuffer)); // convert buffer to byte array
-    const hashHex = hashArray
-      .map((b) => b.toString(16).padStart(2, "0"))
-      .join(""); // convert bytes to hex string
-    return hashHex;
-  }
-  async function md5(str) {
-    const encoder = new TextEncoder();
-    const data = encoder.encode(str);
-    const hashBuffer = await crypto.subtle.digest("MD5", data);
-    const hashArray = Array.from(new Uint8Array(hashBuffer)); // convert buffer to byte array
-    const hashHex = hashArray
-      .map((b) => b.toString(16).padStart(2, "0"))
-      .join(""); // convert bytes to hex string
-    return hashHex;
-  }
-  
-  export default {
-    async fetch(request, env, ctx) {
-      const db = env.blog_summary.withSession();
-      const counter_db = env.blog_counter
-      const url = new URL(request.url);
-      const query = decodeURIComponent(url.searchParams.get('id'));
-      var commonHeader = {
-        'Access-Control-Allow-Origin': '*',
-        'Access-Control-Allow-Methods': "*",
-        'Access-Control-Allow-Headers': "*",
-        'Access-Control-Max-Age': '86400',
+  const encoder = new TextEncoder();
+  const data = encoder.encode(str);
+  const hashBuffer = await crypto.subtle.digest("SHA-256", data);
+  const hashArray = Array.from(new Uint8Array(hashBuffer)); // convert buffer to byte array
+  const hashHex = hashArray
+    .map((b) => b.toString(16).padStart(2, "0"))
+    .join(""); // convert bytes to hex string
+  return hashHex;
+}
+async function md5(str) {
+  const encoder = new TextEncoder();
+  const data = encoder.encode(str);
+  const hashBuffer = await crypto.subtle.digest("MD5", data);
+  const hashArray = Array.from(new Uint8Array(hashBuffer)); // convert buffer to byte array
+  const hashHex = hashArray
+    .map((b) => b.toString(16).padStart(2, "0"))
+    .join(""); // convert bytes to hex string
+  return hashHex;
+}
+
+export default {
+  async fetch(request, env, ctx) {
+    const db = env.blog_summary.withSession();
+    const counter_db = env.blog_counter
+    const url = new URL(request.url);
+    const query = decodeURIComponent(url.searchParams.get('id'));
+    var commonHeader = {
+      'Access-Control-Allow-Origin': '*',
+      'Access-Control-Allow-Methods': "*",
+      'Access-Control-Allow-Headers': "*",
+      'Access-Control-Max-Age': '86400',
+    }
+    if (url.pathname.startsWith("/ai_chat")) {
+      // 获取请求中的文本数据
+      if (!(request.headers.get('accept') || '').includes('text/event-stream')) {
+        return Response.redirect("https://mabbs.github.io", 302);
       }
-      if (url.pathname.startsWith("/ai_chat")) {
-        // 获取请求中的文本数据
-        if (!(request.headers.get('accept') || '').includes('text/event-stream')) {
-          return Response.redirect("https://mabbs.github.io", 302);
+      // const req = await request.formData();
+      let questsion = decodeURIComponent(url.searchParams.get('info'))
+      let notes = [];
+      let refer = [];
+      let contextMessage;
+      if (query != "null") {
+        try {
+          const result = String(await db.prepare(
+            "SELECT content FROM blog_summary WHERE id = ?1"
+          ).bind(query).first("content"));
+          contextMessage = result.length > 6000 ?
+            result.slice(0, 3000) + result.slice(-3000) :
+            result.slice(0, 6000)
+        } catch (e) {
+          console.error({
+            message: e.message
+          });
+          contextMessage = "无法获取到文章内容";
         }
-        // const req = await request.formData();
-        let questsion = decodeURIComponent(url.searchParams.get('info'))
-        let notes = [];
-        let refer = [];
-        let contextMessage;
-        if (query != "null") {
-          try {
-            const result = String(await db.prepare(
-              "SELECT content FROM blog_summary WHERE id = ?1"
-            ).bind(query).first("content"));
-            contextMessage = result.length > 6000 ?
-              result.slice(0, 3000) + result.slice(-3000) :
-              result.slice(0, 6000)
-          } catch (e) {
-            console.error({
-              message: e.message
-            });
-            contextMessage = "无法获取到文章内容";
-          }
-          notes.push("content");
-        } else {
-          try {
-            const response = await env.AI.run(
-              "@cf/meta/m2m100-1.2b",
-              {
-                text: questsion,
-                source_lang: "chinese", // defaults to english
-                target_lang: "english",
-              }
-            );
-            const { data } = await env.AI.run(
-              "@cf/baai/bge-base-en-v1.5",
-              {
-                text: response.translated_text,
-              }
-            );
-            let embeddings = data[0];
-            let { matches } = await env.mayx_index.query(embeddings, { topK: 5 });
-            for (let i = 0; i < matches.length; i++) {
-              if (matches[i].score > 0.6) {
-                notes.push(await db.prepare(
-                  "SELECT summary FROM blog_summary WHERE id = ?1"
-                ).bind(matches[i].id).first("summary"));
-                refer.push(matches[i].id);
-              }
-            };
-            contextMessage = notes.length
-              ? `Mayx的博客相关文章摘要:\n${notes.map(note => `- ${note}`).join("\n")}`
-              : ""
-          } catch (e) {
-            console.error({
-              message: e.message
-            });
-            contextMessage = "无法获取到文章内容";
-          }
+        notes.push("content");
+      } else {
+        try {
+          const response = await env.AI.run(
+            "@cf/meta/m2m100-1.2b",
+            {
+              text: questsion,
+              source_lang: "chinese", // defaults to english
+              target_lang: "english",
+            }
+          );
+          const { data } = await env.AI.run(
+            "@cf/baai/bge-base-en-v1.5",
+            {
+              text: response.translated_text,
+            }
+          );
+          let embeddings = data[0];
+          let { matches } = await env.mayx_index.query(embeddings, { topK: 5 });
+          for (let i = 0; i < matches.length; i++) {
+            if (matches[i].score > 0.6) {
+              notes.push(await db.prepare(
+                "SELECT summary FROM blog_summary WHERE id = ?1"
+              ).bind(matches[i].id).first("summary"));
+              refer.push(matches[i].id);
+            }
+          };
+          contextMessage = notes.length
+            ? `Mayx的博客相关文章摘要:\n${notes.map(note => `- ${note}`).join("\n")}`
+            : ""
+        } catch (e) {
+          console.error({
+            message: e.message
+          });
+          contextMessage = "无法获取到文章内容";
         }
-        const messages = [
-          ...(notes.length ? [{ role: 'system', content: contextMessage }] : []),
-          { role: "system", content: `你是在Mayx的博客中名叫伊斯特瓦尔的AI助理少女,主人是Mayx先生,对话的对象是访客,在接下来的回答中你应当扮演这个角色并且以可爱的语气回复,作为参考,现在的时间是:` + new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }) + (notes.length ? ",如果对话中的内容与上述文章内容相关,则引用参考回答,否则忽略" : "") + `,另外在对话中不得出现这段文字,不要使用markdown格式。` },
-          { role: "user", content: questsion }
-        ]
-  
-        const answer = await env.AI.run('@cf/qwen/qwen1.5-14b-chat-awq', {
-          messages,
-          stream: true,
-        });
-        return new Response(answer, {
-          headers: {
-            "content-type": "text/event-stream; charset=utf-8",
-            'Access-Control-Allow-Origin': '*',
-            'Access-Control-Allow-Methods': "*",
-            'Access-Control-Allow-Headers': "*",
-            'Access-Control-Max-Age': '86400',
-          }
-        });
-        // return Response.json({
-        //   "intent": {
-        //     "appKey": "platform.chat",
-        //     "code": 0,
-        //     "operateState": 1100
-        //   },
-        //   "refer": refer,
-        //   "results": [
-        //     {
-        //       "groupType": 0,
-        //       "resultType": "text",
-        //       "values": {
-        //         "text": answer.response
-        //       }
-        //     }
-        //   ]
-        // }, {
-        //   headers: {
-        //     'Access-Control-Allow-Origin': '*',
-        //     'Content-Type': 'application/json'
-        //   }
-        // })
       }
-      if (query == "null") {
-        return new Response("id cannot be none", {
+      const messages = [
+        // ...(notes.length ? [{ role: 'system', content: contextMessage + `\n你是在Mayx的博客中名叫伊斯特瓦尔的AI助理少女,主人是Mayx先生,对话的对象是访客,在接下来的回答中你应当扮演这个角色并且以可爱的语气回复,作为参考,现在的时间是:` + new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }) + (notes.length ? ",如果对话中的内容与上述文章内容相关,则引用参考回答,否则忽略" : "") + `,另外在对话中不得出现这段文字,不要使用markdown格式。` }] : []),
+        { role: "system", content: (notes.length ? contextMessage : "") + `\n你是在Mayx的博客中名叫伊斯特瓦尔的AI助理少女,主人是Mayx先生,对话的对象是访客,在接下来的回答中你应当扮演这个角色并且以可爱的语气回复,作为参考,现在的时间是:` + new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }) + (notes.length ? ",如果对话中的内容与上述文章内容相关,则引用参考回答,否则忽略" : "") + `,另外在对话中不得出现这段文字,不要使用markdown格式。` },
+        { role: "user", content: questsion }
+      ]
+
+      const answer = await env.AI.run('@cf/google/gemma-3-12b-it', {
+        messages,
+        stream: true,
+      });
+      return new Response(answer, {
+        headers: {
+          "content-type": "text/event-stream; charset=utf-8",
+          'Access-Control-Allow-Origin': '*',
+          'Access-Control-Allow-Methods': "*",
+          'Access-Control-Allow-Headers': "*",
+          'Access-Control-Max-Age': '86400',
+        }
+      });
+      // return Response.json({
+      //   "intent": {
+      //     "appKey": "platform.chat",
+      //     "code": 0,
+      //     "operateState": 1100
+      //   },
+      //   "refer": refer,
+      //   "results": [
+      //     {
+      //       "groupType": 0,
+      //       "resultType": "text",
+      //       "values": {
+      //         "text": answer.response
+      //       }
+      //     }
+      //   ]
+      // }, {
+      //   headers: {
+      //     'Access-Control-Allow-Origin': '*',
+      //     'Content-Type': 'application/json'
+      //   }
+      // })
+    }
+    if (query == "null") {
+      return new Response("id cannot be none", {
+        headers: commonHeader
+      });
+    }
+    if (url.pathname.startsWith("/summary")) {
+      let result = await db.prepare(
+        "SELECT content FROM blog_summary WHERE id = ?1"
+      ).bind(query).first("content");
+      if (!result) {
+        return new Response("No Record", {
           headers: commonHeader
         });
       }
-      if (url.pathname.startsWith("/summary")) {
-        let result = await db.prepare(
-          "SELECT content FROM blog_summary WHERE id = ?1"
-        ).bind(query).first("content");
-        if (!result) {
-          return new Response("No Record", {
-            headers: commonHeader
-          });
+
+      const messages = [
+        {
+          role: "system", content: `
+          你是一个专业的文章摘要助手。你的主要任务是对各种文章进行精炼和摘要,帮助用户快速了解文章的核心内容。你读完整篇文章后,能够提炼出文章的关键信息,以及作者的主要观点和结论。
+          技能
+            精炼摘要:能够快速阅读并理解文章内容,提取出文章的主要关键点,用简洁明了的中文进行阐述。
+            关键信息提取:识别文章中的重要信息,如主要观点、数据支持、结论等,并有效地进行总结。
+            客观中立:在摘要过程中保持客观中立的态度,避免引入个人偏见。
+          约束
+            输出内容必须以中文进行。
+            必须确保摘要内容准确反映原文章的主旨和重点。
+            尊重原文的观点,不能进行歪曲或误导。
+            在摘要中明确区分事实与作者的意见或分析。
+          提示
+            不需要在回答中注明摘要(不需要使用冒号),只需要输出内容。
+          格式
+            你的回答格式应该如下:
+              这篇文章介绍了<这里是内容>
+          ` },
+        {
+          role: "user", content: result.length > 6000 ?
+            result.slice(0, 3000) + result.slice(-3000) :
+            result.slice(0, 6000)
         }
-  
-        const messages = [
-          {
-            role: "system", content: `
-            你是一个专业的文章摘要助手。你的主要任务是对各种文章进行精炼和摘要,帮助用户快速了解文章的核心内容。你读完整篇文章后,能够提炼出文章的关键信息,以及作者的主要观点和结论。
-            技能
-              精炼摘要:能够快速阅读并理解文章内容,提取出文章的主要关键点,用简洁明了的中文进行阐述。
-              关键信息提取:识别文章中的重要信息,如主要观点、数据支持、结论等,并有效地进行总结。
-              客观中立:在摘要过程中保持客观中立的态度,避免引入个人偏见。
-            约束
-              输出内容必须以中文进行。
-              必须确保摘要内容准确反映原文章的主旨和重点。
-              尊重原文的观点,不能进行歪曲或误导。
-              在摘要中明确区分事实与作者的意见或分析。
-            提示
-              不需要在回答中注明摘要(不需要使用冒号),只需要输出内容。
-            格式
-              你的回答格式应该如下:
-                这篇文章介绍了<这里是内容>
-            ` },
-          {
-            role: "user", content: result.length > 6000 ?
-              result.slice(0, 3000) + result.slice(-3000) :
-              result.slice(0, 6000)
-          }
-        ]
-  
-        const stream = await env.AI.run('@cf/qwen/qwen1.5-14b-chat-awq', {
-          messages,
-          stream: true,
+      ]
+
+      const stream = await env.AI.run('@cf/google/gemma-3-12b-it', {
+        messages,
+        stream: true,
+      });
+
+      return new Response(stream, {
+        headers: {
+          "content-type": "text/event-stream; charset=utf-8",
+          'Access-Control-Allow-Origin': '*',
+          'Access-Control-Allow-Methods': "*",
+          'Access-Control-Allow-Headers': "*",
+          'Access-Control-Max-Age': '86400',
+        }
+      });
+    } else if (url.pathname.startsWith("/get_summary")) {
+      const orig_sha = decodeURIComponent(url.searchParams.get('sign'));
+      let result = await db.prepare(
+        "SELECT content FROM blog_summary WHERE id = ?1"
+      ).bind(query).first("content");
+      if (!result) {
+        return new Response("no", {
+          headers: commonHeader
         });
-  
-        return new Response(stream, {
-          headers: {
-            "content-type": "text/event-stream; charset=utf-8",
-            'Access-Control-Allow-Origin': '*',
-            'Access-Control-Allow-Methods': "*",
-            'Access-Control-Allow-Headers': "*",
-            'Access-Control-Max-Age': '86400',
-          }
+      }
+      let result_sha = await sha(result);
+      if (result_sha != orig_sha) {
+        return new Response("no", {
+          headers: commonHeader
         });
-      } else if (url.pathname.startsWith("/get_summary")) {
-        const orig_sha = decodeURIComponent(url.searchParams.get('sign'));
-        let result = await db.prepare(
-          "SELECT content FROM blog_summary WHERE id = ?1"
-        ).bind(query).first("content");
-        if (!result) {
-          return new Response("no", {
-            headers: commonHeader
+      } else {
+        let resp = await db.prepare(
+          "SELECT summary FROM blog_summary WHERE id = ?1"
+        ).bind(query).first("summary");
+        if (!resp) {
+          const messages = [
+            {
+              role: "system", content: `
+          你是一个专业的文章摘要助手。你的主要任务是对各种文章进行精炼和摘要,帮助用户快速了解文章的核心内容。你读完整篇文章后,能够提炼出文章的关键信息,以及作者的主要观点和结论。
+          技能
+            精炼摘要:能够快速阅读并理解文章内容,提取出文章的主要关键点,用简洁明了的中文进行阐述。
+            关键信息提取:识别文章中的重要信息,如主要观点、数据支持、结论等,并有效地进行总结。
+            客观中立:在摘要过程中保持客观中立的态度,避免引入个人偏见。
+          约束
+            输出内容必须以中文进行。
+            必须确保摘要内容准确反映原文章的主旨和重点。
+            尊重原文的观点,不能进行歪曲或误导。
+            在摘要中明确区分事实与作者的意见或分析。
+          提示
+            不需要在回答中注明摘要(不需要使用冒号),只需要输出内容。
+          格式
+            你的回答格式应该如下:
+              这篇文章介绍了<这里是内容>
+          ` },
+            {
+              role: "user", content: result.length > 6000 ?
+                result.slice(0, 3000) + result.slice(-3000) :
+                result.slice(0, 6000)
+            }
+          ]
+
+          const answer = await env.AI.run('@cf/google/gemma-3-12b-it', {
+            messages,
+            stream: false,
           });
+          resp = answer.response
+          await db.prepare("UPDATE blog_summary SET summary = ?1 WHERE id = ?2")
+            .bind(resp, query).run();
         }
-        let result_sha = await sha(result);
-        if (result_sha != orig_sha) {
-          return new Response("no", {
-            headers: commonHeader
-          });
-        } else {
-          let resp = await db.prepare(
-            "SELECT summary FROM blog_summary WHERE id = ?1"
-          ).bind(query).first("summary");
-          if (!resp) {
-            const messages = [
-              {
-                role: "system", content: `
-            你是一个专业的文章摘要助手。你的主要任务是对各种文章进行精炼和摘要,帮助用户快速了解文章的核心内容。你读完整篇文章后,能够提炼出文章的关键信息,以及作者的主要观点和结论。
-            技能
-              精炼摘要:能够快速阅读并理解文章内容,提取出文章的主要关键点,用简洁明了的中文进行阐述。
-              关键信息提取:识别文章中的重要信息,如主要观点、数据支持、结论等,并有效地进行总结。
-              客观中立:在摘要过程中保持客观中立的态度,避免引入个人偏见。
-            约束
-              输出内容必须以中文进行。
-              必须确保摘要内容准确反映原文章的主旨和重点。
-              尊重原文的观点,不能进行歪曲或误导。
-              在摘要中明确区分事实与作者的意见或分析。
-            提示
-              不需要在回答中注明摘要(不需要使用冒号),只需要输出内容。
-            格式
-              你的回答格式应该如下:
-                这篇文章介绍了<这里是内容>
-            ` },
-              {
-                role: "user", content: result.length > 6000 ?
-                  result.slice(0, 3000) + result.slice(-3000) :
-                  result.slice(0, 6000)
-              }
-            ]
-  
-            const answer = await env.AI.run('@cf/qwen/qwen1.5-14b-chat-awq', {
-              messages,
-              stream: false,
-            });
-            resp = answer.response
-            await db.prepare("UPDATE blog_summary SET summary = ?1 WHERE id = ?2")
-              .bind(resp, query).run();
-          }
-          let is_vec = await db.prepare(
-            "SELECT `is_vec` FROM blog_summary WHERE id = ?1"
-          ).bind(query).first("is_vec");
-          if (is_vec == 0) {
-            const response = await env.AI.run(
-              "@cf/meta/m2m100-1.2b",
-              {
-                text: resp,
-                source_lang: "chinese", // defaults to english
-                target_lang: "english",
-              }
-            );
-            const { data } = await env.AI.run(
-              "@cf/baai/bge-base-en-v1.5",
-              {
-                text: response.translated_text,
-              }
-            );
-            let embeddings = data[0];
-            await env.mayx_index.upsert([{
-              id: query,
-              values: embeddings
-            }]);
-            await db.prepare("UPDATE blog_summary SET is_vec = 1 WHERE id = ?1")
-              .bind(query).run();
-          }
-          return new Response(resp, {
-            headers: commonHeader
-          });
+        let is_vec = await db.prepare(
+          "SELECT `is_vec` FROM blog_summary WHERE id = ?1"
+        ).bind(query).first("is_vec");
+        if (is_vec == 0) {
+          const response = await env.AI.run(
+            "@cf/meta/m2m100-1.2b",
+            {
+              text: resp,
+              source_lang: "chinese", // defaults to english
+              target_lang: "english",
+            }
+          );
+          const { data } = await env.AI.run(
+            "@cf/baai/bge-base-en-v1.5",
+            {
+              text: response.translated_text,
+            }
+          );
+          let embeddings = data[0];
+          await env.mayx_index.upsert([{
+            id: query,
+            values: embeddings
+          }]);
+          await db.prepare("UPDATE blog_summary SET is_vec = 1 WHERE id = ?1")
+            .bind(query).run();
         }
-      } else if (url.pathname.startsWith("/is_uploaded")) {
-        const orig_sha = decodeURIComponent(url.searchParams.get('sign'));
+        return new Response(resp, {
+          headers: commonHeader
+        });
+      }
+    } else if (url.pathname.startsWith("/is_uploaded")) {
+      const orig_sha = decodeURIComponent(url.searchParams.get('sign'));
+      let result = await db.prepare(
+        "SELECT content FROM blog_summary WHERE id = ?1"
+      ).bind(query).first("content");
+      if (!result) {
+        return new Response("no", {
+          headers: commonHeader
+        });
+      }
+      let result_sha = await sha(result);
+      if (result_sha != orig_sha) {
+        return new Response("no", {
+          headers: commonHeader
+        });
+      } else {
+        return new Response("yes", {
+          headers: commonHeader
+        });
+      }
+    } else if (url.pathname.startsWith("/upload_blog")) {
+      if (request.method == "POST") {
+        const data = await request.text();
         let result = await db.prepare(
           "SELECT content FROM blog_summary WHERE id = ?1"
         ).bind(query).first("content");
         if (!result) {
-          return new Response("no", {
-            headers: commonHeader
-          });
-        }
-        let result_sha = await sha(result);
-        if (result_sha != orig_sha) {
-          return new Response("no", {
-            headers: commonHeader
-          });
-        } else {
-          return new Response("yes", {
-            headers: commonHeader
-          });
-        }
-      } else if (url.pathname.startsWith("/upload_blog")) {
-        if (request.method == "POST") {
-          const data = await request.text();
-          let result = await db.prepare(
+          await db.prepare("INSERT INTO blog_summary(id, content) VALUES (?1, ?2)")
+            .bind(query, data).run();
+          result = await db.prepare(
             "SELECT content FROM blog_summary WHERE id = ?1"
           ).bind(query).first("content");
-          if (!result) {
-            await db.prepare("INSERT INTO blog_summary(id, content) VALUES (?1, ?2)")
-              .bind(query, data).run();
-            result = await db.prepare(
-              "SELECT content FROM blog_summary WHERE id = ?1"
-            ).bind(query).first("content");
-          }
-          if (result != data) {
-            await db.prepare("UPDATE blog_summary SET content = ?1, summary = NULL, is_vec = 0 WHERE id = ?2")
-              .bind(data, query).run();
-          }
-          return new Response("OK", {
-            headers: commonHeader
-          });
-        } else {
-          return new Response("need post", {
-            headers: commonHeader
-          });
         }
-      } else if (url.pathname.startsWith("/count_click")) {
-        let id_md5 = await md5(query);
-        let count = await counter_db.prepare("SELECT `counter` FROM `counter` WHERE `url` = ?1")
-          .bind(id_md5).first("counter");
-        if (url.pathname.startsWith("/count_click_add")) {
-          if (!count) {
-            await counter_db.prepare("INSERT INTO `counter` (`url`, `counter`) VALUES (?1, 1)")
-              .bind(id_md5).run();
-            count = 1;
-          } else {
-            count += 1;
-            await counter_db.prepare("UPDATE `counter` SET `counter` = ?1 WHERE `url` = ?2")
-              .bind(count, id_md5).run();
-          }
-        }
-        if (!count) {
-          count = 0;
+        if (result != data) {
+          await db.prepare("UPDATE blog_summary SET content = ?1, summary = NULL, is_vec = 0 WHERE id = ?2")
+            .bind(data, query).run();
         }
-        return new Response(count, {
+        return new Response("OK", {
           headers: commonHeader
         });
-      } else if (url.pathname.startsWith("/suggest")) {
-        let resp = [];
-        let update_time = url.searchParams.get('update');
-        if (update_time) {
-          let result = await env.mayx_index.getByIds([
-            query
-          ]);
-          if (result.length) {
-            let cache = await db.prepare("SELECT `id`, `suggest`, `suggest_update` FROM `blog_summary` WHERE `id` = ?1")
-              .bind(query).first();
-            if (!cache.id) {
-              return Response.json(resp, {
-                headers: commonHeader
-              });
-            }
-            if (update_time != cache.suggest_update) {
-              resp = await env.mayx_index.query(result[0].values, { topK: 6 });
-              resp = resp.matches;
-              resp.splice(0, 1);
-              await db.prepare("UPDATE `blog_summary` SET `suggest_update` = ?1, `suggest` = ?2 WHERE `id` = ?3")
-                .bind(update_time, JSON.stringify(resp), query).run();
-              commonHeader["x-suggest-cache"] = "miss"
-            } else {
-              resp = JSON.parse(cache.suggest);
-              commonHeader["x-suggest-cache"] = "hit"
-            }
+      } else {
+        return new Response("need post", {
+          headers: commonHeader
+        });
+      }
+    } else if (url.pathname.startsWith("/count_click")) {
+      let id_md5 = await md5(query);
+      let count = await counter_db.prepare("SELECT `counter` FROM `counter` WHERE `url` = ?1")
+        .bind(id_md5).first("counter");
+      if (url.pathname.startsWith("/count_click_add")) {
+        if (!count) {
+          await counter_db.prepare("INSERT INTO `counter` (`url`, `counter`) VALUES (?1, 1)")
+            .bind(id_md5).run();
+          count = 1;
+        } else {
+          count += 1;
+          await counter_db.prepare("UPDATE `counter` SET `counter` = ?1 WHERE `url` = ?2")
+            .bind(count, id_md5).run();
+        }
+      }
+      if (!count) {
+        count = 0;
+      }
+      return new Response(count, {
+        headers: commonHeader
+      });
+    } else if (url.pathname.startsWith("/suggest")) {
+      let resp = [];
+      let update_time = url.searchParams.get('update');
+      if (update_time) {
+        let result = await env.mayx_index.getByIds([
+          query
+        ]);
+        if (result.length) {
+          let cache = await db.prepare("SELECT `id`, `suggest`, `suggest_update` FROM `blog_summary` WHERE `id` = ?1")
+            .bind(query).first();
+          if (!cache.id) {
+            return Response.json(resp, {
+              headers: commonHeader
+            });
+          }
+          if (update_time != cache.suggest_update) {
+            resp = await env.mayx_index.query(result[0].values, { topK: 6 });
+            resp = resp.matches;
+            resp.splice(0, 1);
+            await db.prepare("UPDATE `blog_summary` SET `suggest_update` = ?1, `suggest` = ?2 WHERE `id` = ?3")
+              .bind(update_time, JSON.stringify(resp), query).run();
+            commonHeader["x-suggest-cache"] = "miss"
+          } else {
+            resp = JSON.parse(cache.suggest);
+            commonHeader["x-suggest-cache"] = "hit"
           }
-          resp = resp.map(respObj => {
-            respObj.id = encodeURI(respObj.id);
-            return respObj;
-          });
         }
-        return Response.json(resp, {
-          headers: commonHeader
+        resp = resp.map(respObj => {
+          respObj.id = encodeURI(respObj.id);
+          return respObj;
         });
-      } else if (url.pathname.startsWith("/***")) {
-        let resp = await db.prepare("SELECT `id`, `summary` FROM `blog_summary` WHERE `suggest_update` IS NOT NULL").run();
-        const resultObject = resp.results.reduce((acc, item) => {
-          acc[item.id] = item.summary; // 将每个项的 id 作为键,summary 作为值
-          return acc;
-        }, {}); // 初始值为空对象
-        return Response.json(resultObject);
-      } else {
-        return Response.redirect("https://mabbs.github.io", 302)
       }
+      return Response.json(resp, {
+        headers: commonHeader
+      });
+    } else {
+      return Response.redirect("https://mabbs.github.io", 302)
     }
-  }
+  }
+}

+ 1 - 1
_tools/envs_post-receive

@@ -5,7 +5,7 @@ git --work-tree=/home/mayx/blog --git-dir=/home/mayx/blog.git checkout -f
 cd blog
 mkdir Mabbs
 curl -L -o Mabbs/README.md https://github.com/Mabbs/Mabbs/raw/main/README.md
-bundle2.7 exec jekyll build -d ../public_html
+bundle exec jekyll build -d ../public_html
 tar czvf MayxBlog.tgz --exclude-vcs ../public_html/
 mv MayxBlog.tgz ../public_html/
 cd ../public_html/