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.
Files changed (38) hide show
  1. {rainycode-1.5.2 → rainycode-1.5.5}/PKG-INFO +2 -1
  2. {rainycode-1.5.2 → rainycode-1.5.5}/common_base/aiorequests.py +13 -4
  3. {rainycode-1.5.2 → rainycode-1.5.5}/common_base/exception.py +8 -6
  4. {rainycode-1.5.2 → rainycode-1.5.5}/common_depends/auth_depend.py +7 -6
  5. {rainycode-1.5.2 → rainycode-1.5.5}/common_servers/service/auth_svc.py +4 -4
  6. {rainycode-1.5.2 → rainycode-1.5.5}/common_utils/snowflake_util.py +1 -1
  7. {rainycode-1.5.2 → rainycode-1.5.5}/common_utils/wechat_util.py +3 -3
  8. {rainycode-1.5.2 → rainycode-1.5.5}/core/base_config.py +1 -1
  9. {rainycode-1.5.2 → rainycode-1.5.5}/core/start.py +1 -1
  10. {rainycode-1.5.2 → rainycode-1.5.5}/pyproject.toml +2 -1
  11. {rainycode-1.5.2 → rainycode-1.5.5}/rainycode.egg-info/PKG-INFO +2 -1
  12. {rainycode-1.5.2 → rainycode-1.5.5}/rainycode.egg-info/requires.txt +1 -0
  13. {rainycode-1.5.2 → rainycode-1.5.5}/common_base/consts.py +0 -0
  14. {rainycode-1.5.2 → rainycode-1.5.5}/common_base/logging.py +0 -0
  15. {rainycode-1.5.2 → rainycode-1.5.5}/common_base/response.py +0 -0
  16. {rainycode-1.5.2 → rainycode-1.5.5}/common_models/__init__.py +0 -0
  17. {rainycode-1.5.2 → rainycode-1.5.5}/common_models/base_model.py +0 -0
  18. {rainycode-1.5.2 → rainycode-1.5.5}/common_models/file_model.py +0 -0
  19. {rainycode-1.5.2 → rainycode-1.5.5}/common_models/user_model.py +0 -0
  20. {rainycode-1.5.2 → rainycode-1.5.5}/common_models/wechat_model.py +0 -0
  21. {rainycode-1.5.2 → rainycode-1.5.5}/common_servers/api/auth_api.py +0 -0
  22. {rainycode-1.5.2 → rainycode-1.5.5}/common_servers/api/wechat_api.py +0 -0
  23. {rainycode-1.5.2 → rainycode-1.5.5}/common_servers/router.py +0 -0
  24. {rainycode-1.5.2 → rainycode-1.5.5}/common_servers/schemas/auth_schema.py +0 -0
  25. {rainycode-1.5.2 → rainycode-1.5.5}/common_servers/service/file_svc.py +0 -0
  26. {rainycode-1.5.2 → rainycode-1.5.5}/common_servers/service/wechat_svc.py +0 -0
  27. {rainycode-1.5.2 → rainycode-1.5.5}/common_utils/bcrypt_util.py +0 -0
  28. {rainycode-1.5.2 → rainycode-1.5.5}/common_utils/captcha_util.py +0 -0
  29. {rainycode-1.5.2 → rainycode-1.5.5}/common_utils/datetime_util.py +0 -0
  30. {rainycode-1.5.2 → rainycode-1.5.5}/common_utils/ip_util.py +0 -0
  31. {rainycode-1.5.2 → rainycode-1.5.5}/common_utils/jwt_util.py +0 -0
  32. {rainycode-1.5.2 → rainycode-1.5.5}/core/databases/aiodb.py +0 -0
  33. {rainycode-1.5.2 → rainycode-1.5.5}/core/databases/aioredis.py +0 -0
  34. {rainycode-1.5.2 → rainycode-1.5.5}/core/middleware/http_middleware.py +0 -0
  35. {rainycode-1.5.2 → rainycode-1.5.5}/rainycode.egg-info/SOURCES.txt +0 -0
  36. {rainycode-1.5.2 → rainycode-1.5.5}/rainycode.egg-info/dependency_links.txt +0 -0
  37. {rainycode-1.5.2 → rainycode-1.5.5}/rainycode.egg-info/top_level.txt +0 -0
  38. {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.2
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
- # 模块级别连接池,跨请求复用 keep-alive 长连接(而非每次新建 connector)
10
+ # 连接池:跨请求复用 keep-alive 长连接(而非每次新建 connector)。
11
11
  # limit: 全池同时最大连接数(含活跃与 keep-alive 空闲复用)
12
12
  # limit_per_host: 单个 host 端点同时最大连接数,限制对单一对端的并发/占用
13
- _CONNECTOR = aiohttp.TCPConnector(ssl=_SSL_CONTEXT, limit=300, limit_per_host=100)
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=_CONNECTOR, trust_env=True) as session,
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=_CONNECTOR, trust_env=True) as session,
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 = 0,
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="授权数据不完整")
@@ -144,7 +144,7 @@ def get_machine_info() -> tuple[int, int]:
144
144
 
145
145
  return datacenter_id, machine_id
146
146
  except Exception as e:
147
- raise RuntimeError(f"获取机器信息失败: {e}")
147
+ raise RuntimeError(f"获取机器信息失败: {e}") from e
148
148
 
149
149
 
150
150
  # 单例模式,默认实例
@@ -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 = 30
31
+ access_token_expire_minutes: int = 1440
32
32
  # Refresh Token 有效期
33
33
  refresh_token_expire_days: int = 30
34
34
 
@@ -59,7 +59,7 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[Any, Any]:
59
59
  logger.info(f'【{config.app_title}】服务成功启动...')
60
60
  except Exception as e:
61
61
  logger.error(f'【{config.app_title}】服务启动失败: {str(e)}')
62
- raise e
62
+ raise e from e
63
63
 
64
64
  yield
65
65
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "rainycode"
7
- version = "1.5.2"
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.2
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"
@@ -12,6 +12,7 @@ pycryptodome==3.23.0
12
12
  uvicorn[standard]==0.41.0
13
13
  tortoise-orm[asyncmy]==0.25.4
14
14
  pydantic-settings==2.13.1
15
+ python-multipart==0.0.32
15
16
 
16
17
  [release]
17
18
  build==1.4.0
File without changes