dwyeapi 0.9.4__tar.gz → 0.9.5__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.
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/PKG-INFO +1 -1
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/pyproject.toml +1 -1
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/__init__.py +1 -1
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/providers/email/base.py +8 -2
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/providers/email/factory.py +3 -0
- dwyeapi-0.9.5/src/dwyeapi/tasks/schema.py +42 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi.egg-info/PKG-INFO +1 -1
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/tests/test_tasks.py +24 -0
- dwyeapi-0.9.4/src/dwyeapi/tasks/schema.py +0 -31
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/README.md +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/setup.cfg +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/cache.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/config.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/database.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/dependencies.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/dt.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/exceptions.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/health.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/logger.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/masking.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/pagination.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/providers/__init__.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/providers/email/__init__.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/providers/email/config.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/providers/email/protocol.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/providers/email/resend.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/response.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/security.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/tasks/__init__.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/tasks/context.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/tasks/model.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/tasks/pool.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/tasks/registry.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/tasks/router.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/tasks/service.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/tasks/worker.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi.egg-info/SOURCES.txt +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi.egg-info/dependency_links.txt +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi.egg-info/requires.txt +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi.egg-info/top_level.txt +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/tests/test_cache.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/tests/test_config.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/tests/test_database.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/tests/test_dependencies.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/tests/test_dt.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/tests/test_exceptions.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/tests/test_health.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/tests/test_logger.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/tests/test_masking.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/tests/test_pagination.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/tests/test_response.py +0 -0
- {dwyeapi-0.9.4 → dwyeapi-0.9.5}/tests/test_security.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dwyeapi
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.5
|
|
4
4
|
Summary: Lightweight FastAPI infrastructure — exceptions, database, config, security, cache, pagination, logger
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Repository, https://github.com/danweiyuancircle/dwy-ai-experience
|
|
@@ -63,11 +63,17 @@ class EmailProviderBase(ABC):
|
|
|
63
63
|
return "".join(secrets.choice("0123456789") for _ in range(self._length))
|
|
64
64
|
|
|
65
65
|
async def send_code(self, target: str) -> bool:
|
|
66
|
-
"""
|
|
66
|
+
"""生成验证码:先发送成功再写入 Redis。
|
|
67
|
+
|
|
68
|
+
顺序约束:``_send`` 失败时不落库,避免用户未收到邮件却有可校验码。
|
|
69
|
+
发送成功后写 Redis;若写库失败则返回 False(邮件已发出属极端边界,业务可重发覆盖)。
|
|
70
|
+
"""
|
|
67
71
|
code = self._generate_code()
|
|
72
|
+
if not await self._send(target, code):
|
|
73
|
+
return False
|
|
68
74
|
redis = await self._get_redis()
|
|
69
75
|
await redis.set(f"{CODE_KEY_PREFIX}{target}", code, ex=self._ttl)
|
|
70
|
-
return
|
|
76
|
+
return True
|
|
71
77
|
|
|
72
78
|
async def verify_code(self, target: str, code: str) -> bool:
|
|
73
79
|
"""从 Redis 读取存储的验证码比对,成功则删除 key(一次性)。"""
|
|
@@ -64,6 +64,9 @@ def make_email_provider(settings: EmailSettings) -> EmailProvider:
|
|
|
64
64
|
|
|
65
65
|
if not settings.resend.api_key:
|
|
66
66
|
raise ValueError("EMAIL__RESEND__API_KEY 未配置")
|
|
67
|
+
# from_email 启用时必填:缺省会导致 resend 运行时发信失败,尽早在工厂拦截
|
|
68
|
+
if not settings.resend.from_email:
|
|
69
|
+
raise ValueError("EMAIL__RESEND__FROM_EMAIL 未配置")
|
|
67
70
|
return ResendEmailProvider(
|
|
68
71
|
api_key=settings.resend.api_key,
|
|
69
72
|
from_email=settings.resend.from_email,
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""任务 API 请求/响应的 Pydantic schema。"""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
7
|
+
|
|
8
|
+
from dwyeapi.tasks.model import TaskStatus
|
|
9
|
+
|
|
10
|
+
# 与 Task ORM 列 String(50) 对齐;params 键数/体积限制防超大 body
|
|
11
|
+
_TASK_TYPE_MAX_LEN = 50
|
|
12
|
+
_PARAMS_MAX_KEYS = 50
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TaskCreate(BaseModel):
|
|
16
|
+
"""提交新任务的请求体。
|
|
17
|
+
|
|
18
|
+
约束与 ORM / 输入限长规则对齐:``task_type`` 最长 50;``params`` 最多 50 个键。
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
task_type: str = Field(min_length=1, max_length=_TASK_TYPE_MAX_LEN, description="已注册的任务类型名")
|
|
22
|
+
params: dict[str, Any] = Field(
|
|
23
|
+
default_factory=dict,
|
|
24
|
+
description="任务参数;键数量上限 50,避免超大 JSON 入库",
|
|
25
|
+
max_length=_PARAMS_MAX_KEYS,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class TaskResponse(BaseModel):
|
|
30
|
+
"""任务详情响应体,覆盖状态、进度、日志与结果等字段。"""
|
|
31
|
+
|
|
32
|
+
id: str
|
|
33
|
+
task_type: str
|
|
34
|
+
status: TaskStatus
|
|
35
|
+
params: dict[str, Any]
|
|
36
|
+
result: dict[str, Any] | None
|
|
37
|
+
progress: int
|
|
38
|
+
logs: str
|
|
39
|
+
created_at: datetime
|
|
40
|
+
updated_at: datetime
|
|
41
|
+
|
|
42
|
+
model_config = ConfigDict(from_attributes=True)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dwyeapi
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.5
|
|
4
4
|
Summary: Lightweight FastAPI infrastructure — exceptions, database, config, security, cache, pagination, logger
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Repository, https://github.com/danweiyuancircle/dwy-ai-experience
|
|
@@ -127,6 +127,30 @@ class TestSchemas:
|
|
|
127
127
|
assert tc.task_type == "export"
|
|
128
128
|
assert tc.params == {"format": "csv"}
|
|
129
129
|
|
|
130
|
+
def test_task_create_rejects_empty_task_type(self):
|
|
131
|
+
"""task_type 最短 1 字符。"""
|
|
132
|
+
import pytest
|
|
133
|
+
from pydantic import ValidationError
|
|
134
|
+
|
|
135
|
+
with pytest.raises(ValidationError):
|
|
136
|
+
TaskCreate(task_type="", params={})
|
|
137
|
+
|
|
138
|
+
def test_task_create_rejects_long_task_type(self):
|
|
139
|
+
"""task_type 最长 50,对齐 ORM String(50)。"""
|
|
140
|
+
import pytest
|
|
141
|
+
from pydantic import ValidationError
|
|
142
|
+
|
|
143
|
+
with pytest.raises(ValidationError):
|
|
144
|
+
TaskCreate(task_type="x" * 51, params={})
|
|
145
|
+
|
|
146
|
+
def test_task_create_rejects_too_many_params_keys(self):
|
|
147
|
+
"""params 键数上限 50。"""
|
|
148
|
+
import pytest
|
|
149
|
+
from pydantic import ValidationError
|
|
150
|
+
|
|
151
|
+
with pytest.raises(ValidationError):
|
|
152
|
+
TaskCreate(task_type="export", params={f"k{i}": i for i in range(51)})
|
|
153
|
+
|
|
130
154
|
async def test_task_response_from_attributes(self, session):
|
|
131
155
|
"""TaskResponse should be constructible from a persisted Task ORM object."""
|
|
132
156
|
task = Task(
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
"""任务 API 请求/响应的 Pydantic schema。"""
|
|
2
|
-
|
|
3
|
-
from datetime import datetime
|
|
4
|
-
from typing import Any
|
|
5
|
-
|
|
6
|
-
from pydantic import BaseModel, ConfigDict
|
|
7
|
-
|
|
8
|
-
from dwyeapi.tasks.model import TaskStatus
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class TaskCreate(BaseModel):
|
|
12
|
-
"""提交新任务的请求体。"""
|
|
13
|
-
|
|
14
|
-
task_type: str
|
|
15
|
-
params: dict[str, Any]
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class TaskResponse(BaseModel):
|
|
19
|
-
"""任务详情响应体,覆盖状态、进度、日志与结果等字段。"""
|
|
20
|
-
|
|
21
|
-
id: str
|
|
22
|
-
task_type: str
|
|
23
|
-
status: TaskStatus
|
|
24
|
-
params: dict[str, Any]
|
|
25
|
-
result: dict[str, Any] | None
|
|
26
|
-
progress: int
|
|
27
|
-
logs: str
|
|
28
|
-
created_at: datetime
|
|
29
|
-
updated_at: datetime
|
|
30
|
-
|
|
31
|
-
model_config = ConfigDict(from_attributes=True)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|