PocketBase的自定义任务【专供LLM请求耗时任务】

在开发客服系统,中间有段实现需要频繁对知识库和提示词CRUE,但是没有后端和前端的支持,数据库接口暂时开发一个人工作量有点大,于是就采用了PocketBase,目前我已经编写了go语言的后台接口,但是这个开源项目确实挺好用的,尤其是对于一些公司算法岗位身兼多职的可以采用该方案,快速实现项目原型开发,提供演示算法,后期可以再自行写对应接口或者交给后端和前端再去实现细节接口API。之前整理的资料分享给大家。

1. 快速开始

|-----------------------------|------------------------|---------------------------------------|
| 任务名 | 执行频率 | 作用 |
| __pbDBOptimize__ | 每天凌晨 0:00(UTC) | 优化数据库表结构,提升查询性能(类似 SQLite 的 VACUUM) |
| __pbMFACleanup__ | 每小时 0 分钟 | 清理过期的多因素认证(MFA)令牌 |
| __pbOTPCleanup__ | 每小时 0 分钟 | 清理过期的一次性密码(OTP)验证码 |
| __pbLogsCleanup__ | 每 6 小时(0, 6, 12, 18 点) | 删除旧的日志记录(防止日志膨胀) |
| __pbRateLimitersCleanup__ | 每天 2:00(UTC) | 清理过期的速率限制(rate limiter)数据 |

2. 添加自定义任务

vim pu_public/pb_hooks.js

复制代码
import { syncChatHistory } from "./sync_chat_history.js";

// 注册定时任务
cronAdd("sync_chat_history", "0 2 * * *", syncChatHistory);

// 你还可以注册其他钩子,例如:
// onRecordBeforeCreate(e => { ... })

PocketBase 的 pb_hooks.js(JavaScript 扩展)支持多种 钩子(Hooks)调度方法(Cron),用于在特定事件发生时执行自定义逻辑。

这些钩子运行在 Deno 环境 中,拥有对 $app(核心应用实例)、数据库、HTTP 客户端等的完全访问权限。

3. 支持的钩子类型总览

|------------------|----------------------------------------------------------------------------------------------------------------------------------------|--------------------------------|
| 钩子类别 | 方法名 | 触发时机 |
| 服务启动 | onBeforeServe | PocketBase 启动前(可注册自定义路由) |
| 记录(Record)操作 | onRecordBeforeCreate,onRecordAfterCreate,onRecordBeforeUpdate,onRecordAfterUpdate,onRecordBeforeDelete,onRecordAfterDelete | 对 collections 中的记录进行 CRUD 操作前后 |
| 认证/用户 | onModelBeforeCreate,onModelAfterCreate(适用于 _users表) | 用户注册、登录等(较少用,通常用 Record 钩子) |
| 文件上传 | onFileBeforeUpload onFileAfterUpload | 上传文件前后 |
| 定时任务 | cronAdd(id, expr, handler) | 注册周期性后台任务 |

📌 注意:所有钩子函数必须在 pb_hooks.js顶层直接调用(不能嵌套在其他函数内)。

4. 详细说明 + 实战案例
4.1. onBeforeServe ------ 启动前注册自定义 API 路由
复制代码
// pb_public/pb_hooks.js

onBeforeServe(() => {
  // 注册 GET /api/hello
  $app.on("GET", "/api/hello", (c) => {
    return c.json({ message: "Hello from custom route!" });
  });

  // 注册 POST /api/webhook
  $app.on("POST", "/api/webhook", async (c) => {
    const body = await c.req.parseBody();
    console.log("Webhook received:", body);
    return c.json({ ok: true });
  });
});

用途:创建 Webhook、外部回调、自定义 API 接口。

