mirror of
https://github.com/aaPanel/aaPanel.git
synced 2026-08-17 21:25:47 +02:00
2245 lines
92 KiB
Python
2245 lines
92 KiB
Python
# coding: utf-8
|
||
# -------------------------------------------------------------------
|
||
# aapanel
|
||
# -------------------------------------------------------------------
|
||
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
|
||
# -------------------------------------------------------------------
|
||
# Author: lkq <lkq@bt.cn>
|
||
# -------------------------------------------------------------------
|
||
|
||
# ------------------------------
|
||
# Go模型
|
||
# ------------------------------
|
||
|
||
import os, sys, re, json, shutil, psutil, time
|
||
import traceback
|
||
|
||
from projectModelV2.base import projectBase
|
||
from typing import Dict, Union, List, Optional
|
||
import public, firewalls, panelSite
|
||
|
||
try:
|
||
from BTPanel import cache
|
||
from projectModelV2.aapanelpygvm import pygvm
|
||
except:
|
||
pass
|
||
|
||
|
||
class mobj:
|
||
port = ps = ''
|
||
|
||
|
||
class main(projectBase):
|
||
_panel_path = public.get_panel_path()
|
||
_go_path = '/www/server/go_project'
|
||
_log_name = 'project management'
|
||
_go_pid_path = '/var/tmp/gopids'
|
||
_go_logs_path = "/www/wwwlogs/go"
|
||
_go_logs = '{}/vhost/logs'.format(_go_path)
|
||
_go_run_scripts = '{}/vhost/scripts'.format(_go_path)
|
||
_vhost_path = '{}/vhost'.format(_panel_path)
|
||
_pids = None
|
||
__log_split_script_py = public.get_panel_path() + '/script/run_log_split.py'
|
||
|
||
def __init__(self):
|
||
if not os.path.exists(self._go_path):
|
||
os.makedirs(self._go_path, 493)
|
||
|
||
if not os.path.exists(self._go_pid_path):
|
||
public.ExecShell("mkdir -p /var/tmp/gopids/ && chmod 777 /var/tmp/gopids/")
|
||
|
||
if not os.path.exists(self._go_logs_path):
|
||
public.ExecShell("mkdir -p %s && chmod 777 %s" % (self._go_logs_path, self._go_logs_path))
|
||
|
||
if not os.path.exists(self._go_run_scripts):
|
||
public.ExecShell("mkdir -p %s && chmod 777 %s" % (self._go_run_scripts, self._go_run_scripts))
|
||
|
||
self._init_gvm()
|
||
|
||
def return_result(self,get_value,status=0,data={}):
|
||
"""
|
||
公共返回方法:根据get值是否为None返回对应结果
|
||
:param get_value: 需要判断的get变量
|
||
:return: get非None返回(0, 0, True);get为None返回True
|
||
"""
|
||
if get_value is not None:
|
||
return public.return_message(status, 0, data)
|
||
else:
|
||
return data
|
||
|
||
def get_system_user(self, get):
|
||
'''
|
||
@name 获取系统所有的用户
|
||
@Author:lkq 2021-09-06
|
||
@return list
|
||
'''
|
||
path = '/etc/passwd'
|
||
user_list = public.ReadFile(path)
|
||
resutl = []
|
||
result2 = ["root", "www", "mysql"]
|
||
if isinstance(user_list, str):
|
||
user_list = user_list.split('\n')
|
||
[resutl.append(x.split(":")[0]) for x in user_list if x.split(":")[0] != '']
|
||
return self.return_result(get,0,resutl)
|
||
else:
|
||
return self.return_result(get,0,result2)
|
||
|
||
def get_project_find(self, project_name):
|
||
'''
|
||
@name 获取指定项目配置
|
||
@author hwliang<2021-08-09>
|
||
@param project_name<string> 项目名称
|
||
@return dict
|
||
'''
|
||
project_info = public.M('sites').where('project_type=? AND name=?', ('Go', project_name)).find()
|
||
if isinstance(project_info, str):
|
||
raise public.PanelError('Database query error:'+ project_info)
|
||
if not project_info: return False
|
||
project_info['project_config'] = json.loads(project_info['project_config'])
|
||
return project_info
|
||
|
||
def get_other_pids(self, pid):
|
||
'''
|
||
@name 获取其他进程pid列表
|
||
@author hwliang<2021-08-10>
|
||
@param pid: string<项目pid>
|
||
@return list
|
||
'''
|
||
plugin_name = None
|
||
for pid_name in os.listdir(self._go_pid_path):
|
||
pid_file = '{}/{}'.format(self._go_pid_path, pid_name)
|
||
try:
|
||
s_pid = int(public.readFile(pid_file))
|
||
except:
|
||
continue
|
||
if pid == s_pid:
|
||
plugin_name = pid_name[:-4]
|
||
break
|
||
project_find = self.get_project_find(plugin_name)
|
||
if not project_find: return []
|
||
if not self._pids: self._pids = psutil.pids()
|
||
all_pids = []
|
||
for i in self._pids:
|
||
try:
|
||
p = psutil.Process(i)
|
||
if p.cwd() == project_find['path'] and p.username() == project_find['project_config']['run_user']:
|
||
if p.name() in ['node', 'npm', 'pm2']:
|
||
all_pids.append(i)
|
||
except:
|
||
continue
|
||
return all_pids
|
||
|
||
def get_project_pids(self, get=None, pid=None):
|
||
'''
|
||
@name 获取项目进程pid列表
|
||
@author hwliang<2021-08-10>
|
||
@param pid: string<项目pid>
|
||
@return list
|
||
'''
|
||
if get: pid = int(get.pid)
|
||
if not self._pids: self._pids = psutil.pids()
|
||
project_pids = []
|
||
|
||
for i in self._pids:
|
||
try:
|
||
p = psutil.Process(i)
|
||
if p.status()=="zombie":
|
||
continue
|
||
if p.ppid() == pid:
|
||
if i in project_pids:
|
||
continue
|
||
project_pids.append(i)
|
||
except:
|
||
continue
|
||
other_pids = []
|
||
for i in project_pids:
|
||
other_pids += self.get_project_pids(pid=i)
|
||
if os.path.exists('/proc/{}'.format(pid)):
|
||
project_pids.append(pid)
|
||
|
||
all_pids = list(set(project_pids + other_pids))
|
||
if not all_pids:
|
||
all_pids = self.get_other_pids(pid)
|
||
return self.return_result(get,0,sorted(all_pids))
|
||
|
||
def get_project_run_state(self, get=None, project_name=None):
|
||
'''
|
||
@name 获取项目运行状态
|
||
@author hwliang<2021-08-12>
|
||
@param get<dict_obj>{
|
||
project_name: string<项目名称>
|
||
}
|
||
@param project_name<string> 项目名称
|
||
@return bool
|
||
'''
|
||
if get:
|
||
project_name = get.project_name.strip()
|
||
pid_file = "{}/{}.pid".format(self._go_pid_path, project_name)
|
||
try:
|
||
pid = int(public.readFile(pid_file))
|
||
except:
|
||
pid = 0
|
||
if not pid or not os.path.exists('/proc/{}'.format(pid)):
|
||
pid = self.get_pid_by_command(project_name)
|
||
if not pid:
|
||
return self.return_result(get,0,False)
|
||
pids = self.get_project_pids(pid=pid)
|
||
if not pids:
|
||
return self.return_result(get,0,False)
|
||
return self.return_result(get,0,True)
|
||
|
||
def get_pid_by_command(self, project_name: str):
|
||
"""
|
||
@name 根据命令获取进程pid
|
||
@author baozi<2024-06-06>
|
||
@param project_name<string> 项目名称
|
||
@return int | None
|
||
"""
|
||
project_find = self.get_project_find(project_name)
|
||
project_config = project_find['project_config']
|
||
project_exe = project_config['project_exe']
|
||
project_cmd = project_config['project_cmd']
|
||
cmd_list = self.split_command(project_cmd)
|
||
pids = []
|
||
for i in psutil.process_iter(['pid', 'exe', 'cmdline']):
|
||
try:
|
||
if i.status() == "zombie":
|
||
continue
|
||
if project_exe == i.exe() and cmd_list[1:] == i.cmdline()[1:]:
|
||
pids.append(i.pid)
|
||
except:
|
||
pass
|
||
|
||
running_pid = []
|
||
for pid in pids:
|
||
if pid in psutil.pids():
|
||
running_pid.append(pid)
|
||
|
||
if len(running_pid) == 1:
|
||
pid_file = "{}/{}.pid".format(self._go_pid_path, project_name)
|
||
public.writeFile(pid_file, str(running_pid[0]))
|
||
return running_pid[0]
|
||
|
||
main_pid = []
|
||
for pid in running_pid:
|
||
p = psutil.Process(pid)
|
||
if p.ppid() not in running_pid:
|
||
main_pid.append(pid)
|
||
|
||
if len(main_pid) == 1:
|
||
pid_file = "{}/{}.pid".format(self._go_pid_path, project_name)
|
||
public.writeFile(pid_file, str(main_pid[0]))
|
||
return main_pid[0]
|
||
|
||
return None
|
||
|
||
@staticmethod
|
||
def split_command(command: str):
|
||
res = []
|
||
tmp = ""
|
||
in_quot = False
|
||
for i in command:
|
||
if i in (' ', '\t', '\r'):
|
||
if tmp and not in_quot:
|
||
res.append(tmp)
|
||
tmp = ""
|
||
if in_quot:
|
||
tmp += ' '
|
||
|
||
elif i in ("'", '"'):
|
||
in_quot = not in_quot
|
||
else:
|
||
tmp += i
|
||
|
||
if tmp:
|
||
res.append(tmp)
|
||
|
||
return res
|
||
|
||
def get_process_cpu_time(self, cpu_times):
|
||
cpu_time = 0.00
|
||
for s in cpu_times: cpu_time += s
|
||
return cpu_time
|
||
|
||
def get_cpu_precent(self, p):
|
||
'''
|
||
@name 获取进程cpu使用率
|
||
@author hwliang<2021-08-09>
|
||
@param p: Process<进程对像>
|
||
@return dict
|
||
'''
|
||
skey = "cpu_pre_{}".format(p.pid)
|
||
old_cpu_times = cache.get(skey)
|
||
|
||
process_cpu_time = self.get_process_cpu_time(p.cpu_times())
|
||
if not old_cpu_times:
|
||
cache.set(skey, [process_cpu_time, time.time()], 3600)
|
||
# time.sleep(0.1)
|
||
old_cpu_times = cache.get(skey)
|
||
process_cpu_time = self.get_process_cpu_time(p.cpu_times())
|
||
|
||
old_process_cpu_time = old_cpu_times[0]
|
||
old_time = old_cpu_times[1]
|
||
new_time = time.time()
|
||
cache.set(skey, [process_cpu_time, new_time], 3600)
|
||
percent = round(100.00 * (process_cpu_time - old_process_cpu_time) / (new_time - old_time) / psutil.cpu_count(), 2)
|
||
return percent
|
||
|
||
def format_connections(self, connects):
|
||
'''
|
||
@name 获取进程网络连接信息
|
||
@author hwliang<2021-08-09>
|
||
@param connects<pconn>
|
||
@return list
|
||
'''
|
||
result = []
|
||
for i in connects:
|
||
raddr = i.raddr
|
||
if not i.raddr:
|
||
raddr = ('', 0)
|
||
laddr = i.laddr
|
||
if not i.laddr:
|
||
laddr = ('', 0)
|
||
result.append({
|
||
"fd": i.fd,
|
||
"family": i.family,
|
||
"local_addr": laddr[0],
|
||
"local_port": laddr[1],
|
||
"client_addr": raddr[0],
|
||
"client_rport": raddr[1],
|
||
"status": i.status
|
||
})
|
||
return result
|
||
|
||
def get_connects(self, pid):
|
||
'''
|
||
@name 获取进程连接信息
|
||
@author hwliang<2021-08-09>
|
||
@param pid<int>
|
||
@return dict
|
||
'''
|
||
connects = 0
|
||
try:
|
||
if pid == 1: return connects
|
||
tp = '/proc/' + str(pid) + '/fd/'
|
||
if not os.path.exists(tp): return connects
|
||
for d in os.listdir(tp):
|
||
fname = tp + d
|
||
if os.path.islink(fname):
|
||
l = os.readlink(fname)
|
||
if l.find('socket:') != -1: connects += 1
|
||
except:
|
||
pass
|
||
return connects
|
||
|
||
def object_to_dict(self, obj):
|
||
'''
|
||
@name 将对象转换为字典
|
||
@author hwliang<2021-08-09>
|
||
@param obj<object>
|
||
@return dict
|
||
'''
|
||
result = {}
|
||
for name in dir(obj):
|
||
value = getattr(obj, name)
|
||
if not name.startswith('__') and not callable(value) and not name.startswith('_'): result[name] = value
|
||
return result
|
||
|
||
def list_to_dict(self, data):
|
||
'''
|
||
@name 将列表转换为字典
|
||
@author hwliang<2021-08-09>
|
||
@param data<list>
|
||
@return dict
|
||
'''
|
||
result = []
|
||
for s in data:
|
||
result.append(self.object_to_dict(s))
|
||
return result
|
||
|
||
def get_process_info_by_pid(self, pid):
|
||
'''
|
||
@name 获取进程信息
|
||
@author hwliang<2021-08-12>
|
||
@param pid: int<进程id>
|
||
@return dict
|
||
'''
|
||
process_info = {}
|
||
try:
|
||
if not os.path.exists('/proc/{}'.format(pid)): return process_info
|
||
p = psutil.Process(pid)
|
||
# status_ps = {'sleeping': '睡眠', 'running': '活动'}
|
||
with p.oneshot():
|
||
p_mem = p.memory_full_info()
|
||
if p_mem.uss + p_mem.rss + p_mem.pss + p_mem.data == 0: return process_info
|
||
p_state = p.status()
|
||
# if p_state in status_ps: p_state = status_ps[p_state]
|
||
# process_info['exe'] = p.exe()
|
||
process_info['name'] = p.name()
|
||
process_info['pid'] = pid
|
||
process_info['ppid'] = p.ppid()
|
||
process_info['create_time'] = int(p.create_time())
|
||
process_info['status'] = p_state
|
||
process_info['user'] = p.username()
|
||
process_info['memory_used'] = p_mem.uss
|
||
process_info['cpu_percent'] = self.get_cpu_precent(p)
|
||
process_info['io_write_bytes'], process_info['io_read_bytes'] = self.get_io_speed(p)
|
||
process_info['connections'] = self.format_connections(p.connections())
|
||
process_info['connects'] = self.get_connects(pid)
|
||
process_info['open_files'] = self.list_to_dict(p.open_files())
|
||
process_info['threads'] = p.num_threads()
|
||
process_info['exe'] = ' '.join(p.cmdline())
|
||
return process_info
|
||
except:
|
||
return process_info
|
||
|
||
def get_io_speed(self, p):
|
||
'''
|
||
@name 获取磁盘IO速度
|
||
@author hwliang<2021-08-12>
|
||
@param p: Process<进程对像>
|
||
@return list
|
||
'''
|
||
skey = "io_speed_{}".format(p.pid)
|
||
old_pio = cache.get(skey)
|
||
if not hasattr(p, 'io_counters'): return 0, 0
|
||
pio = p.io_counters()
|
||
if not old_pio:
|
||
cache.set(skey, [pio, time.time()], 3600)
|
||
# time.sleep(0.1)
|
||
old_pio = cache.get(skey)
|
||
pio = p.io_counters()
|
||
|
||
old_write_bytes = old_pio[0].write_bytes
|
||
old_read_bytes = old_pio[0].read_bytes
|
||
old_time = old_pio[1]
|
||
|
||
new_time = time.time()
|
||
write_bytes = pio.write_bytes
|
||
read_bytes = pio.read_bytes
|
||
|
||
cache.set(skey, [pio, new_time], 3600)
|
||
|
||
write_speed = int((write_bytes - old_write_bytes) / (new_time - old_time))
|
||
read_speed = int((read_bytes - old_read_bytes) / (new_time - old_time))
|
||
|
||
return write_speed, read_speed
|
||
|
||
def get_project_load_info(self, get=None, project_name=None):
|
||
'''
|
||
@name 获取项目负载信息
|
||
@author hwliang<2021-08-12>
|
||
@param get<dict_obj>{
|
||
project_name: string<项目名称>
|
||
}
|
||
@return dict
|
||
'''
|
||
if get: project_name = get.project_name.strip()
|
||
load_info = {}
|
||
pid_file = "{}/{}.pid".format(self._go_pid_path, project_name)
|
||
if not os.path.exists(pid_file): return self.return_result(get,0,load_info)
|
||
try:
|
||
pid = int(public.readFile(pid_file))
|
||
except:
|
||
return self.return_result(get,0,load_info)
|
||
pids = self.get_project_pids(pid=pid)
|
||
if not pids: return self.return_result(get,0,load_info)
|
||
for i in pids:
|
||
process_info = self.get_process_info_by_pid(i)
|
||
if process_info: load_info[i] = process_info
|
||
return self.return_result(get,0,load_info)
|
||
|
||
def get_ssl_end_date(self, project_name):
|
||
'''
|
||
@name 获取SSL信息
|
||
@author hwliang<2021-08-09>
|
||
@param project_name <string> 项目名称
|
||
@return dict
|
||
'''
|
||
import data
|
||
return data.data().get_site_ssl_info('go_{}'.format(project_name))
|
||
|
||
def get_project_stat(self, project_info, has_load_info=True):
|
||
'''
|
||
@name 获取项目状态信息
|
||
@author hwliang<2021-08-09>
|
||
@param project_info<dict> 项目信息
|
||
@param has_load_info<bool> 是否获取项目信息 后续发现并非获取load_info导致的接口缓慢,故默认为True
|
||
@return list
|
||
'''
|
||
if isinstance(project_info['project_config'],str):
|
||
project_info['project_config'] = json.loads(project_info['project_config'])
|
||
project_info['run'] = self.get_project_run_state(project_name=project_info['name'])
|
||
if has_load_info is True:
|
||
project_info['load_info'] = self.get_project_load_info(project_name=project_info['name'])
|
||
project_info['ssl'] = self.get_ssl_end_date(project_name=project_info['name'])
|
||
project_info['listen'] = []
|
||
project_info['listen_ok'] = True
|
||
if 'load_info' in project_info and 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'] = []
|
||
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']
|
||
|
||
if not project_info['listen'] and project_info['run']:
|
||
project_info['listen'] = self.get_project_listen(project_name=project_info['name'])
|
||
if project_info['listen']:
|
||
project_info['listen_ok'] = project_info['project_config']['port'] in project_info['listen']
|
||
|
||
return project_info
|
||
|
||
def get_project_listen(self, project_name):
|
||
pid_file = "{}/{}.pid".format(self._go_pid_path, project_name)
|
||
try:
|
||
process_pid = int(public.readFile(pid_file))
|
||
except:
|
||
return []
|
||
pids = self.get_project_pids(pid=process_pid)
|
||
ports = set()
|
||
# 获取指定进程及其子进程的所有连接
|
||
connections = psutil.net_connections()
|
||
|
||
# 遍历连接,筛选出指定进程及其子进程的连接
|
||
for conn in connections:
|
||
if conn.pid in set(pids):
|
||
if conn.laddr and conn.status == 'LISTEN':
|
||
ports.add(conn.laddr.port)
|
||
|
||
return list(ports)
|
||
|
||
def exists_nginx_ssl(self, project_name):
|
||
'''
|
||
@name 判断项目是否配置Nginx SSL配置
|
||
@author hwliang<2021-08-09>
|
||
@param project_name: string<项目名称>
|
||
@return tuple
|
||
'''
|
||
config_file = "{}/nginx/go_{}.conf".format(public.get_vhost_path(), project_name)
|
||
if not os.path.exists(config_file):
|
||
return False, False
|
||
|
||
config_body = public.readFile(config_file)
|
||
if not config_body:
|
||
return False, False
|
||
|
||
is_ssl, is_force_ssl = False, False
|
||
if config_body.find('ssl_certificate') != -1:
|
||
is_ssl = True
|
||
if config_body.find('HTTP_TO_HTTPS_START') != -1:
|
||
is_force_ssl = True
|
||
return is_ssl, is_force_ssl
|
||
|
||
def check_port_is_used(self, port, sock=False):
|
||
'''
|
||
@name 检查端口是否被占用
|
||
@author hwliang<2021-08-09>
|
||
@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, 'Go')).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 exists_apache_ssl(self, project_name):
|
||
'''
|
||
@name 判断项目是否配置Apache SSL配置
|
||
@author hwliang<2021-08-09>
|
||
@param project_name: string<项目名称>
|
||
@return bool
|
||
'''
|
||
config_file = "{}/apache/go_{}.conf".format(public.get_vhost_path(), project_name)
|
||
if not os.path.exists(config_file):
|
||
return False, False
|
||
|
||
config_body = public.readFile(config_file)
|
||
if not config_body:
|
||
return False, False
|
||
|
||
is_ssl, is_force_ssl = False, False
|
||
if config_body.find('SSLCertificateFile') != -1:
|
||
is_ssl = True
|
||
if config_body.find('HTTP_TO_HTTPS_START') != -1:
|
||
is_force_ssl = True
|
||
return is_ssl, is_force_ssl
|
||
|
||
def set_apache_config(self, project_find):
|
||
'''
|
||
@name 设置Apache配置
|
||
@author hwliang<2021-08-09>
|
||
@param project_find: dict<项目信息>
|
||
@return bool
|
||
'''
|
||
project_name = project_find['name']
|
||
webservice_status = public.get_multi_webservice_status()
|
||
|
||
# 处理域名和端口
|
||
ports = []
|
||
domains = []
|
||
for d in project_find['project_config']['domains']:
|
||
domain_tmp = d.rsplit(':', 1)
|
||
if len(domain_tmp) == 1: domain_tmp.append(80)
|
||
if not int(domain_tmp[1]) in ports:
|
||
ports.append(int(domain_tmp[1]))
|
||
if not domain_tmp[0] in domains:
|
||
domains.append(domain_tmp[0])
|
||
|
||
config_file = "{}/apache/go_{}.conf".format(self._vhost_path, project_name)
|
||
template_file = "{}/template/apache/go_http.conf".format(self._vhost_path)
|
||
config_body = public.readFile(template_file)
|
||
apache_config_body = ''
|
||
|
||
# 旧的配置文件是否配置SSL
|
||
is_ssl, is_force_ssl = self.exists_apache_ssl(project_name)
|
||
if is_ssl:
|
||
if not 443 in ports: ports.append(443)
|
||
|
||
from panelSite import panelSite
|
||
s = panelSite()
|
||
|
||
# 根据端口列表生成配置
|
||
for p in ports:
|
||
listen_port = p
|
||
if webservice_status:
|
||
if p == 443:
|
||
listen_port = 8290
|
||
else:
|
||
listen_port = 8288
|
||
ssl_config = ''
|
||
if p == 443 and is_ssl:
|
||
ssl_key_file = "{vhost_path}/cert/{project_name}/privkey.pem".format(project_name=project_name, vhost_path=public.get_vhost_path())
|
||
if not os.path.exists(ssl_key_file): continue # 不存在证书文件则跳过
|
||
ssl_config = '''#SSL
|
||
SSLEngine On
|
||
SSLCertificateFile {vhost_path}/cert/{project_name}/fullchain.pem
|
||
SSLCertificateKeyFile {vhost_path}/cert/{project_name}/privkey.pem
|
||
SSLCipherSuite EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5
|
||
SSLProtocol All -SSLv2 -SSLv3 -TLSv1
|
||
SSLHonorCipherOrder On'''.format(project_name=project_name, vhost_path=public.get_vhost_path())
|
||
else:
|
||
if is_force_ssl:
|
||
ssl_config = '''#HTTP_TO_HTTPS_START
|
||
<IfModule mod_rewrite.c>
|
||
RewriteEngine on
|
||
RewriteCond %{SERVER_PORT} !^443$
|
||
RewriteRule (.*) https://%{SERVER_NAME}$1 [L,R=301]
|
||
</IfModule>
|
||
#HTTP_TO_HTTPS_END'''
|
||
|
||
# 生成vhost主体配置
|
||
apache_config_body += config_body.format(
|
||
site_path=project_find['path'],
|
||
server_name='{}.{}'.format(p, project_name),
|
||
domains=' '.join(domains),
|
||
log_path=public.get_logs_path(),
|
||
server_admin='admin@{}'.format(project_name),
|
||
url='http://127.0.0.1:{}'.format(project_find['project_config']['port']),
|
||
port=listen_port,
|
||
ssl_config=ssl_config,
|
||
project_name=project_name
|
||
)
|
||
apache_config_body += "\n"
|
||
|
||
# 添加端口到主配置文件
|
||
if listen_port not in [80]:
|
||
s.apacheAddPort(listen_port)
|
||
|
||
# 写.htaccess
|
||
rewrite_file = "{}/.htaccess".format(project_find['path']) # go项目路径是运行文件,不是项目根目录
|
||
if not os.path.exists(rewrite_file):
|
||
public.writeFile(rewrite_file, '# Please fill in the pseudo static rules or custom Apache configuration here\n')
|
||
from mod.base.web_conf import ap_ext
|
||
apache_config_body = ap_ext.set_extension_by_config(project_name, apache_config_body)
|
||
|
||
# 写配置文件
|
||
public.writeFile(config_file, apache_config_body)
|
||
return True
|
||
|
||
def set_nginx_config(self, project_find):
|
||
'''
|
||
@name 设置Nginx配置
|
||
@author hwliang<2021-08-09>
|
||
@param project_find: dict<项目信息>
|
||
@return bool
|
||
'''
|
||
project_name = project_find['name']
|
||
ports = []
|
||
domains = []
|
||
|
||
for d in project_find['project_config']['domains']:
|
||
domain_tmp = d.rsplit(':', 1)
|
||
if len(domain_tmp) == 1: domain_tmp.append(80)
|
||
if not int(domain_tmp[1]) in ports:
|
||
ports.append(int(domain_tmp[1]))
|
||
if not domain_tmp[0] in domains:
|
||
domains.append(domain_tmp[0])
|
||
listen_ipv6 = public.listen_ipv6()
|
||
is_ssl, is_force_ssl = self.exists_nginx_ssl(project_name)
|
||
listen_ports_list = []
|
||
for p in ports:
|
||
listen_ports_list.append(" listen {};".format(p))
|
||
if listen_ipv6:
|
||
listen_ports_list.append(" listen [::]:{};".format(p))
|
||
|
||
ssl_config = ''
|
||
if is_ssl:
|
||
http3_header = ""
|
||
if self.is_nginx_http3():
|
||
http3_header = '''\n add_header Alt-Svc 'quic=":443"; h3=":443"; h3-29=":443"; h3-27=":443";h3-25=":443"; h3-T050=":443"; h3-Q050=":443";h3-Q049=":443";h3-Q048=":443"; h3-Q046=":443"; h3-Q043=":443"';'''
|
||
http3_header += "\n quic_retry on;\n quic_gso on;"
|
||
if self.ng_ssl_early_data_enabled():
|
||
http3_header += "\n ssl_early_data on;"
|
||
|
||
nginx_ver = public.nginx_version()
|
||
if nginx_ver:
|
||
port_str = ["443"]
|
||
if listen_ipv6:
|
||
port_str.append("[::]:443")
|
||
use_http2_on = False
|
||
for p in port_str:
|
||
listen_str = " listen {} ssl".format(p)
|
||
if nginx_ver < [1, 9, 5]:
|
||
listen_str += ";"
|
||
elif [1, 9, 5] <= nginx_ver < [1, 25, 1]:
|
||
listen_str += " http2;"
|
||
else: # >= [1, 25, 1]
|
||
listen_str += ";"
|
||
use_http2_on = True
|
||
listen_ports_list.append(listen_str)
|
||
|
||
if self.is_nginx_http3():
|
||
listen_ports_list.append(" listen {} quic;".format(p))
|
||
if use_http2_on:
|
||
listen_ports_list.append(" http2 on;")
|
||
if self.is_nginx_http3():
|
||
listen_ports_list.append(" http3 on;")
|
||
|
||
else:
|
||
listen_ports_list.append(" listen 443 ssl;")
|
||
|
||
ssl_config = '''ssl_certificate {vhost_path}/cert/{priject_name}/fullchain.pem;
|
||
ssl_certificate_key {vhost_path}/cert/{priject_name}/privkey.pem;
|
||
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
|
||
ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
|
||
ssl_prefer_server_ciphers on;
|
||
ssl_session_cache shared:SSL:10m;
|
||
ssl_session_timeout 10m;
|
||
add_header Strict-Transport-Security "max-age=31536000";{http3_header}
|
||
error_page 497 https://$host$request_uri;'''.format(vhost_path=self._vhost_path, priject_name=project_name, http3_header=http3_header)
|
||
|
||
if is_force_ssl:
|
||
ssl_config += '''
|
||
#HTTP_TO_HTTPS_START
|
||
if ($server_port !~ 443){
|
||
rewrite ^(/.*)$ https://$host$1 permanent;
|
||
}
|
||
#HTTP_TO_HTTPS_END'''
|
||
|
||
config_file = "{}/nginx/go_{}.conf".format(self._vhost_path, project_name)
|
||
template_file = "{}/template/nginx/go_http.conf".format(self._vhost_path)
|
||
|
||
listen_ports = "\n".join(listen_ports_list).strip()
|
||
|
||
config_body = public.readFile(template_file)
|
||
mut_config = {
|
||
"site_path": project_find['path'],
|
||
"domains": ' '.join(domains),
|
||
"url": 'http://127.0.0.1:{}'.format(project_find['project_config']['port']),
|
||
"ssl_config": ssl_config,
|
||
"listen_ports": listen_ports
|
||
}
|
||
config_body = config_body.format(
|
||
site_path=mut_config["site_path"],
|
||
domains=mut_config["domains"],
|
||
project_name=project_name,
|
||
panel_path=self._panel_path,
|
||
log_path=public.get_logs_path(),
|
||
url=mut_config["url"],
|
||
host='$host',
|
||
listen_ports=listen_ports,
|
||
ssl_config=mut_config["ssl_config"]
|
||
)
|
||
|
||
# # 恢复旧的SSL配置
|
||
# ssl_config = self.get_nginx_ssl_config(project_name)
|
||
# if ssl_config:
|
||
# config_body.replace('#error_page 404/404.html;',ssl_config)
|
||
|
||
rewrite_file = "{panel_path}/vhost/rewrite/go_{project_name}.conf".format(panel_path=self._panel_path,
|
||
project_name=project_name)
|
||
if not os.path.exists(rewrite_file):
|
||
public.writeFile(rewrite_file, '# Please fill in the pseudo static rules or custom NGINX configuration here\n')
|
||
apply_check = "{}/vhost/nginx/well-known/{}.conf".format(self._panel_path, project_name)
|
||
if not os.path.exists("/www/server/panel/vhost/nginx/well-known"):
|
||
os.makedirs("/www/server/panel/vhost/nginx/well-known", 0o600)
|
||
if not os.path.exists(apply_check):
|
||
public.writeFile(apply_check, '')
|
||
from mod.base.web_conf import ng_ext
|
||
config_body = ng_ext.set_extension_by_config(project_name, config_body)
|
||
if not os.path.exists(config_file):
|
||
public.writeFile(config_file, config_body)
|
||
else:
|
||
if not self._replace_nginx_conf(config_file, mut_config):
|
||
public.writeFile(config_file, config_body)
|
||
return True
|
||
|
||
@staticmethod
|
||
def _replace_nginx_conf(config_file, mut_config: dict) -> bool:
|
||
"""尝试替换"""
|
||
data: str = public.readFile(config_file)
|
||
tab_spc = " "
|
||
rep_list = [
|
||
(
|
||
r"([ \f\r\t\v]*listen[^;\n]*;\n(\s*http2\s+on\s*;[^\n]*\n)?)+",
|
||
tab_spc + mut_config["listen_ports"] + "\n"
|
||
),
|
||
(
|
||
r"[ \f\r\t\v]*root [ \f\r\t\v]*/[^;\n]*;",
|
||
" root {};".format(mut_config["site_path"])
|
||
),
|
||
(
|
||
r"[ \f\r\t\v]*server_name [ \f\r\t\v]*[^\n;]*;",
|
||
" server_name {};".format(mut_config["domains"])
|
||
),
|
||
(
|
||
r"[ \f\r\t\v]*location */ *\{ *\n *proxy_pass[^\n;]*;\n *proxy_set_header *Host",
|
||
"{}location / {{\n{}proxy_pass {};\n{}proxy_set_header Host".format(
|
||
tab_spc, tab_spc * 2, mut_config["url"], tab_spc * 2, )
|
||
),
|
||
(
|
||
"[ \f\r\t\v]*#SSL-START(.*\n){2,15}[ \f\r\t\v]*#SSL-END",
|
||
"{}#SSL-START SSL-related configurations\n{}#error_page 404/404.html;\n{}{}\n{}#SSL-END".format(
|
||
tab_spc, tab_spc, tab_spc, mut_config["ssl_config"], tab_spc)
|
||
)
|
||
]
|
||
for rep, info in rep_list:
|
||
if re.search(rep, data):
|
||
data = re.sub(rep, info, data, 1)
|
||
else:
|
||
return False
|
||
|
||
public.writeFile(config_file, data)
|
||
return True
|
||
|
||
def clear_nginx_config(self, project_find):
|
||
'''
|
||
@name 清除nginx配置
|
||
@author hwliang<2021-08-09>
|
||
@param project_find: dict<项目信息>
|
||
@return bool
|
||
'''
|
||
project_name = project_find['name']
|
||
config_file = "{}/nginx/go_{}.conf".format(self._vhost_path, project_name)
|
||
if os.path.exists(config_file):
|
||
os.remove(config_file)
|
||
rewrite_file = "{panel_path}/vhost/rewrite/go_{project_name}.conf".format(panel_path=self._panel_path, project_name=project_name)
|
||
if os.path.exists(rewrite_file):
|
||
os.remove(rewrite_file)
|
||
return True
|
||
|
||
def clear_apache_config(self, project_find):
|
||
'''
|
||
@name 清除apache配置
|
||
@author hwliang<2021-08-09>
|
||
@param project_find: dict<项目信息>
|
||
@return bool
|
||
'''
|
||
project_name = project_find['name']
|
||
config_file = "{}/apache/go_{}.conf".format(self._vhost_path, project_name)
|
||
if os.path.exists(config_file):
|
||
os.remove(config_file)
|
||
return True
|
||
|
||
def clear_config(self, project_name):
|
||
'''
|
||
@name 清除项目配置
|
||
@author hwliang<2021-08-09>
|
||
@param project_name: string<项目名称>
|
||
@return bool
|
||
'''
|
||
project_find = self.get_project_find(project_name)
|
||
if not project_find: return False
|
||
self.clear_nginx_config(project_find)
|
||
self.clear_apache_config(project_find)
|
||
public.serviceReload()
|
||
return True
|
||
|
||
def set_config(self, project_name):
|
||
'''
|
||
@name 设置项目配置
|
||
@author hwliang<2021-08-09>
|
||
@param project_name: string<项目名称>
|
||
@return bool
|
||
'''
|
||
project_find = self.get_project_find(project_name)
|
||
if not project_find: return False
|
||
if not project_find['project_config']: return False
|
||
if not project_find['project_config']['bind_extranet']: return False
|
||
if not project_find['project_config']['domains']: return False
|
||
self.set_nginx_config(project_find)
|
||
self.set_apache_config(project_find)
|
||
public.serviceReload()
|
||
return True
|
||
|
||
def kill_pids(self, get=None, pids=None):
|
||
'''
|
||
@name 结束进程列表
|
||
@author hwliang<2021-08-10>
|
||
@param pids: string<进程pid列表>
|
||
@return dict
|
||
'''
|
||
if get: pids = get.pids
|
||
if not pids: return self.return_result(get,0,'No process')
|
||
pids = sorted(pids, reverse=True)
|
||
for i in pids:
|
||
try:
|
||
p = psutil.Process(i)
|
||
p.terminate()
|
||
except:
|
||
pass
|
||
return self.return_result(get,0,'All processes have ended')
|
||
|
||
def bind_extranet(self, get):
|
||
'''
|
||
@name 绑定外网
|
||
@author hwliang<2021-08-09>
|
||
@param get<dict_obj>{
|
||
project_name: string<项目名称>
|
||
}
|
||
@return dict
|
||
'''
|
||
res_msg = self._check_webserver()
|
||
if res_msg:
|
||
return self.return_result(get,-1,res_msg)
|
||
project_name = get.project_name.strip()
|
||
project_find = self.get_project_find(project_name)
|
||
if not project_find: return self.return_result(get,-1,'The project does not exist.')
|
||
if not project_find['project_config']['domains']: return self.return_result(get,-1,'Please add at least one domain name to the "Domain Management" option first')
|
||
project_find['project_config']['bind_extranet'] = 1
|
||
public.M('sites').where("id=?", (project_find['id'],)).setField('project_config', json.dumps(project_find['project_config']))
|
||
self.set_config(project_name)
|
||
public.WriteLog(self._log_name, 'Go project [{}], enable external network mapping'.format(project_name))
|
||
return self.return_result(get,0,'Successfully enabled external network mapping')
|
||
|
||
def unbind_extranet(self, get):
|
||
'''
|
||
@name 解绑外网
|
||
@author hwliang<2021-08-09>
|
||
@param get<dict_obj>{
|
||
project_name: string<项目名称>
|
||
}
|
||
@return dict
|
||
'''
|
||
project_name = get.project_name.strip()
|
||
self.clear_config(project_name)
|
||
public.serviceReload()
|
||
project_find = self.get_project_find(project_name)
|
||
project_find['project_config']['bind_extranet'] = 0
|
||
public.M('sites').where("id=?", (project_find['id'],)).setField('project_config', json.dumps(project_find['project_config']))
|
||
public.WriteLog(self._log_name, 'Go project [{}], disable external network mapping'.format(project_name))
|
||
return self.return_result(get,0,'Closed successfully')
|
||
|
||
def restart_project(self, get):
|
||
'''
|
||
@name 重启项目
|
||
@author hwliang<2021-08-09>
|
||
@param get<dict_obj>{
|
||
project_name: string<项目名称>
|
||
}
|
||
@return dict
|
||
'''
|
||
project_find = self.get_project_find(get.project_name)
|
||
# 2024.4.3 修复项目过期时间判断不对
|
||
mEdate = time.strftime('%Y-%m-%d', time.localtime())
|
||
if project_find['edate'] != "0000-00-00" and project_find['edate'] < mEdate:
|
||
return self.return_result(get,-1,'The current project has expired, please reset the project expiration time')
|
||
res = self.stop_project(get)
|
||
if res['status']==-1: return res
|
||
|
||
res = self.start_project(get)
|
||
if res['status']==-1: return res
|
||
return self.return_result(get,0,'Restart successful')
|
||
|
||
def stop_project(self, get):
|
||
'''
|
||
@name 停止项目
|
||
@author hwliang<2021-08-09>
|
||
@param get<dict_obj>{
|
||
project_name: string<项目名称>
|
||
}
|
||
@return dict
|
||
'''
|
||
pid_file = "{}/{}.pid".format(self._go_pid_path, get.project_name)
|
||
if not os.path.exists(pid_file): return self.return_result(get,-1,'Project not started')
|
||
try:
|
||
pid = int(public.readFile(pid_file))
|
||
except:
|
||
return self.return_result(get,-1,'Project not started')
|
||
pids = self.get_project_pids(pid=pid)
|
||
if not pids: return self.return_result(get,-1,'Project not started')
|
||
|
||
project_find = self.get_project_find(get.project_name)
|
||
# 2024.4.3 修复项目过期时间判断不对
|
||
mEdate = time.strftime('%Y-%m-%d', time.localtime())
|
||
if project_find['edate'] != "0000-00-00" and project_find['edate'] < mEdate:
|
||
return self.return_result(get,-1,'The current project has expired, please reset the project expiration time')
|
||
|
||
self.kill_pids(pids=pids)
|
||
if os.path.exists(pid_file): os.remove(pid_file)
|
||
self.stop_by_user(self.get_project_find(get.project_name)["id"])
|
||
return self.return_result(get,0,'Stop successfully')
|
||
|
||
def start_project(self, get):
|
||
'''
|
||
@name 启动项目
|
||
@author hwliang<2021-08-09>
|
||
@param get<dict_obj>{
|
||
project_name: string<项目名称>
|
||
}
|
||
@return dict
|
||
'''
|
||
if not os.path.exists(self._go_pid_path):
|
||
public.ExecShell("mkdir -p /var/tmp/gopids/ && chmod 777 /var/tmp/gopids/")
|
||
else:
|
||
ret = public.get_mode_and_user("/var/tmp/gopids/")
|
||
if isinstance(ret, dict):
|
||
if ret['mode'] != 777:
|
||
public.ExecShell("chmod 777 /var/tmp/gopids/")
|
||
project_find = self.get_project_find(get.project_name)
|
||
if not project_find: return self.return_result(get,-1, 'The project does not exist.')
|
||
# 2024.4.3 修复项目过期时间判断不对
|
||
mEdate = time.strftime('%Y-%m-%d', time.localtime())
|
||
if project_find['edate'] != "0000-00-00" and project_find['edate'] < mEdate:
|
||
return self.return_result(get,-1,'The current project has expired, please reset the project expiration time')
|
||
|
||
self._update_project(get.project_name, project_find)
|
||
log_file = "{}/{}.log".format(project_find['project_config']["log_path"], project_find["name"])
|
||
pid_file = "{}/{}.pid".format(self._go_pid_path, get.project_name)
|
||
# public.writeFile(log_file,"")
|
||
public.set_own(log_file, project_find['project_config']['run_user'])
|
||
project_cmd = project_find["project_config"]['project_cmd']
|
||
project_log_status = project_find["project_config"].get('project_log', 1)
|
||
log_exec = ''
|
||
if int(project_log_status):
|
||
log_exec = " &>> {}".format(log_file)
|
||
if 'project_path' in project_find['project_config']:
|
||
jar_path = project_find['project_config']['project_path']
|
||
else:
|
||
jar_path = '/root'
|
||
|
||
project_config = project_find["project_config"]
|
||
pre_sh_list = []
|
||
if 'env_file' in project_config:
|
||
env_file = project_config['env_file']
|
||
if os.path.isfile(env_file):
|
||
pre_sh_list.append("source {}".format(env_file))
|
||
if "env_list" in project_config:
|
||
for env in project_config['env_list']:
|
||
if "k" in env and "v" in env:
|
||
pre_sh_list.append("export {}={}".format(env['k'], env['v']))
|
||
|
||
# 启动脚本
|
||
start_cmd = '''#!/bin/bash
|
||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||
export PATH
|
||
cd {jar_path}
|
||
{pre_sh}
|
||
nohup {project_cmd} {log_exec} &
|
||
echo $! > {pid_file}'''.format(
|
||
jar_path=jar_path,
|
||
project_cmd=project_cmd,
|
||
pid_file=pid_file,
|
||
log_exec=log_exec,
|
||
pre_sh="\n".join(pre_sh_list),
|
||
)
|
||
|
||
script_file = "{}/{}.sh".format(self._go_run_scripts, get.project_name)
|
||
# 写入启动脚本
|
||
public.writeFile(script_file, start_cmd)
|
||
if os.path.exists(pid_file): os.remove(pid_file)
|
||
|
||
if not os.path.exists(log_file):
|
||
public.ExecShell("touch {}".format(log_file))
|
||
public.ExecShell("chown {}:{} {}".format(project_find['project_config']['run_user'],
|
||
project_find['project_config']['run_user'], log_file))
|
||
self._pass_dir_for_user(os.path.dirname(log_file), project_find['project_config']['run_user'])
|
||
|
||
public.ExecShell("chown -R {}:{} {}".format(project_find['project_config']['run_user'], project_find['project_config']['run_user'], jar_path))
|
||
public.set_mode(script_file, 755)
|
||
public.set_own(script_file, project_find['project_config']['run_user'])
|
||
# 执行脚本文件
|
||
p = public.ExecShell("bash {}".format(script_file), user=project_find['project_config']['run_user'], env=os.environ.copy())
|
||
time.sleep(1)
|
||
if not os.path.exists(pid_file):
|
||
return self.return_result(get,-1, 'Startup failed, please try switching the startup user')
|
||
# 获取PID
|
||
try:
|
||
pid = int(public.readFile(pid_file))
|
||
except:
|
||
return self.return_result(get,-1, 'Startup failure:{}'.format(p.replace("\n", "<br>")))
|
||
pids = self.get_project_pids(pid=pid)
|
||
if not pids:
|
||
if os.path.exists(pid_file): os.remove(pid_file)
|
||
return self.return_result(get,-1, 'Startup failure:<br>{}'.format(public.GetNumLines(log_file, 20).replace("\n", "<br>")))
|
||
# return public.returnMsg(True, '启动成功')
|
||
|
||
self.start_by_user(project_find["id"])
|
||
return self.return_result(get,0, 'Startup successful')
|
||
|
||
def get_project_list(self, get):
|
||
'''
|
||
@name 获取项目列表 (非链式调用版本)
|
||
@author hwliang<2021-08-09>
|
||
@modified Gemini<2026-04-07>
|
||
'''
|
||
|
||
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'
|
||
|
||
re_order = get.get('re_order', '')
|
||
p = int(get.p)
|
||
limit = int(get.limit)
|
||
|
||
db_obj = public.M('sites')
|
||
|
||
where_str = "project_type=?"
|
||
where_args = ["Go"]
|
||
|
||
if "type_id" in get and get.type_id:
|
||
try:
|
||
where_str += " AND type_id=?"
|
||
where_args.append(int(get.type_id))
|
||
except:
|
||
pass
|
||
|
||
if 'search' in get:
|
||
get.project_name = get.search.strip()
|
||
search_pattern = "%{}%".format(get.project_name)
|
||
where_str += " AND (name LIKE ? OR ps LIKE ?)"
|
||
where_args.extend([search_pattern, search_pattern])
|
||
|
||
db_obj.where(where_str, tuple(where_args))
|
||
db_obj.order(get.order)
|
||
data_list = db_obj.select()
|
||
|
||
if isinstance(data_list, str) and data_list.startswith("error"):
|
||
raise public.PanelError("Database query error:" + data_list)
|
||
|
||
if not data_list:
|
||
return self.return_result(get, 0, {'data': [], 'page': ''})
|
||
|
||
re_data = None
|
||
if re_order:
|
||
try:
|
||
import data_v2
|
||
res = data_v2.data().get_site_request(public.to_dict_obj({'site_type': 'Go'}))
|
||
if res.get('status') == 0:
|
||
re_data = res.get('message')
|
||
except:
|
||
pass
|
||
|
||
for i in range(len(data_list)):
|
||
data_list[i] = self.get_project_stat(data_list[i])
|
||
|
||
data_list[i]['re_total'] = 0
|
||
if re_data and data_list[i]['name'] in re_data:
|
||
try:
|
||
data_list[i]['re_total'] = re_data[data_list[i]['name']]['total']['request']
|
||
except:
|
||
pass
|
||
|
||
if re_order:
|
||
is_reverse = (re_order == 'desc')
|
||
data_list = sorted(data_list, key=lambda x: x.get('re_total', 0), reverse=is_reverse)
|
||
|
||
count = len(data_list)
|
||
page_info = public.get_page(count, p, limit, get.callback)
|
||
|
||
start = (p - 1) * limit
|
||
end = start + limit
|
||
paged_data = data_list[start:end]
|
||
|
||
result = {
|
||
'data': paged_data,
|
||
'page': page_info['page']
|
||
}
|
||
|
||
return self.return_result(get, 0, result)
|
||
|
||
# 设置批量网站到期时间
|
||
def set_site_etime_multiple(self, get):
|
||
'''
|
||
@name 批量网站到期时间
|
||
@param sites_id "1,2"
|
||
@param edate 2023-11-30
|
||
'''
|
||
if not hasattr(get, 'sites_id'):
|
||
return self.return_result(get,-1,'sites_id is indispensable!')
|
||
sites_id = get.sites_id.split(',')
|
||
set_edate_successfully = []
|
||
set_edate_failed = {}
|
||
for site_id in sites_id:
|
||
get.id = site_id
|
||
site_name = public.M('sites').where("id=?", (site_id,)).getField('name')
|
||
if not site_name:
|
||
continue
|
||
|
||
if hasattr(get, 'edate') and get.edate != '':
|
||
public.M('sites').where('id=?', (get.id,)).setField('edate', get.edate)
|
||
public.WriteLog('TYPE_SITE', 'SITE_EXPIRE_SUCCESS', (site_name, get.edate))
|
||
set_edate_successfully.append(site_name)
|
||
else:
|
||
set_edate_failed[site_name] = 'There was an error during setup, please try again'
|
||
|
||
return self.return_result(get,0,{'status': True, 'msg': 'Successfully set the expiration time for website [{}]'.format(','.join(set_edate_successfully)), 'error': set_edate_failed,
|
||
'success': set_edate_successfully})
|
||
|
||
def project_get_domain(self, get):
|
||
'''
|
||
@name 获取指定项目的域名列表
|
||
@author hwliang<2021-08-09>
|
||
@param get<dict_obj>{
|
||
project_name: string<项目名称>
|
||
}
|
||
@return dict
|
||
'''
|
||
project_id = public.M('sites').where('name=?', (get.project_name,)).getField('id')
|
||
if not project_id:
|
||
return self.return_result(get,-1, 'Site query failed')
|
||
domains = public.M('domain').where('pid=?', (project_id,)).order('id desc').select()
|
||
# project_find = self.get_project_find(get.project_name)
|
||
# if not project_find:
|
||
# return public.return_data(False, '站点查询失败')
|
||
# 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 self.return_result(get,0,domains)
|
||
|
||
def project_remove_domain(self, get):
|
||
'''
|
||
@name 为指定项目删除域名
|
||
@author hwliang<2021-08-09>
|
||
@param get<dict_obj>{
|
||
project_name: string<项目名称>
|
||
domain: string<域名>
|
||
}
|
||
@return dict
|
||
'''
|
||
project_find = self.get_project_find(get.project_name)
|
||
if not project_find:
|
||
return self.return_result(get,-1,'The specified project does not exist')
|
||
last_domain = get.domain
|
||
domain_arr = get.domain.rsplit(':', 1)
|
||
if len(domain_arr) == 1:
|
||
domain_arr.append(80)
|
||
|
||
project_id = public.M('sites').where('name=?', (get.project_name,)).getField('id')
|
||
if len(project_find['project_config']['domains']) == 1: return self.return_result(get,-1,'The project requires at least one domain name')
|
||
domain_id = public.M('domain').where('name=? AND port=? AND pid=?', (domain_arr[0],domain_arr[1], project_id)).getField('id')
|
||
if not domain_id:
|
||
return self.return_result(get,-1,'The specified domain name does not exist')
|
||
public.M('domain').where('id=?', (domain_id,)).delete()
|
||
|
||
if get.domain in project_find['project_config']['domains']:
|
||
project_find['project_config']['domains'].remove(get.domain)
|
||
if get.domain + ":80" in project_find['project_config']['domains']:
|
||
project_find['project_config']['domains'].remove(get.domain + ":80")
|
||
|
||
public.M('sites').where('id=?', (project_id,)).save('project_config', json.dumps(project_find['project_config']))
|
||
public.WriteLog(self._log_name, 'Delete domain name [{}] from project: {}'.format(get.project_name, get.domain))
|
||
self.set_config(get.project_name)
|
||
return self.return_result(get,0, 'Successfully deleted domain name')
|
||
|
||
def project_add_domain(self, get):
|
||
'''
|
||
@name 为指定项目添加域名
|
||
@author hwliang<2021-08-09>
|
||
@param get<dict_obj>{
|
||
project_name: string<项目名称>
|
||
domains: list<域名列表>
|
||
}
|
||
@return dict
|
||
'''
|
||
project_find = self.get_project_find(get.project_name)
|
||
if not project_find:
|
||
return self.return_result(get,-1,'The specified project does not exist')
|
||
project_id = project_find['id']
|
||
domains = get.domains
|
||
check_cloud = False
|
||
flag = False
|
||
res_domains = []
|
||
for domain in domains:
|
||
domain = domain.strip()
|
||
if not domain: continue
|
||
if "[" in domain and "]" in domain: # IPv6格式特殊处理
|
||
if "]:" in domain:
|
||
domain_arr = domain.rsplit(":", 1)
|
||
else:
|
||
domain_arr = [domain]
|
||
else:
|
||
domain_arr = domain.split(':')
|
||
domain_arr[0] = self.check_domain(domain_arr[0])
|
||
if domain_arr[0] is False:
|
||
res_domains.append({"name": domain, "status": False, "msg": 'Domain name format error'})
|
||
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": 'Domain name format error'})
|
||
continue
|
||
except ValueError:
|
||
res_domains.append({"name": domain, "status": False, "msg": 'Domain name format error'})
|
||
continue
|
||
if int(domain_arr[1]) == 443:
|
||
res_domains.append({"name": domain, "status": False, "msg": 'Port 443 is not allowed for Go projects'})
|
||
continue
|
||
|
||
if not public.M('domain').where('name=? and port=?', (domain_arr[0], domain_arr[1])).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, 'Successfully added domain [{}] to project [{}]'.format(domain, get.project_name))
|
||
res_domains.append({"name": domain_arr[0], "status": True, "msg": 'Added successfully'})
|
||
if not check_cloud:
|
||
public.check_domain_cloud(domain_arr[0])
|
||
check_cloud = True
|
||
flag = True
|
||
else:
|
||
public.WriteLog(self._log_name, 'Adding domain name error, domain name [{}] already exists'.format(domain))
|
||
res_domains.append({"name": domain_arr[0], "status": False, "msg": 'Add error, domain name [{}] already exists'.format(domain)})
|
||
if flag:
|
||
public.M('sites').where('id=?', (project_id,)).save('project_config', json.dumps(project_find['project_config']))
|
||
self.set_config(get.project_name)
|
||
|
||
return self.return_result(get,0,self._ckeck_add_domain(get.project_name, res_domains))
|
||
|
||
def get_project_info(self, get):
|
||
'''
|
||
@name 获取指定项目信息
|
||
@author hwliang<2021-08-09>
|
||
@param get<dict_obj>{
|
||
project_name: string<项目名称>
|
||
}
|
||
@return dict
|
||
'''
|
||
# get.project_name=get.get('project_name','').strip()
|
||
project_info = public.M('sites').where('project_type=? AND name=?', ('Go', get.project_name)).find()
|
||
if not project_info: return self.return_result(get,-1,'The specified project does not exist!')
|
||
has_load_info = False
|
||
if "has_load_info" in get and get.has_load_info in (1, "1", "ture", "Ture"):
|
||
has_load_info = True
|
||
project_info = self.get_project_stat(project_info, has_load_info)
|
||
project_info['project_config']['project_log'] = int(project_info['project_config'].get('project_log', 1))
|
||
ps = panelSite.panelSite()
|
||
web_log = 0
|
||
try:
|
||
res = ps.GetLogsStatus(public.to_dict_obj({'name': project_info['name']}))
|
||
if res:
|
||
web_log = 1
|
||
except:
|
||
pass
|
||
project_info['project_config']['web_log'] = web_log
|
||
return self.return_result(get,0,project_info)
|
||
|
||
def project_logs(self, get):
|
||
id = get.id
|
||
status = get.status
|
||
web_info = public.M('sites').where('id=?', (id,)).getField('project_config')
|
||
web_info = json.loads(web_info)
|
||
if int(status) != int(web_info.get('project_log',1)):
|
||
web_info['project_log'] = status
|
||
public.M('sites').where('id=?', (id,)).setField('project_config', json.dumps(web_info))
|
||
return self.return_result(get,0, 'Setting successful, please manually restart the project for the configuration to take effect')
|
||
|
||
def create_project(self, get):
|
||
'''
|
||
@name 创建新的项目
|
||
@author hwliang<2021-08-09>
|
||
@param get<dict_obj>{
|
||
project_name: string<项目名称>
|
||
project_exe: string<项目可执行文件>
|
||
project_ps: string<项目备注信息>
|
||
bind_extranet: int<是否绑定外网> 1:是 0:否
|
||
domains: list<域名列表> ["domain1:80","domain2:80"] // 在bind_extranet=1时,需要填写
|
||
is_power_on: int<是否开机启动> 1:是 0:否
|
||
run_user: string<运行用户>
|
||
project_cmd: string<项目执行的命令>
|
||
port 端口号
|
||
}
|
||
@return dict
|
||
'''
|
||
# project_name = get.project_name.strip()
|
||
project_name=get.get("project_name", "").strip()
|
||
if not re.match("^\w+$", project_name):
|
||
return self.return_result(get,-1,'The format of the project name is incorrect. It supports letters, numbers, underscores, and expressions: ^[0-9A-Za-z_]$')
|
||
public.set_module_logs("create_go_project", "create")
|
||
if public.M('sites').where('name=?', (get.project_name,)).count():
|
||
return self.return_result(get,-1,'The specified project name already exists: {}'.format(get.project_name))
|
||
get.project_exe = get.project_exe.strip()
|
||
if not os.path.exists(get.project_exe):
|
||
return self.return_result(get,-1,'The project directory does not exist: {}'.format(get.project_exe))
|
||
|
||
# 端口占用检测
|
||
try:
|
||
ports = int(get.port)
|
||
if ports < 10 and ports > 65535:
|
||
return self.return_result(get,-1,'The port number is invalid. Please enter a number between 10 and 65535')
|
||
except:
|
||
return self.return_result(get,-1,'The port number is invalid. Please enter a number between 10 and 65535')
|
||
if self.check_port_is_used(get.port):
|
||
return self.return_result(get,-1,'The specified port is already occupied by another application. Please modify your project configuration to use a different port: {}'.format(get.port))
|
||
# if 'domains' in get:
|
||
# domains = json.loads(get.domains)
|
||
# if len(domains)>1:
|
||
# get.bind_extranet=1
|
||
# else:
|
||
# get.bind_extranet=0
|
||
# else:
|
||
# get.domains=[]
|
||
# get.bind_extranet=0
|
||
|
||
domains = []
|
||
if get.bind_extranet == 1:
|
||
domains = get.domains
|
||
public.check_domain_cloud(domains[0])
|
||
for domain in domains:
|
||
if "[" in domain and "]" in domain: # IPv6格式特殊处理
|
||
if "]:" in domain:
|
||
domain_arr = domain.rsplit(":", 1)
|
||
else:
|
||
domain_arr = [domain]
|
||
else:
|
||
domain_arr = domain.split(':')
|
||
domain_arr[0] = self.check_domain(domain_arr[0])
|
||
if domain_arr[0] is False:
|
||
return self.return_result(get,-1,'Domain name format error: {}'.format(domain))
|
||
if len(domain_arr) == 1:
|
||
domain_arr.append("")
|
||
if domain_arr[1] == "":
|
||
domain_arr[1] = 80
|
||
domain += ':80'
|
||
if int(domain_arr[1]) == 443:
|
||
return self.return_result(get,-1,'Port 443 is not allowed for Go projects')
|
||
if public.M('domain').where('name=? AND port=?', (domain_arr[0], domain_arr[1])).count():
|
||
return self.return_result(get,-1,'The specified domain name already exists: {}'.format(domain))
|
||
|
||
if hasattr(get, "env_file") and get.env_file.strip():
|
||
env_file = get.env_file.strip()
|
||
if not os.path.exists(get.env_file):
|
||
return self.return_result(get,-1,'The environment variable file does not exist: {}'.format(get.env_file))
|
||
else:
|
||
env_file = ''
|
||
|
||
if hasattr(get, "env_list") and get.env_list:
|
||
if isinstance(get.env_list, str):
|
||
try:
|
||
env_list = json.loads(get.env_list)
|
||
except:
|
||
return self.return_result(get,-1,'Incorrect format of environment variables')
|
||
else:
|
||
env_list = get.env_list
|
||
|
||
if not isinstance(env_list, list):
|
||
return self.return_result(get,-1,'Incorrect format of environment variables')
|
||
|
||
else:
|
||
env_list = []
|
||
|
||
if not 'project_cmd' in get:
|
||
get.project_cmd = get.project_exe
|
||
# 获取可执行文件的的根目录
|
||
project_path = os.path.dirname(get.project_exe)
|
||
pdata = {
|
||
'name': get.project_name,
|
||
'path': project_path,
|
||
'ps': get.project_ps,
|
||
'status': 1,
|
||
'type_id': 0,
|
||
'project_type': 'Go',
|
||
'project_config': json.dumps(
|
||
{
|
||
'ssl_path': '/www/wwwroot/java_node_ssl',
|
||
'project_name': get.project_name,
|
||
'project_exe': get.project_exe,
|
||
'bind_extranet': get.bind_extranet,
|
||
'domains': [],
|
||
'project_cmd': get.project_cmd,
|
||
'is_power_on': get.is_power_on,
|
||
'run_user': get.run_user,
|
||
'port': int(get.port),
|
||
'project_path': project_path,
|
||
'log_path': self._go_logs_path,
|
||
'porject_log': get.get('porject_log', 1),
|
||
'web_log': get.get('web_log', 1),
|
||
'env_file': env_file,
|
||
'env_list': env_list,
|
||
}
|
||
),
|
||
'addtime': public.getDate()
|
||
}
|
||
project_id = public.M('sites').insert(pdata)
|
||
if get.bind_extranet == 1:
|
||
format_domains = []
|
||
for domain in domains:
|
||
if domain.find(':') == -1: domain += ':80'
|
||
format_domains.append(domain)
|
||
get.domains = format_domains
|
||
self.project_add_domain(get)
|
||
self.set_config(get.project_name)
|
||
public.WriteLog(self._log_name, 'Add Go project [{}]'.format(get.project_name))
|
||
self.start_project(get)
|
||
flag, tip = self._release_firewall(get)
|
||
msg = 'Project added successfully' + ("" if flag else "<br>" + tip)
|
||
return self.return_result(get,0,{"data":msg,"project_id":project_id})
|
||
# return public.return_data(True, msg, project_id)
|
||
|
||
def modify_project(self, get):
|
||
'''
|
||
@name 修改指定项目
|
||
@author hwliang<2021-08-09>
|
||
@param get<dict_obj>{
|
||
project_name: string<项目名称>
|
||
project_exe: string<项目可执行文件> 可以修改
|
||
project_ps: string<项目备注信息> 可以修改
|
||
is_power_on: int<是否开机启动> 1:是 0:否 可以修改
|
||
run_user: string<运行用户> 可以修改
|
||
project_cmd: string<项目执行的命令> 可以修改
|
||
port 端口号 可以修改端口
|
||
}
|
||
@return dict
|
||
'''
|
||
project_find = self.get_project_find(get.project_name)
|
||
if not project_find:
|
||
return self.return_result(get,-1,'The specified project does not exist: {}'.format(get.project_name))
|
||
|
||
if not os.path.exists(get.project_exe):
|
||
return self.return_result(get,-1,'The project directory does not exist: {}'.format(get.project_exe))
|
||
|
||
if hasattr(get, 'port'):
|
||
if int(project_find['project_config']['port']) != int(get.port):
|
||
if self.check_port_is_used(get.get('port/port'), True):
|
||
return self.return_result(get,-1,'The specified port is already occupied by another application. Please modify your project configuration to use a different port: {}'.format(get.port))
|
||
project_find['project_config']['port'] = int(get.port)
|
||
# if hasattr(get,'project_cwd'): project_find['project_config']['project_cwd'] = get.project_cwd.strip()
|
||
if hasattr(get, 'project_exe'): project_find['project_config']['project_exe'] = get.project_exe.strip()
|
||
if hasattr(get, 'is_power_on'): project_find['project_config']['is_power_on'] = get.is_power_on
|
||
if hasattr(get, 'run_user'): project_find['project_config']['run_user'] = get.run_user.strip()
|
||
if hasattr(get, 'project_cmd'): project_find['project_config']['project_cmd'] = get.project_cmd.strip()
|
||
if hasattr(get, 'web_log'): project_find['project_config']['web_log'] = get.web_log
|
||
if hasattr(get, 'project_log'): project_find['project_config']['project_log'] = get.project_log
|
||
|
||
if hasattr(get, "env_file") and get.env_file.strip():
|
||
env_file = get.env_file.strip()
|
||
if not os.path.exists(get.env_file):
|
||
return self.return_result(get,-1,'The environment variable file does not exist: {}'.format(get.env_file))
|
||
project_find['project_config']['env_file'] = env_file
|
||
|
||
if hasattr(get, "env_list") and get.env_list:
|
||
if isinstance(get.env_list, str):
|
||
try:
|
||
env_list = json.loads(get.env_list)
|
||
except:
|
||
return self.return_result(get,-1,'Incorrect format of environment variables')
|
||
else:
|
||
env_list = get.env_list
|
||
|
||
if not isinstance(env_list, list):
|
||
return self.return_result(get,-1,'Incorrect format of environment variables')
|
||
project_find['project_config']['env_list'] = env_list
|
||
project_path = os.path.dirname(get.project_exe)
|
||
pdata = {
|
||
'path': project_path,
|
||
'ps': get.project_ps,
|
||
'project_config': json.dumps(project_find['project_config'])
|
||
}
|
||
|
||
public.M('sites').where('name=?', (get.project_name,)).update(pdata)
|
||
self.set_config(get.project_name)
|
||
try:
|
||
ps = panelSite.panelSite()
|
||
res = ps.GetLogsStatus(public.to_dict_obj({'name': get.project_name}))
|
||
# 如果日志状态与配置状态不一致,则修改日志状态
|
||
if res != bool(int(project_find['project_config']['web_log'])):
|
||
ps.logsOpen(public.to_dict_obj({'id': project_find['id']}))
|
||
except:
|
||
pass
|
||
|
||
public.WriteLog(self._log_name, 'Modify Go project [{}]'.format(get.project_name))
|
||
# 重启项目
|
||
self.stop_project(get)
|
||
self.start_project(get)
|
||
return self.return_result(get,0, 'Successfully modified the project and restarted')
|
||
|
||
def remove_project(self, get):
|
||
'''
|
||
@name 删除指定项目
|
||
@author hwliang<2021-08-09>
|
||
@param get<dict_obj>{
|
||
project_name: string<项目名称>
|
||
}
|
||
@return dict
|
||
'''
|
||
project_find = self.get_project_find(get.project_name)
|
||
if not project_find:
|
||
return self.return_result(get,-1,'The specified project does not exist: {}'.format(get.project_name))
|
||
|
||
self.stop_project(get)
|
||
self.clear_config(get.project_name)
|
||
public.M('domain').where('pid=?', (project_find['id'],)).delete()
|
||
public.M('sites').where('name=?', (get.project_name,)).delete()
|
||
pid_file = "{}/{}.pid".format(self._go_pid_path, get.project_name)
|
||
if os.path.exists(pid_file): os.remove(pid_file)
|
||
script_file = '{}/{}.sh'.format(self._go_run_scripts, get.project_name)
|
||
if os.path.exists(script_file): os.remove(script_file)
|
||
if "log_path" not in project_find['project_config']:
|
||
log_file = "{}/{}.log".format(self._go_logs, project_find["name"])
|
||
else:
|
||
log_file = "{}/{}.log".format(project_find['project_config']["log_path"], project_find["name"])
|
||
if os.path.exists(log_file): os.remove(log_file)
|
||
self.del_crontab(get.project_name.strip())
|
||
from mod.base.web_conf import remove_sites_service_config
|
||
remove_sites_service_config(get.project_name, "go_")
|
||
public.WriteLog(self._log_name, 'Delete Go project [{}]'.format(get.project_name))
|
||
return self.return_result(get,0, 'Project deleted successfully')
|
||
|
||
# xss 防御
|
||
def xsssec(self, text):
|
||
return text.replace('<', '<').replace('>', '>')
|
||
|
||
def last_lines(self, filename, lines=1):
|
||
block_size = 3145928
|
||
block = ''
|
||
nl_count = 0
|
||
start = 0
|
||
fsock = open(filename, 'r')
|
||
try:
|
||
fsock.seek(0, 2)
|
||
curpos = fsock.tell()
|
||
while (curpos > 0):
|
||
curpos -= (block_size + len(block))
|
||
if curpos < 0: curpos = 0
|
||
fsock.seek(curpos)
|
||
try:
|
||
block = fsock.read()
|
||
except:
|
||
continue
|
||
nl_count = block.count('\n')
|
||
if nl_count >= lines: break
|
||
for n in range(nl_count - lines + 1):
|
||
start = block.find('\n', start) + 1
|
||
finally:
|
||
fsock.close()
|
||
return block[start:]
|
||
|
||
def get_project_log(self, get):
|
||
'''
|
||
@name 取项目日志
|
||
@author lkq<2021-08-27>
|
||
@param domain 域名
|
||
@param project_name 项目名称
|
||
@return string
|
||
'''
|
||
project_info = self.get_project_find(get.project_name.strip())
|
||
if not project_info: return self.return_result(get,-1, 'The project does not exist.')
|
||
if "log_path" not in project_info['project_config']:
|
||
log_file = "{}/{}.log".format(self._go_logs, project_info["name"])
|
||
else:
|
||
log_file = "{}/{}.log".format(project_info['project_config']["log_path"], project_info["name"])
|
||
if not os.path.exists(log_file): return self.return_result(get,-1, 'The log file does not exist')
|
||
log_file_size = os.path.getsize(log_file)
|
||
res = {
|
||
"status": True,
|
||
"size": public.to_size(log_file_size),
|
||
"path": log_file.rsplit("/", 1)[0],
|
||
"data": ""
|
||
}
|
||
if log_file_size > 3145928:
|
||
res["data"] = self.xsssec(self.last_lines(log_file, 3000))
|
||
else:
|
||
res["data"] = self.xsssec(public.GetNumLines(log_file, 3000))
|
||
return self.return_result(get,0,res)
|
||
|
||
def auto_run(self):
|
||
'''
|
||
@name 开机自动启动
|
||
'''
|
||
# 获取数据库信息
|
||
project_list = public.M('sites').where('project_type=?', ('Go',)).field('name,path,project_config').select()
|
||
get = public.dict_obj()
|
||
success_count = 0
|
||
error_count = 0
|
||
for project_find in project_list:
|
||
project_config = json.loads(project_find['project_config'])
|
||
if project_config['is_power_on'] in [0, False, '0', None]: continue
|
||
project_name = project_find['name']
|
||
project_state = self.get_project_run_state(project_name=project_name)
|
||
if not project_state:
|
||
get.project_name = project_name
|
||
result = self.start_project(get)
|
||
if not result['status']:
|
||
error_count += 1
|
||
error_msg = 'Automatically start Nodej project [' + project_name + '] failure!'
|
||
public.WriteLog(self._log_name, error_msg)
|
||
else:
|
||
success_count += 1
|
||
success_msg = 'Automatically start Go project [' + project_name + '] success!'
|
||
public.WriteLog(self._log_name, success_msg)
|
||
if (success_count + error_count) < 1: return False
|
||
dene_msg = 'A total of {} Go projects need to be launched, with {} successful and {} failed'.format(success_count + error_count, success_count, error_count)
|
||
public.WriteLog(self._log_name, dene_msg)
|
||
return True
|
||
|
||
def change_log_path(self, get):
|
||
""""修改日志文件地址
|
||
@author baozi <202-03-13>
|
||
@param:
|
||
get ( dict ): 请求: 包含项目名称和新的路径
|
||
@return
|
||
"""
|
||
project_info = self.get_project_find(get.project_name.strip())
|
||
if not project_info: return self.return_result(get,-1, 'The project does not exist.')
|
||
new_log_path = get.path.strip() if "path" in get else None
|
||
if not new_log_path or new_log_path[0] != "/":
|
||
return self.return_result(get,-1, "Path setting error")
|
||
if new_log_path[-1] == "/": new_log_path = new_log_path[:-1]
|
||
if not os.path.exists(new_log_path):
|
||
os.makedirs(new_log_path, mode=0o777)
|
||
project_info['project_config']['log_path'] = new_log_path
|
||
pdata = {
|
||
'name': project_info["name"],
|
||
'project_config': json.dumps(project_info['project_config'])
|
||
}
|
||
public.M('sites').where('name=?', (get.project_name.strip(),)).update(pdata)
|
||
# 重启项目
|
||
# return self.restart_project(get)
|
||
res = self.stop_project(get)
|
||
res = self.start_project(get)
|
||
public.WriteLog(self._log_name, 'GO project [{}], successfully modified log path'.format(get.project_name))
|
||
return self.return_result(get,0, "The project log path has been successfully modified")
|
||
|
||
def for_split(self, logsplit, project):
|
||
"""日志切割方法调用
|
||
@author baozi <202-03-20>
|
||
@param:
|
||
logsplit ( LogSplit ): 日志切割方法,传入 pjanme:项目名称 sfile:日志文件路径 log_prefix:产生的日志文件前缀
|
||
project ( dict ): 项目内容
|
||
@return
|
||
"""
|
||
log_file = "{}/{}.log".format(project['project_config']["log_path"], project["name"])
|
||
logsplit(project["name"], log_file, project["name"])
|
||
|
||
# —————————————
|
||
# 日志切割 |
|
||
# —————————————
|
||
def del_crontab(self, name):
|
||
"""
|
||
@name 删除项目日志切割任务
|
||
@auther hezhihong<2022-10-31>
|
||
@return
|
||
"""
|
||
cron_name = f'[Do not delete] GO project [{name}] run log cutting'
|
||
cron_path = public.GetConfigValue('setup_path') + '/cron/'
|
||
cron_list = public.M('crontab').where("name=?", (cron_name,)).select()
|
||
if cron_list:
|
||
for i in cron_list:
|
||
if not i: continue
|
||
cron_echo = public.M('crontab').where("id=?", (i['id'],)).getField('echo')
|
||
args = {"id": i['id']}
|
||
import crontab
|
||
crontab.crontab().DelCrontab(args)
|
||
del_cron_file = cron_path + cron_echo
|
||
public.ExecShell("crontab -u root -l| grep -v '{}'|crontab -u root -".format(del_cron_file))
|
||
|
||
def add_crontab(self, name, log_conf, python_path):
|
||
"""
|
||
@name 构造站点运行日志切割任务
|
||
"""
|
||
cron_name = f'[Do not delete] GO project [{name}] run log cutting'
|
||
if not public.M('crontab').where('name=?', (cron_name,)).count():
|
||
cmd = '{pyenv} {script_path} {name}'.format(
|
||
pyenv=python_path,
|
||
script_path=self.__log_split_script_py,
|
||
name=name
|
||
)
|
||
args = {
|
||
"name": cron_name,
|
||
"type": 'day' if log_conf["log_size"] == 0 else "minute-n",
|
||
"where1": "" if log_conf["log_size"] == 0 else log_conf["minute"],
|
||
"hour": log_conf["hour"],
|
||
"minute": log_conf["minute"],
|
||
"sName": name,
|
||
"sType": 'toShell',
|
||
"notice": '0',
|
||
"notice_channel": '',
|
||
"save": str(log_conf["num"]),
|
||
"save_local": '1',
|
||
"backupTo": '',
|
||
"sBody": cmd,
|
||
"urladdress": ''
|
||
}
|
||
import crontab
|
||
res = crontab.crontab().AddCrontab(args)
|
||
if res and "id" in res.keys():
|
||
return True, "New task successfully created"
|
||
return False, res["msg"]
|
||
return True
|
||
|
||
def change_cronta(self, name, log_conf):
|
||
"""
|
||
@name 更改站点运行日志切割任务
|
||
"""
|
||
python_path = "/www/server/panel/pyenv/bin/python3"
|
||
if not python_path: return False
|
||
cronInfo = public.M('crontab').where('name=?', (f'[Do not delete] GO project [{name}] run log cutting',)).find()
|
||
if not cronInfo:
|
||
return self.add_crontab(name, log_conf, python_path)
|
||
import crontab
|
||
recrontabMode = crontab.crontab()
|
||
id = cronInfo['id']
|
||
del (cronInfo['id'])
|
||
del (cronInfo['addtime'])
|
||
cronInfo['sBody'] = '{pyenv} {script_path} {name}'.format(
|
||
pyenv=python_path,
|
||
script_path=self.__log_split_script_py,
|
||
name=name
|
||
)
|
||
cronInfo['where_hour'] = log_conf['hour']
|
||
cronInfo['where_minute'] = log_conf['minute']
|
||
cronInfo['save'] = log_conf['num']
|
||
cronInfo['type'] = 'day' if log_conf["log_size"] == 0 else "minute-n"
|
||
cronInfo['where1'] = '' if log_conf["log_size"] == 0 else log_conf['minute']
|
||
|
||
columns = 'where_hour,where_minute,sBody,save,type,where1'
|
||
values = (cronInfo['where_hour'], cronInfo['where_minute'], cronInfo['sBody'], cronInfo['save'], cronInfo['type'], cronInfo['where1'])
|
||
recrontabMode.remove_for_crond(cronInfo['echo'])
|
||
if cronInfo['status'] == 0: return False, 'The current task is in a stopped state. Please enable the task before making any changes!'
|
||
sync_res=recrontabMode.sync_to_crond(cronInfo)
|
||
if not sync_res:
|
||
return False,"crontab task synchronization failed"
|
||
# if not recrontabMode.sync_to_crond(cronInfo)['status']:
|
||
# return False, '写入计划任务失败,请检查磁盘是否可写或是否开启了系统加固!'
|
||
public.M('crontab').where('id=?', (id,)).save(columns, values)
|
||
public.WriteLog('crontab task', 'Plan Task Modification Plan Task [' + cronInfo['name'] + '] successful')
|
||
return True, 'Modified successfully'
|
||
|
||
def mamger_log_split(self, get):
|
||
"""管理日志切割任务
|
||
@author baozi <202-02-27>
|
||
@param:
|
||
get ( dict ): 包含name, mode, hour, minute
|
||
@return
|
||
"""
|
||
name = get.name.strip()
|
||
project = self.get_project_find(name)
|
||
if not project:
|
||
return self.return_result(get,-1, "There is no such project, please try refreshing the page")
|
||
try:
|
||
_log_size = float(get.log_size) if float(get.log_size) >= 0 else 0
|
||
_hour = get.hour.strip() if 0 <= int(get.hour) < 24 else "2"
|
||
_minute = get.minute.strip() if 0 <= int(get.minute) < 60 else '0'
|
||
_num = int(get.num) if 0 < int(get.num) <= 1800 else 180
|
||
_compress = int(get.compress) == 1
|
||
except (ValueError, AttributeError, KeyError):
|
||
_log_size = 0
|
||
_hour = "2"
|
||
_minute = "0"
|
||
_num = 180
|
||
_compress = False
|
||
|
||
if _log_size != 0:
|
||
_log_size = _log_size * 1024 * 1024
|
||
_hour = 0
|
||
_minute = 5
|
||
|
||
log_conf = {
|
||
"log_size": _log_size,
|
||
"hour": _hour,
|
||
"minute": _minute,
|
||
"num": _num,
|
||
"compress": _compress
|
||
}
|
||
flag, msg = self.change_cronta(name, log_conf)
|
||
if flag:
|
||
conf_path = '{}/data/run_log_split.conf'.format(public.get_panel_path())
|
||
if os.path.exists(conf_path):
|
||
try:
|
||
data = json.loads(public.readFile(conf_path))
|
||
except:
|
||
data = {}
|
||
else:
|
||
data = {}
|
||
data[name] = {
|
||
"stype": "size" if bool(_log_size) else "day",
|
||
"log_size": _log_size,
|
||
"limit": _num,
|
||
"compress": _compress,
|
||
}
|
||
public.writeFile(conf_path, json.dumps(data))
|
||
project["project_config"]["log_conf"] = log_conf
|
||
pdata = {
|
||
"project_config": json.dumps(project["project_config"])
|
||
}
|
||
public.M('sites').where('name=?', (name,)).update(pdata)
|
||
return self.return_result(get,0,{"flag":flag,"msg":msg})
|
||
# return public.returnMsg(flag, msg)
|
||
|
||
def set_log_split(self, get):
|
||
"""设置日志计划任务状态
|
||
@author baozi <202-02-27>
|
||
@param:
|
||
get ( dict ): 包含项目名称name
|
||
@return msg : 操作结果
|
||
"""
|
||
name = get.name.strip()
|
||
project_conf = self.get_project_find(name)
|
||
if not project_conf:
|
||
return self.return_result(get,-1, "There is no such project, please try refreshing the page")
|
||
cronInfo = public.M('crontab').where('name=?', (f'[Do not delete] GO project [{name}] run log cutting',)).find()
|
||
if not cronInfo:
|
||
return self.return_result(get,-1, "This project does not have a cutting task for running logs set up")
|
||
|
||
status_msg = ['deactivate', 'enable']
|
||
status = 1
|
||
import crontab
|
||
recrontabMode = crontab.crontab()
|
||
|
||
if cronInfo['status'] == status:
|
||
status = 0
|
||
recrontabMode.remove_for_crond(cronInfo['echo'])
|
||
else:
|
||
cronInfo['status'] = 1
|
||
sync_res=recrontabMode.sync_to_crond(cronInfo)
|
||
if not sync_res:
|
||
return self.return_result(get,-1, "crontab task synchronization failed")
|
||
|
||
public.M('crontab').where('id=?', (cronInfo["id"],)).setField('status', status)
|
||
public.WriteLog('crontab task', 'Modify the plan task [' + cronInfo['name'] + '] status is [' + status_msg[status] + ']')
|
||
return self.return_result(get,0, 'Setup successful')
|
||
|
||
def get_log_split(self, get):
|
||
"""获取站点的日志切割任务
|
||
@author baozi <202-02-27>
|
||
@param:
|
||
get ( dict ): name
|
||
@return msg : 操作结果
|
||
"""
|
||
|
||
name = get.name.strip()
|
||
project_conf = self.get_project_find(name)
|
||
if not project_conf:
|
||
return self.return_result(get,-1, "There is no such project, please try refreshing the page")
|
||
if self._check_old(project_conf):
|
||
return {"status": False, "msg": "After updating the version, it is necessary to restart the project in order to start the log cutting task. We suggest that you find a suitable time to restart the project", "is_old": True}
|
||
cronInfo = public.M('crontab').where('name=?', (f'[Do not delete] GO project [{name}] run log cutting',)).find()
|
||
if not cronInfo:
|
||
return self.return_result(get,-1, "This project does not have a cutting task for running logs set up")
|
||
|
||
if "log_conf" not in project_conf["project_config"]:
|
||
return self.return_result(get,-1, "Log cutting configuration is missing, please try resetting it")
|
||
res = project_conf["project_config"]["log_conf"]
|
||
res["status"] = cronInfo["status"]
|
||
return self.return_result(get,0,res)
|
||
|
||
def _update_project(self, project_name, project_info):
|
||
# 检查是否需要更新
|
||
# 移动日志文件
|
||
# 保存
|
||
target_file = self._go_logs_path + "/" + project_name + ".log"
|
||
if "log_path" in project_info['project_config']:
|
||
return
|
||
log_file = "{}/{}.log".format(self._go_logs, project_name)
|
||
|
||
if os.path.exists(log_file):
|
||
self._move_logs(log_file, target_file)
|
||
if not os.path.exists(target_file):
|
||
return
|
||
else:
|
||
os.remove(log_file)
|
||
|
||
project_info['project_config']["log_path"] = self._go_logs_path
|
||
pdata = {
|
||
'name': project_name,
|
||
'project_config': json.dumps(project_info['project_config'])
|
||
}
|
||
public.M('sites').where('name=?', (project_name,)).update(pdata)
|
||
|
||
def _move_logs(self, s_file, target_file):
|
||
if os.path.getsize(s_file) > 3145928:
|
||
res = self.last_lines(s_file, 3000)
|
||
public.WriteFile(target_file, res)
|
||
else:
|
||
shutil.copyfile(s_file, target_file)
|
||
|
||
def _check_old(self, project_info):
|
||
if not "log_path" in project_info['project_config']:
|
||
return True
|
||
|
||
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"] or not ssl_data.get("cert_data", {}).get("dns", None):
|
||
return {"domains": domains}
|
||
domain_rep = []
|
||
for i in ssl_data["cert_data"]["dns"]:
|
||
if i.startswith("*"):
|
||
_rep = "^[^\.]+\." + i[2:].replace(".", "\.")
|
||
else:
|
||
_rep = "^" + i.replace(".", "\.")
|
||
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 {
|
||
"domains": domains,
|
||
"not_ssl": no_ssl,
|
||
"tip": "This site has enabled SSL certificate, but the domain name added this time is {}, which cannot match the current certificate. If needed, please reapply for the certificate.".format(str(no_ssl))
|
||
}
|
||
return {"domains": domains}
|
||
|
||
def _init_gvm(self) -> None:
|
||
gvm_path = "/usr/bin/pygvm"
|
||
try:
|
||
if not os.path.exists(gvm_path):
|
||
os.symlink('{}/class_v2/projectModelV2/aapanelpygvm.py'.format(self._panel_path), gvm_path)
|
||
os.chmod(gvm_path, mode=0o755)
|
||
except Exception:
|
||
pass
|
||
|
||
@staticmethod
|
||
def _serializer_of_list(s: list, h: list, installed: List[str]) -> List[Dict]:
|
||
return [{
|
||
"version": v.version,
|
||
"type": "stable",
|
||
"installed": True if v.version in installed else False
|
||
} for v in s] + [{
|
||
"version": v.version,
|
||
"type": "history",
|
||
"installed": True if v.version in installed else False
|
||
} for v in h]
|
||
|
||
def list_go_sdk(self, get: public.dict_obj) -> Dict:
|
||
"""
|
||
获取已安装的sdk,可安装的sdk
|
||
"""
|
||
if isinstance(pygvm, str):
|
||
return self.return_result(get,-1, pygvm)
|
||
if "force" in get and get.force == "true":
|
||
pygvm.cmd_clean_cache()
|
||
installed: List[str] = pygvm.api_ls()
|
||
installed.sort(key=lambda x: int(x.split(".")[1]), reverse=True)
|
||
sdk = {"all": [], "streamline": []}
|
||
all_s, all_h, errmsg = pygvm.api_ls_remote(True)
|
||
streamline_s, streamline_h, errmsg = pygvm.api_ls_remote(False)
|
||
if errmsg:
|
||
return self.return_result(get,-1, errmsg)
|
||
|
||
sdk["all"] = self._serializer_of_list(all_s, all_h, installed)
|
||
sdk["streamline"] = self._serializer_of_list(streamline_s, streamline_h, installed)
|
||
|
||
pygvm.get_now_version()
|
||
goproxy = {
|
||
"now": pygvm.get_goproxy(),
|
||
"list": [
|
||
{"name": "qiniuyun", "proxy": "https://goproxy.cn,direct"},
|
||
{"name": "official", "proxy": "https://goproxy.io,direct"},
|
||
{"name": "aliyun", "proxy": "https://mirrors.aliyun.com/goproxy,direct"},
|
||
{"name": "huaweicloud", "proxy": "https://mirrors.huaweicloud.com/repository/goproxy,direct"}
|
||
]
|
||
}
|
||
installing = public.M('tasks').where("status in (-1, 0) and name like 'Install [GO%'", ()).field("name").select()
|
||
in_vers = []
|
||
for i in installing:
|
||
in_vers.append(self._parser_version(i["name"]))
|
||
|
||
for s in sdk["all"]:
|
||
s["is_install"] = s["version"] in in_vers
|
||
|
||
for s in sdk["streamline"]:
|
||
s["is_install"] = s["version"] in in_vers
|
||
|
||
return self.return_result(get,0,{"status": True, "installed": installed, "sdk": sdk, "used": pygvm.now_version, "goproxy":goproxy})
|
||
|
||
@staticmethod
|
||
def set_goproxy(get):
|
||
if "proxy" not in get or not get.proxy:
|
||
|
||
return public.return_message(-1,0, "Please specify the proxy address")
|
||
res = pygvm.set_goproxy(get.proxy)
|
||
if not res:
|
||
return public.return_message(-1,0,"Proxy setting failed. If go is not installed, please install it first")
|
||
return public.return_message(0,0,"Successfully set up proxy")
|
||
|
||
def set_go_environment(self, get):
|
||
get.version = get.name
|
||
return self.use_go_sdk(get)
|
||
|
||
@staticmethod
|
||
def _parser_version(version: str) -> Optional[str]:
|
||
v_rep = r"(?P<target>\d\.\d{1,2}(\.\d{1,2})?)"
|
||
v_res = re.search(v_rep, version)
|
||
if v_res:
|
||
return "go" + v_res.group("target")
|
||
|
||
def install_go_sdk(self, get: public.dict_obj) -> Dict:
|
||
"""
|
||
安装一个版本的sdk
|
||
"""
|
||
if isinstance(pygvm, str):
|
||
return self.return_result(get,-1, pygvm)
|
||
if pygvm.check_use() is False:
|
||
return self.return_result(get,-1, "The critical path cannot be modified. Please check if [System hardening] is enabled")
|
||
pygvm.get_now_version()
|
||
version = self._parser_version(getattr(get, "version", ''))
|
||
if version is None:
|
||
return self.return_result(get,-1, "Version parameter information error")
|
||
|
||
log_path = self._go_path + "/vhost/gvm_log.log"
|
||
if os.path.isfile(log_path):
|
||
if os.stat(log_path).st_size > 0:
|
||
return self.return_result(get,-1, "A version is currently being installed, please wait.")
|
||
out_err = open(log_path, "w")
|
||
pygvm.set_std(out_err, out_err)
|
||
flag, msg = pygvm.api_install(version)
|
||
pygvm.set_std(sys.stdout, sys.stderr)
|
||
time.sleep(0.1)
|
||
out_err.seek(0, 0)
|
||
out_err.truncate()
|
||
out_err.close()
|
||
if not flag:
|
||
return self.return_result(get,-1, msg)
|
||
|
||
return self.return_result(get,0, "Installation successful")
|
||
|
||
def install_go_sdk_async(self, get):
|
||
if isinstance(pygvm, str):
|
||
return self.return_result(get,-1, pygvm)
|
||
pygvm.get_now_version()
|
||
version = self._parser_version(getattr(get, "version", ''))
|
||
if version is None:
|
||
return self.return_result(get,-1, "Version parameter information error")
|
||
|
||
python_bin = "{}/pyenv/bin/python3".format(public.get_panel_path())
|
||
shell_str = "{} {}/class_v2/projectModelV2/aapanelpygvm.py install {}".format(
|
||
python_bin, public.get_panel_path(), version
|
||
)
|
||
|
||
if not os.path.exists("/tmp/panelTask.pl"): # 如果当前任务队列并未执行,就把日志清空
|
||
public.writeFile('/tmp/panelExec.log', '')
|
||
soft_name = "GO-" + version[2:]
|
||
task_id = public.M('tasks').add(
|
||
'id,name,type,status,addtime,execstr',
|
||
(None, 'Install [{}]'.format(soft_name), 'execshell', '0', time.strftime('%Y-%m-%d %H:%M:%S'),
|
||
"{{\n{}\n}}".format(shell_str))
|
||
)
|
||
# self._create_install_wait_msg(task_id, version)
|
||
return self.return_result(get,0, "The installation task has been submitted")
|
||
|
||
@staticmethod
|
||
def _create_install_wait_msg(task_id: int, version: str):
|
||
from panel_msg.msg_file import message_mgr
|
||
|
||
file_path = "/tmp/panelExec.log"
|
||
if not os.path.exists(file_path):
|
||
public.writeFile(file_path, "")
|
||
|
||
soft_name = "GO-" + version[2:]
|
||
data = {
|
||
"soft_name": soft_name,
|
||
"install_status": "Waiting for installation:" + soft_name,
|
||
"file_name": file_path,
|
||
"self_type": "soft_install",
|
||
"status": 0,
|
||
"task_id": task_id
|
||
}
|
||
title = "Waiting for installation:" + soft_name
|
||
res = message_mgr.collect_message(title, ["GO-SDK Management", soft_name], data)
|
||
if isinstance(res, str):
|
||
public.WriteLog("Message Box", "Installation information collection failed")
|
||
return None
|
||
return res
|
||
|
||
def uninstall_go_sdk(self, get: public.dict_obj) -> Dict:
|
||
"""
|
||
卸载一个指定版本的sdk
|
||
"""
|
||
if isinstance(pygvm, str):
|
||
return self.return_result(get,-1, pygvm)
|
||
if pygvm.check_use() is False:
|
||
return self.return_result(get,-1, "The critical path cannot be modified. Please check if system hardening is enabled")
|
||
pygvm.get_now_version()
|
||
version = self._parser_version(getattr(get, "version", ''))
|
||
if version is None:
|
||
return self.return_result(get,-1, "Version parameter information error")
|
||
|
||
flag, msg = pygvm.api_uninstall(version)
|
||
if not flag:
|
||
return self.return_result(get,-1, msg)
|
||
|
||
return self.return_result(get,0, "Uninstalling successful")
|
||
|
||
def use_go_sdk(self, get: public.dict_obj) -> Dict:
|
||
"""
|
||
使用一个版本的sdk
|
||
"""
|
||
if isinstance(pygvm, str):
|
||
return self.return_result(get,-1, pygvm)
|
||
if pygvm.check_use() is False:
|
||
return self.return_result(get,-1, "The critical path cannot be modified. Please check if system hardening is enabled")
|
||
pygvm.get_now_version()
|
||
version = self._parser_version(getattr(get, "version", ''))
|
||
if version is None:
|
||
return self.return_result(get,-1, "Version parameter information error")
|
||
|
||
flag, msg = pygvm.api_use(version)
|
||
if not flag:
|
||
return self.return_result(get,-1, msg)
|
||
|
||
return self.return_result(get,0, "Switched successfully")
|
||
|
||
def get_project_status(self, project_id):
|
||
# 仅使用在项目停止告警中
|
||
project_info = public.M('sites').where('project_type=? AND id=?', ('Go', project_id)).find()
|
||
if not project_info:
|
||
return None, project_info["name"]
|
||
if self.is_stop_by_user(project_id):
|
||
return True, project_info["name"]
|
||
res = self.get_project_run_state(project_name=project_info['name'])
|
||
return res, project_info["name"]
|