lesscode-flask 0.2.12__tar.gz → 0.2.14__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 (70) hide show
  1. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/PKG-INFO +2 -12
  2. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/__init__.py +1 -1
  3. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/app.py +8 -3
  4. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/model/user.py +8 -4
  5. lesscode_flask-0.2.14/lesscode_flask/model/user_limit_policy.py +58 -0
  6. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/setting/__init__.py +19 -15
  7. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/utils/limit/limit_util.py +50 -0
  8. lesscode_flask-0.2.14/lesscode_flask/utils/limit/req/redis_rate_limiter.py +113 -0
  9. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/utils/limit/req_count/redis_count_limiter.py +19 -9
  10. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask.egg-info/PKG-INFO +2 -12
  11. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask.egg-info/SOURCES.txt +1 -0
  12. lesscode_flask-0.2.12/lesscode_flask/utils/limit/req/redis_rate_limiter.py +0 -63
  13. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/README.md +0 -0
  14. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/db/__init__.py +0 -0
  15. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/db/datasource.py +0 -0
  16. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/db/executor.py +0 -0
  17. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/export_data/__init__.py +0 -0
  18. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/export_data/data_download_handler.py +0 -0
  19. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/log/access_log_handler.py +0 -0
  20. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/model/access_log.py +0 -0
  21. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/model/base_model.py +0 -0
  22. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/model/parameterized_query.py +0 -0
  23. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/model/resource_param_template.py +0 -0
  24. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/model/response_result.py +0 -0
  25. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/service/access_log_service.py +0 -0
  26. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/service/base_service.py +0 -0
  27. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/service/resource_param_template_service.py +0 -0
  28. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/setup/__init__.py +0 -0
  29. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/signals.py +0 -0
  30. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/static/swagger.py +0 -0
  31. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/utils/decorator/__init__.py +0 -0
  32. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/utils/decorator/cache.py +0 -0
  33. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/utils/decorator/sql_injection.py +0 -0
  34. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/utils/decorator/swagger.py +0 -0
  35. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/utils/dify_utils.py +0 -0
  36. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/utils/file/file_exporter.py +0 -0
  37. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/utils/file/file_utils.py +0 -0
  38. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/utils/fs_util.py +0 -0
  39. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/utils/helpers.py +0 -0
  40. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/utils/json/NotSortJSONProvider.py +0 -0
  41. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/utils/limit/__init__.py +0 -0
  42. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/utils/limit/req/rate_limiter_handler.py +0 -0
  43. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/utils/limit/req_count/count_limiter_handler.py +0 -0
  44. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/utils/oss/__init__.py +0 -0
  45. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/utils/oss/aliyun_oss.py +0 -0
  46. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/utils/oss/ks3_oss.py +0 -0
  47. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/utils/oss/minio_oss.py +0 -0
  48. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/utils/redis/redis_helper.py +0 -0
  49. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/utils/request/request.py +0 -0
  50. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/utils/swagger/swagger_template.py +0 -0
  51. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/utils/swagger/swagger_util.py +0 -0
  52. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/utils/task/__init__.py +0 -0
  53. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/utils/task/task_helper.py +0 -0
  54. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/utils/thread/thread_utils.py +0 -0
  55. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask/wsgi.py +0 -0
  56. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask.egg-info/dependency_links.txt +0 -0
  57. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask.egg-info/requires.txt +0 -0
  58. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/lesscode_flask.egg-info/top_level.txt +0 -0
  59. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/redash/query_runner/__init__.py +0 -0
  60. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/redash/query_runner/clickhouse.py +0 -0
  61. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/redash/query_runner/elasticsearch.py +0 -0
  62. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/redash/query_runner/kingbase.py +0 -0
  63. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/redash/query_runner/mysql.py +0 -0
  64. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/redash/query_runner/pg.py +0 -0
  65. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/redash/settings/__init__.py +0 -0
  66. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/redash/settings/helpers.py +0 -0
  67. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/redash/utils/__init__.py +0 -0
  68. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/redash/utils/requests_session.py +0 -0
  69. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/setup.cfg +0 -0
  70. {lesscode_flask-0.2.12 → lesscode_flask-0.2.14}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.1
