rainycode 1.5.2__tar.gz → 1.5.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.
- {rainycode-1.5.2 → rainycode-1.5.5}/PKG-INFO +2 -1
- {rainycode-1.5.2 → rainycode-1.5.5}/common_base/aiorequests.py +13 -4
- {rainycode-1.5.2 → rainycode-1.5.5}/common_base/exception.py +8 -6
- {rainycode-1.5.2 → rainycode-1.5.5}/common_depends/auth_depend.py +7 -6
- {rainycode-1.5.2 → rainycode-1.5.5}/common_servers/service/auth_svc.py +4 -4
- {rainycode-1.5.2 → rainycode-1.5.5}/common_utils/snowflake_util.py +1 -1
- {rainycode-1.5.2 → rainycode-1.5.5}/common_utils/wechat_util.py +3 -3
- {rainycode-1.5.2 → rainycode-1.5.5}/core/base_config.py +1 -1
- {rainycode-1.5.2 → rainycode-1.5.5}/core/start.py +1 -1
- {rainycode-1.5.2 → rainycode-1.5.5}/pyproject.toml +2 -1
- {rainycode-1.5.2 → rainycode-1.5.5}/rainycode.egg-info/PKG-INFO +2 -1
- {rainycode-1.5.2 → rainycode-1.5.5}/rainycode.egg-info/requires.txt +1 -0
- {rainycode-1.5.2 → rainycode-1.5.5}/common_base/consts.py +0 -0
- {rainycode-1.5.2 → rainycode-1.5.5}/common_base/logging.py +0 -0
- {rainycode-1.5.2 → rainycode-1.5.5}/common_base/response.py +0 -0
- {rainycode-1.5.2 → rainycode-1.5.5}/common_models/__init__.py +0 -0
- {rainycode-1.5.2 → rainycode-1.5.5}/common_models/base_model.py +0 -0
- {rainycode-1.5.2 → rainycode-1.5.5}/common_models/file_model.py +0 -0
- {rainycode-1.5.2 → rainycode-1.5.5}/common_models/user_model.py +0 -0
- {rainycode-1.5.2 → rainycode-1.5.5}/common_models/wechat_model.py +0 -0
- {rainycode-1.5.2 → rainycode-1.5.5}/common_servers/api/auth_api.py +0 -0
- {rainycode-1.5.2 → rainycode-1.5.5}/common_servers/api/wechat_api.py +0 -0
- {rainycode-1.5.2 → rainycode-1.5.5}/common_servers/router.py +0 -0
- {rainycode-1.5.2 → rainycode-1.5.5}/common_servers/schemas/auth_schema.py +0 -0
- {rainycode-1.5.2 → rainycode-1.5.5}/common_servers/service/file_svc.py +0 -0
- {rainycode-1.5.2 → rainycode-1.5.5}/common_servers/service/wechat_svc.py +0 -0
- {rainycode-1.5.2 → rainycode-1.5.5}/common_utils/bcrypt_util.py +0 -0
- {rainycode-1.5.2 → rainycode-1.5.5}/common_utils/captcha_util.py +0 -0
- {rainycode-1.5.2 → rainycode-1.5.5}/common_utils/datetime_util.py +0 -0
- {rainycode-1.5.2 → rainycode-1.5.5}/common_utils/ip_util.py +0 -0
- {rainycode-1.5.2 → rainycode-1.5.5}/common_utils/jwt_util.py +0 -0
- {rainycode-1.5.2 → rainycode-1.5.5}/core/databases/aiodb.py +0 -0
- {rainycode-1.5.2 → rainycode-1.5.5}/core/databases/aioredis.py +0 -0
- {rainycode-1.5.2 → rainycode-1.5.5}/core/middleware/http_middleware.py +0 -0
- {rainycode-1.5.2 → rainycode-1.5.5}/rainycode.egg-info/SOURCES.txt +0 -0
- {rainycode-1.5.2 → rainycode-1.5.5}/rainycode.egg-info/dependency_links.txt +0 -0
- {rainycode-1.5.2 → rainycode-1.5.5}/rainycode.egg-info/top_level.txt +0 -0
- {rainycode-1.5.2 → rainycode-1.5.5}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rainycode
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.5
|
|
4
4
|
Summary: FastAPI base modules
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
Requires-Dist: aerich==0.9.2
|
|
@@ -17,6 +17,7 @@ Requires-Dist: pycryptodome==3.23.0
|
|
|
17
17
|
Requires-Dist: uvicorn[standard]==0.41.0
|
|
18
18
|
Requires-Dist: tortoise-orm[asyncmy]==0.25.4
|
|
19
19
|
Requires-Dist: pydantic-settings==2.13.1
|
|
20
|
+
Requires-Dist: python-multipart==0.0.32
|
|
20
21
|
Provides-Extra: release
|
|
21
22
|
Requires-Dist: build==1.4.0; extra == "release"
|
|
22
23
|
Requires-Dist: twine==6.1.0; extra == "release"
|
|
@@ -7,10 +7,19 @@ import certifi
|
|
|
7
7
|
# 模块级别 SSL 上下文,使用 certifi 证书,避免重复创建
|
|
8
8
|
_SSL_CONTEXT = ssl.create_default_context(cafile=certifi.where())
|
|
9
9
|
|
|
10
|
-
#
|
|
10
|
+
# 连接池:跨请求复用 keep-alive 长连接(而非每次新建 connector)。
|
|
11
11
|
# limit: 全池同时最大连接数(含活跃与 keep-alive 空闲复用)
|
|
12
12
|
# limit_per_host: 单个 host 端点同时最大连接数,限制对单一对端的并发/占用
|
|
13
|
-
|
|
13
|
+
# 惰性初始化:TCPConnector 构造需要运行中的事件循环,只能在首次请求(async 上下文)时创建,
|
|
14
|
+
# 不能在模块 import 时同步 create(否则抛 RuntimeError: no running event loop)。
|
|
15
|
+
_CONNECTOR: aiohttp.TCPConnector | None = None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _get_connector() -> aiohttp.TCPConnector:
|
|
19
|
+
global _CONNECTOR
|
|
20
|
+
if _CONNECTOR is None:
|
|
21
|
+
_CONNECTOR = aiohttp.TCPConnector(ssl=_SSL_CONTEXT, limit=300, limit_per_host=100)
|
|
22
|
+
return _CONNECTOR
|
|
14
23
|
|
|
15
24
|
|
|
16
25
|
def parse_url(url, params=None, suffix=None):
|
|
@@ -28,7 +37,7 @@ def parse_url(url, params=None, suffix=None):
|
|
|
28
37
|
async def get(url, params=None, headers=None, suffix=None):
|
|
29
38
|
url = parse_url(url, params, suffix)
|
|
30
39
|
async with (
|
|
31
|
-
aiohttp.ClientSession(connector=
|
|
40
|
+
aiohttp.ClientSession(connector=_get_connector(), trust_env=True) as session,
|
|
32
41
|
session.get(url, headers=headers) as response,
|
|
33
42
|
):
|
|
34
43
|
text = await response.text()
|
|
@@ -39,7 +48,7 @@ async def post(url, data=None, headers=None):
|
|
|
39
48
|
if isinstance(data, dict):
|
|
40
49
|
data = json.dumps(data)
|
|
41
50
|
async with (
|
|
42
|
-
aiohttp.ClientSession(connector=
|
|
51
|
+
aiohttp.ClientSession(connector=_get_connector(), trust_env=True) as session,
|
|
43
52
|
session.post(url, data=data, headers=headers) as response,
|
|
44
53
|
):
|
|
45
54
|
text = await response.text()
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
from typing import cast
|
|
2
2
|
|
|
3
|
-
from common_base.logging import logger
|
|
4
|
-
from common_base.response import ErrorReturn
|
|
5
3
|
from fastapi import HTTPException, Request, status
|
|
6
4
|
from fastapi.exceptions import RequestValidationError, ResponseValidationError
|
|
7
5
|
from starlette.responses import JSONResponse
|
|
8
6
|
|
|
7
|
+
from common_base.logging import logger
|
|
8
|
+
from common_base.response import ErrorReturn
|
|
9
|
+
|
|
9
10
|
|
|
10
11
|
class CustomException(Exception):
|
|
11
12
|
"""自定义异常"""
|
|
12
13
|
def __init__(
|
|
13
|
-
self,
|
|
14
|
+
self,
|
|
14
15
|
msg: str | None = None,
|
|
15
|
-
code: int =
|
|
16
|
+
code: int = -1,
|
|
16
17
|
data: object | None = None,
|
|
17
18
|
status_code: int = status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
18
19
|
) -> None:
|
|
@@ -41,10 +42,11 @@ async def RequestValidationHandle(request: Request, exc: RequestValidationError)
|
|
|
41
42
|
"""请求参数验证异常处理器"""
|
|
42
43
|
error_mapping = {
|
|
43
44
|
"Field required": "请求失败,缺少必填项!",
|
|
44
|
-
"value is not a valid list": "类型错误,提交参数应该为列表!",
|
|
45
|
+
"value is not a valid list": "类型错误,提交参数应该为列表!",
|
|
45
46
|
"value is not a valid int": "类型错误,提交参数应该为整数!",
|
|
46
47
|
"value could not be parsed to a boolean": "类型错误,提交参数应该为布尔值!",
|
|
47
|
-
"Input should be a valid list": "类型错误,输入应该是一个有效的列表!"
|
|
48
|
+
"Input should be a valid list": "类型错误,输入应该是一个有效的列表!",
|
|
49
|
+
"String should have at least 6 characters": "密码长度不能小于6位!"
|
|
48
50
|
}
|
|
49
51
|
msg = error_mapping.get(exc.errors()[0].get('msg'), exc.errors()[0].get('msg'))
|
|
50
52
|
logger.error(f"请求地址: {request.url}, 错误信息: {msg}, 错误详情: {exc}")
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import json
|
|
2
2
|
|
|
3
|
+
from fastapi import Depends, Request
|
|
4
|
+
from fastapi.security import OAuth2PasswordBearer, SecurityScopes
|
|
5
|
+
|
|
3
6
|
from common_base.consts import (
|
|
4
7
|
REDIS_PREFIX_BLOCKLIST,
|
|
5
8
|
REDIS_PREFIX_USER_INFO,
|
|
@@ -9,8 +12,6 @@ from common_models.user_model import User
|
|
|
9
12
|
from common_utils.jwt_util import JwtUtil
|
|
10
13
|
from core.databases.aioredis import AioRedis
|
|
11
14
|
from core.start import base_config
|
|
12
|
-
from fastapi import Depends, Request
|
|
13
|
-
from fastapi.security import OAuth2PasswordBearer, SecurityScopes
|
|
14
15
|
|
|
15
16
|
oauth2_scheme = OAuth2PasswordBearer(tokenUrl=f"/api/{base_config.app_name}/auth/login", auto_error=False)
|
|
16
17
|
|
|
@@ -20,15 +21,15 @@ async def get_current_user(request: Request, token: str, security_scopes: Securi
|
|
|
20
21
|
"""
|
|
21
22
|
payload = JwtUtil.decode_token(token)
|
|
22
23
|
if not payload:
|
|
23
|
-
raise CustomException(msg="无效的令牌")
|
|
24
|
+
raise CustomException(msg="无效的令牌", status_code=401)
|
|
24
25
|
|
|
25
26
|
# 检查是否在黑名单中
|
|
26
27
|
if await AioRedis.get(f"{REDIS_PREFIX_BLOCKLIST}{token}"):
|
|
27
|
-
raise CustomException(msg="令牌已失效")
|
|
28
|
+
raise CustomException(msg="令牌已失效", status_code=401)
|
|
28
29
|
|
|
29
30
|
user_id = payload.get("sub")
|
|
30
31
|
if not user_id:
|
|
31
|
-
raise CustomException(msg="无效的令牌")
|
|
32
|
+
raise CustomException(msg="无效的令牌", status_code=401)
|
|
32
33
|
|
|
33
34
|
# 1. 尝试从缓存获取
|
|
34
35
|
user = None
|
|
@@ -65,7 +66,7 @@ async def get_current_user(request: Request, token: str, security_scopes: Securi
|
|
|
65
66
|
async def login_require(request: Request, security_scopes: SecurityScopes, token=Depends(oauth2_scheme)) -> User:
|
|
66
67
|
"""获取用户,获取不到报错"""
|
|
67
68
|
if not token:
|
|
68
|
-
raise CustomException('请先登录')
|
|
69
|
+
raise CustomException('请先登录', status_code=401)
|
|
69
70
|
|
|
70
71
|
return await get_current_user(request, token, security_scopes)
|
|
71
72
|
|
|
@@ -106,11 +106,11 @@ class AuthService:
|
|
|
106
106
|
"""
|
|
107
107
|
user_id = await AioRedis.get(f"{REDIS_PREFIX_REFRESH_TOKEN}{refresh_token}")
|
|
108
108
|
if not user_id:
|
|
109
|
-
raise CustomException(msg="无效或已过期的刷新令牌")
|
|
109
|
+
raise CustomException(msg="无效或已过期的刷新令牌", status_code=401)
|
|
110
110
|
try:
|
|
111
111
|
user_id = int(user_id)
|
|
112
112
|
except (ValueError, TypeError):
|
|
113
|
-
raise CustomException(msg="无效的刷新令牌")
|
|
113
|
+
raise CustomException(msg="无效的刷新令牌", status_code=401) from None
|
|
114
114
|
|
|
115
115
|
user = await User.get_or_none(id=user_id)
|
|
116
116
|
if not user or not user.is_active:
|
|
@@ -209,8 +209,8 @@ class AuthService:
|
|
|
209
209
|
cached_data = json.loads(cache_value)
|
|
210
210
|
redirect_uri = cached_data.get("redirect_uri")
|
|
211
211
|
app_pk = cached_data.get("app_pk")
|
|
212
|
-
except (json.JSONDecodeError, TypeError):
|
|
213
|
-
raise CustomException(msg="授权数据格式错误")
|
|
212
|
+
except (json.JSONDecodeError, TypeError) as e:
|
|
213
|
+
raise CustomException(msg="授权数据格式错误") from e
|
|
214
214
|
|
|
215
215
|
if not redirect_uri or not app_pk:
|
|
216
216
|
raise CustomException(msg="授权数据不完整")
|
|
@@ -50,9 +50,9 @@ class WechatUtil:
|
|
|
50
50
|
return data
|
|
51
51
|
except Exception as e:
|
|
52
52
|
if isinstance(e, CustomException):
|
|
53
|
-
raise e
|
|
53
|
+
raise e from e
|
|
54
54
|
logger.error(f"Wechat API request error: {e}")
|
|
55
|
-
raise CustomException(msg="微信服务暂时不可用")
|
|
55
|
+
raise CustomException(msg="微信服务暂时不可用") from None
|
|
56
56
|
|
|
57
57
|
@classmethod
|
|
58
58
|
async def get_oauth_access_token(cls, appid: str, secret: str, code: str) -> dict:
|
|
@@ -116,4 +116,4 @@ class WechatUtil:
|
|
|
116
116
|
return data
|
|
117
117
|
except Exception as e:
|
|
118
118
|
logger.error(f"Decrypt wechat data failed: {e}")
|
|
119
|
-
raise CustomException(msg="微信数据解密失败")
|
|
119
|
+
raise CustomException(msg="微信数据解密失败") from None
|
|
@@ -28,7 +28,7 @@ class BaseConfig(BaseSettings):
|
|
|
28
28
|
jwt_secret_key: str = "bgb0tnl9d58+6n-6h-ea&u^1#s0ccp!794=krylacjq75vzps$"
|
|
29
29
|
|
|
30
30
|
# Access Token 有效期
|
|
31
|
-
access_token_expire_minutes: int =
|
|
31
|
+
access_token_expire_minutes: int = 1440
|
|
32
32
|
# Refresh Token 有效期
|
|
33
33
|
refresh_token_expire_days: int = 30
|
|
34
34
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "rainycode"
|
|
7
|
-
version = "1.5.
|
|
7
|
+
version = "1.5.5"
|
|
8
8
|
description = "FastAPI base modules"
|
|
9
9
|
requires-python = ">=3.10"
|
|
10
10
|
|
|
@@ -23,6 +23,7 @@ dependencies = [
|
|
|
23
23
|
"uvicorn[standard] == 0.41.0",
|
|
24
24
|
"tortoise-orm[asyncmy] == 0.25.4",
|
|
25
25
|
"pydantic-settings == 2.13.1",
|
|
26
|
+
"python-multipart==0.0.32"
|
|
26
27
|
]
|
|
27
28
|
|
|
28
29
|
[project.optional-dependencies]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rainycode
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.5
|
|
4
4
|
Summary: FastAPI base modules
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
Requires-Dist: aerich==0.9.2
|
|
@@ -17,6 +17,7 @@ Requires-Dist: pycryptodome==3.23.0
|
|
|
17
17
|
Requires-Dist: uvicorn[standard]==0.41.0
|
|
18
18
|
Requires-Dist: tortoise-orm[asyncmy]==0.25.4
|
|
19
19
|
Requires-Dist: pydantic-settings==2.13.1
|
|
20
|
+
Requires-Dist: python-multipart==0.0.32
|
|
20
21
|
Provides-Extra: release
|
|
21
22
|
Requires-Dist: build==1.4.0; extra == "release"
|
|
22
23
|
Requires-Dist: twine==6.1.0; extra == "release"
|
|
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
|