Update to v8.9.0

This commit is contained in:
aapanel.com
2026-05-09 16:02:40 +08:00
parent e08c58909b
commit b674fff397
2734 changed files with 87396 additions and 53302 deletions
+1 -1
View File
@@ -47,7 +47,7 @@ class panelSetup:
if ua.find('spider') != -1 or g.ua.find('bot') != -1:
return abort(403)
g.version = '8.8.0'
g.version = '8.9.0'
g.title = public.GetConfigValue('title')
g.uri = request.path
g.debug = os.path.exists('data/debug.pl')
+4
View File
@@ -348,6 +348,10 @@ def sql_pacth():
if not public.M('sqlite_master').where('type=? AND name=? AND sql LIKE ?', ('table', 'sites','%project_config%')).count():
public.M('sites').execute("alter TABLE sites add project_config STRING DEFAULT '{}'",())
# 添加子站点区分字段,parent_id: -1 空主站,0 主站 ,其他子站
if not public.M('sqlite_master').where('type=? AND name=? AND sql LIKE ?', ('table', 'sites','%parent_id%')).count():
public.M('sites').execute("alter TABLE sites add parent_id STRING DEFAULT 0",())
if not public.M('sqlite_master').where('type=? AND name=? AND sql LIKE ?', ('table', 'backup','%ps%')).count():
public.M('backup').execute("alter TABLE backup add ps STRING DEFAULT 'No'",())
+156 -11
View File
@@ -3701,16 +3701,37 @@ def get_site_php_version(siteName):
@param siteName string 网站名称
@return string
'''
web_server = get_webserver()
if public.get_multi_webservice_status():
site = public.M('sites').where('name = ?',siteName).field('service_type').find()
web_server = site['service_type'] if site['service_type'] else 'nginx'
vhost_path = get_vhost_path()
conf = readFile(vhost_path + '/' + web_server + '/' + siteName + '.conf')
if web_server == 'openlitespeed':
conf = readFile(vhost_path + '/' + web_server + '/detail/' + siteName + '.conf')
conf_path = get_php_conf_path(siteName)
conf = public.readFile(conf_path.replace('//', '/'))
return get_php_version_conf(conf)
# 获取各服务主站子站配置路径
def get_php_conf_path(site_name):
# 区分网站服务类型
web_server = get_webserver()
if get_multi_webservice_status():
service_type = public.M('sites').where('name = ?', (site_name,)).field('service_type,parent_id').find()
if not service_type:
return None
# 如果是子站,则查询其父站服务
if service_type['parent_id'] not in [-1, 0]:
service_type = public.M('sites').where('id = ?', (service_type['parent_id'],)).field('service_type').find()
if not service_type:
return None
web_server = service_type['service_type'] if service_type['service_type'] else 'nginx'
vhost_path = get_vhost_path()
detail = 'detail/' if web_server == 'openlitespeed' else ''
sub = ''
if '/' in site_name:
parent_site_name = get_parent_site_name(site_name)
if parent_site_name:
site_name = parent_site_name[0] + '/' + parent_site_name[1]
sub = 'sub_dir/'
conf_path = f"{vhost_path}/{web_server}/{detail}{sub}{site_name}.conf"
return conf_path
def check_tcp(ip, port):
'''
@@ -10070,8 +10091,8 @@ def try_to_apply_ssl(func):
if ssl_site_id:
try:
print_log(f"Trying To Auto Apply SSL For The Site... Site ID is: {ssl_site_id}")
from ssl_domainModelV2.service import apply_ssl_with_siteId
apply_ssl_with_siteId(int(ssl_site_id))
from ssl_domainModelV2.service import smart_ssl
smart_ssl(int(ssl_site_id))
except Exception as e:
import traceback
print_log(traceback.format_exc())
@@ -10249,4 +10270,128 @@ def generate_random_string(length, include_special=False):
if include_special:
chars += string.punctuation
return ''.join(secrets.choice(chars) for _ in range(length))
return ''.join(secrets.choice(chars) for _ in range(length))
# 切分主站子站名称
def get_parent_site_name(site_name: str):
"""
切分子站名称
输入 192.168.168.193/aaaa/cccc/test 输出 192.168.168.193, aaaa_cccc_test, aaaa/cccc/test
"""
clean_name = site_name.strip().strip('/')
if '/' in clean_name:
parts = clean_name.split('/', 1)
if len(parts) == 2:
parent = parts[0]
child_path = parts[1].replace('/', '_')
if parent and child_path and parts[1] :
return [parent, child_path, parts[1]]
return None
# 获取站点的web配置文件路径
def get_site_web_cfg(id: str = None, site_name: str = None) -> dict:
"""
获取站点的 Nginx/Apache/OLS 配置文件路径
id (可选)
site_name (可选)
:return: dict {'nginx': '', 'apache': '', 'ols': ''}
"""
if id:
site_info = public.M('sites').where("id=?", (id,)).field('name,parent_id').find()
elif site_name:
site_info = public.M('sites').where("name=?", (site_name,)).field('name,parent_id').find()
else:
return {'nginx': '', 'apache': '', 'ols': ''}
if not site_info:
return {'nginx': '', 'apache': '', 'ols': ''}
setup_path = public.get_setup_path()
name = site_info['name']
# 子站: sub_dir/父站名/子站名.conf
parent_id = site_info.get('parent_id')
if parent_id and str(parent_id) not in [-1, 0]:
parent = public.get_parent_site_name(name)
if parent:
conf_name = f"sub_dir/{parent[0]}/{parent[1]}"
else:
conf_name = name
else:
conf_name = name
return {
'nginx': f'{setup_path}/panel/vhost/nginx/{conf_name}.conf',
'apache': f'{setup_path}/panel/vhost/apache/{conf_name}.conf',
'ols': f'{setup_path}/panel/vhost/openlitespeed/detail/{conf_name}.conf',
}
# 在指定区间内(外)替换
def replace_block(text: str, old: str, new: str, block_start: str = None, block_end: str = None, reverse: bool = True):
"""
按行替换文本, 在指定区间内或区间外操作
text: 原始文本
old: 被替换的字符串
new: 替换后的字符串
block_start: 区间开始标记(包含该行)
block_end: 区间结束标记(包含该行)
闭包区间贪婪, 第一个 start 直到最后一个 end
reverse: True=排除区间内不替换, False=仅替换区间内
:return: 替换后的文本
"""
if not block_start or not block_end:
return text.replace(old, new)
start_pat = re.escape(block_start)
end_pat = re.escape(block_end)
# 闭区间: 从 block_start 所在行到 block_end 所在行(含)
pattern = re.compile(
r'(?P<before>(?:.*\n)*?)'
r'(?P<block>.*' + start_pat + r'.*\n(?:.*\n)*?.*'
r'(?=' + end_pat + r').*)'
r'(?P<after>(?:\n.*)*)'
)
m = pattern.search(text)
if not m:
def _should_replace(in_block: bool, reverse: bool) -> bool:
return (not in_block) if reverse else in_block
# 标记不完整, 退回逐行
lines = text.split('\n')
in_block = False
result = []
for line in lines:
is_start = block_start in line
is_end = block_end in line
# 同行时: start优先, 该行视为区间内
if is_start:
in_block = True
if _should_replace(in_block, reverse):
line = line.replace(old, new)
if is_end:
in_block = False
result.append(line)
return '\n'.join(result)
before = m.group('before')
block = m.group('block')
after = m.group('after')
if reverse:
# 区间外替换
return before.replace(old, new) + block + after.replace(old, new)
else:
# 仅区间内替换
return before + block.replace(old, new) + after
# 修改主配置时保护子站配置
def replace_conf_without_sub(text, old, new):
return replace_block(
text=text,
old=old,
new=new,
block_start="#Subdirectory-configuration-start",
block_end="#Subdirectory-configuration-end"
)
+32 -9
View File
@@ -42,11 +42,17 @@ def get_site_php_version(siteName: str) -> str:
webserver = get_webserver()
setup_path = get_setup_path()
conf = readFile(
'{setup_path}/panel/vhost/{webserver}/{siteName}.conf'.format(setup_path=setup_path, webserver=webserver,
siteName=siteName))
name_list = public.get_parent_site_name(siteName)
if webserver == 'openlitespeed':
conf = readFile(setup_path + '/panel/vhost/' + webserver + '/detail/' + siteName + '.conf')
webserver = webserver + '/detail'
if name_list:
conf = readFile(
'{setup_path}/panel/vhost/{webserver}/sub_dir/{parent_name}/{siteName}.conf'.format(setup_path=setup_path,
webserver=webserver,parent_name=name_list[0],
siteName=name_list[1]))
else:
conf = readFile(
'{setup_path}/panel/vhost/{webserver}/{siteName}.conf'.format(setup_path=setup_path, webserver=webserver, siteName=siteName))
if webserver == 'nginx':
rep = r"enable-php-(\w{2,5})[-\w]*\.conf"
elif webserver == 'apache':
@@ -121,8 +127,7 @@ def del_bak(bak_file: str) -> aap_t_simple_result:
# 创建PHP站点
def create_php_site_with_mysql(
domain: str, site_path: str, php_ver_short: str, db_user: str, db_pwd: str,
another_domains: typing.List = (), is_clone=False, **kwargs
domain: str, site_path: str, php_ver_short: str, db_user: str, db_pwd: str, **kwargs
) -> aap_t_simple_site_info:
"""
:param domain: str 网站主域名
@@ -130,14 +135,14 @@ def create_php_site_with_mysql(
:param php_ver_short: str PHP版本号缩写 54、74、80、81...
:param db_user: str 数据库用户名
:param db_pwd: str 数据库用户密码
:param another_domains: list 网站其它解析域名
:param kwargs: another_domains, is_clone, ssl_auto, sub_dir
:return: aap_t_simple_site_info
"""
from panel_site_v2 import panelSite
data = panelSite().AddSite(to_dict_obj({
'webname': json.dumps({
'domain': domain,
'domainlist': list(another_domains),
'domainlist': list(kwargs.get('another_domains', [])),
'count': 0,
}),
'type': 'PHP',
@@ -149,8 +154,10 @@ def create_php_site_with_mysql(
'datapassword': db_pwd,
'codeing': 'utf8mb4',
'ps': domain.replace('.', '_').replace('-', '_'),
'is_clone' : is_clone,
'is_clone': kwargs.get('is_clone', False),
'ssl_auto': kwargs.get('ssl_auto', 0),
'sub_dir': kwargs.get('sub_dir', ''),
'project_type': kwargs.get('project_type', 'PHP')
}))
if int(data.get('status', 0)) != 0:
raise HintException(data.get('message', {})['result'])
@@ -187,6 +194,22 @@ def remove_site(site_id: int) -> public.aap_t_simple_result:
return aap_t_simple_result(int(data.get('status', 0)) == 0, data.get('message', {})['result'])
def wp_remove_site(site_id: int) -> public.aap_t_simple_result:
site_info = M('sites').where('`id` = ?', (site_id,)).field('name').find()
if not isinstance(site_info, dict):
return public.aap_t_simple_result(False, public.lang('No found site-info with id {}',site_id))
from panel_site_v2 import panelSite
data = panelSite().delete_wp_site(to_dict_obj({
'id': site_id,
'webname': site_info['name'],
'ftp': '1',
'path': '1',
'database': '1',
}))
return aap_t_simple_result(int(data.get('status', 0)) == 0, data.get('message', {})['result'])
# 获取可用的PHP版本列表
def get_available_php_ver_shorts(without_static: bool = True) -> typing.List[str]: