前言

对我个人来说, 这2个月可以说是令我痛苦的2个月了, 有史以来第一次”挂科”.

30%的原因出在语言上, 这门课需要理解题目并分析. 偏理论, 我缺乏这方面的复习经验.另外40%的原因出在我没有给这门课很高的重视, 我的作业成绩很高, 轻视考试的深度和难度了. 剩下30%的原因是我单打独斗了. 我给这门课程留下的预习复习时间是别的课程的的1/10甚至更少.

遭遇失败也不能找借口, 要找原因, 所以这学期打算找学习伙伴, 并且学习重心放在这门课上.

说起来很轻巧, 实际上在整整1个月的假期中 (12.13考完-12.27出这门课的成绩-1.13开学)情绪低落了大半个月, 什么都没有做, 也什么都没有做好. 仿佛回到了本科时候被论文和考研压得喘不过气的日子. 做梦惊醒, 呼吸费劲成为常态… 然后养成了现在每天吃维生素D的习惯.

苦中得乐

谢谢亲人和好友, 在我低落的时候开导我安慰我, 让我不至于一蹶不振. 和好友一起在家吃火锅, 打雪仗, 打游戏. 让我的情绪稳定了很多.

去IGA买了三文鱼, 自己在冰箱冻了半个月来杀死寄生虫. 基于售卖的三文鱼的样子, 我得出了一个结论, 国内的三文鱼都是达不到生食品质的, 均为冷冻杀死寄生虫后再拿出来售卖的, 自己冻会更便宜和健康.

吃完竟然不会拉肚子
吃完竟然不会拉肚子

还有最大的一个改变就是驾驶技术, 飞速增长. Montreal迈入冬季, 一周可能有6天都没有太阳, 在体感温度大约为-15℃的天气还是开车出门购物更合适. 偶尔接上朋友去购物, 经常要跑高速. 争取这学期考下来驾照.

博客装修

从上年年底友邻竹子在Mastodon转发了OpenHeart相关的帖子后我尝试部署了一下. 出现了无法使用的问题, 近日仔细查找后发现提供的worker.js多了一次编码, 导致前端GET和POST的地址和KV中所存储的不一样. 在这里提供一些参考性的建议.

Openheart整个流程为: 前端传递-后端存储-前端获取. 当在前端点赞后, 通过脚本(worker.js)创建键值对(KV)进行存储, 并通过前端js来刷新显示最新的点赞计数. 现在来详细的进行部署.

后端存储

本文教程使用Astro和Cloudflare

首先创建一个存储库: 存储和数据库-KV-创建. 如果想开箱即用, 名字就和我一样设置为KV即可.

接下来创建Cloudflare Workers, 代码为:

const instruction = `.^⋁^.
'. .'
\`
OpenHeart protocol API
`;
export default {
async fetch(request, env) {
if (request.method === 'OPTIONS') {
return new Response(null, { headers });
}
if (request.method === 'GET') {
if (url(request).pathname === '/') {
return new Response(instruction, { headers });
} else {
return handleGet(request, env);
}
}
if (request.method === 'POST') return handlePost(request, env);
},
};
const headers = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET,POST",
"Access-Control-Max-Age": "86400",
};
function error(text, code = 400) {
return new Response(text, { headers, status: code });
}
function url(request) {
return new URL(request.url);
}
function ensureEmoji(emoji) {
const segments = Array.from(
new Intl.Segmenter({ granularity: 'grapheme' }).segment(emoji.trim())
);
const parsedEmoji = segments.length > 0 ? segments[0].segment : null;
if (/\p{Emoji}/u.test(parsedEmoji)) return parsedEmoji;
}
// Generate KV keys with single encoding
function generateKey(domain, uid, emoji) {
const encodedDomain = encodeURIComponent(domain); // Single encoding
const encodedUid = encodeURIComponent(uid); // Single encoding
return `${encodedDomain}:${encodedUid}:${emoji}`;
}
async function handleGet(request, env) {
const [domain, ...uidParts] = url(request).pathname.slice(1).split('/');
const decodedDomain = decodeURIComponent(domain); // 解码
const decodedUid = decodeURIComponent(uidParts.join('/')); // 解码
const prefix = `${encodeURIComponent(decodedDomain)}:${encodeURIComponent(decodedUid)}`; // 单次编码
console.log("GET Prefix:", prefix); // 调试日志
const res = await env.KV.list({ prefix });
const list = {};
for (const key of res.keys) {
const value = await env.KV.get(key.name);
const [, uidDecoded, emoji] = key.name.split(':').map(decodeURIComponent); // 解码键值
const obj = list[uidDecoded] || {};
obj[emoji] = Number(value);
list[uidDecoded] = obj;
}
console.log("GET Response Data:", list); // 调试日志
return new Response(
JSON.stringify(list[decodedUid] || {}),
{ headers: { ...headers, 'Content-Type': 'application/json;charset=UTF-8' } }
);
}
async function handlePost(request, env) {
const urlObject = url(request);
const path = urlObject.pathname.slice(1);
if (path === '') return error('pathname missing');
const [domain, ...uidParts] = path.split('/');
const decodedDomain = decodeURIComponent(domain); // 解码
const decodedUid = decodeURIComponent(uidParts.join('/')); // 解码
const emoji = ensureEmoji(await request.text());
if (!emoji) return error('request body should contain an emoji');
// Generate the key with single encoding
const key = generateKey(decodedDomain, decodedUid, emoji);
console.log("POST Generated Key:", key); // 调试日志
const currentCount = Number(await env.KV.get(key) || 0);
await env.KV.put(key, currentCount + 1);
return new Response('recorded', { headers });
}

在推送后回到Worker的设置界面, 绑定刚刚创建的KV, 名称均设置为KV. 当我使用自定义域的时候出现了问题, 因此我使用了默认的workers.dev域名.

前端传递/获取

接下来就是前端的传递. 在Openheart的协议中, 你需要在访问API时提供一个target和uid, 这个target可以是任何字符串, 于是我设置为当前博文的链接并将uid默认为like. 所以根据官方的文档, 可以通过以下命令来进行POST(点赞)和GET(获取点赞数)操作:

Terminal window
curl -d '💯' -X POST 'https://xxx.workers.dev/xxx.example.com/like'
recorded%
curl 'https://xxx.workers.dev/xxx.example.com/like'
{"❤️":1}%

在这个过程中, 特殊字符, 例如:/会根据worker.js设置来影响对KV的存储. 因此需要对url编码一次来转换这些特殊字符.

---
// Get current URL dynamically
const currentUrl = Astro.url.toString();
---
<div class="like">
<open-heart href={`https://xxx.workers.dev/${currentUrl}/like`} data-umami-event="Like" emoji="❤️">❤️</open-heart>
</div>

这样的请求和存储就保持一致了, 你可以通过开发者工具来确认这一点

最后就是前端样式, 由于我的基于benji的二次修改, 这是benji的文章链接, 可以自行访问获取.

书影音游

颓废的这段时间都用来看小说了, 重新看了一遍龙族.

Failed to fetch resource data.

第七集的抽帧让我感到惊艳, 就是最后的结尾略显仓促, 杰斯陪葬的动机我感觉不是很清晰.

圣诞时候和大家一起用Switch玩了.

Failed to fetch resource data.

一部不错的魔幻史诗, 在刚出的时候觉得名字好听体验了一下, 被冗长的任务打断了. 现在创新号重新回坑过完了剧情, 很棒.

Webmention

手动提交
如果你在自己的博客或社交网络中提到了这篇文章,并发送 Webmention,这里会自动展示那些回复、喜欢和转发。