fastapi-openrpc 0.1.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. fastapi_openrpc-0.1.3/.env.example +5 -0
  2. fastapi_openrpc-0.1.3/.gitignore +21 -0
  3. fastapi_openrpc-0.1.3/.pre-commit-config.yaml +46 -0
  4. fastapi_openrpc-0.1.3/CLAUDE.md +76 -0
  5. fastapi_openrpc-0.1.3/Justfile +73 -0
  6. fastapi_openrpc-0.1.3/PKG-INFO +25 -0
  7. fastapi_openrpc-0.1.3/README.md +206 -0
  8. fastapi_openrpc-0.1.3/README_zh.md +196 -0
  9. fastapi_openrpc-0.1.3/docs/agents/domain.md +47 -0
  10. fastapi_openrpc-0.1.3/docs/agents/issue-tracker.md +52 -0
  11. fastapi_openrpc-0.1.3/docs/agents/triage-labels.md +25 -0
  12. fastapi_openrpc-0.1.3/docs/brainstorms/2026-04-23-openrpc-components-schemas-requirements.md +127 -0
  13. fastapi_openrpc-0.1.3/docs/brainstorms/2026-04-23-rpc-context-type-injection-requirements.md +97 -0
  14. fastapi_openrpc-0.1.3/docs/brainstorms/2026-06-01-replay-protection-requirements.md +102 -0
  15. fastapi_openrpc-0.1.3/docs/en/index.md +844 -0
  16. fastapi_openrpc-0.1.3/docs/explanation-concurrency.md +233 -0
  17. fastapi_openrpc-0.1.3/docs/howto-error-handling.md +465 -0
  18. fastapi_openrpc-0.1.3/docs/index.md +11 -0
  19. fastapi_openrpc-0.1.3/docs/plans/2026-04-23-001-refactor-rpc-context-type-injection-plan.md +252 -0
  20. fastapi_openrpc-0.1.3/docs/plans/2026-04-23-002-refactor-openrpc-components-schemas-plan.md +474 -0
  21. fastapi_openrpc-0.1.3/docs/plans/2026-06-01-001-feat-replay-protection-plan.md +260 -0
  22. fastapi_openrpc-0.1.3/docs/plans/2026-06-01-002-fix-code-review-issues-plan.md +353 -0
  23. fastapi_openrpc-0.1.3/docs/tutorial-pydantic-models.md +353 -0
  24. fastapi_openrpc-0.1.3/docs/zh/index.md +852 -0
  25. fastapi_openrpc-0.1.3/examples/hmac_auth_dependency.py +66 -0
  26. fastapi_openrpc-0.1.3/examples/minimal_app.py +41 -0
  27. fastapi_openrpc-0.1.3/examples/pydantic_models.py +120 -0
  28. fastapi_openrpc-0.1.3/pyproject.toml +62 -0
  29. fastapi_openrpc-0.1.3/src/fastapi_openrpc/__init__.py +104 -0
  30. fastapi_openrpc-0.1.3/src/fastapi_openrpc/authentication.py +248 -0
  31. fastapi_openrpc-0.1.3/src/fastapi_openrpc/authorization.py +79 -0
  32. fastapi_openrpc-0.1.3/src/fastapi_openrpc/constants.py +43 -0
  33. fastapi_openrpc-0.1.3/src/fastapi_openrpc/decorator.py +517 -0
  34. fastapi_openrpc-0.1.3/src/fastapi_openrpc/document.py +329 -0
  35. fastapi_openrpc-0.1.3/src/fastapi_openrpc/errors.py +125 -0
  36. fastapi_openrpc-0.1.3/src/fastapi_openrpc/registry.py +132 -0
  37. fastapi_openrpc-0.1.3/src/fastapi_openrpc/router.py +905 -0
  38. fastapi_openrpc-0.1.3/src/fastapi_openrpc/types.py +99 -0
  39. fastapi_openrpc-0.1.3/tests/http/.httpyac.js +55 -0
  40. fastapi_openrpc-0.1.3/tests/http/api.http +150 -0
  41. fastapi_openrpc-0.1.3/tests/http/hmac.http +32 -0
  42. fastapi_openrpc-0.1.3/tests/http/minimal.http +25 -0
  43. fastapi_openrpc-0.1.3/tests/http/pydantic.http +38 -0
  44. fastapi_openrpc-0.1.3/tests/test_authentication.py +821 -0
  45. fastapi_openrpc-0.1.3/tests/test_authorization.py +179 -0
  46. fastapi_openrpc-0.1.3/tests/test_decorator.py +434 -0
  47. fastapi_openrpc-0.1.3/tests/test_document.py +470 -0
  48. fastapi_openrpc-0.1.3/tests/test_errors.py +126 -0
  49. fastapi_openrpc-0.1.3/tests/test_registry.py +160 -0
  50. fastapi_openrpc-0.1.3/tests/test_router.py +496 -0
  51. fastapi_openrpc-0.1.3/uv.lock +655 -0
