管理台: - 内容管理新增名称搜索(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 个全部通过
171 lines
7.0 KiB
Markdown
171 lines
7.0 KiB
Markdown
# Awesome Index
|
||
|
||
把网上牛逼的东西收进一个库:给人用,也给 AI 用。
|
||
精选开源软件 / 微服务 / SaaS / 网站 / 工具 / 脚本 / 插件的结构化数据库,内置 MCP 接口。
|
||
|
||
- 设计方案:[design/DESIGN.md](design/DESIGN.md)(Riso 印刷风格 + 静态原型)
|
||
- 框架设计:[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)(模块划分 / 数据模型 / 端点)
|
||
|
||
## 快速开始
|
||
|
||
### 本地开发
|
||
|
||
```bash
|
||
npm install
|
||
npm run dev # http://localhost:3000(--watch 热重载)
|
||
npm test # node:test,27 个用例
|
||
```
|
||
|
||
首次启动自动完成建表与种子数据。默认管理员 `admin`,密码取 `ADMIN_INIT_PASSWORD`(默认 `admin`)。
|
||
|
||
### Docker 部署
|
||
|
||
```bash
|
||
docker build -t awesome-index:latest .
|
||
bash start.sh # 用新镜像重启容器
|
||
```
|
||
|
||
`start.sh` 会检查 `.env`(不存在则从 `.env.example` 复制),然后 `docker run` 启动容器。数据持久化在宿主机 `./data/awesome.duckdb`,重启不丢数据。
|
||
|
||
## 给人的入口
|
||
|
||
| 页面 | 地址 |
|
||
|---|---|
|
||
| 首页(搜索 / MCP 说明 / 最近收录) | `/` |
|
||
| 列表页(类型 + 嵌套标签过滤) | `/entries?q=关键字&type=工具&tags=1,2&sort=stars` |
|
||
| 详情页(元数据 / 相关项目 / 评论 / 机读 JSON) | `/entries/:slug` |
|
||
| 随机游览 | `/random` |
|
||
| 登录 | `/login` |
|
||
| 管理台 | `/admin` |
|
||
|
||
管理台分区(**点菜单每次都重新向后台取数**,不需要手动刷新浏览器):
|
||
|
||
| 分区 | 说明 |
|
||
|---|---|
|
||
| 内容管理 | 名称搜索(标题 / slug / URL / 描述)+ 状态筛选,**每页 20 条**;pending 条目按钮为「预览」,active 为「查看」 |
|
||
| 标签管理 | 无限嵌套标签树,拖拽调整层级 |
|
||
| 评论管理 | 评论列表 + **联系方式**列 |
|
||
| 评论人员 | 以联系方式为身份依据,可启用 / 禁用 / 改名 / 删除 |
|
||
| 内容源 | 人工 / AI 接口 / 定时拉取(rss / html / eryajf-weekly) |
|
||
| 账号管理 | 管理员可见 |
|
||
|
||
### 评论规则
|
||
|
||
访客评论需要提供**昵称 + 联系方式**(手机号或邮箱):
|
||
|
||
- 联系方式**只存后台,前台详情页不展示**。
|
||
- **首次评论**以「昵称 + 联系方式」建档入库。
|
||
- **之后评论**必须昵称与该联系方式登记的一致,且未被管理员禁用,否则分别返回 400 / 403。
|
||
- 管理员可在「评论人员」分区按联系方式禁用某人;禁用后其评论被拒,历史评论保留。
|
||
|
||
### 相关项目
|
||
|
||
每个条目都能关联若干「相关项目」,在详情页侧栏展示,同时进入机读 JSON 的 `related` 字段。
|
||
|
||
- 关联在管理台「编辑条目 → 相关项目」里维护。
|
||
- **关联是双向的**:A 关联 B 后,B 的详情页也会出现 A(底层只存一条记录)。
|
||
- 前台只展示已上架(`active`)的相关条目。
|
||
|
||
## 给 AI 的入口(MCP)
|
||
|
||
> **一句话:读用 MCP(免密钥),写用 REST(要 `X-Ingest-Key`)。**
|
||
|
||
### 只读部分:MCP
|
||
|
||
```
|
||
POST {BASE_URL}/mcp
|
||
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。
|
||
|
||
|
||
## 内容源(自动化收录)
|
||
|
||
三类来源汇聚到同一条目表:
|
||
|
||
| kind | 说明 | 新条目状态 |
|
||
|---|---|---|
|
||
| `human` | 管理台人工录入(常开) | active |
|
||
| `ai` | `POST /api/ingest/entry` + `X-Ingest-Key`(或 `Authorization: Bearer`)头 | pending |
|
||
| `feed` | 定时拉取外部站点(rss / html / eryajf-weekly 适配器),管理台可手动触发 | pending |
|
||
|
||
首个站点适配器:二丫讲梵学习周刊(wiki.eryajf.net)。运行记录见管理台「内容源 → 运行记录」。
|
||
|
||
## 技术栈
|
||
|
||
Node.js 22 (ESM) · Express 5 · EJS · DuckDB (`@duckdb/node-api`) · zod · express-session · @modelcontextprotocol/sdk · node-cron · cheerio
|