feat: 管理台搜索/分区刷新、评论人员体系、相关项目,修复内容源自动获取,完善 MCP 自描述
Build and Deploy / build (push) Successful in 1m32s
Build and Deploy / deploy (push) Successful in 26s

管理台:
- 内容管理新增名称搜索(title/slug/url/description),状态与翻页链接保留搜索词
- 菜单改为真实链接,每次点击重新向后台取数渲染分区,不再依赖手动刷新
- 内容管理每页固定 20 条
- pending 条目按钮改为「预览」,active 为「查看」

评论体系:
- 评论提交新增必填联系方式(手机号/邮箱),前端不做展示
- 首次评论以「昵称+联系方式」建档,之后需匹配且未被禁用
- 后台评论管理新增联系方式列;新增评论人员管理页,可启用/禁用/改名/删除
- 评论昵称输入框不再预置 admin

相关项目:
- 新增 entry_relations 双向关联,详情页侧栏展示并写入机读 JSON
- 编辑页可添加/移除关联

内容源自动获取:
- 修复 rss 适配器把 feed url 当成条目 url 的根本问题,支持 Atom link/href 与相对链接
- eryajf-weekly 限定正文容器取链接,修正项目说明的提取与「(opens new window)」清理
- pipeline 增加轮内去重与单条失败隔离;scheduler 对非法 cron 明确告警

MCP:
- 重写 instructions 为八节说明,新增 usage 工具返回结构化接入文档
- 补全各工具的参数、用途与返回格式说明;baseUrl 取自当次请求
- 首页 MCP 区块与 README 同步补充写入示例与踩坑清单