@@ -0,0 +1,5 @@
1
+ # httpYac / API 测试配置
2
+ # 复制为 .env 并填入实际值
3
+ # cp .env.example .env
4
+
5
+ HOST=http://localhost:8000
@@ -0,0 +1,21 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .venv/
8
+
9
+ # Testing
10
+ .pytest_cache/
11
+ .coverage
12
+ htmlcov/
13
+
14
+ # Linting
15
+ .ruff_cache/
16
+
17
+ # httpYac
18
+ .env
19
+ .env.local
20
+ .env.*.local
21
+ .httpyac.cache
@@ -0,0 +1,46 @@
1
+ # .pre-commit-config.yaml
2
+ # 使用方法:
3
+ # pre-commit install # 安装 hook(首次)
4
+ # pre-commit run --all-files # 手动运行所有文件
5
+ # pre-commit run # 仅运行 staged 文件
6
+ # pre-commit autoupdate # 更新 hook 版本
7
+
8
+ repos:
9
+ # ========================
10
+ # Ruff — Lint + Format
11
+ # ========================
12
+ - repo: https://github.com/astral-sh/ruff-pre-commit
13
+ rev: v0.11.5
14
+ hooks:
15
+ - id: ruff
16
+ args: [--fix]
17
+ - id: ruff-format
18
+
19
+ # ========================
20
+ # Pyright — 类型检查
21
+ # ========================
22
+ - repo: https://github.com/RobertCraigie/pyright-python
23
+ rev: v1.1.402
24
+ hooks:
25
+ - id: pyright
26
+ additional_dependencies:
27
+ - fastapi>=0.100.0
28
+ - pydantic>=2.0
29
+ - pytest>=8.0
30
+ - pytest-asyncio>=0.23
31
+ - httpx>=0.27
32
+ - starlette>=0.30.0
33
+ - uvicorn>=0.30.0
34
+
35
+ # ========================
36
+ # 通用工具
37
+ # ========================
38
+ - repo: https://github.com/pre-commit/pre-commit-hooks
39
+ rev: v5.0.0
40
+ hooks:
41
+ - id: check-yaml
42
+ - id: check-json
43
+ - id: check-added-large-files
44
+ args: [--maxkb=500]
45
+ - id: end-of-file-fixer
46
+ - id: trailing-whitespace
@@ -0,0 +1,76 @@
1
+ # CLAUDE.md
2
+
3
+ These rules apply to every task in this project unless explicitly overridden.
4
+ Bias: caution over speed on non-trivial work. Use judgment on trivial tasks.
5
+
6
+ ## Agent Skills
7
+
8
+ ### Issue tracker
9
+
10
+ Local markdown issues under `.scratch/<feature-slug>/`. See `docs/agents/issue-tracker.md`.
11
+
12
+ ### Triage labels
13
+
14
+ Canonical labels: `needs-triage`, `needs-info`, `ready-for-agent`, `ready-for-human`, `wontfix`. See `docs/agents/triage-labels.md`.
15
+
16
+ ### Domain docs
17
+
18
+ Single-context: one `CONTEXT.md` + `docs/adr/` at repo root. See `docs/agents/domain.md`.
19
+
20
+ ## Rule 1 — Think Before Coding
21
+ State assumptions explicitly. If uncertain, ask rather than guess.
22
+ Present multiple interpretations when ambiguity exists.
23
+ Push back when a simpler approach exists.
24
+ Stop when confused. Name what's unclear.
25
+
26
+ ## Rule 2 — Simplicity First
27
+ Minimum code that solves the problem. Nothing speculative.
28
+ No features beyond what was asked. No abstractions for single-use code.
29
+ Test: would a senior engineer say this is overcomplicated? If yes, simplify.
30
+
31
+ ## Rule 3 — Surgical Changes
32
+ Touch only what you must. Clean up only your own mess.
33
+ Don't "improve" adjacent code, comments, or formatting.
34
+ Don't refactor what isn't broken. Match existing style.
35
+
36
+ ## Rule 4 — Goal-Driven Execution
37
+ Define success criteria. Loop until verified.
38
+ Don't follow steps. Define success and iterate.
39
+ Strong success criteria let you loop independently.
40
+
41
+ ## Rule 5 — Use the model only for judgment calls
42
+ Use me for: classification, drafting, summarization, extraction.
43
+ Do NOT use me for: routing, retries, deterministic transforms.
44
+ If code can answer, code answers.
45
+
46
+ ## Rule 6 — Token budgets are not advisory
47
+ Per-task: 4,000 tokens. Per-session: 30,000 tokens.
48
+ If approaching budget, summarize and start fresh.
49
+ Surface the breach. Do not silently overrun.
50
+
51
+ ## Rule 7 — Surface conflicts, don't average them
52
+ If two patterns contradict, pick one (more recent / more tested).
53
+ Explain why. Flag the other for cleanup.
54
+ Don't blend conflicting patterns.
55
+
56
+ ## Rule 8 — Read before you write
57
+ Before adding code, read exports, immediate callers, shared utilities.
58
+ "Looks orthogonal" is dangerous. If unsure why code is structured a way, ask.
59
+
60
+ ## Rule 9 — Tests verify intent, not just behavior
61
+ Tests must encode WHY behavior matters, not just WHAT it does.
62
+ A test that can't fail when business logic changes is wrong.
63
+
64
+ ## Rule 10 — Checkpoint after every significant step
65
+ Summarize what was done, what's verified, what's left.
66
+ Don't continue from a state you can't describe back.
67
+ If you lose track, stop and restate.
68
+
69
+ ## Rule 11 — Match the codebase's conventions, even if you disagree
70
+ Conformance > taste inside the codebase.
71
+ If you genuinely think a convention is harmful, surface it. Don't fork silently.
72
+
73
+ ## Rule 12 — Fail loud
74
+ "Completed" is wrong if anything was skipped silently.
75
+ "Tests pass" is wrong if any were skipped.
76
+ Default to surfacing uncertainty, not hiding it.
@@ -0,0 +1,73 @@
1
+ # Justfile for fastapi-openrpc
2
+
3
+ # 默认目标:运行所有测试
4
+ default: test
5
+
6
+ # 安装依赖
7
+ install:
8
+ uv sync
9
+
10
+ # 开发依赖
11
+ install-dev:
12
+ uv sync --extra dev
13
+
14
+ # 运行所有测试
15
+ test:
16
+ uv run pytest tests/ -v
17
+
18
+ # 运行测试并显示覆盖率
19
+ test-cov:
20
+ uv run pytest tests/ -v --cov=src/fastapi_openrpc --cov-report=term-missing
21
+
22
+ # 启动 minimal 示例(PORT 环境变量可覆盖,如 PORT=9000 just run-minimal)
23
+ run-minimal:
24
+ uv run uvicorn examples.minimal_app:app --host 0.0.0.0 --port 10333 --reload
25
+
26
+ # 测试 minimal 示例(启动服务,退出时关闭)
27
+ test-minimal:
28
+ bash -c 'uv run uvicorn examples.minimal_app:app --host 0.0.0.0 --port 10333 & PID=$$!; trap "kill $PID 2>/dev/null; wait $PID 2>/dev/null; true" EXIT; npx httpyac tests/http/minimal.http -a'
29
+
30
+ # 启动 HMAC 认证示例(需先设置 HMAC_SECRET_KEY)
31
+ run-hmac:
32
+ HEADER_SIGNING_KEY=my-secret-key uv run uvicorn examples.hmac_auth_dependency:app --host 0.0.0.0 --port 10334 --reload
33
+
34
+ test-hmac:
35
+ bash -c 'HMAC_SECRET_KEY=my-secret-key uv run uvicorn examples.hmac_auth_dependency:app --host 0.0.0.0 --port 10334 & PID=$$!; trap "kill $PID 2>/dev/null; wait $PID 2>/dev/null; true" EXIT; npx httpyac tests/http/hmac.http -a'
36
+
37
+ # 启动 Pydantic 模型示例
38
+ run-pydantic:
39
+ uv run uvicorn examples.pydantic_models:app --host 0.0.0.0 --port 10335 --reload
40
+
41
+ test-pydantic:
42
+ bash -c 'uv run uvicorn examples.pydantic_models:app --host 0.0.0.0 --port 10335 & PID=$$!; trap "kill $PID 2>/dev/null; wait $PID 2>/dev/null; true" EXIT; npx httpyac tests/http/pydantic.http -a'
43
+
44
+ # Lint
45
+ lint:
46
+ uv run ruff check src/ tests/
47
+
48
+ # 格式化
49
+ fmt:
50
+ uv run ruff format src/ tests/
51
+
52
+ # 类型检查
53
+ typecheck:
54
+ uv run pyright
55
+
56
+ # 完整开发检查:lint + typecheck + test
57
+ check: lint typecheck test
58
+
59
+ # 运行 pre-commit(仅 staged 文件)
60
+ precommit:
61
+ uv run pre-commit run
62
+
63
+ # 运行 pre-commit 所有文件
64
+ precommit-all:
65
+ uv run pre-commit run --all-files
66
+
67
+ # 安装 pre-commit hook(首次克隆后运行一次)
68
+ precommit-install:
69
+ pre-commit install
70
+
71
+ # 列出所有可用目标
72
+ help:
73
+ just --list
@@ -0,0 +1,25 @@
1
+ Metadata-Version: 2.4
2
+ Name: fastapi-openrpc
3
+ Version: 0.1.3
4
+ Summary: FastAPI JSON-RPC 2.0 + OpenRPC 1.0 service discovery plugin
5
+ Author-email: Ye Wenbin <yewenbin@winwin-tech.com>
6
+ License: MIT
7
+ Keywords: ai-agent,discover,fastapi,json-rpc,openrpc,rpc
8
+ Classifier: Framework :: FastAPI
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Requires-Python: >=3.11
14
+ Requires-Dist: fastapi>=0.100.0
15
+ Requires-Dist: pydantic>=2.0
16
+ Requires-Dist: typing-extensions>=4.8
17
+ Provides-Extra: dev
18
+ Requires-Dist: fakeredis>=2.21; extra == 'dev'
19
+ Requires-Dist: httpx>=0.27; extra == 'dev'
20
+ Requires-Dist: mypy>=1.10; extra == 'dev'
21
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
22
+ Requires-Dist: pytest>=8.0; extra == 'dev'
23
+ Requires-Dist: ruff>=0.4; extra == 'dev'
24
+ Provides-Extra: redis
25
+ Requires-Dist: redis[hiredis]>=5.0; extra == 'redis'
@@ -0,0 +1,206 @@
1
+ # fastapi-openrpc
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/fastapi-openrpc)](https://pypi.org/project/fastapi-openrpc/)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/fastapi-openrpc.svg)](https://pypi.org/project/fastapi-openrpc/)
5
+ [![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
6
+
7
+ Declarative JSON-RPC 2.0 interface and OpenRPC 1.0 service discovery for FastAPI applications.
8
+
9
+ ## Features
10
+
11
+ - **Declarative Registration** — `@method()` decorator with automatic JSON Schema inference
12
+ - **OpenRPC Service Discovery** — `GET /openrpc.json` returns complete OpenRPC document
13
+ - **Pydantic Support** — Automatic schema generation with `$ref` references
14
+ - **RpcContext Propagation** — Secure caller context injection via FastAPI `Depends()`
15
+ - **HMAC Authentication** — Built-in signature verification with Nonce replay protection
16
+ - **Role-Based Authorization** — Method-level role declarations with composition semantics
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ pip install fastapi-openrpc
22
+ ```
23
+
24
+ Optional dependencies:
25
+
26
+ ```bash
27
+ # Redis support (Nonce replay protection)
28
+ pip install fastapi-openrpc[redis]
29
+
30
+ # Development dependencies
31
+ pip install fastapi-openrpc[dev]
32
+ ```
33
+
34
+ ## Quick Start
35
+
36
+ ```python
37
+ from fastapi import FastAPI
38
+ from fastapi_openrpc import OpenRpcRouter, RpcContext, method
39
+
40
+ app = FastAPI()
41
+
42
+ openrpc_router = OpenRpcRouter(
43
+ title="My API",
44
+ version="1.0.0",
45
+ description="My JSON-RPC service",
46
+ )
47
+
48
+ @method(name="getUser")
49
+ async def get_user(ctx: RpcContext, user_id: str) -> dict:
50
+ """Get user information by user_id."""
51
+ return {"id": user_id, "name": "Alice", "roles": ctx.roles}
52
+
53
+ @method(name="listUsers")
54
+ async def list_users(ctx: RpcContext, limit: int = 10) -> list[dict]:
55
+ """Return a list of users."""
56
+ return [{"id": i, "name": f"User {i}"} for i in range(limit)]
57
+
58
+ app.include_router(openrpc_router)
59
+ ```
60
+
61
+ After starting the server, access `GET /openrpc.json` to view the OpenRPC service document.
62
+
63
+ ## Authentication
64
+
65
+ ### HMAC Authentication
66
+
67
+ ```python
68
+ from fastapi_openrpc.authentication import HMACAuthentication
69
+
70
+ auth = HMACAuthentication(secret_key="your-secret-key")
71
+ openrpc_router = OpenRpcRouter(
72
+ authentication=auth,
73
+ )
74
+ ```
75
+
76
+ ### Nonce Replay Protection
77
+
78
+ ```python
79
+ auth = HMACAuthentication(
80
+ secret_key="your-secret-key",
81
+ enable_nonce=True,
82
+ redis_url="redis://localhost:6379",
83
+ nonce_ttl=3600,
84
+ redis_required=True, # Reject requests when Redis is unavailable
85
+ connect_timeout=2.0, # Redis connection timeout (seconds)
86
+ operation_timeout=5.0, # Redis operation timeout (seconds)
87
+ cleanup_timeout=2.0, # Redis cleanup timeout (seconds)
88
+ )
89
+ ```
90
+
91
+ Header format:
92
+
93
+ ```json
94
+ {
95
+ "roles": ["admin"],
96
+ "nonce": "550e8400-e29b-41d4-a716-446655440000"
97
+ }
98
+ ```
99
+
100
+ ## Authorization
101
+
102
+ ```python
103
+ from fastapi_openrpc import Require
104
+
105
+ @method(name="adminAction", required_roles=["admin"])
106
+ async def admin_action(ctx: RpcContext) -> dict:
107
+ """Admin only."""
108
+ return {"status": "done"}
109
+
110
+ @method(name="editContent", required_roles=["editor", "admin"], require=Require.ALL)
111
+ async def edit_content(ctx: RpcContext) -> dict:
112
+ """Requires both editor and admin roles."""
113
+ return {"status": "edited"}
114
+ ```
115
+
116
+ ## Error Handling
117
+
118
+ ```python
119
+ from fastapi_openrpc import InvalidParams, MethodNotFound
120
+
121
+ @method(name="divide")
122
+ async def divide(ctx: RpcContext, a: float, b: float) -> float:
123
+ if b == 0:
124
+ raise InvalidParams("Division by zero")
125
+ return a / b
126
+ ```
127
+
128
+ Built-in error codes:
129
+
130
+ | Error | Code |
131
+ |-------|------|
132
+ | Parse error | -32700 |
133
+ | Invalid Request | -32600 |
134
+ | Method Not Found | -32601 |
135
+ | Invalid Params | -32602 |
136
+ | Internal Error | -32603 |
137
+ | Forbidden | -32000 |
138
+ | Replay Detected | -32002 |
139
+
140
+ Library reserves -32000 ~ -32099, application custom errors start from -32100.
141
+
142
+ ## API Reference
143
+
144
+ ### OpenRpcRouter
145
+
146
+ ```python
147
+ from concurrent.futures import ThreadPoolExecutor
148
+
149
+ executor = ThreadPoolExecutor(max_workers=4)
150
+ openrpc_router = OpenRpcRouter(
151
+ title="My API",
152
+ version="1.0.0",
153
+ description="...",
154
+ servers=[{"url": "https://api.example.com"}],
155
+ authentication=HMACAuthentication(secret_key="..."), # Optional
156
+ executor=executor, # Optional, for sync handler thread pool
157
+ )
158
+ app.include_router(openrpc_router)
159
+ ```
160
+
161
+ ### @method() Decorator
162
+
163
+ ```python
164
+ @method(
165
+ name="myMethod", # RPC method name
166
+ tags=["admin"], # OpenRPC tags
167
+ summary="Short description",
168
+ deprecated=False,
169
+ required_roles=["admin"], # Required roles
170
+ require=Require.ALL, # Role composition semantics
171
+ )
172
+ async def my_handler(ctx: RpcContext, arg1: str, arg2: int = 10) -> dict:
173
+ return {"result": arg1}
174
+ ```
175
+
176
+ > **Note**: `ctx: RpcContext` is a reserved parameter name for injecting caller context.
177
+
178
+ ## Usage Examples
179
+
180
+ ```bash
181
+ # Call RPC method
182
+ curl -X POST http://localhost:8000/ \
183
+ -H "Content-Type: application/json" \
184
+ -d '{"jsonrpc": "2.0", "id": 1, "method": "getUser", "params": {"user_id": "42"}}'
185
+
186
+ # Service discovery
187
+ curl http://localhost:8000/openrpc.json
188
+ ```
189
+
190
+ ## Documentation
191
+
192
+ For detailed user documentation, see [docs/index.md](docs/index.md).
193
+
194
+ Additional guides:
195
+
196
+ - [Tutorial: Pydantic Models](docs/tutorial-pydantic-models.md) — 4 scenarios covering flat params, nested models, optional fields, and validation
197
+ - [How-to: Error Handling](docs/howto-error-handling.md) — custom errors, Nonce replay protection, best practices
198
+ - [Explanation: Concurrency Model](docs/explanation-concurrency.md) — ThreadPoolExecutor, sync handler wrapping, closure safety
199
+
200
+ ## Contributing
201
+
202
+ Issues and Pull Requests are welcome.
203
+
204
+ ## License
205
+
206
+ MIT
@@ -0,0 +1,196 @@
1
+ # fastapi-openrpc
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/fastapi-openrpc)](https://pypi.org/project/fastapi-openrpc/)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/fastapi-openrpc.svg)](https://pypi.org/project/fastapi-openrpc/)
5
+ [![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
6
+
7
+ 为 FastAPI 应用提供声明式 JSON-RPC 2.0 接口和 OpenRPC 1.0 服务发现支持。
8
+
9
+ ## 特性
10
+
11
+ - **声明式注册** — `@method()` 装饰器,自动推断 JSON Schema
12
+ - **OpenRPC 服务发现** — `GET /openrpc.json` 返回完整 OpenRPC 文档
13
+ - **Pydantic 支持** — 自动生成 schema,支持 `$ref` 引用
14
+ - **RpcContext 传递** — 通过 FastAPI `Depends()` 安全注入调用者上下文
15
+ - **HMAC 认证** — 内置签名验证,支持 Nonce 重放攻击防护
16
+ - **角色授权** — 方法级角色声明与组合语义支持
17
+
18
+ ## 安装
19
+
20
+ ```bash
21
+ pip install fastapi-openrpc
22
+ ```
23
+
24
+ 可选依赖:
25
+
26
+ ```bash
27
+ # Redis 支持(Nonce 重放防护)
28
+ pip install fastapi-openrpc[redis]
29
+
30
+ # 开发依赖
31
+ pip install fastapi-openrpc[dev]
32
+ ```
33
+
34
+ ## 快速开始
35
+
36
+ ```python
37
+ from fastapi import FastAPI
38
+ from fastapi_openrpc import OpenRpcRouter, RpcContext, method
39
+
40
+ app = FastAPI()
41
+
42
+ openrpc_router = OpenRpcRouter(
43
+ title="My API",
44
+ version="1.0.0",
45
+ description="My JSON-RPC service",
46
+ )
47
+
48
+ @method(name="getUser")
49
+ async def get_user(ctx: RpcContext, user_id: str) -> dict:
50
+ """根据 user_id 返回用户信息。"""
51
+ return {"id": user_id, "name": "Alice", "roles": ctx.roles}
52
+
53
+ @method(name="listUsers")
54
+ async def list_users(ctx: RpcContext, limit: int = 10) -> list[dict]:
55
+ """返回用户列表。"""
56
+ return [{"id": i, "name": f"User {i}"} for i in range(limit)]
57
+
58
+ app.include_router(openrpc_router)
59
+ ```
60
+
61
+ 启动后访问 `GET /openrpc.json` 查看 OpenRPC 服务文档。
62
+
63
+ ## 认证
64
+
65
+ ### HMAC 认证
66
+
67
+ ```python
68
+ from fastapi_openrpc.authentication import HMACAuthentication
69
+
70
+ auth = HMACAuthentication(secret_key="your-secret-key")
71
+ openrpc_router = OpenRpcRouter(
72
+ authentication=auth,
73
+ )
74
+ ```
75
+
76
+ ### Nonce 重放防护
77
+
78
+ ```python
79
+ auth = HMACAuthentication(
80
+ secret_key="your-secret-key",
81
+ enable_nonce=True,
82
+ redis_url="redis://localhost:6379",
83
+ nonce_ttl=3600,
84
+ redis_required=True, # Redis 不可用时拒绝请求
85
+ connect_timeout=2.0, # Redis 连接超时(秒)
86
+ operation_timeout=5.0, # Redis 操作超时(秒)
87
+ cleanup_timeout=2.0, # Redis 清理超时(秒)
88
+ )
89
+ ```
90
+
91
+ Header 格式:
92
+
93
+ ```json
94
+ {
95
+ "roles": ["admin"],
96
+ "nonce": "550e8400-e29b-41d4-a716-446655440000"
97
+ }
98
+ ```
99
+
100
+ ## 授权
101
+
102
+ ```python
103
+ from fastapi_openrpc import Require
104
+
105
+ @method(name="adminAction", required_roles=["admin"])
106
+ async def admin_action(ctx: RpcContext) -> dict:
107
+ """仅限 admin 角色。"""
108
+ return {"status": "done"}
109
+
110
+ @method(name="editContent", required_roles=["editor", "admin"], require=Require.ALL)
111
+ async def edit_content(ctx: RpcContext) -> dict:
112
+ """需要同时拥有 editor 和 admin 角色。"""
113
+ return {"status": "edited"}
114
+ ```
115
+
116
+ ## 错误处理
117
+
118
+ ```python
119
+ from fastapi_openrpc import InvalidParams, MethodNotFound
120
+
121
+ @method(name="divide")
122
+ async def divide(ctx: RpcContext, a: float, b: float) -> float:
123
+ if b == 0:
124
+ raise InvalidParams("Division by zero")
125
+ return a / b
126
+ ```
127
+
128
+ 内置错误码:
129
+
130
+ | 错误 | 码 |
131
+ |------|-----|
132
+ | Parse error | -32700 |
133
+ | Invalid Request | -32600 |
134
+ | Method Not Found | -32601 |
135
+ | Invalid Params | -32602 |
136
+ | Internal Error | -32603 |
137
+ | Forbidden | -32000 |
138
+ | Replay Detected | -32002 |
139
+
140
+ 库保留 -32000 ~ -32099,应用自定义错误从 -32100 起。
141
+
142
+ ## API 参考
143
+
144
+ ### OpenRpcRouter
145
+
146
+ ```python
147
+ openrpc_router = OpenRpcRouter(
148
+ title="My API",
149
+ version="1.0.0",
150
+ description="...",
151
+ servers=[{"url": "https://api.example.com"}],
152
+ authentication=HMACAuthentication(secret_key="..."), # 可选
153
+ )
154
+ app.include_router(openrpc_router)
155
+ ```
156
+
157
+ ### @method() 装饰器
158
+
159
+ ```python
160
+ @method(
161
+ name="myMethod", # RPC 方法名
162
+ tags=["admin"], # OpenRPC 标签
163
+ summary="简短描述",
164
+ deprecated=False,
165
+ required_roles=["admin"], # 所需角色
166
+ require=Require.ALL, # 角色组合语义
167
+ )
168
+ async def my_handler(ctx: RpcContext, arg1: str, arg2: int = 10) -> dict:
169
+ return {"result": arg1}
170
+ ```
171
+
172
+ > **注意**: 参数 `ctx: RpcContext` 是保留参数名,用于注入调用者上下文。
173
+
174
+ ## 调用示例
175
+
176
+ ```bash
177
+ # 调用 RPC 方法
178
+ curl -X POST http://localhost:8000/ \
179
+ -H "Content-Type: application/json" \
180
+ -d '{"jsonrpc": "2.0", "id": 1, "method": "getUser", "params": {"user_id": "42"}}'
181
+
182
+ # 服务发现
183
+ curl http://localhost:8000/openrpc.json
184
+ ```
185
+
186
+ ## 完整文档
187
+
188
+ 详细的用户文档请参阅 [docs/index.md](docs/zh/index.md)。
189
+
190
+ ## 贡献
191
+
192
+ 欢迎提交 Issue 和 Pull Request。
193
+
194
+ ## License
195
+
196
+ MIT
@@ -0,0 +1,47 @@
1
+ # Domain Context
2
+
3
+ Single-context project: one `CONTEXT.md` at repo root + `docs/adr/` for architectural decisions.
4
+
5
+ ## CONTEXT.md
6
+
7
+ Project-level context: what the project does, key concepts, terminology, and patterns.
8
+
9
+ ## docs/adr/
10
+
11
+ Architecture Decision Records documenting significant choices.
12
+
13
+ ### ADR format
14
+
15
+ ```
16
+ docs/adr/<YYYY-MM-DD>-<short-title>.md
17
+ ```
18
+
19
+ Example: `docs/adr/2024-01-15-use-pydantic-v2-for-validation.md`
20
+
21
+ ### ADR template
22
+
23
+ ```markdown
24
+ # ADR-001: Use Pydantic V2 for validation
25
+
26
+ **Status:** Accepted
27
+ **Date:** 2024-01-15
28
+
29
+ ## Context
30
+
31
+ What problem prompted this decision?
32
+
33
+ ## Decision
34
+
35
+ What was decided?
36
+
37
+ ## Consequences
38
+
39
+ ### Positive
40
+ - Benefit 1
41
+
42
+ ### Negative
43
+ - Tradeoff 1
44
+
45
+ ### Neutral
46
+ - Side effect 1
47
+ ```