2
2
  Name: lesscode-flask
3
- Version: 0.2.12
3
+ Version: 0.2.14
4
4
  Summary: lesscode-flask 是基于flask的web开发脚手架项目,该项目初衷为简化开发过程,让研发人员更加关注业务。
5
5
  Home-page: https://lesscode-flask
6
6
  Author: Chao.yy
@@ -26,16 +26,6 @@ Requires-Dist: gevent==25.5.1
26
26
  Requires-Dist: openpyxl==3.1.5
27
27
  Requires-Dist: iputil==0.3.1
28
28
  Requires-Dist: phone==0.4.5
29
- Dynamic: author
30
- Dynamic: author-email
31
- Dynamic: classifier
32
- Dynamic: description
33
- Dynamic: description-content-type
34
- Dynamic: home-page
35
- Dynamic: platform
36
- Dynamic: requires-dist
37
- Dynamic: requires-python
38
- Dynamic: summary
39
29
 
40
30
  # lesscode-flask
41
31
 
@@ -1,4 +1,4 @@
1
- __version__ = '0.2.12'
1
+ __version__ = '0.2.14'
2
2
 
3
3
  import functools
4
4
  import logging
@@ -24,7 +24,7 @@ from lesscode_flask.utils.decorator.sql_injection import contains_sql_injection
24
24
  from lesscode_flask.utils.helpers import inject_args, generate_uuid, app_config
25
25
  from lesscode_flask.utils.json.NotSortJSONProvider import NotSortJSONProvider
26
26
  from lesscode_flask.utils.limit import RedisRateLimiter
27
- from lesscode_flask.utils.limit.limit_util import get_rate_limit_info, get_count_limit_info
27
+ from lesscode_flask.utils.limit.limit_util import get_rate_limit_info, get_count_limit_info, get_user_limit_policy
28
28
  from lesscode_flask.utils.limit.req.rate_limiter_handler import RateLimitHandler
29
29
  from lesscode_flask.utils.limit.req_count.count_limiter_handler import CountLimitHandler
30
30
  from lesscode_flask.utils.limit.req_count.redis_count_limiter import RedisCountLimiter
@@ -69,14 +69,19 @@ class Lesscoder(Flask):
69
69
 
70
70
  def preprocess_request(self) -> ft.ResponseReturnValue | None:
71
71
  v = super(Lesscoder, self).preprocess_request()
72
+ user_limit_policy = None
72
73
  if hasattr(self, 'countLimiter') and self.countLimiter:
73
- allowed, remaining = self.countLimiter.is_allowed(*get_count_limit_info())
74
+ if not user_limit_policy:
75
+ user_limit_policy = get_user_limit_policy()
76
+ allowed, remaining = self.countLimiter.is_allowed(user_limit_policy)
74
77
  if not allowed:
75
78
  countLimitHandler = self.config.get("COUNT_LIMIT_HANDLER", CountLimitHandler)
76
79
  countLimitHandler(request,remaining).response_handler()
77
80
  if hasattr(self, 'rateLimiter') and self.rateLimiter:
81
+ if not user_limit_policy:
82
+ user_limit_policy = get_user_limit_policy()
78
83
  # 是否触发限流 延迟时间 超出数量
79
- allowed,delay, excess = self.rateLimiter.is_limited(*get_rate_limit_info())
84
+ allowed,delay, excess = self.rateLimiter.is_limited(user_limit_policy)
80
85
  if not allowed :
81
86
  rateLimitHandler = self.config.get("RATE_LIMIT_HANDLER", RateLimitHandler)
82
87
  rateLimitHandler(request,delay, excess).response_handler()
