huace-aigc-auth-client 1.1.21__py3-none-any.whl → 1.1.28__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.
@@ -172,4 +172,4 @@ __all__ = [
172
172
  # 用户上下文
173
173
  "get_current_user",
174
174
  ]
175
- __version__ = "1.1.21"
175
+ __version__ = "1.1.28"
@@ -18,8 +18,7 @@ class ApiStatsCollector:
18
18
  api_url: str,
19
19
  app_id: str,
20
20
  app_secret: str,
21
- token: str,
22
- batch_size: int = 10,
21
+ batch_size: int = 50,
23
22
  flush_interval: float = 5.0,
24
23
  enabled: bool = True
25
24
  ):
@@ -30,7 +29,6 @@ class ApiStatsCollector:
30
29
  api_url: 统计接口 URL(如:http://auth.example.com/api/sdk/stats/report/batch)
31
30
  app_id: 应用 ID
32
31
  app_secret: 应用密钥
33
- token: 用户访问令牌
34
32
  batch_size: 批量提交大小
35
33
  flush_interval: 刷新间隔(秒)
36
34
  enabled: 是否启用
@@ -38,7 +36,6 @@ class ApiStatsCollector:
38
36
  self.api_url = api_url.rstrip('/')
39
37
  self.app_id = app_id
40
38
  self.app_secret = app_secret
41
- self.token = token
42
39
  self.batch_size = batch_size
43
40
  self.flush_interval = flush_interval
44
41
  self.enabled = enabled
@@ -69,6 +66,7 @@ class ApiStatsCollector:
69
66
  api_method: str,
70
67
  status_code: int,
71
68
  response_time: float,
69
+ token: str,
72
70
  error_message: Optional[str] = None,
73
71
  request_params: Optional[Dict[str, Any]] = None
74
72
  ):
@@ -80,21 +78,26 @@ class ApiStatsCollector:
80
78
  api_method: 请求方法
81
79
  status_code: 状态码
82
80
  response_time: 响应时间(秒)
81
+ token: 用户访问令牌
83
82
  error_message: 错误信息
84
83
  request_params: 请求参数(包含 headers, query_params, view_params, request_body, form_params)
85
84
  """
86
85
  if not self.enabled:
87
86
  return
88
87
 
88
+ # 过滤重定向请求(3xx 状态码),这些通常是框架自动处理的,不应统计
89
+ if 300 <= status_code < 400:
90
+ return
91
+
89
92
  try:
90
93
  stat_data = {
91
94
  'api_path': api_path,
92
95
  'api_method': api_method,
93
96
  'status_code': status_code,
94
97
  'response_time': response_time,
98
+ 'token': token,
95
99
  'error_message': error_message,
96
- 'request_params': request_params,
97
- 'timestamp': datetime.utcnow().isoformat()
100
+ 'request_params': request_params
98
101
  }
99
102
  self.queue.put_nowait(stat_data)
100
103
  except queue.Full:
@@ -135,19 +138,50 @@ class ApiStatsCollector:
135
138
  self._flush_buffer(buffer)
136
139
 
137
140
  def _flush_buffer(self, buffer: List[Dict[str, Any]]):
138
- """刷新缓冲区:批量提交统计数据"""
141
+ """刷新缓冲区:按token分组批量提交统计数据"""
139
142
  if not buffer:
140
143
  return
141
144
 
145
+ try:
146
+ # 按token分组
147
+ token_groups: Dict[str, List[Dict[str, Any]]] = {}
148
+ for stat in buffer:
149
+ token = stat.get('token')
150
+ if not token:
151
+ continue
152
+
153
+ if token not in token_groups:
154
+ token_groups[token] = []
155
+
156
+ # 移除token字段后添加到分组
157
+ stat_copy = stat.copy()
158
+ stat_copy.pop('token', None)
159
+ token_groups[token].append(stat_copy)
160
+
161
+ # 对每个token分组分别提交
162
+ for token, stats in token_groups.items():
163
+ self._submit_stats(token, stats)
164
+
165
+ except Exception:
166
+ pass # 静默失败,不影响主流程
167
+
168
+ def _submit_stats(self, token: str, stats: List[Dict[str, Any]]):
169
+ """
170
+ 提交统计数据到服务端
171
+
172
+ Args:
173
+ token: 用户访问令牌
174
+ stats: 统计数据列表(已移除token字段)
175
+ """
142
176
  try:
143
177
  headers = {
144
178
  'X-App-Id': self.app_id,
145
179
  'X-App-Secret': self.app_secret,
146
- 'Authorization': f'Bearer {self.token}',
180
+ 'Authorization': f'Bearer {token}',
147
181
  'Content-Type': 'application/json'
148
182
  }
149
183
 
150
- payload = {'stats': buffer}
184
+ payload = {'stats': stats}
151
185
 
152
186
  response = requests.post(
153
187
  f'{self.api_url}/stats/report/batch',
@@ -173,7 +207,6 @@ def init_api_stats_collector(
173
207
  api_url: str,
174
208
  app_id: str,
175
209
  app_secret: str,
176
- token: str,
177
210
  batch_size: int = 10,
178
211
  flush_interval: float = 5.0,
179
212
  enabled: bool = True
@@ -185,7 +218,6 @@ def init_api_stats_collector(
185
218
  api_url: 统计接口 URL
186
219
  app_id: 应用 ID
187
220
  app_secret: 应用密钥
188
- token: 用户访问令牌
189
221
  batch_size: 批量提交大小
190
222
  flush_interval: 刷新间隔(秒)
191
223
  enabled: 是否启用
@@ -198,7 +230,6 @@ def init_api_stats_collector(
198
230
  api_url=api_url,
199
231
  app_id=app_id,
200
232
  app_secret=app_secret,
201
- token=token,
202
233
  batch_size=batch_size,
203
234
  flush_interval=flush_interval,
204
235
  enabled=enabled
@@ -224,6 +255,7 @@ def collect_api_stat(
224
255
  api_method: str,
225
256
  status_code: int,
226
257
  response_time: float,
258
+ token: str,
227
259
  error_message: Optional[str] = None,
228
260
  request_params: Optional[Dict[str, Any]] = None
229
261
  ):
@@ -231,7 +263,21 @@ def collect_api_stat(
231
263
  快捷方法:收集接口统计数据
232
264
 
233
265
  使用全局收集器实例
266
+ 注意:会自动过滤 3xx 重定向状态码的请求
267
+
268
+ Args:
269
+ api_path: 接口路径
270
+ api_method: 请求方法
271
+ status_code: 状态码
272
+ response_time: 响应时间(秒)
273
+ token: 用户访问令牌
274
+ error_message: 错误信息
275
+ request_params: 请求参数
234
276
  """
