gomyck-tools 1.3.1__py3-none-any.whl → 1.3.2__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.
- ctools/__init__.py +0 -0
- ctools/aes_tools.py +35 -0
- ctools/api_result.py +55 -0
- ctools/application.py +386 -0
- ctools/b64.py +7 -0
- ctools/bashPath.py +13 -0
- ctools/bottle_web_base.py +169 -0
- ctools/bottle_webserver.py +143 -0
- ctools/bottle_websocket.py +75 -0
- ctools/browser_element_tools.py +314 -0
- ctools/call.py +71 -0
- ctools/cftp.py +74 -0
- ctools/cjson.py +54 -0
- ctools/ckafka.py +159 -0
- ctools/compile_tools.py +18 -0
- ctools/console.py +55 -0
- ctools/coord_trans.py +127 -0
- ctools/credis.py +111 -0
- ctools/cron_lite.py +252 -0
- ctools/ctoken.py +34 -0
- ctools/cword.py +30 -0
- ctools/czip.py +130 -0
- ctools/database.py +185 -0
- ctools/date_utils.py +43 -0
- ctools/dict_wrapper.py +20 -0
- ctools/douglas_rarefy.py +136 -0
- ctools/download_tools.py +57 -0
- ctools/enums.py +4 -0
- ctools/ex.py +31 -0
- ctools/excelOpt.py +36 -0
- ctools/html_soup.py +35 -0
- ctools/http_utils.py +24 -0
- ctools/images_tools.py +27 -0
- ctools/imgDialog.py +44 -0
- ctools/metrics.py +131 -0
- ctools/mqtt_utils.py +289 -0
- ctools/obj.py +20 -0
- ctools/pacth.py +74 -0
- ctools/plan_area_tools.py +97 -0
- ctools/process_pool.py +36 -0
- ctools/pty_tools.py +72 -0
- ctools/resource_bundle_tools.py +121 -0
- ctools/rsa.py +70 -0
- ctools/screenshot_tools.py +127 -0
- ctools/sign.py +20 -0
- ctools/sm_tools.py +49 -0
- ctools/snow_id.py +76 -0
- ctools/str_diff.py +20 -0
- ctools/string_tools.py +85 -0
- ctools/sys_info.py +157 -0
- ctools/sys_log.py +89 -0
- ctools/thread_pool.py +35 -0
- ctools/upload_tools.py +40 -0
- ctools/win_canvas.py +83 -0
- ctools/win_control.py +106 -0
- ctools/word_fill.py +562 -0
- ctools/word_fill_entity.py +46 -0
- ctools/work_path.py +69 -0
- {gomyck_tools-1.3.1.dist-info → gomyck_tools-1.3.2.dist-info}/METADATA +1 -1
- gomyck_tools-1.3.2.dist-info/RECORD +62 -0
- gomyck_tools-1.3.2.dist-info/top_level.txt +1 -0
- gomyck_tools-1.3.1.dist-info/RECORD +0 -4
- gomyck_tools-1.3.1.dist-info/top_level.txt +0 -1
- {gomyck_tools-1.3.1.dist-info → gomyck_tools-1.3.2.dist-info}/WHEEL +0 -0
ctools/__init__.py
ADDED
File without changes
|
ctools/aes_tools.py
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
from cryptography.fernet import Fernet
|
2
|
+
|
3
|
+
def generate_key():
|
4
|
+
"""
|
5
|
+
生成 AES key
|
6
|
+
Returns 32 bytes key
|
7
|
+
-------
|
8
|
+
"""
|
9
|
+
key = Fernet.generate_key()
|
10
|
+
return key.decode()
|
11
|
+
|
12
|
+
def aes_encrypt(sec_key, plaintext):
|
13
|
+
"""
|
14
|
+
aes加密
|
15
|
+
:param sec_key: 加密 key
|
16
|
+
:param plaintext: 明文信息
|
17
|
+
:return: 加密后的信息
|
18
|
+
"""
|
19
|
+
cipher = Fernet(sec_key)
|
20
|
+
ciphertext = cipher.encrypt(plaintext.encode())
|
21
|
+
return ciphertext.decode()
|
22
|
+
|
23
|
+
|
24
|
+
def aes_decrypt(sec_key, ciphertext):
|
25
|
+
"""
|
26
|
+
aes解密
|
27
|
+
:param sec_key: 加密 key
|
28
|
+
:param ciphertext: 密文
|
29
|
+
:return: 解密后的明文信息
|
30
|
+
"""
|
31
|
+
cipher = Fernet(sec_key)
|
32
|
+
decrypted_data = cipher.decrypt(ciphertext)
|
33
|
+
return decrypted_data.decode()
|
34
|
+
|
35
|
+
|
ctools/api_result.py
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
from ctools import cjson
|
2
|
+
|
3
|
+
cjson.str_value_keys = [
|
4
|
+
"obj_id",
|
5
|
+
]
|
6
|
+
|
7
|
+
|
8
|
+
class _ResEnum(object):
|
9
|
+
def __init__(self, code: int, message: str):
|
10
|
+
self.code = code
|
11
|
+
self.message = message
|
12
|
+
|
13
|
+
def __eq__(self, o: object) -> bool:
|
14
|
+
return self.code == o
|
15
|
+
|
16
|
+
class R(object):
|
17
|
+
class Code:
|
18
|
+
|
19
|
+
@staticmethod
|
20
|
+
def cus_code(code, msg):
|
21
|
+
return _ResEnum(code, msg)
|
22
|
+
|
23
|
+
SUCCESS = _ResEnum(200, "成功")
|
24
|
+
FAIL = _ResEnum(400, "失败")
|
25
|
+
ERROR = _ResEnum(500, "异常")
|
26
|
+
|
27
|
+
def __init__(self, code: int, message: str, data=""):
|
28
|
+
self.code = code
|
29
|
+
self.message = message
|
30
|
+
self.data = data
|
31
|
+
|
32
|
+
def _to_json_str(self):
|
33
|
+
return cjson.unify_to_str(cjson.dumps(self))
|
34
|
+
|
35
|
+
@staticmethod
|
36
|
+
def parser(r_json: str):
|
37
|
+
return R(**cjson.loads(r_json))
|
38
|
+
|
39
|
+
@staticmethod
|
40
|
+
def ok(data=None, resp=Code.SUCCESS, msg=None, to_json_str=True):
|
41
|
+
if not to_json_str:
|
42
|
+
return R(resp.code, msg if msg is not None else resp.message, data)
|
43
|
+
return R(resp.code, msg if msg is not None else resp.message, data)._to_json_str()
|
44
|
+
|
45
|
+
@staticmethod
|
46
|
+
def fail(msg=None, resp=Code.FAIL, data=None, to_json_str=True):
|
47
|
+
if not to_json_str:
|
48
|
+
return R(resp.code, msg if msg is not None else resp.message, data)
|
49
|
+
return R(resp.code, msg if msg is not None else resp.message, data)._to_json_str()
|
50
|
+
|
51
|
+
@staticmethod
|
52
|
+
def error(msg=None, resp=Code.ERROR, data=None, to_json_str=True):
|
53
|
+
if not to_json_str:
|
54
|
+
return R(resp.code, msg if msg is not None else resp.message, data)
|
55
|
+
return R(resp.code, msg if msg is not None else resp.message, data)._to_json_str()
|
ctools/application.py
ADDED
@@ -0,0 +1,386 @@
|
|
1
|
+
import os
|
2
|
+
import shutil
|
3
|
+
import socket
|
4
|
+
import subprocess
|
5
|
+
import time
|
6
|
+
import zipfile
|
7
|
+
from configparser import ConfigParser
|
8
|
+
|
9
|
+
from PIL import Image
|
10
|
+
|
11
|
+
from ctools import work_path, call, sign, thread_pool
|
12
|
+
from ctools.sys_info import get_os_architecture, get_os_architecture4x
|
13
|
+
|
14
|
+
"""
|
15
|
+
本模块引用的依赖, 不允许引用sys_log模块, 否则将报错: 循环引用
|
16
|
+
打印使用print即可, 程序启动后, 会自动接管为log
|
17
|
+
"""
|
18
|
+
|
19
|
+
|
20
|
+
class Server:
|
21
|
+
version = '' # 版本信息
|
22
|
+
port = 8010 # 服务端口
|
23
|
+
metricsPort = 8011 # 获取指标信息端口
|
24
|
+
wsPort = 8012 # websocket服务端口
|
25
|
+
startTime = time.time() # 系统启动时间
|
26
|
+
baseWorkPath = work_path.get_user_work_path('rpa') # 基础的工作目录
|
27
|
+
sysLogPath = os.path.join(baseWorkPath, ".logs") # 系统日志存储目录
|
28
|
+
pythonHome = os.path.join(work_path.get_Users_path(), 'Public/python-3.8.9') # python根目录
|
29
|
+
indicatorsPath = os.path.join(baseWorkPath, "indicators") # 指标信息存储目录
|
30
|
+
screenshotPath = os.path.join(baseWorkPath, "screenshot") # 截图存储目录
|
31
|
+
controlServerAddr = None
|
32
|
+
|
33
|
+
|
34
|
+
class Authorization:
|
35
|
+
enabled = True # 访问权限验证开关
|
36
|
+
whitelist = []
|
37
|
+
|
38
|
+
|
39
|
+
class Database:
|
40
|
+
url_biz = os.path.join(work_path.get_user_work_path(), "AppData/Local/SystemWin32Core", 'systemRDB') # 数据库文件地址
|
41
|
+
url_func = os.path.join(work_path.get_user_work_path(), "AppData/Local/SystemWin32Core", 'explorerSearch') # 函数库地址
|
42
|
+
url_auth = os.path.join(work_path.get_user_work_path(), "AppData/Local/SystemWin32Core", 'tmp') # 授权库地址
|
43
|
+
pool_size = 5
|
44
|
+
max_overflow = 25
|
45
|
+
|
46
|
+
|
47
|
+
class Upload:
|
48
|
+
upload_path = os.path.join(Server.baseWorkPath, 'uploads') # 上传文件存储目录
|
49
|
+
driver_path = os.path.join(Server.baseWorkPath, 'uploadDriver') # 上传驱动存储目录
|
50
|
+
source_pkg_path = os.path.join(Server.baseWorkPath, 'source_pkg') # 资源中心位置
|
51
|
+
module_path = os.path.join(Server.baseWorkPath, 'module_path') # 上传组件存储目录
|
52
|
+
|
53
|
+
|
54
|
+
class Schedule:
|
55
|
+
rpa_script_path = os.path.join(work_path.get_user_work_path(), ".cache/.tmp") # 执行流程存储目录
|
56
|
+
template_output_path = os.path.join(Server.baseWorkPath, 'document') # 使用模板生成的文档存储目录
|
57
|
+
play_wright_slow_mo = 0 # play wright每一步执行延时时间, 单位毫秒
|
58
|
+
|
59
|
+
|
60
|
+
@call.once
|
61
|
+
def sync_config():
|
62
|
+
path = os.path.join(work_path.get_app_path(), "application.ini")
|
63
|
+
conf = ConfigParser()
|
64
|
+
if os.path.exists(path):
|
65
|
+
conf.read(path)
|
66
|
+
sections = conf.sections()
|
67
|
+
######### Server 必须放在第一个初始化的位置 ########
|
68
|
+
if "Server" in sections:
|
69
|
+
server_conf = conf['Server']
|
70
|
+
Server.version = server_conf['version']
|
71
|
+
Server.port = int(server_conf['port'])
|
72
|
+
Server.baseWorkPath = work_path.get_user_work_path(server_conf['baseWorkPath'])
|
73
|
+
Server.sysLogPath = os.path.join(Server.baseWorkPath, ".logs")
|
74
|
+
######### Server 必须放在第一个初始化的位置 ########
|
75
|
+
if "Database" in sections:
|
76
|
+
database = conf['Database']
|
77
|
+
Database.url_biz = database['url_biz'] if database['url_biz'] else Database.url_biz
|
78
|
+
Database.url_func = database['url_func'] if database['url_func'] else Database.url_func
|
79
|
+
Database.url_auth = database['url_auth'] if database['url_auth'] else Database.url_auth
|
80
|
+
Database.pool_size = int(database['poolSize'])
|
81
|
+
Database.max_overflow = int(database['maxOverflow'])
|
82
|
+
if "Upload" in sections:
|
83
|
+
upload_path_section = conf['Upload']
|
84
|
+
Upload.upload_path = os.path.join(Server.baseWorkPath, upload_path_section['path'])
|
85
|
+
Upload.driver_path = os.path.join(Server.baseWorkPath, upload_path_section['driver_path'])
|
86
|
+
Upload.source_pkg_path = os.path.join(Server.baseWorkPath, upload_path_section['source_pkg_path'])
|
87
|
+
if "Schedule" in sections:
|
88
|
+
schedule = conf['Schedule']
|
89
|
+
Schedule.template_output_path = os.path.join(Server.baseWorkPath, schedule['templateOutputPath'])
|
90
|
+
Schedule.play_wright_slow_mo = schedule['playWrightSlowMo']
|
91
|
+
if "Authorization" in sections:
|
92
|
+
auth = conf['Authorization']
|
93
|
+
Authorization.enabled = bool(int(auth['enabled']))
|
94
|
+
whitelist_paths = [w.strip() for w in auth['whitelist'].split(',')]
|
95
|
+
Authorization.whitelist.extend(whitelist_paths)
|
96
|
+
|
97
|
+
os.makedirs(Server.baseWorkPath, exist_ok=True)
|
98
|
+
os.makedirs(Server.indicatorsPath, exist_ok=True)
|
99
|
+
os.makedirs(Server.screenshotPath, exist_ok=True)
|
100
|
+
os.makedirs(Upload.source_pkg_path, exist_ok=True)
|
101
|
+
os.makedirs(Schedule.template_output_path, exist_ok=True)
|
102
|
+
os.makedirs(Upload.module_path, exist_ok=True)
|
103
|
+
else:
|
104
|
+
print('配置文件不存在!')
|
105
|
+
raise Exception('配置文件不存在!')
|
106
|
+
|
107
|
+
|
108
|
+
def check_database():
|
109
|
+
db_file = os.path.join(work_path.get_user_work_path(), "AppData/Local/SystemWin32Core", 'systemRDB')
|
110
|
+
if os.path.exists(db_file):
|
111
|
+
db_size = os.path.getsize(db_file)
|
112
|
+
if db_size < 2000 * 1024: os.remove(db_file)
|
113
|
+
db_file = os.path.join(work_path.get_user_work_path(), "AppData/Local/SystemWin32Core", 'tmp')
|
114
|
+
if os.path.exists(db_file):
|
115
|
+
db_size = os.path.getsize(db_file)
|
116
|
+
if db_size < 2000 * 1024: os.remove(db_file)
|
117
|
+
|
118
|
+
def validate_sign():
|
119
|
+
sign_app_val = sign.generate_signature(work_path.get_install_path('rpa-server.exe'))
|
120
|
+
with open(work_path.get_install_path('rpa-server.sign')) as sign_file:
|
121
|
+
sign_file_val = sign_file.readline()
|
122
|
+
if 'rpa-dev-sign' == sign_file_val.strip(): return
|
123
|
+
if sign_app_val != sign_file_val: raise Exception('程序签名验证失败!!!')
|
124
|
+
|
125
|
+
|
126
|
+
def path_config():
|
127
|
+
print('start config path')
|
128
|
+
driverPath = Upload.driver_path
|
129
|
+
pythonPath = os.path.join(work_path.get_Users_path(), 'Public/python-3.8.9')
|
130
|
+
hplPath = os.path.join(work_path.get_user_work_path(), 'AppData/Local/ms-playwright')
|
131
|
+
taguiPath = os.path.join(work_path.get_user_work_path(), 'AppData/Roaming/tagui')
|
132
|
+
os.environ['PATH'] = os.pathsep.join([pythonPath, os.path.join(pythonPath, 'Scripts'), driverPath, os.path.join(hplPath, 'chromium-920619\chrome-win'), taguiPath, os.environ['PATH']])
|
133
|
+
print('end config path')
|
134
|
+
|
135
|
+
|
136
|
+
def sync_version(callFunc):
|
137
|
+
destFilePath = os.path.join(Server.baseWorkPath, "version")
|
138
|
+
driverPath = Upload.driver_path
|
139
|
+
pythonPath = os.path.join(work_path.get_Users_path(), 'Public/python-3.8.9')
|
140
|
+
msPlayPath = os.path.join(work_path.get_user_work_path(), 'AppData/Local/ms-playwright')
|
141
|
+
taguiPath = os.path.join(work_path.get_user_work_path(), 'AppData/Roaming/tagui')
|
142
|
+
if not os.path.exists(destFilePath):
|
143
|
+
try:
|
144
|
+
shutil.rmtree(pythonPath)
|
145
|
+
except Exception:
|
146
|
+
pass
|
147
|
+
try:
|
148
|
+
shutil.rmtree(msPlayPath)
|
149
|
+
except Exception:
|
150
|
+
pass
|
151
|
+
try:
|
152
|
+
shutil.rmtree(driverPath)
|
153
|
+
except Exception:
|
154
|
+
pass
|
155
|
+
try:
|
156
|
+
shutil.rmtree(taguiPath)
|
157
|
+
except Exception:
|
158
|
+
pass
|
159
|
+
from ctools.pacth import Patch
|
160
|
+
patch = Patch(oldVersion='V1.0.0', newVersion=Server.version, pythonPath=pythonPath, playwrightPath=msPlayPath, driverPath=driverPath)
|
161
|
+
patch.apply_patch()
|
162
|
+
if callFunc: callFunc()
|
163
|
+
with open(destFilePath, 'w') as newVersion:
|
164
|
+
newVersion.write(Server.version)
|
165
|
+
print('初始化安装, 版本信息为: {}'.format(Server.version))
|
166
|
+
newVersion.flush()
|
167
|
+
else:
|
168
|
+
with open(destFilePath, 'r') as oldVersion:
|
169
|
+
oldV = oldVersion.readline()
|
170
|
+
print('本地版本信息为: {}, 程序版本信息为: {}'.format(oldV, Server.version))
|
171
|
+
oldVersion.close()
|
172
|
+
if oldV == Server.version and '-snapshot' not in oldV: return
|
173
|
+
print('开始升级本地程序..')
|
174
|
+
from ctools.pacth import Patch
|
175
|
+
patch = Patch(oldVersion=oldV, newVersion=Server.version, pythonPath=pythonPath, playwrightPath=msPlayPath, driverPath=driverPath)
|
176
|
+
patch.apply_patch()
|
177
|
+
if callFunc: callFunc()
|
178
|
+
with open(destFilePath, 'w') as newVersion:
|
179
|
+
newVersion.write(Server.version)
|
180
|
+
print('程序升级成功, 更新版本信息为: {}'.format(Server.version))
|
181
|
+
newVersion.flush()
|
182
|
+
|
183
|
+
|
184
|
+
def sync_database():
|
185
|
+
for db in [['simpleYWI', 'systemRDB', False], ['simpleHSI', 'explorerSearch', True], ['simpleSQI', 'tmp', False]]:
|
186
|
+
dbPath = os.path.join(work_path.get_app_path(), "extension", db[0])
|
187
|
+
destDBPath = os.path.join(work_path.get_user_work_path(), "AppData/Local/SystemWin32Core")
|
188
|
+
os.makedirs(destDBPath, exist_ok=True)
|
189
|
+
destFilePath = os.path.join(destDBPath, db[1])
|
190
|
+
try:
|
191
|
+
if db[2] and os.path.exists(destFilePath): os.remove(destFilePath)
|
192
|
+
except Exception:
|
193
|
+
pass
|
194
|
+
if not os.path.exists(destFilePath):
|
195
|
+
shutil.copy(dbPath, destFilePath)
|
196
|
+
else:
|
197
|
+
pass
|
198
|
+
|
199
|
+
|
200
|
+
def sync_python_env():
|
201
|
+
print('SDK 开始安装...')
|
202
|
+
sourcePath = os.path.join(work_path.get_install_path(), "sources", 'python-3.8.9-{}.zip'.format(get_os_architecture()))
|
203
|
+
destPLPath = os.path.join(work_path.get_Users_path(), 'Public/python-3.8.9')
|
204
|
+
unzipPLPath = os.path.join(work_path.get_Users_path(), 'Public')
|
205
|
+
if not os.path.exists(destPLPath):
|
206
|
+
with zipfile.ZipFile(sourcePath, 'r') as zip_ref:
|
207
|
+
zip_ref.extractall(unzipPLPath)
|
208
|
+
print('SDK 安装成功(0)')
|
209
|
+
else:
|
210
|
+
print('SDK 安装成功(1)')
|
211
|
+
print('SDK 环境变量配置成功')
|
212
|
+
|
213
|
+
|
214
|
+
def sync_python_module():
|
215
|
+
print('SDK 安装模块开始')
|
216
|
+
module_list = []
|
217
|
+
|
218
|
+
if os.path.exists(Upload.module_path):
|
219
|
+
for file_name in os.listdir(Upload.module_path):
|
220
|
+
m_path = os.path.join(Upload.module_path, file_name)
|
221
|
+
module_list.append(m_path)
|
222
|
+
|
223
|
+
upgrade_module_path = os.path.join(work_path.get_install_path(), "sources/UpgradeModule")
|
224
|
+
init_flag = os.path.join(upgrade_module_path, "init")
|
225
|
+
upgrade_module_path = os.path.join(upgrade_module_path, get_os_architecture4x())
|
226
|
+
if not os.path.exists(init_flag) and os.path.exists(upgrade_module_path):
|
227
|
+
for file_name in os.listdir(upgrade_module_path):
|
228
|
+
m_path = os.path.join(upgrade_module_path, file_name)
|
229
|
+
module_list.append(m_path)
|
230
|
+
|
231
|
+
if module_list:
|
232
|
+
|
233
|
+
freeze_msg = ""
|
234
|
+
pip_path = os.path.join(Server.pythonHome, 'Scripts/pip.exe')
|
235
|
+
cmd = [pip_path, "freeze"]
|
236
|
+
proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
|
237
|
+
stderr=subprocess.PIPE, close_fds=True)
|
238
|
+
if proc:
|
239
|
+
stdout, stderr = proc.communicate()
|
240
|
+
freeze_msg = str(stdout)
|
241
|
+
|
242
|
+
for m_path in module_list:
|
243
|
+
if os.path.isfile(m_path):
|
244
|
+
installed = False
|
245
|
+
m = os.path.basename(m_path)
|
246
|
+
m_name = m.split("-")
|
247
|
+
if len(m_name) >= 2:
|
248
|
+
installed = "%s-%s" % (m_name[0], m_name[1]) in freeze_msg or "%s==%s" % (m_name[0], m_name[1]) in freeze_msg
|
249
|
+
|
250
|
+
if not installed:
|
251
|
+
cmd = [pip_path, "install", "--no-dependencies", m_path]
|
252
|
+
proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
|
253
|
+
stderr=subprocess.PIPE, close_fds=True)
|
254
|
+
if proc:
|
255
|
+
stdout, stderr = proc.communicate()
|
256
|
+
if stdout:
|
257
|
+
if "Successfully installed" in str(stdout) or "already installed" in str(stdout):
|
258
|
+
print("SDK 安装本地模块%s成功" % m)
|
259
|
+
else:
|
260
|
+
print("SDK 安装本地模块%s失败" % m)
|
261
|
+
|
262
|
+
if not os.path.exists(init_flag):
|
263
|
+
with open(init_flag, "w") as f:
|
264
|
+
f.write("success")
|
265
|
+
f.flush()
|
266
|
+
print('SDK 安装本地模块完成')
|
267
|
+
|
268
|
+
|
269
|
+
def sync_python_interpreter():
|
270
|
+
destPLPath = os.path.join(work_path.get_Users_path(), 'Public/python-3.8.9')
|
271
|
+
enhancePath = os.path.join(work_path.get_Users_path(), "Public/enhance", 'enhance-{}.zip'.format(get_os_architecture()))
|
272
|
+
originPath = os.path.join(work_path.get_Users_path(), "Public/enhance", 'origin-{}.zip'.format(get_os_architecture()))
|
273
|
+
initFlag = os.path.join(work_path.get_Users_path(), "Public/enhance", 'init')
|
274
|
+
flagValue = 'can not find dest dir'
|
275
|
+
os.makedirs(os.path.join(work_path.get_Users_path(), "Public/enhance"), exist_ok=True)
|
276
|
+
if not os.path.exists(initFlag):
|
277
|
+
try:
|
278
|
+
if os.path.exists(enhancePath):
|
279
|
+
with zipfile.ZipFile(enhancePath, 'r') as zip_ref:
|
280
|
+
zip_ref.extractall(destPLPath)
|
281
|
+
flagValue = 'enhance'
|
282
|
+
print('enhance enabled..')
|
283
|
+
elif os.path.exists(originPath):
|
284
|
+
with zipfile.ZipFile(originPath, 'r') as zip_ref:
|
285
|
+
zip_ref.extractall(destPLPath)
|
286
|
+
flagValue = 'origin'
|
287
|
+
print('origin enabled..')
|
288
|
+
with open(initFlag, 'w') as flag:
|
289
|
+
flag.write(flagValue)
|
290
|
+
print('interpreter init success: {}'.format(flagValue))
|
291
|
+
flag.flush()
|
292
|
+
except Exception as e:
|
293
|
+
print('interpreter init error: {}'.format(e))
|
294
|
+
else:
|
295
|
+
with open(initFlag, 'r') as flag:
|
296
|
+
flagValue = flag.readline()
|
297
|
+
print('interpreter flag: {}'.format(flagValue))
|
298
|
+
flag.close()
|
299
|
+
|
300
|
+
|
301
|
+
def sync_driver():
|
302
|
+
print('DRIVER 开始安装...')
|
303
|
+
sourcePath = os.path.join(work_path.get_install_path(), "sources", 'uploadDriver.zip')
|
304
|
+
destPLPath = Upload.driver_path
|
305
|
+
unzipPLPath = Upload.driver_path
|
306
|
+
if not os.path.exists(os.path.join(destPLPath)):
|
307
|
+
with zipfile.ZipFile(sourcePath, 'r') as zip_ref:
|
308
|
+
zip_ref.extractall(unzipPLPath)
|
309
|
+
print('DRIVER 安装成功(0)')
|
310
|
+
else:
|
311
|
+
print('DRIVER 安装成功(1)')
|
312
|
+
|
313
|
+
print('DRIVER 配置成功')
|
314
|
+
|
315
|
+
|
316
|
+
def sync_tagui():
|
317
|
+
print('HTG ENGINE 开始安装...')
|
318
|
+
plPath = os.path.join(work_path.get_install_path(), "sources", 'htg-source.zip')
|
319
|
+
destPLPath = os.path.join(work_path.get_user_work_path(), 'AppData/Roaming/tagui')
|
320
|
+
unzipPLPath = os.path.join(work_path.get_user_work_path(), 'AppData/Roaming/')
|
321
|
+
if not os.path.exists(os.path.join(destPLPath)):
|
322
|
+
with zipfile.ZipFile(plPath, 'r') as zip_ref:
|
323
|
+
zip_ref.extractall(unzipPLPath)
|
324
|
+
print('HTG ENGINE 依赖安装成功(0)')
|
325
|
+
else:
|
326
|
+
print('HTG ENGINE 依赖安装成功(1)')
|
327
|
+
|
328
|
+
|
329
|
+
def sync_playwright():
|
330
|
+
print('HPL ENGINE 开始安装...')
|
331
|
+
plPath = os.path.join(work_path.get_install_path(), "sources", 'ms-playwright.zip')
|
332
|
+
destPLPath = os.path.join(work_path.get_user_work_path(), 'AppData/Local/ms-playwright')
|
333
|
+
unzipPLPath = os.path.join(work_path.get_user_work_path(), 'AppData/Local/')
|
334
|
+
if not os.path.exists(os.path.join(destPLPath)):
|
335
|
+
with zipfile.ZipFile(plPath, 'r') as zip_ref:
|
336
|
+
zip_ref.extractall(unzipPLPath)
|
337
|
+
print('HPL ENGINE 依赖安装成功(0)')
|
338
|
+
else:
|
339
|
+
print('HPL ENGINE 依赖安装成功(1)')
|
340
|
+
|
341
|
+
def sync_paddleocr_model():
|
342
|
+
model_path = os.path.join(work_path.get_install_path(), "sources", 'paddleocr-model.zip')
|
343
|
+
paddleocr_path = work_path.get_user_work_path(".paddleocr")
|
344
|
+
if not os.path.exists(paddleocr_path) and os.path.exists(model_path) and get_os_architecture() == "64":
|
345
|
+
print('安装paddleocr模型文件开始')
|
346
|
+
with zipfile.ZipFile(model_path, 'r') as zip_ref:
|
347
|
+
zip_ref.extractall(paddleocr_path)
|
348
|
+
print('安装paddleocr模型文件结束')
|
349
|
+
|
350
|
+
|
351
|
+
def clean_temp_dir(target_file='rpa_ident'):
|
352
|
+
root_folder = work_path.get_user_temp_path()
|
353
|
+
print("Start clear cache...")
|
354
|
+
for folder_name in os.listdir(root_folder):
|
355
|
+
folder_path = os.path.join(root_folder, folder_name)
|
356
|
+
if os.path.isdir(folder_path):
|
357
|
+
file_path = os.path.join(folder_path, target_file)
|
358
|
+
if os.path.exists(file_path) and os.path.normpath(folder_path) != os.path.normpath(work_path.get_app_path()):
|
359
|
+
try:
|
360
|
+
shutil.rmtree(folder_path)
|
361
|
+
except Exception:
|
362
|
+
pass
|
363
|
+
print("End clear cache...")
|
364
|
+
shutil.rmtree(Schedule.rpa_script_path, ignore_errors=True)
|
365
|
+
|
366
|
+
|
367
|
+
def is_port_in_use(port):
|
368
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
369
|
+
return s.connect_ex(('localhost', port)) == 0
|
370
|
+
|
371
|
+
|
372
|
+
def system_tray(app_server):
|
373
|
+
pystray = __import__('pystray')
|
374
|
+
try:
|
375
|
+
def on_quit_clicked(app_icon):
|
376
|
+
thread_pool.submit(app_server.stop)
|
377
|
+
app_icon.stop()
|
378
|
+
|
379
|
+
image = Image.open(os.path.join(work_path.get_app_path(), "images/ico.png"))
|
380
|
+
menu = (
|
381
|
+
pystray.MenuItem(text='退出', action=on_quit_clicked),
|
382
|
+
)
|
383
|
+
icon = pystray.Icon("name", image, "RPA客户端", menu)
|
384
|
+
icon.run()
|
385
|
+
except Exception:
|
386
|
+
pass
|
ctools/b64.py
ADDED
ctools/bashPath.py
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
import inspect
|
2
|
+
import os
|
3
|
+
import sys
|
4
|
+
|
5
|
+
|
6
|
+
def path(subPath: str = '') -> str:
|
7
|
+
if getattr(sys, 'frozen', False):
|
8
|
+
base_path = sys._MEIPASS
|
9
|
+
else:
|
10
|
+
caller_frame = inspect.currentframe().f_back
|
11
|
+
caller_path = caller_frame.f_globals["__file__"]
|
12
|
+
base_path = os.path.dirname(caller_path)
|
13
|
+
return base_path + os.path.sep + subPath
|
@@ -0,0 +1,169 @@
|
|
1
|
+
import inspect
|
2
|
+
import threading
|
3
|
+
from functools import wraps
|
4
|
+
|
5
|
+
import bottle
|
6
|
+
from bottle import response, Bottle, request
|
7
|
+
|
8
|
+
from ctools import ctoken
|
9
|
+
from ctools.dict_wrapper import DictWrapper
|
10
|
+
from ctools.api_result import R
|
11
|
+
from ctools.sys_log import flog as log
|
12
|
+
|
13
|
+
bottle.BaseRequest.MEMFILE_MAX = 1024 * 1024 * 50
|
14
|
+
func_has_params = {}
|
15
|
+
|
16
|
+
class GlobalState:
|
17
|
+
lock = threading.Lock()
|
18
|
+
withOutLoginURI = [
|
19
|
+
'/',
|
20
|
+
'/index',
|
21
|
+
'/login'
|
22
|
+
]
|
23
|
+
allowRemoteCallURI = [
|
24
|
+
|
25
|
+
]
|
26
|
+
token = {}
|
27
|
+
interceptors = []
|
28
|
+
|
29
|
+
def init_app(context_path=None):
|
30
|
+
app = Bottle()
|
31
|
+
app.context_path = context_path
|
32
|
+
|
33
|
+
@app.hook('before_request')
|
34
|
+
def before_request():
|
35
|
+
for interceptor in GlobalState.interceptors:
|
36
|
+
res: R = interceptor['func']()
|
37
|
+
if res.code != 200: bottle.abort(res.code, res.message)
|
38
|
+
|
39
|
+
@app.error(401)
|
40
|
+
def unauthorized(error):
|
41
|
+
response.status = 401
|
42
|
+
log.error("系统未授权: {} {} {}".format(error.body, request.method, request.fullpath))
|
43
|
+
return R.error(resp=R.Code.cus_code(401, "系统未授权! {}".format(error.body)))
|
44
|
+
|
45
|
+
@app.error(403)
|
46
|
+
def unauthorized(error):
|
47
|
+
response.status = 403
|
48
|
+
log.error("访问受限: {} {} {}".format(error.body, request.method, request.fullpath))
|
49
|
+
return R.error(resp=R.Code.cus_code(403, "访问受限: {}".format(error.body)))
|
50
|
+
|
51
|
+
@app.error(404)
|
52
|
+
def not_found(error):
|
53
|
+
response.status = 404
|
54
|
+
log.error("404 not found : {} {} {}".format(error.body, request.method, request.fullpath))
|
55
|
+
return R.error(resp=R.Code.cus_code(404, "资源未找到: {}".format(error.body)))
|
56
|
+
|
57
|
+
@app.error(405)
|
58
|
+
def method_not_allow(error):
|
59
|
+
response.status = 405
|
60
|
+
log.error("请求方法错误: {} {} {}".format(error.status_line, request.method, request.fullpath))
|
61
|
+
return R.error(resp=R.Code.cus_code(405, '请求方法错误: {}'.format(error.status_line)))
|
62
|
+
|
63
|
+
@app.error(500)
|
64
|
+
def internal_error(error):
|
65
|
+
response.status = 500
|
66
|
+
log.error("系统发生错误: {} {} {}".format(error.body, request.method, request.fullpath))
|
67
|
+
return R.error(msg='系统发生错误: {}'.format(error.exception))
|
68
|
+
|
69
|
+
@app.hook('after_request')
|
70
|
+
def after_request():
|
71
|
+
enable_cors()
|
72
|
+
|
73
|
+
app.install(params_resolve)
|
74
|
+
return app
|
75
|
+
|
76
|
+
def enable_cors():
|
77
|
+
response.headers['Access-Control-Allow-Origin'] = '*'
|
78
|
+
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
|
79
|
+
request_headers = request.headers.get('Access-Control-Request-Headers')
|
80
|
+
response.headers['Access-Control-Allow-Headers'] = request_headers if request_headers else ''
|
81
|
+
response.headers['Access-Control-Expose-Headers'] = '*'
|
82
|
+
|
83
|
+
# annotation
|
84
|
+
def before_intercept(order=0):
|
85
|
+
def decorator(func):
|
86
|
+
log.info("add before interceptor: {}".format(func.__name__))
|
87
|
+
GlobalState.interceptors.append({'order': order, 'func': func})
|
88
|
+
GlobalState.interceptors = sorted(GlobalState.interceptors, key=lambda x: x['order'])
|
89
|
+
return decorator
|
90
|
+
|
91
|
+
# annotation
|
92
|
+
def rule(key):
|
93
|
+
def return_func(func):
|
94
|
+
@wraps(func)
|
95
|
+
def decorated(*args, **kwargs):
|
96
|
+
# if GlobalState.licenseInfo is not None and key not in GlobalState.licenseInfo.access_module:
|
97
|
+
# log.error("系统未授权! {} {}".format(request.fullpath, '当前请求的模块未授权!请联系管理员!'))
|
98
|
+
# return R.error(resp=R.Code.cus_code(9999, "系统未授权! {}".format('当前请求的模块未授权!请联系管理员!')))
|
99
|
+
return func(*args, **kwargs)
|
100
|
+
return decorated
|
101
|
+
return return_func
|
102
|
+
|
103
|
+
# annotation or plugins, has auto install, don't need to call
|
104
|
+
def params_resolve(func):
|
105
|
+
@wraps(func)
|
106
|
+
def decorated(*args, **kwargs):
|
107
|
+
if func_has_params.get(request.fullpath) is not None and not func_has_params.get(request.fullpath):
|
108
|
+
return func(*args, **kwargs)
|
109
|
+
if func_has_params.get(request.fullpath) is None:
|
110
|
+
sig = inspect.signature(func)
|
111
|
+
params = sig.parameters
|
112
|
+
if not params.get('params'):
|
113
|
+
func_has_params[request.fullpath] = False
|
114
|
+
return func(*args, **kwargs)
|
115
|
+
else:
|
116
|
+
func_has_params[request.fullpath] = True
|
117
|
+
if request.method == 'GET':
|
118
|
+
queryStr = request.query.decode('utf-8')
|
119
|
+
page_info = PageInfo(
|
120
|
+
page_size=10 if request.headers.get('page_size') is None else int(request.headers.get('page_size')),
|
121
|
+
page_index=1 if request.headers.get('page_index') is None else int(request.headers.get('page_index'))
|
122
|
+
)
|
123
|
+
queryStr.page_info = page_info
|
124
|
+
return func(params=queryStr, *args, **kwargs)
|
125
|
+
elif request.method == 'POST':
|
126
|
+
query_params = request.query.decode('utf-8')
|
127
|
+
content_type = request.get_header('content-type')
|
128
|
+
if content_type == 'application/json':
|
129
|
+
params = request.json or {}
|
130
|
+
dict_wrapper = DictWrapper(params)
|
131
|
+
dict_wrapper.update(query_params.dict)
|
132
|
+
return func(params=dict_wrapper, *args, **kwargs)
|
133
|
+
elif 'multipart/form-data' in content_type:
|
134
|
+
form_data = request.forms.decode()
|
135
|
+
form_files = request.files.decode()
|
136
|
+
dict_wrapper = DictWrapper(form_data)
|
137
|
+
dict_wrapper.update(query_params.dict)
|
138
|
+
dict_wrapper.files = form_files
|
139
|
+
return func(params=dict_wrapper, *args, **kwargs)
|
140
|
+
elif 'application/x-www-form-urlencoded' in content_type:
|
141
|
+
params = request.forms.decode()
|
142
|
+
dict_wrapper = DictWrapper(params.dict)
|
143
|
+
dict_wrapper.update(query_params.dict)
|
144
|
+
return func(params=dict_wrapper, *args, **kwargs)
|
145
|
+
elif 'text/plain' in content_type:
|
146
|
+
params = request.body.read().decode('utf-8')
|
147
|
+
dict_wrapper = DictWrapper({'body': params})
|
148
|
+
dict_wrapper.update(query_params.dict)
|
149
|
+
return func(params=dict_wrapper, *args, **kwargs)
|
150
|
+
else:
|
151
|
+
return func(*args, **kwargs)
|
152
|
+
return decorated
|
153
|
+
|
154
|
+
class PageInfo:
|
155
|
+
def __init__(self, page_size, page_index):
|
156
|
+
self.page_size = page_size
|
157
|
+
self.page_index = page_index
|
158
|
+
|
159
|
+
# 通用的鉴权方法
|
160
|
+
def common_auth_verify(aes_key):
|
161
|
+
if request.path.startswith('/static') or request.path in GlobalState.withOutLoginURI:
|
162
|
+
return R.ok(to_json_str=False)
|
163
|
+
auth_token = request.get_header('Authorization')
|
164
|
+
if auth_token is None:
|
165
|
+
auth_token = request.get_cookie('Authorization')
|
166
|
+
payload = ctoken.get_payload(auth_token, aes_key)
|
167
|
+
if payload:
|
168
|
+
return R.ok(to_json_str=False)
|
169
|
+
return R.error(resp=R.Code.cus_code(401, "未授权"), to_json_str=False)
|