@@ -41,7 +41,7 @@ class User(flask_login.UserMixin, PermissionsCheckMixin):
41
41
  email: str = None,
42
42
  type: int = 0, account_status: str = None, permissions=[], permissions_url=[], roleIds=[],
43
43
  client_id: str = None,
44
- sub: str = None):
44
+ sub: str = None,limit_policy: str =None):
45
45
  # '账号id',
46
46
  self.id = id
47
47
  # 用户名
@@ -67,6 +67,8 @@ class User(flask_login.UserMixin, PermissionsCheckMixin):
67
67
  self.permissions_url = permissions_url
68
68
  # # 角色集合
69
69
  self.roleIds = roleIds
70
+ # 限速策略
71
+ self.limit_policy = limit_policy
70
72
 
71
73
  #
72
74
  @property
@@ -124,7 +126,8 @@ class User(flask_login.UserMixin, PermissionsCheckMixin):
124
126
  "sub": self.sub if self.sub else "",
125
127
  "permissions": json.dumps(self.permissions),
126
128
  "permissions_url": json.dumps(self.permissions_url),
127
- "roleIds": json.dumps(self.roleIds)
129
+ "roleIds": json.dumps(self.roleIds),
130
+ "limit_policy": self.limit_policy
128
131
 
129
132
  }
130
133
 
@@ -136,7 +139,7 @@ class User(flask_login.UserMixin, PermissionsCheckMixin):
136
139
  display_name=user_data.get("display_name", ""), phone_no=user_data.get("phone_no", ""),
137
140
  email=user_data.get("email", ""), type=user_data.get("type", 0),
138
141
  account_status=user_data.get("account_status", 1),
139
- client_id=user_data.get("client_id", ""), sub=user_data.get("sub", ""))
142
+ client_id=user_data.get("client_id", ""), sub=user_data.get("sub", ""),limit_policy =user_data.get("limit_policy"))
140
143
  permissions = user_data.get("permissions", [])
141
144
  if isinstance(permissions, str):
142
145
  user.permissions = json.loads(permissions)
@@ -193,7 +196,8 @@ class User(flask_login.UserMixin, PermissionsCheckMixin):
193
196
  phone_no=user_dict["phone_no"],
194
197
  permissions=user_dict.get("permissions", []),
195
198
  roleIds=user_dict["roleIds"],
196
- client_id=user_dict["client_id"]
199
+ client_id=user_dict["client_id"],
200
+ limit_policy = user_dict.get("limit_policy", None),
197
201
  )
198
202
  return user
199
203
  return User()