277
+ # 过滤重定向请求(3xx 状态码),这些通常是框架自动处理的,不应统计
278
+ if 300 <= status_code < 400:
279
+ return
280
+
235
281
  collector = get_api_stats_collector()
236
282
  if collector:
237
283
  collector.collect(
@@ -239,6 +285,7 @@ def collect_api_stat(
239
285
  api_method=api_method,
240
286
  status_code=status_code,
241
287
  response_time=response_time,
288
+ token=token,
242
289
  error_message=error_message,
243
290
  request_params=request_params
244
291
  )
@@ -570,24 +570,28 @@ class AuthMiddleware:
570
570
  self.exclude_paths = exclude_paths or []
571
571
  self.exclude_prefixes = exclude_prefixes or []
572
572
  self.enable_stats = enable_stats
573
- self.stats_collector = None
574
573
 
575
- # 如果启用统计,设置统计接口 URL(默认使用 client 的 base_url)
574
+ # 如果启用统计,初始化全局统计收集器
576
575
  if self.enable_stats:
577
576
  self.stats_api_url = stats_api_url or f"{self.client.base_url}/sdk"
577
+ self._init_global_stats_collector()
578
578
 
579
- def _init_stats_collector(self, token: str):
580
- """初始化统计收集器(延迟初始化)"""
581
- if not self.enable_stats or self.stats_collector is not None:
579
+ def _init_global_stats_collector(self):
580
+ """初始化全局统计收集器(只初始化一次)"""
581
+ if not self.enable_stats:
582
582
  return
583
583
 
584
584
  try:
585
- from .api_stats_collector import init_api_stats_collector
586
- self.stats_collector = init_api_stats_collector(
585
+ from .api_stats_collector import init_api_stats_collector, get_api_stats_collector
586
+
587
+ # 检查是否已经初始化
588
+ if get_api_stats_collector() is not None:
589
+ return
590
+
591
+ init_api_stats_collector(
587
592
  api_url=self.stats_api_url,
588
593
  app_id=self.client.app_id,
589
594
  app_secret=self.client.app_secret,
590
- token=token,
591
595
  batch_size=10,
592
596
  flush_interval=5.0,
593
597
  enabled=True
@@ -615,21 +619,31 @@ class AuthMiddleware:
615
619
  "form_params": None
616
620
  }
617
621
 
618
- # 获取请求体(JSON 或文本)
622
+ # 获取 content-type
623
+ content_type = request.headers.get('Content-Type', '').lower()
624
+
625
+ # 获取请求体(使用白名单方式,只处理已知安全的内容类型)
619
626
  if request.is_json:
620
627
  try:
621
628
  params["request_body"] = request.get_json(silent=True)
622
629
  except Exception:
623
630
  pass
624
- elif request.data:
631
+ elif 'application/x-www-form-urlencoded' in content_type:
632
+ # 表单数据
633
+ if request.form:
634
+ params["form_params"] = request.form.to_dict(flat=False)
635
+ elif 'multipart/form-data' in content_type:
636
+ # 文件上传请求,不读取 request.data,避免损坏文件流
637
+ # 只收集表单字段(非文件字段)
638
+ if request.form:
639
+ params["form_params"] = request.form.to_dict(flat=False)
640
+ elif content_type.startswith('text/') and request.data and len(request.data) < 10240:
641
+ # 只读取文本类型的请求体,且限制大小避免内存问题
625
642
  try:
626
643
  params["request_body"] = request.data.decode('utf-8')
627
644
  except Exception:
628
- params["request_body"] = str(request.data)
629
-
630
- # 获取表单数据
631
- if request.form:
632
- params["form_params"] = request.form.to_dict(flat=False)
645
+ pass
646
+ # 其他类型(如 application/octet-stream 等二进制流)直接跳过,不读取
633
647
 
634
648
  return params
635
649
  except Exception as e:
@@ -656,28 +670,32 @@ class AuthMiddleware:
656
670
  "form_params": None
657
671
  }
658
672
 
659
- # 获取请求体
660
- content_type = request.headers.get("content-type", "")
673
+ # 获取请求体(使用白名单方式,只处理已知安全的内容类型)
674
+ content_type = request.headers.get("content-type", "").lower()
675
+ # 读取请求体(需要特殊处理以避免消耗流)
676
+ body = b""
677
+ if request.method in ["POST", "PUT", "PATCH"]:
678
+ try:
679
+ body = await request.body()
680
+ except:
681
+ pass
682
+ params["request_body"] = body.decode('utf-8', errors='ignore')
661
683
 
662
684
  if "application/json" in content_type:
663
685
  try:
664
686
  params["request_body"] = await request.json()
665
687
  except Exception:
666
688
  pass
667
- elif "application/x-www-form-urlencoded" in content_type or "multipart/form-data" in content_type:
689
+ elif "application/x-www-form-urlencoded" in content_type:
668
690
  try:
669
691
  form = await request.form()
670
692
  params["form_params"] = {k: v for k, v in form.items()}
671
693
  except Exception:
672
694
  pass
673
- else:
674
- # 尝试读取原始body
675
- try:
676
- body = await request.body()
677
- if body:
678
- params["request_body"] = body.decode('utf-8')
679
- except Exception:
680
- pass
695
+ elif "multipart/form-data" in content_type:
696
+ # 文件上传请求,不读取 body,避免损坏文件流
697
+ params["form_params"] = {"skipped_multipart": True}
698
+ # 其他类型(如 application/octet-stream 等二进制流)直接跳过,不读取
681
699
 
682
700
  return params
683
701
  except Exception as e:
@@ -690,22 +708,27 @@ class AuthMiddleware:
690
708
  api_method: str,
691
709
  status_code: int,
692
710
  response_time: float,
711
+ token: str,
693
712
  error_message: Optional[str] = None,
694
713
  request_params: Optional[Dict[str, Any]] = None
695
714
  ):
