Skip to main content
Glama

nbdpsy-api

小红书矩阵账号运营的纯 REST API 后台服务。远程 AI agent 通过 HTTP 调用端点完成 账号托管、cookie 共享、笔记发布;人不直接用 UI,登录只交给一个 chrome 插件。


架构总览

一句话:单进程 FastAPI(REST)服务,apikey 鉴权,把发布/账号/cookie 做成 REST 端点, 登录交给 chrome 插件,全服务只有一套 sync Camoufox 浏览器栈。

远程 agent ──(HTTP, Bearer apikey)───────────────────────▶  /api/*
chrome 插件 ──(HTTP, Bearer apikey)──────────────────────▶  /api/cookies/import
                                        │
                              单进程 FastAPI
                        ├─ apikey 中间件(RBAC 上下文)
                        ├─ REST 端点面(账号/cookie/发布/插件/管理员/自描述)
                        ├─ 发布队列(asyncio.Queue + per-account 锁 + to_thread)
                        │     └─▶ sync Camoufox(Xvfb :99)── xiaohongshu.com
                        └─ 可选 cookie 周期巡检(COOKIE_CHECK_INTERVAL>0 才起)

设计取舍:

  • 去 celery / redis:发布调度的真相源本就是 DB 状态机,单进程用 asyncio 队列 + 内存 per-account 锁等价替代,甩掉两个重依赖。发布状态机 pending → publishing → published | failed,失败按 PUBLISH_RETRY_SCHEDULE 退避重试,进程重启自动恢复 僵死 job。

  • 单套 sync 浏览器栈:只保留发布 + cookie 检测用的 sync Camoufox,采集/AI 栈整包不搬。

  • apikey + 二元 RBAC:每个运营者(operator)一把 apikey;管理员(admin)全见, 普通 operator 只能操作被 grant_account_access 授权的号。cookie 每个小红书账号唯一 一行(共享 cookie),不按 operator 分裂。

  • 登录外置:服务端不做登录,chrome 插件把用户已登录的 cookie 推到 /cookies/import

  • 视频能力(搬运/再制作/修订):/api/video/* 端点建任务,由独立 asyncio worker 进程 (nbdpsy-video-worker.service,与 API 进程隔离——API 重启不杀长任务)轮询 DB 状态机执行。 能力覆盖 YouTube 搬运(下载→转写→翻译→配音→烧字幕出片)、分镜级再制作(remake)与成片 自然语言修订(revise 增量重制);产物落 DATA_DIR/uploads/video/ 经 HMAC token 目录直链下载。 详见 app/video/docs/DEPLOY.md「视频 worker」节。


Related MCP server: xiaohongshu-mcp-py

目录结构

app/
  server.py            # create_app():FastAPI 装配 + lifespan
  core/                # config(Settings) / db(async SQLAlchemy) / security(Fernet + apikey hash)
  auth/                # apikey 中间件 / ContextVar 运营者上下文 / RBAC guards / bootstrap root
  models/              # operator / operator_account_access / xhs_account / publish_job
  services/            # operator_service / account_service / cookie_service(纯业务层)
  http/                # REST 端点:system / manifest / accounts / admin / cookies / publish / extension / downloads / video
  browser/             # sync_client(Camoufox 发布/检测) / profile_guard / fingerprint / cookie_checker
  publish/             # queue(asyncio 队列 + 锁) / scheduler(状态机 + 恢复) / runtime(调度器单例)
  video/               # 视频搬运/再制作管线:providers(薄 AI 直连) / scheduler(方案 C 调度器) /
                       #   worker(独立进程入口) / stages / paths(HMAC 产物目录) / pipeline(七阶 + remake 全家)
alembic/               # DB 迁移
chrome-extension/      # Manifest V3 插件(推 cookie)
scripts/               # xvfb.sh / run.sh / pack_extension.sh
tests/                 # 单测 + tests/e2e(冒烟,含 slow)

启动步骤

前置:Python 3.12、Xvfbzip 已装(which Xvfb zip)。

# 1. venv(依赖只装在项目 venv 内,不要用系统 python)
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt

# 2. 配置:复制样例并按需改(生产必须换 SECRET_KEY / 设 ROOT_ADMIN_APIKEY)
cp .env.example .env

# 3. 一键起(迁移 DB → 打包插件 → 起 uvicorn;内部会先确保 Xvfb)
bash scripts/run.sh

scripts/run.sh 依次做:xvfb.sh start(确保虚拟显示)→ alembic upgrade head(迁移) → pack_extension.sh(生成 DATA_DIR/extension.zip)→ uvicorn app.server:create_app --factory --host $API_HOST --port $API_PORT。全程用 .venv/bin/ 下的解释器/工具。

也可手动分步:

bash scripts/xvfb.sh start                # 启 Xvfb :99(start/stop/status,幂等)
.venv/bin/alembic upgrade head            # DB 迁移
bash scripts/pack_extension.sh            # 打包插件 zip
.venv/bin/uvicorn app.server:create_app --factory --host 0.0.0.0 --port 8848

服务起来后:健康探活 GET /healthz(免鉴权),自描述接口 GET /api/manifest(需 apikey)。


apikey 与首个管理员

  • 首个 root 管理员由 lifespan 的 bootstrap_admin 引导:

    • 配了 ROOT_ADMIN_APIKEY:用它建/对齐 root(幂等,重启同 key 不重复建)。

    • 没配:仅当库里还没 root 时,自动生成一把并在日志里打印一次明文(务必立即保存)。

  • 之后用 root 的 apikey 调 POST /api/operators 建其它运营者,每次返回一次性明文 apikey (库内只存 SHA256 hash,无法再次读取;忘了用 POST /api/operators/{id}/rotate-apikey 重置)。

  • 远程 agent / 插件带 apikey 的方式:HTTP 头 Authorization: Bearer <apikey> (或 X-API-Key: <apikey>)。

创建更多管理员

用 root(或任一 admin)的 apikey 调管理端点:

  • 新建 admin:POST /api/operators {"name":"小李","role":"admin"} → 返回一次性明文 apikey。

  • 把已有运营者提权:PATCH /api/operators/{id} {"role":"admin"};停用:{"enabled":false}

  • 分配小红书账号使用权:POST /api/operators/{id}/grants {"xhs_account_id":...}; 回收:DELETE /api/operators/{id}/grants/{xhs_account_id}

  • 只有 admin 能调这些;普通 operator 调会被拒(403)。

查看账号 / 运营者状态

本服务无前端,查看都通过带 admin apikey 的请求调 REST 端点:

  • 所有小红书账号 + 登录状态:GET /api/accounts(admin 全见)→ 每个含 status / cookie_status(valid/invalid/captcha/restricted/unknown)/ last_check_at / 昵称等(不含 cookie)。 restricted = cookie 有效但账号被小红书挂了风控验证墙(检测时探他人主页撞出), 需运营用手机小红书 App 扫码验证后重新检测;撞墙历史见 risk_events 表。

  • 刷新某号实时活性:POST /api/accounts/{id}/cookie-checks 异步——返回 {check_id} 后用 GET /api/cookie-checks/{check_id} 轮询到 valid/invalid/captcha/restricted/error, 把非 error 的状态写回(error 是基础设施失败,保留原值不误标)。 想自动周期巡检:设 COOKIE_CHECK_INTERVAL(秒,>0 才起,默认 0)。

  • 发布任务状态:GET /api/publish-jobs?account_id=&status=(均可选)。

  • 所有运营者:GET /api/operators → id/name/role/enabled(不含 apikey);某人授权了哪些号: GET /api/operators/{id}/grants

不经 agent 快速瞄一眼(直接查库):

.venv/bin/python -c "import sqlite3;[print(r) for r in sqlite3.connect('data/nbdpsy.db').execute('select id,name,nickname,status,cookie_status,last_check_at from xhs_accounts')]"

插件配置

  1. 带 apikey 调 GET /api/extension 拿到 download_url(指向 /downloads/extension.zip, 免鉴权可直接下)、版本与安装步骤。

  2. 下载解压到固定目录 → chrome://extensions 开「开发者模式」→「加载已解压的扩展程序」。

  3. 在插件弹窗填 serverUrl(本服务地址,即 PUBLIC_BASE_URL)与 apikey(连接本服务的 同一把 key)。

  4. 用户在 chrome 里登录好小红书后,插件把 cookie 推到 POST /api/cookies/import,服务端 sameSite 规范化 + Fernet 加密后 upsert 到该账号唯一一行。


API 端点清单

共 24 个 REST 端点,分 6 组。除白名单(/healthz/downloads/*)外均需 apikey, 且按 RBAC 收窄到 caller 有权的账号(admin 全见)。完整契约(含 params/returns/errors/notes) 以 GET /api/manifest 为准,下表只给概览。

system(2)

方法 + 路径

说明

GET /api/whoami

回显当前运营者身份(诊断)

GET /api/manifest

服务自描述:全部端点契约 + 工作流叙事 + 错误契约 + caller 身份

admin(8,仅管理员)

方法 + 路径

说明

POST /api/operators

建运营者,返一次性明文 apikey

GET /api/operators

列全部运营者(不含 apikey)

PATCH /api/operators/{operator_id}

局部更新 role/enabled/name(留空不改)

DELETE /api/operators/{operator_id}

删运营者并级联清授权

POST /api/operators/{operator_id}/rotate-apikey

重置 apikey,旧 key 立即失效

POST /api/operators/{operator_id}/grants

授权某号(幂等)

DELETE /api/operators/{operator_id}/grants/{xhs_account_id}

回收授权(幂等)

GET /api/operators/{operator_id}/grants

列某运营者已授权的号

accounts(6,RBAC 收窄)

方法 + 路径

说明

GET /api/accounts

列可见账号(不含 cookie)

GET /api/accounts/{account_id}

查单个账号元信息

PATCH /api/accounts/{account_id}

改内部展示名(安全字段)

DELETE /api/accounts/{account_id}

删账号并清其授权

GET /api/accounts/{account_id}/cookies

解密回读该号 cookie(需 access)

GET /api/login/poll

轮询登录完成信号(自 since 起有无新号/新登录)

cookies(3)

方法 + 路径

说明

POST /api/cookies/import

灌 cookie,upsert 唯一号

POST /api/accounts/{account_id}/cookie-checks

异步起浏览器巡检,立即返 check_id

GET /api/cookie-checks/{check_id}

轮询巡检结果

publish(4,RBAC 收窄)

方法 + 路径

说明

POST /api/publish-jobs

建发布任务并入队

GET /api/publish-jobs/{job_id}

查任务状态

GET /api/publish-jobs

列任务(按可见账号过滤,可加 ?account_id=&status=)

POST /api/publish-jobs/{job_id}/cancel

取消(仅 pending 可取消)

POST /api/publish-jobsimages 每项为 http(s) URL / data URI / {b64, ext};不传 schedule_time 立即入队,传 ISO8601 字符串则定时发布(调度器扫到期后自取)。图片在发布 runner 里再物料化成本地文件,端点本身不碰浏览器。

extension(1)

方法 + 路径

说明

GET /api/extension

插件下载地址 + 版本 + 安装引导 + /api/login/poll 起点时间


接入(三步)

远程 agent 接入本服务不需要装任何客户端 / 插件 / SDK——公网地址 + 一把 operator apikey 即可,直接用 HTTP 调 REST 端点。本部署的公网地址为 https://mcp.nbdpsy.com(经反向代理 回源 localhost:8848),下文示例即用它;你自建部署时替换成自己的域名。

  1. 拿 apikey:向管理员索取一把 operator apikey(见「apikey 与首个管理员」)。

  2. 确认可达:

    curl https://mcp.nbdpsy.com/healthz          # 应返回 {"ok":true}
  3. 读 manifest 自解释:

    curl -H "Authorization: Bearer <你的-apikey>" https://mcp.nbdpsy.com/api/manifest

    返回体一次性给全部端点契约(method/path/params/returns/errors/notes)、工作流叙事 (workflows)、约束(constraints)、错误契约(error_contract)与你的身份(caller)。 agent 读完这一个接口即可自解释地干活,不需要额外文档。

apikey 是密钥:别写进公开仓库 / 截图 / 聊天分享。泄露了让管理员用 POST /api/operators/{id}/rotate-apikey 轮换。

要点:

  • 除白名单(/healthz/downloads/*)外所有调用都要带 operator 的 apikey (Authorization: Bearer <apikey>X-API-Key: <apikey>);访问不属于自己的账号 403。

  • 发布是异步的:POST /api/publish-jobs 只返回 {job_id},结果靠 GET /api/publish-jobs/{job_id} 轮询到 published/failed。

  • 登录没有 REST 端点:小红书登录靠人 + chrome 插件在真实浏览器完成,登录完成判据是 GET /api/login/poll(细节见 manifest 的 workflows)。

  • cookie 是共享的:多个有 access 的 operator 共用同一份账号 cookie,谁更新写同一行。


部署

  • 反向代理PUBLIC_BASE_URL(对外域名)代理到本机 API_PORT(FastAPI 监听)。 REST 端点、插件推 cookie、插件包下载走同一入口。

  • 用 systemd 托管 scripts/run.sh(exec uvicorn 让信号/退出码直通)。

  • Xvfb 由 scripts/run.sh 内的 xvfb.sh start 确保;也可单独用 systemd 常驻。

  • .env / app/core/config.py 后必须重启进程——pydantic BaseSettings 在进程 启动时锁定字段集合,运行中改配置不生效。

  • 可选后台 cookie 巡检:COOKIE_CHECK_INTERVAL 设为 >0(秒)时,lifespan 起一个轻量 协程周期性对 cookie_status=valid 的号逐个跑登录检测并写回状态(号间隔 ≥5s 防频控); 默认 0 关闭。restricted(被风控)的号不进周期巡检——继续每轮起浏览器只会把限流 催得更狠,恢复靠人工扫码后手动触发一次检测。


坑(务必先读)

  • SECRET_KEY 不能换:cookie 用它派生的 Fernet key 加密落库。换了 key,存量 login_cookies 全部解密失败(且 decrypt_cookies 静默返回空串,不报错),等于所有号 掉登录。迁移/换机原样沿用旧 SECRET_KEY

  • Xvfb :99 必须先起:sync Camoufox 需要虚拟显示;XVFB_DISPLAY 改了要与 xvfb.sh 一致。NVIDIA + Xvfb 环境下发布客户端已强制 MOZ_HEADLESS / LIBGL_ALWAYS_SOFTWARE 等,避免 glxtest 卡启动。

  • Camoufox profile 锁:Firefox 系单写锁,同一号并发启动必挂 "Firefox is already running"。服务用 per-account 内存锁串行同号操作;若进程异常残留锁,profile_guard 会清锁 + 精确杀孤儿(argv 精确匹配,account_2 不误杀 account_20)。

  • 发布走服务器 IP:cookie 虽在用户住宅浏览器诞生,发布操作却发生在服务器 IP + 固定 指纹上。个别对 IP 敏感的号可能触发风控(发布中判 failed,可加冷却)。IP 不一致是既有 现实,非本服务新增。

  • 改代码 / prompt 后要重启:uvicorn 进程启动时加载模块,改 .py 不 restart 不生效。


测试

# 全量单测(不含需真号的 slow/e2e,CI 用这条)
.venv/bin/pytest -m "not slow" -v

# e2e 冒烟:RBAC 链默认跑(纯 DB/REST 调用);发布链需真 cookie,缺则自动 skip
.venv/bin/pytest tests/e2e -v
# 手动跑发布链(需 Xvfb + 真 cookie):
NBDPSY_E2E_COOKIES='[{"name":"...","value":"..."}]' \
  .venv/bin/pytest tests/e2e/test_smoke.py -m slow -v

测试全程用隔离临时 sqlite(不碰生产库),slow 标记的用例默认不在 CI 跑。

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    MCP server for automating Xiaohongshu (RED Note). Publish posts, search content, comment, like, and analyze creator data on China's largest lifestyle social platform.
    24
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    A MCP server for Xiaohongshu that enables account login, copywriting generation, and automatic publishing with AI-generated images via conversation.
    12
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    MCP server for automatically publishing text, image, and video notes to Xiaohongshu (Little Red Book).
    1
    MIT