@@ -0,0 +1,58 @@
1
+ # coding: utf-8
2
+ import json
3
+ from typing import Union
4
+
5
+
6
+ class UserLimitPolicy():
7
+
8
+ def __init__(
9
+ self,
10
+ id: str= None,
11
+ policy_name: str= None,
12
+ client_id: str= None,
13
+ rate_limit_enable: int = 0,
14
+ rate_limit_window_sec: int = 10,
15
+ rate_limit_rate: int = 5,
16
+ rate_limit_burst: int = 0,
17
+ count_limit_enable: int = 0,
18
+ count_limit_window_sec: int = 43200,
19
+ count_limit_count: int = 500,
20
+ white_list: Union[str, list] = None,
21
+ alert_webhook_url: str = None,
22
+ ):
23
+ """
24
+ 初始化 AuthUserLimitPolicy 对象。
25
+
26
+ :param policy_name: 限流策略名称
27
+ :param client_id: 所属应用 ID
28
+ :param rate_limit_enable: 是否启用频率限流(默认 0)
29
+ :param rate_limit_window_sec: 频率窗口时间(秒,默认 1)
30
+ :param rate_limit_rate: 每窗口期内允许请求数(QPS,默认 1)
31
+ :param rate_limit_burst: 允许突发流量数(默认 0)
32
+ :param count_limit_enable: 是否启用计数量限流(默认 0)
33
+ :param count_limit_window_sec: 计数窗口时间(秒,默认 43200)
34
+ :param count_limit_count: 窗口期内最大访问量(默认 500)
35
+ :param white_list: 限流白名单路径列表(JSON 格式)
36
+ :param alert_webhook_url: 报警 webhook URL
37
+ """
38
+ self.id = id
39
+ self.policy_name = policy_name
40
+ self.client_id = client_id
41
+ # 限流参数类型转换为int
42
+ self.rate_limit_enable = int(rate_limit_enable) if rate_limit_enable is not None else 0
43
+ self.rate_limit_window_sec = int(rate_limit_window_sec) if rate_limit_window_sec is not None else 1
44
+ self.rate_limit_rate = int(rate_limit_rate) if rate_limit_rate is not None else 1
45
+ self.rate_limit_burst = int(rate_limit_burst) if rate_limit_burst is not None else 0
46
+ self.count_limit_enable = int(count_limit_enable) if count_limit_enable is not None else 0
47
+ self.count_limit_window_sec = int(count_limit_window_sec) if count_limit_window_sec is not None else 43200
48
+ self.count_limit_count = int(count_limit_count) if count_limit_count is not None else 500
49
+ self.alert_webhook_url = alert_webhook_url
50
+
51
+ # 修改后的 white_list 处理逻辑
52
+ if isinstance(white_list, str):
53
+ self.white_list = json.loads(white_list) if white_list else []
54
+ elif isinstance(white_list, list):
55
+ self.white_list = white_list
56
+ else:
57
+ self.white_list = []
58
+
@@ -94,28 +94,32 @@ class BaseConfig:
94
94
  RATE_LIMIT_ENABLE: bool = False
95
95
  # 触发限流的处理函数
96
96
  RATE_LIMIT_HANDLER = RateLimitHandler
97
- # 限流频率窗口 单位是每秒
98
- RATE_LIMIT_TIME_WINDOW = 1
99
- # 限流频率
100
- RATE_LIMIT_RATE = 1
101
- # 限流频率允许的突发值 请求速率超过(rate + brust)的请求会被直接拒绝。
102
- RATE_LIMIT_BURST = 0
97
+ # # 限流频率窗口 单位是每秒
98
+ # RATE_LIMIT_TIME_WINDOW = 1
99
+ # # 限流频率
100
+ # RATE_LIMIT_RATE = 1
101
+ # # 限流频率允许的突发值 请求速率超过(rate + brust)的请求会被直接拒绝。
102
+ # RATE_LIMIT_BURST = 0
103
103
 
104
104
  # 是否启用限流计数验证
105
105
  COUNT_LIMIT_ENABLE: bool = False
106
106
  # 触发限流的处理函数
107
107
  COUNT_LIMIT_HANDLER = CountLimitHandler
108
- # 窗口期内最大访问量
109
- COUNT_LIMIT_COUNT = 500
110
- # 窗口期时长 单位秒
111
- COUNT_LIMIT_TIME_WINDOW = 60*60*12
108
+ # # 窗口期内最大访问量
109
+ # COUNT_LIMIT_COUNT = 500
110
+ # # 窗口期时长 单位秒
111
+ # COUNT_LIMIT_TIME_WINDOW = 60*60*12
112
112
  # 白名单
113
113
  LIMIT_WHITE_LIST: list = ["/ike2b/user/WXOA/login", "/ike2b/detail/company/node/get_note",
114
- "/ike2b/user/attention/attention_company_status",
115
- "/ike2b/pay/wx_pay/sq_coin/get_payment_status",
116
- "/toolEngine/targetedInvestmentPromotion/get_reach_chain",
117
- "/ike2b/user/WXOA/bind", "/ike2b/select/park/park_list",
118
- "/ike2b/pool/company/area_distribution/park_company_count_by_special_tag"]
114
+ "/ike2b/user/attention/attention_company_status",
115
+ "/ike2b/pay/wx_pay/sq_coin/get_payment_status",
116
+ "/toolEngine/targetedInvestmentPromotion/get_reach_chain",
117
+ "/ike2b/user/WXOA/bind", "/ike2b/select/park/park_list",
118
+ "/ike2b/pool/company/area_distribution/park_company_count_by_special_tag",
119
+ "/ike2b/pool/region/list/search_region_list_by_range",
120
+ "/ike2b/pool/park/list/search_park_list_by_range",
121
+ ]
122
+
119
123
  # 飞书 webhook URL 常量定义 用于向指定的飞书机器人发送消息的 webhook 地址