696
715
  """收集接口统计"""
697
- if not self.enable_stats or not self.stats_collector:
716
+ if not self.enable_stats:
698
717
  return
699
718
 
700
719
  try:
701
- self.stats_collector.collect(
702
- api_path=api_path,
703
- api_method=api_method,
704
- status_code=status_code,
705
- response_time=response_time,
706
- error_message=error_message,
707
- request_params=request_params
708
- )
720
+ from .api_stats_collector import get_api_stats_collector
721
+ collector = get_api_stats_collector()
722
+ if collector:
723
+ collector.collect(
724
+ api_path=api_path,
725
+ api_method=api_method,
726
+ status_code=status_code,
727
+ response_time=response_time,
728
+ token=token,
729
+ error_message=error_message,
730
+ request_params=request_params
731
+ )
709
732
  except Exception:
710
733
  pass # 静默失败
711
734
 
@@ -726,9 +749,83 @@ class AuthMiddleware:
726
749
  return None
727
750
  return authorization[7:]
728
751
 
752
+ def get_fastapi_middleware_class(self, user_info_callback: Callable = None):
753
+ """
754
+ 获取 FastAPI 中间件类(推荐使用,避免请求体读取问题)
755
+
756
+ 使用方法:
757
+ from starlette.middleware.base import BaseHTTPMiddleware
758
+ app.add_middleware(auth_middleware.get_fastapi_middleware_class())
759
+
760
+ Returns:
761
+ 继承于 BaseHTTPMiddleware 的中间件类
762
+ """
763
+ from fastapi.responses import JSONResponse
764
+ from starlette.middleware.base import BaseHTTPMiddleware
765
+
766
+ auth_middleware_instance = self
767
+
768
+ class AuthHTTPMiddleware(BaseHTTPMiddleware):
769
+ async def dispatch(self, request, call_next):
770
+ path = request.url.path
771
+ start_time = time.time()
772
+
773
+ # 收集请求参数
774
+ request_params = await auth_middleware_instance._collect_fastapi_request_params(request) if auth_middleware_instance.enable_stats else None
775
+
776
+ # 检查是否跳过
777
+ if auth_middleware_instance._should_skip(path):
778
+ return await call_next(request)
779
+
780
+ # 获取 Authorization header
781
+ authorization = request.headers.get("Authorization")
782
+ token = auth_middleware_instance._extract_token(authorization)
783
+
784
+ if not token:
785
+ logger.warning("AuthMiddleware未提供认证信息")
786
+ response_time = time.time() - start_time
787
+ auth_middleware_instance._collect_stats(path, request.method, 401, response_time, "", "未提供认证信息", request_params)
788
+ return JSONResponse(
789
+ status_code=401,
790
+ content={"code": 401, "message": "未提供认证信息", "data": None}
791
+ )
792
+
793
+ # 验证 token
794
+ try:
795
+ user_info = auth_middleware_instance.client.get_user_info(token)
796
+ # 将用户信息存储到 request.state
797
+ request.state.user_info = user_info
798
+ # 设置上下文
799
+ set_current_user(dataclasses.asdict(user_info))
800
+ if user_info_callback:
801
+ await user_info_callback(request, user_info)
802
+ except AigcAuthError as e:
803
+ logger.error(f"AuthMiddleware认证失败: {e.message}")
804
+ response_time = time.time() - start_time
805
+ auth_middleware_instance._collect_stats(path, request.method, 401, response_time, token, e.message, request_params)
806
+ return JSONResponse(
807
+ status_code=401,
808
+ content={"code": e.code, "message": e.message, "data": None}
809
+ )
810
+
811
+ # 处理请求
812
+ try:
813
+ response = await call_next(request)
814
+ response_time = time.time() - start_time
815
+ auth_middleware_instance._collect_stats(path, request.method, response.status_code, response_time, token, None, request_params)
816
+ return response
817
+ except Exception as e:
818
+ response_time = time.time() - start_time
819
+ auth_middleware_instance._collect_stats(path, request.method, 500, response_time, token, str(e), request_params)
820
+ raise
821
+ finally:
822
+ clear_current_user()
823
+
824
+ return AuthHTTPMiddleware
825
+
729
826
  async def fastapi_middleware(self, request, call_next, user_info_callback: Callable = None):
