标签 Cloudflare KV 下的文章

已经测试了一段时间,没有发现什么大问题,并且还在持续更新中,基于 Cloudflare 全生态部署。

  • 数据库使用 D1 SQL
  • 登录 token 基于 KV Workers 进行校验
  • 接口基于 Cloudflare Workers 进行构建
  • 管理后台及评论端 js 托管到 Cloudflare Pages

我已经将我的网站全面接入该程序,测试链接:

1. 评论端

2. 后台管理


功能:

功能方面足以满足多站点使用。

  • 极速响应:基于 Cloudflare 全球边缘网络
  • 安全可靠:内置管理员认证、CORS 保护等
  • 易于集成:提供完整的 REST API,支持定制前端评论组件
  • 管理后台:提供完善的后台管理界面,方便评论管理
  • 评论审核:支持手动审核评论,防止垃圾评论
  • 禁止评论:支持屏蔽 IP 和拉黑邮箱
  • 邮件通知:集成各大邮箱厂商(逐步接入),支持自定义通知模板


文档中介绍了部署的流程,感兴趣的佬友可以试试看,顺便提交一下反馈,还在不断优化。

支持从其他评论框架进行迁移,文档中均有说明。


📌 转载信息
原作者:
anghunk
转载时间:
2026/1/21 22:14:01

这两天死了么很火,作为小白的我也想试试,但是收费了,花钱不存在的。
纯小白,代码 ai 写的,我不会。简简单单的实现了功能,里面还可以加很多功能,注册用户,多用户通知,我就不为难 ai 了,交给你们这群程序员了~嘿嘿~

原理就是签到证明自己活着。

实现:每天都要充电,iPhone 用户插电运行快捷指令,访问固定域名,记录下来,然后到期没有再运行这个快捷指令就证明死了。发 bark 给接收人。

需要用到 Cloudflare 的 worker 和 kv,新建 kv 名称写 CLOCKIN_KV,里面的值是账号密码,照着抄就行。


worker 绑定要写对变量,如图:


worker 中设置运行时间,我设置的是一天两次 0 9,21 * * * 用的白嫖的域名。


iPhone 快捷指令就是一个简单的请求,我的用户名是 mengnimen 密码是 123456,到时候根据你自己在 kv 里设置的值自己改。

https://你的域名/clockin?user=mengnimen&pwd=123456

快捷新建一个 获取 URL 内容 即可。


在自动化中添加一个充电运行的,记得把立即运行打开

以后就是每次充电运行,记录你还活着,一天不记录,就会发通知给 bark 接收人

纯小白,代码 ai 写的,我不会。代码中需要添加 bark 地址,多长时间认为活着也可以改,自己看注释就行了。简简单单的实现了功能,里面还可以加很多功能,注册用户,多用户通知,我就不为难 ai 了,交给你们这群程序员了~嘿嘿

export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);
    const path = url.pathname;

    try {
      if (path === "/" || path === "/health") return json({ ok: true });

      if (path === "/clockin") return await handleClockin(url, env);
      if (path === "/check") return await handleCheck(url, env);

      return json({ ok: false, error: "Not Found" }, 404);
    } catch (e) {
      return json({ ok: false, error: e?.message || String(e) }, 500);
    }
  },

  async scheduled(event, env, ctx) {
    ctx.waitUntil(runTimeoutCheck(env));
  },
};

const CONFIG = {
  ALERT_AFTER_DAYS: 1,
  MIN_ALERT_INTERVAL_HOURS: 6,
  DEFAULT_NOTIFY: { type: "bark", url: "" }, // 这里填默认 bark 或钉钉
  TITLE: "死了",
  NOTIFY_ON_CLOCKIN: false,
};

async function handleClockin(url, env) {
  const user = (url.searchParams.get("user") || "").trim();
  const pwd = (url.searchParams.get("pwd") || "").trim();
  if (!user || !pwd) return json({ ok: false, error: "Missing user or pwd" }, 400);

  const ok = await verifyUserPlain(user, pwd, env);
  if (!ok) return json({ ok: false, error: "Auth failed" }, 401);

  const now = Date.now();
  await env.CLOCKIN_KV.put(kLastTime(user), String(now));

  if (CONFIG.NOTIFY_ON_CLOCKIN) {
    await sendNotify(env, user, { title: "还活着", body: `已记录:${formatTime(now)}` });
  }

  return json({ ok: true, user, last_time: now, last_time_text: formatTime(now) });
}