120
124
  LIMIT_FS_WEBHOOK_URL = "https://open.feishu.cn/open-apis/bot/v2/hook/545140ef-8234-4167-9f0c-8ee72abae430"
121
125
 
@@ -3,7 +3,11 @@ import hashlib
3
3
  from flask import request, current_app
4
4
 
5
5
  from lesscode_flask.model.user import flask_login
6
+ from lesscode_flask.model.user_limit_policy import UserLimitPolicy
6
7
  from lesscode_flask.utils.helpers import app_config
8
+ from lesscode_flask.utils.redis.redis_helper import RedisHelper
9
+
10
+
7
11
  def limit_key_encode() -> str:
8
12
  limit_key = None
9
13
  # 白名单
@@ -25,6 +29,25 @@ def limit_key_encode() -> str:
25
29
  limit_key = hashlib.md5(limit_key.encode('utf-8')).hexdigest()
26
30
  return hashlib.md5(limit_key.encode('utf-8')).hexdigest()
27
31
 
32
+ def limit_key(request_url) -> str:
33
+ """
34
+ 构建访问计数信息的redis 缓存key
35
+ :param request_url:
36
+ :return:
37
+ """
38
+ limit_key = None
39
+ # 1、获取当前用户
40
+ current_user = flask_login.current_user
41
+ if current_user and not current_user.is_anonymous_user:
42
+ user_id = current_user.id
43
+ # 拼接 key 用户id+url
44
+ limit_key = f"{user_id}:{request_url}"
45
+ # 如果键为空,使用客户端IP
46
+ if not limit_key:
47
+ limit_key = f"{request.remote_addr}{request_url}"
48
+ # 对键进行哈希处理,避免键过长
49
+ limit_key = hashlib.md5(limit_key.encode('utf-8')).hexdigest()
50
+ return limit_key
28
51
 
29
52
  def get_rate_limit_info() -> str:
