1899 lines
90 KiB
Python
1899 lines
90 KiB
Python
# coding: utf-8
|
||
# -------------------------------------------------------------------
|
||
# yakpanel
|
||
# -------------------------------------------------------------------
|
||
# Copyright (c) 2015-2017 yakpanel(https://www.yakpanel.com) All rights reserved.
|
||
# -------------------------------------------------------------------
|
||
# Author: sww <sww@yakpanel.com>
|
||
# -------------------------------------------------------------------
|
||
# php异步项目部署模块
|
||
# ------------------------------
|
||
import traceback
|
||
import re, os, sys, json, time, hashlib
|
||
import shutil
|
||
|
||
if '/www/server/panel/' not in sys.path:
|
||
sys.path.insert(0, '/www/server/panel/')
|
||
from mod.base.web_conf import IpRestrict, remove_sites_service_config
|
||
from mod.base.process.process import RealProcess
|
||
from mod.base.process.server import RealServer
|
||
from mod.base.database_tool import add_database
|
||
from mod.base.web_conf import NginxDomainTool, check_domain
|
||
from mod.base.web_conf.dir_tool import DirTool
|
||
from mod.base.web_conf.proxy import Proxy
|
||
from mod.base.backup_tool import VersionTool
|
||
from mod.base.web_conf.referer import Referer
|
||
from mod.base.web_conf.redirect import Redirect
|
||
from mod.base.process.server import syssafe_admin
|
||
|
||
if '/www/server/panel/class/' not in sys.path:
|
||
sys.path.insert(0, '/www/server/panel/class/')
|
||
import public
|
||
import tarfile
|
||
import psutil
|
||
|
||
try:
|
||
import zipfile
|
||
except:
|
||
public.ExecShell("btpip install zipfile")
|
||
import zipfile
|
||
try:
|
||
import gzip
|
||
except:
|
||
public.ExecShell("btpip install indexed-gzip")
|
||
import gzip
|
||
try:
|
||
import bz2
|
||
except:
|
||
public.ExecShell("btpip install bz2file")
|
||
import bz2
|
||
|
||
|
||
class main(IpRestrict, RealProcess, Proxy, Redirect): # 继承并使用同ip黑白名单限制 SSLManager ssl继承证书管理
|
||
_phpa_path = '/www/server/phpa_project' #
|
||
_phpa_logs = '{}/logs'.format(_phpa_path)
|
||
setupPath = public.get_setup_path()
|
||
is_ipv6 = os.path.exists(setupPath + '/panel/data/ipv6.pl')
|
||
siteName = None
|
||
sitePath = None
|
||
sitePort = None
|
||
phpVersion = None
|
||
pids = None
|
||
_log_name = '项目管理'
|
||
_php_logs_path = "/www/wwwlogs/php_async"
|
||
__proxyfile = '{}/data/proxyfile.json'.format(public.get_panel_path())
|
||
group_file = "/www/server/panel/data/phpa_project_groups.json"
|
||
|
||
def __init__(self):
|
||
IpRestrict.__init__(self)
|
||
Proxy.__init__(self)
|
||
RealProcess.__init__(self)
|
||
Redirect.__init__(self)
|
||
if not os.path.exists(self.group_file): public.writeFile(self.group_file, '{}')
|
||
|
||
def create_project(self, get):
|
||
try:
|
||
public.set_module_logs('phpmod', 'create_phpmod', 1)
|
||
if not hasattr(get, 'webname'):
|
||
return public.returnResult(False, '请输入域名!')
|
||
if not hasattr(get, 'site_path'):
|
||
return public.returnResult(False, '请输入项目路径!')
|
||
if not os.path.exists(get.site_path): public.ExecShell('mkdir -p {}'.format(get.site_path))
|
||
if not hasattr(get, 'project_proxy_path') and hasattr(get, 'open_proxy') and int(get.open_proxy) == 1:
|
||
return public.returnResult(False, '设置反向代理!')
|
||
if not hasattr(get, 'project_port') or get.project_port == '':
|
||
get.project_port = 0
|
||
if not hasattr(get, 'php_version'):
|
||
return public.returnResult(False, '请选择PHP版本!')
|
||
if not hasattr(get, 'project_cmd'):
|
||
return public.returnResult(False, '请输入启动命令!')
|
||
if not hasattr(get, 'is_power_on'):
|
||
get.is_power_on = 1
|
||
if not hasattr(get, 'project_ps'):
|
||
get.project_ps = ''
|
||
if not hasattr(get, 'sql'):
|
||
get.sql = ""
|
||
if not hasattr(get, 'run_user'):
|
||
get.run_user = 'www'
|
||
if not hasattr(get, 'composer_version'):
|
||
get.composer_version = '2.7.3'
|
||
if not os.path.exists('/www/server/phpa_project/logs'):
|
||
public.ExecShell('mkdir -p /www/server/phpa_project/logs')
|
||
public.ExecShell('chmod -R 777 /www/server/phpa_project/logs')
|
||
# 设置项目目录权限
|
||
public.ExecShell("chown -R {}:{} {}".format(get.run_user.strip(), get.run_user.strip(), get.site_path))
|
||
public.ExecShell("chmod -R 755 {}".format(get.site_path))
|
||
webname = json.loads(get.webname)
|
||
# 域名重复性检查
|
||
domains = [webname['domain']] + webname['domainlist']
|
||
for domain in domains:
|
||
if public.M('sites').where('name=?', (domain.split(':')[0].strip(),)).count():
|
||
return public.returnResult(False, '指定域名已存在: {}'.format(domain))
|
||
|
||
# 域名存在检查
|
||
get.project_cmd = get.project_cmd.strip()
|
||
|
||
# 创建项目
|
||
if public.M('sites').where('name=?', (webname['domain'].split(':')[0].strip(),)).count():
|
||
return public.returnResult(False, '指定项目已存在: {}'.format(webname))
|
||
|
||
self.siteName = self.ToPunycode(webname['domain'].split(':')[0].strip())
|
||
self.sitePath = self.ToPunycodePath(get.site_path)
|
||
self.sitePort = 80 if ':' not in webname['domain'] else webname['domain'].split(':')[1].strip()
|
||
self.phpVersion = get.php_version
|
||
# 添加nginx网站配置文件
|
||
self.nginxAdd()
|
||
# 添加多余的域名
|
||
domainlist = []
|
||
for domain in domains:
|
||
domainname = domain.split(':')[0].strip()
|
||
domainport = '80' if ':' not in domain else domain.split(':')[1].strip()
|
||
domainlist.append((domainname, domainport))
|
||
if len(domainlist) > 1:
|
||
NginxDomainTool().nginx_set_domain(self.siteName, *domainlist)
|
||
# 创建默认文档
|
||
if not os.path.exists('{}/index.ttml'.format(self.sitePath)):
|
||
connect = """
|
||
<!doctype html>
|
||
<html>
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<title>恭喜,异步项目创建成功!</title>
|
||
<style>
|
||
.container {
|
||
width: 60%;
|
||
margin: 10% auto 0;
|
||
background-color: #f0f0f0;
|
||
padding: 2% 5%;
|
||
border-radius: 10px
|
||
}
|
||
|
||
ul {
|
||
padding-left: 20px;
|
||
}
|
||
|
||
ul li {
|
||
line-height: 2.3
|
||
}
|
||
|
||
a {
|
||
color: #20a53a
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<h1>恭喜,异步项目创建成功!</h1>
|
||
<h3>这是默认index.html,本页面由系统自动生成</h3>
|
||
<ul>
|
||
<li>本页面在项目目录下的index.html</li>
|
||
<li>您可以修改、删除或覆盖本页面</li>
|
||
<li>您可以将web目录修改为您的前端文件所在目录</li>
|
||
<li>您也可以将后端直接反代到此域名</li>
|
||
</ul>
|
||
</div>
|
||
</body>
|
||
"""
|
||
public.writeFile('{}/index.html'.format(self.sitePath), connect)
|
||
# 添加反向代理
|
||
if hasattr(get, 'open_proxy') and int(get.open_proxy) == 1:
|
||
args = {
|
||
"proxyname": self.siteName,
|
||
"sitename": self.siteName,
|
||
"proxydir": get.project_proxy_path,
|
||
"proxysite": "http://127.0.0.1:{}".format(get.project_port),
|
||
"todomain": "$host",
|
||
"type": "1",
|
||
"cache": "1",
|
||
"subfilter": '[{"sub1":"","sub2":""},{"sub1":"","sub2":""},{"sub1":"","sub2":""}]',
|
||
"advanced": "1",
|
||
"cachetime": "1",
|
||
}
|
||
|
||
self.create_proxy(public.to_dict_obj(args))
|
||
# 修改启动命令
|
||
start_cmd = get.project_cmd
|
||
if 'php' == get.project_cmd[:3]:
|
||
get.project_cmd = '/www/server/php/{}/bin/php -c {}/php-cli.ini {}'.format(get.php_version, get.site_path, get.project_cmd[3:])
|
||
else:
|
||
get.project_cmd = '/www/server/php/{}/bin/php -c {}/php-cli.ini {}'.format(get.php_version, get.site_path, get.project_cmd)
|
||
is_fork = 0
|
||
if ' -d' in get.project_cmd:
|
||
is_fork = 1
|
||
# 创建服务 并设置开机启动
|
||
realserver = RealServer()
|
||
realserver.create_daemon(self.siteName, '', get.project_cmd, get.site_path, get.run_user.strip(), get.is_power_on, logs_file=os.path.join(self._phpa_logs, self.siteName + '.log'),
|
||
is_fork=is_fork)
|
||
# 安装依赖
|
||
dependence = 0
|
||
if hasattr(get, 'install_dependence') and get.install_dependence == "1":
|
||
public.run_thread(self.install_dependence, (self.siteName, get.php_version, get.site_path, get.composer_version))
|
||
dependence = 1
|
||
else:
|
||
public.ExecShell('cp /www/server/php/{}/etc/php-cli.ini {}/'.format(get.php_version, get.site_path))
|
||
public.ExecShell("sed -i '/disable_functions/d' {}".format(os.path.join(get.site_path, 'php-cli.ini')))
|
||
public.ExecShell("chown root:root {}".format(os.path.join(get.site_path, 'php-cli.ini')))
|
||
public.ExecShell("chomd 644 {}".format(os.path.join(get.site_path, 'php-cli.ini')))
|
||
# 写入数据库
|
||
pdata = {
|
||
'name': self.siteName,
|
||
'path': get.site_path,
|
||
'ps': get.project_ps,
|
||
'status': 1,
|
||
'type_id': 0,
|
||
'project_type': 'PHP',
|
||
'project_config': json.dumps(
|
||
{
|
||
'ssl_path': '/www/wwwroot/phpa_ssl',
|
||
'sitename': self.siteName,
|
||
'domains': [webname['domain']] + webname['domainlist'],
|
||
'project_path': get.site_path,
|
||
'project_cmd': get.project_cmd,
|
||
'is_power_on': get.is_power_on,
|
||
'port': int(get.project_port),
|
||
'log_path': self._log_name,
|
||
'php_version': get.php_version,
|
||
'dependence': dependence,
|
||
'start_cmd': start_cmd,
|
||
'project_port': get.project_port,
|
||
'type': 'PHPMOD',
|
||
'run_user': get.run_user.strip(),
|
||
}
|
||
),
|
||
'addtime': public.getDate()
|
||
}
|
||
pid = public.M('sites').insert(pdata)
|
||
for domain in domains:
|
||
domain_arr = domain.split(':')
|
||
if len(domain_arr) == 1:
|
||
domain_arr.append(80)
|
||
domain_arr[0] = self.ToPunycode(domain_arr[0].strip())
|
||
public.M('domain').add('pid,name,port,addtime', (pid, domain_arr[0], domain_arr[1], public.getDate()))
|
||
|
||
# 检查是否创建数据库
|
||
if get.sql == "MYSQL":
|
||
if not (hasattr(get, 'sql_user') or hasattr(get, 'sql_pwd') or hasattr(get, 'sql_codeing')):
|
||
return public.returnResult(False, '请填写数据库信息')
|
||
mysql_data = {
|
||
"server_id": 0,
|
||
"database_name": get.sql_user,
|
||
"db_user": get.sql_user,
|
||
"password": get.sql_pwd,
|
||
"dataAccess": "ip",
|
||
"address": "127.0.0.1",
|
||
"codeing": get.sql_codeing,
|
||
"ps": "",
|
||
"listen_ip": "0.0.0.0/0",
|
||
"host": "",
|
||
"pid": pid,
|
||
}
|
||
add_database(db_type="mysql", data=mysql_data)
|
||
|
||
self.modify_project_run_state(public.to_dict_obj({'sitename': self.siteName, 'project_action': 'start'}))
|
||
# 写日志
|
||
public.WriteLog(self._log_name, '添加PHP动态项目{}'.format(webname))
|
||
return public.returnResult(True, 'Successfully added' + ("" if dependence == 0 else ",正在安装依赖!"))
|
||
except:
|
||
return public.returnResult(False, str(traceback.format_exc()))
|
||
|
||
def re_install_dependence(self, get):
|
||
if not hasattr(get, 'id'):
|
||
return public.returnResult(False, '参数缺失:ids!')
|
||
project_config = public.M('sites').where('id=?', (get.id,)).select()[0]
|
||
project_config = json.loads(project_config['project_config'])
|
||
if os.path.exists('/tmp/php_mod_{}.pl'.format(project_config['name'])) and int(project_config['dependence']) == 1:
|
||
pid = public.readFile('/tmp/php_mod_{}.pl'.format(project_config['name']))
|
||
try:
|
||
import psutil
|
||
p = psutil.Process(int(pid))
|
||
if p.name() == 'Yak-Panel' or p.name() == 'btpython':
|
||
return public.returnResult(False, '正在安装中请稍后重试!')
|
||
except:
|
||
pass
|
||
public.run_thread(self.install_dependence, (project_config['name'], project_config['php_version'], project_config['path']))
|
||
return public.returnResult(True, '正在安装依赖!')
|
||
|
||
def check_install(self, get):
|
||
if not hasattr(get, 'name'):
|
||
return public.returnResult(False, '参数缺失:name!')
|
||
if not hasattr(get, 'php_version'):
|
||
return public.returnResult(False, '参数缺失:php_version!')
|
||
if get.name == 'swoole':
|
||
res = public.ExecShell("/www/server/php/{}/bin/php --ri {}|grep Version|awk '{{print $3}}'".format(get.php_version, get.name))
|
||
if res[0] != '':
|
||
return public.returnResult(True, res[0].strip())
|
||
return public.returnResult(False, '未安装!请前往PHP-安装扩展中安装{}!'.format(get.name))
|
||
if get.name == 'fileinfo':
|
||
res = public.ExecShell("/www/server/php/{}/bin/php --ri {}".format(get.php_version, get.name))
|
||
if res[0] != '' and 'fileinfo' in res[0] and 'enabled' in res[0]:
|
||
return public.returnResult(True, '已安装!')
|
||
return public.returnResult(False, '未安装!请前往PHP-安装扩展中安装{}!'.format(get.name))
|
||
|
||
def print_log(self, path, log):
|
||
public.writeFile(path, log, 'a+')
|
||
|
||
def check_auto_install(self, get):
|
||
if not hasattr(get, 'path'):
|
||
return public.returnResult(False, '参数缺失:path!')
|
||
if not os.path.exists("{}/composer.json".format(get.path)):
|
||
return False
|
||
if not os.path.exists('{}/composer.lock'.format(get.path)) or not os.path.exists('{}/vendor'.format(get.path)):
|
||
return True
|
||
return False
|
||
|
||
def get_swoole_correspondence_php(self, get):
|
||
if not hasattr(get, 'swoole_version') and get.swoole_version.strip() in ['2', '4', '5']:
|
||
return public.returnResult(False, '参数缺失:swoole_version!')
|
||
swoole_version = get.swoole_version.strip()
|
||
php_correspondence = {
|
||
'2': ['52', '53', '54', '55', '70', '71', '72'],
|
||
'4': ['72', '73', '74', '80', '81', '82'],
|
||
'5': ['80', '81', '82', '83'],
|
||
}
|
||
php_list = php_correspondence[swoole_version]
|
||
result = []
|
||
for php in php_list:
|
||
setup_status = os.path.exists('/www/server/php/{}/bin/php'.format(php))
|
||
res = public.ExecShell("/www/server/php/{}/bin/php --ri swoole|grep Version|awk '{{print $3}}'".format(php))[0]
|
||
swoole_status = True if res != '' else False
|
||
swoole_version = res.strip()
|
||
result.append({
|
||
'php_version': php,
|
||
'setup_status': setup_status,
|
||
'swoole_status': swoole_status,
|
||
'swoole_version': swoole_version,
|
||
'setup_swoole_isactive': True if swoole_version != '' and swoole_version[0] == get.swoole_version else False
|
||
})
|
||
return public.returnResult(True, data=result, msg='获取成功!')
|
||
|
||
@syssafe_admin
|
||
def install_composer(self, composer_version, logs_path):
|
||
composer_name = '/usr/bin/composer{}'.format(('_' + composer_version.replace('.', '_')) if composer_version != '' else '')
|
||
if not os.path.exists(composer_name) and composer_version != '':
|
||
url = public.get_url() + '/src/compose/composer_{}'.format(composer_version.replace('.', '_'))
|
||
public.ExecShell('wget -O {} {} &> {}'.format(composer_name, url, logs_path))
|
||
if os.path.exists(composer_name):
|
||
public.ExecShell('chmod +x {}'.format(composer_name))
|
||
public.ExecShell('echo "{}" >> {}'.format('composer{}下载成功!'.format(composer_version), logs_path))
|
||
|
||
def install_dependence(self, webname, php_version, site_path, composer_version='2.7.3'):
|
||
logs_path = '/tmp/{}_install_dependence.log'.format(webname)
|
||
|
||
# 检查锁和所文件
|
||
if os.path.exists('{}/composer.lock'.format(site_path)):
|
||
if os.path.exists('{}/vendor'.format(site_path)):
|
||
public.ExecShell('echo "{}" >> {}'.format('composer.lock和vendor目录存在,不需要安装依赖!', logs_path))
|
||
try:
|
||
project_list = public.M('sites').where('project_type=? and name=?', ('PHP', webname)).field('project_config').select()[0]
|
||
project_config = json.loads(project_list['project_config'])
|
||
project_config['dependence'] = 0
|
||
public.M('sites').where('project_type=? and name=?', ('PHP', webname)).setField('project_config', json.dumps(project_config))
|
||
except:
|
||
pass
|
||
if not os.path.exists('/www/server/phpa_project/logs'):
|
||
public.ExecShell('mkdir -p /www/server/phpa_project/logs')
|
||
public.ExecShell('chmod -R 755 /www/server/phpa_project/logs')
|
||
RealServer().daemon_admin(str(webname), 'start')
|
||
return
|
||
else:
|
||
public.ExecShell('echo "{}" >> {}'.format('composer.lock存在,但vendor目录不存在,开始安装依赖!', logs_path))
|
||
else:
|
||
public.ExecShell('echo "{}" > {}'.format('composer.lock不存在,开始安装依赖!', logs_path))
|
||
self.install_composer(composer_version, logs_path)
|
||
public.writeFile('/tmp/php_mod_{}.pl'.format(webname), str(os.getpid()))
|
||
public.ExecShell('cp /www/server/php/{}/etc/php-cli.ini {}/'.format(php_version, site_path))
|
||
public.ExecShell("sed -i '/disable_functions/d' {}".format(os.path.join(site_path, 'php-cli.ini')))
|
||
# 尝试安装composer中的依赖
|
||
self.print_log(logs_path, '开始安装composer依赖!\n')
|
||
# self.print_log(logs_path, 'cd {} && export COMPOSER_ALLOW_SUPERUSER=1 && /www/server/php/{}/bin/php -c {} /usr/bin/composer{} install --no-interaction &>> {}'.format(site_path, php_version,
|
||
# os.path.join(site_path,
|
||
# 'php-cli.ini'),
|
||
# '_' + composer_version.replace(
|
||
# '.',
|
||
# '_') if composer_version != '' else '',
|
||
# logs_path))
|
||
public.ExecShell(
|
||
'cd {} && export COMPOSER_ALLOW_SUPERUSER=1 && /www/server/php/{}/bin/php -c {} /usr/bin/composer{} install --no-interaction &>> {}'.format(site_path, php_version,
|
||
os.path.join(site_path, 'php-cli.ini'),
|
||
'_' + composer_version.replace('.',
|
||
'_') if composer_version != '' else '',
|
||
logs_path))
|
||
# 查看安装依赖报错
|
||
logs = public.readFile(logs_path)
|
||
if "No composer.lock file present. Updating dependencies to latest instead of installing from lock file" in logs and not os.path.exists('{}/composer.lock'.format(site_path)):
|
||
self.print_log(logs_path, '\ncomposer.lock文件不存在,安装依赖时需要使用此文件,请前往项目官网下载composer.lock文件放入{}目录后前往<compsoer页面>重新安装依赖!'.format(site_path))
|
||
public.ExecShell('cp /www/server/php/{}/etc/php-cli.ini {} >> {}'.format(php_version, site_path, logs_path))
|
||
public.ExecShell("sed -i '/disable_functions/d' {}".format(os.path.join(site_path, 'php-cli.ini')))
|
||
public.ExecShell("chown root:root {}".format(os.path.join(site_path, 'php-cli.ini')))
|
||
public.ExecShell("chomd 644 {}".format(os.path.join(site_path, 'php-cli.ini')))
|
||
self.print_log(logs_path, '安装依赖结束!')
|
||
try:
|
||
project_list = public.M('sites').where('project_type=? and name=?', ('PHP', webname)).field('project_config').select()[0]
|
||
project_config = json.loads(project_list['project_config'])
|
||
project_config['dependence'] = 0
|
||
public.M('sites').where('project_type=? and name=?', ('PHP', webname)).setField('project_config', json.dumps(project_config))
|
||
except:
|
||
pass
|
||
if not os.path.exists('/www/server/phpa_project/logs'):
|
||
public.ExecShell('mkdir -p /www/server/phpa_project/logs')
|
||
public.ExecShell('chmod -R 755 /www/server/phpa_project/logs')
|
||
self.modify_project_run_state(public.to_dict_obj({'sitename': webname, 'project_action': 'start'}))
|
||
|
||
def check_development_setup(self, php_version, name):
|
||
try:
|
||
# /ajax?action=GetPHPConfig
|
||
import ajax
|
||
a = ajax.ajax()
|
||
get = public.to_dict_obj({})
|
||
get.version = php_version
|
||
res = a.GetPHPConfig(get)
|
||
for i in res['libs']:
|
||
if i['name'] == name and i['status'] == True:
|
||
return True
|
||
return False
|
||
except:
|
||
return False
|
||
|
||
# 域名编码转换
|
||
def ToPunycode(self, domain):
|
||
import re
|
||
if sys.version_info[0] == 2: domain = domain.encode('utf8')
|
||
tmp = domain.split('.')
|
||
newdomain = ''
|
||
for dkey in tmp:
|
||
if dkey == '*': continue
|
||
# 匹配非ascii字符
|
||
match = re.search(u"[\x80-\xff]+", dkey)
|
||
if not match: match = re.search(u"[\u4e00-\u9fa5]+", dkey)
|
||
if not match:
|
||
newdomain += dkey + '.'
|
||
else:
|
||
if sys.version_info[0] == 2:
|
||
newdomain += 'xn--' + dkey.decode('utf-8').encode('punycode') + '.'
|
||
else:
|
||
newdomain += 'xn--' + dkey.encode('punycode').decode('utf-8') + '.'
|
||
if tmp[0] == '*': newdomain = "*." + newdomain
|
||
return newdomain[0:-1]
|
||
|
||
def ToPunycodePath(self, path):
|
||
if sys.version_info[0] == 2: path = path.encode('utf-8')
|
||
if os.path.exists(path): return path
|
||
import re
|
||
match = re.search(u"[\x80-\xff]+", path)
|
||
if not match: match = re.search(u"[\u4e00-\u9fa5]+", path)
|
||
if not match: return path
|
||
npath = ''
|
||
for ph in path.split('/'):
|
||
npath += '/' + self.ToPunycode(ph)
|
||
return npath.replace('//', '/')
|
||
|
||
# 添加到nginx
|
||
def nginxAdd(self):
|
||
if not os.path.exists('/www/server/panel/class/404_settings.json'):
|
||
public.writeFile('/www/server/panel/class/404_settings.json', json.dumps({}))
|
||
|
||
template = None
|
||
use_template = public.readFile("{}/data/use_nginx_template.pl".format(public.get_panel_path()))
|
||
if isinstance(use_template, str):
|
||
template_file = "{}/data/nginx_template/{}".format(public.get_panel_path(), use_template)
|
||
if os.path.isfile(template_file):
|
||
template = public.readFile(template_file)
|
||
|
||
with open('/www/server/panel/class/404_settings.json', 'r') as f:
|
||
settings = json.load(f)
|
||
status = settings.get('status', "0")
|
||
filename = settings.get('filename', "404.html")
|
||
error_page_line = 'error_page 404 /' + filename + ';'
|
||
|
||
if status == "0":
|
||
error_page_line = '#' + error_page_line
|
||
else:
|
||
error_page_line = 'error_page 404 /' + filename + ';'
|
||
|
||
listen_ipv6 = ''
|
||
if self.is_ipv6: listen_ipv6 = "\n listen [::]:%s;" % self.sitePort
|
||
|
||
conf = r'''server
|
||
{{
|
||
listen {listen_port};{listen_ipv6}
|
||
server_name {site_name};
|
||
index index.php index.html index.htm default.php default.htm default.html;
|
||
root {site_path};
|
||
#CERT-APPLY-CHECK--START
|
||
# 用于SSL证书申请时的文件验证相关配置 -- 请勿删除
|
||
include /www/server/panel/vhost/nginx/well-known/{site_name}.conf;
|
||
#CERT-APPLY-CHECK--END
|
||
|
||
#SSL-START {ssl_start_msg}
|
||
#error_page 404/404.html;
|
||
#SSL-END
|
||
|
||
#ERROR-PAGE-START {err_page_msg}
|
||
{error_page_line}
|
||
#error_page 502 /502.html;
|
||
#ERROR-PAGE-END
|
||
|
||
#PHP-INFO-START {php_info_start}
|
||
include enable-php-{php_version}.conf;
|
||
#PHP-INFO-END
|
||
|
||
#REWRITE-START {rewrite_start_msg}
|
||
include {setup_path}/panel/vhost/rewrite/{site_name}.conf;
|
||
#REWRITE-END
|
||
|
||
#禁止访问的文件或目录
|
||
location ~ ^/(\.user.ini|\.htaccess|\.git|\.env|\.svn|\.project|LICENSE|README.md)
|
||
{{
|
||
return 404;
|
||
}}
|
||
|
||
#一键申请SSL证书验证目录相关设置
|
||
location ~ \.well-known{{
|
||
allow all;
|
||
}}
|
||
|
||
#禁止在证书验证目录放入敏感文件
|
||
if ( $uri ~ "^/\.well-known/.*\.(php|jsp|py|js|css|lua|ts|go|zip|tar\.gz|rar|7z|sql|bak)$" ) {{
|
||
return 403;
|
||
}}
|
||
|
||
location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf)$
|
||
{{
|
||
expires 30d;
|
||
error_log /dev/null;
|
||
access_log /dev/null;
|
||
}}
|
||
|
||
location ~ .*\\.(js|css)?$
|
||
{{
|
||
expires 12h;
|
||
error_log /dev/null;
|
||
access_log /dev/null;
|
||
}}
|
||
access_log {log_path}/{site_name}.log;
|
||
error_log {log_path}/{site_name}.error.log;
|
||
}}'''.format(
|
||
listen_port=self.sitePort,
|
||
listen_ipv6=listen_ipv6,
|
||
site_path=self.sitePath,
|
||
ssl_start_msg=public.getMsg('NGINX_CONF_MSG1'),
|
||
err_page_msg=public.getMsg('NGINX_CONF_MSG2'),
|
||
php_info_start=public.getMsg('NGINX_CONF_MSG3'),
|
||
php_version=self.phpVersion,
|
||
setup_path=self.setupPath,
|
||
rewrite_start_msg=public.getMsg('NGINX_CONF_MSG4'),
|
||
log_path=self.get_sites_log_path(),
|
||
site_name=self.siteName,
|
||
error_page_line=error_page_line
|
||
)
|
||
|
||
template_conf = None
|
||
if isinstance(template, str):
|
||
try:
|
||
template_conf = template.format(
|
||
listen_port=self.sitePort,
|
||
listen_ipv6=listen_ipv6,
|
||
site_path=self.sitePath,
|
||
ssl_start_msg=public.getMsg('NGINX_CONF_MSG1'),
|
||
err_page_msg=public.getMsg('NGINX_CONF_MSG2'),
|
||
php_info_start=public.getMsg('NGINX_CONF_MSG3'),
|
||
php_version=self.phpVersion,
|
||
setup_path=self.setupPath,
|
||
rewrite_start_msg=public.getMsg('NGINX_CONF_MSG4'),
|
||
log_path=self.get_sites_log_path(),
|
||
site_name=self.siteName,
|
||
error_page_line=error_page_line
|
||
)
|
||
except:
|
||
template_conf = None
|
||
# 写配置文件
|
||
if not os.path.exists("/www/server/panel/vhost/nginx/well-known"):
|
||
os.makedirs("/www/server/panel/vhost/nginx/well-known", 0o600)
|
||
public.writeFile("/www/server/panel/vhost/nginx/well-known/{}.conf".format(self.siteName), "")
|
||
filename = self.setupPath + '/panel/vhost/nginx/' + self.siteName + '.conf'
|
||
if template_conf is not None:
|
||
public.writeFile(filename, template_conf)
|
||
else:
|
||
public.writeFile(filename, conf)
|
||
|
||
# 生成伪静态文件
|
||
urlrewritePath = self.setupPath + '/panel/vhost/rewrite'
|
||
urlrewriteFile = urlrewritePath + '/' + self.siteName + '.conf'
|
||
if not os.path.exists(urlrewritePath): os.makedirs(urlrewritePath)
|
||
open(urlrewriteFile, 'w+').close()
|
||
if not os.path.exists(urlrewritePath):
|
||
public.writeFile(urlrewritePath, '')
|
||
|
||
return True
|
||
|
||
# 删除站点
|
||
def DeleteSite(self, get, multiple=None):
|
||
try:
|
||
proxyconf = [] if not os.path.exists(self.__proxyfile) else json.loads(public.readFile(self.__proxyfile))
|
||
id = get.id
|
||
if public.M('sites').where('id=?', (id,)).count() < 1: return public.returnResult(False, '指定站点不存在!')
|
||
siteName = get.webname
|
||
get.siteName = siteName
|
||
# 删除反向代理
|
||
for i in range(len(proxyconf) - 1, -1, -1):
|
||
if proxyconf[i]["sitename"] == siteName:
|
||
del proxyconf[i]
|
||
public.writeFile(self.__proxyfile, json.dumps(proxyconf))
|
||
m_path = self.setupPath + '/panel/vhost/nginx/proxy/' + siteName
|
||
if os.path.exists(m_path): public.ExecShell("rm -rf %s" % m_path)
|
||
|
||
# 删除目录保护
|
||
_dir_aith_file = "%s/panel/data/site_dir_auth.json" % self.setupPath
|
||
_dir_aith_conf = public.readFile(_dir_aith_file)
|
||
# 删除保护目录
|
||
if _dir_aith_conf:
|
||
try:
|
||
_dir_aith_conf = json.loads(_dir_aith_conf)
|
||
if siteName in _dir_aith_conf:
|
||
del (_dir_aith_conf[siteName])
|
||
except:
|
||
pass
|
||
public.writeFile(_dir_aith_file, _dir_aith_conf)
|
||
dir_aith_path = self.setupPath + '/panel/vhost/nginx/dir_auth/' + siteName
|
||
if os.path.exists(dir_aith_path): public.ExecShell("rm -rf %s" % dir_aith_path)
|
||
|
||
# 删除重定向
|
||
__redirectfile = "%s/panel/data/redirect.conf" % self.setupPath
|
||
redirectconf = [] if not os.path.exists(__redirectfile) else json.loads(public.readFile(__redirectfile))
|
||
for i in range(len(redirectconf) - 1, -1, -1):
|
||
if redirectconf[i]["sitename"] == siteName:
|
||
del redirectconf[i]
|
||
public.writeFile(__redirectfile, json.dumps(redirectconf))
|
||
m_path = self.setupPath + '/panel/vhost/nginx/redirect/' + siteName
|
||
if os.path.exists(m_path): public.ExecShell("rm -rf %s" % m_path)
|
||
|
||
# 删除配置文件
|
||
confPath = self.setupPath + '/panel/vhost/nginx/' + siteName + '.conf'
|
||
if os.path.exists(confPath): os.remove(confPath)
|
||
|
||
# 删除伪静态文件
|
||
filename = '/www/server/panel/vhost/rewrite/' + siteName + '.conf'
|
||
if os.path.exists(filename):
|
||
os.remove(filename)
|
||
public.ExecShell("rm -f " + confPath + '/rewrite/' + siteName + "_*")
|
||
|
||
# 删除日志文件
|
||
filename = public.GetConfigValue('logs_path') + '/' + siteName + '*'
|
||
public.ExecShell("rm -f " + filename)
|
||
|
||
# 重载服务
|
||
public.serviceReload()
|
||
|
||
# 从数据库删除
|
||
public.M('sites').where("id=?", (id,)).delete()
|
||
public.M('binding').where("pid=?", (id,)).delete()
|
||
public.M('domain').where("pid=?", (id,)).delete()
|
||
public.WriteLog('TYPE_SITE', "SITE_DEL_SUCCESS", (siteName,))
|
||
|
||
# 是否删除关联数据库
|
||
if hasattr(get, 'database'):
|
||
if get.database == '1':
|
||
find = public.M('databases').where("pid=?", (id,)).field('id,name').find()
|
||
if find:
|
||
import database
|
||
get.name = find['name']
|
||
get.id = find['id']
|
||
database.database().DeleteDatabase(get)
|
||
|
||
# 是否删除关联FTP
|
||
if hasattr(get, 'ftp'):
|
||
if get.ftp == '1':
|
||
find = public.M('ftps').where("pid=?", (id,)).field('id,name').find()
|
||
if find:
|
||
import ftp
|
||
get.username = find['name']
|
||
get.id = find['id']
|
||
ftp.ftp().DeleteUser(get)
|
||
try:
|
||
# 删除项目
|
||
Redirect().remove_redirect_by_project_name(siteName)
|
||
except:
|
||
pass
|
||
RealServer().del_daemon(siteName)
|
||
remove_sites_service_config(siteName)
|
||
return public.returnResult(True, '删除成功')
|
||
except:
|
||
return public.returnResult(False, traceback.format_exc())
|
||
|
||
def get_sites_log_path(get=None):
|
||
log_path = public.readFile("{}/data/sites_log_path.pl".format(public.get_panel_path()))
|
||
if isinstance(log_path, str) and os.path.isdir(log_path):
|
||
return log_path
|
||
return public.GetConfigValue('logs_path')
|
||
|
||
# 检查端口是否被占用
|
||
def check_port_is_used(self, port, sock=False):
|
||
'''
|
||
@name 检查端口是否被占用
|
||
@author sww
|
||
@param port: int<端口>
|
||
@return bool
|
||
'''
|
||
if not isinstance(port, int): port = int(port)
|
||
if port == 0: return False
|
||
project_list = public.M('sites').where('status=? AND project_type=?', (1, 'PHP')).field(
|
||
'name,path,project_config').select()
|
||
for project_find in project_list:
|
||
project_config = json.loads(project_find['project_config'])
|
||
if not 'port' in project_config: continue
|
||
try:
|
||
if int(project_config['port']) == port:
|
||
return True
|
||
except:
|
||
continue
|
||
if sock: return False
|
||
return public.check_tcp('127.0.0.1', port)
|
||
|
||
# 获取项目列表
|
||
def get_project_list(self, get):
|
||
'''
|
||
@name 获取项目列表
|
||
@author sww
|
||
@param get<dict_obj>{
|
||
sitename: string<项目名称>
|
||
}
|
||
@return dict
|
||
'''
|
||
try:
|
||
if not 'p' in get: get.p = 1
|
||
if not 'limit' in get: get.limit = 20
|
||
if not 'callback' in get: get.callback = ''
|
||
if not 'order' in get: get.order = 'id desc'
|
||
type_id = None
|
||
if "type_id" in get:
|
||
try:
|
||
type_id = int(get.type_id)
|
||
except:
|
||
type_id = None
|
||
|
||
if 'search' in get:
|
||
get.sitename = get.search.strip()
|
||
search = "%{}%".format(get.sitename)
|
||
if type_id is None:
|
||
count = public.M('sites').where('project_type=? AND (name LIKE ? OR ps LIKE ?)', ('PHP', search, search)).count()
|
||
data = public.get_page(count, int(get.p), int(get.limit), get.callback)
|
||
data['data'] = public.M('sites').where('project_type=? AND (name LIKE ? OR ps LIKE ?)', ('PHP', search, search)).limit(data['shift'] + ',' + data['row']).order(get.order).select()
|
||
else:
|
||
count = public.M('sites').where('project_type=? AND (name LIKE ? OR ps LIKE ?) AND type_id = ?',
|
||
('PHP', search, search, type_id)).count()
|
||
data = public.get_page(count, int(get.p), int(get.limit), get.callback)
|
||
data['data'] = public.M('sites').where('project_type=? AND (name LIKE ? OR ps LIKE ?) AND type_id = ?', ('PHP', search, search, type_id)).limit(
|
||
data['shift'] + ',' + data['row']).order(get.order).select()
|
||
else:
|
||
if type_id is None:
|
||
count = public.M('sites').where('project_type=?', 'PHP').count()
|
||
data = public.get_page(count, int(get.p), int(get.limit), get.callback)
|
||
data['data'] = public.M('sites').where('project_type=?', 'PHP').limit(data['shift'] + ',' + data['row']).order(get.order).select()
|
||
else:
|
||
count = public.M('sites').where('project_type=? AND type_id = ?', ('PHP', type_id)).count()
|
||
data = public.get_page(count, int(get.p), int(get.limit), get.callback)
|
||
data['data'] = public.M('sites').where('project_type=? AND type_id = ?', ('PHP', type_id)).limit(data['shift'] + ',' + data['row']).order(get.order).select()
|
||
|
||
for i in range(len(data['data'])):
|
||
data['data'][i] = self.get_project_stat(data['data'][i])
|
||
logs_path = '/tmp/{}_install_dependence.log'.format(data['data'][i]['name'])
|
||
if not os.path.exists(logs_path) or int(os.path.getmtime(logs_path)) - int(time.time()) > 20:
|
||
data['data'][i]['dependence'] = 0
|
||
return public.returnResult(True, data=data)
|
||
except:
|
||
return public.returnResult(False, '获取项目列表失败')
|
||
|
||
# 获取项目状态信息
|
||
def get_project_stat(self, project_info):
|
||
'''
|
||
@name 获取项目状态信息
|
||
@author sww
|
||
@param project_info<dict> 项目信息
|
||
@return list
|
||
'''
|
||
project_info['project_config'] = json.loads(project_info['project_config'])
|
||
# project_info['project_config']['bind_extranet'] = int(project_info['project_config']['bind_extranet'])
|
||
# project_info['run'] = self.get_project_run_state(sitename=project_info['name'])
|
||
# project_info['load_info'] = self.get_project_load_info(sitename=project_info['name'])
|
||
project_info['run'] = RealServer().daemon_status(project_info['name'])['status']
|
||
project_info['ssl'] = self.get_ssl_end_date(sitename=project_info['name'])
|
||
project_info['listen'] = []
|
||
project_info['listen_ok'] = True
|
||
# if project_info['load_info']:
|
||
# for pid in project_info['load_info'].keys():
|
||
# if not 'connections' in project_info['load_info'][pid]:
|
||
# project_info['load_info'][pid]['connections'] = []
|
||
# if 'connections' in project_info['load_info'][pid]:
|
||
# for conn in project_info['load_info'][pid]['connections']:
|
||
# if not conn['status'] == 'LISTEN': continue
|
||
# if not conn['local_port'] in project_info['listen']:
|
||
# project_info['listen'].append(conn['local_port'])
|
||
# if project_info['listen']:
|
||
# project_info['listen_ok'] = project_info['project_config']['port'] in project_info['listen']
|
||
return project_info
|
||
|
||
# 获取指定项目的域名列表
|
||
def project_get_domain(self, get):
|
||
'''
|
||
@name 获取指定项目的域名列表
|
||
@author sww
|
||
@param get<dict_obj>{
|
||
sitename: string<项目名称>
|
||
}
|
||
@return dict
|
||
'''
|
||
try:
|
||
project_id = public.M('sites').where('name=?', (get.sitename,)).getField('id')
|
||
domains = public.M('domain').where('pid=?', (project_id,)).order('id desc').select()
|
||
# project_find = self.get_project_find(get.sitename)
|
||
# if len(domains) != len(project_find['project_config']['domains']):
|
||
# public.M('domain').where('pid=?', (project_id,)).delete()
|
||
# if not project_find: return []
|
||
# for d in project_find['project_config']['domains']:
|
||
# domain = {}
|
||
# arr = d.split(':')
|
||
# if len(arr) < 2: arr.append(80)
|
||
# domain['name'] = arr[0]
|
||
# domain['port'] = int(arr[1])
|
||
# domain['pid'] = project_id
|
||
# domain['addtime'] = public.getDate()
|
||
# public.M('domain').insert(domain)
|
||
# if project_find['project_config']['domains']:
|
||
# domains = public.M('domain').where('pid=?', (project_id,)).select()
|
||
return public.returnResult(True, data=domains)
|
||
except:
|
||
return public.returnResult(False, traceback.format_exc())
|
||
|
||
# 获取指定项目配置
|
||
def get_project_find(self, sitename):
|
||
'''
|
||
@name 获取指定项目配置
|
||
@author sww
|
||
@param sitename<string> 项目名称
|
||
@return dict
|
||
'''
|
||
project_info = public.M('sites').where('project_type=? AND name=?', ('PHP', sitename)).find()
|
||
if not project_info: return False
|
||
project_info['project_config'] = json.loads(project_info['project_config'])
|
||
if 'run_user' not in project_info['project_config'].keys():
|
||
project_info['run_user'] = 'www'
|
||
return project_info
|
||
|
||
# 获取项目SSL信息
|
||
def get_ssl_end_date(self, sitename):
|
||
'''
|
||
@name 获取SSL信息
|
||
@author sww
|
||
@param sitename <string> 项目名称
|
||
@return dict
|
||
'''
|
||
import data
|
||
return data.data().get_site_ssl_info(sitename)
|
||
|
||
# 删除指定项目中的域名
|
||
def project_remove_domain(self, get):
|
||
'''
|
||
@name 为指定项目删除域名
|
||
@author sww
|
||
@param get<dict_obj>{
|
||
sitename: string<项目名称>
|
||
domain: string<域名>
|
||
}
|
||
@return dict
|
||
'''
|
||
try:
|
||
project_find = self.get_project_find(get.sitename)
|
||
if not project_find:
|
||
return public.returnResult(False, 'The specified item does not exist')
|
||
result = []
|
||
domain_list = json.loads(get.domain)
|
||
project_id = public.M('sites').where('name=?', (get.sitename,)).getField('id')
|
||
for domain in domain_list:
|
||
if public.M('domain').where('pid=?', (project_id,)).count() == 1:
|
||
result.append({'name': domain, 'msg': "项目中至少需要一个域名", "status": False})
|
||
continue
|
||
domain_id = public.M('domain').where('name=? AND pid=?', (domain, project_id)).getField('id')
|
||
if not domain_id:
|
||
return public.returnResult(False, '指定域名不存在')
|
||
public.M('domain').where('id=?', (domain_id,)).delete()
|
||
public.WriteLog(self._log_name, '从项目:{},删除域名{}'.format(get.sitename, get.domain))
|
||
result.append({'name': domain, 'msg': "删除成功", "status": True})
|
||
domain_list = public.M('domain').where('pid=?', (project_id)).select()
|
||
domain_list = [(domain['name'], str(domain['port'])) for domain in domain_list]
|
||
NginxDomainTool().nginx_set_domain(get.sitename, *domain_list)
|
||
return public.returnResult(True, data=result)
|
||
except:
|
||
return public.returnResult(False, traceback.format_exc())
|
||
|
||
# 为指定项目添加域名
|
||
def project_add_domain(self, get):
|
||
'''
|
||
@name 为指定项目添加域名
|
||
@author sww
|
||
@param get<dict_obj>{
|
||
sitename: string<项目名称>
|
||
domains: list<域名列表>
|
||
}
|
||
@return dict
|
||
'''
|
||
try:
|
||
project_find = self.get_project_find(get.sitename)
|
||
if not project_find:
|
||
return public.returnResult(False, 'The specified item does not exist')
|
||
project_id = project_find['id']
|
||
domains = get.domains
|
||
if not isinstance(domains, list):
|
||
domains = json.loads(domains)
|
||
flag = False
|
||
res_domains = []
|
||
for domain in domains:
|
||
domain = domain.strip()
|
||
if not domain: continue
|
||
if not self.check_domain(domain):
|
||
res_domains.append({"name": domain, "status": False, "msg": '域名格式错误'})
|
||
continue
|
||
domain_arr = domain.split(':')
|
||
domain_arr[0] = check_domain(domain_arr[0])
|
||
domain_arr[0] = self.ToPunycode(domain_arr[0])
|
||
if domain_arr[0] is False:
|
||
res_domains.append({"name": domain, "status": False, "msg": '域名格式错误'})
|
||
continue
|
||
if len(domain_arr) == 1:
|
||
domain_arr.append("")
|
||
if domain_arr[1] == "":
|
||
domain_arr[1] = 80
|
||
domain += ':80'
|
||
try:
|
||
if not (0 < int(domain_arr[1]) < 65535):
|
||
res_domains.append({"name": domain, "status": False, "msg": '域名格式错误'})
|
||
continue
|
||
except ValueError:
|
||
res_domains.append({"name": domain, "status": False, "msg": '域名格式错误'})
|
||
continue
|
||
if not public.M('domain').where('name=?', (domain_arr[0],)).count():
|
||
public.M('domain').add('name,pid,port,addtime',
|
||
(domain_arr[0], project_id, domain_arr[1], public.getDate()))
|
||
if not domain in project_find['project_config']['domains']:
|
||
project_find['project_config']['domains'].append(domain)
|
||
public.WriteLog(self._log_name, '成功添加域名{}到项目{}'.format(domain, get.sitename))
|
||
res_domains.append({"name": domain_arr[0], "status": True, "msg": '添加成功'})
|
||
flag = True
|
||
else:
|
||
public.WriteLog(self._log_name, '添加域名错误,域名{}已存在'.format(domain))
|
||
res_domains.append({"name": domain_arr[0], "status": False, "msg": '添加失败,域名{}已存在'.format(domain)})
|
||
if flag:
|
||
public.M('sites').where('id=?', (project_id,)).save('project_config', json.dumps(project_find['project_config']))
|
||
domain_list = public.M('domain').where('pid=?', (project_id)).select()
|
||
domain_list = [(domain['name'], str(domain['port'])) for domain in domain_list]
|
||
NginxDomainTool().nginx_set_domain(get.sitename, *domain_list)
|
||
return self._ckeck_add_domain(get.sitename, res_domains)
|
||
except:
|
||
return public.returnResult(False, traceback.format_exc())
|
||
|
||
def _ckeck_add_domain(self, site_name, domains):
|
||
from panelSite import panelSite
|
||
ssl_data = panelSite().GetSSL(type("get", tuple(), {"siteName": site_name})())
|
||
if not ssl_data["status"]: return public.returnResult(True, data={"domains": domains}, msg="添加成功")
|
||
domain_rep = []
|
||
for i in ssl_data["cert_data"]["dns"]:
|
||
if i.startswith("*"):
|
||
_rep = r"^[^\.]+\." + i[2:].replace(".", r"\.")
|
||
else:
|
||
_rep = "^" + i.replace(".", r"\.")
|
||
domain_rep.append(_rep)
|
||
no_ssl = []
|
||
for domain in domains:
|
||
if not domain["status"]: continue
|
||
for _rep in domain_rep:
|
||
if re.search(_rep, domain["name"]):
|
||
break
|
||
else:
|
||
no_ssl.append(domain["name"])
|
||
if no_ssl:
|
||
return public.returnResult(True, data={
|
||
"domains": domains,
|
||
"not_ssl": no_ssl,
|
||
"tip": "本站点已启用SSL证书,但本次添加的域名:{},无法匹配当前证书,如有需求,请重新申请证书。".format(str(no_ssl))
|
||
}, msg="添加成功")
|
||
return public.returnResult(True, data={"domains": domains}, msg="添加成功")
|
||
|
||
# 获取项目状态
|
||
def get_project_run_state(self, get):
|
||
'''
|
||
@name 获取项目运行状态信息
|
||
@param sitename<string> 项目名称
|
||
@return dict
|
||
'''
|
||
try:
|
||
sitename = get.sitename.strip()
|
||
result = {}
|
||
project_info = self.get_project_find(sitename)
|
||
res = RealServer().daemon_status(sitename)
|
||
result['status'] = res['status']
|
||
result['sitename'] = sitename
|
||
result['is_power_on'] = int(project_info["project_config"]['is_power_on'])
|
||
result['project_path'] = project_info['path'] # type : str
|
||
result['project_cmd'] = project_info["project_config"].get('start_cmd', '')
|
||
result['project_port'] = project_info['project_config']['port']
|
||
if result['project_path'][-1] != "/":
|
||
result['project_path'] += "/"
|
||
result['site_run_path'] = DirTool().get_site_run_path(sitename).replace(result['project_path'].rstrip("/"), "")
|
||
if not result['site_run_path']:
|
||
result['site_run_path'] = "/"
|
||
result['php_version'] = project_info['project_config']['php_version']
|
||
result['run_user'] = project_info['project_config'].get('run_user', 'www')
|
||
result['ps'] = project_info['ps']
|
||
result['pid'] = RealServer().get_daemon_pid(sitename)["data"]
|
||
if result['pid'] in [0, '0', ''] and result['status']:
|
||
res = public.ExecShell("""ps -aux |grep "{}"|grep -v "grep"|awk '{{print $2}}'""".format(result['project_cmd']))[0]
|
||
res = res.strip().split("\n")
|
||
if res and len(res) >= 1:
|
||
res.sort()
|
||
result['pid'] = res[0]
|
||
result['Listen'] = []
|
||
if result['pid']:
|
||
children = RealProcess().get_process_tree(result['pid'])["data"]
|
||
for chi in children:
|
||
for i in chi.get("connections", []):
|
||
port = i.get("local_port", '')
|
||
addr = i.get("local_addr", '')
|
||
status = i.get("status", 'dasd')
|
||
if addr == '0.0.0.0':
|
||
addr = public.GetLocalIp()
|
||
if port and addr and status.lower() == 'listen':
|
||
result['Listen'].append((addr, port))
|
||
result['Listen'] = list(set(result['Listen']))
|
||
|
||
return public.returnResult(True, data=result)
|
||
except:
|
||
public.print_log(traceback.format_exc())
|
||
return public.returnResult(False, '获取项目状态失败')
|
||
|
||
def domain_to_puny_code(self, domain: str) -> str:
|
||
new_domain = ''
|
||
for dkey in domain.split('.'):
|
||
if dkey == '*' or dkey == "":
|
||
continue
|
||
# 匹配非ascii字符
|
||
match = re.search(u"[\x80-\xff]+", dkey)
|
||
if not match:
|
||
match = re.search(u"[\u4e00-\u9fa5]+", dkey)
|
||
if not match:
|
||
new_domain += dkey + '.'
|
||
else:
|
||
new_domain += 'xn--' + dkey.encode('punycode').decode('utf-8') + '.'
|
||
if domain.startswith('*.'):
|
||
new_domain = "*." + new_domain
|
||
return new_domain[:-1]
|
||
|
||
def check_domain(self, domain: str):
|
||
domain = self.domain_to_puny_code(domain)
|
||
# 判断通配符域名格式
|
||
if domain.find('*') != -1 and domain.find('*.') == -1:
|
||
return None
|
||
|
||
# 判断域名格式
|
||
rep_domain = re.compile(r"^([\w\-*]{1,100}\.){1,24}([\w\-]{1,24}|[\w\-]{1,24}\.[\w\-]{1,24})$")
|
||
if not rep_domain.match(domain):
|
||
return None
|
||
return domain
|
||
|
||
# 修改项目运行状态
|
||
def modify_project_run_state(self, get):
|
||
sitename = get.sitename
|
||
project_action = get.project_action
|
||
logs_path = "/www/server/phpa_project/logs"
|
||
if not os.path.exists(logs_path):
|
||
public.ExecShell('mkdir -p {}'.format(logs_path))
|
||
public.ExecShell('chmod 777 -R {}'.format(logs_path))
|
||
if project_action in ['start', 'restart']:
|
||
self.async_dependence_config(sitename)
|
||
res = RealServer().daemon_admin(sitename, project_action)
|
||
if res['status'] and project_action in ['start', 'restart']:
|
||
public.M('sites').where('name=? and project_type=?', (sitename, 'PHP')).setField('status', 1)
|
||
elif res['status'] or project_action == 'stop':
|
||
public.M('sites').where('name=? and project_type=?', (sitename, 'PHP')).setField('status', 0)
|
||
# if project_action in ['start', 'restart'] and not res['status']:
|
||
# log = os.path.join(self._phpa_logs, sitename + '.log')
|
||
# logs = public.GetNumLines(log, 5)
|
||
# res['msg'] += '\n' + logs
|
||
# res['msg'] += '\n 详情前往日志-项目日志查看'
|
||
|
||
if project_action in ['start', 'restart']:
|
||
site_info = self.get_project_find(sitename)
|
||
for i in range(20):
|
||
time.sleep(0.1)
|
||
conn = public.ExecShell('systemctl status {}'.format(sitename))
|
||
public.print_log(conn)
|
||
if 'deactivating' in conn[0]:
|
||
# 开启forking模式
|
||
sys_conf = '/usr/lib/systemd/system/{}.service'.format(sitename)
|
||
sys_conf = public.readFile(sys_conf)
|
||
public.print_log(sys_conf)
|
||
if 'Type=forking' not in sys_conf:
|
||
sys_conf = sys_conf.replace('Type=simple', 'Type=forking')
|
||
public.writeFile('/usr/lib/systemd/system/{}.service'.format(sitename), sys_conf)
|
||
public.ExecShell('systemctl daemon-reload')
|
||
public.ExecShell('systemctl restart {}'.format(sitename))
|
||
|
||
pid = RealServer().get_daemon_pid(sitename)["data"]
|
||
if pid in [0, '0', '']:
|
||
res = public.ExecShell("""ps -aux |grep "{}"|grep -v "grep"|awk '{{print $2}}'""".format(site_info['project_config']['project_cmd']))[0]
|
||
res = res.strip().split("\n")
|
||
if res and len(res) >= 1:
|
||
res.sort()
|
||
pid = res[0]
|
||
if pid not in [0, '0', '']:
|
||
pids = psutil.Process(int(pid)).children(recursive=True)
|
||
pids = [str(i.pid) for i in pids]
|
||
pids.append(str(pid))
|
||
if pid not in [0, '0']:
|
||
res = public.ExecShell('lsof -i |grep -E "{}" |grep LISTEN'.format('|'.join(pids)))
|
||
if res[0]:
|
||
return public.returnResult(True, '启动成功')
|
||
return public.returnResult(False, '启动失败')
|
||
return public.returnResult(True, '关闭成功')
|
||
|
||
def async_dependence_config(self, sitename):
|
||
"""
|
||
增加安装依赖的配置文件
|
||
"""
|
||
try:
|
||
config = self.get_project_find(sitename)
|
||
# public.ExecShell("chown -R {}:{} {}".format(config['project_config'].run_user.strip(), config['project_config'].run_user.strip(), get.site_path))
|
||
# public.ExecShell("chmod -R 755 {}".format(config['path']))
|
||
php_version = config['project_config']['php_version']
|
||
php_ini_path = os.path.join(config['path'], 'php-cli.ini')
|
||
public.ExecShell('cp /www/server/php/{}/etc/php-cli.ini {}'.format(php_version, config['path']))
|
||
public.ExecShell('chown root:root {}'.format(php_ini_path))
|
||
public.ExecShell('chmod 644 {}'.format(php_ini_path))
|
||
public.ExecShell("sed -i '/disable_functions/d' {}".format(php_ini_path))
|
||
public.writeFile(php_ini_path, php_cli_ini)
|
||
|
||
except:
|
||
pass
|
||
|
||
# 修改项目网站运行目录
|
||
def modify_project_path(self, get):
|
||
if not hasattr(get, 'new_run_path_sub'):
|
||
return public.returnResult(False, '新的运行目录不能为空')
|
||
new_run_path_sub = get.new_run_path_sub
|
||
sitename = get.sitename.strip()
|
||
project_info = self.get_project_find(sitename)
|
||
DirTool().modify_site_run_path(sitename, project_info['path'], new_run_path_sub)
|
||
return public.returnResult(True, '修改成功')
|
||
|
||
def modify_project(self, get):
|
||
try:
|
||
if not hasattr(get, 'sitename'):
|
||
return public.returnResult(False, 'The project name cannot be empty')
|
||
if not hasattr(get, 'project_cmd'):
|
||
return public.returnResult(False, 'project_cmd不能为空')
|
||
if not hasattr(get, 'project_path'):
|
||
return public.returnResult(False, 'project_path不能为空')
|
||
if not hasattr(get, 'site_run_path'):
|
||
return public.returnResult(False, 'site_run_path不能为空')
|
||
if not hasattr(get, 'run_user'):
|
||
get.run_user = 'www'
|
||
|
||
public.ExecShell('chown -R {}:{} {}'.format(get.run_user, get.run_user, get.project_path))
|
||
public.ExecShell('chmod 755 {}'.format(get.project_path))
|
||
config = self.get_project_find(get.sitename)
|
||
config['project_config']['run_user'] = get.run_user
|
||
start_cmd = get.project_cmd
|
||
if 'php' == get.project_cmd[:3]:
|
||
get.project_cmd = '/www/server/php/{}/bin/php -c {}/php-cli.ini {}'.format(get.php_version, get.project_path, get.project_cmd[3:])
|
||
else:
|
||
get.project_cmd = '/www/server/php/{}/bin/php -c {}/php-cli.ini {}'.format(get.php_version, get.project_path, get.project_cmd)
|
||
if config['project_config'].get('start_cmd') != start_cmd:
|
||
config['project_config']['start_cmd'] = start_cmd
|
||
if config['path'] != get.project_path:
|
||
if not os.path.exists(get.project_path):
|
||
return public.returnResult(False, '项目目录不存在')
|
||
config['path'] = get.project_path
|
||
config['project_config']['project_path'] = get.project_path
|
||
|
||
# get.site_run_path = os.path.join(config['path'], get.site_run_path.lstrip("/"))
|
||
config['project_config']['site_run_path'] = get.site_run_path.rstrip('/')
|
||
get.new_run_path_sub = config['project_config']['site_run_path']
|
||
self.modify_project_path(get)
|
||
if config['project_config']['php_version'] != get.php_version:
|
||
config['project_config']['php_version'] = get.php_version
|
||
public.ExecShell('cp /www/server/php/{}/etc/php-cli.ini {}'.format(get.php_version, config['path']))
|
||
public.ExecShell('chown root:root {}'.format(os.path.join(config['path'], 'php-cli.ini')))
|
||
public.ExecShell('chmod 644 {}'.format(os.path.join(config['path'], 'php-cli.ini')))
|
||
public.ExecShell("sed -i '/disable_functions/d' {}".format(os.path.join(config['path'], 'php-cli.ini')))
|
||
if config['project_config']['project_cmd'] != get.project_cmd or config['project_config']['is_power_on'] != get.get('is_power_on', 1) or config['project_config'][
|
||
'run_user'] != get.run_user:
|
||
config['project_config']['is_power_on'] = get.get('is_power_on', 1)
|
||
config['project_config']['project_cmd'] = get.project_cmd
|
||
systemd_conf = '/usr/lib/systemd/system/{}.service'.format(get.sitename)
|
||
conf = public.readFile(systemd_conf)
|
||
is_fork = 0
|
||
if conf:
|
||
if 'Type=forking' in conf:
|
||
is_fork = 1
|
||
realserver = RealServer()
|
||
realserver.create_daemon(get.sitename, '', get.project_cmd, get.project_path, config['project_config'].get('run_user', 'www'), get.is_power_on,
|
||
logs_file=os.path.join(self._phpa_logs, get.sitename + '.log'), is_fork=is_fork)
|
||
if config['ps'] != get.get('ps', '') and get.get('ps', ''):
|
||
public.M('sites').where('name=? and project_type=?', (get.sitename, 'PHP')).setField('ps', get.get('ps', ''))
|
||
config['project_config']['port'] = get.get('project_port', 0)
|
||
public.M('sites').where('name=? and project_type=?', (get.sitename, 'PHP')).setField('project_config', json.dumps(config['project_config']))
|
||
return public.returnResult(True, '修改成功')
|
||
except:
|
||
return public.returnResult(False, traceback.format_exc())
|
||
|
||
def get_project_log(self, get):
|
||
log_file = self._phpa_logs + '/' + get.sitename + '.log'
|
||
if not os.path.exists(log_file):
|
||
return public.returnResult(status=True, msg='暂无项目日志')
|
||
return public.returnResult(True, msg=public.GetNumLines(log_file, 1000))
|
||
|
||
def get_access_log(self, get):
|
||
from logsModel.siteModel import main as siteModel
|
||
get.sitename = get.siteName.strip()
|
||
sitelog = siteModel()
|
||
return public.returnResult(True, data=sitelog.get_site_access_logs(get)['msg'])
|
||
|
||
def get_error_log(self, get):
|
||
from logsModel.siteModel import main as siteModel
|
||
get.sitename = get.siteName.strip()
|
||
sitelog = siteModel()
|
||
return public.returnResult(True, data=sitelog.get_site_error_logs(get)['msg'])
|
||
|
||
def get_config_file(self, get):
|
||
result = {}
|
||
site_name = get.sitename
|
||
conf = self.get_project_find(site_name)
|
||
result['nginx配置文件'] = self.setupPath + '/panel/vhost/nginx/' + site_name + '.conf'
|
||
result['php-cli配置文件'] = os.path.join(conf['path'], 'php-cli.ini')
|
||
env_path = os.path.join(conf['path'], '.env')
|
||
if os.path.exists(env_path):
|
||
result['.env配置文件'] = env_path
|
||
result['伪静态配置文件'] = "/www/server/panel/vhost/rewrite/{}.conf".format(site_name)
|
||
composer_path = os.path.join(conf['path'], 'composer.json')
|
||
if os.path.exists(composer_path):
|
||
result['composer配置文件'] = composer_path
|
||
return public.returnResult(True, data=result)
|
||
|
||
# 上传版本
|
||
def upload_version(self, get):
|
||
"""
|
||
上传压缩包并存储为版本
|
||
"""
|
||
if not hasattr(get, 'sitename'):
|
||
return public.returnResult(False, 'The project name cannot be empty')
|
||
if not hasattr(get, 'version'):
|
||
return public.returnResult(False, '版本号不能为空')
|
||
if not hasattr(get, 'ps'):
|
||
get.ps = ''
|
||
try:
|
||
upload_files = os.path.join("/tmp", get.f_name)
|
||
from files import files
|
||
fileObj = files()
|
||
ff = fileObj.upload(get)
|
||
if type(ff) == int:
|
||
return ff
|
||
if not ff['status']:
|
||
return public.returnResult(False, ff['msg'])
|
||
output_dir = str(os.path.join('/tmp', public.GetRandomString(16)))
|
||
os.makedirs(output_dir, 777)
|
||
if not self.extract_archive(upload_files, output_dir)[0]:
|
||
return public.returnResult(False, '解压失败,仅支持zip,tar.gz,tar,bz2,gz,xz格式的压缩包')
|
||
if len(os.listdir(output_dir)) == 1:
|
||
output_dir = str(os.path.join(output_dir, os.listdir(output_dir)[0]))
|
||
versiontool = VersionTool()
|
||
res = versiontool.publish_by_src_path(get.sitename, output_dir, get.version, get.ps, sync=True)
|
||
public.ExecShell('rm -rf {}'.format(output_dir))
|
||
public.ExecShell('rm -rf {}'.format(upload_files))
|
||
if res is None:
|
||
return public.returnResult(True, '添加成功')
|
||
return public.returnResult(False, '添加失败' + res)
|
||
except:
|
||
return public.returnResult(False, traceback.format_exc())
|
||
|
||
def extract_archive(self, file_path, output_dir):
|
||
name = os.path.basename(file_path)
|
||
if name.endswith('.tar.gz'):
|
||
with tarfile.open(file_path, 'r:gz') as tar:
|
||
tar.extractall(output_dir)
|
||
elif name.endswith('.zip'):
|
||
with zipfile.ZipFile(file_path, 'r') as zip_ref:
|
||
zip_ref.extractall(output_dir)
|
||
elif name.endswith('.gz'):
|
||
with gzip.open(file_path, 'rb') as f_in, open(os.path.join(output_dir, name[:-3]), 'wb') as f_out:
|
||
shutil.copyfileobj(f_in, f_out)
|
||
elif name.endswith('.tar'):
|
||
with tarfile.open(file_path, 'r') as tar:
|
||
tar.extractall(output_dir)
|
||
elif name.endswith('.bz2'):
|
||
with open(file_path, 'rb') as f_in, open(os.path.join(output_dir, name[:-4]), 'wb') as f_out:
|
||
with bz2.BZ2File(f_in) as bz:
|
||
shutil.copyfileobj(bz, f_out)
|
||
# elif name.endswith('.xz'):
|
||
# with lzma.open(file_path, 'rb') as f_in, open(os.path.join(output_dir, name[:-3]), 'wb') as f_out:
|
||
# shutil.copyfileobj(f_in, f_out)
|
||
else:
|
||
return False, '文件格式错误.'
|
||
|
||
return True, '解压成功'
|
||
|
||
# 获取列表
|
||
def get_version_list(self, get):
|
||
if not hasattr(get, 'sitename'):
|
||
return public.returnResult(False, 'The project name cannot be empty')
|
||
versiontool = VersionTool()
|
||
return public.returnResult(True, data=versiontool.version_list(get.sitename))
|
||
|
||
# 删除版本
|
||
def remove_version(self, get):
|
||
if not hasattr(get, 'sitename'):
|
||
return public.returnResult(False, 'The project name cannot be empty')
|
||
if not hasattr(get, 'version'):
|
||
return public.returnResult(False, '版本号不能为空')
|
||
versiontool = VersionTool()
|
||
if versiontool.remove(get.sitename, get.version) is None:
|
||
return public.returnResult(True, '删除成功')
|
||
return public.returnResult(False, '删除失败')
|
||
|
||
# 恢复版本
|
||
def recover_version(self, get):
|
||
try:
|
||
if not hasattr(get, 'sitename'):
|
||
return public.returnResult(False, 'The project name cannot be empty')
|
||
if not hasattr(get, 'version'):
|
||
return publihuoc.returnResult(False, '版本号不能为空')
|
||
conf = self.get_project_find(get.sitename)
|
||
versiontool = VersionTool()
|
||
res = versiontool.recover(get.sitename, get.version, conf['path'], DirTool().get_site_run_path(get.sitename))
|
||
if res is not True:
|
||
return public.returnResult(False, res)
|
||
return public.returnResult(True, '恢复成功')
|
||
except:
|
||
return public.returnResult(False, traceback.format_exc())
|
||
|
||
def now_file_backup(self, get):
|
||
try:
|
||
if not hasattr(get, 'sitename'):
|
||
return public.returnResult(False, 'The project name cannot be empty')
|
||
if not hasattr(get, 'version'):
|
||
return public.returnResult(False, '版本号不能为空')
|
||
if not hasattr(get, 'ps'):
|
||
get.ps = ''
|
||
config = self.get_project_find(get.sitename)
|
||
path = config['path']
|
||
versiontool = VersionTool()
|
||
res = versiontool.publish_by_src_path(get.sitename, path, get.version, get.ps, sync=True)
|
||
if res is None:
|
||
return public.returnResult(True, '添加成功')
|
||
return public.returnResult(False, '添加失败' + res)
|
||
except:
|
||
return public.returnResult(False, traceback.format_exc())
|
||
|
||
def set_version_ps(self, get):
|
||
if not hasattr(get, 'sitename'):
|
||
return public.returnResult(False, 'The project name cannot be empty')
|
||
if not hasattr(get, 'version'):
|
||
return public.returnResult(False, '版本号不能为空')
|
||
if not hasattr(get, 'ps'):
|
||
get.ps = ''
|
||
versiontool = VersionTool()
|
||
versiontool.set_ps(get.sitename, get.version, get.ps)
|
||
return public.returnResult(True, '设置成功')
|
||
|
||
def get_setup_log(self, get):
|
||
log_file = "/tmp/{}_install_dependence.log".format(get.sitename)
|
||
if not os.path.exists(log_file):
|
||
return public.returnResult(True, data='暂无项目日志')
|
||
return public.returnResult(True, data=public.GetNumLines(log_file, 1000))
|
||
|
||
def add_crontab(self, get):
|
||
try:
|
||
if not hasattr(get, 'sitename'):
|
||
return public.returnResult(False, 'The project name cannot be empty')
|
||
if not hasattr(get, 'cron_name'):
|
||
return public.returnResult(False, 'The timed task name cannot be empty')
|
||
if not hasattr(get, 'sBody'):
|
||
return public.returnResult(False, '定时任务内容不能为空')
|
||
args = {
|
||
"name": get.cron_name,
|
||
"type": get.type,
|
||
"where1": get.where1,
|
||
"hour": get.hour,
|
||
"minute": get.minute,
|
||
"week": get.week,
|
||
"sType": "toShell",
|
||
"sName": "",
|
||
"backupTo": "localhost",
|
||
"save": get.sitename,
|
||
"sBody": get.sBody,
|
||
"urladdress": "",
|
||
"flock": get.flock
|
||
}
|
||
import crontab
|
||
res = crontab.crontab().AddCrontab(public.to_dict_obj(args))
|
||
if res['status']:
|
||
return public.returnResult(True, msg='添加成功!')
|
||
return public.returnResult(False, res['msg'])
|
||
except:
|
||
return public.returnResult(False, traceback.format_exc())
|
||
|
||
def get_crontab_list(self, get):
|
||
try:
|
||
if not hasattr(get, 'sitename'):
|
||
return public.returnResult(False, 'The project name cannot be empty')
|
||
cront = public.M('crontab').where('save=?', (get.sitename,)).select()
|
||
|
||
data = []
|
||
for i in range(len(cront)):
|
||
tmp = cront[i]
|
||
|
||
if cront[i]['type'] == "day":
|
||
tmp['type_zh'] = public.getMsg('CRONTAB_TODAY')
|
||
tmp['cycle'] = public.getMsg('CRONTAB_TODAY_CYCLE',
|
||
(str(cront[i]['where_hour']), str(cront[i]['where_minute'])))
|
||
elif cront[i]['type'] == "day-n":
|
||
tmp['type_zh'] = public.getMsg('CRONTAB_N_TODAY', (str(cront[i]['where1']),))
|
||
tmp['cycle'] = public.getMsg('CRONTAB_N_TODAY_CYCLE', (
|
||
str(cront[i]['where1']), str(cront[i]['where_hour']), str(cront[i]['where_minute'])))
|
||
elif cront[i]['type'] == "hour":
|
||
tmp['type_zh'] = public.getMsg('CRONTAB_HOUR')
|
||
tmp['cycle'] = public.getMsg('CRONTAB_HOUR_CYCLE', (str(cront[i]['where_minute']),))
|
||
elif cront[i]['type'] == "hour-n":
|
||
tmp['type_zh'] = public.getMsg('CRONTAB_N_HOUR', (str(cront[i]['where1']),))
|
||
tmp['cycle'] = public.getMsg('CRONTAB_N_HOUR_CYCLE',
|
||
(str(cront[i]['where1']), str(cront[i]['where_minute'])))
|
||
elif cront[i]['type'] == "minute-n":
|
||
tmp['type_zh'] = public.getMsg('CRONTAB_N_MINUTE', (str(cront[i]['where1']),))
|
||
tmp['cycle'] = public.getMsg('CRONTAB_N_MINUTE_CYCLE', (str(cront[i]['where1']),))
|
||
elif cront[i]['type'] == "week":
|
||
tmp['type_zh'] = public.getMsg('CRONTAB_WEEK')
|
||
if not cront[i]['where1']: cront[i]['where1'] = '0'
|
||
tmp['cycle'] = public.getMsg('CRONTAB_WEEK_CYCLE', (
|
||
self.toWeek(int(cront[i]['where1'])), str(cront[i]['where_hour']),
|
||
str(cront[i]['where_minute'])))
|
||
elif cront[i]['type'] == "month":
|
||
tmp['type_zh'] = public.getMsg('CRONTAB_MONTH')
|
||
tmp['cycle'] = public.getMsg('CRONTAB_MONTH_CYCLE', (
|
||
str(cront[i]['where1']), str(cront[i]['where_hour']), str(cront[i]['where_minute'])))
|
||
|
||
log_file = '/www/server/cron/{}.log'.format(tmp['echo'])
|
||
if os.path.exists(log_file):
|
||
tmp['addtime'] = self.get_last_exec_time(log_file)
|
||
data.append(tmp)
|
||
|
||
for i in data:
|
||
if i['backup_mode'] == "1":
|
||
i['backup_mode'] = 1
|
||
else:
|
||
i['backup_mode'] = 0
|
||
if i['db_backup_path'] == "":
|
||
i['db_backup_path'] = "/www/backup"
|
||
|
||
if not i.get('rname', ''):
|
||
i['rname'] = i['name']
|
||
if i['time_type'] == 'sweek':
|
||
i['type'] = 'sweek'
|
||
week_str = self.toweek(i['time_set'])
|
||
if week_str: # 检查week_str是否为空
|
||
i['type_zh'] = week_str
|
||
i['cycle'] = "每" + week_str + i['special_time'] + "执行"
|
||
|
||
elif i['time_type'] == 'sday':
|
||
i['type'] = 'sweek'
|
||
i['type_zh'] = i['special_time']
|
||
i['cycle'] = "每天" + i['special_time'] + "执行"
|
||
|
||
elif i['time_type'] == 'smonth':
|
||
i['type'] = 'sweek'
|
||
i['type_zh'] = i['special_time']
|
||
i['cycle'] = "每月" + i['time_set'] + "号" + i['special_time'] + "执行"
|
||
if i['sType'] == 'site_restart':
|
||
i['cycle'] = "每天" + i['special_time'] + "执行"
|
||
return public.returnResult(True, data=data)
|
||
except:
|
||
return public.returnResult(False, traceback.format_exc())
|
||
|
||
# 转换大写星期
|
||
def toWeek(self, num):
|
||
wheres = {
|
||
0: public.getMsg('CRONTAB_SUNDAY'),
|
||
1: public.getMsg('CRONTAB_MONDAY'),
|
||
2: public.getMsg('CRONTAB_TUESDAY'),
|
||
3: public.getMsg('CRONTAB_WEDNESDAY'),
|
||
4: public.getMsg('CRONTAB_THURSDAY'),
|
||
5: public.getMsg('CRONTAB_FRIDAY'),
|
||
6: public.getMsg('CRONTAB_SATURDAY')
|
||
}
|
||
try:
|
||
return wheres[num]
|
||
except:
|
||
return ''
|
||
|
||
def get_last_exec_time(self, log_file):
|
||
'''
|
||
@name 获取上次执行时间
|
||
@author hwliang
|
||
@param log_file<string> 日志文件路径
|
||
@return format_date
|
||
'''
|
||
exec_date = ''
|
||
try:
|
||
log_body = public.GetNumLines(log_file, 20)
|
||
if log_body:
|
||
log_arr = log_body.split('\n')
|
||
date_list = []
|
||
for i in log_arr:
|
||
if i.find('★') != -1 and i.find('[') != -1 and i.find(']') != -1:
|
||
date_list.append(i)
|
||
if date_list:
|
||
exec_date = date_list[-1].split(']')[0].split('[')[1]
|
||
except:
|
||
pass
|
||
|
||
finally:
|
||
if not exec_date:
|
||
exec_date = public.format_date(times=int(os.path.getmtime(log_file)))
|
||
return exec_date
|
||
|
||
def start_task(self, get):
|
||
try:
|
||
if not hasattr(get, 'id'):
|
||
return public.returnResult(False, 'ID不能为空')
|
||
import crontab
|
||
res = crontab.crontab().StartTask(get)
|
||
if res['status']:
|
||
return public.returnResult(True, '启动成功')
|
||
return public.returnResult(False, res['msg'])
|
||
except:
|
||
return public.returnResult(False, traceback.format_exc())
|
||
|
||
def modify_crontab_status(self, get):
|
||
try:
|
||
if not hasattr(get, 'id'):
|
||
return public.returnResult(False, 'ID不能为空')
|
||
if hasattr(get, 'status'):
|
||
cronInfo = public.M('crontab').where('id=?', (get.id,)).select()[0]
|
||
if int(cronInfo['status']) == int(get.status):
|
||
return public.returnResult(True, '修改成功')
|
||
get.if_stop = False
|
||
import crontab
|
||
res = crontab.crontab().set_cron_status(get)
|
||
if res['status']:
|
||
return public.returnResult(True, '修改成功')
|
||
return public.returnResult(False, res['msg'])
|
||
except:
|
||
return public.returnResult(False, traceback.format_exc())
|
||
|
||
def remove_crontab(self, get):
|
||
try:
|
||
if not hasattr(get, 'id'):
|
||
return public.returnResult(False, 'ID不能为空')
|
||
import crontab
|
||
res = crontab.crontab().DelCrontab(get)
|
||
if res['status']:
|
||
return public.returnResult(True, '删除成功')
|
||
return public.returnResult(False, res['msg'])
|
||
except:
|
||
return public.returnResult(False, traceback.format_exc())
|
||
|
||
def modify_crontab(self, get):
|
||
try:
|
||
if not hasattr(get, 'id'):
|
||
return public.returnResult(False, 'ID不能为空')
|
||
self.remove_crontab(get)
|
||
res = self.add_crontab(get)
|
||
res['msg'] = res['msg'].replace('添加', '修改')
|
||
return res
|
||
except:
|
||
return public.returnResult(False, traceback.format_exc())
|
||
|
||
def get_crontab_log(self, get):
|
||
try:
|
||
if not hasattr(get, 'id'):
|
||
return public.returnResult(False, 'ID不能为空')
|
||
import crontab
|
||
res = crontab.crontab().GetLogs(get)
|
||
return public.returnResult(True, res['msg'])
|
||
except:
|
||
return public.returnResult(False, traceback.format_exc())
|
||
|
||
def clearn_logs(self, get):
|
||
try:
|
||
if not hasattr(get, 'id'):
|
||
return public.returnResult(False, 'The project name cannot be empty')
|
||
import crontab
|
||
crontab.crontab().DelLogs(get)
|
||
return public.returnResult(True, '清空成功')
|
||
except:
|
||
return public.returnResult(False, traceback.format_exc())
|
||
|
||
def get_group_file(self):
|
||
config = json.loads(public.readFile(self.group_file))
|
||
return config
|
||
|
||
def get_group_list(self, get):
|
||
try:
|
||
config = self.get_group_file()
|
||
result = []
|
||
for i, j in config.items():
|
||
tmp = []
|
||
for k in j['project_list']:
|
||
con = self.get_project_run_state(public.to_dict_obj({'sitename': k}))['data']
|
||
if con:
|
||
tmp.append(con)
|
||
else:
|
||
self.group_remove_project(public.to_dict_obj({'group_name': i, 'sitename': k}))
|
||
j['project_list'] = tmp
|
||
j['name'] = i
|
||
result.append(j)
|
||
|
||
for i in result:
|
||
for j in i['project_list']:
|
||
if not j:
|
||
print(j)
|
||
continue
|
||
if not j['status']:
|
||
i['status'] = 0
|
||
break
|
||
if len(i['project_list']) == 0:
|
||
i['status'] = 0
|
||
return public.returnResult(True, data=result)
|
||
except:
|
||
return public.returnResult(False, traceback.format_exc())
|
||
|
||
def set_order(self, get):
|
||
project_list = get.project_list.strip(',').split(',')
|
||
group_name = get.group_name
|
||
config = self.get_group_file()
|
||
if group_name not in config:
|
||
return public.returnResult(False, '组名不存在')
|
||
config[group_name]['project_list'] = project_list
|
||
public.writeFile(self.group_file, json.dumps(config))
|
||
return public.returnResult(True, '设置成功')
|
||
|
||
def create_group(self, get):
|
||
try:
|
||
if not hasattr(get, 'group_name'):
|
||
return public.returnResult(False, '组名不能为空')
|
||
config = self.get_group_file()
|
||
if get.group_name in config:
|
||
return public.returnResult(False, '组名已存在')
|
||
config[get.group_name] = {'interval': get.get('interval', 30), 'project_list': [], 'status': 0}
|
||
public.writeFile(self.group_file, json.dumps(config))
|
||
return public.returnResult(True, '添加成功')
|
||
except:
|
||
return public.returnResult(False, traceback.format_exc())
|
||
|
||
def remove_group(self, get):
|
||
if not hasattr(get, 'group_name'):
|
||
return public.returnResult(False, '组名不能为空')
|
||
config = self.get_group_file()
|
||
if get.group_name not in config:
|
||
return public.returnResult(False, '组名不存在')
|
||
del config[get.group_name]
|
||
public.writeFile(self.group_file, json.dumps(config))
|
||
return public.returnResult(True, '删除成功')
|
||
|
||
def group_add_project(self, get):
|
||
try:
|
||
if not hasattr(get, 'group_name'):
|
||
return public.returnResult(False, '组名不能为空')
|
||
if not hasattr(get, 'sitename'):
|
||
return public.returnResult(False, '项目名不能为空')
|
||
config = self.get_group_file()
|
||
if get.group_name not in config:
|
||
return public.returnResult(False, '组名不存在')
|
||
if get.sitename not in config[get.group_name]['project_list']:
|
||
config[get.group_name]['project_list'].append(get.sitename)
|
||
flag = 1
|
||
for i in config[get.group_name]['project_list']:
|
||
if not self.get_project_run_state(public.to_dict_obj({'sitename': i}))['status']:
|
||
flag = 0
|
||
break
|
||
if flag:
|
||
config[get.group_name]['status'] = 1
|
||
public.writeFile(self.group_file, json.dumps(config))
|
||
return public.returnResult(True, '添加成功')
|
||
except:
|
||
return public.returnResult(False, traceback.format_exc())
|
||
|
||
def group_remove_project(self, get):
|
||
try:
|
||
if not hasattr(get, 'group_name'):
|
||
return public.returnResult(False, '组名不能为空')
|
||
if not hasattr(get, 'sitename'):
|
||
return public.returnResult(False, '项目名不能为空')
|
||
config = self.get_group_file()
|
||
if get.group_name not in config:
|
||
return public.returnResult(False, '组名不存在')
|
||
if get.sitename not in config[get.group_name]['project_list']:
|
||
return public.returnResult(False, 'The project does not exist')
|
||
config[get.group_name]['project_list'].remove(get.sitename)
|
||
flag = 1
|
||
for i in config[get.group_name]['project_list']:
|
||
if not self.get_project_run_state(public.to_dict_obj({'sitename': i}))['status']:
|
||
flag = 0
|
||
break
|
||
if flag:
|
||
config[get.group_name]['status'] = 1
|
||
else:
|
||
config[get.group_name]['status'] = 0
|
||
public.writeFile(self.group_file, json.dumps(config))
|
||
return public.returnResult(True, '删除成功')
|
||
except:
|
||
return public.returnResult(False, traceback.format_exc())
|
||
|
||
def set_group_interval(self, get):
|
||
if not hasattr(get, 'group_name'):
|
||
return public.returnResult(False, '组名不能为空')
|
||
if not hasattr(get, 'interval'):
|
||
return public.returnResult(False, '间隔时间不能为空')
|
||
config = self.get_group_file()
|
||
if get.group_name not in config:
|
||
return public.returnResult(False, '组名不存在')
|
||
config[get.group_name]['interval'] = get.interval
|
||
public.writeFile(self.group_file, json.dumps(config))
|
||
return public.returnResult(True, '设置成功')
|
||
|
||
def set_group_status(self, get):
|
||
if not hasattr(get, 'group_name'):
|
||
return public.returnResult(False, '组名不能为空')
|
||
if not hasattr(get, 'status'):
|
||
return public.returnResult(False, '状态不能为空')
|
||
config = self.get_group_file()
|
||
if get.group_name not in config:
|
||
return public.returnResult(False, '组名不存在')
|
||
if get.status == 'stop':
|
||
public.run_thread(self.stop_groups, (get.group_name,))
|
||
return public.returnResult(True, '开始停止项目')
|
||
if get.status == 'start':
|
||
public.run_thread(self.start_groups, (get.group_name,))
|
||
return public.returnResult(True, '开始启动项目')
|
||
return public.returnResult(True, '设置成功')
|
||
|
||
def stop_groups(self, group):
|
||
config = self.get_group_file()
|
||
if group not in config:
|
||
return public.returnResult(False, '组名不存在')
|
||
config[group]['status'] = 0
|
||
for i in config[group]['project_list']:
|
||
self.modify_project_run_state(public.to_dict_obj({'sitename': i, 'project_action': 'stop'}))
|
||
public.writeFile(self.group_file, json.dumps(config))
|
||
|
||
def start_groups(self, group):
|
||
config = self.get_group_file()
|
||
self.stop_groups(group)
|
||
if group not in config:
|
||
return public.returnResult(False, '组名不存在')
|
||
for i in config[group]['project_list']:
|
||
self.modify_project_run_state(public.to_dict_obj({'sitename': i, 'project_action': 'start'}))
|
||
if config[group]['project_list'].index(i) == len(config[group]['project_list']) - 1:
|
||
continue
|
||
time.sleep(int(config[group]['interval']))
|
||
flag = 1
|
||
for i in config[group]['project_list']:
|
||
if not self.get_project_run_state(public.to_dict_obj({'sitename': i}))['status']:
|
||
flag = 0
|
||
break
|
||
if flag:
|
||
config[group]['status'] = 1
|
||
else:
|
||
config[group]['status'] = 0
|
||
public.writeFile(self.group_file, json.dumps(config))
|
||
|
||
# 取代理配置文件
|
||
def get_proxy_file(self, get):
|
||
try:
|
||
import files
|
||
conf = [] if not os.path.exists(self.__proxyfile) else json.loads(public.readFile(self.__proxyfile))
|
||
get.webserver = public.GetWebServer()
|
||
sitename = get.sitename
|
||
proxyname = get.proxyname
|
||
proxyname_md5 = self.__calc_md5(proxyname)
|
||
get.path = "%s/panel/vhost/%s/proxy/%s/%s_%s.conf" % (self.setupPath, get.webserver, sitename, proxyname_md5, sitename)
|
||
for i in conf:
|
||
if proxyname == i["proxyname"] and sitename == i["sitename"] and i["type"] != 1:
|
||
return public.returnResult(False, '代理已暂停')
|
||
f = files.files()
|
||
return public.returnResult(True, data=(f.GetFileBody(get), get.path))
|
||
except:
|
||
return public.returnResult(False, traceback.format_exc())
|
||
|
||
# 计算proxyname md5
|
||
def __calc_md5(self, proxyname):
|
||
md5 = hashlib.md5()
|
||
md5.update(proxyname.encode('utf-8'))
|
||
return md5.hexdigest()
|
||
|
||
# 保存重定向配置文件
|
||
def save_proxy_file(self, get):
|
||
import files
|
||
f = files.files()
|
||
get.data = get.config
|
||
return public.returnResult(True, f.SaveFileBody(get))
|
||
|
||
def set_cron_status_all(self, get):
|
||
import crontab
|
||
return crontab.crontab().set_cron_status_all(get)
|
||
|
||
def get_index_conf(self, get):
|
||
if not hasattr(get, 'id'):
|
||
return public.returnResult(False, msg='请输入id')
|
||
sitename = public.M('sites').where('id=?', (get.id,)).getField('name')
|
||
if not sitename:
|
||
return public.returnResult(False, msg='The project does not exist')
|
||
res = DirTool().get_index_conf(sitename)
|
||
if str(res) == str:
|
||
return public.returnResult(False, msg='获取失败' + res)
|
||
else:
|
||
return public.returnResult(True, data=res)
|
||
|
||
def set_index_conf(self, get):
|
||
if not hasattr(get, 'id'):
|
||
return public.returnResult(False, msg='请输入id')
|
||
if not hasattr(get, 'Index'):
|
||
return public.returnResult(False, msg='配置不能为空')
|
||
try:
|
||
sitename = public.M('sites').where('id=?', (get.id,)).getField('name')
|
||
index = get.Index
|
||
if type(get.Index) == str:
|
||
index = get.Index.split(',')
|
||
DirTool().set_index_conf(sitename, file_list=index)
|
||
return public.returnResult(True, msg='设置成功')
|
||
except:
|
||
return public.returnResult(False, msg='设置失败')
|
||
|
||
def get_referer_security(self, get):
|
||
if not hasattr(get, 'id'):
|
||
return public.returnResult(False, msg='请输入id')
|
||
sitename = public.M('sites').where('id=?', (get.id,)).getField('name')
|
||
if not sitename:
|
||
return public.returnResult(False, msg='The project does not exist')
|
||
get.site_name = sitename
|
||
return Referer('').get_referer_security(get)
|
||
|
||
def set_referer_security(self, get):
|
||
return Referer('').set_referer_security(get)
|
||
|
||
def get_system_user_list(self, get=None):
|
||
"""
|
||
默认只返回uid>= 1000 的用户 和 root
|
||
get中包含 sys_user 返回 uid>= 100 的用户 和 root
|
||
get中包含 all_user 返回所有的用户
|
||
"""
|
||
sys_user = False
|
||
all_user = False
|
||
if get is not None:
|
||
if hasattr(get, "sys_user"):
|
||
sys_user = True
|
||
if hasattr(get, "all_user"):
|
||
all_user = True
|
||
|
||
user_set = set()
|
||
with open('/etc/passwd') as fp:
|
||
for line in fp.readlines():
|
||
tmp = line.split(':')
|
||
user_name = tmp[0]
|
||
uid = int(tmp[2])
|
||
if uid == 0:
|
||
user_set.add(user_name)
|
||
continue
|
||
if uid >= 1000:
|
||
user_set.add(user_name)
|
||
continue
|
||
if uid >= 100 and sys_user:
|
||
user_set.add(user_name)
|
||
continue
|
||
if all_user:
|
||
user_set.add(user_name)
|
||
continue
|
||
|
||
return list(user_set)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
m = main()
|
||
m.install_dependence("555.com", "82", "/xiaopacai/laravel11.5.0/", composer_version="2.7.3")
|