# Fridge API Base URL: `https://fridge.stinkycat.club`。本文可公开读取,不含私人库存或凭据。 设置 `FRIDGE_URL` 为生产网站地址,`FRIDGE_ACCESS_KEY` 为访问密钥。UI 和 API 使用同一份数据库;日常口述由外部 agent 整理,API 不保存原文或数量。 ## 认证与已有登录 - **使用已登录浏览器的 agent**:网页登录使用 Google(与其他 stinkycat 应用相同账号)。所有者已在你的浏览器完成登录后,直接使用 UI 即可,无需再索取 Bearer。也可以在同一浏览器打开 `/api/inventory` 等 GET 接口,浏览器自动携带本站会话;写入优先使用 UI。会话失效后再通过 Google 登录。 - **独立连接器、命令行或服务端 agent**:请求需带 `Authorization: Bearer `。这些工具不会自动继承已登录浏览器的会话,需将有效密钥配置到它们自己的安全凭据存储中。 Google 登录不会生成、显示或轮换 API 密钥;应用没有用于查看密钥的设置页。密钥由库存所有者通过安全方式单独提供,文档和网页不会返回密钥。若使用独立连接器且缺少有效密钥,请提供其安全填写入口,不要求在聊天里粘贴密钥,也不要重复要求 Google 登录。 私人接口接受有效 Bearer 或本站登录会话。**显式提供错误的 Authorization 会返回 401,即使同时携带有效 Cookie**;使用浏览器会话时不要附加旧 Bearer。Cookie 认证的写请求还要求同源 Origin,普通 UI 会处理。不要把 401 当作空库存,也不能仅凭 401 推断是 Google 登录迁移使密钥失效;应检查实际使用的凭据和访问方式。 读写均返回 JSON,响应禁止缓存。`/api` 可公开读取接口索引,不含库存。 ## 接口 | 方法与路径 | 用途 | |---|---| | `GET /api/inventory` | 当前库存 | | `GET /api/products` | 查找食物,包括归档 | | `GET /api/products/{id}` | 单项与状态历史 | | `POST /api/products` | 新增,返回 201 | | `PATCH /api/products/{id}` | 编辑 / 归档 / 回购,返回 200 | ## 调用示例 ```sh FRIDGE_URL=https://fridge.stinkycat.club # FRIDGE_ACCESS_KEY 从调用环境或安全凭据存储读取,不写进 URL 或日志。 # 现有库存 curl --fail-with-body "$FRIDGE_URL/api/inventory" \ -H "Authorization: Bearer $FRIDGE_ACCESS_KEY" # 按名称找商品,包括归档;可叠加 category/location/status curl --fail-with-body --get "$FRIDGE_URL/api/products" \ -H "Authorization: Bearer $FRIDGE_ACCESS_KEY" \ --data-urlencode 'q=白味噌' # 商品全部信息与状态历史 curl --fail-with-body "$FRIDGE_URL/api/products/$PRODUCT_ID" \ -H "Authorization: Bearer $FRIDGE_ACCESS_KEY" # 新增;每个新操作生成一次请求 ID,重试复用该值。 REQUEST_ID=$(uuidgen) curl --fail-with-body "$FRIDGE_URL/api/products" \ -H "Authorization: Bearer $FRIDGE_ACCESS_KEY" \ -H 'Content-Type: application/json' \ -H "Idempotency-Key: $REQUEST_ID" \ --data '{"name":"白味噌","category":"调味料","location":"冷藏"}' # 没有了(归档);回购时改为 available curl --fail-with-body -X PATCH "$FRIDGE_URL/api/products/$PRODUCT_ID" \ -H "Authorization: Bearer $FRIDGE_ACCESS_KEY" \ -H 'Content-Type: application/json' \ --data '{"status":"unavailable"}' ``` ## 写入做法 / 备注 `POST /api/products` 可随其他字段提交 `notes`。更新现有商品时只提交要改的备注: ```sh curl --fail-with-body -X PATCH "$FRIDGE_URL/api/products/$PRODUCT_ID" \ -H "Authorization: Bearer $FRIDGE_ACCESS_KEY" \ -H 'Content-Type: application/json' \ --data '{"notes":"鸡腿加入腌料,冷藏腌制。\n烹饪前按包装说明处理。"}' ``` 写入返回的 `product.notes` 就是保存结果。`GET /api/products/{id}`、`GET /api/products` 和 `GET /api/inventory` 均包含 `notes`。更新前先读取商品;API 是整体替换备注,不会自动追加或合并。需要追加时先读再组合,避免覆盖已有内容。清空使用 `{"notes":null}`。 ## 字段与响应 - 名称:必填,1–120 字符。创建时默认 `available`。 - 类别:`零食`、`调味料`、`蔬菜`、`肉类`、`冷冻食品`,可 `null`。 - 位置:`冷藏`、`冷冻`、`常温`,可 `null`。 - 状态:`available`(家里有)或 `unavailable`(家里没有)。 - `notes`:可选的做法 / 备注,类型为 `string | null`,最多 10000 个 UTF-16 代码单元(常用汉字计 1,emoji 通常计 2)。保留换行和非空文本原样;仅按纯文本显示,不执行 HTML 或渲染 Markdown。不能包含 NUL(`\u0000`)。类型或长度不符返回 400 `INVALID_NOTES`。 - 创建时省略 `notes` 默认为 `null`;`PATCH` 省略时保留原值,传 `null`、空字符串或全空白字符串清空。旧商品默认 `null`。归档 / 回购保留备注;只改备注不增加状态历史。 - `PATCH` 只提交需要修改的字段,避免把旧数据覆盖回去。类别与位置独立。 - 每项含 `id`(UUID)、`name`、`category`、`location`、`status`、`notes`、`created_at`、`updated_at`;时间为 ISO 8601。详情的 `history` 按时间升序包含 `{id, status, created_at}`,包括初次录入和之后的状态变化。 - 查询参数:`q` 为名称子串(最多 120 字符),`category` / `location` 使用上方中文值,`status` 仅用于 `/api/products`;`/api/inventory` 始终只返回 `available`。不分页。 - 列表返回 `{ "products": [...] }`;单项和写入返回 `{ "product": {...} }`;单项详情的 `product.history` 包含状态历史(列表与写入响应不含历史)。 - 已归档商品仍能查询;回购修改同一 ID 为 `available`。重复设置同一状态不增加历史。没有数量扣减。 - `POST` 的 `Idempotency-Key` 是 8–128 位字母、数字、下划线或连字符;重试相同内容返回同一商品,不会新增一份。相同 key 搭配不同内容返回 409。新操作使用新 key。 - JSON 请求体上限为 65536 字节(64 KiB),包括字段名、转义和其他字段;超限返回 413。 - 错误为 `{ "error": { "code": "...", "message": "..." } }`;401 未认证、403 来源无效、400 输入错误、404 不存在、409 请求标识冲突、413 内容过大、415 非 JSON、500/503 服务错误。 ## Agent 使用规则 按用户当前请求执行读取或写入。处理口述时先查是否已存在,同名候选不明确再补问。普通录入直接执行;无需逐条确认或保留原话。不要把「吃了一包」推断为家里已经没有。