30
53
  """
@@ -47,5 +70,32 @@ def get_count_limit_info() -> str:
47
70
  # 窗口期时长 单位秒
48
71
  time_window = app_config.get("COUNT_LIMIT_TIME_WINDOW",3600)
49
72
  limit_key = limit_key_encode()
73
+ user_limit_policy = get_user_limit_policy()
50
74
  return limit_key, count, time_window
51
75
 
76
+
77
+ def get_user_limit_policy():
78
+ limit_policy_id = None # 初始化变量
79
+ user_limit_policy = None # 初始化变量
80
+ REDIS_OAUTH_KEY = app_config.get("REDIS_OAUTH_KEY", "redis")
81
+
82
+ # 1、获取当前用户
83
+ current_user = flask_login.current_user
84
+ if current_user and not current_user.is_anonymous_user:
85
+ limit_policy_id = current_user.limit_policy
86
+ # item["white_list"] = json.dumps(item.get("white_list", []))
87
+ if limit_policy_id:
88
+ key = f"limit_policy:{limit_policy_id}"
89
+ limit_policy_id = RedisHelper(REDIS_OAUTH_KEY).sync_hgetall(key)
90
+ user_limit_policy = UserLimitPolicy(**limit_policy_id)
91
+
92
+ if user_limit_policy:
93
+ return user_limit_policy
94
+ else:
95
+ # 如果没有策略就构建默认策略
96
+ REDIS_RATE_LIMIT_ENABLE = app_config.get("RATE_LIMIT_ENABLE", False)
97
+ REDIS_COUNT_LIMIT_ENABLE = app_config.get("COUNT_LIMIT_ENABLE", False)
98
+ LIMIT_WHITE_LIST = app_config.get("LIMIT_WHITE_LIST", False)
99
+ return UserLimitPolicy(rate_limit_enable=int(REDIS_RATE_LIMIT_ENABLE),
100
+ count_limit_enable=int(REDIS_COUNT_LIMIT_ENABLE),white_list=LIMIT_WHITE_LIST)
101
+
@@ -0,0 +1,113 @@
1
+ import time
2
+ import logging
3
+
4
+ from flask import request
5
+
6
+ from lesscode_flask.model.user_limit_policy import UserLimitPolicy
7
+ from lesscode_flask.utils.limit.limit_util import limit_key
8
+ from lesscode_flask.utils.redis.redis_helper import RedisHelper
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ class RedisRateLimiter:
14
+ """
15
+ Redis限流实现
16
+ """
17
+
18
+ def __init__(self, redis_key):
19
+ self.redis_key = redis_key
20
+
21
+ def is_limited(self,user_limit_policy: UserLimitPolicy) -> tuple:
22
+ """
23
+ 检查是否触发限流
24
+ :param user_limit_policy: 策略信息
25
+ :return: (allowed,delay, excess) 是否触发限流 延迟时间和超出数量
26
+ """
27
+
28
+ rate: float = user_limit_policy.rate_limit_rate
29
+ burst: float = user_limit_policy.rate_limit_burst
30
+ time_window: int = user_limit_policy.rate_limit_window_sec
31
+ rate_limit_enable = user_limit_policy.rate_limit_enable
32
+ limit_white_list = user_limit_policy.white_list
33
+ request_url = request.url_rule.rule
34
+ # 未启用限流 或者 在白名单中 直接返回允许
35
+ if rate_limit_enable == 0 or request_url in limit_white_list:
36
+ # 白名单访问 直接返回None
37
+ return True, 0, 0
38
+ # 构造Redis键
39
+ key = f"limit_req:{limit_key(request_url)}"
40
+ # 存储键的键
41
+ excess_key = f"{key}excess"
42
+ # 存储最近一次请求时间毫秒的键
43
+ last_key = f"{key}last"
44
+ # 当前时间的毫秒表示形式
45
+ now = time.time() * 1000 # 转换为毫秒
46
+
47
+ try:
48
+ # 获取上次记录的信息
49
+ excess = RedisHelper(self.redis_key).sync_get(excess_key)
50
+ last =RedisHelper(self.redis_key).sync_get(last_key)
51
+
52
+ if excess is not None and last is not None:
53
+ excess = float(excess)
54
+ # 获取最近一次请求的时间毫秒值
55
+ last = float(last)
56
+ # 计算当前与最近一次访问的时间差
57
+ elapsed = now - last
58
+ # 根据时间差计算当前的excess值
59
+ excess = max(excess - (rate * abs(elapsed) / 1000*time_window) + 1, 0)
60
+ else:
61
+ excess = 0
62
+ # 返回延迟时间和超出数量
63
+ delay = excess / rate
64
+ # 检查是否超出限制
65
+ if excess > burst+rate:
66
+ return False, delay, excess # 被拒绝
67
+ # 更新Redis中的值
68
+ RedisHelper(self.redis_key).sync_set(excess_key, excess)
69
+ RedisHelper(self.redis_key).sync_set(last_key, now)
70
+ return True,delay, excess
71
+
72
+ except Exception as e:
73
+ logger.error(f"Redis rate limiter error: {e}")
74
+ return True, 0,0
75
+ # if not key: # 没有key表示不验证
76
+ # return True, 0,0
77
+ # # 构造Redis键
78
+ # key = f"limit_req:{key}"
79
+ # # 存储键的键
80
+ # excess_key = f"{key}excess"
81
+ # # 存储最近一次请求时间毫秒的键
82
+ # last_key = f"{key}last"
83
+ # # 当前时间的毫秒表示形式
84
+ # now = time.time() * 1000 # 转换为毫秒
85
+ #
86
+ # try:
87
+ # # 获取上次记录的信息
88
+ # excess = RedisHelper(self.redis_key).sync_get(excess_key)
89
+ # last =RedisHelper(self.redis_key).sync_get(last_key)
90
+ #
91
+ # if excess is not None and last is not None:
92
+ # excess = float(excess)
93
+ # # 获取最近一次请求的时间毫秒值
94
+ # last = float(last)
95
+ # # 计算当前与最近一次访问的时间差
96
+ # elapsed = now - last
97
+ # # 根据时间差计算当前的excess值
98
+ # excess = max(excess - (rate * abs(elapsed) / 1000*time_window) + 1, 0)
99
+ # else:
100
+ # excess = 0
101
+ # # 返回延迟时间和超出数量
102
+ # delay = excess / rate
103
+ # # 检查是否超出限制
104
+ # if excess > burst+rate:
105
+ # return False, delay, excess # 被拒绝
106
+ # # 更新Redis中的值
107
+ # RedisHelper(self.redis_key).sync_set(excess_key, excess)
108
+ # RedisHelper(self.redis_key).sync_set(last_key, now)
109
+ # return True,delay, excess
110
+ #
111
+ # except Exception as e:
112
+ # logger.error(f"Redis rate limiter error: {e}")
113
+ # return True, 0,0
@@ -1,4 +1,9 @@
1
1
  import logging
2
+
3
+ from flask import request
4
+
5
+ from lesscode_flask.model.user_limit_policy import UserLimitPolicy
6
+ from lesscode_flask.utils.limit.limit_util import limit_key
2
7
  from lesscode_flask.utils.redis.redis_helper import RedisHelper
3
8
 
4
9
  logger = logging.getLogger(__name__)
@@ -9,18 +14,23 @@ class RedisCountLimiter:
9
14
  def __init__(self, redis_key):
10
15
  self.redis_key = redis_key
11
16
 
12
- def is_allowed(self, key, count, time_window, cost=1):
17
+ def is_allowed(self, user_limit_policy: UserLimitPolicy):
13
18
  """
