2025年08月27日 19:47:46

折腾/memos

想到了如何通过CF Workers 让新版Memos 兼容广场,以下代码AI生成

新建一个workers

const NEW_ORIGIN = "https://memos.ee";

const KV = MEMOS_MAP; // KV 绑定名

const CORS_HEADERS = {

 "Access-Control-Allow-Origin": "*",

 "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",

 "Access-Control-Allow-Headers": "Content-Type, Authorization"

};



/* ---------- 工具:原子自增数字 id ---------- */

async function allocNumericId() {

 const val = await KV.get("next_id");

 const next = val ? Number(val) + 1 : 10000;

 await KV.put("next_id", String(next));

 return next;

}



/* ---------- 工具:建立 UID↔数字 双向映射 ---------- */

async function getNumericId(uid) {

 let num = await KV.get(`uid:${uid}`);

 if (!num) {

 num = await allocNumericId();

 await KV.put(`uid:${uid}`, String(num));

 await KV.put(`num:${num}`, uid);

 }

 return Number(num);

}



/* ---------- 用户信息内存缓存 ---------- */

const userCache = new Map();

async function fetchUser(uid) {

 if (userCache.has(uid)) return userCache.get(uid);

 const r = await fetch(`${NEW_ORIGIN}/api/v1/users/${uid}`);

 if (!r.ok) return null;

 const data = await r.json();

 userCache.set(uid, data);

 return data;

}



addEventListener("fetch", (event) => {

 event.respondWith(handleRequest(event.request));

});



async function handleRequest(req) {

 const url = new URL(req.url);



 if (req.method === "OPTIONS") {

 return new Response(null, { status: 204, headers: CORS_HEADERS });

 }



 /bin /config /dev /etc /home /lib /media /memos /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var ---------- 旧 /api/v1/memo 兼容 ---------- */

 if (url.pathname === "/api/v1/memo") {

 const creatorId = url.searchParams.get("creatorId") || "1";

 const rowStatus = url.searchParams.get("rowStatus") || "NORMAL";

 const limit = Number(url.searchParams.get("limit") || "10");

 const offset = Number(url.searchParams.get("offset") || "0");



 const need = offset + limit;

 const up = new URL("/api/v1/memos", NEW_ORIGIN);

 up.searchParams.set("parent", `users/${creatorId}`);

 up.searchParams.set("state", rowStatus);

 up.searchParams.set("pageSize", Math.min(need, 1000));



 const r = await fetch(up, {

 headers: { Authorization: req.headers.get("Authorization") || "" }

 });

 if (!r.ok) return new Response(await r.text(), { status: r.status });



 const { memos = [] } = await r.json();

 const slice = memos.slice(offset, offset + limit);



 /bin /config /dev /etc /home /lib /media /memos /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var 给每条记录分配数字 id 并缓存 */

 for (const m of slice) {

 const uid = m.name.split("/")[1];

 await getNumericId(uid);

 }



 /bin /config /dev /etc /home /lib /media /memos /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var 读 KV 拿到数字 id */

 const uidSet = new Set(slice.map(m => Number(m.creator.split("/")[1])));

 await Promise.all([...uidSet].map(uid => fetchUser(uid)));



 const legacy = await Promise.all(

 slice.map(async m => {

 const uid = Number(m.creator.split("/")[1]);

 const user = userCache.get(uid);

 const num = await getNumericId(m.name.split("/")[1]);

 return {

 id: num, // ← 数字 id

 rowStatus: m.state,

 creatorId: uid,

 createdTs: Math.floor(new Date(m.createTime).getTime() / 1000),

 updatedTs: Math.floor(new Date(m.updateTime).getTime() / 1000),

 displayTs: Math.floor(new Date(m.displayTime).getTime() / 1000),

 content: m.content,

 visibility: m.visibility.toUpperCase(),

 pinned: m.pinned,

 parent: null,

 creatorName: user?.displayName || "",

 creatorUsername: user?.username || "",

 resourceList: m.attachments || [],

 relationList: m.relations || []

 };

 })

 );



 return new Response(JSON.stringify(legacy), {

 headers: { "content-type": "application/json", ...CORS_HEADERS }

 });

 }



 /bin /config /dev /etc /home /lib /media /memos /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var ---------- /m/ 双向 301 ---------- */

 const match = url.pathname.match(/^\/m\/([^/]+)$/);

 if (match) {

 const slug = match[1];



 // 1) 纯数字 → 查 UID

 if (/^\d+$/.test(slug)) {

 const uid = await KV.get(`num:${slug}`);

 if (uid) return Response.redirect(`${NEW_ORIGIN}/memos/${uid}`, 301);

 else return new Response("Not Found", { status: 404 });

 }



 // 2) UID → 查数字并写入 KV

 const num = await getNumericId(slug);

 return Response.redirect(`${NEW_ORIGIN}/m/${num}`, 301);

 }



 /bin /config /dev /etc /home /lib /media /memos /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var ---------- 其余全部 301 ---------- */

 return Response.redirect(NEW_ORIGIN + url.pathname + url.search, 301);

}

https://memos.ee更改为自己的memos地址 把代码粘贴进去 点击部署

在KV中新建一个数据库命名空间随便填写

在workers中绑定命名空间 变量名称MEMOS_MAPKV 命名空间选择上面创建好的

最好给workers绑定好域名例如 https://old.memos.ee

使用memobbs 广场演示 https://memobbs.memos.ee

2025年08月27日 10:21:06

吐槽

看到七牛云有越南轻量云忍不住搞了一台看看,结果测了一下很失望,这线路简直太垃圾了

https://paste.spiritlhl.net/#/show/eMApU.txt

2025年08月26日 16:32:08

感觉使用Memos的人少了很多!~广场上都没人了.

2025年08月26日 09:49:33

这种回拨过去是空号的电话号码是怎么打到我手机来的🤔

2025年08月24日 19:32:04

让ai写了个扫描三字母的im域名,在没有注册的域名中挑了两个。注册完就后悔了,我真是手贱。得剁手。

2025年08月20日 14:08:12

[转]Let's Encrypt 颁发证书中包含的 CRL 链接完全被墙

V2EX 网友发现 (https://v2ex.com/t/1153589) Let's Encrypt 证书所依赖的证书吊销列表(CRL)服务器域名在中国大陆遭到完全屏蔽。发帖人通过 curl 命令演示,在访问使用 Let's Encrypt 证书的网站时,由于客户端无法连接到被墙的 CRL 服务器来验证证书是否已被吊销,导致 TLS 握手失败,并返回“吊销服务器离线”的错误。这是独立于今天凌晨443端口大阻断事件之外的一个新的问题。

由于Let's Encrypt免费证书应用广泛,将会影响部分对安全要求较高的桌面或移动应用程序,如果其网络堆栈强制检查 CRL,则可能无法正常访问相关服务。但对于大多数普通用户的日常网页浏览影响较小,因为主流浏览器通常不采用实时在线检查 CRL 的方式。而是通过“CRLSet”的机制批量下发已知的被吊销证书信息。

尽管如此,对于国内网站和开发者来说,这仍是一个潜在的稳定性和可访问性风险。

2025年08月20日 09:32:05

vidhub也有了安卓客户端,好像还不需要会员,那我买了个🍎版的会员算啥子

2025年08月19日 21:49:39

母猫肚子大了似乎是有了😁

2025年08月17日 15:35:54

又下雨了,哪儿都去不了。睡觉睡觉

2025年08月16日 16:18:16

「Debian系统挂载WebDav网盘」

https://www.imsun.org/archives/1761.html

From「老孙博客」