pixelarraythirdparty 1.1.2__tar.gz → 1.1.4__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.
- {pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/PKG-INFO +1 -1
- {pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/pixelarraythirdparty/__init__.py +1 -1
- {pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/pixelarraythirdparty/unified_login/unified_login.py +210 -19
- {pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/pixelarraythirdparty.egg-info/PKG-INFO +1 -1
- {pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/pyproject.toml +1 -1
- {pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/LICENSE +0 -0
- {pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/MANIFEST.in +0 -0
- {pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/pixelarraythirdparty/client.py +0 -0
- {pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/pixelarraythirdparty/cron/__init__.py +0 -0
- {pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/pixelarraythirdparty/cron/cron.py +0 -0
- {pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/pixelarraythirdparty/order/__init__.py +0 -0
- {pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/pixelarraythirdparty/order/order.py +0 -0
- {pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/pixelarraythirdparty/product/__init__.py +0 -0
- {pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/pixelarraythirdparty/product/product.py +0 -0
- {pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/pixelarraythirdparty/unified_login/__init__.py +0 -0
- {pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/pixelarraythirdparty/user/__init__.py +0 -0
- {pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/pixelarraythirdparty/user/user.py +0 -0
- {pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/pixelarraythirdparty.egg-info/SOURCES.txt +0 -0
- {pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/pixelarraythirdparty.egg-info/dependency_links.txt +0 -0
- {pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/pixelarraythirdparty.egg-info/requires.txt +0 -0
- {pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/pixelarraythirdparty.egg-info/top_level.txt +0 -0
- {pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/setup.cfg +0 -0
|
@@ -92,21 +92,38 @@ class GoogleLogin(AsyncClient):
|
|
|
92
92
|
class WechatLogin(AsyncClient):
|
|
93
93
|
"""
|
|
94
94
|
微信 OAuth2 登录客户端
|
|
95
|
+
|
|
96
|
+
支持两种登录方式:
|
|
97
|
+
- desktop: PC端扫码登录(使用微信开放平台)
|
|
98
|
+
- mobile: 微信公众号OAuth登录(手机端微信内打开)
|
|
95
99
|
|
|
96
100
|
使用示例:
|
|
97
101
|
```
|
|
98
102
|
wechat = WechatLogin(api_key="your_api_key")
|
|
99
|
-
|
|
103
|
+
# PC端扫码登录
|
|
104
|
+
user_info, success = await wechat.login(login_type="desktop")
|
|
105
|
+
# 微信公众号登录(手机端)
|
|
106
|
+
user_info, success = await wechat.login(login_type="mobile")
|
|
100
107
|
```
|
|
101
108
|
"""
|
|
102
109
|
|
|
103
110
|
def __init__(self, api_key: str):
|
|
104
111
|
super().__init__(api_key)
|
|
105
112
|
|
|
106
|
-
async def _get_auth_url(self) -> Tuple[Optional[Dict[str, str]], bool]:
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
113
|
+
async def _get_auth_url(self, login_type: str = "desktop") -> Tuple[Optional[Dict[str, str]], bool]:
|
|
114
|
+
"""
|
|
115
|
+
获取微信授权URL
|
|
116
|
+
|
|
117
|
+
:param login_type: 登录类型,desktop 表示PC端扫码登录,mobile 表示微信公众号登录
|
|
118
|
+
"""
|
|
119
|
+
if login_type == "mobile":
|
|
120
|
+
# 微信公众号OAuth登录(手机端)
|
|
121
|
+
endpoint = "/api/unified-login/wechat-official/auth-url"
|
|
122
|
+
else:
|
|
123
|
+
# PC端扫码登录
|
|
124
|
+
endpoint = "/api/unified-login/wechat/auth-url"
|
|
125
|
+
|
|
126
|
+
data, success = await self._request("POST", endpoint)
|
|
110
127
|
if not success:
|
|
111
128
|
return None, False
|
|
112
129
|
auth_url = data.get("auth_url")
|
|
@@ -114,14 +131,24 @@ class WechatLogin(AsyncClient):
|
|
|
114
131
|
return None, False
|
|
115
132
|
return data, True
|
|
116
133
|
|
|
117
|
-
async def login(self, timeout: int = 180) -> Tuple[Dict, bool]:
|
|
134
|
+
async def login(self, login_type: str = "desktop", timeout: int = 180) -> Tuple[Dict, bool]:
|
|
118
135
|
"""
|
|
119
136
|
仿 Supabase CLI 的一键登录流程:打开浏览器完成授权,
|
|
120
137
|
终端端轮询等待登录结果
|
|
121
138
|
|
|
139
|
+
:param login_type: 登录类型,desktop 表示PC端扫码登录,mobile 表示微信公众号登录
|
|
122
140
|
:param timeout: 等待用户完成授权的超时时间(秒)
|
|
141
|
+
|
|
142
|
+
使用示例:
|
|
143
|
+
```
|
|
144
|
+
wechat = WechatLogin(api_key="your_api_key")
|
|
145
|
+
# PC端扫码登录
|
|
146
|
+
user_info, success = await wechat.login(login_type="desktop")
|
|
147
|
+
# 微信公众号登录(手机端)
|
|
148
|
+
user_info, success = await wechat.login(login_type="mobile")
|
|
149
|
+
```
|
|
123
150
|
"""
|
|
124
|
-
auth_data, success = await self._get_auth_url()
|
|
151
|
+
auth_data, success = await self._get_auth_url(login_type)
|
|
125
152
|
if not success or not auth_data:
|
|
126
153
|
return {}, False
|
|
127
154
|
|
|
@@ -133,7 +160,7 @@ class WechatLogin(AsyncClient):
|
|
|
133
160
|
|
|
134
161
|
webbrowser.open(auth_url, new=2)
|
|
135
162
|
|
|
136
|
-
return await self._wait_for_wechat_login(state, timeout)
|
|
163
|
+
return await self._wait_for_wechat_login(state, timeout, login_type)
|
|
137
164
|
|
|
138
165
|
def _extract_state(self, auth_url: Optional[str]) -> Optional[str]:
|
|
139
166
|
if not auth_url:
|
|
@@ -149,15 +176,28 @@ class WechatLogin(AsyncClient):
|
|
|
149
176
|
return None
|
|
150
177
|
|
|
151
178
|
async def _wait_for_wechat_login(
|
|
152
|
-
self, state: str, timeout: int
|
|
179
|
+
self, state: str, timeout: int, login_type: str = "desktop"
|
|
153
180
|
) -> Tuple[Dict, bool]:
|
|
181
|
+
"""
|
|
182
|
+
等待微信登录结果
|
|
183
|
+
|
|
184
|
+
:param state: 登录状态标识
|
|
185
|
+
:param timeout: 超时时间(秒)
|
|
186
|
+
:param login_type: 登录类型,desktop 表示PC端扫码登录,mobile 表示微信公众号登录
|
|
187
|
+
"""
|
|
154
188
|
interval = 2
|
|
155
189
|
total_checks = max(1, timeout // interval) if timeout > 0 else 1
|
|
156
190
|
|
|
191
|
+
# 根据登录类型选择不同的等待接口
|
|
192
|
+
if login_type == "mobile":
|
|
193
|
+
endpoint = "/api/unified-login/wechat-official/wait-login"
|
|
194
|
+
else:
|
|
195
|
+
endpoint = "/api/unified-login/wechat/wait-login"
|
|
196
|
+
|
|
157
197
|
for _ in range(total_checks):
|
|
158
198
|
status, response = await self._request_raw(
|
|
159
199
|
"POST",
|
|
160
|
-
|
|
200
|
+
endpoint,
|
|
161
201
|
json={"state": state},
|
|
162
202
|
)
|
|
163
203
|
|
|
@@ -255,16 +295,167 @@ class GitHubLogin(AsyncClient):
|
|
|
255
295
|
return {}, False
|
|
256
296
|
|
|
257
297
|
|
|
258
|
-
class
|
|
298
|
+
class DouyinLogin(AsyncClient):
|
|
299
|
+
"""
|
|
300
|
+
抖音 OAuth2 登录客户端
|
|
301
|
+
|
|
302
|
+
使用示例:
|
|
303
|
+
```
|
|
304
|
+
douyin = DouyinLogin(api_key="your_api_key")
|
|
305
|
+
user_info, success = await douyin.login()
|
|
306
|
+
```
|
|
307
|
+
"""
|
|
308
|
+
|
|
259
309
|
def __init__(self, api_key: str):
|
|
260
310
|
super().__init__(api_key)
|
|
261
311
|
|
|
262
|
-
async def
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
312
|
+
async def _get_auth_url(self) -> Tuple[Optional[Dict[str, str]], bool]:
|
|
313
|
+
data, success = await self._request(
|
|
314
|
+
"POST", "/api/unified-login/douyin/auth-url"
|
|
315
|
+
)
|
|
316
|
+
if not success:
|
|
317
|
+
return None, False
|
|
318
|
+
auth_url = data.get("auth_url")
|
|
319
|
+
if not auth_url:
|
|
320
|
+
return None, False
|
|
321
|
+
return data, True
|
|
322
|
+
|
|
323
|
+
async def login(self, timeout: int = 180) -> Tuple[Dict, bool]:
|
|
324
|
+
"""
|
|
325
|
+
仿 Supabase CLI 的一键登录流程:打开浏览器完成授权,
|
|
326
|
+
终端端轮询等待登录结果
|
|
327
|
+
|
|
328
|
+
:param timeout: 等待用户完成授权的超时时间(秒)
|
|
329
|
+
"""
|
|
330
|
+
auth_data, success = await self._get_auth_url()
|
|
331
|
+
if not success or not auth_data:
|
|
332
|
+
return {}, False
|
|
333
|
+
|
|
334
|
+
auth_url = auth_data.get("auth_url")
|
|
335
|
+
state = auth_data.get("state") or self._extract_state(auth_url)
|
|
336
|
+
|
|
337
|
+
if not auth_url or not state:
|
|
338
|
+
return {}, False
|
|
339
|
+
|
|
340
|
+
webbrowser.open(auth_url, new=2)
|
|
341
|
+
|
|
342
|
+
return await self._wait_for_douyin_login(state, timeout)
|
|
343
|
+
|
|
344
|
+
def _extract_state(self, auth_url: Optional[str]) -> Optional[str]:
|
|
345
|
+
if not auth_url:
|
|
346
|
+
return None
|
|
347
|
+
try:
|
|
348
|
+
parsed = urllib.parse.urlparse(auth_url)
|
|
349
|
+
query = urllib.parse.parse_qs(parsed.query)
|
|
350
|
+
values = query.get("state")
|
|
351
|
+
if values:
|
|
352
|
+
return values[0]
|
|
353
|
+
except Exception:
|
|
354
|
+
return None
|
|
355
|
+
return None
|
|
356
|
+
|
|
357
|
+
async def _wait_for_douyin_login(
|
|
358
|
+
self, state: str, timeout: int
|
|
359
|
+
) -> Tuple[Dict, bool]:
|
|
360
|
+
interval = 2
|
|
361
|
+
total_checks = max(1, timeout // interval) if timeout > 0 else 1
|
|
362
|
+
|
|
363
|
+
for _ in range(total_checks):
|
|
364
|
+
status, response = await self._request_raw(
|
|
365
|
+
"POST",
|
|
366
|
+
"/api/unified-login/douyin/wait-login",
|
|
367
|
+
json={"state": state},
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
if status == 200 and response.get("success") is True:
|
|
371
|
+
return response.get("data", {}), True
|
|
372
|
+
|
|
373
|
+
if status in (400, 408):
|
|
374
|
+
break
|
|
375
|
+
|
|
376
|
+
await asyncio.sleep(interval)
|
|
377
|
+
|
|
378
|
+
return {}, False
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
class GitLabLogin(AsyncClient):
|
|
382
|
+
"""
|
|
383
|
+
GitLab OAuth2 登录客户端
|
|
384
|
+
|
|
385
|
+
使用示例:
|
|
386
|
+
```
|
|
387
|
+
gitlab = GitLabLogin(api_key="your_api_key")
|
|
388
|
+
user_info, success = await gitlab.login()
|
|
389
|
+
```
|
|
390
|
+
"""
|
|
391
|
+
|
|
392
|
+
def __init__(self, api_key: str):
|
|
393
|
+
super().__init__(api_key)
|
|
394
|
+
|
|
395
|
+
async def _get_auth_url(self) -> Tuple[Optional[Dict[str, str]], bool]:
|
|
396
|
+
data, success = await self._request(
|
|
397
|
+
"POST", "/api/unified-login/gitlab/auth-url"
|
|
398
|
+
)
|
|
399
|
+
if not success:
|
|
400
|
+
return None, False
|
|
401
|
+
auth_url = data.get("auth_url")
|
|
402
|
+
if not auth_url:
|
|
403
|
+
return None, False
|
|
404
|
+
return data, True
|
|
405
|
+
|
|
406
|
+
async def login(self, timeout: int = 180) -> Tuple[Dict, bool]:
|
|
407
|
+
"""
|
|
408
|
+
仿 Supabase CLI 的一键登录流程:打开浏览器完成授权,
|
|
409
|
+
终端端轮询等待登录结果
|
|
410
|
+
|
|
411
|
+
:param timeout: 等待用户完成授权的超时时间(秒)
|
|
412
|
+
"""
|
|
413
|
+
auth_data, success = await self._get_auth_url()
|
|
414
|
+
if not success or not auth_data:
|
|
270
415
|
return {}, False
|
|
416
|
+
|
|
417
|
+
auth_url = auth_data.get("auth_url")
|
|
418
|
+
state = auth_data.get("state") or self._extract_state(auth_url)
|
|
419
|
+
|
|
420
|
+
if not auth_url or not state:
|
|
421
|
+
return {}, False
|
|
422
|
+
|
|
423
|
+
webbrowser.open(auth_url, new=2)
|
|
424
|
+
|
|
425
|
+
return await self._wait_for_gitlab_login(state, timeout)
|
|
426
|
+
|
|
427
|
+
def _extract_state(self, auth_url: Optional[str]) -> Optional[str]:
|
|
428
|
+
if not auth_url:
|
|
429
|
+
return None
|
|
430
|
+
try:
|
|
431
|
+
parsed = urllib.parse.urlparse(auth_url)
|
|
432
|
+
query = urllib.parse.parse_qs(parsed.query)
|
|
433
|
+
values = query.get("state")
|
|
434
|
+
if values:
|
|
435
|
+
return values[0]
|
|
436
|
+
except Exception:
|
|
437
|
+
return None
|
|
438
|
+
return None
|
|
439
|
+
|
|
440
|
+
async def _wait_for_gitlab_login(
|
|
441
|
+
self, state: str, timeout: int
|
|
442
|
+
) -> Tuple[Dict, bool]:
|
|
443
|
+
interval = 2
|
|
444
|
+
total_checks = max(1, timeout // interval) if timeout > 0 else 1
|
|
445
|
+
|
|
446
|
+
for _ in range(total_checks):
|
|
447
|
+
status, response = await self._request_raw(
|
|
448
|
+
"POST",
|
|
449
|
+
"/api/unified-login/gitlab/wait-login",
|
|
450
|
+
json={"state": state},
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
if status == 200 and response.get("success") is True:
|
|
454
|
+
return response.get("data", {}), True
|
|
455
|
+
|
|
456
|
+
if status in (400, 408):
|
|
457
|
+
break
|
|
458
|
+
|
|
459
|
+
await asyncio.sleep(interval)
|
|
460
|
+
|
|
461
|
+
return {}, False
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/pixelarraythirdparty/cron/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/pixelarraythirdparty/order/__init__.py
RENAMED
|
File without changes
|
{pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/pixelarraythirdparty/order/order.py
RENAMED
|
File without changes
|
{pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/pixelarraythirdparty/product/__init__.py
RENAMED
|
File without changes
|
{pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/pixelarraythirdparty/product/product.py
RENAMED
|
File without changes
|
|
File without changes
|
{pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/pixelarraythirdparty/user/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/pixelarraythirdparty.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{pixelarraythirdparty-1.1.2 → pixelarraythirdparty-1.1.4}/pixelarraythirdparty.egg-info/requires.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|