14
19
  检查是否允许请求
15
- :param key: 限流键
16
- :param count: 时间窗口内的请求数量限制
17
- :param time_window: 时间窗口(秒)
18
- :param cost: 本次请求消耗的令牌数
19
20
  :return: (allowed, remaining)
20
21
  """
21
- if not key: # 没有key表示不验证
22
- return True, 0
23
- key = f"limit_count:{key}"
22
+ count = user_limit_policy.count_limit_count
23
+ time_window = user_limit_policy.count_limit_window_sec
24
+ count_limit_enable = user_limit_policy.count_limit_enable
25
+ limit_white_list = user_limit_policy.white_list
26
+ cost = 1 # 每次消耗一次数
27
+ request_url = request.url_rule.rule
28
+ # 未启用限流 或者 在白名单中 直接返回允许
29
+ if count_limit_enable == 0 or request_url in limit_white_list:
30
+ # 白名单访问 直接返回None
31
+ return True, 0, 0
32
+ # 限流键
33
+ key = f"limit_count:{limit_key(request_url)}"
24
34
 
25
35
  try:
26
36
  remaining = RedisHelper(self.redis_key).sync_get(key)
@@ -28,7 +38,7 @@ class RedisCountLimiter:
28
38
  remaining = count-cost
29
39
  RedisHelper(self.redis_key).sync_set(key,remaining,time_window)
30
40
  else:
31
- # 键存在,减少令牌数
41
+ # 键存在,每次减少相应数量
32
42
  remaining = RedisHelper(self.redis_key).sync_incrby(name=key,amount=0- cost)
33
43
  if remaining < 0:
34
44
  return False, remaining
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.1
2
2
  Name: lesscode-flask
3
- Version: 0.2.12
3
+ Version: 0.2.14
4
4
  Summary: lesscode-flask 是基于flask的web开发脚手架项目,该项目初衷为简化开发过程,让研发人员更加关注业务。
5
5
  Home-page: https://lesscode-flask
6
6
  Author: Chao.yy
@@ -26,16 +26,6 @@ Requires-Dist: gevent==25.5.1
26
26
  Requires-Dist: openpyxl==3.1.5
27
27
  Requires-Dist: iputil==0.3.1
28
28
  Requires-Dist: phone==0.4.5
29
- Dynamic: author
30
- Dynamic: author-email
31
- Dynamic: classifier
32
- Dynamic: description
33
- Dynamic: description-content-type
34
- Dynamic: home-page
35
- Dynamic: platform
36
- Dynamic: requires-dist
37
- Dynamic: requires-python
38
- Dynamic: summary
39
29
 
40
30
  # lesscode-flask
41
31
 
@@ -21,6 +21,7 @@ lesscode_flask/model/parameterized_query.py
21
21
  lesscode_flask/model/resource_param_template.py
22
22
  lesscode_flask/model/response_result.py
23
23
  lesscode_flask/model/user.py
24
+ lesscode_flask/model/user_limit_policy.py
24
25
  lesscode_flask/service/access_log_service.py
25
26
  lesscode_flask/service/base_service.py
26
27
  lesscode_flask/service/resource_param_template_service.py
@@ -1,63 +0,0 @@
1
- import time
2
- import logging
3
- from lesscode_flask.utils.redis.redis_helper import RedisHelper
4
-
5
- logger = logging.getLogger(__name__)
6
-
7
-
8
- class RedisRateLimiter:
9
- """
10
- Redis限流实现
11
- """
12
-
13
- def __init__(self, redis_key):
14
- self.redis_key = redis_key
15
-
16
- def is_limited(self, key: str, rate: float, burst: float,time_window:int=1) -> tuple:
17
- """
18
- 检查是否触发限流
19
- :param key: 限流键
20
- :param rate: 每秒请求速率
21
- :param burst: 突发请求数
22
- :param time_window: 窗口期
23
- :return: (allowed,delay, excess) 是否触发限流 延迟时间和超出数量
24
- """
25
- if not key: # 没有key表示不验证
26
- return True, 0,0
27
- # 构造Redis键
28
- key = f"limit_req:{key}"
29
- # 存储键的键
30
- excess_key = f"{key}excess"
31
- # 存储最近一次请求时间毫秒的键
32
- last_key = f"{key}last"
33
- # 当前时间的毫秒表示形式
34
- now = time.time() * 1000 # 转换为毫秒
35
-
36
- try:
37
- # 获取上次记录的信息
38
- excess = RedisHelper(self.redis_key).sync_get(excess_key)
39
- last =RedisHelper(self.redis_key).sync_get(last_key)
40
-
41
- if excess is not None and last is not None:
42
- excess = float(excess)
43
- # 获取最近一次请求的时间毫秒值
44
- last = float(last)
45
- # 计算当前与最近一次访问的时间差
46
- elapsed = now - last
47
- # 根据时间差计算当前的excess值
48
- excess = max(excess - (rate * abs(elapsed) / 1000*time_window) + 1, 0)
49
- else:
50
- excess = 0
51
- # 返回延迟时间和超出数量
52
- delay = excess / rate
53
- # 检查是否超出限制
54
- if excess > burst+rate:
55
- return False, delay, excess # 被拒绝
56
- # 更新Redis中的值
57
- RedisHelper(self.redis_key).sync_set(excess_key, excess)
58
- RedisHelper(self.redis_key).sync_set(last_key, now)
59
- return True,delay, excess
60
-
61
- except Exception as e:
62
- logger.error(f"Redis rate limiter error: {e}")
63
- return True, 0,0