pixelarraylib 1.0.7__tar.gz → 1.0.9__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.
- {pixelarraylib-1.0.7/pixelarraylib.egg-info → pixelarraylib-1.0.9}/PKG-INFO +1 -1
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/__init__.py +1 -1
- pixelarraylib-1.0.9/pixelarraylib/aliyun/acr.py +514 -0
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/aliyun/aliyun_email.py +124 -2
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/aliyun/billing.py +3 -3
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/aliyun/content_scanner.py +6 -6
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/aliyun/eci.py +6 -6
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/aliyun/eip.py +3 -3
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/aliyun/fc.py +2 -2
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/aliyun/oss.py +5 -5
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/aliyun/sms.py +61 -2
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/aliyun/sts.py +3 -3
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/db_utils/mysql.py +10 -10
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/db_utils/redis.py +18 -18
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/decorators/decorators.py +2 -2
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/monitor/feishu.py +64 -1
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/scripts/__init__.py +1 -1
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/system/common.py +6 -8
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9/pixelarraylib.egg-info}/PKG-INFO +1 -1
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pyproject.toml +1 -1
- pixelarraylib-1.0.7/pixelarraylib/aliyun/acr.py +0 -265
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/LICENSE +0 -0
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/MANIFEST.in +0 -0
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/README.md +0 -0
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/__main__.py +0 -0
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/aliyun/__init__.py +0 -0
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/aliyun/domain.py +0 -0
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/aliyun/ecs.py +0 -0
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/decorators/__init__.py +0 -0
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/gitlab/__init__.py +0 -0
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/gitlab/code_analyzer.py +0 -0
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/gitlab/pypi_package_manager.py +0 -0
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/monitor/__init__.py +0 -0
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/net/request.py +0 -0
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/scripts/build_website.py +0 -0
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/scripts/collect_code_to_txt.py +0 -0
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/scripts/create_test_case_files.py +0 -0
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/scripts/nginx_proxy_to_ecs.py +0 -0
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/scripts/remove_empty_lines.py +0 -0
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/system/__init__.py +0 -0
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib.egg-info/SOURCES.txt +0 -0
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib.egg-info/dependency_links.txt +0 -0
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib.egg-info/entry_points.txt +0 -0
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib.egg-info/requires.txt +0 -0
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib.egg-info/top_level.txt +0 -0
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/requirements.txt +0 -0
- {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/setup.cfg +0 -0
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
from typing import List
|
|
6
|
+
|
|
7
|
+
from alibabacloud_cr20181201.client import Client as cr20181201Client
|
|
8
|
+
from alibabacloud_credentials.client import Client as CredentialClient
|
|
9
|
+
from alibabacloud_tea_openapi import models as open_api_models
|
|
10
|
+
from alibabacloud_cr20181201 import models as cr_20181201_models
|
|
11
|
+
from alibabacloud_tea_util import models as util_models
|
|
12
|
+
from alibabacloud_tea_util.client import Client as UtilClient
|
|
13
|
+
|
|
14
|
+
from pixelarraylib.monitor.feishu import Feishu
|
|
15
|
+
|
|
16
|
+
feishu_alert = Feishu("devtoolkit服务报警")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ACRUtils:
|
|
20
|
+
def __init__(self, region_id: str, access_key_id: str, access_key_secret: str):
|
|
21
|
+
self.region_id = region_id
|
|
22
|
+
self.access_key_id = access_key_id
|
|
23
|
+
self.access_key_secret = access_key_secret
|
|
24
|
+
self.client = self._create_client()
|
|
25
|
+
|
|
26
|
+
def _create_client(self):
|
|
27
|
+
credential = CredentialClient()
|
|
28
|
+
config = open_api_models.Config(
|
|
29
|
+
credential=credential,
|
|
30
|
+
access_key_id=self.access_key_id,
|
|
31
|
+
access_key_secret=self.access_key_secret,
|
|
32
|
+
)
|
|
33
|
+
# Endpoint 请参考 https://api.aliyun.com/product/cr
|
|
34
|
+
config.endpoint = f"cr.{self.region_id}.aliyuncs.com"
|
|
35
|
+
return cr20181201Client(config)
|
|
36
|
+
|
|
37
|
+
def list_instances(self):
|
|
38
|
+
"""
|
|
39
|
+
description:
|
|
40
|
+
列出容器镜像服务实例
|
|
41
|
+
parameters:
|
|
42
|
+
None
|
|
43
|
+
return:
|
|
44
|
+
dict: 包含实例列表的响应数据
|
|
45
|
+
success(bool): 是否成功
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
request = cr_20181201_models.ListInstanceRequest()
|
|
50
|
+
response = self.client.list_instance(request)
|
|
51
|
+
return response.body.to_map(), True
|
|
52
|
+
except Exception as e:
|
|
53
|
+
print(f"列出实例失败: {e}")
|
|
54
|
+
return {}, False
|
|
55
|
+
|
|
56
|
+
def create_namespace(self, instance_id: str, namespace_name: str):
|
|
57
|
+
"""
|
|
58
|
+
description:
|
|
59
|
+
创建命名空间
|
|
60
|
+
parameters:
|
|
61
|
+
instance_id (str): 实例ID
|
|
62
|
+
namespace_name (str): 命名空间名称
|
|
63
|
+
return:
|
|
64
|
+
dict: 创建结果
|
|
65
|
+
success(bool): 是否成功
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
try:
|
|
69
|
+
request = cr_20181201_models.CreateNamespaceRequest(
|
|
70
|
+
instance_id=instance_id, namespace_name=namespace_name
|
|
71
|
+
)
|
|
72
|
+
response = self.client.create_namespace(request)
|
|
73
|
+
return response.body.to_map(), True
|
|
74
|
+
except Exception as e:
|
|
75
|
+
print(f"创建命名空间失败: {e}")
|
|
76
|
+
return {}, False
|
|
77
|
+
|
|
78
|
+
def delete_namespace(self, instance_id: str, namespace_name: str):
|
|
79
|
+
"""
|
|
80
|
+
description:
|
|
81
|
+
删除命名空间
|
|
82
|
+
parameters:
|
|
83
|
+
instance_id (str): 实例ID
|
|
84
|
+
namespace_name (str): 命名空间名称
|
|
85
|
+
return:
|
|
86
|
+
dict: 删除结果
|
|
87
|
+
success(bool): 是否成功
|
|
88
|
+
"""
|
|
89
|
+
try:
|
|
90
|
+
request = cr_20181201_models.DeleteNamespaceRequest(
|
|
91
|
+
instance_id=instance_id, namespace_name=namespace_name
|
|
92
|
+
)
|
|
93
|
+
response = self.client.delete_namespace(request)
|
|
94
|
+
return response.body.to_map(), True
|
|
95
|
+
except Exception as e:
|
|
96
|
+
print(f"删除命名空间失败: {e}")
|
|
97
|
+
return {}, False
|
|
98
|
+
|
|
99
|
+
def list_namespaces(self, instance_id: str):
|
|
100
|
+
"""
|
|
101
|
+
description:
|
|
102
|
+
列出命名空间
|
|
103
|
+
parameters:
|
|
104
|
+
instance_id (str): 实例ID
|
|
105
|
+
return:
|
|
106
|
+
dict: 包含命名空间列表的响应数据
|
|
107
|
+
success(bool): 是否成功
|
|
108
|
+
"""
|
|
109
|
+
try:
|
|
110
|
+
request = cr_20181201_models.ListNamespaceRequest(instance_id=instance_id)
|
|
111
|
+
response = self.client.list_namespace(request)
|
|
112
|
+
return response.body.to_map(), True
|
|
113
|
+
except Exception as e:
|
|
114
|
+
print(f"列出命名空间失败: {e}")
|
|
115
|
+
return {}, False
|
|
116
|
+
|
|
117
|
+
def exists_namespace(self, instance_id: str, namespace_name: str):
|
|
118
|
+
"""
|
|
119
|
+
description:
|
|
120
|
+
判断命名空间是否存在
|
|
121
|
+
parameters:
|
|
122
|
+
instance_id (str): 实例ID
|
|
123
|
+
namespace_name (str): 命名空间名称
|
|
124
|
+
return:
|
|
125
|
+
bool: 是否存在
|
|
126
|
+
"""
|
|
127
|
+
response, success = self.list_namespaces(instance_id)
|
|
128
|
+
if not success:
|
|
129
|
+
return False, False
|
|
130
|
+
namespace_names = [
|
|
131
|
+
namespace["NamespaceName"] for namespace in response["Namespaces"]
|
|
132
|
+
]
|
|
133
|
+
return namespace_name in namespace_names, True
|
|
134
|
+
|
|
135
|
+
def create_repository(
|
|
136
|
+
self,
|
|
137
|
+
instance_id: str,
|
|
138
|
+
namespace_name: str,
|
|
139
|
+
repository_name: str,
|
|
140
|
+
repository_type: str,
|
|
141
|
+
summary: str,
|
|
142
|
+
):
|
|
143
|
+
"""
|
|
144
|
+
description:
|
|
145
|
+
创建仓库
|
|
146
|
+
parameters:
|
|
147
|
+
instance_id (str): 实例ID
|
|
148
|
+
namespace_name (str): 命名空间名称
|
|
149
|
+
repository_name (str): 仓库名称
|
|
150
|
+
repository_type (str): 仓库类型
|
|
151
|
+
summary (str): 仓库摘要
|
|
152
|
+
return:
|
|
153
|
+
dict: 创建结果
|
|
154
|
+
success(bool): 是否成功
|
|
155
|
+
"""
|
|
156
|
+
if not re.fullmatch(r"[a-z\-]+", repository_name):
|
|
157
|
+
print("仓库名称只能包含小写英文字母和横杠")
|
|
158
|
+
return {}, False
|
|
159
|
+
try:
|
|
160
|
+
request = cr_20181201_models.CreateRepositoryRequest(
|
|
161
|
+
instance_id=instance_id,
|
|
162
|
+
repo_namespace_name=namespace_name,
|
|
163
|
+
repo_name=repository_name,
|
|
164
|
+
repo_type=repository_type,
|
|
165
|
+
summary=summary,
|
|
166
|
+
)
|
|
167
|
+
response = self.client.create_repository(request)
|
|
168
|
+
return response.body.to_map(), True
|
|
169
|
+
except Exception as e:
|
|
170
|
+
print(f"创建仓库失败: {e}")
|
|
171
|
+
return {}, False
|
|
172
|
+
|
|
173
|
+
def list_repositories(self, instance_id: str, namespace_name: str):
|
|
174
|
+
"""
|
|
175
|
+
description:
|
|
176
|
+
列出仓库
|
|
177
|
+
parameters:
|
|
178
|
+
instance_id (str): 实例ID
|
|
179
|
+
namespace_name (str): 命名空间名称
|
|
180
|
+
return:
|
|
181
|
+
dict: 包含仓库列表的响应数据
|
|
182
|
+
success(bool): 是否成功
|
|
183
|
+
"""
|
|
184
|
+
try:
|
|
185
|
+
request = cr_20181201_models.ListRepositoryRequest(
|
|
186
|
+
instance_id=instance_id, repo_namespace_name=namespace_name
|
|
187
|
+
)
|
|
188
|
+
response = self.client.list_repository(request)
|
|
189
|
+
return response.body.to_map(), True
|
|
190
|
+
except Exception as e:
|
|
191
|
+
print(f"列出仓库失败: {e}")
|
|
192
|
+
return {}, False
|
|
193
|
+
|
|
194
|
+
def exists_repository(
|
|
195
|
+
self, instance_id: str, namespace_name: str, repository_name: str
|
|
196
|
+
):
|
|
197
|
+
"""
|
|
198
|
+
description:
|
|
199
|
+
判断仓库是否存在
|
|
200
|
+
parameters:
|
|
201
|
+
instance_id (str): 实例ID
|
|
202
|
+
namespace_name (str): 命名空间名称
|
|
203
|
+
repository_name (str): 仓库名称
|
|
204
|
+
return:
|
|
205
|
+
bool: 是否存在
|
|
206
|
+
"""
|
|
207
|
+
response, success = self.list_repositories(instance_id, namespace_name)
|
|
208
|
+
if not success:
|
|
209
|
+
return False, False
|
|
210
|
+
repository_names = [
|
|
211
|
+
repository["RepoName"] for repository in response["Repositories"]
|
|
212
|
+
]
|
|
213
|
+
return repository_name in repository_names, True
|
|
214
|
+
|
|
215
|
+
def delete_repository(
|
|
216
|
+
self, instance_id: str, namespace_name: str, repository_id: str
|
|
217
|
+
):
|
|
218
|
+
"""
|
|
219
|
+
description:
|
|
220
|
+
删除仓库
|
|
221
|
+
parameters:
|
|
222
|
+
instance_id (str): 实例ID
|
|
223
|
+
namespace_name (str): 命名空间名称
|
|
224
|
+
repository_id (str): 仓库ID
|
|
225
|
+
return:
|
|
226
|
+
dict: 删除结果
|
|
227
|
+
success(bool): 是否成功
|
|
228
|
+
"""
|
|
229
|
+
try:
|
|
230
|
+
request = cr_20181201_models.DeleteRepositoryRequest(
|
|
231
|
+
instance_id=instance_id,
|
|
232
|
+
repo_namespace_name=namespace_name,
|
|
233
|
+
repo_id=repository_id,
|
|
234
|
+
)
|
|
235
|
+
response = self.client.delete_repository(request)
|
|
236
|
+
return response.body.to_map(), True
|
|
237
|
+
except Exception as e:
|
|
238
|
+
print(f"删除仓库失败: {e}")
|
|
239
|
+
return {}, False
|
|
240
|
+
|
|
241
|
+
def get_repository(
|
|
242
|
+
self, instance_id: str, namespace_name: str, repository_name: str
|
|
243
|
+
):
|
|
244
|
+
"""
|
|
245
|
+
description:
|
|
246
|
+
获取仓库详情信息
|
|
247
|
+
parameters:
|
|
248
|
+
instance_id (str): 实例ID
|
|
249
|
+
namespace_name (str): 命名空间名称
|
|
250
|
+
repository_name (str): 仓库名称
|
|
251
|
+
return:
|
|
252
|
+
dict: 详情信息
|
|
253
|
+
success(bool): 是否成功
|
|
254
|
+
"""
|
|
255
|
+
try:
|
|
256
|
+
request = cr_20181201_models.GetRepositoryRequest(
|
|
257
|
+
instance_id=instance_id,
|
|
258
|
+
repo_namespace_name=namespace_name,
|
|
259
|
+
repo_name=repository_name,
|
|
260
|
+
)
|
|
261
|
+
response = self.client.get_repository(request)
|
|
262
|
+
return response.body.to_map(), True
|
|
263
|
+
except Exception as e:
|
|
264
|
+
print(f"获取仓库详情失败: {e}")
|
|
265
|
+
return {}, False
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
class ACRUtilsAsync:
|
|
269
|
+
def __init__(self, region_id: str, access_key_id: str, access_key_secret: str):
|
|
270
|
+
self.region_id = region_id
|
|
271
|
+
self.access_key_id = access_key_id
|
|
272
|
+
self.access_key_secret = access_key_secret
|
|
273
|
+
self.client = self._create_client()
|
|
274
|
+
|
|
275
|
+
def _create_client(self):
|
|
276
|
+
credential = CredentialClient()
|
|
277
|
+
config = open_api_models.Config(
|
|
278
|
+
credential=credential,
|
|
279
|
+
access_key_id=self.access_key_id,
|
|
280
|
+
access_key_secret=self.access_key_secret,
|
|
281
|
+
)
|
|
282
|
+
# Endpoint 请参考 https://api.aliyun.com/product/cr
|
|
283
|
+
config.endpoint = f"cr.{self.region_id}.aliyuncs.com"
|
|
284
|
+
return cr20181201Client(config)
|
|
285
|
+
|
|
286
|
+
async def list_instances(self):
|
|
287
|
+
"""
|
|
288
|
+
description:
|
|
289
|
+
列出容器镜像服务实例
|
|
290
|
+
parameters:
|
|
291
|
+
None
|
|
292
|
+
return:
|
|
293
|
+
dict: 包含实例列表的响应数据
|
|
294
|
+
success(bool): 是否成功
|
|
295
|
+
"""
|
|
296
|
+
|
|
297
|
+
try:
|
|
298
|
+
request = cr_20181201_models.ListInstanceRequest()
|
|
299
|
+
response = await self.client.list_instance_async(request)
|
|
300
|
+
return response.body.to_map(), True
|
|
301
|
+
except Exception as e:
|
|
302
|
+
print(f"列出实例失败: {e}")
|
|
303
|
+
return {}, False
|
|
304
|
+
|
|
305
|
+
async def create_namespace(self, instance_id: str, namespace_name: str):
|
|
306
|
+
"""
|
|
307
|
+
description:
|
|
308
|
+
创建命名空间
|
|
309
|
+
parameters:
|
|
310
|
+
instance_id (str): 实例ID
|
|
311
|
+
namespace_name (str): 命名空间名称
|
|
312
|
+
return:
|
|
313
|
+
dict: 创建结果
|
|
314
|
+
success(bool): 是否成功
|
|
315
|
+
"""
|
|
316
|
+
|
|
317
|
+
try:
|
|
318
|
+
request = cr_20181201_models.CreateNamespaceRequest(
|
|
319
|
+
instance_id=instance_id, namespace_name=namespace_name
|
|
320
|
+
)
|
|
321
|
+
response = await self.client.create_namespace_async(request)
|
|
322
|
+
return response.body.to_map(), True
|
|
323
|
+
except Exception as e:
|
|
324
|
+
print(f"创建命名空间失败: {e}")
|
|
325
|
+
return {}, False
|
|
326
|
+
|
|
327
|
+
async def delete_namespace(self, instance_id: str, namespace_name: str):
|
|
328
|
+
"""
|
|
329
|
+
description:
|
|
330
|
+
删除命名空间
|
|
331
|
+
parameters:
|
|
332
|
+
instance_id (str): 实例ID
|
|
333
|
+
namespace_name (str): 命名空间名称
|
|
334
|
+
return:
|
|
335
|
+
dict: 删除结果
|
|
336
|
+
success(bool): 是否成功
|
|
337
|
+
"""
|
|
338
|
+
try:
|
|
339
|
+
request = cr_20181201_models.DeleteNamespaceRequest(
|
|
340
|
+
instance_id=instance_id, namespace_name=namespace_name
|
|
341
|
+
)
|
|
342
|
+
response = await self.client.delete_namespace_async(request)
|
|
343
|
+
return response.body.to_map(), True
|
|
344
|
+
except Exception as e:
|
|
345
|
+
print(f"删除命名空间失败: {e}")
|
|
346
|
+
return {}, False
|
|
347
|
+
|
|
348
|
+
async def list_namespaces(self, instance_id: str):
|
|
349
|
+
"""
|
|
350
|
+
description:
|
|
351
|
+
列出命名空间
|
|
352
|
+
parameters:
|
|
353
|
+
instance_id (str): 实例ID
|
|
354
|
+
return:
|
|
355
|
+
dict: 包含命名空间列表的响应数据
|
|
356
|
+
success(bool): 是否成功
|
|
357
|
+
"""
|
|
358
|
+
try:
|
|
359
|
+
request = cr_20181201_models.ListNamespaceRequest(instance_id=instance_id)
|
|
360
|
+
response = await self.client.list_namespace_async(request)
|
|
361
|
+
return response.body.to_map(), True
|
|
362
|
+
except Exception as e:
|
|
363
|
+
print(f"列出命名空间失败: {e}")
|
|
364
|
+
return {}, False
|
|
365
|
+
|
|
366
|
+
async def exists_namespace(self, instance_id: str, namespace_name: str):
|
|
367
|
+
"""
|
|
368
|
+
description:
|
|
369
|
+
判断命名空间是否存在
|
|
370
|
+
parameters:
|
|
371
|
+
instance_id (str): 实例ID
|
|
372
|
+
namespace_name (str): 命名空间名称
|
|
373
|
+
return:
|
|
374
|
+
bool: 是否存在
|
|
375
|
+
"""
|
|
376
|
+
response, success = await self.list_namespaces(instance_id)
|
|
377
|
+
if not success:
|
|
378
|
+
return False, False
|
|
379
|
+
namespace_names = [
|
|
380
|
+
namespace["NamespaceName"] for namespace in response["Namespaces"]
|
|
381
|
+
]
|
|
382
|
+
return namespace_name in namespace_names, True
|
|
383
|
+
|
|
384
|
+
async def create_repository(
|
|
385
|
+
self,
|
|
386
|
+
instance_id: str,
|
|
387
|
+
namespace_name: str,
|
|
388
|
+
repository_name: str,
|
|
389
|
+
repository_type: str,
|
|
390
|
+
summary: str,
|
|
391
|
+
):
|
|
392
|
+
"""
|
|
393
|
+
description:
|
|
394
|
+
创建仓库
|
|
395
|
+
parameters:
|
|
396
|
+
instance_id (str): 实例ID
|
|
397
|
+
namespace_name (str): 命名空间名称
|
|
398
|
+
repository_name (str): 仓库名称
|
|
399
|
+
repository_type (str): 仓库类型
|
|
400
|
+
summary (str): 仓库摘要
|
|
401
|
+
return:
|
|
402
|
+
dict: 创建结果
|
|
403
|
+
success(bool): 是否成功
|
|
404
|
+
"""
|
|
405
|
+
if not re.fullmatch(r"[a-z\-]+", repository_name):
|
|
406
|
+
print("仓库名称只能包含小写英文字母和横杠")
|
|
407
|
+
return {}, False
|
|
408
|
+
try:
|
|
409
|
+
request = cr_20181201_models.CreateRepositoryRequest(
|
|
410
|
+
instance_id=instance_id,
|
|
411
|
+
repo_namespace_name=namespace_name,
|
|
412
|
+
repo_name=repository_name,
|
|
413
|
+
repo_type=repository_type,
|
|
414
|
+
summary=summary,
|
|
415
|
+
)
|
|
416
|
+
response = await self.client.create_repository_async(request)
|
|
417
|
+
return response.body.to_map(), True
|
|
418
|
+
except Exception as e:
|
|
419
|
+
print(f"创建仓库失败: {e}")
|
|
420
|
+
return {}, False
|
|
421
|
+
|
|
422
|
+
async def list_repositories(self, instance_id: str, namespace_name: str):
|
|
423
|
+
"""
|
|
424
|
+
description:
|
|
425
|
+
列出仓库
|
|
426
|
+
parameters:
|
|
427
|
+
instance_id (str): 实例ID
|
|
428
|
+
namespace_name (str): 命名空间名称
|
|
429
|
+
return:
|
|
430
|
+
dict: 包含仓库列表的响应数据
|
|
431
|
+
success(bool): 是否成功
|
|
432
|
+
"""
|
|
433
|
+
try:
|
|
434
|
+
request = cr_20181201_models.ListRepositoryRequest(
|
|
435
|
+
instance_id=instance_id, repo_namespace_name=namespace_name
|
|
436
|
+
)
|
|
437
|
+
response = await self.client.list_repository_async(request)
|
|
438
|
+
return response.body.to_map(), True
|
|
439
|
+
except Exception as e:
|
|
440
|
+
print(f"列出仓库失败: {e}")
|
|
441
|
+
return {}, False
|
|
442
|
+
|
|
443
|
+
async def exists_repository(
|
|
444
|
+
self, instance_id: str, namespace_name: str, repository_name: str
|
|
445
|
+
):
|
|
446
|
+
"""
|
|
447
|
+
description:
|
|
448
|
+
判断仓库是否存在
|
|
449
|
+
parameters:
|
|
450
|
+
instance_id (str): 实例ID
|
|
451
|
+
namespace_name (str): 命名空间名称
|
|
452
|
+
repository_name (str): 仓库名称
|
|
453
|
+
return:
|
|
454
|
+
bool: 是否存在
|
|
455
|
+
"""
|
|
456
|
+
response, success = await self.list_repositories(instance_id, namespace_name)
|
|
457
|
+
if not success:
|
|
458
|
+
return False, False
|
|
459
|
+
repository_names = [
|
|
460
|
+
repository["RepoName"] for repository in response["Repositories"]
|
|
461
|
+
]
|
|
462
|
+
return repository_name in repository_names, True
|
|
463
|
+
|
|
464
|
+
async def delete_repository(
|
|
465
|
+
self, instance_id: str, namespace_name: str, repository_id: str
|
|
466
|
+
):
|
|
467
|
+
"""
|
|
468
|
+
description:
|
|
469
|
+
删除仓库
|
|
470
|
+
parameters:
|
|
471
|
+
instance_id (str): 实例ID
|
|
472
|
+
namespace_name (str): 命名空间名称
|
|
473
|
+
repository_id (str): 仓库ID
|
|
474
|
+
return:
|
|
475
|
+
dict: 删除结果
|
|
476
|
+
success(bool): 是否成功
|
|
477
|
+
"""
|
|
478
|
+
try:
|
|
479
|
+
request = cr_20181201_models.DeleteRepositoryRequest(
|
|
480
|
+
instance_id=instance_id,
|
|
481
|
+
repo_namespace_name=namespace_name,
|
|
482
|
+
repo_id=repository_id,
|
|
483
|
+
)
|
|
484
|
+
response = await self.client.delete_repository_async(request)
|
|
485
|
+
return response.body.to_map(), True
|
|
486
|
+
except Exception as e:
|
|
487
|
+
print(f"删除仓库失败: {e}")
|
|
488
|
+
return {}, False
|
|
489
|
+
|
|
490
|
+
async def get_repository(
|
|
491
|
+
self, instance_id: str, namespace_name: str, repository_name: str
|
|
492
|
+
):
|
|
493
|
+
"""
|
|
494
|
+
description:
|
|
495
|
+
获取仓库详情信息
|
|
496
|
+
parameters:
|
|
497
|
+
instance_id (str): 实例ID
|
|
498
|
+
namespace_name (str): 命名空间名称
|
|
499
|
+
repository_name (str): 仓库名称
|
|
500
|
+
return:
|
|
501
|
+
dict: 详情信息
|
|
502
|
+
success(bool): 是否成功
|
|
503
|
+
"""
|
|
504
|
+
try:
|
|
505
|
+
request = cr_20181201_models.GetRepositoryRequest(
|
|
506
|
+
instance_id=instance_id,
|
|
507
|
+
repo_namespace_name=namespace_name,
|
|
508
|
+
repo_name=repository_name,
|
|
509
|
+
)
|
|
510
|
+
response = await self.client.get_repository_async(request)
|
|
511
|
+
return response.body.to_map(), True
|
|
512
|
+
except Exception as e:
|
|
513
|
+
print(f"获取仓库详情失败: {e}")
|
|
514
|
+
return {}, False
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import asyncio
|
|
1
2
|
import os
|
|
2
3
|
import time
|
|
3
4
|
import random
|
|
@@ -122,9 +123,130 @@ class AliyunEmailSender:
|
|
|
122
123
|
if response.status_code == 200:
|
|
123
124
|
return True
|
|
124
125
|
else:
|
|
125
|
-
|
|
126
|
+
print(f"发送验证邮件失败: {response}")
|
|
126
127
|
return False
|
|
127
128
|
|
|
128
129
|
except Exception as e:
|
|
129
|
-
|
|
130
|
+
print(traceback.format_exc())
|
|
131
|
+
return False
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class AliyunEmailSenderAsync:
|
|
135
|
+
def __init__(self, access_key_id, access_key_secret):
|
|
136
|
+
self.client = Dm20151123Client(
|
|
137
|
+
open_api_models.Config(
|
|
138
|
+
access_key_id=access_key_id,
|
|
139
|
+
access_key_secret=access_key_secret,
|
|
140
|
+
endpoint="dm.aliyuncs.com",
|
|
141
|
+
)
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
async def generate_verification_code(self, length=6):
|
|
145
|
+
"""
|
|
146
|
+
description:
|
|
147
|
+
生成数字验证码
|
|
148
|
+
parameters:
|
|
149
|
+
length(int): 验证码长度
|
|
150
|
+
return:
|
|
151
|
+
str: 验证码
|
|
152
|
+
"""
|
|
153
|
+
return await asyncio.to_thread(
|
|
154
|
+
lambda: "".join(str(random.randint(0, 9)) for _ in range(length))
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
def create_verification_email_content(
|
|
158
|
+
self, username, verification_code, email_type, validity=15
|
|
159
|
+
):
|
|
160
|
+
"""
|
|
161
|
+
description:
|
|
162
|
+
创建邮件HTML内容
|
|
163
|
+
parameters:
|
|
164
|
+
username(str): 用户名
|
|
165
|
+
validity(int): 验证码有效期
|
|
166
|
+
return:
|
|
167
|
+
str: 邮件HTML内容
|
|
168
|
+
"""
|
|
169
|
+
email_type_str = "注册" if email_type == "register" else "找回密码"
|
|
170
|
+
return f"""
|
|
171
|
+
<html>
|
|
172
|
+
<head>
|
|
173
|
+
<style>
|
|
174
|
+
body {{ font-family: Arial, sans-serif; line-height: 1.6; }}
|
|
175
|
+
.container {{ max-width: 600px; margin: 0 auto; padding: 20px; border: 1px solid #e1e8f7; border-radius: 10px; }}
|
|
176
|
+
.header {{ background: linear-gradient(to right, #1a2980, #26d0ce); color: white; padding: 15px; text-align: center; border-radius: 8px; }}
|
|
177
|
+
.code {{ font-size: 28px; font-weight: bold; text-align: center; margin: 25px 0; letter-spacing: 5px; color: #e74c3c; }}
|
|
178
|
+
.footer {{ margin-top: 30px; text-align: center; color: #777; font-size: 12px; }}
|
|
179
|
+
</style>
|
|
180
|
+
</head>
|
|
181
|
+
<body>
|
|
182
|
+
<div class="container">
|
|
183
|
+
<div class="header">
|
|
184
|
+
<h2>北京矩阵像素科技有限公司</h2>
|
|
185
|
+
</div>
|
|
186
|
+
|
|
187
|
+
<p>尊敬的 <strong>{username}</strong>:</p>
|
|
188
|
+
<p>您正在进行邮箱{email_type_str}验证,您的验证码为:</p>
|
|
189
|
+
<div class="code">{verification_code}</div>
|
|
190
|
+
<p>该验证码 <strong>{validity}分钟</strong> 内有效,请尽快完成验证。</p>
|
|
191
|
+
<p>如非本人操作,请忽略此邮件。</p>
|
|
192
|
+
|
|
193
|
+
<div class="footer">
|
|
194
|
+
<p>阿里云邮件推送服务 | 安全验证</p>
|
|
195
|
+
<p>© {time.strftime('%Y')} 企业注册系统 版权所有</p>
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
</body>
|
|
199
|
+
</html>
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
def get_email_subject(self, email_type):
|
|
203
|
+
if email_type == "register":
|
|
204
|
+
return "您的注册验证码"
|
|
205
|
+
elif email_type == "forgot":
|
|
206
|
+
return "您的找回密码验证码"
|
|
207
|
+
else:
|
|
208
|
+
return "您的验证码"
|
|
209
|
+
|
|
210
|
+
async def send_verification_email(
|
|
211
|
+
self, to_address, verification_code, email_type, username="用户"
|
|
212
|
+
):
|
|
213
|
+
"""
|
|
214
|
+
description:
|
|
215
|
+
发送验证邮件
|
|
216
|
+
parameters:
|
|
217
|
+
to_address(str): 收件人邮箱地址
|
|
218
|
+
email_type(str): 邮件类型
|
|
219
|
+
username(str): 用户名
|
|
220
|
+
return:
|
|
221
|
+
dict: 发送结果
|
|
222
|
+
"""
|
|
223
|
+
assert email_type in [
|
|
224
|
+
"register",
|
|
225
|
+
"forgot",
|
|
226
|
+
], "邮件类型错误,当前只有register和forgot两种类型"
|
|
227
|
+
try:
|
|
228
|
+
response = await self.client.single_send_mail_with_options_async(
|
|
229
|
+
dm_20151123_models.SingleSendMailRequest(
|
|
230
|
+
account_name="captcha_new@captcha.pixelarrayai.com",
|
|
231
|
+
address_type=1,
|
|
232
|
+
to_address=to_address,
|
|
233
|
+
subject=self.get_email_subject(email_type),
|
|
234
|
+
html_body=self.create_verification_email_content(
|
|
235
|
+
username=username,
|
|
236
|
+
verification_code=verification_code,
|
|
237
|
+
email_type=email_type,
|
|
238
|
+
),
|
|
239
|
+
reply_to_address=False,
|
|
240
|
+
from_alias="系统验证", # 发信人昵称
|
|
241
|
+
),
|
|
242
|
+
util_models.RuntimeOptions(),
|
|
243
|
+
)
|
|
244
|
+
if response.status_code == 200:
|
|
245
|
+
return True
|
|
246
|
+
else:
|
|
247
|
+
print(f"发送验证邮件失败: {response}")
|
|
248
|
+
return False
|
|
249
|
+
|
|
250
|
+
except Exception as e:
|
|
251
|
+
print(traceback.format_exc())
|
|
130
252
|
return False
|
|
@@ -244,7 +244,7 @@ class BillingUtils:
|
|
|
244
244
|
return formatted_response
|
|
245
245
|
|
|
246
246
|
except Exception as e:
|
|
247
|
-
|
|
247
|
+
print(traceback.format_exc())
|
|
248
248
|
error_response = {
|
|
249
249
|
"Message": add_comment(f"Format error: {str(e)}", "Message"),
|
|
250
250
|
"RequestId": add_comment(
|
|
@@ -326,7 +326,7 @@ class BillingUtils:
|
|
|
326
326
|
return formatted_response
|
|
327
327
|
|
|
328
328
|
except Exception as error:
|
|
329
|
-
|
|
329
|
+
print(traceback.format_exc())
|
|
330
330
|
error_msg = getattr(error, "message", str(error))
|
|
331
331
|
# 诊断地址
|
|
332
332
|
diagnose_url = ""
|
|
@@ -473,5 +473,5 @@ class BillingUtils:
|
|
|
473
473
|
return output_path
|
|
474
474
|
|
|
475
475
|
except Exception as e:
|
|
476
|
-
|
|
476
|
+
print(traceback.format_exc())
|
|
477
477
|
raise Exception(f"保存账单数据失败: {str(e)}")
|