4.2. 记录操作钩子(最常用)
4.2.1. onRecordBeforeCreate ------ 创建前校验或修改数据
复制代码
onRecordBeforeCreate((e) => {
  // 只对 'orders' collection 生效
  if (e.collection.name !== "orders") return;

  // 自动设置创建时间(如果前端没传)
  if (!e.record.get("created_at")) {
    e.record.set("created_at", new Date().toISOString());
  }

  // 校验金额 > 0
  if (e.record.get("amount") <= 0) {
    throw new Error("Amount must be greater than 0");
  }
});
4.2.2. onRecordAfterCreate ------ 创建后触发通知
复制代码
onRecordAfterCreate(async (e) => {
  if (e.collection.name === "support_tickets") {
    // 发送邮件或企业微信通知
    await fetch("https://qyapi.weixin.qq.com/...", {
      method: "POST",
      body: JSON.stringify({
        msgtype: "text",
        text: { content: `新工单 #${e.record.id} 已创建` }
      })
    });
  }
});
4.2.3. onRecordBeforeUpdate ------ 禁止修改某些字段
复制代码
onRecordBeforeUpdate((e) => {
  if (e.collection.name === "users") {
    // 禁止修改邮箱
    if (e.record.get("email") !== e.oldRecord.get("email")) {
      throw new Error("Email cannot be changed");
    }
  }
});
4.2.4. onRecordAfterDelete ------ 清理关联数据
复制代码
onRecordAfterDelete(async (e) => {
  if (e.collection.name === "projects") {
    // 删除该项目下的所有任务
    await $app.dao().deleteRecordsByExpr("tasks", "project = {:id}", { id: e.record.id });
  }
});
4.3. 文件上传钩子
复制代码
onFileBeforeUpload((e) => {
  // 限制文件类型
  if (!e.file.name.endsWith(".pdf")) {
    throw new Error("Only PDF files are allowed");
  }

  // 限制大小(10MB)
  if (e.file.size > 10 * 1024 * 1024) {
    throw new Error("File too large");
  }
});

onFileAfterUpload((e) => {
  console.log("File uploaded:", e.file.key);
  // 可触发 OCR、转码等
});
4.4. 定时任务(Cron)
复制代码
// 每天凌晨 2 点清理过期 token
cronAdd("cleanup_tokens", "0 2 * * *", async () => {
  const now = new Date().toISOString();
  const expired = await $app.dao().findRecordsByExpr(
    "auth_tokens",
    "expires <= {:now}",
    { now }
  );
  for (const rec of expired) {
    await $app.dao().deleteRecord(rec);
  }
  console.log(`Cleaned ${expired.length} expired tokens`);
});
4.5. 其他实用技巧
4.5.1. 获取当前用户(在 Record 钩子中)
复制代码
onRecordBeforeCreate((e) => {
  const userId = e.context.authRecord?.id; // 当前操作用户 ID
  if (userId) {
    e.record.set("created_by", userId);
  }
});
4.5.2. 调试日志
复制代码
console.log("Hook triggered:", e.collection.name);
console.error("Validation failed");

日志会输出到终端(pocketbase serve 的控制台)。

5. 注意事项

|----------|-----------------------------|
| 事项 | 说明 |
| 文件位置 | 必须是 pb_public/pb_hooks.js |
| 重启生效 | 修改后需重启 pocketbase serve |
| 异步支持 | 所有钩子都支持 async/await |
| 错误处理 | 抛出 Error 会中断操作并返回 400 |
| 权限 | 钩子以系统权限运行(可访问所有数据) |
| 性能 | 避免在钩子中做耗时操作(会阻塞请求) |

6. 完整示例模板
复制代码
// pb_public/pb_hooks.js

// 1. 自定义路由
onBeforeServe(() => {
  $app.on("GET", "/api/status", (c) => c.json({ alive: true }));
});

// 2. 自动填充创建者
onRecordBeforeCreate((e) => {
  if (e.context.authRecord) {
    e.record.set("created_by", e.context.authRecord.id);
  }
});

// 3. 定时清理
cronAdd("daily_cleanup", "0 2 * * *", async () => {
  // ...清理逻辑
});

// 4. 文件校验
onFileBeforeUpload((e) => {
  if (e.file.size > 5e6) throw new Error("Max 5MB");
});
7. 官方参考
相关推荐
Robot侠6 小时前
极简LLM入门指南1
llm·llama
大模型教程7 小时前
这份中国人写的大模型书,在外网竟然被刷爆了!
程序员·llm·agent
大模型教程7 小时前
大模型入门指南 - Training:小白也能看懂的“模型训练”全解析
程序员·llm·agent
AI大模型8 小时前
一文读懂大模型智能体(Agent):从理论到实践
程序员·llm·agent
AndrewHZ8 小时前
【大模型技术学习】大模型压力测试全攻略:以Qwen3-32B为例
人工智能·大模型·llm·压力测试·模型部署·通义千问·qwen3-32b
暴风鱼划水9 小时前
大型语言模型(入门篇)A
人工智能·语言模型·自然语言处理·大模型·llm
坐吃山猪1 天前
BrowserUse12-源码-MCP模块
python·llm·playwright·browser-use
智泊AI1 天前
AI大模型优化了谁?程序员还是产品经理?
llm
自动驾驶小学生1 天前
Transformer和LLM前沿内容(1):Transformer and LLM(注定成为经典)
人工智能·深度学习·llm·transformer