async function handleCheck(url, env) {
  const user = (url.searchParams.get("user") || "").trim();
  const pwd = (url.searchParams.get("pwd") || "").trim();
  if (!user || !pwd) return json({ ok: false, error: "Missing user or pwd" }, 400);

  const ok = await verifyUserPlain(user, pwd, env);
  if (!ok) return json({ ok: false, error: "Auth failed" }, 401);

  const now = Date.now();
  const lastStr = await env.CLOCKIN_KV.get(kLastTime(user));
  const lastMs = lastStr ? Number(lastStr) : null;

  const thresholdMs = CONFIG.ALERT_AFTER_DAYS * 24 * 60 * 60 * 1000;
  return json({
    ok: true,
    user,
    now,
    now_text: formatTime(now),
    last_time: lastMs,
    last_time_text: lastMs ? formatTime(lastMs) : null,
    since_hours: lastMs ? round2((now - lastMs) / 3600000) : null,
    threshold_days: CONFIG.ALERT_AFTER_DAYS,
    is_timeout: lastMs ? (now - lastMs > thresholdMs) : true,
  });
}

async function runTimeoutCheck(env) {
  const now = Date.now();
  const thresholdMs = CONFIG.ALERT_AFTER_DAYS * 24 * 60 * 60 * 1000;
  const minIntervalMs = CONFIG.MIN_ALERT_INTERVAL_HOURS * 60 * 60 * 1000;

  let cursor = undefined;
  let checked = 0;
  let alerted = 0;

  while (true) {
    const listRes = await env.CLOCKIN_KV.list({ prefix: "user_pwd:", cursor });
    cursor = listRes.cursor;

    for (const k of listRes.keys) {
      const user = k.name.slice("user_pwd:".length);
      checked++;

      const lastStr = await env.CLOCKIN_KV.get(kLastTime(user));
      const lastMs = lastStr ? Number(lastStr) : 0;

      const timeout = !lastMs || (now - lastMs > thresholdMs);
      if (!timeout) continue;

      const lastAlertStr = await env.CLOCKIN_KV.get(kLastAlert(user));
      const lastAlertMs = lastAlertStr ? Number(lastAlertStr) : 0;
      if (lastAlertMs && (now - lastAlertMs < minIntervalMs)) continue;

      const hours = lastMs ? round2((now - lastMs) / 3600000) : null;
      const body = lastMs
        ? `你已超过 ${hours} 小时未进行“活着打卡”。\n上次记录:${formatTime(lastMs)}`
        : `未找到打卡记录,请连接充电器触发一次打卡。\n当前时间:${formatTime(now)}`;

      const ok = await sendNotify(env, user, { title: CONFIG.TITLE, body });
      if (ok) {
        alerted++;
        await env.CLOCKIN_KV.put(kLastAlert(user), String(now));
      }
    }

    if (listRes.list_complete) break;
  }

  await env.CLOCKIN_KV.put("meta:last_run", String(now));
  await env.CLOCKIN_KV.put("meta:last_run_summary", JSON.stringify({ now, checked, alerted }));
  return { checked, alerted, now };
}

/** 明文校验:KV 中 user_pwd:<user> 直接存明文 */
async function verifyUserPlain(user, pwd, env) {
  const stored = await env.CLOCKIN_KV.get(kUserPwd(user));
  if (!stored) return false;
  return stored === pwd;
}

async function sendNotify(env, user, msg) {
  let notify = null;
  const perUserStr = await env.CLOCKIN_KV.get(kNotify(user));
  if (perUserStr) {
    try { notify = JSON.parse(perUserStr); } catch {}
  }
  const type = notify?.type || CONFIG.DEFAULT_NOTIFY.type;
  const url = notify?.url || CONFIG.DEFAULT_NOTIFY.url;
  if (!url) return false;

  if (type === "dingtalk") return await sendDingTalk(url, msg);
  return await sendBark(url, msg);
}

async function sendBark(baseUrl, msg) {
  const title = encodeURIComponent(msg.title || "提醒");
  const body = encodeURIComponent(msg.body || "");
  const u = `${baseUrl.replace(/\/$/, "")}/${title}/${body}`;
  const r = await fetch(u, { method: "GET" });
  return r.ok;
}

async function sendDingTalk(webhookUrl, msg) {
  const payload = { msgtype: "text", text: { content: `${msg.title || "提醒"}\n\n${msg.body || ""}` } };
  const r = await fetch(webhookUrl, {
    method: "POST",
    headers: { "content-type": "application/json;charset=utf-8" },
    body: JSON.stringify(payload),
  });
  return r.ok;
}

const kUserPwd = (user) => `user_pwd:${user}`;
const kLastTime = (user) => `last_time:${user}`;
const kLastAlert = (user) => `last_alert:${user}`;
const kNotify = (user) => `notify:${user}`;

function json(obj, status = 200) {
  return new Response(JSON.stringify(obj, null, 2), {
    status,
    headers: { "content-type": "application/json; charset=utf-8" },
  });
}
function formatTime(ms) { return new Date(ms).toISOString(); }
function round2(n) { return Math.round(n * 100) / 100; }

📌 转载信息
转载时间:
2026/1/12 10:36:11