mirror of https://github.com/jumpserver/jumpserver
perf: navicat 移到企业版
parent
678df5bf3e
commit
344451ba55
|
@ -1,4 +0,0 @@
|
|||
## Navicat Premium
|
||||
|
||||
- 需要先手动导入License激活
|
||||
|
|
@ -1,411 +0,0 @@
|
|||
import os
|
||||
import shutil
|
||||
import time
|
||||
|
||||
import winreg
|
||||
import win32api
|
||||
import win32con
|
||||
|
||||
import const as c
|
||||
|
||||
from pywinauto import Application
|
||||
from pywinauto.keyboard import send_keys
|
||||
from pywinauto.controls.uia_controls import (
|
||||
EditWrapper, ComboBoxWrapper, ButtonWrapper
|
||||
)
|
||||
|
||||
from common import wait_pid, BaseApplication, _messageBox
|
||||
|
||||
_default_path = r'C:\Program Files\PremiumSoft\Navicat Premium 16\navicat.exe'
|
||||
|
||||
|
||||
class AppletApplication(BaseApplication):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.path = _default_path
|
||||
self.username = self.account.username
|
||||
self.password = self.account.secret
|
||||
self.privileged = self.account.privileged
|
||||
self.host = self.asset.address
|
||||
self.port = self.asset.get_protocol_port(self.protocol)
|
||||
self.db = self.asset.spec_info.db_name
|
||||
self.name = '%s-%s-%s' % (self.host, self.db, int(time.time()))
|
||||
self.use_ssl = self.asset.spec_info.use_ssl
|
||||
self.client_key = self.asset.secret_info.client_key
|
||||
self.client_key_path = None
|
||||
self.pid = None
|
||||
self.app = None
|
||||
|
||||
@staticmethod
|
||||
def get_cert_path():
|
||||
win_user_name = win32api.GetUserName()
|
||||
cert_path = r'C:\Users\%s\AppData\Roaming\Navicat\certs' % win_user_name
|
||||
return cert_path
|
||||
|
||||
def clean_up(self):
|
||||
protocols = (
|
||||
'NavicatMARIADB', 'NavicatMONGODB', 'Navicat',
|
||||
'NavicatORA', 'NavicatMSSQL', 'NavicatPG'
|
||||
)
|
||||
for p in protocols:
|
||||
sub_key = r'Software\PremiumSoft\%s\Servers' % p
|
||||
try:
|
||||
win32api.RegDeleteTree(winreg.HKEY_CURRENT_USER, sub_key)
|
||||
except Exception:
|
||||
pass
|
||||
cert_path = self.get_cert_path()
|
||||
shutil.rmtree(cert_path, ignore_errors=True)
|
||||
|
||||
def gen_asset_file(self):
|
||||
if self.use_ssl and self.client_key:
|
||||
cert_path = self.get_cert_path()
|
||||
if not os.path.exists(cert_path):
|
||||
os.makedirs(cert_path, exist_ok=True)
|
||||
filepath = os.path.join(cert_path, str(int(time.time())))
|
||||
with open(filepath, 'w') as f:
|
||||
f.write(self.client_key)
|
||||
self.client_key_path = filepath
|
||||
|
||||
@staticmethod
|
||||
def edit_regedit():
|
||||
sub_key = r'Software\PremiumSoft\NavicatPremium'
|
||||
try:
|
||||
key = winreg.CreateKey(winreg.HKEY_CURRENT_USER, sub_key)
|
||||
# 禁止弹出欢迎页面
|
||||
winreg.SetValueEx(key, 'AlreadyShowNavicatV16WelcomeScreen', 0, winreg.REG_DWORD, 1)
|
||||
# 禁止开启自动检查更新
|
||||
winreg.SetValueEx(key, 'AutoCheckUpdate', 0, winreg.REG_DWORD, 0)
|
||||
# 禁止弹出初始化界面
|
||||
winreg.SetValueEx(key, 'ShareUsageData', 0, winreg.REG_DWORD, 0)
|
||||
except Exception as err:
|
||||
print('Launch error: %s' % err)
|
||||
|
||||
def launch(self):
|
||||
# 清理因为异常未关闭的会话历史记录
|
||||
self.clean_up()
|
||||
# 生成资产依赖的相关文件
|
||||
self.gen_asset_file()
|
||||
# 修改注册表,达到一些配置目的
|
||||
self.edit_regedit()
|
||||
|
||||
@staticmethod
|
||||
def _exec_commands(commands):
|
||||
for command in commands:
|
||||
pre_check = command.get('pre_check', lambda: True)
|
||||
if not pre_check():
|
||||
_messageBox('程序启动异常,请重新连接!!', 'Error', win32con.MB_DEFAULT_DESKTOP_ONLY)
|
||||
return
|
||||
|
||||
time.sleep(0.5)
|
||||
if command['type'] == 'key':
|
||||
send_keys(' '.join(command['commands']))
|
||||
elif command['type'] == 'action':
|
||||
for f in command['commands']:
|
||||
f()
|
||||
|
||||
def _pre_check_is_password_input(self):
|
||||
try:
|
||||
self.app.window(best_match='Connection Password')
|
||||
except Exception:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _action_ele_click(self, ele_name, conn_win=None):
|
||||
if not conn_win:
|
||||
conn_win = self.app.window(best_match='Dialog'). \
|
||||
child_window(title_re='New Connection')
|
||||
conn_win.child_window(best_match=ele_name).click()
|
||||
|
||||
def _fill_mysql_auth_info(self):
|
||||
conn_window = self.app.window(best_match='Dialog'). \
|
||||
child_window(title_re='New Connection')
|
||||
|
||||
name_ele = conn_window.child_window(best_match='Edit5')
|
||||
EditWrapper(name_ele.element_info).set_edit_text(self.name)
|
||||
|
||||
host_ele = conn_window.child_window(best_match='Edit4')
|
||||
EditWrapper(host_ele.element_info).set_edit_text(self.host)
|
||||
|
||||
port_ele = conn_window.child_window(best_match='Edit2')
|
||||
EditWrapper(port_ele.element_info).set_edit_text(self.port)
|
||||
|
||||
username_ele = conn_window.child_window(best_match='Edit1')
|
||||
EditWrapper(username_ele.element_info).set_edit_text(self.username)
|
||||
|
||||
def _get_mysql_commands(self):
|
||||
commands = [
|
||||
{
|
||||
'type': 'key',
|
||||
'commands': [
|
||||
'%f', c.DOWN, c.RIGHT, c.ENTER
|
||||
],
|
||||
},
|
||||
{
|
||||
'type': 'action',
|
||||
'commands': [
|
||||
self._fill_mysql_auth_info, lambda: self._action_ele_click('Save password')
|
||||
]
|
||||
},
|
||||
{
|
||||
'type': 'key',
|
||||
'commands': [c.ENTER]
|
||||
}
|
||||
]
|
||||
return commands
|
||||
|
||||
def _get_mariadb_commands(self):
|
||||
commands = [
|
||||
{
|
||||
'type': 'key',
|
||||
'commands': [
|
||||
'%f', c.DOWN, c.RIGHT, c.DOWN * 5, c.ENTER,
|
||||
],
|
||||
},
|
||||
{
|
||||
'type': 'action',
|
||||
'commands': [
|
||||
self._fill_mysql_auth_info, lambda: self._action_ele_click('Save password')
|
||||
]
|
||||
},
|
||||
{
|
||||
'type': 'key',
|
||||
'commands': [c.ENTER]
|
||||
}
|
||||
]
|
||||
return commands
|
||||
|
||||
def _fill_mongodb_auth_info(self):
|
||||
conn_window = self.app.window(best_match='Dialog'). \
|
||||
child_window(title_re='New Connection')
|
||||
|
||||
auth_type_ele = conn_window.child_window(best_match='ComboBox2')
|
||||
ComboBoxWrapper(auth_type_ele.element_info).select('Password')
|
||||
|
||||
name_ele = conn_window.child_window(best_match='Edit5')
|
||||
EditWrapper(name_ele.element_info).set_edit_text(self.name)
|
||||
|
||||
host_ele = conn_window.child_window(best_match='Edit4')
|
||||
EditWrapper(host_ele.element_info).set_edit_text(self.host)
|
||||
|
||||
port_ele = conn_window.child_window(best_match='Edit2')
|
||||
EditWrapper(port_ele.element_info).set_edit_text(self.port)
|
||||
|
||||
db_ele = conn_window.child_window(best_match='Edit6')
|
||||
EditWrapper(db_ele.element_info).set_edit_text(self.db)
|
||||
|
||||
username_ele = conn_window.child_window(best_match='Edit1')
|
||||
EditWrapper(username_ele.element_info).set_edit_text(self.username)
|
||||
|
||||
def _get_mongodb_commands(self):
|
||||
commands = [
|
||||
{
|
||||
'type': 'key',
|
||||
'commands': [
|
||||
'%f', c.DOWN, c.RIGHT, c.DOWN * 6, c.ENTER,
|
||||
],
|
||||
},
|
||||
{
|
||||
'type': 'action',
|
||||
'commands': [
|
||||
self._fill_mongodb_auth_info, lambda: self._action_ele_click('Save password')
|
||||
]
|
||||
},
|
||||
{
|
||||
'type': 'key',
|
||||
'commands': [c.ENTER]
|
||||
}
|
||||
]
|
||||
if self.use_ssl:
|
||||
ssl_commands = [
|
||||
{
|
||||
'type': 'key',
|
||||
'commands': [c.TAB * 5, c.RIGHT * 3, c.TAB]
|
||||
},
|
||||
{
|
||||
'type': 'action',
|
||||
'commands': [
|
||||
lambda: self._action_ele_click('Use SSL'),
|
||||
lambda: self._action_ele_click('Use authentication'),
|
||||
]
|
||||
},
|
||||
{
|
||||
'type': 'key',
|
||||
'commands': [c.TAB, self.client_key_path]
|
||||
},
|
||||
{
|
||||
'type': 'action',
|
||||
'commands': [lambda: self._action_ele_click('Allow invalid host names')]
|
||||
}
|
||||
]
|
||||
commands = commands[:2] + ssl_commands + commands[2:]
|
||||
return commands
|
||||
|
||||
def _fill_postgresql_auth_info(self):
|
||||
conn_window = self.app.window(best_match='Dialog'). \
|
||||
child_window(title_re='New Connection')
|
||||
|
||||
name_ele = conn_window.child_window(best_match='Edit6')
|
||||
EditWrapper(name_ele.element_info).set_edit_text(self.name)
|
||||
|
||||
host_ele = conn_window.child_window(best_match='Edit5')
|
||||
EditWrapper(host_ele.element_info).set_edit_text(self.host)
|
||||
|
||||
port_ele = conn_window.child_window(best_match='Edit2')
|
||||
EditWrapper(port_ele.element_info).set_edit_text(self.port)
|
||||
|
||||
db_ele = conn_window.child_window(best_match='Edit4')
|
||||
EditWrapper(db_ele.element_info).set_edit_text(self.db)
|
||||
|
||||
username_ele = conn_window.child_window(best_match='Edit1')
|
||||
EditWrapper(username_ele.element_info).set_edit_text(self.username)
|
||||
|
||||
def _get_postgresql_commands(self):
|
||||
commands = [
|
||||
{
|
||||
'type': 'key',
|
||||
'commands': [
|
||||
'%f', c.DOWN, c.RIGHT, c.DOWN, c.ENTER,
|
||||
],
|
||||
},
|
||||
{
|
||||
'type': 'action',
|
||||
'commands': [
|
||||
self._fill_postgresql_auth_info, lambda: self._action_ele_click('Save password')
|
||||
]
|
||||
},
|
||||
{
|
||||
'type': 'key',
|
||||
'commands': [c.ENTER]
|
||||
}
|
||||
]
|
||||
return commands
|
||||
|
||||
def _fill_sqlserver_auth_info(self):
|
||||
conn_window = self.app.window(best_match='Dialog'). \
|
||||
child_window(title_re='New Connection')
|
||||
|
||||
name_ele = conn_window.child_window(best_match='Edit5')
|
||||
EditWrapper(name_ele.element_info).set_edit_text(self.name)
|
||||
|
||||
host_ele = conn_window.child_window(best_match='Edit4')
|
||||
EditWrapper(host_ele.element_info).set_edit_text('%s,%s' % (self.host, self.port))
|
||||
|
||||
db_ele = conn_window.child_window(best_match='Edit3')
|
||||
EditWrapper(db_ele.element_info).set_edit_text(self.db)
|
||||
|
||||
username_ele = conn_window.child_window(best_match='Edit6')
|
||||
EditWrapper(username_ele.element_info).set_edit_text(self.username)
|
||||
|
||||
def _get_sqlserver_commands(self):
|
||||
commands = [
|
||||
{
|
||||
'type': 'key',
|
||||
'commands': [
|
||||
'%f', c.DOWN, c.RIGHT, c.DOWN * 4, c.ENTER,
|
||||
],
|
||||
},
|
||||
{
|
||||
'type': 'action',
|
||||
'commands': [
|
||||
self._fill_sqlserver_auth_info, lambda: self._action_ele_click('Save password')
|
||||
]
|
||||
},
|
||||
{
|
||||
'type': 'key',
|
||||
'commands': [c.ENTER]
|
||||
}
|
||||
]
|
||||
return commands
|
||||
|
||||
def _fill_oracle_auth_info(self):
|
||||
conn_window = self.app.window(best_match='Dialog'). \
|
||||
child_window(title_re='New Connection')
|
||||
|
||||
name_ele = conn_window.child_window(best_match='Edit6')
|
||||
EditWrapper(name_ele.element_info).set_edit_text(self.name)
|
||||
|
||||
host_ele = conn_window.child_window(best_match='Edit5')
|
||||
EditWrapper(host_ele.element_info).set_edit_text(self.host)
|
||||
|
||||
port_ele = conn_window.child_window(best_match='Edit3')
|
||||
EditWrapper(port_ele.element_info).set_edit_text(self.port)
|
||||
|
||||
db_ele = conn_window.child_window(best_match='Edit2')
|
||||
EditWrapper(db_ele.element_info).set_edit_text(self.db)
|
||||
|
||||
username_ele = conn_window.child_window(best_match='Edit')
|
||||
EditWrapper(username_ele.element_info).set_edit_text(self.username)
|
||||
|
||||
if self.privileged:
|
||||
conn_window.child_window(best_match='Advanced', control_type='TabItem').click_input()
|
||||
role_ele = conn_window.child_window(best_match='ComboBox2')
|
||||
ComboBoxWrapper(role_ele.element_info).select('SYSDBA')
|
||||
|
||||
def _get_oracle_commands(self):
|
||||
commands = [
|
||||
{
|
||||
'type': 'key',
|
||||
'commands': [
|
||||
'%f', c.DOWN, c.RIGHT, c.DOWN * 2, c.ENTER,
|
||||
],
|
||||
},
|
||||
{
|
||||
'type': 'action',
|
||||
'commands': [
|
||||
lambda: self._action_ele_click('Save password'), self._fill_oracle_auth_info
|
||||
]
|
||||
},
|
||||
{
|
||||
'type': 'key',
|
||||
'commands': [c.ENTER]
|
||||
}
|
||||
]
|
||||
return commands
|
||||
|
||||
def run(self):
|
||||
self.launch()
|
||||
self.app = Application(backend='uia')
|
||||
work_dir = os.path.dirname(self.path)
|
||||
self.app.start(self.path, work_dir=work_dir)
|
||||
self.pid = self.app.process
|
||||
|
||||
# 检测是否为试用版本
|
||||
try:
|
||||
trial_btn = self.app.top_window().child_window(
|
||||
best_match='Trial', control_type='Button'
|
||||
)
|
||||
ButtonWrapper(trial_btn.element_info).click()
|
||||
time.sleep(0.5)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 根据协议获取相应操作命令
|
||||
action = getattr(self, '_get_%s_commands' % self.protocol, None)
|
||||
if action is None:
|
||||
raise ValueError('This protocol is not supported: %s' % self.protocol)
|
||||
commands = action()
|
||||
# 关闭掉桌面许可弹框
|
||||
commands.insert(0, {'type': 'key', 'commands': (c.ESC,)})
|
||||
# 登录
|
||||
commands.extend([
|
||||
{
|
||||
'type': 'key',
|
||||
'commands': (
|
||||
'%f', c.DOWN * 5, c.ENTER
|
||||
)
|
||||
},
|
||||
{
|
||||
'type': 'key',
|
||||
'commands': (self.password, c.ENTER),
|
||||
'pre_check': self._pre_check_is_password_input
|
||||
}
|
||||
])
|
||||
self._exec_commands(commands)
|
||||
|
||||
def wait(self):
|
||||
try:
|
||||
wait_pid(self.pid)
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
self.clean_up()
|
|
@ -1,215 +0,0 @@
|
|||
import abc
|
||||
import base64
|
||||
import json
|
||||
import locale
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from subprocess import CREATE_NO_WINDOW
|
||||
|
||||
_blockInput = None
|
||||
_messageBox = None
|
||||
if sys.platform == 'win32':
|
||||
import ctypes
|
||||
from ctypes import wintypes
|
||||
import win32ui
|
||||
|
||||
# import win32con
|
||||
|
||||
_messageBox = win32ui.MessageBox
|
||||
|
||||
_blockInput = ctypes.windll.user32.BlockInput
|
||||
_blockInput.argtypes = [wintypes.BOOL]
|
||||
_blockInput.restype = wintypes.BOOL
|
||||
|
||||
|
||||
def block_input():
|
||||
if _blockInput:
|
||||
_blockInput(True)
|
||||
|
||||
|
||||
def unblock_input():
|
||||
if _blockInput:
|
||||
_blockInput(False)
|
||||
|
||||
|
||||
def decode_content(content: bytes) -> str:
|
||||
for encoding_name in ['utf-8', 'gbk', 'gb2312']:
|
||||
try:
|
||||
return content.decode(encoding_name)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
encoding_name = locale.getpreferredencoding()
|
||||
return content.decode(encoding_name)
|
||||
|
||||
|
||||
def notify_err_message(msg):
|
||||
if _messageBox:
|
||||
_messageBox(msg, 'Error')
|
||||
|
||||
|
||||
def check_pid_alive(pid) -> bool:
|
||||
# tasklist /fi "PID eq 508" /fo csv
|
||||
# '"映像名称","PID","会话名 ","会话# ","内存使用 "\r\n"wininit.exe","508","Services","0","6,920 K"\r\n'
|
||||
try:
|
||||
|
||||
csv_ret = subprocess.check_output(["tasklist", "/fi", f'PID eq {pid}', "/fo", "csv"],
|
||||
creationflags=CREATE_NO_WINDOW)
|
||||
content = decode_content(csv_ret)
|
||||
content_list = content.strip().split("\r\n")
|
||||
if len(content_list) != 2:
|
||||
print("check pid {} ret invalid: {}".format(pid, content))
|
||||
return False
|
||||
ret_pid = content_list[1].split(",")[1].strip('"')
|
||||
return str(pid) == ret_pid
|
||||
except Exception as e:
|
||||
print("check pid {} err: {}".format(pid, e))
|
||||
return False
|
||||
|
||||
|
||||
def wait_pid(pid):
|
||||
while 1:
|
||||
time.sleep(5)
|
||||
ok = check_pid_alive(pid)
|
||||
if not ok:
|
||||
print("pid {} is not alive".format(pid))
|
||||
break
|
||||
|
||||
|
||||
class DictObj:
|
||||
def __init__(self, in_dict: dict):
|
||||
assert isinstance(in_dict, dict)
|
||||
for key, val in in_dict.items():
|
||||
if isinstance(val, (list, tuple)):
|
||||
setattr(self, key, [DictObj(x) if isinstance(x, dict) else x for x in val])
|
||||
else:
|
||||
setattr(self, key, DictObj(val) if isinstance(val, dict) else val)
|
||||
|
||||
|
||||
class User(DictObj):
|
||||
id: str
|
||||
name: str
|
||||
username: str
|
||||
|
||||
|
||||
class Specific(DictObj):
|
||||
# web
|
||||
autofill: str
|
||||
username_selector: str
|
||||
password_selector: str
|
||||
submit_selector: str
|
||||
script: list
|
||||
|
||||
# database
|
||||
db_name: str
|
||||
use_ssl: str
|
||||
|
||||
|
||||
class Secret(DictObj):
|
||||
client_key: str
|
||||
|
||||
|
||||
class Category(DictObj):
|
||||
value: str
|
||||
label: str
|
||||
|
||||
|
||||
class Protocol(DictObj):
|
||||
id: str
|
||||
name: str
|
||||
port: int
|
||||
|
||||
|
||||
class Asset(DictObj):
|
||||
id: str
|
||||
name: str
|
||||
address: str
|
||||
protocols: list[Protocol]
|
||||
category: Category
|
||||
spec_info: Specific
|
||||
secret_info: Secret
|
||||
|
||||
def get_protocol_port(self, protocol):
|
||||
for item in self.protocols:
|
||||
if item.name == protocol:
|
||||
return item.port
|
||||
return None
|
||||
|
||||
|
||||
class LabelValue(DictObj):
|
||||
label: str
|
||||
value: str
|
||||
|
||||
|
||||
class Account(DictObj):
|
||||
id: str
|
||||
name: str
|
||||
username: str
|
||||
secret: str
|
||||
privileged: bool
|
||||
secret_type: LabelValue
|
||||
|
||||
|
||||
class Platform(DictObj):
|
||||
charset: str
|
||||
name: str
|
||||
charset: LabelValue
|
||||
type: LabelValue
|
||||
|
||||
|
||||
class Manifest(DictObj):
|
||||
name: str
|
||||
version: str
|
||||
path: str
|
||||
exec_type: str
|
||||
connect_type: str
|
||||
protocols: list[str]
|
||||
|
||||
|
||||
def get_manifest_data() -> dict:
|
||||
current_dir = os.path.dirname(__file__)
|
||||
manifest_file = os.path.join(current_dir, 'manifest.json')
|
||||
try:
|
||||
with open(manifest_file, "r", encoding='utf8') as f:
|
||||
return json.load(f)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return {}
|
||||
|
||||
|
||||
def read_app_manifest(app_dir) -> dict:
|
||||
main_json_file = os.path.join(app_dir, "manifest.json")
|
||||
if not os.path.exists(main_json_file):
|
||||
return {}
|
||||
with open(main_json_file, 'r', encoding='utf8') as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def convert_base64_to_dict(base64_str: str) -> dict:
|
||||
try:
|
||||
data_json = base64.decodebytes(base64_str.encode('utf-8')).decode('utf-8')
|
||||
return json.loads(data_json)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return {}
|
||||
|
||||
|
||||
class BaseApplication(abc.ABC):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.app_name = kwargs.get('app_name', '')
|
||||
self.protocol = kwargs.get('protocol', '')
|
||||
self.manifest = Manifest(kwargs.get('manifest', {}))
|
||||
self.user = User(kwargs.get('user', {}))
|
||||
self.asset = Asset(kwargs.get('asset', {}))
|
||||
self.account = Account(kwargs.get('account', {}))
|
||||
self.platform = Platform(kwargs.get('platform', {}))
|
||||
|
||||
@abc.abstractmethod
|
||||
def run(self):
|
||||
raise NotImplementedError('run')
|
||||
|
||||
@abc.abstractmethod
|
||||
def wait(self):
|
||||
raise NotImplementedError('wait')
|
|
@ -1,8 +0,0 @@
|
|||
|
||||
UP = '{UP}'
|
||||
LEFT = '{LEFT}'
|
||||
DOWN = '{DOWN}'
|
||||
RIGHT = '{RIGHT}'
|
||||
TAB = '{VK_TAB}'
|
||||
ESC = '{ESC}'
|
||||
ENTER = '{VK_RETURN}'
|
|
@ -1,3 +0,0 @@
|
|||
- zh:
|
||||
display_name: Navicat premium 16
|
||||
comment: 数据库管理软件
|
Binary file not shown.
Before Width: | Height: | Size: 5.0 KiB |
|
@ -1,22 +0,0 @@
|
|||
import sys
|
||||
|
||||
from common import (block_input, unblock_input)
|
||||
from common import convert_base64_to_dict
|
||||
from app import AppletApplication
|
||||
|
||||
|
||||
def main():
|
||||
base64_str = sys.argv[1]
|
||||
data = convert_base64_to_dict(base64_str)
|
||||
applet_app = AppletApplication(**data)
|
||||
block_input()
|
||||
applet_app.run()
|
||||
unblock_input()
|
||||
applet_app.wait()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
main()
|
||||
except Exception as e:
|
||||
print(e)
|
|
@ -1,17 +0,0 @@
|
|||
name: navicat
|
||||
display_name: Navicat premium 16
|
||||
comment: Database management software
|
||||
version: 0.1
|
||||
exec_type: python
|
||||
author: JumpServer Team
|
||||
type: general
|
||||
update_policy: always
|
||||
tags:
|
||||
- database
|
||||
protocols:
|
||||
- mysql
|
||||
- mariadb
|
||||
- postgresql
|
||||
- sqlserver
|
||||
- oracle
|
||||
- mongodb
|
|
@ -1,6 +0,0 @@
|
|||
type: manual # exe, zip, manual
|
||||
# 从这里下载的: https://www.navicat.com.cn/download/direct-download?product=navicat_premium_en_x64.exe
|
||||
source:
|
||||
destination: C:\Program Files\PremiumSoft\Navicat Premium 16
|
||||
program: C:\Program Files\PremiumSoft\Navicat Premium 16\navicat.exe
|
||||
md5: 6c2c25fa56c75254c6bbcba043000063
|
|
@ -183,19 +183,6 @@
|
|||
GOOGLE_DEFAULT_CLIENT_ID: ''
|
||||
GOOGLE_DEFAULT_CLIENT_SECRET: ''
|
||||
|
||||
- name: Download navicat161_premium_en package (navicat)
|
||||
ansible.windows.win_get_url:
|
||||
url: "{{ APPLET_DOWNLOAD_HOST }}/download/applets/navicat161_premium_en_x64.exe"
|
||||
dest: "{{ ansible_env.TEMP }}\\navicat161_premium_en_x64.exe"
|
||||
validate_certs: "{{ not IGNORE_VERIFY_CERTS }}"
|
||||
|
||||
- name: Install navicat (navicat)
|
||||
ansible.windows.win_package:
|
||||
path: "{{ ansible_env.TEMP }}\\navicat161_premium_en_x64.exe"
|
||||
state: present
|
||||
arguments:
|
||||
- /SILENT
|
||||
|
||||
- name: Generate tinkerd component config
|
||||
ansible.windows.win_shell:
|
||||
"tinkerd config --hostname {{ HOST_NAME }} --core_host {{ CORE_HOST }}
|
||||
|
|
Loading…
Reference in New Issue