730
827
  """
731
- FastAPI 中间件
828
+ FastAPI 中间件(旧方法,推荐使用 get_fastapi_middleware_class)
732
829
 
733
830
  使用方法:
734
831
  @app.middleware("http")
@@ -736,12 +833,14 @@ class AuthMiddleware:
736
833
  return await auth_middleware.fastapi_middleware(request, call_next)
737
834
  """
738
835
  from fastapi.responses import JSONResponse
836
+
837
+ # 处理代理头部,确保重定向(如果有)使用正确的协议
838
+ forwarded_proto = request.headers.get("x-forwarded-proto")
839
+ if forwarded_proto:
840
+ request.scope["scheme"] = forwarded_proto
739
841
 
740
842
  path = request.url.path
741
843
  start_time = time.time()
742
-
743
- # 收集请求参数
744
- request_params = await self._collect_fastapi_request_params(request) if self.enable_stats else None
745
844
 
746
845
  # 检查是否跳过
747
846
  if self._should_skip(path):
@@ -754,7 +853,9 @@ class AuthMiddleware:
754
853
  if not token:
755
854
  logger.warning("AuthMiddleware未提供认证信息")
756
855
  response_time = time.time() - start_time
757
- self._collect_stats(path, request.method, 401, response_time, "未提供认证信息", request_params)
856
+ # 收集请求参数
857
+ request_params = await self._collect_fastapi_request_params(request) if self.enable_stats else None
858
+ self._collect_stats(path, request.method, 401, response_time, "", "未提供认证信息", request_params)
758
859
  return JSONResponse(
759
860
  status_code=401,
760
861
  content={"code": 401, "message": "未提供认证信息", "data": None}
@@ -763,24 +864,18 @@ class AuthMiddleware:
763
864
  # 验证 token
764
865
  try:
765
866
  user_info = self.client.get_user_info(token)
766
- # 初始化统计收集器(第一次有token时)
767
- if self.enable_stats and self.stats_collector is None:
768
- self._init_stats_collector(token)
769
867
  # 将用户信息存储到 request.state
770
868
  request.state.user_info = user_info
771
869
  # 设置上下文
772
870
  set_current_user(dataclasses.asdict(user_info))
773
-
774
- # 处理代理头部,确保重定向(如果有)使用正确的协议
775
- forwarded_proto = request.headers.get("x-forwarded-proto")
776
- if forwarded_proto:
777
- request.scope["scheme"] = forwarded_proto
778
871
  if user_info_callback:
779
872
  await user_info_callback(request, user_info)
780
873
  except AigcAuthError as e:
781
874
  logger.error(f"AuthMiddleware认证失败: {e.message}")
782
875
  response_time = time.time() - start_time
783
- self._collect_stats(path, request.method, 401, response_time, e.message, request_params)
876
+ # 收集请求参数
877
+ request_params = await self._collect_fastapi_request_params(request) if self.enable_stats else None
878
+ self._collect_stats(path, request.method, 401, response_time, token, e.message, request_params)
784
879
  return JSONResponse(
785
880
  status_code=401,
786
881
  content={"code": e.code, "message": e.message, "data": None}
@@ -790,11 +885,15 @@ class AuthMiddleware:
790
885
  try:
791
886
  response = await call_next(request)
792
887
  response_time = time.time() - start_time
793
- self._collect_stats(path, request.method, response.status_code, response_time, None, request_params)
888
+ # 收集请求参数
889
+ request_params = await self._collect_fastapi_request_params(request) if self.enable_stats else None
890
+ self._collect_stats(path, request.method, response.status_code, response_time, token, None, request_params)
794
891
  return response
795
892
  except Exception as e:
893
+ # 收集请求参数
894
+ request_params = await self._collect_fastapi_request_params(request) if self.enable_stats else None
796
895
  response_time = time.time() - start_time
797
- self._collect_stats(path, request.method, 500, response_time, str(e), request_params)
896
+ self._collect_stats(path, request.method, 500, response_time, token, str(e), request_params)
798
897
  raise
799
898
  finally:
800
899
  clear_current_user()
@@ -828,7 +927,7 @@ class AuthMiddleware:
828
927
  if not token:
829
928
  logger.warning("AuthMiddleware未提供认证信息")
830
929
  response_time = time.time() - g.start_time
831
- self._collect_stats(path, request.method, 401, response_time, "未提供认证信息", g.request_params)
930
+ self._collect_stats(path, request.method, 401, response_time, "", "未提供认证信息", g.request_params)
832
931
  return jsonify({
833
932
  "code": 401,
834
933
  "message": "未提供认证信息",
@@ -838,9 +937,6 @@ class AuthMiddleware:
838
937
  # 验证 token
839
938
  try:
840
939
  user_info = self.client.get_user_info(token)
841
- # 初始化统计收集器(第一次有token时)
842
- if self.enable_stats and self.stats_collector is None:
843
- self._init_stats_collector(token)
844
940
  # 将用户信息存储到 flask.g
845
941
  g.user_info = user_info
846
942
  # 设置上下文
@@ -850,7 +946,7 @@ class AuthMiddleware:
850
946
  except AigcAuthError as e:
851
947
  logger.error(f"AuthMiddleware认证失败: {e.message}")
852
948
  response_time = time.time() - g.start_time
853
- self._collect_stats(path, request.method, 401, response_time, e.message, g.request_params)
949
+ self._collect_stats(path, request.method, 401, response_time, token, e.message, g.request_params)
854
950
  return jsonify({
855
951
  "code": e.code,
856
952
  "message": e.message,
@@ -876,11 +972,15 @@ class AuthMiddleware:
876
972
  if hasattr(g, 'start_time'):
877
973
  response_time = time.time() - g.start_time
878
974
  request_params = getattr(g, 'request_params', None)
975
+ # 从 request 的 Authorization header 获取 token
976
+ authorization = request.headers.get("Authorization")
977
+ token = self._extract_token(authorization) or ""
879
978
  self._collect_stats(
880
979
  request.path,
881
980
  request.method,
882
981
  response.status_code,
883
982
  response_time,
983
+ token,
884
984
  None,
885
985
  request_params
886
986
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: huace-aigc-auth-client
3
- Version: 1.1.21
3
+ Version: 1.1.28
4
4
  Summary: 华策AIGC Auth Client - 提供 Token 验证、用户信息获取、权限检查、旧系统接入等功能
5
5
  Author-email: Huace <support@huace.com>
6
6
  License: MIT
@@ -1,12 +1,12 @@
1
- huace_aigc_auth_client/__init__.py,sha256=E7sfAphd04F3WFwxnwnPvIUpIW7XQ36Anc4qvsuEo74,4630
2
- huace_aigc_auth_client/api_stats_collector.py,sha256=5qsxHjsx7rELsO4PSmrDYxXAWuJBfB0HrOI-PqTV52I,8698
1
+ huace_aigc_auth_client/__init__.py,sha256=nCpT58WU83NkEpdyMiq35t4Zd261cdp45N_07NRoisY,4630
2
+ huace_aigc_auth_client/api_stats_collector.py,sha256=UCUu2CHWsJyiAM58-XLQtfxWK0fjZ8TTdw90gv_Gx04,10347
3
3
  huace_aigc_auth_client/legacy_adapter.py,sha256=TVCBAKejE2z2HQFsEwDW8LMiaIkXNfz3Mxv6_E-UJFY,24102
4
- huace_aigc_auth_client/sdk.py,sha256=03OzVIhawoBXWNfq8AUfdUEPLlQMrBhfsNSYANOrnwg,31346
4
+ huace_aigc_auth_client/sdk.py,sha256=49UrQnS96a_m-FM9KVSqmE8x4J-R4qpYsE4KgzasvFE,36742
5
5
  huace_aigc_auth_client/user_context.py,sha256=KzevYLsLv1hv8rlvRw83FT-HugeoBJSJ1Pi56iLWyTE,5592
6
6
  huace_aigc_auth_client/webhook.py,sha256=XQZYEbMoqIdqZWCGSTcedeDKJpDbUVSq5g08g-6Qucg,4124
7
7
  huace_aigc_auth_client/webhook_flask.py,sha256=Iosu4dBtRhQZM_ytn-bn82MpVsyOiV28FBnt7Tfh31U,7225
8
- huace_aigc_auth_client-1.1.21.dist-info/licenses/LICENSE,sha256=z7dgC7KljhBLNvKjN15391nMj3aLt0gbud8-Yf1F8EQ,1063
9
- huace_aigc_auth_client-1.1.21.dist-info/METADATA,sha256=NNnIyfDXLdjYBv4Xr63AioZ8FSCv26RevMBA8VEUijg,23629
10
- huace_aigc_auth_client-1.1.21.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
11
- huace_aigc_auth_client-1.1.21.dist-info/top_level.txt,sha256=kbv0nQ6PQ0JVneWPH7O2AbtlJnP7AjvFJ6JjM6ZEBxo,23
12
- huace_aigc_auth_client-1.1.21.dist-info/RECORD,,
8
+ huace_aigc_auth_client-1.1.28.dist-info/licenses/LICENSE,sha256=z7dgC7KljhBLNvKjN15391nMj3aLt0gbud8-Yf1F8EQ,1063
9
+ huace_aigc_auth_client-1.1.28.dist-info/METADATA,sha256=9TkNkzidAbMP7nb2drl1FfSOAumh6ngs7ueiik40mnE,23629
10
+ huace_aigc_auth_client-1.1.28.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
11
+ huace_aigc_auth_client-1.1.28.dist-info/top_level.txt,sha256=kbv0nQ6PQ0JVneWPH7O2AbtlJnP7AjvFJ6JjM6ZEBxo,23
12
+ huace_aigc_auth_client-1.1.28.dist-info/RECORD,,