pixelarraythirdparty 1.2.3__py3-none-any.whl → 1.2.4__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.
- pixelarraythirdparty/__init__.py +1 -1
- pixelarraythirdparty/unified_login/unified_login.py +55 -399
- {pixelarraythirdparty-1.2.3.dist-info → pixelarraythirdparty-1.2.4.dist-info}/METADATA +1 -1
- {pixelarraythirdparty-1.2.3.dist-info → pixelarraythirdparty-1.2.4.dist-info}/RECORD +7 -7
- {pixelarraythirdparty-1.2.3.dist-info → pixelarraythirdparty-1.2.4.dist-info}/WHEEL +0 -0
- {pixelarraythirdparty-1.2.3.dist-info → pixelarraythirdparty-1.2.4.dist-info}/licenses/LICENSE +0 -0
- {pixelarraythirdparty-1.2.3.dist-info → pixelarraythirdparty-1.2.4.dist-info}/top_level.txt +0 -0
pixelarraythirdparty/__init__.py
CHANGED
|
@@ -9,57 +9,29 @@ from pixelarraythirdparty.client import AsyncClient
|
|
|
9
9
|
class OAuth2Login(AsyncClient):
|
|
10
10
|
"""
|
|
11
11
|
统一的 OAuth2 登录客户端基类
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
支持所有基于 OAuth2 的第三方登录(Google、微信、GitHub、GitLab、抖音等)
|
|
14
14
|
通过配置不同的端点来支持不同的提供商
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
使用示例:
|
|
17
17
|
```
|
|
18
18
|
# 服务端使用场景(推荐)
|
|
19
|
-
|
|
19
|
+
oauth2_login = OAuth2Login(
|
|
20
20
|
api_key="your_api_key",
|
|
21
|
-
provider="google"
|
|
21
|
+
provider="google" # 支持的提供商:google, wechat, github, gitlab, douyin
|
|
22
22
|
)
|
|
23
23
|
# 1. 获取授权URL
|
|
24
|
-
auth_data, success = await
|
|
24
|
+
auth_data, success = await oauth2_login.get_auth_url()
|
|
25
25
|
if success:
|
|
26
26
|
auth_url = auth_data.get("auth_url")
|
|
27
27
|
state = auth_data.get("state")
|
|
28
28
|
# 将auth_url返回给前端,让用户点击授权
|
|
29
29
|
# 2. 等待登录结果(在服务端轮询)
|
|
30
|
-
user_info, success = await
|
|
31
|
-
|
|
32
|
-
# 测试场景(仅用于本地测试,会打开浏览器)
|
|
33
|
-
google = OAuth2Login(
|
|
34
|
-
api_key="your_api_key",
|
|
35
|
-
provider="google"
|
|
36
|
-
)
|
|
37
|
-
user_info, success = await google.login()
|
|
38
|
-
|
|
39
|
-
# 微信PC端扫码登录
|
|
40
|
-
wechat = OAuth2Login(
|
|
41
|
-
api_key="your_api_key",
|
|
42
|
-
provider="wechat",
|
|
43
|
-
login_type="desktop"
|
|
44
|
-
)
|
|
45
|
-
user_info, success = await wechat.login()
|
|
46
|
-
|
|
47
|
-
# 微信手机端登录
|
|
48
|
-
wechat_mobile = OAuth2Login(
|
|
49
|
-
api_key="your_api_key",
|
|
50
|
-
provider="wechat",
|
|
51
|
-
login_type="mobile"
|
|
52
|
-
)
|
|
53
|
-
user_info, success = await wechat_mobile.login()
|
|
54
|
-
|
|
55
|
-
# 支持refresh_token的提供商(如Google、GitLab)
|
|
56
|
-
if user_info.get("refresh_token"):
|
|
57
|
-
token_data, success = await google.refresh_access_token(
|
|
58
|
-
user_info.get("refresh_token")
|
|
59
|
-
)
|
|
30
|
+
user_info, success = await oauth2_login.wait_for_login(state, timeout=180)
|
|
31
|
+
print(user_info, success)
|
|
60
32
|
```
|
|
61
33
|
"""
|
|
62
|
-
|
|
34
|
+
|
|
63
35
|
# 提供商端点映射
|
|
64
36
|
PROVIDER_ENDPOINTS = {
|
|
65
37
|
"google": {
|
|
@@ -89,13 +61,8 @@ class OAuth2Login(AsyncClient):
|
|
|
89
61
|
"wait_login": "/api/unified-login/douyin/wait-login",
|
|
90
62
|
},
|
|
91
63
|
}
|
|
92
|
-
|
|
93
|
-
def __init__(
|
|
94
|
-
self,
|
|
95
|
-
api_key: str,
|
|
96
|
-
provider: str,
|
|
97
|
-
login_type: Optional[str] = None
|
|
98
|
-
):
|
|
64
|
+
|
|
65
|
+
def __init__(self, api_key: str, provider: str, login_type: Optional[str] = None):
|
|
99
66
|
"""
|
|
100
67
|
description:
|
|
101
68
|
初始化OAuth2登录客户端
|
|
@@ -107,19 +74,19 @@ class OAuth2Login(AsyncClient):
|
|
|
107
74
|
super().__init__(api_key)
|
|
108
75
|
self.provider = provider.lower()
|
|
109
76
|
self.login_type = login_type
|
|
110
|
-
|
|
77
|
+
|
|
111
78
|
# 验证提供商是否支持
|
|
112
79
|
if self.provider not in self.PROVIDER_ENDPOINTS:
|
|
113
80
|
raise ValueError(
|
|
114
81
|
f"不支持的提供商: {provider}。"
|
|
115
82
|
f"支持的提供商: {', '.join(self.PROVIDER_ENDPOINTS.keys())}"
|
|
116
83
|
)
|
|
117
|
-
|
|
84
|
+
|
|
118
85
|
# 微信特殊处理:根据login_type选择不同的端点
|
|
119
86
|
if self.provider == "wechat":
|
|
120
87
|
if login_type == "mobile":
|
|
121
88
|
self.provider = "wechat-official"
|
|
122
|
-
|
|
89
|
+
|
|
123
90
|
def _get_endpoint(self, endpoint_type: str) -> str:
|
|
124
91
|
"""
|
|
125
92
|
description:
|
|
@@ -132,16 +99,14 @@ class OAuth2Login(AsyncClient):
|
|
|
132
99
|
endpoints = self.PROVIDER_ENDPOINTS.get(self.provider, {})
|
|
133
100
|
endpoint = endpoints.get(endpoint_type)
|
|
134
101
|
if not endpoint:
|
|
135
|
-
raise ValueError(
|
|
136
|
-
f"提供商 {self.provider} 不支持 {endpoint_type} 端点"
|
|
137
|
-
)
|
|
102
|
+
raise ValueError(f"提供商 {self.provider} 不支持 {endpoint_type} 端点")
|
|
138
103
|
return endpoint
|
|
139
|
-
|
|
104
|
+
|
|
140
105
|
async def get_auth_url(self) -> Tuple[Optional[Dict[str, str]], bool]:
|
|
141
106
|
"""
|
|
142
107
|
description:
|
|
143
108
|
获取OAuth授权URL(公共方法,供服务端调用)
|
|
144
|
-
|
|
109
|
+
|
|
145
110
|
服务端应该调用此方法获取授权URL,然后将URL返回给前端让用户点击授权。
|
|
146
111
|
获取到state后,使用wait_for_login方法等待登录结果。
|
|
147
112
|
return:
|
|
@@ -156,35 +121,12 @@ class OAuth2Login(AsyncClient):
|
|
|
156
121
|
if not auth_url:
|
|
157
122
|
return None, False
|
|
158
123
|
return data, True
|
|
159
|
-
|
|
160
|
-
def
|
|
161
|
-
"""
|
|
162
|
-
description:
|
|
163
|
-
从授权URL中提取state参数
|
|
164
|
-
parameters:
|
|
165
|
-
auth_url(str, optional): 授权URL
|
|
166
|
-
return:
|
|
167
|
-
state(str, optional): state参数值,如果提取失败则返回None
|
|
168
|
-
"""
|
|
169
|
-
if not auth_url:
|
|
170
|
-
return None
|
|
171
|
-
try:
|
|
172
|
-
parsed = urllib.parse.urlparse(auth_url)
|
|
173
|
-
query = urllib.parse.parse_qs(parsed.query)
|
|
174
|
-
values = query.get("state")
|
|
175
|
-
if values:
|
|
176
|
-
return values[0]
|
|
177
|
-
except Exception:
|
|
178
|
-
return None
|
|
179
|
-
return None
|
|
180
|
-
|
|
181
|
-
async def wait_for_login(
|
|
182
|
-
self, state: str, timeout: int = 180
|
|
183
|
-
) -> Tuple[Dict, bool]:
|
|
124
|
+
|
|
125
|
+
async def wait_for_login(self, state: str, timeout: int = 180) -> Tuple[Dict, bool]:
|
|
184
126
|
"""
|
|
185
127
|
description:
|
|
186
128
|
等待登录结果,轮询检查登录状态(公共方法,供服务端调用)
|
|
187
|
-
|
|
129
|
+
|
|
188
130
|
服务端在用户点击授权后,调用此方法轮询等待登录结果。
|
|
189
131
|
此方法会持续轮询直到登录成功或超时。
|
|
190
132
|
parameters:
|
|
@@ -197,56 +139,29 @@ class OAuth2Login(AsyncClient):
|
|
|
197
139
|
interval = 2
|
|
198
140
|
total_checks = max(1, timeout // interval) if timeout > 0 else 1
|
|
199
141
|
endpoint = self._get_endpoint("wait_login")
|
|
200
|
-
|
|
142
|
+
|
|
201
143
|
for _ in range(total_checks):
|
|
202
144
|
status, response = await self._request_raw(
|
|
203
145
|
"POST",
|
|
204
146
|
endpoint,
|
|
205
147
|
json={"state": state},
|
|
206
148
|
)
|
|
207
|
-
|
|
149
|
+
|
|
208
150
|
if status == 200 and response.get("success") is True:
|
|
209
151
|
return response.get("data", {}), True
|
|
210
|
-
|
|
152
|
+
|
|
211
153
|
if status in (400, 408):
|
|
212
154
|
break
|
|
213
|
-
|
|
155
|
+
|
|
214
156
|
await asyncio.sleep(interval)
|
|
215
|
-
|
|
157
|
+
|
|
216
158
|
return {}, False
|
|
217
|
-
|
|
218
|
-
async def login(self, timeout: int = 180) -> Tuple[Dict, bool]:
|
|
219
|
-
"""
|
|
220
|
-
description:
|
|
221
|
-
仿 Supabase CLI 的一键登录流程:打开浏览器完成授权,终端端轮询等待登录结果
|
|
222
|
-
|
|
223
|
-
注意:此方法仅用于本地测试场景,会自动打开浏览器。
|
|
224
|
-
在生产环境的服务端场景中,应该使用get_auth_url和wait_for_login方法。
|
|
225
|
-
parameters:
|
|
226
|
-
timeout(int, optional): 等待用户完成授权的超时时间(秒),默认为180
|
|
227
|
-
return:
|
|
228
|
-
user_info(dict): 用户信息字典
|
|
229
|
-
success(bool): 是否成功
|
|
230
|
-
"""
|
|
231
|
-
auth_data, success = await self.get_auth_url()
|
|
232
|
-
if not success or not auth_data:
|
|
233
|
-
return {}, False
|
|
234
|
-
|
|
235
|
-
auth_url = auth_data.get("auth_url")
|
|
236
|
-
state = auth_data.get("state") or self._extract_state(auth_url)
|
|
237
|
-
|
|
238
|
-
if not auth_url or not state:
|
|
239
|
-
return {}, False
|
|
240
|
-
|
|
241
|
-
webbrowser.open(auth_url, new=2)
|
|
242
|
-
|
|
243
|
-
return await self.wait_for_login(state, timeout)
|
|
244
|
-
|
|
159
|
+
|
|
245
160
|
async def refresh_access_token(self, refresh_token: str) -> Tuple[Dict, bool]:
|
|
246
161
|
"""
|
|
247
162
|
description:
|
|
248
163
|
使用refresh_token刷新access_token(仅支持Google和GitLab)
|
|
249
|
-
|
|
164
|
+
|
|
250
165
|
注意:GitLab 采用 token rotation(令牌轮换)机制,每次刷新时会返回新的 refresh_token,
|
|
251
166
|
旧的 refresh_token 会立即失效。必须保存新的 refresh_token 并替换旧的。
|
|
252
167
|
Google 也可能在某些情况下返回新的 refresh_token。
|
|
@@ -260,7 +175,7 @@ class OAuth2Login(AsyncClient):
|
|
|
260
175
|
endpoint = self._get_endpoint("refresh_token")
|
|
261
176
|
except ValueError:
|
|
262
177
|
return {}, False
|
|
263
|
-
|
|
178
|
+
|
|
264
179
|
data, success = await self._request(
|
|
265
180
|
"POST",
|
|
266
181
|
endpoint,
|
|
@@ -290,16 +205,6 @@ class GoogleLogin(AsyncClient):
|
|
|
290
205
|
if success:
|
|
291
206
|
access_token = user_info.get("access_token")
|
|
292
207
|
refresh_token = user_info.get("refresh_token")
|
|
293
|
-
|
|
294
|
-
# 使用refresh_token刷新access_token
|
|
295
|
-
if refresh_token:
|
|
296
|
-
token_data, success = await google.refresh_access_token(refresh_token)
|
|
297
|
-
if success:
|
|
298
|
-
new_access_token = token_data.get("access_token")
|
|
299
|
-
|
|
300
|
-
# 测试场景(仅用于本地测试,会打开浏览器)
|
|
301
|
-
google = GoogleLogin(api_key="your_api_key")
|
|
302
|
-
user_info, success = await google.login()
|
|
303
208
|
```
|
|
304
209
|
"""
|
|
305
210
|
|
|
@@ -310,7 +215,7 @@ class GoogleLogin(AsyncClient):
|
|
|
310
215
|
"""
|
|
311
216
|
description:
|
|
312
217
|
获取Google OAuth授权URL(公共方法,供服务端调用)
|
|
313
|
-
|
|
218
|
+
|
|
314
219
|
服务端应该调用此方法获取授权URL,然后将URL返回给前端让用户点击授权。
|
|
315
220
|
获取到state后,使用wait_for_login方法等待登录结果。
|
|
316
221
|
return:
|
|
@@ -327,53 +232,11 @@ class GoogleLogin(AsyncClient):
|
|
|
327
232
|
return None, False
|
|
328
233
|
return data, True
|
|
329
234
|
|
|
330
|
-
async def
|
|
331
|
-
"""
|
|
332
|
-
description:
|
|
333
|
-
仿 Supabase CLI 的一键登录流程:打开浏览器完成授权,终端端轮询等待登录结果
|
|
334
|
-
|
|
335
|
-
注意:此方法仅用于本地测试场景,会自动打开浏览器。
|
|
336
|
-
在生产环境的服务端场景中,应该使用get_auth_url和wait_for_login方法。
|
|
337
|
-
parameters:
|
|
338
|
-
timeout(int, optional): 等待用户完成授权的超时时间(秒),默认为180
|
|
339
|
-
return:
|
|
340
|
-
user_info(dict): 用户信息字典
|
|
341
|
-
success(bool): 是否成功
|
|
342
|
-
"""
|
|
343
|
-
auth_data, success = await self.get_auth_url()
|
|
344
|
-
if not success or not auth_data:
|
|
345
|
-
return {}, False
|
|
346
|
-
|
|
347
|
-
auth_url = auth_data.get("auth_url")
|
|
348
|
-
state = auth_data.get("state") or self._extract_state(auth_url)
|
|
349
|
-
|
|
350
|
-
if not auth_url or not state:
|
|
351
|
-
return {}, False
|
|
352
|
-
|
|
353
|
-
webbrowser.open(auth_url, new=2)
|
|
354
|
-
|
|
355
|
-
return await self.wait_for_login(state, timeout)
|
|
356
|
-
|
|
357
|
-
def _extract_state(self, auth_url: Optional[str]) -> Optional[str]:
|
|
358
|
-
if not auth_url:
|
|
359
|
-
return None
|
|
360
|
-
try:
|
|
361
|
-
parsed = urllib.parse.urlparse(auth_url)
|
|
362
|
-
query = urllib.parse.parse_qs(parsed.query)
|
|
363
|
-
values = query.get("state")
|
|
364
|
-
if values:
|
|
365
|
-
return values[0]
|
|
366
|
-
except Exception:
|
|
367
|
-
return None
|
|
368
|
-
return None
|
|
369
|
-
|
|
370
|
-
async def wait_for_login(
|
|
371
|
-
self, state: str, timeout: int = 180
|
|
372
|
-
) -> Tuple[Dict, bool]:
|
|
235
|
+
async def wait_for_login(self, state: str, timeout: int = 180) -> Tuple[Dict, bool]:
|
|
373
236
|
"""
|
|
374
237
|
description:
|
|
375
238
|
等待Google登录结果,轮询检查登录状态(公共方法,供服务端调用)
|
|
376
|
-
|
|
239
|
+
|
|
377
240
|
服务端在用户点击授权后,调用此方法轮询等待登录结果。
|
|
378
241
|
此方法会持续轮询直到登录成功或超时。
|
|
379
242
|
parameters:
|
|
@@ -426,7 +289,7 @@ class GoogleLogin(AsyncClient):
|
|
|
426
289
|
class WechatLogin(AsyncClient):
|
|
427
290
|
"""
|
|
428
291
|
微信 OAuth2 登录客户端
|
|
429
|
-
|
|
292
|
+
|
|
430
293
|
支持两种登录方式:
|
|
431
294
|
- desktop: PC端扫码登录(使用微信开放平台)
|
|
432
295
|
- mobile: 微信公众号OAuth登录(手机端微信内打开)
|
|
@@ -443,7 +306,8 @@ class WechatLogin(AsyncClient):
|
|
|
443
306
|
# 将auth_url返回给前端,让用户扫码授权
|
|
444
307
|
# 等待登录结果
|
|
445
308
|
user_info, success = await wechat.wait_for_login(state, timeout=180, login_type="desktop")
|
|
446
|
-
|
|
309
|
+
print(user_info, success)
|
|
310
|
+
|
|
447
311
|
# 微信公众号登录(手机端)
|
|
448
312
|
auth_data, success = await wechat.get_auth_url(login_type="mobile")
|
|
449
313
|
if success:
|
|
@@ -452,21 +316,20 @@ class WechatLogin(AsyncClient):
|
|
|
452
316
|
# 将auth_url返回给前端,让用户在微信内打开授权
|
|
453
317
|
# 等待登录结果
|
|
454
318
|
user_info, success = await wechat.wait_for_login(state, timeout=180, login_type="mobile")
|
|
455
|
-
|
|
456
|
-
# 测试场景(仅用于本地测试,会打开浏览器)
|
|
457
|
-
wechat = WechatLogin(api_key="your_api_key")
|
|
458
|
-
user_info, success = await wechat.login(login_type="desktop")
|
|
319
|
+
print(user_info, success)
|
|
459
320
|
```
|
|
460
321
|
"""
|
|
461
322
|
|
|
462
323
|
def __init__(self, api_key: str):
|
|
463
324
|
super().__init__(api_key)
|
|
464
325
|
|
|
465
|
-
async def get_auth_url(
|
|
326
|
+
async def get_auth_url(
|
|
327
|
+
self, login_type: str = "desktop"
|
|
328
|
+
) -> Tuple[Optional[Dict[str, str]], bool]:
|
|
466
329
|
"""
|
|
467
330
|
description:
|
|
468
331
|
获取微信授权URL(公共方法,供服务端调用)
|
|
469
|
-
|
|
332
|
+
|
|
470
333
|
服务端应该调用此方法获取授权URL,然后将URL返回给前端让用户点击授权。
|
|
471
334
|
获取到state后,使用wait_for_login方法等待登录结果。
|
|
472
335
|
parameters:
|
|
@@ -481,7 +344,7 @@ class WechatLogin(AsyncClient):
|
|
|
481
344
|
else:
|
|
482
345
|
# PC端扫码登录
|
|
483
346
|
endpoint = "/api/unified-login/wechat/auth-url"
|
|
484
|
-
|
|
347
|
+
|
|
485
348
|
data, success = await self._request("POST", endpoint)
|
|
486
349
|
if not success:
|
|
487
350
|
return None, False
|
|
@@ -490,62 +353,13 @@ class WechatLogin(AsyncClient):
|
|
|
490
353
|
return None, False
|
|
491
354
|
return data, True
|
|
492
355
|
|
|
493
|
-
async def login(self, login_type: str = "desktop", timeout: int = 180) -> Tuple[Dict, bool]:
|
|
494
|
-
"""
|
|
495
|
-
description:
|
|
496
|
-
仿 Supabase CLI 的一键登录流程:打开浏览器完成授权,终端端轮询等待登录结果
|
|
497
|
-
|
|
498
|
-
注意:此方法仅用于本地测试场景,会自动打开浏览器。
|
|
499
|
-
在生产环境的服务端场景中,应该使用get_auth_url和wait_for_login方法。
|
|
500
|
-
parameters:
|
|
501
|
-
login_type(str, optional): 登录类型,desktop表示PC端扫码登录,mobile表示微信公众号登录,默认为desktop
|
|
502
|
-
timeout(int, optional): 等待用户完成授权的超时时间(秒),默认为180
|
|
503
|
-
return:
|
|
504
|
-
user_info(dict): 用户信息字典
|
|
505
|
-
success(bool): 是否成功
|
|
506
|
-
"""
|
|
507
|
-
auth_data, success = await self.get_auth_url(login_type)
|
|
508
|
-
if not success or not auth_data:
|
|
509
|
-
return {}, False
|
|
510
|
-
|
|
511
|
-
auth_url = auth_data.get("auth_url")
|
|
512
|
-
state = auth_data.get("state") or self._extract_state(auth_url)
|
|
513
|
-
|
|
514
|
-
if not auth_url or not state:
|
|
515
|
-
return {}, False
|
|
516
|
-
|
|
517
|
-
webbrowser.open(auth_url, new=2)
|
|
518
|
-
|
|
519
|
-
return await self.wait_for_login(state, timeout, login_type)
|
|
520
|
-
|
|
521
|
-
def _extract_state(self, auth_url: Optional[str]) -> Optional[str]:
|
|
522
|
-
"""
|
|
523
|
-
description:
|
|
524
|
-
从授权URL中提取state参数
|
|
525
|
-
parameters:
|
|
526
|
-
auth_url(str, optional): 授权URL
|
|
527
|
-
return:
|
|
528
|
-
state(str, optional): state参数值,如果提取失败则返回None
|
|
529
|
-
"""
|
|
530
|
-
if not auth_url:
|
|
531
|
-
return None
|
|
532
|
-
try:
|
|
533
|
-
parsed = urllib.parse.urlparse(auth_url)
|
|
534
|
-
query = urllib.parse.parse_qs(parsed.query)
|
|
535
|
-
values = query.get("state")
|
|
536
|
-
if values:
|
|
537
|
-
return values[0]
|
|
538
|
-
except Exception:
|
|
539
|
-
return None
|
|
540
|
-
return None
|
|
541
|
-
|
|
542
356
|
async def wait_for_login(
|
|
543
357
|
self, state: str, timeout: int = 180, login_type: str = "desktop"
|
|
544
358
|
) -> Tuple[Dict, bool]:
|
|
545
359
|
"""
|
|
546
360
|
description:
|
|
547
361
|
等待微信登录结果,轮询检查登录状态(公共方法,供服务端调用)
|
|
548
|
-
|
|
362
|
+
|
|
549
363
|
服务端在用户点击授权后,调用此方法轮询等待登录结果。
|
|
550
364
|
此方法会持续轮询直到登录成功或超时。
|
|
551
365
|
parameters:
|
|
@@ -599,10 +413,7 @@ class GitHubLogin(AsyncClient):
|
|
|
599
413
|
# 将auth_url返回给前端,让用户点击授权
|
|
600
414
|
# 2. 等待登录结果(在服务端轮询)
|
|
601
415
|
user_info, success = await github.wait_for_login(state, timeout=180)
|
|
602
|
-
|
|
603
|
-
# 测试场景(仅用于本地测试,会打开浏览器)
|
|
604
|
-
github = GitHubLogin(api_key="your_api_key")
|
|
605
|
-
user_info, success = await github.login()
|
|
416
|
+
print(user_info, success)
|
|
606
417
|
```
|
|
607
418
|
"""
|
|
608
419
|
|
|
@@ -613,7 +424,7 @@ class GitHubLogin(AsyncClient):
|
|
|
613
424
|
"""
|
|
614
425
|
description:
|
|
615
426
|
获取GitHub OAuth授权URL(公共方法,供服务端调用)
|
|
616
|
-
|
|
427
|
+
|
|
617
428
|
服务端应该调用此方法获取授权URL,然后将URL返回给前端让用户点击授权。
|
|
618
429
|
获取到state后,使用wait_for_login方法等待登录结果。
|
|
619
430
|
return:
|
|
@@ -630,61 +441,11 @@ class GitHubLogin(AsyncClient):
|
|
|
630
441
|
return None, False
|
|
631
442
|
return data, True
|
|
632
443
|
|
|
633
|
-
async def
|
|
634
|
-
"""
|
|
635
|
-
description:
|
|
636
|
-
仿 Supabase CLI 的一键登录流程:打开浏览器完成授权,终端端轮询等待登录结果
|
|
637
|
-
|
|
638
|
-
注意:此方法仅用于本地测试场景,会自动打开浏览器。
|
|
639
|
-
在生产环境的服务端场景中,应该使用get_auth_url和wait_for_login方法。
|
|
640
|
-
parameters:
|
|
641
|
-
timeout(int, optional): 等待用户完成授权的超时时间(秒),默认为180
|
|
642
|
-
return:
|
|
643
|
-
user_info(dict): 用户信息字典
|
|
644
|
-
success(bool): 是否成功
|
|
645
|
-
"""
|
|
646
|
-
auth_data, success = await self.get_auth_url()
|
|
647
|
-
if not success or not auth_data:
|
|
648
|
-
return {}, False
|
|
649
|
-
|
|
650
|
-
auth_url = auth_data.get("auth_url")
|
|
651
|
-
state = auth_data.get("state") or self._extract_state(auth_url)
|
|
652
|
-
|
|
653
|
-
if not auth_url or not state:
|
|
654
|
-
return {}, False
|
|
655
|
-
|
|
656
|
-
webbrowser.open(auth_url, new=2)
|
|
657
|
-
|
|
658
|
-
return await self.wait_for_login(state, timeout)
|
|
659
|
-
|
|
660
|
-
def _extract_state(self, auth_url: Optional[str]) -> Optional[str]:
|
|
661
|
-
"""
|
|
662
|
-
description:
|
|
663
|
-
从授权URL中提取state参数
|
|
664
|
-
parameters:
|
|
665
|
-
auth_url(str, optional): 授权URL
|
|
666
|
-
return:
|
|
667
|
-
state(str, optional): state参数值,如果提取失败则返回None
|
|
668
|
-
"""
|
|
669
|
-
if not auth_url:
|
|
670
|
-
return None
|
|
671
|
-
try:
|
|
672
|
-
parsed = urllib.parse.urlparse(auth_url)
|
|
673
|
-
query = urllib.parse.parse_qs(parsed.query)
|
|
674
|
-
values = query.get("state")
|
|
675
|
-
if values:
|
|
676
|
-
return values[0]
|
|
677
|
-
except Exception:
|
|
678
|
-
return None
|
|
679
|
-
return None
|
|
680
|
-
|
|
681
|
-
async def wait_for_login(
|
|
682
|
-
self, state: str, timeout: int = 180
|
|
683
|
-
) -> Tuple[Dict, bool]:
|
|
444
|
+
async def wait_for_login(self, state: str, timeout: int = 180) -> Tuple[Dict, bool]:
|
|
684
445
|
"""
|
|
685
446
|
description:
|
|
686
447
|
等待GitHub登录结果,轮询检查登录状态(公共方法,供服务端调用)
|
|
687
|
-
|
|
448
|
+
|
|
688
449
|
服务端在用户点击授权后,调用此方法轮询等待登录结果。
|
|
689
450
|
此方法会持续轮询直到登录成功或超时。
|
|
690
451
|
parameters:
|
|
@@ -731,10 +492,7 @@ class DouyinLogin(AsyncClient):
|
|
|
731
492
|
# 将auth_url返回给前端,让用户点击授权
|
|
732
493
|
# 2. 等待登录结果(在服务端轮询)
|
|
733
494
|
user_info, success = await douyin.wait_for_login(state, timeout=180)
|
|
734
|
-
|
|
735
|
-
# 测试场景(仅用于本地测试,会打开浏览器)
|
|
736
|
-
douyin = DouyinLogin(api_key="your_api_key")
|
|
737
|
-
user_info, success = await douyin.login()
|
|
495
|
+
print(user_info, success)
|
|
738
496
|
```
|
|
739
497
|
"""
|
|
740
498
|
|
|
@@ -745,7 +503,7 @@ class DouyinLogin(AsyncClient):
|
|
|
745
503
|
"""
|
|
746
504
|
description:
|
|
747
505
|
获取抖音OAuth授权URL(公共方法,供服务端调用)
|
|
748
|
-
|
|
506
|
+
|
|
749
507
|
服务端应该调用此方法获取授权URL,然后将URL返回给前端让用户点击授权。
|
|
750
508
|
获取到state后,使用wait_for_login方法等待登录结果。
|
|
751
509
|
return:
|
|
@@ -762,61 +520,11 @@ class DouyinLogin(AsyncClient):
|
|
|
762
520
|
return None, False
|
|
763
521
|
return data, True
|
|
764
522
|
|
|
765
|
-
async def
|
|
766
|
-
"""
|
|
767
|
-
description:
|
|
768
|
-
仿 Supabase CLI 的一键登录流程:打开浏览器完成授权,终端端轮询等待登录结果
|
|
769
|
-
|
|
770
|
-
注意:此方法仅用于本地测试场景,会自动打开浏览器。
|
|
771
|
-
在生产环境的服务端场景中,应该使用get_auth_url和wait_for_login方法。
|
|
772
|
-
parameters:
|
|
773
|
-
timeout(int, optional): 等待用户完成授权的超时时间(秒),默认为180
|
|
774
|
-
return:
|
|
775
|
-
user_info(dict): 用户信息字典
|
|
776
|
-
success(bool): 是否成功
|
|
777
|
-
"""
|
|
778
|
-
auth_data, success = await self.get_auth_url()
|
|
779
|
-
if not success or not auth_data:
|
|
780
|
-
return {}, False
|
|
781
|
-
|
|
782
|
-
auth_url = auth_data.get("auth_url")
|
|
783
|
-
state = auth_data.get("state") or self._extract_state(auth_url)
|
|
784
|
-
|
|
785
|
-
if not auth_url or not state:
|
|
786
|
-
return {}, False
|
|
787
|
-
|
|
788
|
-
webbrowser.open(auth_url, new=2)
|
|
789
|
-
|
|
790
|
-
return await self.wait_for_login(state, timeout)
|
|
791
|
-
|
|
792
|
-
def _extract_state(self, auth_url: Optional[str]) -> Optional[str]:
|
|
793
|
-
"""
|
|
794
|
-
description:
|
|
795
|
-
从授权URL中提取state参数
|
|
796
|
-
parameters:
|
|
797
|
-
auth_url(str, optional): 授权URL
|
|
798
|
-
return:
|
|
799
|
-
state(str, optional): state参数值,如果提取失败则返回None
|
|
800
|
-
"""
|
|
801
|
-
if not auth_url:
|
|
802
|
-
return None
|
|
803
|
-
try:
|
|
804
|
-
parsed = urllib.parse.urlparse(auth_url)
|
|
805
|
-
query = urllib.parse.parse_qs(parsed.query)
|
|
806
|
-
values = query.get("state")
|
|
807
|
-
if values:
|
|
808
|
-
return values[0]
|
|
809
|
-
except Exception:
|
|
810
|
-
return None
|
|
811
|
-
return None
|
|
812
|
-
|
|
813
|
-
async def wait_for_login(
|
|
814
|
-
self, state: str, timeout: int = 180
|
|
815
|
-
) -> Tuple[Dict, bool]:
|
|
523
|
+
async def wait_for_login(self, state: str, timeout: int = 180) -> Tuple[Dict, bool]:
|
|
816
524
|
"""
|
|
817
525
|
description:
|
|
818
526
|
等待抖音登录结果,轮询检查登录状态(公共方法,供服务端调用)
|
|
819
|
-
|
|
527
|
+
|
|
820
528
|
服务端在用户点击授权后,调用此方法轮询等待登录结果。
|
|
821
529
|
此方法会持续轮询直到登录成功或超时。
|
|
822
530
|
parameters:
|
|
@@ -863,13 +571,7 @@ class GitLabLogin(AsyncClient):
|
|
|
863
571
|
# 将auth_url返回给前端,让用户点击授权
|
|
864
572
|
# 2. 等待登录结果(在服务端轮询)
|
|
865
573
|
user_info, success = await gitlab.wait_for_login(state, timeout=180)
|
|
866
|
-
|
|
867
|
-
# 使用refresh_token刷新access_token
|
|
868
|
-
token_data, success = await gitlab.refresh_access_token(user_info.get("refresh_token"))
|
|
869
|
-
|
|
870
|
-
# 测试场景(仅用于本地测试,会打开浏览器)
|
|
871
|
-
gitlab = GitLabLogin(api_key="your_api_key")
|
|
872
|
-
user_info, success = await gitlab.login()
|
|
574
|
+
print(user_info, success)
|
|
873
575
|
```
|
|
874
576
|
"""
|
|
875
577
|
|
|
@@ -880,7 +582,7 @@ class GitLabLogin(AsyncClient):
|
|
|
880
582
|
"""
|
|
881
583
|
description:
|
|
882
584
|
获取GitLab OAuth授权URL(公共方法,供服务端调用)
|
|
883
|
-
|
|
585
|
+
|
|
884
586
|
服务端应该调用此方法获取授权URL,然后将URL返回给前端让用户点击授权。
|
|
885
587
|
获取到state后,使用wait_for_login方法等待登录结果。
|
|
886
588
|
return:
|
|
@@ -897,53 +599,11 @@ class GitLabLogin(AsyncClient):
|
|
|
897
599
|
return None, False
|
|
898
600
|
return data, True
|
|
899
601
|
|
|
900
|
-
async def
|
|
901
|
-
"""
|
|
902
|
-
description:
|
|
903
|
-
仿 Supabase CLI 的一键登录流程:打开浏览器完成授权,终端端轮询等待登录结果
|
|
904
|
-
|
|
905
|
-
注意:此方法仅用于本地测试场景,会自动打开浏览器。
|
|
906
|
-
在生产环境的服务端场景中,应该使用get_auth_url和wait_for_login方法。
|
|
907
|
-
parameters:
|
|
908
|
-
timeout(int, optional): 等待用户完成授权的超时时间(秒),默认为180
|
|
909
|
-
return:
|
|
910
|
-
user_info(dict): 用户信息字典
|
|
911
|
-
success(bool): 是否成功
|
|
912
|
-
"""
|
|
913
|
-
auth_data, success = await self.get_auth_url()
|
|
914
|
-
if not success or not auth_data:
|
|
915
|
-
return {}, False
|
|
916
|
-
|
|
917
|
-
auth_url = auth_data.get("auth_url")
|
|
918
|
-
state = auth_data.get("state") or self._extract_state(auth_url)
|
|
919
|
-
|
|
920
|
-
if not auth_url or not state:
|
|
921
|
-
return {}, False
|
|
922
|
-
|
|
923
|
-
webbrowser.open(auth_url, new=2)
|
|
924
|
-
|
|
925
|
-
return await self.wait_for_login(state, timeout)
|
|
926
|
-
|
|
927
|
-
def _extract_state(self, auth_url: Optional[str]) -> Optional[str]:
|
|
928
|
-
if not auth_url:
|
|
929
|
-
return None
|
|
930
|
-
try:
|
|
931
|
-
parsed = urllib.parse.urlparse(auth_url)
|
|
932
|
-
query = urllib.parse.parse_qs(parsed.query)
|
|
933
|
-
values = query.get("state")
|
|
934
|
-
if values:
|
|
935
|
-
return values[0]
|
|
936
|
-
except Exception:
|
|
937
|
-
return None
|
|
938
|
-
return None
|
|
939
|
-
|
|
940
|
-
async def wait_for_login(
|
|
941
|
-
self, state: str, timeout: int = 180
|
|
942
|
-
) -> Tuple[Dict, bool]:
|
|
602
|
+
async def wait_for_login(self, state: str, timeout: int = 180) -> Tuple[Dict, bool]:
|
|
943
603
|
"""
|
|
944
604
|
description:
|
|
945
605
|
等待GitLab登录结果,轮询检查登录状态(公共方法,供服务端调用)
|
|
946
|
-
|
|
606
|
+
|
|
947
607
|
服务端在用户点击授权后,调用此方法轮询等待登录结果。
|
|
948
608
|
此方法会持续轮询直到登录成功或超时。
|
|
949
609
|
parameters:
|
|
@@ -977,7 +637,7 @@ class GitLabLogin(AsyncClient):
|
|
|
977
637
|
"""
|
|
978
638
|
description:
|
|
979
639
|
使用refresh_token刷新access_token
|
|
980
|
-
|
|
640
|
+
|
|
981
641
|
注意:GitLab 采用 token rotation(令牌轮换)机制,每次刷新时会返回新的 refresh_token,
|
|
982
642
|
旧的 refresh_token 会立即失效。必须保存新的 refresh_token 并替换旧的。
|
|
983
643
|
parameters:
|
|
@@ -1041,9 +701,7 @@ class SMSLogin(AsyncClient):
|
|
|
1041
701
|
)
|
|
1042
702
|
return bool(success)
|
|
1043
703
|
|
|
1044
|
-
async def _wait_for_login(
|
|
1045
|
-
self, phone: str, timeout: int
|
|
1046
|
-
) -> Tuple[Dict, bool]:
|
|
704
|
+
async def _wait_for_login(self, phone: str, timeout: int) -> Tuple[Dict, bool]:
|
|
1047
705
|
"""
|
|
1048
706
|
等待短信登录结果
|
|
1049
707
|
|
|
@@ -1133,9 +791,7 @@ class EmailLogin(AsyncClient):
|
|
|
1133
791
|
)
|
|
1134
792
|
return bool(success)
|
|
1135
793
|
|
|
1136
|
-
async def _wait_for_login(
|
|
1137
|
-
self, email: str, timeout: int
|
|
1138
|
-
) -> Tuple[Dict, bool]:
|
|
794
|
+
async def _wait_for_login(self, email: str, timeout: int) -> Tuple[Dict, bool]:
|
|
1139
795
|
"""
|
|
1140
796
|
等待邮箱登录结果
|
|
1141
797
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
pixelarraythirdparty/__init__.py,sha256=
|
|
1
|
+
pixelarraythirdparty/__init__.py,sha256=ZPQ3uKaT_Scaf4p_JVcXXb90OdZ01QLEkXh57BGGnuY,582
|
|
2
2
|
pixelarraythirdparty/client.py,sha256=Ym_IZ6cdoZJoMKW61ZRTfQ81-Hay5dpLTgExoANZwI4,3171
|
|
3
3
|
pixelarraythirdparty/cron/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
pixelarraythirdparty/cron/cron.py,sha256=nv4e2hX_UkEJ-kbEARbInU2J6aREyYZ61dZ-4b9UWJI,3146
|
|
@@ -11,11 +11,11 @@ pixelarraythirdparty/product/product.py,sha256=5fgv2Ck860epYXxipY83vePziubCIlocF
|
|
|
11
11
|
pixelarraythirdparty/project/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
12
|
pixelarraythirdparty/project/project.py,sha256=a8swjckyn4y3NlIx0-tMbcRwDY9woOijGi7lb1wN7Ok,2455
|
|
13
13
|
pixelarraythirdparty/unified_login/__init__.py,sha256=tzy3nmRv-qZID-6kYRFarqQVrmkUsEI7D6de_PFu7Tg,327
|
|
14
|
-
pixelarraythirdparty/unified_login/unified_login.py,sha256=
|
|
14
|
+
pixelarraythirdparty/unified_login/unified_login.py,sha256=chpB8Yj6yoPrh821LRe4BbDlLkK4Kn9n5X6FBiINUp0,29007
|
|
15
15
|
pixelarraythirdparty/user/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
16
|
pixelarraythirdparty/user/user.py,sha256=SqufSAVMxQElz-NqtlOZs_dG7UtbuE-ygLAyml9oKpY,5761
|
|
17
|
-
pixelarraythirdparty-1.2.
|
|
18
|
-
pixelarraythirdparty-1.2.
|
|
19
|
-
pixelarraythirdparty-1.2.
|
|
20
|
-
pixelarraythirdparty-1.2.
|
|
21
|
-
pixelarraythirdparty-1.2.
|
|
17
|
+
pixelarraythirdparty-1.2.4.dist-info/licenses/LICENSE,sha256=O-g1dUr0U50rSIvmWE9toiVkSgFpVt72_MHITbWvAqA,1067
|
|
18
|
+
pixelarraythirdparty-1.2.4.dist-info/METADATA,sha256=2m92kvoPsCInrZsLpS094MxadQzGipcKcYtlEqa6GHY,993
|
|
19
|
+
pixelarraythirdparty-1.2.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
20
|
+
pixelarraythirdparty-1.2.4.dist-info/top_level.txt,sha256=dzG2Ut8j7noUqj_0ZQjcIDAeHYCh_9WtlxjAxtoyufo,21
|
|
21
|
+
pixelarraythirdparty-1.2.4.dist-info/RECORD,,
|
|
File without changes
|
{pixelarraythirdparty-1.2.3.dist-info → pixelarraythirdparty-1.2.4.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|