aury-boot 0.0.4__py3-none-any.whl → 0.0.7__py3-none-any.whl

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 (122) hide show
  1. aury/boot/__init__.py +2 -2
  2. aury/boot/_version.py +2 -2
  3. aury/boot/application/__init__.py +60 -36
  4. aury/boot/application/adapter/__init__.py +112 -0
  5. aury/boot/application/adapter/base.py +511 -0
  6. aury/boot/application/adapter/config.py +242 -0
  7. aury/boot/application/adapter/decorators.py +259 -0
  8. aury/boot/application/adapter/exceptions.py +202 -0
  9. aury/boot/application/adapter/http.py +325 -0
  10. aury/boot/application/app/__init__.py +12 -8
  11. aury/boot/application/app/base.py +12 -0
  12. aury/boot/application/app/components.py +137 -44
  13. aury/boot/application/app/middlewares.py +9 -4
  14. aury/boot/application/app/startup.py +249 -0
  15. aury/boot/application/config/__init__.py +36 -1
  16. aury/boot/application/config/multi_instance.py +216 -0
  17. aury/boot/application/config/settings.py +398 -149
  18. aury/boot/application/constants/components.py +6 -0
  19. aury/boot/application/errors/handlers.py +17 -3
  20. aury/boot/application/middleware/logging.py +21 -120
  21. aury/boot/application/rpc/__init__.py +2 -2
  22. aury/boot/commands/__init__.py +30 -10
  23. aury/boot/commands/app.py +131 -1
  24. aury/boot/commands/docs.py +104 -17
  25. aury/boot/commands/generate.py +22 -22
  26. aury/boot/commands/init.py +68 -17
  27. aury/boot/commands/server/app.py +2 -3
  28. aury/boot/commands/templates/project/AGENTS.md.tpl +221 -0
  29. aury/boot/commands/templates/project/README.md.tpl +2 -2
  30. aury/boot/commands/templates/project/aury_docs/00-overview.md.tpl +59 -0
  31. aury/boot/commands/templates/project/aury_docs/01-model.md.tpl +184 -0
  32. aury/boot/commands/templates/project/aury_docs/02-repository.md.tpl +206 -0
  33. aury/boot/commands/templates/project/aury_docs/03-service.md.tpl +398 -0
  34. aury/boot/commands/templates/project/aury_docs/04-schema.md.tpl +95 -0
  35. aury/boot/commands/templates/project/aury_docs/05-api.md.tpl +116 -0
  36. aury/boot/commands/templates/project/aury_docs/06-exception.md.tpl +118 -0
  37. aury/boot/commands/templates/project/aury_docs/07-cache.md.tpl +122 -0
  38. aury/boot/commands/templates/project/aury_docs/08-scheduler.md.tpl +32 -0
  39. aury/boot/commands/templates/project/aury_docs/09-tasks.md.tpl +38 -0
  40. aury/boot/commands/templates/project/aury_docs/10-storage.md.tpl +115 -0
  41. aury/boot/commands/templates/project/aury_docs/11-logging.md.tpl +131 -0
  42. aury/boot/commands/templates/project/aury_docs/12-admin.md.tpl +56 -0
  43. aury/boot/commands/templates/project/aury_docs/13-channel.md.tpl +104 -0
  44. aury/boot/commands/templates/project/aury_docs/14-mq.md.tpl +102 -0
  45. aury/boot/commands/templates/project/aury_docs/15-events.md.tpl +147 -0
  46. aury/boot/commands/templates/project/aury_docs/16-adapter.md.tpl +403 -0
  47. aury/boot/commands/templates/project/{CLI.md.tpl → aury_docs/99-cli.md.tpl} +19 -19
  48. aury/boot/commands/templates/project/config.py.tpl +10 -10
  49. aury/boot/commands/templates/project/env_templates/_header.tpl +10 -0
  50. aury/boot/commands/templates/project/env_templates/admin.tpl +49 -0
  51. aury/boot/commands/templates/project/env_templates/cache.tpl +14 -0
  52. aury/boot/commands/templates/project/env_templates/database.tpl +22 -0
  53. aury/boot/commands/templates/project/env_templates/log.tpl +18 -0
  54. aury/boot/commands/templates/project/env_templates/messaging.tpl +46 -0
  55. aury/boot/commands/templates/project/env_templates/rpc.tpl +28 -0
  56. aury/boot/commands/templates/project/env_templates/scheduler.tpl +18 -0
  57. aury/boot/commands/templates/project/env_templates/service.tpl +18 -0
  58. aury/boot/commands/templates/project/env_templates/storage.tpl +38 -0
  59. aury/boot/commands/templates/project/env_templates/third_party.tpl +43 -0
  60. aury/boot/commands/templates/project/modules/tasks.py.tpl +1 -1
  61. aury/boot/common/logging/__init__.py +26 -674
  62. aury/boot/common/logging/context.py +132 -0
  63. aury/boot/common/logging/decorators.py +118 -0
  64. aury/boot/common/logging/format.py +315 -0
  65. aury/boot/common/logging/setup.py +214 -0
  66. aury/boot/contrib/admin_console/auth.py +2 -3
  67. aury/boot/contrib/admin_console/install.py +1 -1
  68. aury/boot/domain/models/mixins.py +48 -1
  69. aury/boot/domain/pagination/__init__.py +94 -0
  70. aury/boot/domain/repository/impl.py +1 -1
  71. aury/boot/domain/repository/interface.py +1 -1
  72. aury/boot/domain/transaction/__init__.py +8 -9
  73. aury/boot/infrastructure/__init__.py +86 -29
  74. aury/boot/infrastructure/cache/backends.py +102 -18
  75. aury/boot/infrastructure/cache/base.py +12 -0
  76. aury/boot/infrastructure/cache/manager.py +153 -91
  77. aury/boot/infrastructure/channel/__init__.py +24 -0
  78. aury/boot/infrastructure/channel/backends/__init__.py +9 -0
  79. aury/boot/infrastructure/channel/backends/memory.py +83 -0
  80. aury/boot/infrastructure/channel/backends/redis.py +88 -0
  81. aury/boot/infrastructure/channel/base.py +92 -0
  82. aury/boot/infrastructure/channel/manager.py +203 -0
  83. aury/boot/infrastructure/clients/__init__.py +22 -0
  84. aury/boot/infrastructure/clients/rabbitmq/__init__.py +9 -0
  85. aury/boot/infrastructure/clients/rabbitmq/config.py +46 -0
  86. aury/boot/infrastructure/clients/rabbitmq/manager.py +288 -0
  87. aury/boot/infrastructure/clients/redis/__init__.py +28 -0
  88. aury/boot/infrastructure/clients/redis/config.py +51 -0
  89. aury/boot/infrastructure/clients/redis/manager.py +264 -0
  90. aury/boot/infrastructure/database/config.py +7 -16
  91. aury/boot/infrastructure/database/manager.py +16 -38
  92. aury/boot/infrastructure/events/__init__.py +18 -21
  93. aury/boot/infrastructure/events/backends/__init__.py +11 -0
  94. aury/boot/infrastructure/events/backends/memory.py +86 -0
  95. aury/boot/infrastructure/events/backends/rabbitmq.py +193 -0
  96. aury/boot/infrastructure/events/backends/redis.py +162 -0
  97. aury/boot/infrastructure/events/base.py +127 -0
  98. aury/boot/infrastructure/events/manager.py +224 -0
  99. aury/boot/infrastructure/mq/__init__.py +24 -0
  100. aury/boot/infrastructure/mq/backends/__init__.py +9 -0
  101. aury/boot/infrastructure/mq/backends/rabbitmq.py +179 -0
  102. aury/boot/infrastructure/mq/backends/redis.py +167 -0
  103. aury/boot/infrastructure/mq/base.py +143 -0
  104. aury/boot/infrastructure/mq/manager.py +239 -0
  105. aury/boot/infrastructure/scheduler/manager.py +7 -3
  106. aury/boot/infrastructure/storage/__init__.py +9 -9
  107. aury/boot/infrastructure/storage/base.py +17 -5
  108. aury/boot/infrastructure/storage/factory.py +0 -1
  109. aury/boot/infrastructure/tasks/__init__.py +2 -2
  110. aury/boot/infrastructure/tasks/config.py +5 -13
  111. aury/boot/infrastructure/tasks/manager.py +55 -33
  112. {aury_boot-0.0.4.dist-info → aury_boot-0.0.7.dist-info}/METADATA +20 -2
  113. aury_boot-0.0.7.dist-info/RECORD +197 -0
  114. aury/boot/commands/templates/project/DEVELOPMENT.md.tpl +0 -1397
  115. aury/boot/commands/templates/project/env.example.tpl +0 -213
  116. aury/boot/infrastructure/events/bus.py +0 -362
  117. aury/boot/infrastructure/events/config.py +0 -52
  118. aury/boot/infrastructure/events/consumer.py +0 -134
  119. aury/boot/infrastructure/events/models.py +0 -63
  120. aury_boot-0.0.4.dist-info/RECORD +0 -137
  121. {aury_boot-0.0.4.dist-info → aury_boot-0.0.7.dist-info}/WHEEL +0 -0
  122. {aury_boot-0.0.4.dist-info → aury_boot-0.0.7.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,118 @@
1
+ # 异常处理
2
+
3
+ 框架提供了统一的异常处理机制,所有异常都会被全局异常处理中间件捕获并转换为标准的 HTTP 响应。
4
+
5
+ ## 6.1 异常处理流程
6
+
7
+ ```
8
+ 请求 → API 路由 → Service → Repository → 抛出异常
9
+
10
+ 全局异常处理中间件(Middleware)
11
+
12
+ 转换为 ErrorResponse Schema
13
+
14
+ JSON 响应返回客户端
15
+ ```
16
+
17
+ **流程说明**:
18
+ 1. 业务代码抛出异常(如 `NotFoundError`)
19
+ 2. 框架的全局异常处理中间件自动捕获
20
+ 3. 根据异常类型转换为对应的 HTTP 状态码和错误响应
21
+ 4. 返回统一格式的 JSON 错误响应
22
+
23
+ **响应格式**:
24
+ ```json
25
+ {{
26
+ "code": "NOT_FOUND",
27
+ "message": "用户不存在",
28
+ "data": null
29
+ }}
30
+ ```
31
+
32
+ ## 6.2 内置异常
33
+
34
+ ```python
35
+ from aury.boot.application.errors import (
36
+ BaseError,
37
+ NotFoundError, # 404
38
+ AlreadyExistsError, # 409
39
+ ValidationError, # 422
40
+ UnauthorizedError, # 401
41
+ ForbiddenError, # 403
42
+ BusinessError, # 400
43
+ )
44
+
45
+ # 使用示例
46
+ raise NotFoundError("用户不存在", resource=user_id)
47
+ raise AlreadyExistsError(f"邮箱 {{email}} 已被注册")
48
+ raise UnauthorizedError("未登录或登录已过期")
49
+ ```
50
+
51
+ ## 6.3 自定义异常
52
+
53
+ **文件**: `{package_name}/exceptions/order.py`
54
+
55
+ ```python
56
+ from enum import Enum
57
+ from fastapi import status
58
+ from aury.boot.application.errors import BaseError
59
+
60
+
61
+ # 推荐:定义错误码枚举
62
+ class OrderErrorCode(str, Enum):
63
+ ORDER_ERROR = "5000"
64
+ ORDER_NOT_FOUND = "5001"
65
+ INSUFFICIENT_STOCK = "5002"
66
+ PAYMENT_FAILED = "5003"
67
+
68
+
69
+ class OrderError(BaseError):
70
+ default_message = "订单错误"
71
+ default_code = OrderErrorCode.ORDER_ERROR
72
+ default_status_code = status.HTTP_400_BAD_REQUEST
73
+
74
+
75
+ class OrderNotFoundError(OrderError):
76
+ default_message = "订单不存在"
77
+ default_code = OrderErrorCode.ORDER_NOT_FOUND
78
+ default_status_code = status.HTTP_404_NOT_FOUND
79
+
80
+
81
+ class InsufficientStockError(OrderError):
82
+ default_message = "库存不足"
83
+ default_code = OrderErrorCode.INSUFFICIENT_STOCK
84
+ default_status_code = status.HTTP_400_BAD_REQUEST
85
+
86
+
87
+ # 使用
88
+ raise OrderNotFoundError() # 使用默认值
89
+ raise OrderError(message="订单ID无效") # 自定义消息
90
+ raise InsufficientStockError(message=f"商品 {{product_id}} 库存不足")
91
+ ```
92
+
93
+ **Error Code 规范**:
94
+ - **推荐使用枚举**定义错误码,枚举值必须为数字字符串(如 `"5001"`)
95
+ - 框架预留编码范围:1xxx 通用、2xxx 数据库、3xxx 业务、4xxx 外部服务、**5xxx+ 用户自定义**
96
+
97
+ ## 6.4 异常与 Schema 的关系
98
+
99
+ 异常处理中间件会自动将异常转换为 Schema 响应:
100
+
101
+ ```python
102
+ # Service 层抛出异常
103
+ raise NotFoundError("用户不存在")
104
+
105
+ # 中间件捕获并转换为响应
106
+ # HTTP 状态码:404
107
+ # 响应体:
108
+ # {{
109
+ # "code": "NOT_FOUND",
110
+ # "message": "用户不存在",
111
+ # "data": null
112
+ # }}
113
+ ```
114
+
115
+ **最佳实践**:
116
+ - 在 Service 层抛出业务异常,不要在 API 层手动处理
117
+ - 使用框架内置异常或自定义异常,不要直接抛出 `Exception`
118
+ - 自定义异常继承 `BaseError`,框架会自动处理
@@ -0,0 +1,122 @@
1
+ # 缓存
2
+
3
+ 框架的 `CacheManager` 支持**命名多实例**,可以为不同用途配置不同的缓存后端。
4
+
5
+ ## 7.1 基本用法
6
+
7
+ ```python
8
+ from aury.boot.infrastructure.cache import CacheManager
9
+
10
+ # 默认实例
11
+ cache = CacheManager.get_instance()
12
+ await cache.set("key", value, expire=300) # 5 分钟
13
+ value = await cache.get("key")
14
+ await cache.delete("key")
15
+ ```
16
+
17
+ ## 7.2 多实例使用
18
+
19
+ ```python
20
+ # 获取不同用途的缓存实例
21
+ session_cache = CacheManager.get_instance("session")
22
+ rate_limit_cache = CacheManager.get_instance("rate_limit")
23
+
24
+ # 分别初始化(可以使用不同的后端或配置)
25
+ await session_cache.initialize(
26
+ backend="redis",
27
+ url="redis://localhost:6379/1", # 使用不同的 Redis DB
28
+ )
29
+ await rate_limit_cache.initialize(
30
+ backend="memory",
31
+ max_size=10000,
32
+ )
33
+ ```
34
+
35
+ ## 7.3 缓存装饰器
36
+
37
+ ```python
38
+ cache = CacheManager.get_instance()
39
+
40
+ @cache.cached(expire=300, key_prefix="user")
41
+ async def get_user(user_id: int):
42
+ # 缓存键自动生成:user:<func_name>:<args_hash>
43
+ return await repo.get(user_id)
44
+ ```
45
+
46
+ ## 7.4 支持的后端
47
+
48
+ - `redis` - Redis(推荐生产用)
49
+ - `memory` - 内存缓存(开发/测试用)
50
+ - `memcached` - Memcached
51
+
52
+ ## 7.5 API 响应缓存装饰器
53
+
54
+ 使用 `@cache.cache_response()` 装饰器自动缓存 API 响应:
55
+
56
+ ```python
57
+ cache = CacheManager.get_instance()
58
+
59
+ # 基本用法:自动生成缓存键
60
+ @router.get("/{{id}}")
61
+ @cache.cache_response(expire=300)
62
+ async def get_todo(id: UUID, service: TodoService = Depends(get_service)):
63
+ entity = await service.get(id)
64
+ return BaseResponse(code=200, message="获取成功", data=TodoResponse.model_validate(entity))
65
+
66
+ # 自定义缓存键
67
+ @router.get("/{{id}}")
68
+ @cache.cache_response(
69
+ expire=300,
70
+ key_builder=lambda id, **kwargs: f"todo:{{id}}"
71
+ )
72
+ async def get_todo(id: UUID, service: TodoService = Depends(get_service)):
73
+ entity = await service.get(id)
74
+ return BaseResponse(code=200, message="获取成功", data=TodoResponse.model_validate(entity))
75
+ ```
76
+
77
+ ## 7.6 模式删除(delete_pattern)
78
+
79
+ 使用通配符批量删除缓存:
80
+
81
+ ```python
82
+ # 删除所有 todo 相关缓存
83
+ await cache.delete_pattern("api:todo:*")
84
+
85
+ # 删除所有列表缓存
86
+ await cache.delete_pattern("api:todo:list:*")
87
+ ```
88
+
89
+ ## 7.7 缓存清理策略
90
+
91
+ 数据变更时应及时清理相关缓存:
92
+
93
+ ```python
94
+ # 更新时清理缓存
95
+ @router.put("/{{id}}")
96
+ async def update_todo(id: UUID, data: TodoUpdate, service: TodoService = Depends(get_service)):
97
+ entity = await service.update(id, data)
98
+
99
+ # 清理单个 + 模式删除
100
+ await cache.delete(f"api:todo:{{id}}")
101
+ await cache.delete_pattern("api:todo:list:*") # 清理所有列表缓存
102
+
103
+ return BaseResponse(code=200, message="更新成功", data=TodoResponse.model_validate(entity))
104
+
105
+ # 删除时清理缓存
106
+ @router.delete("/{{id}}")
107
+ async def delete_todo(id: UUID, service: TodoService = Depends(get_service)):
108
+ await service.delete(id)
109
+ await cache.delete(f"api:todo:{{id}}")
110
+ await cache.delete_pattern("api:todo:*") # 清理所有相关缓存
111
+ return BaseResponse(code=200, message="删除成功", data=None)
112
+ ```
113
+
114
+ ### 7.7.1 缓存键命名规范
115
+
116
+ ```python
117
+ # 推荐格式:{{layer}}:{{resource}}:{{identifier}}
118
+ f"api:todo:{{id}}" # 单个资源
119
+ f"api:todo:list:{{page}}" # 列表
120
+ f"api:todo:statistics" # 统计
121
+ f"service:user:{{user_id}}" # Service 层缓存
122
+ ```
@@ -0,0 +1,32 @@
1
+ # 定时任务(Scheduler)
2
+
3
+ **文件**: `{package_name}/schedules/__init__.py`
4
+
5
+ ```python
6
+ """定时任务模块。"""
7
+
8
+ from aury.boot.common.logging import logger
9
+ from aury.boot.infrastructure.scheduler import SchedulerManager
10
+
11
+ scheduler = SchedulerManager.get_instance()
12
+
13
+
14
+ @scheduler.scheduled_job("interval", seconds=60)
15
+ async def every_minute():
16
+ """每 60 秒执行。"""
17
+ logger.info("定时任务执行中...")
18
+
19
+
20
+ @scheduler.scheduled_job("cron", hour=0, minute=0)
21
+ async def daily_task():
22
+ """每天凌晨执行。"""
23
+ logger.info("每日任务执行中...")
24
+
25
+
26
+ @scheduler.scheduled_job("cron", day_of_week="mon", hour=9)
27
+ async def weekly_report():
28
+ """每周一 9 点执行。"""
29
+ logger.info("周报任务执行中...")
30
+ ```
31
+
32
+ 启用方式:配置 `SCHEDULER_ENABLED=true`,框架自动加载 `{package_name}/schedules/` 模块。
@@ -0,0 +1,38 @@
1
+ # 异步任务(Dramatiq)
2
+
3
+ **文件**: `{package_name}/tasks/__init__.py`
4
+
5
+ ```python
6
+ """异步任务模块。"""
7
+
8
+ from aury.boot.common.logging import logger
9
+ from aury.boot.infrastructure.tasks import conditional_task
10
+
11
+
12
+ @conditional_task()
13
+ def send_email(to: str, subject: str, body: str):
14
+ """异步发送邮件。"""
15
+ logger.info(f"发送邮件到 {{to}}: {{subject}}")
16
+ # 实际发送逻辑...
17
+ return {{"status": "sent"}}
18
+
19
+
20
+ @conditional_task()
21
+ def process_order(order_id: str):
22
+ """异步处理订单。"""
23
+ logger.info(f"处理订单: {{order_id}}")
24
+ ```
25
+
26
+ 调用方式:
27
+
28
+ ```python
29
+ # 异步执行(发送到队列)
30
+ send_email.send("user@example.com", "Hello", "World")
31
+
32
+ # 延迟执行
33
+ send_email.send_with_options(args=("user@example.com", "Hello", "World"), delay=60000) # 60秒后
34
+ ```
35
+
36
+ 启用方式:
37
+ 1. 配置 `TASK_BROKER_URL`(如 `redis://localhost:6379/0`)
38
+ 2. 运行 Worker:`aury worker`
@@ -0,0 +1,115 @@
1
+ # 对象存储(基于 aury-sdk-storage)
2
+
3
+ ## 10.1 安装
4
+
5
+ ```bash
6
+ # 完整安装(S3/COS/OSS + STS 支持)
7
+ uv add "aury-sdk-storage[aws]"
8
+ # 或
9
+ pip install "aury-sdk-storage[aws]"
10
+ ```
11
+
12
+ ## 10.2 基本用法(StorageManager)
13
+
14
+ `StorageManager` 支持**命名多实例**,可以同时管理多个存储后端。
15
+
16
+ ```python
17
+ from aury.boot.infrastructure.storage import (
18
+ StorageManager, StorageConfig, StorageBackend, StorageFile,
19
+ )
20
+
21
+ # 默认实例
22
+ storage = StorageManager.get_instance()
23
+ await storage.initialize(StorageConfig(
24
+ backend=StorageBackend.COS,
25
+ bucket_name="my-bucket-1250000000",
26
+ region="ap-guangzhou",
27
+ endpoint="https://cos.ap-guangzhou.myqcloud.com",
28
+ access_key_id="AKIDxxxxx",
29
+ access_key_secret="xxxxx",
30
+ ))
31
+
32
+ # 多实例示例:源存储和目标存储
33
+ source = StorageManager.get_instance("source")
34
+ target = StorageManager.get_instance("target")
35
+ await source.initialize(StorageConfig(backend=StorageBackend.COS, ...))
36
+ await target.initialize(StorageConfig(backend=StorageBackend.S3, ...))
37
+
38
+ # 上传文件(返回 URL)
39
+ # data 支持: bytes / BytesIO / BinaryIO
40
+ url = await storage.upload_file(
41
+ StorageFile(
42
+ object_name="user/123/avatar.png",
43
+ data=image_bytes,
44
+ content_type="image/png",
45
+ )
46
+ )
47
+
48
+ # 下载文件
49
+ content = await storage.download_file("user/123/avatar.png")
50
+
51
+ # 获取预签名 URL
52
+ url = await storage.get_file_url("user/123/avatar.png", expires_in=3600)
53
+
54
+ # 检查文件是否存在
55
+ exists = await storage.file_exists("user/123/avatar.png")
56
+
57
+ # 删除文件
58
+ await storage.delete_file("user/123/avatar.png")
59
+ ```
60
+
61
+ ## 10.3 STS 临时凭证(前端直传)
62
+
63
+ ```python
64
+ from aury.sdk.storage.sts import (
65
+ STSProviderFactory, ProviderType, STSRequest, ActionType,
66
+ )
67
+
68
+ # 创建腾讯云 STS Provider
69
+ provider = STSProviderFactory.create(
70
+ ProviderType.TENCENT,
71
+ secret_id="AKIDxxxxx",
72
+ secret_key="xxxxx",
73
+ )
74
+
75
+ # 签发临时上传凭证
76
+ credentials = await provider.get_credentials(
77
+ STSRequest(
78
+ bucket="my-bucket-1250000000",
79
+ region="ap-guangzhou",
80
+ allow_path="user/123/",
81
+ action_type=ActionType.WRITE,
82
+ duration_seconds=900,
83
+ )
84
+ )
85
+
86
+ # 返回给前端
87
+ return {{
88
+ "accessKeyId": credentials.access_key_id,
89
+ "secretAccessKey": credentials.secret_access_key,
90
+ "sessionToken": credentials.session_token,
91
+ "expiration": credentials.expiration.isoformat(),
92
+ "bucket": credentials.bucket,
93
+ "region": credentials.region,
94
+ "endpoint": credentials.endpoint,
95
+ }}
96
+ ```
97
+
98
+ ## 10.4 本地存储(开发测试)
99
+
100
+ ```python
101
+ from aury.boot.infrastructure.storage import (
102
+ StorageManager, StorageConfig, StorageBackend, StorageFile,
103
+ )
104
+
105
+ storage = StorageManager.get_instance()
106
+ await storage.initialize(StorageConfig(
107
+ backend=StorageBackend.LOCAL,
108
+ base_path="./dev_storage",
109
+ ))
110
+
111
+ url = await storage.upload_file(
112
+ StorageFile(object_name="test.txt", data=b"hello")
113
+ )
114
+ # url: file:///path/to/dev_storage/default/test.txt
115
+ ```
@@ -0,0 +1,131 @@
1
+ # 日志
2
+
3
+ 基于 loguru 的日志系统,trace_id 自动注入每条日志,无需手动记录。
4
+
5
+ ## 11.1 基本用法
6
+
7
+ ```python
8
+ from aury.boot.common.logging import logger
9
+
10
+ # trace_id 自动包含在日志格式中,无需手动记录
11
+ logger.info("操作成功")
12
+ logger.warning("警告信息")
13
+ logger.error("错误信息")
14
+ logger.exception("异常信息") # 自动记录堆栈
15
+ ```
16
+
17
+ 输出示例:
18
+ ```
19
+ 2024-01-15 12:00:00 | INFO | app.service:create:42 | abc123 - 操作成功
20
+ ```
21
+
22
+ ## 11.2 注入用户信息
23
+
24
+ 框架不内置用户系统,但支持注入自定义请求上下文:
25
+
26
+ ```python
27
+ # app/auth/context.py
28
+ from contextvars import ContextVar
29
+ from aury.boot.common.logging import register_request_context
30
+
31
+ _user_id: ContextVar[str] = ContextVar("user_id", default="")
32
+
33
+ def set_user_id(uid: str) -> None:
34
+ _user_id.set(uid)
35
+
36
+ # 启动时注册(只需一次)
37
+ register_request_context("user_id", _user_id.get)
38
+ ```
39
+
40
+ 在认证中间件中设置(order < 100 以在日志中间件前执行):
41
+
42
+ ```python
43
+ class AuthMiddleware(Middleware):
44
+ order = 50 # 在日志中间件(order=100)之前执行
45
+
46
+ async def dispatch(self, request, call_next):
47
+ user = await verify_token(request)
48
+ if user:
49
+ set_user_id(str(user.id))
50
+ return await call_next(request)
51
+ ```
52
+
53
+ 结果:
54
+ ```
55
+ ← GET /api/users | 状态: 200 | 耗时: 0.05s | Trace-ID: abc123
56
+ [REQUEST_CONTEXT] Trace-ID: abc123 | user_id: 123
57
+ ```
58
+
59
+ ## 11.3 性能监控装饰器
60
+
61
+ ```python
62
+ from aury.boot.common.logging import log_performance, log_exceptions
63
+
64
+ @log_performance(threshold=0.5) # 超过 0.5 秒记录警告
65
+ async def slow_operation():
66
+ ...
67
+
68
+ @log_exceptions # 自动记录异常
69
+ async def risky_operation():
70
+ ...
71
+ ```
72
+
73
+ ## 11.4 HTTP 请求日志
74
+
75
+ 框架内置 `RequestLoggingMiddleware` 自动记录:
76
+
77
+ ```
78
+ # 请求日志(包含查询参数和请求体)
79
+ → POST /api/users | 参数: {{'page': '1'}} | Body: {{"name": "test"}} | Trace-ID: abc123
80
+
81
+ # 响应日志(包含状态码和耗时)
82
+ ← POST /api/users | 状态: 201 | 耗时: 0.123s | Trace-ID: abc123
83
+
84
+ # 慢请求警告(超过 1 秒)
85
+ 慢请求: GET /api/reports | 耗时: 2.345s (超过1秒) | Trace-ID: abc123
86
+ ```
87
+
88
+ ## 11.5 自定义日志文件
89
+
90
+ 为特定业务创建独立的日志文件:
91
+
92
+ ```python
93
+ from aury.boot.common.logging import register_log_sink, logger
94
+
95
+ # 启动时注册(生成 payment_2024-01-15.log)
96
+ register_log_sink("payment", filter_key="payment")
97
+
98
+ # 业务代码中使用
99
+ logger.bind(payment=True).info(f"支付成功 | 订单: {{order_id}}")
100
+ ```
101
+
102
+ ## 11.6 异步任务链路追踪
103
+
104
+ 跨进程任务需要手动传递 trace_id:
105
+
106
+ ```python
107
+ from aury.boot.common.logging import get_trace_id, set_trace_id
108
+
109
+ # 发送任务时传递
110
+ process_order.send(order_id="123", trace_id=get_trace_id())
111
+
112
+ # 任务执行时恢复
113
+ @tm.conditional_task()
114
+ async def process_order(order_id: str, trace_id: str | None = None):
115
+ if trace_id:
116
+ set_trace_id(trace_id)
117
+ logger.info(f"处理订单: {{order_id}}") # 自动包含 trace_id
118
+ ```
119
+
120
+ ## 11.7 服务上下文隔离
121
+
122
+ 日志自动按服务类型分离:
123
+
124
+ ```
125
+ logs/
126
+ ├── api_info_2024-01-15.log # API 服务日志
127
+ ├── api_error_2024-01-15.log
128
+ ├── scheduler_info_2024-01-15.log # 调度器日志
129
+ ├── worker_info_2024-01-15.log # Worker 日志
130
+ └── access_2024-01-15.log # HTTP 访问日志
131
+ ```
@@ -0,0 +1,56 @@
1
+ # 管理后台(Admin Console,基于 SQLAdmin)
2
+
3
+ 默认提供可选的 SQLAdmin 后台(组件自动装配)。启用后路径默认为 `/api/admin-console`。
4
+
5
+ - 组件开关与配置由环境变量控制;启用后框架会在启动时自动挂载后台路由。
6
+ - SQLAdmin 通常需要同步 SQLAlchemy Engine;如果你使用的是异步 `DATABASE__URL`,建议单独设置同步的 `ADMIN__DATABASE_URL`(框架也会尝试自动推导常见驱动映射)。
7
+
8
+ ## 快速启用(.env)
9
+
10
+ ```bash
11
+ # 启用与基本路径
12
+ ADMIN__ENABLED=true
13
+ ADMIN__PATH=/api/admin-console
14
+
15
+ # 认证(二选一,推荐 basic 或 bearer)
16
+ ADMIN__AUTH_MODE=basic
17
+ ADMIN__AUTH_SECRET_KEY=CHANGE_ME_TO_A_RANDOM_SECRET
18
+ ADMIN__AUTH_BASIC_USERNAME=admin
19
+ ADMIN__AUTH_BASIC_PASSWORD=change_me
20
+
21
+ # 如果使用 bearer
22
+ # ADMIN__AUTH_MODE=bearer
23
+ # ADMIN__AUTH_SECRET_KEY=CHANGE_ME
24
+ # ADMIN__AUTH_BEARER_TOKENS=["token1","token2"]
25
+
26
+ # 如需显式提供同步数据库 URL(可选)
27
+ # ADMIN__DATABASE_URL=postgresql+psycopg://user:pass@localhost:5432/{project_name}
28
+ ```
29
+
30
+ ## 注册后台视图
31
+
32
+ **文件**: `admin_console.py`
33
+
34
+ ```python
35
+ from sqladmin import ModelView
36
+ from {package_name}.models.user import User
37
+
38
+ class UserAdmin(ModelView, model=User):
39
+ column_list = [User.id, User.username, User.email]
40
+
41
+ # 方式一:声明式(简单)
42
+ ADMIN_VIEWS = [UserAdmin]
43
+
44
+ # 方式二:函数注册(更灵活)
45
+ # def register_admin(admin):
46
+ # admin.add_view(UserAdmin)
47
+ ```
48
+
49
+ ## 自定义认证(可选,高阶)
50
+
51
+ - 通过 `ADMIN__AUTH_BACKEND=module:attr` 指定自定义 backend;或在 `admin_console.py` 实现 `register_admin_auth(config)` 返回 SQLAdmin 的 `AuthenticationBackend`。
52
+ - 生产环境下必须设置 `ADMIN__AUTH_SECRET_KEY`,不允许 `none` 模式。
53
+
54
+ ## 访问
55
+
56
+ 启动服务后访问:`http://localhost:8000/api/admin-console`(或你配置的 `ADMIN__PATH`)。