#coding: utf-8 # +------------------------------------------------------------------- # | aaPanel # +------------------------------------------------------------------- # | Copyright (c) 2015-2099 aaPanel(www.aapanel.com) All rights reserved. # +------------------------------------------------------------------- # | Author: hwliang # +------------------------------------------------------------------- #+-------------------------------------------------------------------- #| 自动部署网站 #+-------------------------------------------------------------------- import public,json,os,time,sys,re from BTPanel import session,cache class obj: id=0 class plugin_deployment: # __setupPath = 'data' __panelPath = '/www/server/panel' __setupPath = '{}/data'.format(__panelPath) logPath = 'data/deployment_speed.json' __tmp = '/www/server/panel/temp/' timeoutCount = 0 oldTime = 0 _speed_key = 'dep_download_speed' # 获取PHP一键部署列表 def GetList(self, get): jsonFile = self.__setupPath + '/deployment_list.json' if not os.path.exists(jsonFile): return public.returnMsg(False, 'Configuration file not exist') try: data = json.loads(public.readFile(jsonFile)) except: return public.returnMsg(False, 'Configuration file not exist') if 'type' in get and get.get('type', 0) in ['1', '2', '3', '4', '5']: php_type = int(get.get('type', 0)) type_data = [] for i in data['list']: if i['type'] == php_type: type_data.append(i) data['list'] = type_data return public.return_message(0,0, data) #获取图标 def get_icon(self,pinfo): path = '/www/server/panel/BTPanel/static/img/dep_ico' if not os.path.exists(path): os.makedirs(path,384) filename = "%s/%s.png" % (path, pinfo['name']) m_uri = pinfo['min_image'] pinfo['min_image'] = '/static/img/dep_ico/%s.png' % pinfo['name'] if sys.version_info[0] == 2: filename = filename.encode('utf-8') if os.path.exists(filename): if os.path.getsize(filename) > 100: return pinfo public.ExecShell("wget -O " + filename + ' https://www.bt.cn' + m_uri + " &") return pinfo #获取插件列表 def GetDepList(self,get): jsonFile = self.__setupPath + '/deployment_list.json' if not os.path.exists(jsonFile): return public.returnMsg(False, public.lang("Profile does not exist!")) data = json.loads(public.readFile(jsonFile)) return self.get_input_list(data) #获取本地导入的插件 def get_input_list(self,data): try: jsonFile = self.__setupPath + '/deployment_list_other.json' if not os.path.exists(jsonFile): return data i_data = json.loads(public.readFile(jsonFile)) for d in i_data: data['list'].append(d) return data except:return data #导入程序包 def AddPackage(self,get): jsonFile = self.__setupPath + '/deployment_list_other.json' if not os.path.exists(jsonFile): public.writeFile(jsonFile,'[]') pinfo = {} pinfo['name'] = get.name pinfo['title'] = get.title pinfo['version'] = get.version pinfo['php'] = get.php pinfo['ps'] = get.ps pinfo['official'] = '#' pinfo['sort'] = 1000 pinfo['min_image'] = '' pinfo['id'] = 0 pinfo['type'] = 100 pinfo['enable_functions'] = get.enable_functions pinfo['author'] = 'Import from local' from werkzeug.utils import secure_filename from flask import request f = request.files['dep_zip'] s_path = self.__panelPath + '/package' if not os.path.exists(s_path): os.makedirs(s_path,384) s_file = s_path + '/' + pinfo['name'] + '.zip' if os.path.exists(s_file): os.remove(s_file) f.save(s_file) os.chmod(s_file,384) pinfo['versions'] = [] version = {"cpu_limit": 1, "dependnet": "", "m_version":pinfo['version'], "mem_limit": 32, "os_limit": 0, "size": os.path.getsize(s_file), "version": "0", "download":"", "version_msg": "test2"} version['md5'] = self.GetFileMd5(s_file) pinfo['versions'].append(version) data = json.loads(public.readFile(jsonFile)) is_exists = False for i in range(len(data)): if data[i]['name'] == pinfo['name']: data[i] = pinfo is_exists = True if not is_exists: data.append(pinfo) public.writeFile(jsonFile,json.dumps(data)) return public.returnMsg(True, public.lang("Import Success!")) #取本地包信息 def GetPackageOther(self,get): p_name = get.p_name jsonFile = self.__setupPath + '/deployment_list_other.json' if not os.path.exists(jsonFile): public.returnMsg(False,'could not find [%s]' % p_name) data = json.loads(public.readFile(jsonFile)) for i in range(len(data)): if data[i]['name'] == p_name: return data[i] return public.returnMsg(False,'could not find [%s]' % p_name) #删除程序包 def DelPackage(self,get): jsonFile = self.__setupPath + '/deployment_list_other.json' if not os.path.exists(jsonFile): return public.returnMsg(False, public.lang("Profile does not exist!")) data = {} data = json.loads(public.readFile(jsonFile)) tmp = [] for d in data: if d['name'] == get.dname: s_file = self.__panelPath + '/package/' + d['name'] + '.zip' if os.path.exists(s_file): os.remove(s_file) continue tmp.append(d) data = tmp public.writeFile(jsonFile,json.dumps(data)) return public.returnMsg(True, public.lang("Successfully deleted!")) #下载文件 def DownloadFile(self,url,filename): try: path = os.path.dirname(filename) if not os.path.exists(path): os.makedirs(path) import urllib,socket socket.setdefaulttimeout(20) self.pre = 0 self.oldTime = time.time() if sys.version_info[0] == 2: urllib.urlretrieve(url,filename=filename,reporthook= self.DownloadHook) else: urllib.request.urlretrieve(url,filename=filename,reporthook= self.DownloadHook) self.WriteLogs(json.dumps({'name':'Download File','total':0,'used':0,'pre':0,'speed':0, 'status': 0})) except: if self.timeoutCount > 5: return self.timeoutCount += 1 time.sleep(5) self.DownloadFile(url,filename) #下载文件进度回调 def DownloadHook(self,count, blockSize, totalSize): used = count * blockSize pre1 = int((100.0 * used / totalSize)) if self.pre != pre1: dspeed = used / (time.time() - self.oldTime) speed = {'name':'Download File','total':totalSize,'used':used,'pre':self.pre,'speed':dspeed,'status': 0} self.WriteLogs(json.dumps(speed)) self.pre = pre1 #写输出日志 def WriteLogs(self,logMsg): fp = open(self.logPath,'w+') fp.write(logMsg) fp.close() #一键安装网站程序 def SetupPackage(self, get): """ 一键部署网站 """ self.WriteLogs( json.dumps({'name': 'Create a new website...', 'total': 0, 'used': 0, 'pre': 0, 'speed': 0, 'status': 0})) # 检查项目目录 path = get.get('path') try: if os.path.isdir(path): with os.scandir(path) as it: if any(it): return public.return_message(-1, 0, public.lang("The directory is not empty.")) except PermissionError: return public.return_message(-1, 0, public.lang("You do not have access to this directory.")) from panel_site_v2 import panelSite site_obj = panelSite() res = site_obj.AddSite(get, multiple=1) if res['status'] != 0: self.WriteLogs(json.dumps( {'name': f'Website creation failed: {res['message']}', 'total': 0, 'used': 0, 'pre': 0, 'speed': 0, 'status': 1})) return public.return_message(-1, 0, res['message']) try: res_data = {} res_data['databaseStatus'] = res['message']['databaseStatus'] # 检查数据库是否安装 if not os.path.exists(public.get_setup_path() +'/mysql/bin/mysql'): res_data['databaseStatus'] = False res['message']['databaseErrorMsg'] = public.lang("The MYSQL database has not been installed. Please add the database manually!") if res_data['databaseStatus']: res_data['databaseUser'] = res['message']['databaseUser'] res_data['databasePass'] = res['message']['databasePass'] res_data['databaseName'] = res['message']['databaseUser'] else: res_data['databaseErrorMsg'] = res['message']['databaseErrorMsg'] res_data['databaseUser'] = '' res_data['databasePass'] = '' res_data['databaseName'] = '' # 取基础信息 name = get.dname site_id = res['message']['siteId'] php_version = get.version find = public.M('sites').where('id=?', (site_id,)).field('id,path,name').find() if not find: self.WriteLogs(json.dumps({'name':f'Website creation failed.','total':0,'used':0,'pre':0,'speed':0,'status': 1})) raise ValueError( public.lang('Website creation failed.')) path = find['path'] pk_version = get.get('pk_version') if path.replace('//','/') == '/': raise ValueError( public.lang("Dangerous website root directory!")) if not get.get('pk_version'): raise ValueError( public.lang("Missing template version parameter!")) #获取包信息 pinfo = self.GetPackageInfo(name, pk_version) if not pinfo: raise ValueError( public.lang("The specified package does not exist.!")) # PHP 版本 if get.version not in pinfo['php']: raise ValueError( f"Unsupported PHP version! Support: [{pinfo['php']}]") #检查本地包 self.WriteLogs(json.dumps({'name':'Verifying package...','total':0,'used':0,'pre':0,'speed':0, 'status': 0})) pack_path = self.__panelPath + '/package' if not os.path.exists(pack_path): os.makedirs(pack_path,384) packageZip = pack_path + '/'+ name + '.zip' isDownload = False if os.path.exists(packageZip): md5str = self.GetFileMd5(packageZip) if md5str != pinfo['md5']: isDownload = True else: isDownload = True #下载文件 if isDownload: self.WriteLogs(json.dumps({'name':'Downloading file ...','total':0,'used':0,'pre':0,'speed':0, 'status': 0})) if pinfo['download']: self.DownloadFile(pinfo['download'], packageZip) if not os.path.exists(packageZip): raise ValueError('File download failed!' + packageZip) ok = self.set_temp_file(packageZip,path) if not ok: raise ValueError( public.lang("Cannot find [aaPanel Auto Deployment Configuration File] in the installation package")) #设置权限 self.WriteLogs(json.dumps({'name':'Setting permissions','total':0,'used':0,'pre':0,'speed':0, 'status': 0})) public.ExecShell('chmod -R 755 ' + path) public.ExecShell('chown -R www.www ' + path) if pinfo['chmod']: for chm in pinfo['chmod']: public.ExecShell('chmod -R ' + str(chm['mode']) + ' ' + (path + '/' + chm['path']).replace('//','/')) #安装PHP扩展 self.WriteLogs(json.dumps({'name':'Install the necessary PHP extensions','total':0,'used':0,'pre':0,'speed':0, 'status': 0})) import files_v2 mfile = files_v2.files() if type(pinfo['ext']) != list : pinfo['ext'] = pinfo['ext'].strip().split(',') if pinfo['ext']: pinfo['install_msg'] = "Please wait for the PHP extension installation to be completed.\n" + pinfo['install_msg'] for ext in pinfo['ext']: if ext == 'pathinfo': import config_v2 con = config_v2.config() get.version = php_version get.type = 'on' con.setPathInfo(get) else: get.name = ext get.version = php_version get.type = '1' mfile.InstallSoft(get) #解禁PHP函数 if 'enable_functions' in pinfo: try: if type(pinfo['enable_functions']) == str : pinfo['enable_functions'] = pinfo['enable_functions'].strip().split(',') php_f = public.GetConfigValue('setup_path') + '/php/' + php_version + '/etc/php.ini' php_c = public.readFile(php_f) rep = "disable_functions\\s*=\\s{0,1}(.*)\n" tmp = re.search(rep,php_c).groups() disable_functions = tmp[0].split(',') for fun in pinfo['enable_functions']: fun = fun.strip() if fun in disable_functions: disable_functions.remove(fun) disable_functions = ','.join(disable_functions) php_c = re.sub(rep, 'disable_functions = ' + disable_functions + "\n", php_c) public.writeFile(php_f,php_c) public.phpReload(php_version) except:pass #执行额外shell进行依赖安装 self.WriteLogs(json.dumps({'name':'Execute extra SHELL','total':0,'used':0,'pre':0,'speed':0, 'status': 0})) if os.path.exists(path+'/install.sh'): public.ExecShell('cd '+path+' && bash ' + 'install.sh ' + find['name'] + " &> /tmp/dep_install.log") public.ExecShell('rm -f ' + path+'/install.sh') #是否执行Composer if os.path.exists(path + '/composer.json'): self.WriteLogs(json.dumps({'name':'Execute Composer','total':0,'used':0,'pre':0,'speed':0, 'status': 0})) if not os.path.exists(path + '/composer.lock'): execPHP = '/www/server/php/' + php_version +'/bin/php' if execPHP: phpini = '/www/server/php/' + php_version + '/etc/php.ini' phpiniConf = public.readFile(phpini) phpiniConf = phpiniConf.replace('proc_open,proc_get_status,','') public.writeFile(phpini,phpiniConf) public.ExecShell('nohup cd '+path+' && '+execPHP+' /usr/bin/composer install -vvv > /tmp/composer.log 2>&1 &') #写伪静态 self.WriteLogs(json.dumps({'name':'Set URL rewrite','total':0,'used':0,'pre':0,'speed':0, 'status': 0})) swfile = path + '/nginx.rewrite' if os.path.exists(swfile): rewriteConf = public.readFile(swfile) dwfile = self.__panelPath + '/vhost/rewrite/' + find['name'] + '.conf' public.writeFile(dwfile,rewriteConf) swfile = path + '/.htaccess' if os.path.exists(swfile): swpath = (path + '/'+ pinfo['run'] + '/.htaccess').replace('//','/') if pinfo['run'] != '/' and not os.path.exists(swpath): public.writeFile(swpath, public.readFile(swfile)) #删除伪静态文件 public.ExecShell("rm -f " + path + '/*.rewrite') #删除多余文件 rm_file = path + '/index.html' if os.path.exists(rm_file): rm_file_body = public.readFile(rm_file) if rm_file_body.find('panel-heading') != -1: os.remove(rm_file) #设置运行目录 self.WriteLogs(json.dumps({'name':'Set the run directory','total':0,'used':0,'pre':0,'speed':0, 'status': 0})) if pinfo['run'] != '/': site_obj.SetSiteRunPath(public.to_dict_obj({"id":find['id'],"runPath":pinfo['run']})) #导入数据 self.WriteLogs(json.dumps({'name':'Import database','total':0,'used':0,'pre':0,'speed':0, 'status': 0})) if os.path.exists(path+'/import.sql'): databaseInfo = public.M('databases').where('pid=?',(find['id'],)).field('username,password').find() if databaseInfo: public.ExecShell('/www/server/mysql/bin/mysql -u' + databaseInfo['username'] + ' -p' + databaseInfo['password'] + ' ' + databaseInfo['username'] + ' < ' + path + '/import.sql') public.ExecShell('rm -f ' + path + '/import.sql') siteConfigFile = (path + '/' + pinfo['config']).replace('//','/') if os.path.exists(siteConfigFile): siteConfig = public.readFile(siteConfigFile) siteConfig = siteConfig.replace('BT_DB_USERNAME',databaseInfo['username']) siteConfig = siteConfig.replace('BT_DB_PASSWORD',databaseInfo['password']) siteConfig = siteConfig.replace('BT_DB_NAME',databaseInfo['username']) public.writeFile(siteConfigFile,siteConfig) #清理文件和目录 if 'remove_file' in pinfo: self.WriteLogs(json.dumps({'name':'Delete unnecessary files','total':0,'used':0,'pre':0,'speed':0, 'status': 0})) if type(pinfo['remove_file']) == str : pinfo['remove_file'] = pinfo['remove_file'].strip().split(',') for f_path in pinfo['remove_file']: if not f_path: continue filename = (path + '/' + f_path).replace('//','/') if os.path.exists(filename): if not os.path.isdir(filename): if f_path.find('.user.ini') != -1: public.ExecShell("chattr -i " + filename) os.remove(filename) else: public.ExecShell("rm -rf " + filename) public.serviceReload() res_data['dname'] = name res_data['php_version'] = php_version res_data['site_path'] = path res_data['site_name'] = find['name'] res_data['install_msg'] = pinfo['install_msg'] if pinfo['install_msg'] else '' # 埋点 public.set_module_logs("one_deployment",f"one_deployment_{name}") public.set_module_logs("one_deployment","one_deployment_total") self.WriteLogs(json.dumps({'name':'Deployment completed','total':0,'used':0,'pre':0,'speed':0,'status': 1})) return public.return_message(0, 0, res_data) except Exception as e: # 回滚删除网站 try: site_obj.DeleteSite(public.to_dict_obj({'id': site_id,'webname':find['name'],'ftp':1,'database':1,'path':1})) except: pass self.WriteLogs(json.dumps({'name':f'Deployment failed:{e}','total':0,'used':0,'pre':0,'speed':0,'status': 1})) return public.return_message(-1, 0, str(e)) # 处理临时文件并解压 def set_temp_file(self, filename, path): check_res = public.ExecShell('unzip -t ' + filename) if check_res[1].find('errors detected') != -1 or check_res[1].find('cannot find') != -1: self.WriteLogs(json.dumps( {'name': 'Error: The compressed file is damaged or not a valid ZIP file.', 'total': 0, 'used': 0, 'pre': 0, 'speed': 0,'status': 0})) return False self.WriteLogs(json.dumps({'name': 'The file is being decompressed and saved to the target directory....', 'total': 0, 'used': 0, 'pre': 0, 'speed': 0,'status': 0})) if not os.path.exists(path): os.makedirs(path) unzip_cmd = 'unzip -o ' + filename + ' -d ' + path public.ExecShell(unzip_cmd) if os.path.exists(path): self.WriteLogs(json.dumps({'name': 'Compression completed', 'total': 100, 'used': 100, 'pre': 100, 'speed': 0,'status': 0})) return True else: return False def copy_to(self,src,dst): try: if src[-1] == '/': src = src[:-1] if dst[-1] == '/': dst = dst[:-1] if not os.path.exists(src): return False if not os.path.exists(dst): os.makedirs(dst) import shutil for p_name in os.listdir(src): f_src = src + '/' + p_name f_dst = dst + '/' + p_name if os.path.isdir(f_src): print(shutil.copytree(f_src,f_dst)) else: print(shutil.copyfile(f_src,f_dst)) return True except: return False #获取进度 def GetSpeed(self,get): try: if not os.path.exists(self.logPath):return public.returnMsg(False, public.lang("There are currently no deployment tasks!")) return json.loads(public.readFile(self.logPath)) except: return {'name':'Ready to deploy','total':0,'used':0,'pre':0,'speed':0, 'status': 1} #获取包信息 def GetPackageInfo(self,name, version): data = self.GetDepList(None) if not data: return False downUrl = public.get_url() + '/install/package' for info in data['list']: if info['name'] == name: for v in info['version_list']: if version == v['version']: v['download'] = v['download'].replace('{Download}', downUrl) return v return False #检查指定包是否存在 def CheckPackageExists(self,name): data = self.GetDepList(None) if not data: return False for info in data['list']: if info['name'] == name: return True return False #文件的MD5值 def GetFileMd5(self,filename): if not os.path.isfile(filename): return False import hashlib myhash = hashlib.md5() f = open(filename,'rb') while True: b = f.read(8096) if not b : break myhash.update(b) f.close() return myhash.hexdigest() #获取站点标识 def GetSiteId(self,get): return public.M('sites').where('name=?',(get.webname,)).getField('id') # 邮局插件安装roundcube调用 目录放到 'plugin/mail_sys'; def SetupPackage_roundcube(self, get): name = get.dname site_name = get.site_name php_version = get.php_version # 取基础信息 find = public.M('sites').where('name=?', (site_name,)).field('id,path').find() path = find['path'] pinfo = { "username": "", "ps": "Free and Open Source Webmail Software", "php": "72,73,74,80", "run": "", "name": "roundcube", "title": "Roundcube", "type": 6, "chmod": "", "ext": "pathinfo,exif", "version": "1.6.11", "install": "", "download": f"{public.OfficialDownloadBase()}/install/package/roundcubemail.zip", "password": "", "config": "/config/config.inc.php", "md5": "785660db6540692b5c0eb240b41816e9" } # 检查本地包 self.WriteLogs( json.dumps({'name': "Verifying package...", 'total': 0, 'used': 0, 'pre': 0, 'speed': 0,'status': 0})) # 安装包 packageZip = 'plugin/mail_sys/' + name + '.zip' isDownload = False if os.path.exists(packageZip): md5str = self.GetFileMd5(packageZip) if md5str != pinfo['md5']: isDownload = True else: isDownload = True # 删除多余文件 rm_file = path + '/index.html' if os.path.exists(rm_file): os.remove(rm_file) # 下载文件 if isDownload: self.WriteLogs( json.dumps({'name': "Download", 'total': 0, 'used': 0, 'pre': 0, 'speed': 0,'status': 0})) self.DownloadFile(pinfo['download'], packageZip) if not os.path.exists(packageZip): return public.returnMsg(False, public.lang("File download failed!")) self.WriteLogs(json.dumps({'name': "Unpacking the package...", 'total': 0, 'used': 0, 'pre': 0, 'speed': 0,'status': 0})) public.ExecShell('unzip -o ' + packageZip + ' -d ' + path + '/') # 设置权限 self.WriteLogs( json.dumps({'name': public.GetMsg("SET_PERMISSION"), 'total': 0, 'used': 0, 'pre': 0, 'speed': 0,'status': 0})) public.ExecShell('chmod -R 755 ' + path) public.ExecShell('chown -R www.www ' + path) if pinfo['chmod'] != "": access = pinfo['chmod'].split(',') for chm in access: tmp = chm.split('|') if len(tmp) != 2: continue; public.ExecShell('chmod -R ' + tmp[0] + ' ' + path + '/' + tmp[1]) # # 安装PHP扩展 # self.WriteLogs(json.dumps( # {'name': public.GetMsg("INSTALL_NECESSARY_PHP_EXT"), 'total': 0, 'used': 0, 'pre': 0, 'speed': 0})) # if pinfo['ext'] != '': # exts = pinfo['ext'].split(',') # import files # mfile = files.files() # for ext in exts: # if ext == 'pathinfo': # import config # con = config.config() # get.version = php_version # get.type = 'on' # con.setPathInfo(get) # else: # get.name = ext # get.version = php_version # get.type = '1' # mfile.InstallSoft(get) # 执行额外shell进行依赖安装 self.WriteLogs( json.dumps({'name': public.GetMsg("EXECUTE_EXTRA_SHELL"), 'total': 0, 'used': 0, 'pre': 0, 'speed': 0,'status': 0})) if os.path.exists(path + '/install.sh'): public.ExecShell('cd ' + path + ' && bash ' + 'install.sh') public.ExecShell('rm -f ' + path + '/install.sh') # 是否执行Composer if os.path.exists(path + '/composer.json'): self.WriteLogs(json.dumps({'name': 'Execute Composer', 'total': 0, 'used': 0, 'pre': 0, 'speed': 0,'status': 0})) if not os.path.exists(path + '/composer.lock'): execPHP = '/www/server/php/' + php_version + '/bin/php' if execPHP: if public.get_url().find('125.88'): public.ExecShell( 'cd ' + path + ' && ' + execPHP + ' /usr/bin/composer config repo.packagist composer https://packagist.phpcomposer.com') import panelSite phpini = '/www/server/php/' + php_version + '/etc/php.ini' phpiniConf = public.readFile(phpini) phpiniConf = phpiniConf.replace('proc_open,proc_get_status,', '') public.writeFile(phpini, phpiniConf) public.ExecShell( 'nohup cd ' + path + ' && ' + execPHP + ' /usr/bin/composer install -vvv > /tmp/composer.log 2>&1 &') # 写伪静态 self.WriteLogs( json.dumps({'name': public.GetMsg("SET_URL_REWRITE"), 'total': 0, 'used': 0, 'pre': 0, 'speed': 0,'status': 0})) swfile = path + '/nginx.rewrite' if os.path.exists(swfile): rewriteConf = public.readFile(swfile) dwfile = self.__panelPath + '/vhost/rewrite/' + site_name + '.conf' public.writeFile(dwfile, rewriteConf) # 删除伪静态文件 public.ExecShell("rm -f " + path + '/*.rewrite') # 设置运行目录 self.WriteLogs(json.dumps({'name': public.GetMsg("SET_RUN_DIR"), 'total': 0, 'used': 0, 'pre': 0, 'speed': 0,'status': 0})) if pinfo['run'] != '/': import panelSite siteObj = panelSite.panelSite() mobj = obj() mobj.id = find['id'] mobj.runPath = pinfo['run'] # return find['id'] siteObj.SetSiteRunPath(mobj) # 导入数据 self.WriteLogs(json.dumps({'name': public.GetMsg("IMPORT_DB"), 'total': 0, 'used': 0, 'pre': 0, 'speed': 0,'status': 0})) if os.path.exists(path + '/import.sql'): databaseInfo = public.M('databases').where('pid=?', (find['id'],)).field('username,password').find() if databaseInfo: public.ExecShell('/www/server/mysql/bin/mysql -u' + databaseInfo['username'] + ' -p' + databaseInfo[ 'password'] + ' ' + databaseInfo['username'] + ' < ' + path + '/import.sql') public.ExecShell('rm -f ' + path + '/import.sql') # /www/wwwroot/moyumao.top + '/' + /config/config.inc.php siteConfigFile = path + '/' + pinfo['config'] if os.path.exists(siteConfigFile): siteConfig = public.readFile(siteConfigFile) siteConfig = siteConfig.replace('BT_DB_USERNAME', databaseInfo['username']) siteConfig = siteConfig.replace('BT_DB_PASSWORD', databaseInfo['password']) siteConfig = siteConfig.replace('BT_DB_NAME', databaseInfo['username']) # public.print_log("写入数据库文件 ---{}".format(siteConfigFile)) public.writeFile(siteConfigFile, siteConfig) public.serviceReload() # 提交安装统计 import threading import requests threading.Thread(target=requests.post, kwargs={ 'url': '{}/api/panel/panel_count_daily'.format(public.OfficialApiBase()), 'data': { 'name': 'webmail_Roundcube', }}).start() self.WriteLogs( json.dumps({'name': public.GetMsg("READY_DEPLOY"), 'total': 0, 'used': 0, 'pre': 0, 'speed': 0,'status': 1})) return public.returnMsg(True, pinfo)