。
- 而且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
-Read more ->
-```
- 就可以了。只是这样有个缺点,这样写的订阅文件不够“纯粹”,用验证器验证会显示“[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引入呢。
-
-# 感想
- 虽然有一些技术会因为各种各样的原因消失,但这不代表我们就要妥协一些东西,总有一些不同的技术可以解决相同的问题,所以我们只需要用其他的技术去实现就好了。不过这也是没办法的事情,毕竟没人能改变浏览器厂商们的决策啊😂。
\ No newline at end of file
diff --git a/_tools/ai-summary.js b/_tools/ai-summary.js
index 2284418..4d6999d 100644
--- a/_tools/ai-summary.js
+++ b/_tools/ai-summary.js
@@ -1,381 +1,388 @@
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',
- }
- if (url.pathname.startsWith("/ai_chat")) {
- // 获取请求中的文本数据
- if (!(request.headers.get('accept') || '').includes('text/event-stream')) {
- return Response.redirect("https://mabbs.github.io", 302);
+ 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 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 = "无法获取到文章内容";
+ if (url.pathname.startsWith("/ai_chat")) {
+ // 获取请求中的文本数据
+ if (!(request.headers.get('accept') || '').includes('text/event-stream')) {
+ return Response.redirect("https://mabbs.github.io", 302);
}
- 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 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 = "无法获取到文章内容";
+ }
}
+ 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'
+ // }
+ // })
}
- 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", {
+ if (query == "null") {
+ return new Response("id cannot be none", {
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 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
- });
- }
- 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/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 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
- });
- }
- } 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();
+ if (url.pathname.startsWith("/summary")) {
let 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(
+ 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 stream = await env.AI.run('@cf/qwen/qwen1.5-14b-chat-awq', {
+ 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
+ });
+ }
+ 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
+ });
+ }
+ } 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 != 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;
+ 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 {
- count += 1;
- await counter_db.prepare("UPDATE `counter` SET `counter` = ?1 WHERE `url` = ?2")
- .bind(count, id_md5).run();
+ return new Response("need post", {
+ headers: commonHeader
+ });
}
- }
- 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 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 {
- resp = JSON.parse(cache.suggest);
- commonHeader["x-suggest-cache"] = "hit"
+ count += 1;
+ await counter_db.prepare("UPDATE `counter` SET `counter` = ?1 WHERE `url` = ?2")
+ .bind(count, id_md5).run();
}
}
- resp = resp.map(respObj => {
- respObj.id = encodeURI(respObj.id);
- return respObj;
+ 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
+ });
+ } 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)
}
- }
-}
\ No newline at end of file
+ }
\ No newline at end of file
diff --git a/_tools/envs_post-receive b/_tools/envs_post-receive
index 6212158..c9465eb 100644
--- a/_tools/envs_post-receive
+++ b/_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
-bundle exec jekyll build -d ../public_html
+bundle2.7 exec jekyll build -d ../public_html
tar czvf MayxBlog.tgz --exclude-vcs ../public_html/
mv MayxBlog.tgz ../public_html/
cd ../public_html/
diff --git a/assets/css/feed.css b/assets/css/feed.css
deleted file mode 100644
index a81b03e..0000000
--- a/assets/css/feed.css
+++ /dev/null
@@ -1,131 +0,0 @@
-@namespace atom "http://www.w3.org/2005/Atom";
-@namespace content "http://purl.org/rss/1.0/modules/content/";
-@namespace dc "http://purl.org/dc/elements/1.1/";
-
-body,
-rss,
-atom|feed {
- font-family: -apple-system, BlinkMacSystemFont,
- "Segoe UI", Roboto,
- "Noto Sans SC", "PingFang SC",
- "Microsoft YaHei", Arial, sans-serif;
- background: #f4f5f7;
- color: #222;
- margin: 0;
- padding: 2em 1em;
- /* 左右内边距 1em,竖屏不贴边 */
- font-size: 14px;
- line-height: 1.6;
-
- max-width: 780px;
- /* 最大宽度,桌面端居中 */
- margin-left: auto;
- margin-right: auto;
-}
-
-channel>title,
-atom|feed>atom|title {
- display: block;
- font-size: 1.7em;
- font-weight: 700;
- margin: 0 0 1.2em 0;
- text-align: center;
- letter-spacing: -0.01em;
-}
-
-item,
-atom|entry {
- display: block;
- background: #ffffff;
- padding: 1.1em 1.2em;
- margin-bottom: 1.1em;
- border-radius: 14px;
- box-shadow:
- 0 8px 24px rgba(0, 0, 0, 0.04),
- 0 2px 6px rgba(0, 0, 0, 0.03);
-}
-
-item>title,
-atom|entry>atom|title {
- display: block;
- font-size: 1.15em;
- font-weight: 600;
- margin-bottom: 0.45em;
- color: #111;
- letter-spacing: -0.01em;
-}
-
-item>description,
-atom|entry>atom|summary {
- display: block;
- color: #555;
- line-height: 1.65;
- max-height: 16.5em;
- overflow: hidden;
- position: relative;
-}
-
-item>description::after,
-atom|entry>atom|summary::after {
- content: "…";
- position: absolute;
- right: 0.3em;
- bottom: 0;
- padding-left: 1.5em;
- background: linear-gradient(to right,
- rgba(255, 255, 255, 0),
- #ffffff 70%);
- font-weight: 600;
-}
-
-link,
-guid,
-pubDate,
-author,
-category,
-comments,
-source,
-enclosure,
-content|encoded,
-dc|creator,
-lastBuildDate,
-atom|id,
-atom|link,
-atom|updated,
-atom|published,
-atom|author,
-atom|category,
-atom|rights,
-atom|content,
-language,
-generator {
- display: none;
-}
-
-channel>description,
-atom|feed>atom|subtitle {
- display: block;
- margin: 0.4em 0 2em 0;
- /* 与条目明显拉开 */
- text-align: center;
- color: #666;
- font-size: 0.95em;
-}
-
-channel>title,
-atom|feed>atom|title {
- margin-bottom: 0.4em;
- /* 原来较紧,这里放松 */
-}
-
-atom|feed>atom|subtitle::after,
-channel>description::after {
- content: "这是一个订阅源(Feed)。复制当前URL到任何支持 Atom/RSS 的阅读器,即可订阅本博客的最新文章。\A以下展示了此订阅源包含的最新文章:";
- display: block;
- text-align: center;
- font-size: 0.95em;
- color: #666;
- margin: 1em 0 2em 0;
- line-height: 1.5;
- white-space: pre-wrap;
-}
\ No newline at end of file
diff --git a/assets/css/xslt.css b/assets/css/xslt.css
deleted file mode 100644
index ac981f3..0000000
--- a/assets/css/xslt.css
+++ /dev/null
@@ -1,35 +0,0 @@
-@namespace xsl "http://www.w3.org/1999/XSL/Transform";
-
-xsl|template {
- display: none !important;
-}
-
-:root {
- display: flex !important;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- min-height: 100vh;
- background-color: #f8f9fa;
- margin: 0;
- padding: 2em 1em;
- font-family: system-ui, -apple-system, sans-serif;
- box-sizing: border-box;
- margin-left: max(1em, env(safe-area-inset-left));
- margin-right: max(1em, env(safe-area-inset-right));
-}
-
-:root::before {
- content: "💀 这个 XSLT 模板已被谷歌 (Chrome) 杀死";
- display: block;
- color: #d93025;
- font-size: 24px;
- font-weight: 800;
- padding: 20px;
- border: 2px solid #d93025;
- border-radius: 8px;
- background: #fff1f0;
- margin-bottom: 10px;
- box-shadow: 0 4px 12px rgba(217, 48, 37, 0.1);
- text-align: center;
-}
\ No newline at end of file
diff --git a/index.html b/index.html
index 35942de..f880503 100644
--- a/index.html
+++ b/index.html
@@ -67,7 +67,6 @@ image: https://screenshot.mayx.eu.org/
Mayx的公开服务
凯露&危险生存( CHS | JA | KO )
- Brainfuck可视化演示
留言板
Links
代理列表
diff --git a/other_repo_list.md b/other_repo_list.md
deleted file mode 100644
index e8036eb..0000000
--- a/other_repo_list.md
+++ /dev/null
@@ -1,9 +0,0 @@
----
-layout: default
-title: 其他Git仓库镜像列表
----
-
-# 其他Git仓库镜像列表
-目前已有的社区/个人类型实例托管Git仓库共有{{ site.data.other_repo_list | size }}个:
-{% for item in site.data.other_repo_list %}- <{{ item.repo_url }}>
-{% endfor %}
\ No newline at end of file
diff --git a/proxylist.md b/proxylist.md
index ab397e0..819bca8 100644
--- a/proxylist.md
+++ b/proxylist.md
@@ -18,12 +18,10 @@ title: 代理列表
{% for item in site.data.proxylist.mirrors %}- <{{ item }}>
{% endfor %}
-# Git仓库列表
-{% for item in site.data.proxylist.repos %}- <{{ item }}>
+# Git列表
+{% for item in site.data.proxylist.gits %}- <{{ item }}>
{% endfor %}
-其他更多社区/个人类型实例托管的Git仓库列表参见[这里](/other_repo_list.html)
-
# 服务架构
```mermaid
graph LR;
diff --git a/rss.xml b/rss.xml
index a2ed4dd..2f09425 100644
--- a/rss.xml
+++ b/rss.xml
@@ -3,7 +3,6 @@
-
{{ site.title | xml_escape }}