物理实验室作品搜索服务。线上仅保留 Cloudflare Worker、静态搜索页面和 D1 数据库;仓库保留作品收录程序,自动调度当前关闭。
cloudflare/public/index.html:Cloudflare 静态搜索页面cloudflare/worker.mjs:搜索 API 与可选的 Groq 查询词扩展- Cloudflare D1
plworks:线上查询数据源(data+data_fts倒排索引) data.db:仓库内权威数据快照src/scripts/updateDatabase.ts:作品收录入口src/scripts/exportD1Sql.ts:将data.db导出为 D1 SQL
收录程序遍历 Experiment 与 Discussion 两种内容类型,不使用查询 Tag 过滤。收录记录的 source 来源字段根据作品详情返回的 Tags 判断:Experiment + 精选 为“实验精选”,Discussion + 精选 为“黑洞精选”,Discussion + 小说 或 小说专区 为“黑洞小说”,其余组合为“其他”。带有“小作品”标签的作品会被过滤。
工作流使用 GitHub Environment pl-search。
导入数据到Cloudflare D1:手动将现有data.db导入 D1并部署 Worker收录作品到 Cloudflare D1:手动收录新作品、更新data.db、覆盖 D1并部署 Worker
两个工作流都仅支持 workflow_dispatch。自动收录的实现已保留,定时触发当前关闭。
敏感值使用 GitHub Environment Secrets:
CLOUDFLARE_API_TOKEN
PL_USERNAME
PL_PASSWORD
OPENAI_API_KEY
OPENAI_API_KEY 用于全部 AI 分析请求。
普通配置使用 GitHub Environment Variables:
CLOUDFLARE_ACCOUNT_ID
D1_DATABASE_ID=1ff32e2b-ab3c-4f78-aa15-9313e095e237
DB_PATH=./data.db
D1_EXPORT_FILE=./cloudflare/d1/data.sql
PL_BASE_URL=https://physics-api-cn.turtlesim.com
OPENAI_MODEL=gpt-4o-mini
OPENAI_BASE_URL=https://api.openai.com/v1
AI_REQUEST_TIMEOUT_MS=45000
SKIP=0
TAKE=-100
COLLECT_PAGE_SIZE=20
COLLECT_BATCH_SIZE=20
COLLECT_ANALYZE_CONCURRENCY=5
COLLECT_INSERT_CONCURRENCY=5
COLLECT_PAGE_DELAY_MS=0
COLLECT_BATCH_DELAY_MS=0
Worker 的 Groq 查询扩展由 Cloudflare Worker Secret GROQ_API_KEY 控制,模型可通过 Cloudflare Variables GROQ_KEYWORD_MODEL、GROQ_MODEL 和 GROQ_BASE_URL 配置。
npm ci
npm run build
npm test
npm run update-db
npm run flexible-collect -- --take -50
npm run export-d1
npm run start:render线上服务:https://s.pltown.online
作品详情:https://s.pltown.online/w/<作品ID>
主题检索:https://s.pltown.online/q/<关键词>,例如 https://s.pltown.online/q/力学(关于力学的作品)
全部作品:https://s.pltown.online/works
Sitemap:https://s.pltown.online/sitemap.xml
Worker Cron 每 6 小时:用 Google Search Console API 提交 sitemap,IndexNow 推送 Bing/Yandex/Naver/Seznam,百度普通收录主动推送。Google 需要 Worker Secret GOOGLE_SA_JSON,百度需要 BAIDU_ZHANZHANG_TOKEN。手动补提交用 Secret SEO_SUBMIT_KEY 调 GET /api/seo/submit?key=,不要复用公开的 IndexNow key。
当 D1 免费额度(每日 500 万行读取)耗尽时,前台会从 Cloudflare 自动回退到 Render 上运行的同构后端:
render/server.mjs:用仓库内data.db快照做内存检索,无需云数据库,同时提供/api/*、/w/:id、/q/:query、/works、sitemap.xml、robots.txtrender.yaml:Render Blueprint,plan: free,启动命令node render/server.mjs- 前端
cloudflare/public/index.html顶部的FALLBACK_API:填入 Render 服务地址(如https://pl-search-fallback.onrender.com)后,请求会先试 Cloudflare,失败再试 Render
搜索核心 cloudflare/search-core.mjs 由 Worker 与 Render 共用,保证两边排序一致。Render 侧为子串匹配,是 FTS 二字切词的超集。
部署 Render:在 Render 控制台连接本仓库,使用仓库根目录的 render.yaml 创建 Blueprint,等待首次构建完成,再把生成的 https://<service>.onrender.com 填入 FALLBACK_API 并重新部署 Cloudflare Worker。
Worker 启动时自动在 D1 plworks 创建三张表(无需额外控制台操作):
events:前端埋点事件(搜索、打开作品、卡片展开、筛选等)search_terms:搜索词统计(服务端/api/search时记录,按词聚合计数)error_logs:API 异常日志(catch 时写入,含 path / message / stack)
前端通过 navigator.sendBeacon 上报 POST /api/track(body:{event, data}),Worker 异步写入 D1。
查询统计接口(只读,供分析用):
GET /api/stats?type=terms 搜索词 TOP50(term, count, last_searched_at)
GET /api/stats?type=events 事件类型聚合 TOP50(event, count, last_ts)
GET /api/stats?type=errors 最近 50 条错误日志(id, ts, path, message, extra)
GET /api/stats?type=seo 国内外索引提交状态(Google / IndexNow / 百度)