pixelarraythirdparty 1.0.8__tar.gz → 1.1.0__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.0.8 → pixelarraythirdparty-1.1.0}/PKG-INFO +1 -1
- {pixelarraythirdparty-1.0.8 → pixelarraythirdparty-1.1.0}/pixelarraythirdparty/__init__.py +2 -2
- pixelarraythirdparty-1.1.0/pixelarraythirdparty/unified_login/__init__.py +16 -0
- pixelarraythirdparty-1.1.0/pixelarraythirdparty/unified_login/unified_login.py +18 -0
- {pixelarraythirdparty-1.0.8 → pixelarraythirdparty-1.1.0}/pixelarraythirdparty.egg-info/PKG-INFO +1 -1
- {pixelarraythirdparty-1.0.8 → pixelarraythirdparty-1.1.0}/pixelarraythirdparty.egg-info/SOURCES.txt +2 -2
- {pixelarraythirdparty-1.0.8 → pixelarraythirdparty-1.1.0}/pyproject.toml +1 -1
- pixelarraythirdparty-1.0.8/pixelarraythirdparty/filestorage/__init__.py +0 -6
- pixelarraythirdparty-1.0.8/pixelarraythirdparty/filestorage/filestorage.py +0 -286
- {pixelarraythirdparty-1.0.8 → pixelarraythirdparty-1.1.0}/LICENSE +0 -0
- {pixelarraythirdparty-1.0.8 → pixelarraythirdparty-1.1.0}/MANIFEST.in +0 -0
- {pixelarraythirdparty-1.0.8 → pixelarraythirdparty-1.1.0}/pixelarraythirdparty/client.py +0 -0
- {pixelarraythirdparty-1.0.8 → pixelarraythirdparty-1.1.0}/pixelarraythirdparty/cron/__init__.py +0 -0
- {pixelarraythirdparty-1.0.8 → pixelarraythirdparty-1.1.0}/pixelarraythirdparty/cron/cron.py +0 -0
- {pixelarraythirdparty-1.0.8 → pixelarraythirdparty-1.1.0}/pixelarraythirdparty/order/__init__.py +0 -0
- {pixelarraythirdparty-1.0.8 → pixelarraythirdparty-1.1.0}/pixelarraythirdparty/order/order.py +0 -0
- {pixelarraythirdparty-1.0.8 → pixelarraythirdparty-1.1.0}/pixelarraythirdparty/product/__init__.py +0 -0
- {pixelarraythirdparty-1.0.8 → pixelarraythirdparty-1.1.0}/pixelarraythirdparty/product/product.py +0 -0
- {pixelarraythirdparty-1.0.8 → pixelarraythirdparty-1.1.0}/pixelarraythirdparty/user/__init__.py +0 -0
- {pixelarraythirdparty-1.0.8 → pixelarraythirdparty-1.1.0}/pixelarraythirdparty/user/user.py +0 -0
- {pixelarraythirdparty-1.0.8 → pixelarraythirdparty-1.1.0}/pixelarraythirdparty.egg-info/dependency_links.txt +0 -0
- {pixelarraythirdparty-1.0.8 → pixelarraythirdparty-1.1.0}/pixelarraythirdparty.egg-info/requires.txt +0 -0
- {pixelarraythirdparty-1.0.8 → pixelarraythirdparty-1.1.0}/pixelarraythirdparty.egg-info/top_level.txt +0 -0
- {pixelarraythirdparty-1.0.8 → pixelarraythirdparty-1.1.0}/setup.cfg +0 -0
|
@@ -8,9 +8,10 @@ PixelArray 第三方微服务客户端
|
|
|
8
8
|
- product: 产品管理模块
|
|
9
9
|
- cron: 定时任务管理模块
|
|
10
10
|
- user: 用户管理模块
|
|
11
|
+
- unified_login: 统一登录模块
|
|
11
12
|
"""
|
|
12
13
|
|
|
13
|
-
__version__ = "1.0
|
|
14
|
+
__version__ = "1.1.0"
|
|
14
15
|
__author__ = "Lu qi"
|
|
15
16
|
__email__ = "qi.lu@pixelarrayai.com"
|
|
16
17
|
|
|
@@ -20,5 +21,4 @@ __all__ = [
|
|
|
20
21
|
"cron",
|
|
21
22
|
"user",
|
|
22
23
|
"order",
|
|
23
|
-
"filestorage",
|
|
24
24
|
]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from .unified_login import (
|
|
2
|
+
ThirdPartyAuthUrlRequest,
|
|
3
|
+
ThirdPartyAuthUrlResponse,
|
|
4
|
+
ThirdPartyLoginRequest,
|
|
5
|
+
ThirdPartyLoginResponse,
|
|
6
|
+
UnifiedLoginClientAsync,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"ThirdPartyAuthUrlRequest",
|
|
11
|
+
"ThirdPartyAuthUrlResponse",
|
|
12
|
+
"ThirdPartyLoginRequest",
|
|
13
|
+
"ThirdPartyLoginResponse",
|
|
14
|
+
"UnifiedLoginClientAsync",
|
|
15
|
+
]
|
|
16
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from client.pypi.pixelarraythirdparty.client import AsyncClient
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class GoogleLogin(AsyncClient):
|
|
5
|
+
async def _get_auth_url(self):
|
|
6
|
+
pass
|
|
7
|
+
|
|
8
|
+
async def _get_code_from_redirect_uri(self, redirect_uri: str):
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
async def _get_user_info(self, token: str) -> dict:
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
async def login(self) -> dict:
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
async def logout(self) -> dict:
|
|
18
|
+
pass
|
{pixelarraythirdparty-1.0.8 → pixelarraythirdparty-1.1.0}/pixelarraythirdparty.egg-info/SOURCES.txt
RENAMED
|
@@ -10,11 +10,11 @@ pixelarraythirdparty.egg-info/requires.txt
|
|
|
10
10
|
pixelarraythirdparty.egg-info/top_level.txt
|
|
11
11
|
pixelarraythirdparty/cron/__init__.py
|
|
12
12
|
pixelarraythirdparty/cron/cron.py
|
|
13
|
-
pixelarraythirdparty/filestorage/__init__.py
|
|
14
|
-
pixelarraythirdparty/filestorage/filestorage.py
|
|
15
13
|
pixelarraythirdparty/order/__init__.py
|
|
16
14
|
pixelarraythirdparty/order/order.py
|
|
17
15
|
pixelarraythirdparty/product/__init__.py
|
|
18
16
|
pixelarraythirdparty/product/product.py
|
|
17
|
+
pixelarraythirdparty/unified_login/__init__.py
|
|
18
|
+
pixelarraythirdparty/unified_login/unified_login.py
|
|
19
19
|
pixelarraythirdparty/user/__init__.py
|
|
20
20
|
pixelarraythirdparty/user/user.py
|
|
@@ -1,286 +0,0 @@
|
|
|
1
|
-
from pixelarraythirdparty.client import AsyncClient
|
|
2
|
-
from typing import Dict, Any, Optional, List, Tuple, Callable, Union
|
|
3
|
-
import os
|
|
4
|
-
import asyncio
|
|
5
|
-
import aiohttp
|
|
6
|
-
import mimetypes
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class FileStorageManagerAsync(AsyncClient):
|
|
10
|
-
async def upload(
|
|
11
|
-
self, file_path: str, parent_id: Optional[int] = None
|
|
12
|
-
) -> Tuple[bool, Dict[str, Any]]:
|
|
13
|
-
"""
|
|
14
|
-
description:
|
|
15
|
-
上传文件(合并了初始化、分片上传、完成上传三个步骤)
|
|
16
|
-
parameters:
|
|
17
|
-
file_path: 文件路径(str)
|
|
18
|
-
parent_id: 父文件夹ID(可选)
|
|
19
|
-
return:
|
|
20
|
-
- data: 结果数据
|
|
21
|
-
- success: 是否成功
|
|
22
|
-
"""
|
|
23
|
-
# 读取文件数据
|
|
24
|
-
with open(file_path, "rb") as f:
|
|
25
|
-
file_bytes = f.read()
|
|
26
|
-
|
|
27
|
-
total_size = len(file_bytes)
|
|
28
|
-
chunk_size = 2 * 1024 * 1024 # 2MB
|
|
29
|
-
|
|
30
|
-
# 1. 初始化上传
|
|
31
|
-
file_name = os.path.basename(file_path)
|
|
32
|
-
mime_type = mimetypes.guess_type(file_path)[0]
|
|
33
|
-
init_data = {
|
|
34
|
-
"filename": file_name,
|
|
35
|
-
"file_type": mime_type,
|
|
36
|
-
"total_size": total_size,
|
|
37
|
-
}
|
|
38
|
-
if parent_id is not None:
|
|
39
|
-
init_data["parent_id"] = parent_id
|
|
40
|
-
|
|
41
|
-
init_result, success = await self._request(
|
|
42
|
-
"POST", "/api/file_storage/upload/init", json=init_data
|
|
43
|
-
)
|
|
44
|
-
if not success:
|
|
45
|
-
return {}, False
|
|
46
|
-
|
|
47
|
-
upload_id = init_result.get("upload_id")
|
|
48
|
-
chunk_urls = init_result.get("chunk_urls", [])
|
|
49
|
-
total_chunks = len(chunk_urls)
|
|
50
|
-
|
|
51
|
-
if not upload_id or not chunk_urls:
|
|
52
|
-
return {}, False
|
|
53
|
-
|
|
54
|
-
# 2. 上传所有分片
|
|
55
|
-
parts = []
|
|
56
|
-
|
|
57
|
-
async def upload_single_chunk(chunk_index: int, chunk_data: bytes):
|
|
58
|
-
"""上传单个分片"""
|
|
59
|
-
chunk_info = chunk_urls[chunk_index]
|
|
60
|
-
part_number = chunk_info.get("part_number")
|
|
61
|
-
url = chunk_info.get("url")
|
|
62
|
-
|
|
63
|
-
if not url or not part_number:
|
|
64
|
-
return None
|
|
65
|
-
|
|
66
|
-
# 使用预签名URL直接上传到OSS(PUT请求)
|
|
67
|
-
async with aiohttp.ClientSession() as session:
|
|
68
|
-
async with session.put(url, data=chunk_data) as resp:
|
|
69
|
-
if resp.status == 200:
|
|
70
|
-
etag = resp.headers.get("ETag", "").strip('"')
|
|
71
|
-
return {
|
|
72
|
-
"part_number": part_number,
|
|
73
|
-
"etag": etag,
|
|
74
|
-
"chunk_index": chunk_index,
|
|
75
|
-
}
|
|
76
|
-
return None
|
|
77
|
-
|
|
78
|
-
# 并发上传所有分片
|
|
79
|
-
tasks = []
|
|
80
|
-
for i in range(total_chunks):
|
|
81
|
-
start = i * chunk_size
|
|
82
|
-
end = min(start + chunk_size, total_size)
|
|
83
|
-
chunk_data = file_bytes[start:end]
|
|
84
|
-
tasks.append(upload_single_chunk(i, chunk_data))
|
|
85
|
-
|
|
86
|
-
# 等待所有分片上传完成,并收集结果
|
|
87
|
-
results = await asyncio.gather(*tasks)
|
|
88
|
-
|
|
89
|
-
# 检查上传结果并更新进度
|
|
90
|
-
# 按chunk_index排序,确保parts顺序正确
|
|
91
|
-
sorted_results = sorted(
|
|
92
|
-
[r for r in results if r is not None], key=lambda x: x.get("chunk_index", 0)
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
if len(sorted_results) != total_chunks:
|
|
96
|
-
return {}, False
|
|
97
|
-
|
|
98
|
-
for i, result in enumerate(sorted_results):
|
|
99
|
-
parts.append({"part_number": result["part_number"], "etag": result["etag"]})
|
|
100
|
-
|
|
101
|
-
# 3. 完成上传
|
|
102
|
-
complete_data = {
|
|
103
|
-
"upload_id": upload_id,
|
|
104
|
-
"parts": parts,
|
|
105
|
-
}
|
|
106
|
-
complete_result, success = await self._request(
|
|
107
|
-
"POST", "/api/file_storage/upload/complete", json=complete_data
|
|
108
|
-
)
|
|
109
|
-
if not success:
|
|
110
|
-
return {}, False
|
|
111
|
-
|
|
112
|
-
return complete_result, True
|
|
113
|
-
|
|
114
|
-
async def list_files(
|
|
115
|
-
self,
|
|
116
|
-
parent_id: Optional[int] = None,
|
|
117
|
-
is_folder: Optional[bool] = None,
|
|
118
|
-
page: int = 1,
|
|
119
|
-
page_size: int = 50,
|
|
120
|
-
) -> Tuple[List[Dict[str, Any]], bool]:
|
|
121
|
-
"""
|
|
122
|
-
description:
|
|
123
|
-
获取文件列表
|
|
124
|
-
parameters:
|
|
125
|
-
parent_id: 父文件夹ID(可选)
|
|
126
|
-
is_folder: 是否只查询文件夹(可选)
|
|
127
|
-
page: 页码(可选)
|
|
128
|
-
page_size: 每页数量(可选)
|
|
129
|
-
return:
|
|
130
|
-
- data: 文件列表数据
|
|
131
|
-
- success: 是否成功
|
|
132
|
-
"""
|
|
133
|
-
data = {
|
|
134
|
-
"page": page,
|
|
135
|
-
"page_size": page_size,
|
|
136
|
-
}
|
|
137
|
-
if parent_id is not None:
|
|
138
|
-
data["parent_id"] = parent_id
|
|
139
|
-
if is_folder is not None:
|
|
140
|
-
data["is_folder"] = is_folder
|
|
141
|
-
|
|
142
|
-
result, success = await self._request(
|
|
143
|
-
"POST", "/api/file_storage/files/list", json=data
|
|
144
|
-
)
|
|
145
|
-
if not success:
|
|
146
|
-
return {}, False
|
|
147
|
-
return result, True
|
|
148
|
-
|
|
149
|
-
async def create_folder(
|
|
150
|
-
self,
|
|
151
|
-
folder_name: str,
|
|
152
|
-
parent_id: Optional[int] = None,
|
|
153
|
-
) -> Tuple[Dict[str, Any], bool]:
|
|
154
|
-
"""
|
|
155
|
-
description:
|
|
156
|
-
创建文件夹
|
|
157
|
-
parameters:
|
|
158
|
-
folder_name: 文件夹名称
|
|
159
|
-
parent_id: 父文件夹ID(可选)
|
|
160
|
-
return:
|
|
161
|
-
- data: 文件夹数据
|
|
162
|
-
- success: 是否成功
|
|
163
|
-
"""
|
|
164
|
-
data = {
|
|
165
|
-
"folder_name": folder_name,
|
|
166
|
-
}
|
|
167
|
-
if parent_id is not None:
|
|
168
|
-
data["parent_id"] = parent_id
|
|
169
|
-
|
|
170
|
-
data, success = await self._request(
|
|
171
|
-
"POST", "/api/file_storage/files/folder/create", json=data
|
|
172
|
-
)
|
|
173
|
-
if not success:
|
|
174
|
-
return {}, False
|
|
175
|
-
return data, True
|
|
176
|
-
|
|
177
|
-
async def delete_file(
|
|
178
|
-
self,
|
|
179
|
-
record_id: int,
|
|
180
|
-
) -> Tuple[Dict[str, Any], bool]:
|
|
181
|
-
"""
|
|
182
|
-
description:
|
|
183
|
-
删除文件或文件夹
|
|
184
|
-
parameters:
|
|
185
|
-
record_id: 文件或文件夹ID
|
|
186
|
-
return:
|
|
187
|
-
- data: 结果数据
|
|
188
|
-
- success: 是否成功
|
|
189
|
-
"""
|
|
190
|
-
data, success = await self._request(
|
|
191
|
-
"DELETE", f"/api/file_storage/files/{record_id}"
|
|
192
|
-
)
|
|
193
|
-
if not success:
|
|
194
|
-
return {}, False
|
|
195
|
-
return data, True
|
|
196
|
-
|
|
197
|
-
async def get_folder_path(
|
|
198
|
-
self,
|
|
199
|
-
record_id: int,
|
|
200
|
-
) -> Tuple[List[Dict[str, Any]], bool]:
|
|
201
|
-
"""
|
|
202
|
-
description:
|
|
203
|
-
获取文件夹的完整路径
|
|
204
|
-
parameters:
|
|
205
|
-
record_id: 文件夹ID
|
|
206
|
-
return:
|
|
207
|
-
- data: 文件夹路径列表
|
|
208
|
-
- success: 是否成功
|
|
209
|
-
"""
|
|
210
|
-
data, success = await self._request(
|
|
211
|
-
"GET", f"/api/file_storage/files/{record_id}/path"
|
|
212
|
-
)
|
|
213
|
-
if not success:
|
|
214
|
-
return [], False
|
|
215
|
-
# 如果data是字典,尝试获取data字段(因为API返回的是{"data": [...]})
|
|
216
|
-
if isinstance(data, dict):
|
|
217
|
-
path_list = data.get("data", [])
|
|
218
|
-
if isinstance(path_list, list):
|
|
219
|
-
return path_list, True
|
|
220
|
-
# 如果data本身就是列表,直接返回
|
|
221
|
-
if isinstance(data, list):
|
|
222
|
-
return data, True
|
|
223
|
-
return [], False
|
|
224
|
-
|
|
225
|
-
async def generate_signed_url(
|
|
226
|
-
self,
|
|
227
|
-
record_id: int,
|
|
228
|
-
expires: int = 3600,
|
|
229
|
-
) -> Tuple[Dict[str, Any], bool]:
|
|
230
|
-
"""
|
|
231
|
-
生成签名URL(异步版本)
|
|
232
|
-
"""
|
|
233
|
-
data = {
|
|
234
|
-
"expires": expires,
|
|
235
|
-
}
|
|
236
|
-
data, success = await self._request(
|
|
237
|
-
"POST", f"/api/file_storage/files/{record_id}/generate_url", json=data
|
|
238
|
-
)
|
|
239
|
-
if not success:
|
|
240
|
-
return {}, False
|
|
241
|
-
return data, True
|
|
242
|
-
|
|
243
|
-
async def download(
|
|
244
|
-
self,
|
|
245
|
-
record_id: int,
|
|
246
|
-
save_path: str,
|
|
247
|
-
) -> Tuple[Dict[str, Any], bool]:
|
|
248
|
-
"""
|
|
249
|
-
description:
|
|
250
|
-
下载文件
|
|
251
|
-
parameters:
|
|
252
|
-
record_id: 文件记录ID
|
|
253
|
-
save_path: 保存路径
|
|
254
|
-
return:
|
|
255
|
-
- data: 下载结果数据
|
|
256
|
-
- success: 是否成功
|
|
257
|
-
"""
|
|
258
|
-
# 1. 生成签名URL
|
|
259
|
-
signed_url_data, success = await self.generate_signed_url(record_id)
|
|
260
|
-
if not success:
|
|
261
|
-
return {}, False
|
|
262
|
-
|
|
263
|
-
signed_url = signed_url_data.get("signed_url")
|
|
264
|
-
file_record = signed_url_data.get("file_record", {})
|
|
265
|
-
total_size = file_record.get("file_size", 0)
|
|
266
|
-
|
|
267
|
-
if not signed_url:
|
|
268
|
-
return {}, False
|
|
269
|
-
|
|
270
|
-
# 2. 下载文件
|
|
271
|
-
async with aiohttp.ClientSession() as session:
|
|
272
|
-
async with session.get(signed_url) as resp:
|
|
273
|
-
if resp.status != 200:
|
|
274
|
-
return {}, False
|
|
275
|
-
|
|
276
|
-
file_data = b""
|
|
277
|
-
downloaded = 0
|
|
278
|
-
|
|
279
|
-
async for chunk in resp.content.iter_chunked(8192): # 8KB chunks
|
|
280
|
-
file_data += chunk
|
|
281
|
-
downloaded += len(chunk)
|
|
282
|
-
|
|
283
|
-
with open(save_path, "wb") as f:
|
|
284
|
-
f.write(file_data)
|
|
285
|
-
|
|
286
|
-
return {"total_size": total_size, "success": True}, True
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pixelarraythirdparty-1.0.8 → pixelarraythirdparty-1.1.0}/pixelarraythirdparty/cron/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{pixelarraythirdparty-1.0.8 → pixelarraythirdparty-1.1.0}/pixelarraythirdparty/order/__init__.py
RENAMED
|
File without changes
|
{pixelarraythirdparty-1.0.8 → pixelarraythirdparty-1.1.0}/pixelarraythirdparty/order/order.py
RENAMED
|
File without changes
|
{pixelarraythirdparty-1.0.8 → pixelarraythirdparty-1.1.0}/pixelarraythirdparty/product/__init__.py
RENAMED
|
File without changes
|
{pixelarraythirdparty-1.0.8 → pixelarraythirdparty-1.1.0}/pixelarraythirdparty/product/product.py
RENAMED
|
File without changes
|
{pixelarraythirdparty-1.0.8 → pixelarraythirdparty-1.1.0}/pixelarraythirdparty/user/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pixelarraythirdparty-1.0.8 → pixelarraythirdparty-1.1.0}/pixelarraythirdparty.egg-info/requires.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|