IHateMoney 7.2.1 API/CLI 使用详解
IHateMoney 7.2.1 REST API 与管理 CLI 全端点实测详解:Basic/Bearer Token 双认证、项目/成员/账单/统计/货币五域逐条请求与真实回显、错误码语义(401 防枚举、405 禁列项目)、账单无分页与跨币种无汇率源等行为边界,附 routes 全路由表与 db/delete-project/get-project-count 等 9 条 CLI 命令实测。
IHateMoney 7.2.1 API/CLI 使用详解
目标:对 IHateMoney 7.2.1 的 REST API 全部端点与管理 CLI 全部子命令做逐条实测——认证方式、请求参数、真实响应回显、错误码语义与行为边界(成员更新全量替换、账单无分页、跨币种无汇率源、db 子命令工作目录依赖等)。已在 Ubuntu 22.04 + ihatemoney 7.2.1(pip 安装 + Gunicorn/systemd 形态,安装过程见《Ubuntu 22.04 安装 IHateMoney 7.2.1 指南》)实测通过,API 基地址
http://127.0.0.1:8000。
一、环境信息
- 操作系统:Ubuntu 22.04.5 LTS(x86_64)
- IHateMoney:7.2.1(pip 安装,虚拟环境
/home/ihatemoney/ihatemoney) - 运行形态:Gunicorn 26.2.0(3 workers,监听 127.0.0.1:8000)+ systemd(
ihatemoney.service) - API 客户端:curl(系统自带)
- 数据库:SQLite
/var/lib/ihatemoney/ihatemoney.sqlite(alembic 迁移头c941aaca38c2) - 配置基线:
ALLOW_PUBLIC_PROJECT_CREATION = True、ENABLE_CAPTCHA = False(与《IHateMoney 7.2.1 配置参数详解》第六章生产定稿一致;若您按该篇把创建收口为 False,本文所有「创建/更新项目」请求会返回 400,详见其 5.3 节实测) - 演示项目口令约定:测试项目
apitest私有码test1234、clitest私有码cli12345,均为演示值
二、前置条件(端点/认证/客户端工具)
确认服务健康,并拿到可用的 API 端点清单:
systemctl is-active ihatemoney.service
curl -s -w "\nHTTP %{http_code}\n" http://127.0.0.1:8000/healthcheck
实测回显:
active
OK
HTTP 200
/healthcheck 是应用自带健康检查端点(无认证),接监控探活直接用它。
认证模型一句话概括:除了创建项目,其余一切请求都要带「项目 ID + 私有码」的 Basic 认证,或用该项目签发的 Bearer Token。没有全局管理员 API 账号(管理口令只作用于 Web 端管理页,不作用于 API,实测见《IHateMoney 7.2.1 配置参数详解》5.3 节)。
准备一个干净测试项目(后文全部示例基于它):
curl -s -w "\nHTTP %{http_code}\n" -X POST http://127.0.0.1:8000/api/projects \
-d 'name=API 测试项目&id=apitest&password=test1234&contact_email=api@example.com'
实测回显:
"apitest"
HTTP 201
三、API 总览
- 协议与风格:REST,主格式 JSON;POST/PUT 同时接受
application/x-www-form-urlencoded与 JSON body(两种均有实测);无独立版本前缀,端点固定在/api/下。 - 认证方式:① 项目级 Basic(
id:私有码);② Bearer Token(向项目签发,见 4.2);③ 创建项目(POST /api/projects)免认证。 - 限流:7.2.1 无内置限流参数(配置模板与镜像默认 Env 均无 RATELIMIT 项),官方文档亦未声明频控;公网实例如需限流在 Nginx 层实现。
API 路由总表(取自 ihatemoney routes 实测回显的 api.* 行,完整路由表见 7.7):
| Endpoint | Methods | Rule |
|---|---|---|
| api.projectshandler | POST | /api/projects |
| api.projecthandler | DELETE, GET, PUT | /api/projects/<project_id> |
| api.tokenhandler | GET | /api/projects/<project_id>/token |
| api.membershandler | GET, POST | /api/projects/<project_id>/members |
| api.memberhandler | DELETE, GET, PUT | /api/projects/<project_id>/members/<member_id> |
| api.billshandler | GET, POST | /api/projects/<project_id>/bills |
| api.billhandler | DELETE, GET, PUT | /api/projects/<project_id>/bills/<bill_id> |
| api.projectstatshandler | GET | /api/projects/<project_id>/statistics |
| api.currencieshandler | GET | /api/currencies |
四、认证与首次调用
4.1 Basic 认证
用项目 ID 与私有码做 HTTP Basic,查项目信息即「首次调用」:
curl -s -w "\nHTTP %{http_code}\n" --basic -u apitest:test1234 \
http://127.0.0.1:8000/api/projects/apitest
实测回显:
{"id": "apitest", "name": "API \u6d4b\u8bd5\u9879\u76ee", "contact_email": "api@example.com", "logging_preference": 1, "members": [], "default_currency": "XXX"}
HTTP 200
(中文按 JSON 默认转为 \uXXXX 转义序列,属标准行为,客户端解码后即原文。)
4.2 Token 认证(签发与使用)
Token 端点是 GET(官方示例即 GET;实测误用 POST 会得到 405):
curl -s --basic -u apitest:test1234 http://127.0.0.1:8000/api/projects/apitest/token
实测回显:
{"token": "WyJhcGl0ZXN0Il0.ErD4nxzYFN9dT3htRM2YhG-cKBs"}
token 由「["apitest"] 的 Base64 + 签名」构成(WyJhcGl0ZXN0Il0 解码即 ["apitest"]),与项目绑定。后续请求用 Bearer 头替代 Basic:
T=$(curl -s --basic -u apitest:test1234 http://127.0.0.1:8000/api/projects/apitest/token | sed -E 's/.*"token": "([^"]+)".*/\1/')
curl -s -o /dev/null -w "Bearer查项目 HTTP %{http_code}\n" --oauth2-bearer "$T" http://127.0.0.1:8000/api/projects/apitest
curl -s -o /dev/null -w "Bearer查成员 HTTP %{http_code}\n" --oauth2-bearer "$T" http://127.0.0.1:8000/api/projects/apitest/members
curl -s -o /dev/null -w "无效token HTTP %{http_code}\n" --oauth2-bearer "invalid.token.value" http://127.0.0.1:8000/api/projects/apitest
实测回显:
Bearer查项目 HTTP 200
Bearer查成员 HTTP 200
无效token HTTP 401
token 还可直接用作 Web 加入链接(官方文档定位:授予该项目读写权限、但不能改项目设置):
curl -s -o /dev/null -w "GET /apitest/join/<token> HTTP %{http_code}\n" \
"http://127.0.0.1:8000/apitest/join/$T"
实测回显:
GET /apitest/join/<token> HTTP 200
4.3 错误码语义(实测甄别)
| 场景 | 实测状态码 | 实测响应体 |
|---|---|---|
| 私有码错误 | 401 | {"message": "The server could not verify that you are authorized to access the URL requested. You either supplied the wrong credentials (e.g. a bad password), or your browser doesn't understand how to supply the credentials required."} |
| 项目不存在 | 401 | 与私有码错误完全相同(同一文案同一码) |
| 未带认证 | 401 | 同上 |
对 /api/projects 发 GET(列项目) | 405 | {"message": "The method is not allowed for the requested URL."} |
| 表单校验失败 | 400 | {"<字段名>": ["<原因>"]} |
| 项目内资源不存在(如账单 id) | 404 | "Not Found" |
注意两点:其一,「密码错」与「项目不存在」返回完全相同的 401,这是防枚举的安全设计,客户端脚本不能靠 401/404 区分两者;其二,官方出于安全不提供项目列表接口,GET /api/projects 直接 405(路由表里该端点只有 POST),自动化场景需自行维护项目 ID 清单。
五、各 API 域详解
5.1 项目域
创建项目 POST /api/projects(免认证)——必填 name/id/password/contact_email,可选 default_currency(ISO 4217,默认 XXX 表示无货币)。id 会被归一化(大写转小写等,实测提交 id=TestProj 返回 "testproj"):
curl -s -w "\nHTTP %{http_code}\n" -X POST http://127.0.0.1:8000/api/projects \
-d 'name=API 测试项目&id=apitest&password=test1234&contact_email=api@example.com'
实测回显:
"apitest"
HTTP 201
重复 id 与缺必填字段的失败形态(实测回显):
{"id": ["A project with this identifier (\"apitest\") already exists. Please choose a new identifier"]} ← 400
{"contact_email": ["This field is required."]} ← 400
也支持 JSON body 建项目(实测 201,回显 "jsontest")。
更新项目 PUT /api/projects/<id>——必须带 current_password(当前私有码),这是防「仅持 token 即可改设置」的安全措施;id 永远不可改。实测改私有码:
curl -s -w "\nHTTP %{http_code}\n" --basic -u apitest:test1234 -X PUT \
http://127.0.0.1:8000/api/projects/apitest \
-d 'name=API 测试项目&id=apitest¤t_password=test1234&password=newcode9&contact_email=api@example.com'
实测回显:
"UPDATED"
HTTP 200
随后旧私有码立即失效(401)、新私有码可查询(200);缺 current_password 的更新返回 {"current_password": ["This field is required."]} 400。default_currency 也在此端点修改(实测改 CNY 返回 "UPDATED",跨币种账单的连带影响见 5.5)。
删除项目 DELETE /api/projects/<id>(Basic 认证):
curl -s -w "\nHTTP %{http_code}\n" --basic -u apitest:test1234 -X DELETE \
http://127.0.0.1:8000/api/projects/apitest
实测回显:
"DELETED"
HTTP 200
删除后再查同项目返回 401(与「不存在」同形态,见 4.3)。
5.2 成员域
添加成员 POST /api/projects/<id>/members,参数 name,可选 weight(权重,默认 1)。实测依次添加 alice、bob、carol(weight=2)、dave,响应为纯数字 id(依次 1/2/3/4)。
成员列表 GET /api/projects/<id>/members:
curl -s --basic -u apitest:test1234 http://127.0.0.1:8000/api/projects/apitest/members
实测回显:
[{"id": 1, "name": "alice", "weight": 1.0, "activated": true}, {"id": 2, "name": "bob", "weight": 1.0, "activated": true}, {"id": 3, "name": "carol", "weight": 2.0, "activated": true}, {"id": 4, "name": "dave", "weight": 1.0, "activated": true}]
更新成员 PUT /api/projects/<id>/members/<member_id>——实测发现 7.2.1 的一个重要行为边界:PUT 是全量替换,未传的字段直接回退默认值。只传 name 会把成员停用并把权重打回 1:
curl -s -w "\nHTTP %{http_code}\n" --basic -u apitest:test1234 -X PUT \
http://127.0.0.1:8000/api/projects/apitest/members/3 -d 'name=Carol'
实测回显:
{"id": 7, "name": "Carol", "weight": 1.0, "activated": false}
HTTP 200
(weight 由 2.0 变 1.0、activated 由 true 变 false——与官方旧版文档「只传 name 也安全」的示例行为不一致,7.2.1 实测必须显式带上。)改回需传全字段,推荐 JSON body:
curl -s -w "\nHTTP %{http_code}\n" --basic -u apitest:test1234 -X PUT \
http://127.0.0.1:8000/api/projects/apitest/members/3 \
-H 'Content-Type: application/json' \
-d '{"name": "Carol", "weight": 2, "activated": true}'
实测回显:
{"id": 7, "name": "Carol", "weight": 2.0, "activated": true}
HTTP 200
删除成员 DELETE /api/projects/<id>/members/<member_id>——实测发现软硬两种删除:无账单引用的成员被硬删(实测删除 dave(id=8) 后重新添加,新 id=9,id 8 消失且列表无痕);有账单引用的成员被软删(实测删除有账单的 carol,返回 "OK",列表中仍可见但 activated: false):
[{"id": 5, "name": "alice", "weight": 1.0, "activated": true}, {"id": 6, "name": "bob", "weight": 1.0, "activated": true}, {"id": 7, "name": "Carol", "weight": 2.0, "activated": false}, {"id": 9, "name": "dave", "weight": 1.0, "activated": true}]
连带影响:activated=false 的成员不能作为账单的付款人/分摊人,选它会报 {"payed_for": ["'3' is not a valid choice for this field."]} 400(实测)。
5.3 账单域
创建账单 POST /api/projects/<id>/bills——必填 what/payer(成员 id)/payed_for(成员 id,多人用重复参数或 JSON 数组)/amount;可选 date(缺省为当天,实测不传 date 自动记为 2026-10-23)、original_currency、external_link。响应为纯数字账单 id。表单形态实测:
curl -s --basic -u apitest:test1234 -X POST http://127.0.0.1:8000/api/projects/apitest/bills \
-d 'what=晚餐&date=2026-10-20&payer=5&payed_for=6&payed_for=7&amount=150'
实测回显:
1
JSON 形态实测(多人分摊用数组):
curl -s --basic -u apitest:test1234 -X POST http://127.0.0.1:8000/api/projects/apitest/bills \
-H 'Content-Type: application/json' \
-d '{"what": "超市采购", "date": "2026-10-22", "payer": 6, "payed_for": [5, 7], "amount": 90}'
实测回显:
2
账单列表 GET /api/projects/<id>/bills(实测默认按日期倒序排列):
curl -s --basic -u apitest:test1234 http://127.0.0.1:8000/api/projects/apitest/bills
实测回显(节选首条,注意 7.2.1 新增 bill_type 字段):
[{"id": 3, "payer_id": 6, "owers": [{"id": 5, "name": "alice", "weight": 1.0, "activated": true}, {"id": 7, "name": "Carol", "weight": 2.0, "activated": true}], "amount": 60.0, "date": "2026-10-23", "creation_date": "2026-10-23", "what": "\u7535\u5f71\u7968", "bill_type": "Expense", "external_link": "", "original_currency": "XXX", "converted_amount": 60.0}, ...]
查单条 GET .../bills/<bill_id>——字段含 amount(原币金额)与 converted_amount(换算到项目默认货币后的金额,无货币项目两者相等)。
修改账单 PUT .../bills/<bill_id>(全字段重传,返回账单 id,实测 200 回显 3);删除账单 DELETE .../bills/<bill_id>(回显 "OK",实测 200)。查询不存在的账单返回 "Not Found" 404(实测 bill id 99999)。缺 payed_for 的创建返回 {"payed_for": ["This field is required."]} 400。
5.4 统计域
GET /api/projects/<id>/statistics——按成员返回 paid(垫付)、spent(消费,负值)、transferred/received(结算转账)、balance(净额)。基于上节三笔账单(晚餐 150=alice 垫付、超市 90 与电影 80=bob 垫付,carol 权重 2)实测:
curl -s --basic -u apitest:test1234 http://127.0.0.1:8000/api/projects/apitest/statistics
实测回显:
[{"member": {"id": 5, "name": "alice", "weight": 1.0, "activated": true}, "spent": -56.66666666666667, "paid": 150.0, "transferred": 0.0, "received": -0.0, "balance": 93.33333333333333}, {"member": {"id": 6, "name": "bob", "weight": 1.0, "activated": true}, "spent": -50.0, "paid": 170.0, "transferred": 0.0, "received": -0.0, "balance": 120.0}, {"member": {"id": 7, "name": "Carol", "weight": 2.0, "activated": true}, "spent": -213.33333333333334, "paid": 0.0, "transferred": 0.0, "received": -0.0, "balance": -213.33333333333334}]
数字可人工复核(权重分摊):晚餐 150 由 bob(1)+carol(2) 分摊,carol 承担 150×2/3=100;同理超市 90×2/3=60、电影 80×2/3≈53.33,合计 213.33,与回显一致。
5.5 货币域
GET /api/currencies 返回全部支持的 ISO 4217 货币码(实测首段 ["AED", "AFN", "ALL", ...],含无货币占位 XXX)。
重要边界:把项目 default_currency 改为 CNY 后,再创建 original_currency=USD 的账单被直接拒绝:
curl -s -w "\nHTTP %{http_code}\n" --basic -u apitest:test1234 -X POST \
http://127.0.0.1:8000/api/projects/apitest/bills \
-d 'what=跨境订阅&payer=5&payed_for=6&amount=100&original_currency=USD'
实测回显:
{"original_currency": ["Failed to convert from USD currency to CNY"]}
HTTP 400
7.2.1 没有内置汇率源,有默认货币的项目无法记其他币种的账单。建议:统一币种实例保持默认 XXX(各账单自记本币金额),或所有账单与项目默认货币一致。
5.6 错误码汇总
| 码 | 触发场景(实测) | 应对 |
|---|---|---|
| 400 | 校验失败(缺字段/重复 id/已停用成员入选/跨币种无汇率/ALLOW_PUBLIC_PROJECT_CREATION=False 拦停) | 看响应体字段级原因 |
| 401 | 私有码错、项目不存在、未认证、无效 token、项目已删除 | 同文案,无法区分,核对 id 与私有码 |
| 404 | 项目内资源不存在(账单/成员 id 无效) | 核对资源 id |
| 405 | 对只读/不存在的方法调端点(GET /api/projects、POST /token) | 改用正确方法 |
六、分页/过滤/排序通用约定
7.2.1 API 没有分页、过滤、排序参数,实测证据:
curl -s --basic -u apitest:test1234 "http://127.0.0.1:8000/api/projects/apitest/bills?limit=1" | python3 -c 'import sys,json; print(len(json.load(sys.stdin)))'
curl -s --basic -u apitest:test1234 "http://127.0.0.1:8000/api/projects/apitest/bills?order=name" | head -c 40; echo
实测回显(limit=1 仍返回全部 3 条;order=name 输出与不带参数完全相同):
3
[{"id": 3, "payer_id": 6, "owers": [{"id": 5, "nam
行为约定:账单列表默认按日期倒序固定返回全量;成员列表全量返回;项目列表不存在(405)。账单量大的项目(数千笔以上)如需分页/检索,建议在客户端拉全量后自行处理,或直接读数据库(表 bill/person,只读)。
七、CLI 管理命令详解
7.1 命令清单与调用形态
ihatemoney --help 实测回显(命令全集):
Commands:
db Perform database migrations.
delete-project Delete a project
generate-config Generate front-end server configuration
generate_password_hash Get password from user and hash it without...
get-project-count Count projets with at least x bills and at less...
limiter Flask-Limiter maintenance & utility commands
routes Show the routes for the app.
run Run a development server.
runserver Deprecated, use the "run" command instead
shell Run a shell in the app context.
调用形态(服务以专用用户运行时):
runuser -u ihatemoney -- bash -c 'source /home/ihatemoney/ihatemoney/bin/activate && ihatemoney <命令>'
注意:每个 ihatemoney 命令执行前都会加载应用并自动建库/跑迁移(输出形如 INFO [alembic.runtime.migration] Context impl SQLiteImpl. 属正常现象,非报错)。
7.2 generate-config——生成配置模板
ihatemoney generate-config --help
实测回显(支持 5 种模板):
Usage: ihatemoney generate-config [OPTIONS] {ihatemoney.cfg|apache-vhost.conf|
gunicorn.conf.py|supervisord.conf|nginx.conf
}
Generate front-end server configuration
用法(重定向落盘)与逐模板用途见《Ubuntu 22.04 安装 IHateMoney 7.2.1 指南》方案 A 实测段。
7.3 generate_password_hash——生成管理口令哈希
交互读入口令并输出 pbkdf2 哈希。7.2.1 只认 pbkdf2: 前缀、方法串格式为 pbkdf2:摘要:轮数(OpenSSL 3 下必须显式 sha256),完整三坑实测与配置方法见《IHateMoney 7.2.1 配置参数详解》5.2 节,此处不重复。
7.4 db——数据库迁移
子命令含 current/heads/history/check/upgrade/downgrade 等(ihatemoney db --help 可见全集)。实测查询当前迁移状态:
工作目录硬约束(实测踩坑):db 子命令默认在 当前目录 找 migrations/,不在包目录下执行会静默失败(rc=1、无任何错误文本)。实测对比:
$ cd /home/ihatemoney && ihatemoney db current # rc=1,无输出
$ cd /home/ihatemoney/ihatemoney/lib/python3.11/site-packages/ihatemoney && ihatemoney db current
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
c941aaca38c2 (head) # rc=0
定稿用法(先 cd 到包目录):
PKG=/home/ihatemoney/ihatemoney/lib/python3.11/site-packages/ihatemoney
runuser -u ihatemoney -- bash -c "source /home/ihatemoney/ihatemoney/bin/activate && cd $PKG && ihatemoney db current && ihatemoney db heads"
实测回显:
c941aaca38c2 (head)
c941aaca38c2 (head)
db history 实测(前 3 行,最近迁移在顶部):
06884b17c50f -> c941aaca38c2 (head), remove ip recording
7a9b38559992 -> 06884b17c50f, fix password hash length
927ed575acbd -> 7a9b38559992, new bill type attribute added
db check 在 SQLite 实例上实测返回 1 并输出模型与库结构的类型比对噪声(如 Detected type change from TEXT() to Enum(...))——这是 alembic 对 SQLite 类型比对能力的限制,判断升级状态以 db current/db heads 一致为准,不必因 check 报告告警。
7.5 get-project-count——统计活跃项目
源码参数默认值:print_emails=False、bills=0、days=73000(约 200 年,即不限时间)。语义:统计「账单数严格大于 bills 且最近一笔账单在 days 天内」的项目数。实测三种调用(测试项目 clitest 有 2 笔当天账单):
runuser -u ihatemoney -- bash -c 'source /home/ihatemoney/ihatemoney/bin/activate && ihatemoney get-project-count'
实测回显:
Number of projects: 1
ihatemoney get-project-count "" 1 30(超过 1 笔且 30 天内活跃)→ Number of projects: 1;ihatemoney get-project-count y 1 30(附带联系邮箱)实测回显:
Number of projects: 1
Contact email: cli@example.com
适用场景:实例管理员巡检活跃度(配合 journalctl 与数据库备份节奏决策)。
7.6 delete-project——删除项目
无交互确认,直接删除(源码即 db.session.delete(project)),删错只能从备份恢复。实测删除不存在的项目:
Project "nosuchproj" not found
(红色提示,rc=0。)实测删除 clitest 后验证:API 查询 401、get-project-count 归 0(均实测通过)。
7.7 routes——全路由表
ihatemoney routes 实测回显(API 之外的全部 Web 路由,做反向代理路径规划与安全收敛时对照):
Endpoint Methods Rule
----------------------------- ---------------- ---------------------------------------------------------
main.add_bill GET, POST /<project_id>/add
main.add_member GET, POST /<project_id>/members/add
main.add_settlement_bill POST /<project_id>/settle
main.admin GET, POST /admin
main.authenticate GET, POST /authenticate
main.change_lang GET /lang/<lang>
main.create_project GET, POST /create
main.dashboard GET /dashboard
main.dashboard_delete_project POST /dashboard/<project_id>/delete
main.delete_bill POST /<project_id>/delete/<int:bill_id>
main.delete_project POST /<project_id>/delete
main.demo GET /demo
main.edit_bill GET, POST /<project_id>/edit/<int:bill_id>
main.edit_member GET, POST /<project_id>/members/<member_id>/edit
main.edit_project GET, POST /<project_id>/edit
main.erase_history POST /<project_id>/erase_history
main.exit GET, POST /exit
main.export_project GET /<project_id>/export/<string:file>.<string:format>
main.favicon GET /favicon.ico
main.feed GET /<project_id>/feed/<string:token>.xml
main.health GET /healthcheck
main.history GET /<project_id>/history
main.home GET /
main.import_project POST /<project_id>/import
main.invite GET, POST /<project_id>/invite
main.join_project GET /<project_id>/join/<string:token>
main.list_bills GET /<project_id>/
main.mobile GET /mobile
main.password_reminder_sent GET /password-reminder-sent
main.reactivate POST /<project_id>/members/<member_id>/reactivate
main.remind_password GET, POST /password-reminder
main.remove_member POST /<project_id>/members/<member_id>/delete
main.reset_password GET, POST /reset-password
main.settle_bill GET /<project_id>/settle_bills
main.statistics GET /<project_id>/statistics
static GET /static/<path:filename>
(api.* 9 条已在第三章列表,此处略。)值得注意:/<project_id>/export/<file>.<format> 支持导出——file 取 bills(账单明细)或 transactions(结算转账清单),format 取 csv 或 json(其余取值 404,分支逻辑实测源码确认);/dashboard 即管理仪表盘(受 ACTIVATE_ADMIN_DASHBOARD 与 ADMIN_PASSWORD 控制,见配置篇)。
7.8 run / runserver——开发服务器
ihatemoney run 拉起 Werkzeug 开发服务器(-p 端口、-h 地址),实测:
runuser -u ihatemoney -- bash -c 'source /home/ihatemoney/ihatemoney/bin/activate && ihatemoney run -p 8001 -h 127.0.0.1 &'
sleep 4
curl -s -o /dev/null -w "dev server HTTP %{http_code}\n" http://127.0.0.1:8001/
实测回显:
dev server HTTP 200
INFO [werkzeug] WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
开发服务器仅供调试(启动日志自带 Werkzeug 警告),生产一律 Gunicorn/systemd(见 B1)。runserver 为弃用别名,勿再使用。
7.9 shell / limiter
shell 在应用上下文内开 Python Shell(可直查 Project/Person/Bill 模型,排查数据问题用,交互式工具不做回显截图);limiter 为 Flask-Limiter 维护命令,实测其子命令清单:
Commands:
clear Clear limits for a specific key
config View the extension configuration
limits Enumerate details about all routes with rate limits
(默认配置未启用限流,命令日常用不上,列此处备查。)
八、常见问题(FAQ)
Q1:API 返回 401,到底是密码错了还是项目不存在?
无法区分——两者返回完全相同的 401 与文案(实测见 4.3),这是防枚举设计。核对项目 id 拼写与私有码即可;用 token 的场景先重新签发一次 token 排除签名密钥变更(SECRET_KEY 换过会导致旧 token 全部失效)。
Q2:PUT 改成员后,成员被停用、权重也丢了?
7.2.1 的成员 PUT 是全量替换:未传 activated 会被置 false、未传 weight 回退 1.0(实测见 5.2)。改成员必须带全字段,推荐 JSON body {"name": "Carol", "weight": 2, "activated": true}。停用的成员立即不能被账单选用('3' is not a valid choice)。
Q3:脚本想拉全部项目列表,怎么做?
API 层无解:GET /api/projects 是 405(官方安全设计,见 4.3)。方案:调用方自行维护项目清单(建项目时登记);或管理员在服务器上只读查库(SQLite select id from project;)。
Q4:记外币账单报 Failed to convert from USD currency to CNY?
项目设了默认货币后,7.2.1 要求账单币种可换算到默认货币,而应用无内置汇率源(实测见 5.5)。保持项目为 XXX(无货币)或统一币种记账。
Q5:ihatemoney db current 没有任何输出、退出码 1?
db 子命令默认在当前目录找 migrations/,不在包目录执行即静默失败(实测见 7.4)。先 cd 到 site-packages/ihatemoney 再执行。
Q6:token 是永久的吗?能干什么、不能干什么?
token 与项目绑定(payload 即项目 id 数组 + 签名),无过期时间,但 SECRET_KEY 变更后全部失效。能通过 Bearer 调该项目全部 API;用作 Web 链接 /apitest/join/<token> 时授予读写访问但不能改项目设置(官方文档定位)。签发通道(GET /token)本身需要 Basic 认证,泄露面可控。
Q7:升级后 API 建项目突然全部 400?
检查 ALLOW_PUBLIC_PROJECT_CREATION 是否被改成 False——该参数连 REST API 的建项目与改项目一起拦停且无管理口令旁路(4 个实测回显见《IHateMoney 7.2.1 配置参数详解》5.3 节)。需要 API 自动化的实例保持 True。
九、参考链接
- 官方 REST API 文档:https://ihatemoney.readthedocs.io/en/latest/api.html
- 源码 API 实现:https://github.com/spiral-project/ihatemoney/tree/master/ihatemoney/api
- 源码 CLI 实现:https://github.com/spiral-project/ihatemoney/blob/master/ihatemoney/manage.py
- 本专栏其他篇目:《IHateMoney 7.2.1 介绍与选型指南》《Ubuntu 22.04 安装 IHateMoney 7.2.1 指南》《IHateMoney 7.2.1 Docker Compose 部署指南》《IHateMoney 7.2.1 配置参数详解》