测试: 新增 comments/relations/admin/ingest-adapters 用例,共 29 个全部通过
This commit is contained in:
cheney
2026-10-27 11:22:26 +08:00
parent 3f7b48325f
commit 806761103b
29 changed files with 1727 additions and 133 deletions
+108 -6
View File
@@ -13,7 +13,7 @@
```bash
npm install
npm run dev # http://localhost:3000(--watch 热重载)
npm test # node:test,10 个用例
npm test # node:test,27 个用例
```
首次启动自动完成建表与种子数据。默认管理员 `admin`,密码取 `ADMIN_INIT_PASSWORD`(默认 `admin`)。
@@ -33,24 +33,126 @@ bash start.sh # 用新镜像重启容器
|---|---|
| 首页(搜索 / MCP 说明 / 最近收录) | `/` |
| 列表页(类型 + 嵌套标签过滤) | `/entries?q=关键字&type=工具&tags=1,2&sort=stars` |
| 详情页(元数据 / 评论 / 机读 JSON) | `/entries/:slug` |
| 详情页(元数据 / 相关项目 / 评论 / 机读 JSON) | `/entries/:slug` |
| 随机游览 | `/random` |
| 登录 | `/login` |
| 管理台(内容/标签/评论/内容源/账号) | `/admin` |
| 管理台 | `/admin` |
管理台分区(**点菜单每次都重新向后台取数**,不需要手动刷新浏览器):
| 分区 | 说明 |
|---|---|
| 内容管理 | 名称搜索(标题 / slug / URL / 描述)+ 状态筛选,**每页 20 条**;pending 条目按钮为「预览」,active 为「查看」 |
| 标签管理 | 无限嵌套标签树,拖拽调整层级 |
| 评论管理 | 评论列表 + **联系方式**列 |
| 评论人员 | 以联系方式为身份依据,可启用 / 禁用 / 改名 / 删除 |
| 内容源 | 人工 / AI 接口 / 定时拉取(rss / html / eryajf-weekly) |
| 账号管理 | 管理员可见 |
### 评论规则
访客评论需要提供**昵称 + 联系方式**(手机号或邮箱):
- 联系方式**只存后台,前台详情页不展示**。
- **首次评论**以「昵称 + 联系方式」建档入库。
- **之后评论**必须昵称与该联系方式登记的一致,且未被管理员禁用,否则分别返回 400 / 403。
- 管理员可在「评论人员」分区按联系方式禁用某人;禁用后其评论被拒,历史评论保留。
### 相关项目
每个条目都能关联若干「相关项目」,在详情页侧栏展示,同时进入机读 JSON 的 `related` 字段。
- 关联在管理台「编辑条目 → 相关项目」里维护。
- **关联是双向的**:A 关联 B 后,B 的详情页也会出现 A(底层只存一条记录)。
- 前台只展示已上架(`active`)的相关条目。
## 给 AI 的入口(MCP)
Streamable HTTP · 只读 · 无需密钥:
> **一句话:读用 MCP(免密钥),写用 REST(要 `X-Ingest-Key`)。**
### 只读部分:MCP
```
POST {BASE_URL}/mcp
tools: search_entries / get_entry / random_entry / list_tags
transport: Streamable HTTP
auth: none
Accept: application/json, text/event-stream # 正规客户端自动带上;curl 直连需手写
```
**本端点不校验任何密钥**,MCP 连接里无需配置 token,它只提供读取能力。
客户端配置:
```json
{
"mcpServers": {
"awesome-index": {
"url": "{BASE_URL}/mcp"
}
}
}
```
一句话让 Agent 自己接入:
> 请把 URL 为 `{BASE_URL}/mcp` 的 MCP 服务器添加到你的客户端配置中,名称用 awesome-index,完成后告诉我现在可以调用哪些工具。
可用工具:
| tool | 参数 | 说明 |
|---|---|---|
| `search_entries` | `q`(必填), `limit`(1–50,默认 20) | 按关键字 / 类型 / 标签搜索。**查重先走它** |
| `get_entry` | `slug`(必填,也可传标题关键字自动匹配) | 取单条完整详情与元数据 |
| `random_entry` | — | 随机返回一条已上架内容,用于探索发现 |
| `list_tags` | — | 列出全部标签组、嵌套结构与收录数量 |
| `usage` | — | 返回完整接入说明(含写入接口与踩坑清单) |
结果格式:返回体在 `result.content[0].text` 里,是一段 **JSON 字符串,需要再解析一次**。
连接 `initialize` 后返回的 `instructions` 字段同样带有完整的接入与写入说明(AI 客户端通常会自动注入)。
### 写入部分:AI 录入接口
MCP 只读,不能写入。要让 Agent 把新内容录进本库,请使用 HTTP 接口:
```
POST {BASE_URL}/api/ingest/entry # 等价端点:POST {BASE_URL}/api/entries
Content-Type: application/json
```
鉴权请求头(**不要**把 key 放进 URL query):
| 方式 | 示例 |
|---|---|
| `X-Ingest-Key`(推荐) | `X-Ingest-Key: <key>` |
| 标准 Bearer(等价) | `Authorization: Bearer <key>` |
请求体字段:`title`(必填) / `url`(必填,合法 URL,用于去重) / `type`(必填,枚举:微服务 / SaaS / 网站 / 工具 / 应用 / 脚本 / 插件) / `summary`(≤200) / `description_md`(≤5000,Markdown) / `license` / `stars`(整数) / `tags`(字符串数组,自动建标签) / `status`。
> `slug` 不是合法字段,传了会被静默丢弃(服务端按标题自动生成)。
```bash
curl -X POST "{BASE_URL}/api/ingest/entry" \
-H "X-Ingest-Key: <key>" \
-H "Content-Type: application/json" \
-d '{"title":"MinerU","url":"https://github.com/opendatalab/MinerU","type":"工具","tags":["Python","OCR"]}'
```
返回:创建成功 `201 {"ok":true,"data":{"created":true,"id":<n>,"status":"pending"}}`;URL 已存在时 `200 {"created":false,"id":<n>}`(自动去重);鉴权失败 `403 invalid_key`。
### 五个必记的坑
1. **推荐 `X-Ingest-Key` 请求头**(等价 `Authorization: Bearer`);自创的 `X-API-Key`、`?key=` 一律 403。
2. **MCP 免密钥 ≠ 密钥有效**。`/mcp` 根本不查 key,别拿探测 MCP 来验证 ingest key。
3. **提交后默认 `pending`**,前台搜不到是正常的,等管理员审核转 `active`。**不要因为搜不到就重复提交。**
4. **同一 URL 重复提交返回 200 `created:false`**,这不是报错,说明已存在并被去重。
5. `slug` 不是字段,传了会被丢弃。
排错:`curl` 直连 `/mcp` 报 `406` 是缺 `Accept: application/json, text/event-stream`,正规客户端不受影响。
录入条目默认 `pending`(待审核),除非对应 AI 源开启了 direct_publish。
## 内容源(自动化收录)
三类来源汇聚到同一条目表:
@@ -58,7 +160,7 @@ tools: search_entries / get_entry / random_entry / list_tags
| kind | 说明 | 新条目状态 |
|---|---|---|
| `human` | 管理台人工录入(常开) | active |
| `ai` | `POST /api/ingest/entry` + `X-Ingest-Key` 头 | pending |
| `ai` | `POST /api/ingest/entry` + `X-Ingest-Key`(或 `Authorization: Bearer`)头 | pending |
| `feed` | 定时拉取外部站点(rss / html / eryajf-weekly 适配器),管理台可手动触发 | pending |
首个站点适配器:二丫讲梵学习周刊(wiki.eryajf.net)。运行记录见管理台「内容源 → 运行记录」。