6819 lines
238 KiB
Python
6819 lines
238 KiB
Python
# coding: utf-8
|
||
# +-------------------------------------------------------------------
|
||
# | YakPanel
|
||
# +-------------------------------------------------------------------
|
||
# | Copyright (c) 2015-2099 YakPanel(www.yakpanel.com) All rights reserved.
|
||
# +-------------------------------------------------------------------
|
||
# | Author: hwliang <hwl@yakpanel.com>
|
||
# +---
|
||
|
||
from public.hook_import import hook_import
|
||
hook_import()
|
||
|
||
# from .app import *
|
||
# from .routes.flask_hook import *
|
||
# from .routes.v1 import *
|
||
# from .routes.v2 import *
|
||
|
||
import logging
|
||
import sys
|
||
import json
|
||
import os
|
||
import threading
|
||
import time
|
||
import re
|
||
import uuid
|
||
import psutil
|
||
import zipfile
|
||
import types
|
||
|
||
panel_path = '/www/server/panel'
|
||
if not os.name in ['nt']:
|
||
os.chdir(panel_path)
|
||
if not 'class/' in sys.path:
|
||
sys.path.insert(0, 'class/')
|
||
if not 'class_v2/' in sys.path:
|
||
sys.path.insert(0, 'class_v2/')
|
||
|
||
from flask import (
|
||
Flask, session, render_template, send_file, request, redirect, g,
|
||
render_template_string, abort, stream_with_context, Response as Resp
|
||
)
|
||
from cachelib import SimpleCache, SimpleCacheSession
|
||
from werkzeug.wrappers import Response
|
||
from flask_session import Session
|
||
from flask_compress import Compress
|
||
|
||
cache = SimpleCache(5000)
|
||
import public
|
||
|
||
# 初始化Flask应用
|
||
app = Flask(
|
||
__name__,
|
||
template_folder="templates/{}".format(public.GetConfigValue('template'))
|
||
)
|
||
Compress(app)
|
||
try:
|
||
from flask_sock import Sock
|
||
except:
|
||
from flask_sockets import Sockets as Sock
|
||
|
||
sockets = Sock(app)
|
||
# 注册HOOK
|
||
hooks = {}
|
||
if not hooks:
|
||
public.check_hooks()
|
||
# import db
|
||
dns_client = None
|
||
app.config['DEBUG'] = os.path.exists('data/debug.pl')
|
||
app.config['SSL'] = os.path.exists('data/ssl.pl')
|
||
|
||
# 设置BasicAuth
|
||
basic_auth_conf = 'config/basic_auth.json'
|
||
app.config['BASIC_AUTH_OPEN'] = False
|
||
if os.path.exists(basic_auth_conf):
|
||
try:
|
||
ba_conf = json.loads(public.readFile(basic_auth_conf))
|
||
app.config['BASIC_AUTH_USERNAME'] = ba_conf['basic_user']
|
||
app.config['BASIC_AUTH_PASSWORD'] = ba_conf['basic_pwd']
|
||
app.config['BASIC_AUTH_OPEN'] = ba_conf['open']
|
||
except:
|
||
pass
|
||
|
||
# 初始化SESSION服务
|
||
app.secret_key = public.md5(
|
||
str(os.uname()) +
|
||
str(psutil.boot_time())) # uuid.UUID(int=uuid.getnode()).hex[-12:]
|
||
local_ip = None
|
||
my_terms = {}
|
||
|
||
SESSION_TTL = public.get_session_expired()
|
||
app.config['SESSION_MEMCACHED'] = SimpleCacheSession(1000, SESSION_TTL)
|
||
app.config['SESSION_TYPE'] = 'memcached'
|
||
app.config['SESSION_PERMANENT'] = True
|
||
app.config['SESSION_USE_SIGNER'] = True
|
||
app.config['SESSION_KEY_PREFIX'] = 'BT_:'
|
||
app.config['SESSION_COOKIE_NAME'] = public.md5(app.secret_key)
|
||
app.config['PERMANENT_SESSION_LIFETIME'] = SESSION_TTL
|
||
app.config['SESSION_COOKIE_SAMESITE'] = None
|
||
|
||
if app.config['SSL']:
|
||
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
|
||
app.config['SESSION_COOKIE_SECURE'] = True
|
||
|
||
|
||
Session(app)
|
||
|
||
import common
|
||
|
||
# 初始化路由
|
||
comm = common.panelAdmin()
|
||
method_all = ['GET', 'POST']
|
||
method_get = ['GET']
|
||
method_post = ['POST']
|
||
json_header = {'Content-Type': 'application/json; charset=utf-8'}
|
||
text_header = {'Content-Type': 'text/plain; charset=utf-8'}
|
||
cache.set('p_token', 'bmac_' + public.Md5(public.get_mac_address()))
|
||
admin_path_file = 'data/admin_path.pl'
|
||
admin_path = '/'
|
||
if os.path.exists(admin_path_file):
|
||
admin_path = public.readFile(admin_path_file).strip()
|
||
admin_path_checks = [
|
||
'/',
|
||
'/san',
|
||
'/bak',
|
||
'/monitor',
|
||
'/abnormal',
|
||
'/close',
|
||
'/task',
|
||
'/login',
|
||
'/config',
|
||
'/site',
|
||
'/sites',
|
||
'/ftp',
|
||
'/public',
|
||
'/database',
|
||
'/data',
|
||
'/download_file',
|
||
'/control',
|
||
'/crontab',
|
||
'/firewall',
|
||
'/files',
|
||
'/soft',
|
||
'/ajax',
|
||
'/system',
|
||
'/panel_data',
|
||
'/code',
|
||
'/ssl',
|
||
'/plugin',
|
||
'/wxapp',
|
||
'/hook',
|
||
'/safe',
|
||
'/yield',
|
||
'/downloadApi',
|
||
'/pluginApi',
|
||
'/auth',
|
||
'/download',
|
||
'/cloud',
|
||
'/webssh',
|
||
'/connect_event',
|
||
'/panel',
|
||
'/acme',
|
||
'/down',
|
||
'/api',
|
||
'/tips',
|
||
'/message',
|
||
'/warning',
|
||
'/userRegister', # 面板内注册
|
||
'/userLang', # 登录设语言
|
||
'/docker',
|
||
'/btdocker',
|
||
'/breaking_through',
|
||
]
|
||
if admin_path in admin_path_checks: admin_path = '/bt'
|
||
if admin_path[-1] == '/': admin_path = admin_path[:-1]
|
||
uri_match = re.compile(
|
||
r"(^/static/[\w_\./\-]+\.(js|css|png|jpg|gif|ico|svg|woff|woff2|ttf|otf|eot|map)$|^/[\w_\./\-]*$)"
|
||
)
|
||
session_id_match = re.compile(r"^[\w\.\-]+$")
|
||
route_v2 = '/v2' # v2版本路由前缀
|
||
|
||
# load translations
|
||
from public.translations import load_translations
|
||
load_translations()
|
||
# 登录页语言包
|
||
from public.translations import load_login_translations
|
||
load_login_translations()
|
||
|
||
# ========================== Ignore Zipfile Encode Error ==============
|
||
# hook zipfile.ZipInfo._encodeFilenameFlags, ignore the encode error
|
||
_oldEncodeFilenameFlags = zipfile.ZipInfo._encodeFilenameFlags
|
||
|
||
def _newEncodeFilenameFlags(self):
|
||
try:
|
||
return _oldEncodeFilenameFlags(self)
|
||
except:
|
||
return self.filename.encode('utf-8', 'ignore'), self.flag_bits | zipfile._MASK_UTF_FILENAME
|
||
|
||
zipfile.ZipInfo._encodeFilenameFlags = _newEncodeFilenameFlags
|
||
|
||
# ========================== Ignore Error End =========================
|
||
|
||
|
||
# ========================== Init Menu Path Map =======================
|
||
menu_map = {
|
||
'memua': '/', # Home
|
||
'memuasite': '/site', # Website
|
||
'memuawptoolkit': '/wp/toolkit', # WP Toolkit
|
||
'memuaftp': '/ftp', # FTP
|
||
'memuadatabase': '/database', # Databases
|
||
'memudocker': '/docker', # Docker
|
||
'memuacontrol': '/control', # Monitor
|
||
'memuafirewall': '/firewall', # Security
|
||
'memu_btwaf': '/btwaf', # Waf
|
||
'memu_mailsys': '/mail', # Mail Server
|
||
'memuafiles': '/files', # Files
|
||
'menunode': '/node', # Node Management
|
||
'memualogs': '/logs', # Logs
|
||
'menu_ssl': '/ssl_domain', # SSL
|
||
'memuaxterm': '/xterm', # Terminal
|
||
'memuaccount': '/whm', # Account
|
||
'memuacrontab': '/crontab', # Cron
|
||
'memuasoft': '/soft', # App Store
|
||
'memuaconfig': '/config', # Settings
|
||
'dologin': '/login', # Log out
|
||
'memuASSL': '/ssl_domain' # Domain management
|
||
}
|
||
try:
|
||
menu_default_conf_path = os.path.join(panel_path, 'config/menu.json')
|
||
if os.path.exists(menu_default_conf_path):
|
||
menu_read = public.readFile(menu_default_conf_path)
|
||
menu_list = json.loads(menu_read) if menu_read else []
|
||
menu_map = {
|
||
x.get('id').lower(): x.get('href') for x in menu_list if x.get('id') and x.get('href')
|
||
} if menu_list else menu_map
|
||
except Exception as e:
|
||
public.print_log(f"menu config error, {e}")
|
||
|
||
# ========================== Menu Map End ============================
|
||
|
||
|
||
# ===================================Flask HOOK========================#
|
||
# Flask请求勾子
|
||
from flask import current_app
|
||
@app.before_request
|
||
def request_check():
|
||
# 获取客户端真实IP,判断是否启动CDN代理
|
||
CDN_PROXY = current_app.config.get('CDN_PROXY', False)
|
||
if CDN_PROXY:
|
||
if 'CF-Connecting-IP' in request.headers:
|
||
x_real_ip = request.headers['CF-Connecting-IP']
|
||
elif 'X-Forwarded-For' in request.headers:
|
||
x_real_ip = request.headers['X-Forwarded-For'].split(',')[0].strip()
|
||
else:
|
||
x_real_ip = request.headers.get('X-Real-Ip')
|
||
else:
|
||
x_real_ip = request.headers.get('X-Real-Ip')
|
||
if x_real_ip:
|
||
request.remote_addr = x_real_ip
|
||
request.environ.setdefault('REMOTE_PORT', public.get_remote_port())
|
||
# 过滤菜单
|
||
if 'uid' in session and session['uid'] != 1 and not public.user_router_authority():
|
||
if public.M('users').where('id=?', (session['uid'],)).select():
|
||
import config_v2
|
||
menus = config_v2.config().get_menu_list()
|
||
show_menus = []
|
||
if menus.get('status') == 0:
|
||
show_menus = [
|
||
i.get('id', '').lower() for i in menus.get('message', []) if i.get('show') is True
|
||
]
|
||
if request.path == '/':
|
||
try:
|
||
path = menu_map[show_menus[0]]
|
||
if path == '/login':
|
||
return abort(403)
|
||
return redirect('{}'.format(path.lower()), 302)
|
||
except Exception:
|
||
return abort(403)
|
||
if len(show_menus) < 2:
|
||
return abort(403)
|
||
if not public.user_router_authority():
|
||
return abort(403)
|
||
if request.method not in ['GET', 'POST', 'HEAD']: return abort(404)
|
||
g.request_time = time.time()
|
||
g.return_message = False
|
||
|
||
# URI过滤 1
|
||
if request.path not in ('/google/redirect', '/google/callback') and not request.path.startswith('/v2/pmta/'):
|
||
# 路由和URI长度过滤
|
||
if len(request.path) > 256: return abort(403)
|
||
if len(request.url) > 1024: return abort(403)
|
||
|
||
# URI过滤 2
|
||
if not uri_match.match(request.path): return abort(403)
|
||
|
||
# POST参数过滤
|
||
if request.path in [
|
||
'/login',
|
||
'/safe',
|
||
'/hook',
|
||
'/public',
|
||
'/down',
|
||
'/get_app_bind_status',
|
||
'/check_bind',
|
||
'/userRegister',
|
||
'/userLang',
|
||
]:
|
||
pdata = request.form.to_dict()
|
||
for k in pdata.keys():
|
||
if len(k) > 48: return abort(403)
|
||
if len(pdata[k]) > 256: return abort(403)
|
||
# SESSIONID过滤
|
||
session_id = request.cookies.get(app.config['SESSION_COOKIE_NAME'], '')
|
||
if session_id and not session_id_match.match(session_id): return abort(403)
|
||
|
||
# 请求头过滤
|
||
# if not public.filter_headers():
|
||
# return abort(403)
|
||
|
||
if session.get('debug') == 1: return
|
||
g.get_csrf_html_token_key = public.get_csrf_html_token_key()
|
||
|
||
if app.config['BASIC_AUTH_OPEN']:
|
||
if request.path in [
|
||
'/public', '/download', '/mail_sys', '/hook', '/down',
|
||
'/check_bind', '/get_app_bind_status'
|
||
]:
|
||
return
|
||
auth = request.authorization
|
||
if not comm.get_sk(): return
|
||
if not auth: return send_authenticated()
|
||
tips = '_capnis.com'
|
||
if public.md5(auth.username.strip() + tips) != app.config['BASIC_AUTH_USERNAME'] \
|
||
or public.md5(auth.password.strip() + tips) != app.config['BASIC_AUTH_PASSWORD']:
|
||
return send_authenticated()
|
||
|
||
if not request.path in [
|
||
'/safe',
|
||
'/hook',
|
||
'/public',
|
||
'/mail_sys',
|
||
'/down'
|
||
]:
|
||
ip_check = public.check_ip_panel()
|
||
if ip_check: return ip_check
|
||
|
||
if request.path.startswith('/static/') or request.path == '/code':
|
||
if not 'login' in session and not 'admin_auth' in session and not 'down' in session:
|
||
return abort(401)
|
||
domain_check = public.check_domain_panel()
|
||
if domain_check: return domain_check
|
||
if public.is_local():
|
||
not_networks = ['uninstall_plugin', 'install_plugin', 'UpdatePanel']
|
||
if request.args.get('action') in not_networks:
|
||
return public.returnJson( False, 'This feature cannot be used in offline mode!'), json_header
|
||
# 适配docker---- '/docker',
|
||
|
||
path_list = (
|
||
'/site', '/ftp', '/database', '/soft', '/control', '/firewall',
|
||
'/files', '/xterm', '/crontab', '/config', '/docker', '/btdocker','/breaking_through',
|
||
)
|
||
if (request.path.startswith(path_list) or request.path == "/") and request.method == "GET":
|
||
if request.args.get('action') in [
|
||
'get_tmp_token','download_cert'
|
||
]:
|
||
return
|
||
# if request.path in [
|
||
# '/site', '/ftp', '/database', '/soft', '/control', '/firewall',
|
||
# '/files', '/xterm', '/crontab', '/config', '/docker', '/btdocker','/breaking_through',
|
||
# ]:
|
||
if public.is_error_path():
|
||
return redirect('/error', 302)
|
||
# 密码过期相关功能
|
||
if request.path not in ['/config', '/modify_password', '/login']:
|
||
if not session.get("login", False): # 没有登录时不触发密码过期修改
|
||
return
|
||
reslut = session.get('password_expire', None)
|
||
if reslut is None:
|
||
reslut = not public.password_expire_check()
|
||
session['password_expire'] = reslut
|
||
if reslut:
|
||
return redirect('/modify_password', 302)
|
||
|
||
# 新增 适配docker时增加 未测试
|
||
# 处理登录页面相对路径的静态文件
|
||
if request.path.find('/static/') > 0:
|
||
new_auth_path = _auth_path = public.get_admin_path()
|
||
|
||
# 2024/1/3 下午 8:35 检测_auth_path是否有包含2个以上/符号,如果有则取最后一个/符号前的字符串然后替换成_auth_path
|
||
if _auth_path.count('/') > 1:
|
||
new_auth_path = _auth_path[:_auth_path.rfind('/')]
|
||
|
||
if not public.path_safe_check(request.path): return abort(404) # 路径安全检查
|
||
|
||
_new_route = request.path[0:request.path.find('/static/')]
|
||
if request.path.find(_auth_path) == 0:
|
||
static_file = public.get_panel_path() + '/YakPanel' + request.path.replace(_auth_path, '').replace('//', '/')
|
||
if not os.path.exists(static_file): return abort(404)
|
||
return send_file(static_file, conditional=True, etag=True)
|
||
elif request.path.find(new_auth_path) == 0:
|
||
static_file = public.get_panel_path() + '/YakPanel' + request.path.replace(new_auth_path, '').replace('//',
|
||
'/')
|
||
if not os.path.exists(static_file): return abort(404)
|
||
return send_file(static_file, conditional=True, etag=True)
|
||
elif _new_route in admin_path_checks:
|
||
|
||
static_file = public.get_panel_path() + '/YakPanel' + request.path[len(_new_route):].replace('//', '/')
|
||
# if not os.path.exists(static_file): return abort(404)
|
||
# 检测是否是插件静态文件
|
||
plugin_static_file = public.get_panel_path() + '/plugin/' + request.path
|
||
is_plugin_static = os.path.exists(plugin_static_file)
|
||
|
||
# 既不是面板静态文件也不是插件静态文件
|
||
if not os.path.exists(static_file) and not is_plugin_static: return abort(404)
|
||
|
||
# 如果是插件静态文件
|
||
if is_plugin_static:
|
||
return send_file(plugin_static_file, conditional=True, etag=True)
|
||
|
||
# 如果是面板静态文件
|
||
return send_file(static_file, conditional=True, etag=True)
|
||
|
||
if request.path.find('/static/img/soft_ico/ico') >= 0:
|
||
# 路径安全检查
|
||
if public.path_safe_check(request.path) is False:
|
||
return abort(404)
|
||
static_file = "{}/YakPanel/{}".format(panel_path, request.path)
|
||
if not os.path.exists(static_file):
|
||
static_file = "{}/YakPanel/static/img/soft_ico/icon_plug.svg".format(panel_path)
|
||
return send_file(static_file, conditional=True, etag=True)
|
||
|
||
# 处理登录成功状态,更新节点
|
||
if 'login' in session and session['login'] == True:
|
||
if not cache.get('bt_home_node'):
|
||
public.run_thread(public.ExecShell, ('btpython /www/server/panel/script/reload_check.py hour',))
|
||
cache.set('bt_home_node', True, 3600)
|
||
|
||
|
||
# Flask 请求结束勾子
|
||
@app.teardown_request
|
||
def request_end(reques=None):
|
||
if request.method not in ['GET', 'POST']: return
|
||
if not request.path.startswith('/static/') or not request.path.startswith('/v2/static/'):
|
||
# import public
|
||
public.write_request_log(reques)
|
||
|
||
# 当路由为v2版才检测,且不检测/plugin时
|
||
if request.path.startswith('/v2'):
|
||
now_time = time.time()
|
||
session_timeout = session.get('session_timeout', 0)
|
||
if (session_timeout > now_time or session_timeout == 0) and not request.path.startswith('/v2/plugin'):
|
||
# 首页涉及的请求模块,暂不强制
|
||
prefixes = ["/v2/site", "/v2/ftp", "/v2/database", "/v2/docker", "/v2/safe/security/set_security",
|
||
"/v2/safe/security/get_repair_bar","/v2/breaking_through"]
|
||
for prefix in prefixes:
|
||
if request.path.startswith(prefix):
|
||
if 'return_message' in g:
|
||
if not g.return_message:
|
||
# public.print_log("当前路由且未使用统一响应函数public.return_message")
|
||
# return abort(404)
|
||
# return public.returnJson(
|
||
# False, 'Request failed!Request not using unified response!'
|
||
# ), json_header
|
||
pass
|
||
else:
|
||
# return abort(405)
|
||
g.return_message = False
|
||
# public.print_log("当前路由已使用统一响应函数public.return_message")
|
||
break
|
||
if 'api_request' in g:
|
||
if g.api_request:
|
||
session.clear()
|
||
|
||
|
||
# Flask 405页面勾子
|
||
@app.errorhandler(405)
|
||
def error_405(e):
|
||
if request.method not in ['GET', 'POST']: return
|
||
if not session.get('login', None):
|
||
g.auth_error = True
|
||
return public.error_not_login()
|
||
errorStr = '''<html>
|
||
<head><title>405 Not Found</title></head>
|
||
<body>
|
||
<center><h1>请求接口请使用统一响应函数</h1></center>
|
||
<hr><center>nginx</center>
|
||
</body>
|
||
</html>'''
|
||
headers = {"Content-Type": "text/html"}
|
||
return Response(errorStr, status=404, headers=headers)
|
||
|
||
|
||
# Flask 404页面勾子
|
||
@app.errorhandler(404)
|
||
def error_404(e):
|
||
if request.method not in ['GET', 'POST']: return
|
||
if not session.get('login', None):
|
||
g.auth_error = True
|
||
return public.error_not_login()
|
||
errorStr = '''<html>
|
||
<head><title>404 Not Found</title></head>
|
||
<body>
|
||
<center><h1>404 Not Found</h1></center>
|
||
<hr><center>nginx</center>
|
||
</body>
|
||
</html>'''
|
||
headers = {"Content-Type": "text/html"}
|
||
return Response(errorStr, status=404, headers=headers)
|
||
|
||
|
||
# Flask 403页面勾子
|
||
@app.errorhandler(403)
|
||
def error_403(e):
|
||
if request.method not in ['GET', 'POST']: return
|
||
if not session.get('login', None):
|
||
g.auth_error = True
|
||
return public.error_not_login()
|
||
errorStr = '''<html>
|
||
<head><title>403 Forbidden</title></head>
|
||
<body>
|
||
<center><h1>403 Forbidden</h1></center>
|
||
<hr><center>nginx</center>
|
||
</body>
|
||
</html>'''
|
||
headers = {"Content-Type": "text/html"}
|
||
return Response(errorStr, status=403, headers=headers)
|
||
|
||
|
||
# 错误收集
|
||
@app.errorhandler(Exception)
|
||
def error_500(e):
|
||
# handle the hint exception.
|
||
if isinstance(e, public.HintException):
|
||
return public.fail_v2(str(e))
|
||
|
||
# Print error traceback.
|
||
from traceback import format_exc
|
||
public.print_log(format_exc())
|
||
|
||
if request.method not in ['GET', 'POST']: return Response(status=500)
|
||
|
||
if not session.get('login', None):
|
||
g.auth_error = True
|
||
return public.error_not_login()
|
||
|
||
ss = '''404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
|
||
|
||
During handling of the above exception, another exception occurred:'''
|
||
error_info = public.get_error_info().strip().split(ss)[-1].strip()
|
||
|
||
nn = 'During handling of the above exception, another exception occurred:'
|
||
if error_info.find(nn) != -1 and error_info.find('public.error_conn_cloud') != -1:
|
||
error_info = error_info.split(nn)[0].strip()
|
||
|
||
_form = request.form.to_dict()
|
||
if 'username' in _form: _form['username'] = '******'
|
||
if 'password' in _form: _form['password'] = '******'
|
||
if 'phone' in _form: _form['phone'] = '******'
|
||
if 'pem' in _form: _form['pem'] = '******'
|
||
if 'pwd' in _form: _form['pwd'] = '******'
|
||
if 'key' in _form: _form['key'] = '******'
|
||
if 'csr' in _form: _form['csr'] = '******'
|
||
if 'db_user' in _form: _form['db_user'] = '******'
|
||
if 'db_password' in _form: _form['db_pwd'] = '******'
|
||
|
||
request_info = '''REQUEST_DATE: {request_date}
|
||
VERSION: {os_version} - {panel_version}
|
||
REMOTE_ADDR: {remote_addr}
|
||
REQUEST_URI: {method} {full_path}
|
||
REQUEST_FORM: {request_form}
|
||
USER_AGENT: {user_agent}'''.format(
|
||
request_date=public.getDate(),
|
||
remote_addr=public.GetClientIp(),
|
||
method=request.method,
|
||
full_path=public.xsssec(request.full_path),
|
||
request_form=public.xsssec(str(_form)),
|
||
user_agent=public.xsssec(request.headers.get('User-Agent')),
|
||
panel_version=public.version(),
|
||
os_version=public.get_os_version())
|
||
error_title = error_info.split("\n")[-1].replace('public.PanelError: ',
|
||
'').strip()
|
||
if error_info.find('Failed to connect to the cloud server') != -1:
|
||
error_title = "Failed to connect to the cloud server!"
|
||
|
||
result = public.readFile(
|
||
public.get_panel_path() +
|
||
'/YakPanel/templates/default/panel_error.html').format(
|
||
error_title=error_title,
|
||
request_info=request_info,
|
||
error_msg=error_info)
|
||
|
||
# 用户信息
|
||
# if not public.cache_get("infos"):
|
||
# user_info = json.loads(public.ReadFile("{}/data/userInfo.json".format(public.get_panel_path())))
|
||
# public.cache_set("infos", user_info, 1800)
|
||
# else:
|
||
# user_info = public.cache_get("infos")
|
||
try:
|
||
if "import panelSSL" in error_info:
|
||
result = public.ExecShell("btpip list|grep pyOpenSSL")[0]
|
||
error_info = "{}\n版本信息:{}".format(error_info, result.strip())
|
||
except:
|
||
error_info = "{}\n版本信息: 获取失败".format(error_info)
|
||
|
||
# 错误信息
|
||
error_infos = {
|
||
# "UID": user_info['uid'], # 用户ID
|
||
# 'ACCESS_KEY': user_info['access_key'], # 用户密钥
|
||
# 'SERVER_ID': user_info['serverid'], # 服务器ID
|
||
"REQUEST_DATE": public.getDate(), # 请求时间
|
||
"PANEL_VERSION": public.version(), # 面板版本
|
||
"OS_VERSION": public.get_os_version(), # 操作系统版本
|
||
"REMOTE_ADDR": public.GetClientIp(), # 请求IP
|
||
"REQUEST_URI": request.method + request.full_path, # 请求URI
|
||
"REQUEST_FORM": public.xsssec(str(_form)), # 请求表单
|
||
"USER_AGENT": public.xsssec(request.headers.get('User-Agent')), # 客户端连接信息
|
||
"ERROR_INFO": error_info, # 错误信息
|
||
"PACK_TIME": public.readFile("/www/server/panel/config/update_time.pl") if os.path.exists(
|
||
"/www/server/panel/config/update_time.pl") else public.getDate(), # 打包时间
|
||
"TYPE": 100,
|
||
"ERROR_ID": str(e)
|
||
}
|
||
pkey = public.Md5(error_infos["ERROR_ID"])
|
||
|
||
# 提交异常报告
|
||
if not public.cache_get(pkey) and not public.is_self_hosted():
|
||
try:
|
||
public.run_thread(public.httpPost, ("https://geterror.yakpanel.com/bt_error/index.php", error_infos))
|
||
public.cache_set(pkey, 1, 1800)
|
||
except Exception as e:
|
||
pass
|
||
|
||
return Resp(result, 500)
|
||
|
||
|
||
# ===================================Flask HOOK========================#
|
||
|
||
|
||
def _send_panel_static_file(prefix, subpath):
|
||
base = os.path.realpath(os.path.join(public.get_panel_path(), prefix))
|
||
# normpath collapses ".." to help keep resolved path under base
|
||
rel = os.path.normpath(subpath.replace('/', os.sep)).lstrip(os.sep + '.')
|
||
if rel.startswith('..' + os.sep):
|
||
from flask import abort
|
||
abort(404)
|
||
full = os.path.realpath(os.path.join(base, rel))
|
||
if not full.startswith(base) or not os.path.isfile(full):
|
||
from flask import abort
|
||
abort(404)
|
||
return send_file(full, conditional=True)
|
||
|
||
|
||
# ===================================普通路由区========================#
|
||
@app.route('/api/common/getClientIP', methods=method_all)
|
||
def selfhosted_get_client_ip():
|
||
if not public.is_self_hosted():
|
||
from flask import abort
|
||
abort(404)
|
||
return Resp(public.GetClientIp(), mimetype='text/plain; charset=utf-8')
|
||
|
||
|
||
@app.route('/api/panel/checkDomain', methods=method_post)
|
||
def selfhosted_check_domain():
|
||
if not public.is_self_hosted():
|
||
from flask import abort
|
||
abort(404)
|
||
return Resp(json.dumps({'status': True}), mimetype='application/json; charset=utf-8')
|
||
|
||
|
||
@app.route('/api/index/get_time', methods=method_get)
|
||
def selfhosted_get_time():
|
||
if not public.is_self_hosted():
|
||
from flask import abort
|
||
abort(404)
|
||
return Resp(str(int(time.time())), mimetype='text/plain; charset=utf-8')
|
||
|
||
|
||
@app.route('/stop_en.html', methods=method_get)
|
||
def selfhosted_stop_page():
|
||
if not public.is_self_hosted():
|
||
from flask import abort
|
||
abort(404)
|
||
return Resp(
|
||
'<!DOCTYPE html><html><head><meta charset="utf-8"><title>Stopped</title></head>'
|
||
'<body><p>Site stopped.</p></body></html>',
|
||
mimetype='text/html; charset=utf-8',
|
||
)
|
||
|
||
|
||
@app.route('/install/<path:subpath>', methods=method_get)
|
||
def selfhosted_install_files(subpath):
|
||
if not public.is_self_hosted():
|
||
from flask import abort
|
||
abort(404)
|
||
return _send_panel_static_file('install', subpath)
|
||
|
||
|
||
@app.route('/src/<path:subpath>', methods=method_get)
|
||
def selfhosted_src_files(subpath):
|
||
if not public.is_self_hosted():
|
||
from flask import abort
|
||
abort(404)
|
||
return _send_panel_static_file('src', subpath)
|
||
|
||
|
||
@app.route('/linux/<path:subpath>', methods=method_get)
|
||
def selfhosted_linux_files(subpath):
|
||
if not public.is_self_hosted():
|
||
from flask import abort
|
||
abort(404)
|
||
return _send_panel_static_file('linux', subpath)
|
||
|
||
|
||
@app.route('/script/<path:subpath>', methods=method_get)
|
||
def selfhosted_script_files(subpath):
|
||
if not public.is_self_hosted():
|
||
from flask import abort
|
||
abort(404)
|
||
return _send_panel_static_file('script', subpath)
|
||
|
||
|
||
@app.route('/safe_warning/<path:subpath>', methods=method_get)
|
||
def selfhosted_safe_warning_files(subpath):
|
||
if not public.is_self_hosted():
|
||
from flask import abort
|
||
abort(404)
|
||
return _send_panel_static_file('safe_warning', subpath)
|
||
|
||
# @app.route('/', methods=method_all)
|
||
# def home():
|
||
# # 面板首页
|
||
# comReturn = comm.local()
|
||
# if comReturn: return comReturn
|
||
# data = {}
|
||
# data[public.to_string([112,
|
||
# 100])], data['pro_end'], data['ltd_end'] = get_pd()
|
||
# data['siteCount'] = public.M('sites').count()
|
||
# data['ftpCount'] = public.M('ftps').count()
|
||
# data['databaseCount'] = public.M('databases').count()
|
||
# data['lan'] = public.GetLan('index')
|
||
# data['js_random'] = get_js_random()
|
||
# return render_template('index.html', data=data)
|
||
|
||
|
||
@app.route('/', methods=method_get)
|
||
@app.route('/<path:sub_path>', methods=method_get)
|
||
def index_new(sub_path: str = ''):
|
||
|
||
if sub_path == 'unsubscribe.html':
|
||
return render_template('unsubscribe.html')
|
||
|
||
# 面板首页
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
data = {}
|
||
|
||
if sub_path == '':
|
||
data[public.to_string([112, 100])], data['pro_end'], data['ltd_end'] = get_pd()
|
||
data['siteCount'] = public.M('sites').count()
|
||
data['ftpCount'] = public.M('ftps').count()
|
||
data['databaseCount'] = public.M('databases').count()
|
||
data['lan'] = public.GetLan('index')
|
||
data['js_random'] = get_js_random()
|
||
elif sub_path.startswith('config'):
|
||
import system, wxapp, config
|
||
c_obj = config.config()
|
||
data = system.system().GetConcifInfo()
|
||
data['lan'] = public.GetLan('config')
|
||
try:
|
||
data['wx'] = wxapp.wxapp().get_user_info(None)['msg']
|
||
except:
|
||
data['wx'] = 'INIT_WX_NOT_BIND'
|
||
data['api'] = ''
|
||
data['ipv6'] = ''
|
||
sess_out_path = 'data/session_timeout.pl'
|
||
if not os.path.exists(sess_out_path):
|
||
public.writeFile(sess_out_path, '86400')
|
||
s_time_tmp = public.readFile(sess_out_path)
|
||
if not s_time_tmp: s_time_tmp = '0'
|
||
data['session_timeout'] = int(s_time_tmp)
|
||
if c_obj.get_ipv6_listen(None): data['ipv6'] = 'checked'
|
||
if c_obj.get_token(None)['open']: data['api'] = 'checked'
|
||
data['basic_auth'] = c_obj.get_basic_auth_stat(None)
|
||
data['status_code'] = c_obj.get_not_auth_status()
|
||
data['basic_auth']['value'] = public.getMsg('CLOSED')
|
||
if data['basic_auth']['open']:
|
||
data['basic_auth']['value'] = public.getMsg('OPENED')
|
||
data['debug'] = ''
|
||
data['js_random'] = get_js_random()
|
||
if app.config['DEBUG']: data['debug'] = 'checked'
|
||
data['is_local'] = ''
|
||
if public.is_local(): data['is_local'] = 'checked'
|
||
data['public_key'] = public.get_rsa_public_key().replace("\n", "")
|
||
elif sub_path.startswith('soft'):
|
||
import system
|
||
data = system.system().GetConcifInfo()
|
||
data['lan'] = public.GetLan('soft')
|
||
data['js_random'] = get_js_random()
|
||
elif sub_path.startswith('crontab'):
|
||
import system
|
||
data = system.system().GetConcifInfo()
|
||
data['lan'] = public.GetLan('crontab')
|
||
data['js_random'] = get_js_random()
|
||
elif sub_path.startswith('docker'):
|
||
import system
|
||
data = system.system().GetConcifInfo()
|
||
data['js_random'] = get_js_random()
|
||
data['lan'] = public.GetLan('files')
|
||
elif sub_path.startswith('control'):
|
||
import system
|
||
data = system.system().GetConcifInfo()
|
||
data['lan'] = public.GetLan('control')
|
||
data['js_random'] = get_js_random()
|
||
elif sub_path.startswith('logs'):
|
||
data = {}
|
||
data['lan'] = public.GetLan('soft')
|
||
data['show_workorder'] = not os.path.exists('data/not_workorder.pl')
|
||
elif sub_path.startswith('database'):
|
||
import ajax
|
||
from panelPlugin import panelPlugin
|
||
session['phpmyadminDir'] = False
|
||
if panelPlugin().get_phpmyadmin_stat():
|
||
pmd = get_phpmyadmin_dir()
|
||
if pmd:
|
||
session['phpmyadminDir'] = 'http://' + public.GetHost(
|
||
) + ':' + pmd[1] + '/' + pmd[0]
|
||
ajax.ajax().set_phpmyadmin_session()
|
||
import system
|
||
data = system.system().GetConcifInfo()
|
||
data['isSetup'] = os.path.exists(
|
||
public.GetConfigValue('setup_path') + '/mysql/bin')
|
||
data['mysql_root'] = public.M('config').where(
|
||
'id=?', (1,)).getField('mysql_root')
|
||
data['lan'] = public.GetLan('database')
|
||
data['js_random'] = get_js_random()
|
||
elif sub_path.startswith('ftp'):
|
||
FtpPort()
|
||
import system
|
||
data = system.system().GetConcifInfo()
|
||
data['isSetup'] = True
|
||
data['js_random'] = get_js_random()
|
||
if os.path.exists(public.GetConfigValue('setup_path') +
|
||
'/pure-ftpd') == False:
|
||
data['isSetup'] = False
|
||
data['lan'] = public.GetLan('ftp')
|
||
elif sub_path.startswith('site'):
|
||
import system
|
||
data = system.system().GetConcifInfo()
|
||
data['isSetup'] = True
|
||
data['lan'] = public.getLan('site')
|
||
data['js_random'] = get_js_random()
|
||
if os.path.exists(public.GetConfigValue('setup_path') + '/nginx') == False \
|
||
and os.path.exists(public.GetConfigValue('setup_path') + '/apache') == False \
|
||
and os.path.exists('/usr/local/lsws/bin/lswsctrl') == False:
|
||
data['isSetup'] = False
|
||
elif sub_path.startswith('xterm'):
|
||
import system
|
||
data = system.system().GetConcifInfo()
|
||
elif sub_path.startswith('firewall'):
|
||
import system
|
||
data = system.system().GetConcifInfo()
|
||
data['lan'] = public.GetLan('firewall')
|
||
data['js_random'] = get_js_random()
|
||
elif sub_path.startswith('files'):
|
||
import system
|
||
data = system.system().GetConcifInfo()
|
||
data['recycle_bin'] = os.path.exists('data/recycle_bin.pl')
|
||
data['lan'] = public.GetLan('files')
|
||
data['js_random'] = get_js_random()
|
||
elif sub_path.startswith('ssh_security'):
|
||
data['lan'] = public.GetLan('firewall')
|
||
data['js_random'] = get_js_random()
|
||
|
||
data['isSetup'] = True
|
||
if os.path.exists(public.GetConfigValue('setup_path') + '/nginx') == False \
|
||
and os.path.exists(public.GetConfigValue('setup_path') + '/apache') == False \
|
||
and os.path.exists('/usr/local/lsws/bin/lswsctrl') == False:
|
||
data['isSetup'] = False
|
||
|
||
# load translations
|
||
# 登录成功后重启面板 使翻译切换生效
|
||
# public.ExecShell("/etc/init.d/bt start")
|
||
# public.writeFile('data/restart.pl', 'True')
|
||
|
||
from public.translations import load_translations
|
||
load_translations()
|
||
|
||
import base64
|
||
data['translations'] = base64.b64encode(json.dumps(load_translations()).encode()).decode()
|
||
data['public_key'] = public.get_rsa_public_key().replace("\n", "")
|
||
return render_template('index_new.html', data=data)
|
||
|
||
|
||
|
||
@app.route('/xterm', methods=method_post)
|
||
def xterm():
|
||
# YakPanel终端管理
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import ssh_terminal
|
||
ssh_host_admin = ssh_terminal.ssh_host_admin()
|
||
defs = ('get_host_list', 'get_host_find', 'modify_host', 'create_host',
|
||
'remove_host', 'set_sort', 'get_command_list', 'create_command',
|
||
'get_command_find', 'modify_command', 'remove_command')
|
||
return publicObject(ssh_host_admin, defs, None)
|
||
|
||
# 密码过期路由
|
||
@app.route('/modify_password', methods=method_get)
|
||
def modify_password():
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
# if not session.get('password_expire',False): return redirect ('/',302)
|
||
data = {}
|
||
from public.translations import load_translations
|
||
load_translations()
|
||
|
||
import base64
|
||
data['translations'] = base64.b64encode(json.dumps(load_translations()).encode()).decode()
|
||
data['public_key'] = public.get_rsa_public_key()
|
||
g.title = 'The password has expired, please change it!'
|
||
return render_template('index_new.html', data=data)
|
||
|
||
|
||
@app.route('/site', methods=method_post)
|
||
def site(pdata=None):
|
||
# 网站管理
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import panelSite
|
||
siteObject = panelSite.panelSite()
|
||
|
||
defs = (
|
||
'get_auto_restart_rph',
|
||
'remove_auto_restart_rph',
|
||
'auto_restart_rph',
|
||
'check_del_data',
|
||
'upload_csv',
|
||
'create_website_multiple',
|
||
'del_redirect_multiple',
|
||
'del_proxy_multiple',
|
||
'delete_dir_auth_multiple',
|
||
'delete_dir_bind_multiple',
|
||
'delete_domain_multiple',
|
||
'set_site_etime_multiple',
|
||
'set_site_php_version_multiple',
|
||
'delete_website_multiple',
|
||
'set_site_status_multiple',
|
||
'get_site_err_log',
|
||
'get_site_domains',
|
||
'GetRedirectFile',
|
||
'SaveRedirectFile',
|
||
'DeleteRedirect',
|
||
'GetRedirectList',
|
||
'CreateRedirect',
|
||
'ModifyRedirect',
|
||
"set_error_redirect",
|
||
'set_dir_auth',
|
||
'delete_dir_auth',
|
||
'get_dir_auth',
|
||
'modify_dir_auth_pass',
|
||
'reset_wp_db',
|
||
'export_domains',
|
||
'import_domains',
|
||
'GetSiteLogs',
|
||
'GetSiteDomains',
|
||
'GetSecurity',
|
||
'SetSecurity',
|
||
'ProxyCache',
|
||
'CloseToHttps',
|
||
'HttpToHttps',
|
||
'SetEdate',
|
||
'SetRewriteTel',
|
||
'GetCheckSafe',
|
||
'CheckSafe',
|
||
'GetDefaultSite',
|
||
'SetDefaultSite',
|
||
'CloseTomcat',
|
||
'SetTomcat',
|
||
'apacheAddPort',
|
||
'AddSite',
|
||
'GetPHPVersion',
|
||
'SetPHPVersion',
|
||
'DeleteSite',
|
||
'AddDomain',
|
||
'DelDomain',
|
||
'GetDirBinding',
|
||
'AddDirBinding',
|
||
'GetDirRewrite',
|
||
'DelDirBinding',
|
||
'get_site_types',
|
||
'add_site_type',
|
||
'remove_site_type',
|
||
'modify_site_type_name',
|
||
'set_site_type',
|
||
'UpdateRulelist',
|
||
'SetSiteRunPath',
|
||
'GetSiteRunPath',
|
||
'SetPath',
|
||
'SetIndex',
|
||
'GetIndex',
|
||
'GetDirUserINI',
|
||
'SetDirUserINI',
|
||
'GetRewriteList',
|
||
'SetSSL',
|
||
'SetSSLConf',
|
||
'CreateLet',
|
||
'CloseSSLConf',
|
||
'GetSSL',
|
||
'SiteStart',
|
||
'SiteStop',
|
||
'Set301Status',
|
||
'Get301Status',
|
||
'CloseLimitNet',
|
||
'SetLimitNet',
|
||
'GetLimitNet',
|
||
'RemoveProxy',
|
||
'GetProxyList',
|
||
'GetProxyDetals',
|
||
'CreateProxy',
|
||
'ModifyProxy',
|
||
'GetProxyFile',
|
||
'SaveProxyFile',
|
||
'ToBackup',
|
||
'DelBackup',
|
||
'GetSitePHPVersion',
|
||
'logsOpen',
|
||
'GetLogsStatus',
|
||
'CloseHasPwd',
|
||
'SetHasPwd',
|
||
'GetHasPwd',
|
||
'GetDnsApi',
|
||
'SetDnsApi',
|
||
'reset_wp_password',
|
||
'is_update',
|
||
'purge_all_cache',
|
||
'set_fastcgi_cache',
|
||
'update_wp',
|
||
'get_wp_username',
|
||
'get_language',
|
||
'deploy_wp',
|
||
# 网站管理新增
|
||
'test_domains_api',
|
||
'site_rname',
|
||
)
|
||
return publicObject(siteObject, defs, None, pdata)
|
||
|
||
|
||
@app.route('/ftp', methods=method_post)
|
||
def ftp(pdata=None):
|
||
# FTP管理
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import ftp
|
||
ftpObject = ftp.ftp()
|
||
defs = ('AddUser', 'DeleteUser', 'SetUserPassword', 'SetStatus', 'setPort',
|
||
'set_user_home', 'get_login_logs', 'get_action_logs',
|
||
'set_ftp_logs')
|
||
return publicObject(ftpObject, defs, None, pdata)
|
||
|
||
|
||
@app.route('/database', methods=method_post)
|
||
def database(pdata=None):
|
||
# 数据库管理
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import database
|
||
databaseObject = database.database()
|
||
defs = ('GetdataInfo', 'check_del_data', 'get_database_size', 'GetInfo',
|
||
'ReTable', 'OpTable', 'AlTable', 'GetSlowLogs', 'GetRunStatus',
|
||
'SetDbConf', 'GetDbStatus', 'BinLog', 'GetErrorLog',
|
||
'GetMySQLInfo', 'SetDataDir', 'SetMySQLPort', 'AddCloudDatabase',
|
||
'AddDatabase', 'DeleteDatabase', 'SetupPassword',
|
||
'ResDatabasePassword', 'ToBackup', 'DelBackup', 'AddCloudServer',
|
||
'GetCloudServer', 'RemoveCloudServer', 'ModifyCloudServer',
|
||
'InputSql', 'SyncToDatabases', 'SyncGetDatabases',
|
||
'GetDatabaseAccess', 'SetDatabaseAccess', 'get_mysql_user',
|
||
'check_mysql_ssl_status', 'write_ssl_to_mysql', 'GetdataInfo')
|
||
return publicObject(databaseObject, defs, None, pdata)
|
||
|
||
|
||
@app.route('/acme', methods=method_all)
|
||
def acme(pdata=None):
|
||
# Let's 证书管理
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import acme_v2
|
||
acme_v2_object = acme_v2.acme_v2()
|
||
defs = ('get_orders', 'remove_order', 'get_order_find', 'revoke_order',
|
||
'create_order', 'get_account_info', 'set_account_info',
|
||
'update_zip', 'get_cert_init_api', 'get_auths', 'auth_domain',
|
||
'check_auth_status', 'download_cert', 'apply_cert', 'renew_cert',
|
||
'apply_cert_api', 'apply_dns_auth')
|
||
return publicObject(acme_v2_object, defs, None, pdata)
|
||
|
||
|
||
# import panelMessage
|
||
# message_object = panelMessage.panelMessage()
|
||
# @app.route('/message/<action>', methods=method_all)
|
||
# def message(action=None):
|
||
# # 提示消息管理
|
||
# comReturn = comm.local()
|
||
# if comReturn: return comReturn
|
||
# import panelMessage
|
||
# message_object = panelMessage.panelMessage()
|
||
# defs = (
|
||
# 'get_messages', 'get_message_find', 'create_message', 'status_message', 'remove_message', 'get_messages_all')
|
||
# return publicObject(message_object, defs, action, None)
|
||
|
||
|
||
@app.route('/api', methods=method_all)
|
||
def api(pdata=None):
|
||
# APP使用的API接口管理
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import panelApi
|
||
api_object = panelApi.panelApi()
|
||
defs = ('get_token', 'check_bind', 'get_bind_status', 'get_apps',
|
||
'add_bind_app', 'remove_bind_app', 'set_token', 'get_tmp_token',
|
||
'get_app_bind_status', 'login_for_app')
|
||
return publicObject(api_object, defs, None, pdata)
|
||
|
||
|
||
@app.route('/firewall', methods=method_post)
|
||
def firewall(pdata=None):
|
||
# 安全页面
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import firewalls
|
||
firewallObject = firewalls.firewalls()
|
||
defs = ('GetList', 'AddDropAddress', 'DelDropAddress', 'FirewallReload',
|
||
'SetFirewallStatus', 'AddAcceptPort', 'DelAcceptPort',
|
||
'SetSshStatus', 'SetPing', 'SetSshPort', 'GetSshInfo',
|
||
'SetFirewallStatus')
|
||
return publicObject(firewallObject, defs, None, pdata)
|
||
|
||
|
||
|
||
@app.route('/ssh_security', methods=method_all)
|
||
def ssh_security(pdata=None):
|
||
# SSH安全
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
if request.method == method_get[0] and not pdata and not request.args.get(
|
||
'action', '') in ['download_key']:
|
||
return index_new('ssh_security')
|
||
import ssh_security
|
||
firewallObject = ssh_security.ssh_security()
|
||
is_csrf = True
|
||
if request.args.get('action', '') in ['download_key']: is_csrf = False
|
||
defs = ('san_ssh_security', 'set_password', 'set_sshkey', 'stop_key',
|
||
'get_config', 'download_key', 'stop_password', 'get_key',
|
||
'return_ip', 'add_return_ip', 'del_return_ip', 'start_jian',
|
||
'stop_jian', 'get_jian', 'get_logs', 'set_root', 'stop_root',
|
||
'start_auth_method', 'stop_auth_method', 'get_auth_method',
|
||
'check_so_file', 'get_so_file', 'get_pin', 'set_login_send',
|
||
'get_login_send', 'get_msg_push_list', 'clear_login_send')
|
||
return publicObject(firewallObject, defs, None, pdata, is_csrf)
|
||
|
||
|
||
@app.route('/monitor', methods=method_all)
|
||
def panel_monitor(pdata=None):
|
||
# 云控统计信息
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import monitor
|
||
dataObject = monitor.Monitor()
|
||
defs = ('get_spider', 'get_exception', 'get_request_count_qps',
|
||
'load_and_up_flow', 'get_request_count_by_hour')
|
||
return publicObject(dataObject, defs, None, pdata)
|
||
|
||
|
||
@app.route('/san', methods=method_all)
|
||
def san_baseline(pdata=None):
|
||
# 云控安全扫描
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import san_baseline
|
||
dataObject = san_baseline.san_baseline()
|
||
defs = ('start', 'get_api_log', 'get_resut', 'get_ssh_errorlogin',
|
||
'repair', 'repair_all')
|
||
return publicObject(dataObject, defs, None, pdata)
|
||
|
||
|
||
@app.route('/password', methods=method_all)
|
||
def panel_password(pdata=None):
|
||
# 云控密码管理
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import password
|
||
dataObject = password.password()
|
||
defs = ('set_root_password', 'get_mysql_root', 'set_mysql_password',
|
||
'set_panel_password', 'SetPassword', 'SetSshKey', 'StopKey',
|
||
'GetConfig', 'StopPassword', 'GetKey', 'get_databses',
|
||
'rem_mysql_pass', 'set_mysql_access', "get_panel_username")
|
||
return publicObject(dataObject, defs, None, pdata)
|
||
|
||
|
||
@app.route('/warning', methods=method_all)
|
||
def panel_warning(pdata=None):
|
||
# 首页安全警告
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
if public.get_csrf_html_token_key() in session and 'login' in session:
|
||
if not check_csrf():
|
||
return public.ReturnJson(False, 'INIT_CSRF_ERR'), json_header
|
||
get = get_input()
|
||
ikey = 'warning_list'
|
||
import panelWarning
|
||
dataObject = panelWarning.panelWarning()
|
||
if get.action == 'get_list':
|
||
result = cache.get(ikey)
|
||
if not result or 'force' in get:
|
||
result = json.loads('{"ignore":[],"risk":[],"security":[]}')
|
||
try:
|
||
defs = ("get_list",)
|
||
result = publicObject(dataObject, defs, None, pdata)
|
||
cache.set(ikey, result, 3600)
|
||
return result
|
||
except:
|
||
pass
|
||
return result
|
||
|
||
defs = ('get_list', 'set_ignore', 'check_find', 'check_cve',
|
||
'set_vuln_ignore', 'get_scan_bar', 'get_tmp_result',
|
||
'kill_get_list')
|
||
|
||
if get.action in ['set_ignore', 'check_find', 'set_vuln_ignore']:
|
||
cache.delete(ikey)
|
||
return publicObject(dataObject, defs, None, pdata)
|
||
|
||
|
||
@app.route('/bak', methods=method_all)
|
||
def backup_bak(pdata=None):
|
||
# 云控备份服务
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import backup_bak
|
||
dataObject = backup_bak.backup_bak()
|
||
defs = ('get_sites', 'get_databases', 'backup_database', 'backup_site',
|
||
'backup_path', 'get_database_progress', 'get_site_progress',
|
||
'down', 'get_down_progress', 'download_path', 'backup_site_all',
|
||
'get_all_site_progress', 'backup_date_all',
|
||
'get_all_date_progress')
|
||
return publicObject(dataObject, defs, None, pdata)
|
||
|
||
|
||
@app.route('/abnormal', methods=method_all)
|
||
def abnormal(pdata=None):
|
||
# 云控系统统计
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import abnormal
|
||
dataObject = abnormal.abnormal()
|
||
defs = ('mysql_server', 'mysql_cpu', 'mysql_count', 'php_server',
|
||
'php_conn_max', 'php_cpu', 'CPU', 'Memory', 'disk',
|
||
'not_root_user', 'start')
|
||
return publicObject(dataObject, defs, None, pdata)
|
||
|
||
|
||
@app.route('/project/<mod_name>/<def_name>/<stype>', methods=method_all)
|
||
def project(mod_name, def_name, stype=None):
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
from panelProjectController import ProjectController
|
||
project_obj = ProjectController()
|
||
defs = ('model',)
|
||
get = get_input()
|
||
get.action = 'model'
|
||
get.mod_name = mod_name
|
||
get.def_name = def_name
|
||
get.stype = stype
|
||
if stype == "html":
|
||
return project_obj.model(get)
|
||
return publicObject(project_obj, defs, None, get)
|
||
|
||
|
||
@app.route('/msg/<mod_name>/<def_name>', methods=method_all)
|
||
def msgcontroller(mod_name, def_name):
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
from MsgController import MsgController
|
||
project_obj = MsgController()
|
||
defs = ('model',)
|
||
get = get_input()
|
||
get.action = 'model'
|
||
get.mod_name = mod_name
|
||
get.def_name = def_name
|
||
return publicObject(project_obj, defs, None, get)
|
||
|
||
|
||
# @app.route('/docker', methods=method_all)
|
||
# def docker(pdata=None):
|
||
# comReturn = comm.local()
|
||
# if comReturn: return comReturn
|
||
# if request.method == method_get[0]:
|
||
# import system
|
||
# data = system.system().GetConcifInfo()
|
||
# data['js_random'] = get_js_random()
|
||
# data['lan'] = public.GetLan('files')
|
||
# return render_template('docker.html', data=data)
|
||
|
||
|
||
# @app.route('/docker', methods=method_all)
|
||
# @app.route('/docker/<action>', methods=method_all)
|
||
# @app.route('/docker_ifame', methods=method_all)
|
||
# def docker(action=None, pdata=None):
|
||
# if not public.is_bind():
|
||
# return redirect('/bind', 302)
|
||
# comReturn = comm.local()
|
||
# if comReturn: return comReturn
|
||
# if request.method == method_get[0]:
|
||
# import system
|
||
# data = system.system().GetConcifInfo()
|
||
# data['js_random'] = get_js_random()
|
||
# data['lan'] = public.GetLan('files')
|
||
# return render_template('index1.html', data=data)
|
||
|
||
|
||
@app.route('/dbmodel/<mod_name>/<def_name>', methods=method_all)
|
||
def dbmodel(mod_name, def_name):
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
from panelDatabaseController import DatabaseController
|
||
database_obj = DatabaseController()
|
||
defs = ('model',)
|
||
get = get_input()
|
||
get.action = 'model'
|
||
get.mod_name = mod_name
|
||
get.def_name = def_name
|
||
|
||
return publicObject(database_obj, defs, None, get)
|
||
|
||
|
||
@app.route('/files', methods=method_all)
|
||
def files(pdata=None):
|
||
# 文件管理
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
if request.method == method_get[0] and not request.args.get('path') and not pdata:
|
||
return index_new('files')
|
||
import files
|
||
filesObject = files.files()
|
||
defs = ('files_search', 'files_replace', 'get_replace_logs',
|
||
'get_images_resize', 'add_files_rsync', 'get_file_attribute',
|
||
'get_file_hash', 'CreateLink', 'get_progress', 'restore_website',
|
||
'fix_permissions', 'get_all_back', 'restore_path_permissions',
|
||
'del_path_premissions', 'get_path_premissions',
|
||
'back_path_permissions', 'upload_file_exists', 'CheckExistsFiles',
|
||
'GetExecLog', 'GetSearch', 'ExecShell', 'GetExecShellMsg',
|
||
'exec_git', 'exec_composer', 'create_download_url', 'UploadFile',
|
||
'GetDir', 'GetDirNew','CreateFile', 'CreateDir', 'DeleteDir', 'DeleteFile',
|
||
'get_download_url_list', 'remove_download_url',
|
||
'modify_download_url', 'CopyFile', 'CopyDir', 'MvFile',
|
||
'GetFileBody', 'SaveFileBody', 'Zip', 'UnZip',
|
||
'get_download_url_find', 'set_file_ps', 'SearchFiles', 'upload',
|
||
'read_history', 're_history', 'auto_save_temp',
|
||
'get_auto_save_body', 'get_videos', 'GetFileAccess',
|
||
'SetFileAccess', 'GetDirSize', 'SetBatchData', 'BatchPaste',
|
||
'install_rar', 'get_path_size', 'DownloadFile', 'GetTaskSpeed',
|
||
'CloseLogs', 'InstallSoft', 'UninstallSoft', 'SaveTmpFile',
|
||
'get_composer_version', 'exec_composer', 'update_composer',
|
||
'GetTmpFile', 'del_files_store', 'add_files_store',
|
||
'get_files_store', 'del_files_store_types',
|
||
'add_files_store_types', 'exec_git', 'RemoveTask', 'ActionTask',
|
||
'Re_Recycle_bin', 'Get_Recycle_bin', 'Del_Recycle_bin',
|
||
'Close_Recycle_bin', 'Recycle_bin', 'file_webshell_check',
|
||
'dir_webshell_check', 'files_search', 'files_replace',
|
||
'get_replace_logs')
|
||
return publicObject(filesObject, defs, None, pdata)
|
||
|
||
|
||
@app.route('/crontab', methods=method_post)
|
||
def crontab(pdata=None):
|
||
# 计划任务
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import crontab
|
||
crontabObject = crontab.crontab()
|
||
defs = ('GetCrontab', 'AddCrontab', 'GetDataList', 'GetLogs', 'DelLogs',
|
||
'DelCrontab', 'StartTask', 'set_cron_status', 'get_crond_find',
|
||
'modify_crond', 'get_backup_list')
|
||
return publicObject(crontabObject, defs, None, pdata)
|
||
|
||
|
||
@app.route('/config', methods=method_post)
|
||
def config(pdata=None):
|
||
# 面板设置页面
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
|
||
import config
|
||
defs = (
|
||
'send_by_telegram',
|
||
'set_empty',
|
||
'set_backup_notification',
|
||
'get_panel_ssl_status',
|
||
'set_file_deny',
|
||
'del_file_deny',
|
||
'get_file_deny',
|
||
'set_improvement',
|
||
'get_httpd_access_log_format_parameter',
|
||
'set_httpd_format_log_to_website',
|
||
'get_httpd_access_log_format',
|
||
'del_httpd_access_log_format',
|
||
'add_httpd_access_log_format',
|
||
'get_nginx_access_log_format_parameter',
|
||
'set_format_log_to_website',
|
||
'get_nginx_access_log_format',
|
||
'del_nginx_access_log_format',
|
||
'set_click_logs',
|
||
'get_node_config',
|
||
'add_nginx_access_log_format',
|
||
'get_ols_private_cache_status',
|
||
'get_ols_value',
|
||
'set_ols_value',
|
||
'set_node_config',
|
||
'get_ols_private_cache',
|
||
'get_ols_static_cache',
|
||
'set_ols_static_cache',
|
||
'switch_ols_private_cache',
|
||
'set_ols_private_cache',
|
||
'set_coll_open',
|
||
'get_qrcode_data',
|
||
'check_two_step',
|
||
'set_two_step_auth',
|
||
'create_user',
|
||
'remove_user',
|
||
'modify_user',
|
||
'get_key',
|
||
'get_php_session_path',
|
||
'set_php_session_path',
|
||
'get_cert_source',
|
||
'get_users',
|
||
'set_request_iptype',
|
||
'set_local',
|
||
'set_debug',
|
||
'get_panel_error_logs',
|
||
'clean_panel_error_logs',
|
||
'get_menu_list',
|
||
'set_hide_menu_list',
|
||
'get_basic_auth_stat',
|
||
'set_basic_auth',
|
||
'get_cli_php_version',
|
||
'get_tmp_token',
|
||
'get_temp_login',
|
||
'set_temp_login',
|
||
'remove_temp_login',
|
||
'clear_temp_login',
|
||
'get_temp_login_logs',
|
||
'set_cli_php_version',
|
||
'DelOldSession',
|
||
'GetSessionCount',
|
||
'SetSessionConf',
|
||
'set_not_auth_status',
|
||
'GetSessionConf',
|
||
'get_ipv6_listen',
|
||
'set_ipv6_status',
|
||
'GetApacheValue',
|
||
'SetApacheValue',
|
||
'install_msg_module',
|
||
'GetNginxValue',
|
||
'SetNginxValue',
|
||
'get_token',
|
||
'set_token',
|
||
'set_admin_path',
|
||
'is_pro',
|
||
'set_msg_config',
|
||
'get_php_config',
|
||
'get_config',
|
||
'SavePanelSSL',
|
||
'GetPanelSSL',
|
||
'GetPHPConf',
|
||
'SetPHPConf',
|
||
'uninstall_msg_module',
|
||
'GetPanelList',
|
||
'AddPanelInfo',
|
||
'SetPanelInfo',
|
||
'DelPanelInfo',
|
||
'ClickPanelInfo',
|
||
'SetPanelSSL',
|
||
'get_msg_configs',
|
||
'SetTemplates',
|
||
'Set502',
|
||
'setPassword',
|
||
'setUsername',
|
||
'setPanel',
|
||
'setPathInfo',
|
||
'setPHPMaxSize',
|
||
'get_msg_fun',
|
||
'getFpmConfig',
|
||
'setFpmConfig',
|
||
'setPHPMaxTime',
|
||
'syncDate',
|
||
'setPHPDisable',
|
||
'SetControl',
|
||
'get_settings2',
|
||
'del_tg_info',
|
||
'set_tg_bot',
|
||
'ClosePanel',
|
||
'AutoUpdatePanel',
|
||
'SetPanelLock',
|
||
'return_mail_list',
|
||
'del_mail_list',
|
||
'add_mail_address',
|
||
'user_mail_send',
|
||
'get_user_mail',
|
||
'set_dingding',
|
||
'get_dingding',
|
||
'get_settings',
|
||
'user_stmp_mail_send',
|
||
'user_dingding_send',
|
||
'get_login_send',
|
||
'set_login_send',
|
||
'clear_login_send',
|
||
'get_login_log',
|
||
'login_ipwhite',
|
||
'set_ssl_verify',
|
||
'get_ssl_verify',
|
||
'get_password_config',
|
||
'set_password_expire',
|
||
'set_password_safe',
|
||
'get_module_template',
|
||
# 新增nps评分
|
||
'write_nps_new',
|
||
'get_nps_new',
|
||
"check_nps",
|
||
# 提交报错信息 # 错误收集
|
||
'err_collection',
|
||
# 语言包 测试接口
|
||
# 'get_language',
|
||
# 'get_languageinfo',
|
||
'set_language',
|
||
'download_language',
|
||
'upload_language',
|
||
# 'test_language',
|
||
'set_hou',
|
||
'replace_data',
|
||
'set_theme',
|
||
)
|
||
return publicObject(config.config(), defs, None, pdata)
|
||
|
||
|
||
@app.route('/config', methods=method_get)
|
||
def config_old(pdata=None):
|
||
# 面板设置页面
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
|
||
import system, wxapp, config
|
||
c_obj = config.config()
|
||
data = system.system().GetConcifInfo()
|
||
data['lan'] = public.GetLan('config')
|
||
try:
|
||
data['wx'] = wxapp.wxapp().get_user_info(None)['msg']
|
||
except:
|
||
data['wx'] = 'INIT_WX_NOT_BIND'
|
||
data['api'] = ''
|
||
data['ipv6'] = ''
|
||
sess_out_path = 'data/session_timeout.pl'
|
||
if not os.path.exists(sess_out_path):
|
||
public.writeFile(sess_out_path, '86400')
|
||
s_time_tmp = public.readFile(sess_out_path)
|
||
if not s_time_tmp: s_time_tmp = '0'
|
||
data['session_timeout'] = int(s_time_tmp)
|
||
if c_obj.get_ipv6_listen(None): data['ipv6'] = 'checked'
|
||
if c_obj.get_token(None)['open']: data['api'] = 'checked'
|
||
data['basic_auth'] = c_obj.get_basic_auth_stat(None)
|
||
data['status_code'] = c_obj.get_not_auth_status()
|
||
data['basic_auth']['value'] = public.getMsg('CLOSED')
|
||
if data['basic_auth']['open']:
|
||
data['basic_auth']['value'] = public.getMsg('OPENED')
|
||
data['debug'] = ''
|
||
data['js_random'] = get_js_random()
|
||
if app.config['DEBUG']: data['debug'] = 'checked'
|
||
data['is_local'] = ''
|
||
if public.is_local(): data['is_local'] = 'checked'
|
||
data['public_key'] = public.get_rsa_public_key().replace("\n", "")
|
||
return render_template('config.html', data=data)
|
||
|
||
|
||
@app.route('/ajax', methods=method_all)
|
||
def ajax(pdata=None):
|
||
# 面板系统服务状态接口
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import ajax
|
||
ajaxObject = ajax.ajax()
|
||
defs = ('get_lines', 'php_info', 'change_phpmyadmin_ssl_port',
|
||
'set_phpmyadmin_ssl', 'get_phpmyadmin_ssl', 'get_pd',
|
||
'check_user_auth', 'to_not_beta', 'get_beta_logs', 'get_version_logs','apple_beta',
|
||
'GetApacheStatus', 'GetCloudHtml', 'get_pay_type',
|
||
'get_load_average', 'GetOpeLogs', 'GetFpmLogs', 'GetFpmSlowLogs',
|
||
'SetMemcachedCache', 'GetMemcachedStatus', 'GetRedisStatus',
|
||
'GetWarning', 'SetWarning', 'CheckLogin', 'GetSpeed', 'GetAd',
|
||
'phpSort', 'ToPunycode', 'GetBetaStatus', 'SetBeta',
|
||
'setPHPMyAdmin', 'delClose', 'KillProcess', 'GetPHPInfo',
|
||
'GetQiniuFileList', 'get_process_tops', 'get_process_cpu_high',
|
||
'UninstallLib', 'InstallLib', 'SetQiniuAS', 'GetQiniuAS',
|
||
'GetLibList', 'GetProcessList', 'GetNetWorkList', 'GetNginxStatus',
|
||
'GetPHPStatus', 'GetTaskCount', 'GetSoftList', 'GetNetWorkIo',
|
||
'GetDiskIo', 'GetCpuIo', 'CheckInstalled', 'UpdatePanel',
|
||
'GetInstalled', 'GetPHPConfig', 'SetPHPConfig', 'log_analysis',
|
||
'speed_log', 'get_result', 'get_detailed', 'ignore_version')
|
||
|
||
return publicObject(ajaxObject, defs, None, pdata)
|
||
|
||
|
||
@app.route('/system', methods=method_all)
|
||
def system(pdata=None):
|
||
# 面板系统状态接口
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import system
|
||
sysObject = system.system()
|
||
defs = ('get_io_info', 'UpdatePro', 'GetAllInfo', 'GetNetWorkApi',
|
||
'GetLoadAverage', 'ClearSystem', 'GetNetWorkOld', 'GetNetWork',
|
||
'GetDiskInfo', 'GetCpuInfo', 'GetBootTime', 'GetSystemVersion',
|
||
'GetMemInfo', 'GetSystemTotal', 'GetConcifInfo', 'ServiceAdmin',
|
||
'ReWeb', 'RestartServer', 'ReMemory', 'RepPanel')
|
||
return publicObject(sysObject, defs, None, pdata)
|
||
|
||
|
||
@app.route('/deployment', methods=method_all)
|
||
def deployment(pdata=None):
|
||
# 一键部署接口
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import plugin_deployment
|
||
sysObject = plugin_deployment.plugin_deployment()
|
||
defs = ('GetList', 'AddPackage', 'DelPackage', 'SetupPackage', 'GetSpeed',
|
||
'GetPackageOther')
|
||
return publicObject(sysObject, defs, None, pdata)
|
||
|
||
|
||
@app.route('/data', methods=method_all)
|
||
@app.route('/panel_data', methods=method_all)
|
||
def panel_data(pdata=None):
|
||
# 从数据库获取数据接口
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import data
|
||
dataObject = data.data()
|
||
defs = ('setPs', 'getData', 'getFind', 'getKey')
|
||
return publicObject(dataObject, defs, None, pdata)
|
||
|
||
|
||
@app.route('/ssl', methods=method_all)
|
||
def ssl(pdata=None):
|
||
# 商业SSL证书申请接口
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import panelSSL
|
||
toObject = panelSSL.panelSSL()
|
||
defs = (
|
||
'check_url_txt', 'RemoveCert', 'renew_lets_ssl', 'SetCertToSite', 'GetCertList',
|
||
'SaveCert', 'GetCert', 'GetCertName', 'again_verify', 'DelToken', 'GetToken',
|
||
'GetUserInfo', 'GetOrderList', 'GetDVSSL', 'Completed', 'SyncOrder', 'download_cert',
|
||
'set_cert', 'cancel_cert_order', 'get_order_list', 'get_order_find', 'apply_order_pay',
|
||
'get_pay_status', 'apply_order', 'get_verify_info', 'get_verify_result', 'get_product_list',
|
||
'set_verify_info', 'GetSSLInfo', 'downloadCRT', 'GetSSLProduct', 'Renew_SSL', 'Get_Renew_SSL',
|
||
# 新增 购买证书对接接口
|
||
'get_product_list_v2', 'apply_cert_order_pay', 'get_cert_admin', 'apply_order_ca',
|
||
'apply_cert_install_pay',
|
||
# 'pay_test'
|
||
)
|
||
get = get_input()
|
||
|
||
if get.action == 'download_cert':
|
||
from io import BytesIO
|
||
import base64
|
||
result = toObject.download_cert(get)
|
||
|
||
fp = BytesIO(base64.b64decode(result['res']['data']))
|
||
return send_file(fp,
|
||
download_name=result['res']['filename'],
|
||
as_attachment=True,
|
||
mimetype='application/zip')
|
||
result = publicObject(toObject, defs, get.action, get)
|
||
return result
|
||
|
||
|
||
@app.route('/task', methods=method_all)
|
||
def task(pdata=None):
|
||
# 后台任务接口
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import panelTask
|
||
toObject = panelTask.bt_task()
|
||
defs = ('get_task_lists', 'remove_task', 'get_task_find',
|
||
"get_task_log_by_id")
|
||
result = publicObject(toObject, defs, None, pdata)
|
||
return result
|
||
|
||
|
||
@app.route('/plugin', methods=method_all)
|
||
def plugin(pdata=None):
|
||
# 插件系统接口
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import panelPlugin
|
||
pluginObject = panelPlugin.panelPlugin()
|
||
defs = ('get_usually_plugin', 'check_install_limit', 'set_score',
|
||
'get_score', 'update_zip', 'input_zip', 'export_zip', 'add_index',
|
||
'remove_index', 'sort_index', 'install_plugin', 'uninstall_plugin',
|
||
'get_soft_find', 'get_index_list', 'get_soft_list',
|
||
'get_cloud_list', 'check_deps', 'flush_cache', 'GetCloudWarning',
|
||
'install', 'unInstall', 'getPluginList', 'getPluginInfo',
|
||
'get_make_args', 'add_make_args', 'getPluginStatus',
|
||
'setPluginStatus', 'a', 'getCloudPlugin', 'getConfigHtml',
|
||
'savePluginSort', 'del_make_args', 'set_make_args')
|
||
return publicObject(pluginObject, defs, None, pdata)
|
||
|
||
|
||
@app.route('/wxapp', methods=method_all)
|
||
@app.route('/panel_wxapp', methods=method_all)
|
||
def panel_wxapp(pdata=None):
|
||
# 微信小程序绑定接口
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import wxapp
|
||
toObject = wxapp.wxapp()
|
||
defs = ('blind', 'get_safe_log', 'blind_result', 'get_user_info',
|
||
'blind_del', 'blind_qrcode')
|
||
result = publicObject(toObject, defs, None, pdata)
|
||
return result
|
||
|
||
|
||
@app.route('/auth', methods=method_all)
|
||
def auth(pdata=None):
|
||
# 面板认证接口
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import panelAuth
|
||
toObject = panelAuth.panelAuth()
|
||
defs = ('free_trial', 'renew_product_auth', 'auth_activate',
|
||
'get_product_auth', 'get_product_auth_all','get_stripe_session_id',
|
||
'get_re_order_status_plugin', 'create_plugin_other_order',
|
||
'get_order_stat', 'get_voucher_plugin','get_voucher_plugin_all',
|
||
'create_order_voucher_plugin', 'get_product_discount_by',
|
||
'get_re_order_status', 'create_order_voucher', 'create_order',
|
||
'get_order_status', 'get_voucher', 'flush_pay_status',
|
||
'create_serverid', 'check_serverid', 'get_plugin_list',
|
||
'check_plugin', 'get_buy_code', 'check_pay_status',
|
||
'get_renew_code', 'check_renew_code', 'get_business_plugin',
|
||
'get_ad_list', 'check_plugin_end', 'get_plugin_price',
|
||
'get_plugin_remarks', 'get_paypal_session_id',
|
||
'check_paypal_status')
|
||
result = publicObject(toObject, defs, None, pdata)
|
||
return result
|
||
|
||
|
||
@app.route('/download', methods=method_get)
|
||
def download():
|
||
# 文件下载接口
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
filename = request.args.get('filename')
|
||
if filename.find('|') != -1:
|
||
filename = filename.split('|')[1]
|
||
if not filename:
|
||
return public.ReturnJson(False, "INIT_ARGS_ERR"), json_header
|
||
|
||
if filename in [
|
||
'alioss', 'qiniu', 'upyun', 'txcos', 'ftp', 'msonedrive',
|
||
'gcloud_storage', 'gdrive', 'aws_s3', 'obs', 'bos'
|
||
]:
|
||
return panel_cloud(False)
|
||
|
||
import html
|
||
filepath = html.unescape(filename.replace('\x00', ''))
|
||
if '..' in filepath.split('/') or '..' in filepath.split('\\'):
|
||
return public.ReturnJson(False, "INVALID PATH"), json_header
|
||
filename = os.path.abspath(filepath)
|
||
|
||
if not os.path.exists(filename):
|
||
return public.ReturnJson(False, "File not exists"), json_header
|
||
if os.path.isdir(filename):
|
||
return public.ReturnJson(False, "The catalog is not downloadable"), json_header
|
||
|
||
try:
|
||
import stat
|
||
file_stat = os.stat(filename)
|
||
if stat.S_ISSOCK(file_stat.st_mode):
|
||
return public.ReturnJson(False, "Unix domain socket files are not downloadable"), json_header
|
||
elif stat.S_ISCHR(file_stat.st_mode):
|
||
return public.ReturnJson(False, "Character device files cannot be downloaded"), json_header
|
||
elif stat.S_ISBLK(file_stat.st_mode):
|
||
return public.ReturnJson(False, "Block device files are not downloadable"), json_header
|
||
elif stat.S_ISFIFO(file_stat.st_mode):
|
||
return public.ReturnJson(False, "FIFO pipeline files are not downloadable"), json_header
|
||
except:
|
||
pass
|
||
|
||
|
||
|
||
if request.args.get('play') == 'true':
|
||
import panelVideo
|
||
start, end = panelVideo.get_range(request)
|
||
g.return_message = True
|
||
return panelVideo.partial_response(filename, start, end)
|
||
else:
|
||
mimetype = "application/octet-stream"
|
||
extName = filename.split('.')[-1]
|
||
if extName in ['png', 'gif', 'jpeg', 'jpg']: mimetype = None
|
||
public.WriteLog("TYPE_FILE", 'FILE_DOWNLOAD',
|
||
(filename, public.GetClientIp()))
|
||
g.return_message = True
|
||
if not os.path.exists(filename):
|
||
return public.ReturnJson(False, "File not exists"), json_header
|
||
return send_file(filename,
|
||
mimetype=mimetype,
|
||
as_attachment=True,
|
||
etag=True,
|
||
conditional=True,
|
||
download_name=os.path.basename(filename),
|
||
max_age=0)
|
||
|
||
|
||
@app.route('/cloud', methods=method_all)
|
||
def panel_cloud(is_csrf=True):
|
||
# 从对像存储下载备份文件接口
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
if is_csrf:
|
||
if not check_csrf():
|
||
return public.ReturnJson(False, 'INIT_CSRF_ERR'), json_header
|
||
get = get_input()
|
||
_filename = get.filename
|
||
plugin_name = ""
|
||
if _filename.find('|') != -1:
|
||
plugin_name = get.filename.split('|')[1]
|
||
else:
|
||
plugin_name = get.filename
|
||
|
||
if not os.path.exists('plugin/' + plugin_name + '/' + plugin_name +
|
||
'_main.py'):
|
||
return public.returnJson(
|
||
False, 'The specified plugin does not exist!'), json_header
|
||
public.package_path_append('plugin/' + plugin_name)
|
||
plugin_main = __import__(plugin_name + '_main')
|
||
public.mod_reload(plugin_main)
|
||
tmp = eval("plugin_main.%s_main()" % plugin_name)
|
||
if not hasattr(tmp, 'download_file'):
|
||
return public.returnJson(
|
||
False,
|
||
'Specified plugin has no file download function!'), json_header
|
||
download_url = tmp.download_file(get.name)
|
||
if plugin_name == 'ftp':
|
||
if download_url.find("ftp") != 0:
|
||
download_url = "ftp://" + download_url
|
||
else:
|
||
if download_url.find('http') != 0:
|
||
download_url = 'http://' + download_url
|
||
|
||
if "toserver" in get and get.toserver == "true":
|
||
download_dir = "/tmp/"
|
||
if "download_dir" in get:
|
||
download_dir = get.download_dir
|
||
local_file = os.path.join(download_dir, get.name)
|
||
|
||
input_from_local = False
|
||
if "input_from_local" in get:
|
||
input_from_local = True if get.input_from_local == "true" else False
|
||
|
||
if input_from_local:
|
||
if os.path.isfile(local_file):
|
||
return {
|
||
"status": True,
|
||
"msg":
|
||
"The file already exists and will be restored locally.",
|
||
"task_id": -1,
|
||
"local_file": local_file
|
||
}
|
||
from panelTask import bt_task
|
||
task_obj = bt_task()
|
||
task_id = task_obj.create_task('Download file', 1, download_url,
|
||
local_file)
|
||
return {
|
||
"status": True,
|
||
"msg": "The download task was created successfully",
|
||
"local_file": local_file,
|
||
"task_id": task_id
|
||
}
|
||
|
||
return redirect(download_url)
|
||
|
||
|
||
@app.route('/btwaf_error', methods=method_get)
|
||
def btwaf_error():
|
||
# 图标
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
get = get_input()
|
||
p_path = os.path.join('/www/server/panel/plugin/', "btwaf")
|
||
if not os.path.exists(p_path):
|
||
if get.name == 'btwaf' and get.fun == 'index':
|
||
return render_template('error3.html', data={})
|
||
return render_template('error3.html', data={})
|
||
|
||
|
||
@app.route('/favicon.ico', methods=method_get)
|
||
def send_favicon():
|
||
# 图标
|
||
comReturn = comm.local()
|
||
if comReturn: return abort(404)
|
||
s_file = '/www/server/panel/YakPanel/static/favicon.ico'
|
||
if not os.path.exists(s_file): return abort(404)
|
||
return send_file(s_file, conditional=True, etag=True)
|
||
|
||
|
||
@app.route('/rspamd', defaults={'path': ''}, methods=method_all)
|
||
@app.route('/rspamd/<path:path>', methods=method_all)
|
||
def proxy_rspamd_requests(path):
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
param = str(request.url).split('?')
|
||
param = "" if len(param) < 2 else param[-1]
|
||
import requests
|
||
headers = {}
|
||
for h in request.headers.keys():
|
||
headers[h] = request.headers[h]
|
||
if request.method == "GET":
|
||
if re.search(r"\.(js|css)$", path):
|
||
return send_file('/usr/share/rspamd/www/rspamd/' + path,
|
||
conditional=True,
|
||
etag=True)
|
||
if path == "/":
|
||
return send_file('/usr/share/rspamd/www/rspamd/',
|
||
conditional=True,
|
||
etag=True)
|
||
url = "http://127.0.0.1:11334/rspamd/" + path + "?" + param
|
||
for i in [
|
||
'stat', 'auth', 'neighbours', 'list_extractors',
|
||
'list_transforms', 'graph', 'maps', 'actions', 'symbols',
|
||
'history', 'errors', 'check_selector', 'saveactions',
|
||
'savesymbols', 'getmap'
|
||
]:
|
||
if i in path:
|
||
url = "http://127.0.0.1:11334/" + path + "?" + param
|
||
if os.path.exists('/etc/rspamd/passwd'):
|
||
headers['Password'] = public.readFile('/etc/rspamd/passwd')
|
||
req = requests.get(url, headers=headers, stream=True)
|
||
return Resp(stream_with_context(req.iter_content()),
|
||
content_type=req.headers['content-type'], status=req.status_code)
|
||
else:
|
||
url = "http://127.0.0.1:11334/" + path
|
||
for i in request.form.keys():
|
||
data = '{}='.format(i)
|
||
# public.writeFile('/tmp/2',data+"\n","a+")
|
||
req = requests.post(url, data=data, headers=headers, stream=True)
|
||
return Resp(stream_with_context(req.iter_content()),
|
||
content_type=req.headers['content-type'])
|
||
|
||
|
||
@app.route('/tips', methods=method_get)
|
||
def tips():
|
||
# 提示页面
|
||
comReturn = comm.local()
|
||
if comReturn: return abort(404)
|
||
get = get_input()
|
||
if len(get.get_items().keys()) > 1: return abort(404)
|
||
return render_template('tips.html')
|
||
|
||
|
||
# ======================普通路由区============================#
|
||
|
||
# ======================严格排查区域============================#
|
||
|
||
route_path = os.path.join(admin_path, '')
|
||
if not route_path: route_path = '/'
|
||
if route_path[-1] == '/': route_path = route_path[:-1]
|
||
if route_path[0] != '/': route_path = '/' + route_path
|
||
|
||
|
||
@app.route('/login', methods=method_all)
|
||
@app.route(route_path, methods=method_all)
|
||
@app.route(route_path + '/', methods=method_all)
|
||
def login():
|
||
# 面板登录接口
|
||
if os.path.exists('install.pl'): return redirect('/install')
|
||
global admin_check_auth, admin_path, route_path
|
||
is_auth_path = False
|
||
if admin_path != '/bt' and os.path.exists(
|
||
admin_path_file) and not 'admin_auth' in session:
|
||
is_auth_path = True
|
||
# 登录输入验证
|
||
if request.method == method_post[0]:
|
||
#防爆破检测
|
||
import breaking_through
|
||
_breaking_through_obj = breaking_through.main()
|
||
limit_login = _breaking_through_obj.get_login_limit()
|
||
if limit_login:
|
||
return public.return_msg_gettext(False, 'Yakpanel explosion-proof limit, cancel command: bt 33'), json_header
|
||
|
||
if is_auth_path:
|
||
g.auth_error = True
|
||
return public.error_not_login(None)
|
||
v_list = ['username', 'password', 'code', 'vcode', 'cdn_url']
|
||
for v in v_list:
|
||
if v in ['username', 'password']: continue
|
||
pv = request.form.get(v, '').strip()
|
||
if v == 'cdn_url':
|
||
if len(pv) > 32:
|
||
return public.return_msg_gettext(
|
||
False, 'Wrong parameter length!'), json_header
|
||
if not re.match(r"^[\w\.-]+$", pv):
|
||
public.return_msg_gettext(
|
||
False, 'Wrong parameter format!'), json_header
|
||
continue
|
||
|
||
if not pv: continue
|
||
p_len = 32
|
||
if v == 'code': p_len = 4
|
||
if v == 'vcode': p_len = 6
|
||
if len(pv) != p_len:
|
||
if v == 'code':
|
||
return public.returnJson(
|
||
False, 'Verification code length error!'), json_header
|
||
return public.returnJson(
|
||
False, 'Wrong parameter length!'), json_header
|
||
if not re.match(r"^\w+$", pv):
|
||
return public.returnJson(
|
||
False, 'Wrong parameter format!'), json_header
|
||
for n in request.form.keys():
|
||
|
||
if not n in v_list:
|
||
return public.returnJson(
|
||
False,
|
||
'There can be no extra parameters in the login parameters'
|
||
), json_header
|
||
|
||
get = get_input()
|
||
import userlogin
|
||
if hasattr(get, 'tmp_token'):
|
||
result = userlogin.userlogin().request_tmp(get)
|
||
return is_login(result)
|
||
|
||
# 过滤爬虫
|
||
if public.is_spider(): return abort(404)
|
||
if hasattr(get, 'dologin'):
|
||
login_path = '/login'
|
||
if not 'login' in session: return redirect(login_path)
|
||
if os.path.exists(admin_path_file): login_path = route_path
|
||
if session['login'] != False:
|
||
session['login'] = False
|
||
cache.set('dologin', True)
|
||
public.write_log_gettext(
|
||
'Logout', 'Client: {}, has manually exited the panel',
|
||
(public.GetClientIp() + ":" +
|
||
str(request.environ.get('REMOTE_PORT')),))
|
||
if 'tmp_login_expire' in session:
|
||
s_file = 'data/session/{}'.format(session['tmp_login_id'])
|
||
if os.path.exists(s_file):
|
||
os.remove(s_file)
|
||
token_key = public.get_csrf_html_token_key()
|
||
if token_key in session:
|
||
del (session[token_key])
|
||
session.clear()
|
||
sess_file = 'data/sess_files/' + public.get_sess_key()
|
||
if os.path.exists(sess_file):
|
||
try:
|
||
os.remove(sess_file)
|
||
except:
|
||
pass
|
||
sess_tmp_file = public.get_full_session_file()
|
||
if os.path.exists(sess_tmp_file): os.remove(sess_tmp_file)
|
||
g.dologin = True
|
||
return redirect(public.get_admin_path())
|
||
|
||
if is_auth_path:
|
||
if route_path != request.path and route_path + '/' != request.path:
|
||
referer = request.headers.get('Referer', 'err')
|
||
referer_tmp = referer.split('/')
|
||
referer_path = referer_tmp[-1]
|
||
if referer_path == '':
|
||
referer_path = referer_tmp[-2]
|
||
if route_path != '/' + referer_path:
|
||
g.auth_error = True
|
||
# return render_template('autherr.html')
|
||
return public.error_not_login(None)
|
||
|
||
session['admin_auth'] = True
|
||
|
||
comReturn = common.panelSetup().init()
|
||
if comReturn:
|
||
return comReturn
|
||
|
||
if request.method == method_post[0]:
|
||
result = userlogin.userlogin().request_post(get)
|
||
return is_login(result)
|
||
|
||
if request.method == method_get[0]:
|
||
result = userlogin.userlogin().request_get(get)
|
||
if result:
|
||
return result
|
||
data = {}
|
||
data['lan'] = public.GetLan('login')
|
||
data['hosts'] = '[]'
|
||
hosts_file = 'plugin/static_cdn/hosts.json'
|
||
if os.path.exists(hosts_file):
|
||
data['hosts'] = public.get_cdn_hosts()
|
||
if type(data['hosts']) == dict:
|
||
data['hosts'] = '[]'
|
||
else:
|
||
data['hosts'] = json.dumps(data['hosts'])
|
||
data['app_login'] = os.path.exists('data/app_login.pl')
|
||
public.cache_set(
|
||
public.Md5(
|
||
uuid.UUID(int=uuid.getnode()).hex[-12:] +
|
||
public.GetClientIp()), 'check', 360)
|
||
|
||
# 生成登录token
|
||
last_key = 'last_login_token'
|
||
# -----------
|
||
last_time_key = 'last_login_token_time'
|
||
s_time = int(time.time())
|
||
if last_key in session and last_time_key in session:
|
||
# 10秒内不重复生成token
|
||
if s_time - session[last_time_key] > 10:
|
||
session[last_key] = public.GetRandomString(32)
|
||
session[last_time_key] = s_time
|
||
else:
|
||
session[last_key] = public.GetRandomString(32)
|
||
session[last_time_key] = s_time
|
||
|
||
data[last_key] = session[last_key]
|
||
import base64
|
||
data['login_translations'] = base64.b64encode(json.dumps(load_login_translations()).encode()).decode()
|
||
settings = '{}/YakPanel/languages/settings.json'.format(public.get_panel_path())
|
||
# default = json.loads(public.readFile(settings))['default']
|
||
|
||
settings_content = public.readFile(settings)
|
||
try:
|
||
if settings_content and settings_content.strip():
|
||
settings_json = json.loads(settings_content)
|
||
else:
|
||
settings_json = {}
|
||
except Exception as e:
|
||
settings_json = {}
|
||
|
||
default = settings_json.get('default', 'en') # 默认值
|
||
|
||
|
||
if default == '':
|
||
default = 'en'
|
||
data['login_lang'] = default if default else 'en'
|
||
data['public_key'] = public.get_rsa_public_key()
|
||
|
||
|
||
|
||
|
||
import userLang
|
||
get_language = userLang.userLang().get_language(None)['message']
|
||
data['language'] = get_language['default']
|
||
data['language_list'] = get_language['languages']
|
||
|
||
return render_template('login.html', data=data)
|
||
# -----------
|
||
|
||
# rsa_key = 'public_key'
|
||
# session[last_key] = public.GetRandomString(32)
|
||
# data[last_key] = session[last_key]
|
||
# data[rsa_key] = public.get_rsa_public_key().replace("\n", "")
|
||
# return render_template('login.html', data=data)
|
||
|
||
|
||
# 新增面板内注册
|
||
@app.route('/userRegister', methods=method_all)
|
||
def userRegister():
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import userRegister
|
||
reg = userRegister.userRegister()
|
||
defs = ('toRegister',)
|
||
|
||
return publicObject(reg, defs, None, None)
|
||
|
||
|
||
|
||
@app.route('/close', methods=method_get)
|
||
def close():
|
||
# 面板已关闭页面
|
||
if not os.path.exists('data/close.pl'): return redirect('/')
|
||
data = {}
|
||
data['lan'] = public.getLan('close')
|
||
return render_template('close.html', data=data)
|
||
|
||
|
||
@app.route('/get_app_bind_status', methods=method_all)
|
||
def get_app_bind_status(pdata=None):
|
||
# APP绑定状态查询
|
||
if not public.check_app('app_bind'): return abort(404)
|
||
get = get_input()
|
||
if len(get.get_items().keys()) > 2: return 'There are meaningless parameters!'
|
||
v_list = ['bind_token', 'data']
|
||
for n in get.get_items().keys():
|
||
if not n in v_list:
|
||
return public.returnJson(
|
||
False, 'There can be no redundant parameters'), json_header
|
||
import panelApi
|
||
api_object = panelApi.panelApi()
|
||
return json.dumps(api_object.get_app_bind_status(get_input())), json_header
|
||
|
||
|
||
@app.route('/check_bind', methods=method_all)
|
||
def check_bind(pdata=None):
|
||
# APP绑定查询
|
||
if not public.check_app('app_bind'): return abort(404)
|
||
get = get_input()
|
||
if len(get.get_items().keys()) > 4: return 'There are meaningless parameters!'
|
||
v_list = ['bind_token', 'client_brand', 'client_model', 'data']
|
||
for n in get.get_items().keys():
|
||
if not n in v_list:
|
||
return public.returnJson(
|
||
False, 'There can be no redundant parameters'), json_header
|
||
import panelApi
|
||
api_object = panelApi.panelApi()
|
||
return json.dumps(api_object.check_bind(get_input())), json_header
|
||
|
||
|
||
@app.route('/code', methods=method_get)
|
||
def code():
|
||
if not 'code' in session: return ''
|
||
if not session['code']: return ''
|
||
# 获取图片验证码
|
||
try:
|
||
import vilidate
|
||
except:
|
||
public.ExecShell("btpip install Pillow -I")
|
||
return "Pillow not install!"
|
||
vie = vilidate.vieCode()
|
||
codeImage = vie.GetCodeImage(80, 4)
|
||
if sys.version_info[0] == 2:
|
||
try:
|
||
from cStringIO import StringIO
|
||
except:
|
||
from StringIO import StringIO
|
||
out = StringIO()
|
||
else:
|
||
from io import BytesIO
|
||
out = BytesIO()
|
||
codeImage[0].save(out, "png")
|
||
cache.set("codeStr", public.md5("".join(codeImage[1]).lower()), 180)
|
||
cache.set("codeOut", 1, 0.1)
|
||
out.seek(0)
|
||
return send_file(out, mimetype='image/png', max_age=0)
|
||
|
||
|
||
@app.route('/down/<token>', methods=method_all)
|
||
def down(token=None, fname=None):
|
||
# 文件分享对外接口
|
||
try:
|
||
if public.M('download_token').count() == 0: return abort(404)
|
||
fname = request.args.get('fname')
|
||
if fname:
|
||
if (len(fname) > 256): return abort(404)
|
||
if fname: fname = fname.strip('/')
|
||
if not token: return abort(404)
|
||
if len(token) > 48: return abort(404)
|
||
char_list = [
|
||
'\\', '/', ':', '*', '?', '"', '<', '>', '|', ';', '&', '`'
|
||
]
|
||
for char in char_list:
|
||
if char in token: return abort(404)
|
||
if not request.args.get('play') in ['true', None, '']:
|
||
return abort(404)
|
||
args = get_input()
|
||
v_list = ['fname', 'play', 'file_password', 'data']
|
||
for n in args.get_items().keys():
|
||
if not n in v_list:
|
||
return public.returnJson(
|
||
False, 'There can be no redundant parameters'), json_header
|
||
if not re.match(r"^[\w\.]+$", token): return abort(404)
|
||
find = public.M('download_token').where('token=?', (token,)).find()
|
||
|
||
if not find: return abort(404)
|
||
if time.time() > int(find['expire']): return abort(404)
|
||
|
||
if not os.path.exists(find['filename']): return abort(404)
|
||
if find['password'] and not token in session:
|
||
if 'file_password' in args:
|
||
if not re.match(r"^\w+$", args.file_password):
|
||
return public.ReturnJson(False,
|
||
'Wrong password!'), json_header
|
||
if re.match(r"^\d+$", args.file_password):
|
||
args.file_password = str(int(args.file_password))
|
||
args.file_password += ".0"
|
||
if args.file_password != str(find['password']):
|
||
return public.ReturnJson(False,
|
||
'Wrong password!'), json_header
|
||
session[token] = 1
|
||
session['down'] = True
|
||
else:
|
||
pdata = {
|
||
"to_path": "",
|
||
"src_path": find['filename'],
|
||
"password": True,
|
||
"filename": find['filename'].split('/')[-1],
|
||
"ps": find['ps'],
|
||
"total": find['total'],
|
||
"token": find['token'],
|
||
"expire": public.format_date(times=find['expire'])
|
||
}
|
||
session['down'] = True
|
||
return render_template('down.html', data=pdata)
|
||
|
||
if not find['password']:
|
||
session['down'] = True
|
||
session[token] = 1
|
||
|
||
if session[token] != 1:
|
||
return abort(404)
|
||
|
||
filename = find['filename']
|
||
if fname:
|
||
filename = os.path.join(filename, fname)
|
||
if not public.path_safe_check(fname, False): return abort(404)
|
||
if os.path.isdir(filename):
|
||
return get_dir_down(filename, token, find)
|
||
else:
|
||
if os.path.isdir(filename):
|
||
return get_dir_down(filename, token, find)
|
||
|
||
if request.args.get('play') == 'true':
|
||
import panelVideo
|
||
start, end = panelVideo.get_range(request)
|
||
return panelVideo.partial_response(filename, start, end)
|
||
else:
|
||
mimetype = "application/octet-stream"
|
||
extName = filename.split('.')[-1]
|
||
if extName in ['png', 'gif', 'jpeg', 'jpg']: mimetype = None
|
||
b_name = os.path.basename(filename)
|
||
return send_file(filename,
|
||
mimetype=mimetype,
|
||
as_attachment=True,
|
||
download_name=b_name,
|
||
max_age=0)
|
||
except:
|
||
return abort(404)
|
||
|
||
|
||
@app.route('/database/mongodb/<def_name>', methods=method_all)
|
||
@app.route('/database/pgsql/<def_name>', methods=method_all)
|
||
@app.route('/database/redis/<def_name>', methods=method_all)
|
||
@app.route('/database/sqlite/<def_name>', methods=method_all)
|
||
@app.route('/database/sqlserver/<def_name>', methods=method_all)
|
||
def databaseModel(def_name):
|
||
if request.method not in ['GET', 'POST']: return
|
||
path_split = request.path.split("/")
|
||
if len(path_split) < 4: return
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
from panelDatabaseController import DatabaseController
|
||
project_obj = DatabaseController()
|
||
defs = ('model',)
|
||
get = get_input()
|
||
get.action = 'model'
|
||
get.mod_name = path_split[2]
|
||
get.def_name = def_name
|
||
|
||
return publicObject(project_obj, defs, None, get)
|
||
|
||
|
||
# 系统安全模型页面
|
||
@app.route('/safe/firewall/<def_name>', methods=method_all)
|
||
@app.route('/safe/freeip/<def_name>', methods=method_all)
|
||
@app.route('/safe/ips/<def_name>', methods=method_all)
|
||
@app.route('/safe/security/<def_name>', methods=method_all)
|
||
@app.route('/safe/ssh/<def_name>', methods=method_all)
|
||
@app.route('/safe/syslog/<def_name>', methods=method_all)
|
||
def safeModel(def_name):
|
||
if request.method not in ['GET', 'POST']: return
|
||
path_split = request.path.split("/")
|
||
if len(path_split) < 4: return
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
from panelSafeController import SafeController
|
||
project_obj = SafeController()
|
||
defs = ('model',)
|
||
get = get_input()
|
||
get.action = 'model'
|
||
get.mod_name = path_split[2]
|
||
get.def_name = def_name
|
||
|
||
return publicObject(project_obj, defs, None, get)
|
||
|
||
|
||
# 通用模型路由
|
||
@app.route('/<index>/<mod_name>/<def_name>', methods=method_all)
|
||
def allModule(index, mod_name, def_name):
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
p_path = public.get_plugin_path() + '/' + index
|
||
if os.path.exists(p_path):
|
||
return panel_other(index, mod_name, def_name)
|
||
|
||
from panelController import Controller
|
||
controller_obj = Controller()
|
||
defs = ('model',)
|
||
get = get_input()
|
||
get.model_index = index
|
||
get.action = 'model'
|
||
get.mod_name = mod_name
|
||
get.def_name = def_name
|
||
|
||
return publicObject(controller_obj, defs, None, get)
|
||
|
||
|
||
@app.route('/public', methods=method_all)
|
||
def panel_public():
|
||
get = get_input()
|
||
if len("{}".format(get.get_items())) > 1024 * 32:
|
||
return 'ERROR'
|
||
|
||
# 获取ping测试
|
||
if 'get_ping' in get:
|
||
try:
|
||
import panelPing
|
||
p = panelPing.Test()
|
||
get = p.check(get)
|
||
if not get: return 'ERROR'
|
||
result = getattr(p, get['act'])(get)
|
||
result_type = type(result)
|
||
if str(result_type).find('Response') != -1: return result
|
||
return public.getJson(result), json_header
|
||
except:
|
||
return abort(404)
|
||
|
||
if public.cache_get(
|
||
public.Md5(
|
||
uuid.UUID(int=uuid.getnode()).hex[-12:] +
|
||
public.GetClientIp())) != 'check':
|
||
return abort(404)
|
||
global admin_check_auth, admin_path, route_path, admin_path_file
|
||
if admin_path != '/bt' and os.path.exists(
|
||
admin_path_file) and not 'admin_auth' in session:
|
||
return abort(404)
|
||
v_list = ['fun', 'name', 'filename', 'data', 'secret_key']
|
||
for n in get.get_items().keys():
|
||
if not n in v_list:
|
||
return abort(404)
|
||
|
||
get.client_ip = public.GetClientIp()
|
||
num_key = get.client_ip + '_wxapp'
|
||
if not public.get_error_num(num_key, 10):
|
||
return public.return_msg_gettext(
|
||
False,
|
||
'10 consecutive authentication failures are prohibited for 1 hour')
|
||
if not hasattr(get, 'name'): get.name = ''
|
||
if not hasattr(get, 'fun'): return abort(404)
|
||
if not public.path_safe_check("%s/%s" % (get.name, get.fun)):
|
||
return abort(404)
|
||
if get.fun in ['login_qrcode', 'is_scan_ok', 'set_login']:
|
||
# 检查是否验证过安全入口
|
||
if admin_path != '/bt' and os.path.exists(
|
||
admin_path_file) and not 'admin_auth' in session:
|
||
return abort(404)
|
||
# 验证是否绑定了设备
|
||
if not public.check_app('app'):
|
||
return public.return_msg_gettext(False, 'Unbound user')
|
||
import wxapp
|
||
pluwx = wxapp.wxapp()
|
||
checks = pluwx._check(get)
|
||
if type(checks) != bool or not checks:
|
||
public.set_error_num(num_key)
|
||
return public.getJson(checks), json_header
|
||
data = public.getJson(eval('pluwx.' + get.fun + '(get)'))
|
||
return data, json_header
|
||
else:
|
||
return abort(404)
|
||
|
||
|
||
@app.route('/<name>/<fun>', methods=method_all)
|
||
@app.route('/<name>/<fun>/<path:stype>', methods=method_all)
|
||
def panel_other(name=None, fun=None, stype=None):
|
||
# 左侧栏路由
|
||
if name in ('site', 'database', 'docker', 'wp', 'mail', 'security', 'crontab', 'waf', 'setting', 'logs',
|
||
'monitor/system', 'control', 'binds', 'softs', 'modify_password', 'flow', 'ssl_domain', 'node'):
|
||
return index_new('{}/{}'.format(name, fun))
|
||
|
||
# 插件接口
|
||
if public.is_error_path():
|
||
return redirect('/error', 302)
|
||
if not name: return abort(404)
|
||
if not re.match(r"^[\w\-]+$", name): return abort(404)
|
||
if fun and not re.match(r"^[\w\-\.]+$", fun): return abort(404)
|
||
if name != "mail_sys" or fun != "send_mail_http.json":
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
if not stype:
|
||
tmp = fun.split('.')
|
||
fun = tmp[0]
|
||
if len(tmp) == 1: tmp.append('')
|
||
stype = tmp[1]
|
||
if fun:
|
||
if name == 'btwaf' and fun == 'index':
|
||
pass
|
||
if name == 'waf':
|
||
pass
|
||
elif name == 'firewall' and fun == 'get_file':
|
||
pass
|
||
elif fun == 'static':
|
||
pass
|
||
elif stype == 'html':
|
||
pass
|
||
else:
|
||
if public.get_csrf_cookie_token_key(
|
||
) in session and 'login' in session:
|
||
if not check_csrf():
|
||
return public.ReturnJson(
|
||
False,
|
||
'CSRF calibration failed, please login again'
|
||
), json_header
|
||
args = None
|
||
else:
|
||
p_path = public.get_plugin_path() + '/' + name
|
||
if not os.path.exists(p_path): return abort(404)
|
||
args = get_input()
|
||
args_list = [
|
||
'mail_from', 'password', 'mail_to', 'subject', 'content',
|
||
'subtype', 'data'
|
||
]
|
||
for k in args.get_items():
|
||
if not k in args_list: return abort(404)
|
||
|
||
is_accept = False
|
||
if not fun: fun = 'index.html'
|
||
if not stype:
|
||
tmp = fun.split('.')
|
||
fun = tmp[0]
|
||
if len(tmp) == 1: tmp.append('')
|
||
stype = tmp[1]
|
||
|
||
if not name: name = 'coll'
|
||
if not public.path_safe_check("%s/%s/%s" % (name, fun, stype)):
|
||
return abort(404)
|
||
if name.find('./') != -1 or not re.match(r"^[\w-]+$", name):
|
||
return abort(404)
|
||
if not name:
|
||
return public.returnJson(
|
||
False, 'Please pass in the plug-in name!'), json_header
|
||
p_path = public.get_plugin_path() + '/' + name
|
||
if not os.path.exists(p_path):
|
||
if name == 'btwaf' and fun == 'index':
|
||
pdata = {}
|
||
import panelPlugin
|
||
plu_panel = panelPlugin.panelPlugin()
|
||
plugin_list = plu_panel.get_cloud_list()
|
||
if not 'pro' in plugin_list: plugin_list['pro'] = -1
|
||
for p in plugin_list['list']:
|
||
if p['name'] in ['btwaf']:
|
||
if p['endtime'] != 0 and p['endtime'] < time.time():
|
||
pdata['error_msg'] = 1
|
||
break
|
||
return render_template('error3.html', data=pdata)
|
||
return abort(404)
|
||
|
||
# 是否响插件应静态文件
|
||
if fun == 'static':
|
||
if stype.find('./') != -1 or not os.path.exists(p_path + '/static'):
|
||
return abort(404)
|
||
s_file = p_path + '/static/' + stype
|
||
if s_file.find('..') != -1: return abort(404)
|
||
if not re.match(r"^[\w\./-]+$", s_file): return abort(404)
|
||
if not public.path_safe_check(s_file): return abort(404)
|
||
if not os.path.exists(s_file): return abort(404)
|
||
return send_file(s_file, conditional=True, etag=True)
|
||
|
||
# 准备参数
|
||
if not args: args = get_input()
|
||
args.client_ip = public.GetClientIp()
|
||
args.fun = fun
|
||
# 初始化插件对象
|
||
try:
|
||
|
||
import PluginLoader
|
||
try:
|
||
args.s = fun
|
||
data = PluginLoader.plugin_run(name, fun, args)
|
||
if isinstance(data, dict):
|
||
if 'status' in data and data['status'] == False and 'msg' in data:
|
||
if isinstance(data['msg'], str):
|
||
if data['msg'].find('加载失败') != -1 or data['msg'].find('Traceback ') == 0:
|
||
raise public.PanelError(data['msg'])
|
||
except Exception as ex:
|
||
if name == 'btwaf' and fun == 'index' and str(ex).find('未购买') != -1:
|
||
return render_template('error3.html', data={})
|
||
return public.get_error_object(None, plugin_name=name)
|
||
|
||
r_type = type(data)
|
||
if r_type in [Response, Resp]:
|
||
return data
|
||
|
||
# 处理响应
|
||
if stype == 'json': # 响应JSON
|
||
return public.getJson(data), json_header
|
||
elif stype == 'html': # 使用模板
|
||
t_path_root = p_path + '/templates/'
|
||
t_path = t_path_root + fun + '.html'
|
||
if not os.path.exists(t_path):
|
||
return public.returnJson(
|
||
False,
|
||
'The specified template does not exist!'), json_header
|
||
t_body = public.readFile(t_path)
|
||
|
||
# 处理模板包含
|
||
rep = r'{%\s?include\s"(.+)"\s?%}'
|
||
includes = re.findall(rep, t_body)
|
||
for i_file in includes:
|
||
filename = p_path + '/templates/' + i_file
|
||
i_body = 'ERROR: File ' + filename + ' does not exists.'
|
||
if os.path.exists(filename):
|
||
i_body = public.readFile(filename)
|
||
t_body = re.sub(rep.replace('(.+)', i_file), i_body, t_body)
|
||
|
||
return render_template_string(t_body, data=data)
|
||
else: # 直接响应插件返回值,可以是任意flask支持的响应类型
|
||
r_type = type(data)
|
||
if r_type == dict:
|
||
if name == 'btwaf' and 'msg' in data:
|
||
return render_template('error3.html',
|
||
data={"error_msg": data['msg']})
|
||
return public.returnJson(
|
||
False,
|
||
public.getMsg('Bad return type [{}]').format(r_type)), json_header
|
||
# public.getMsg('PUBLIC_ERR_RETURN')), json_header
|
||
return data
|
||
except:
|
||
return public.get_error_info()
|
||
return public.get_error_object(None, plugin_name=name)
|
||
|
||
|
||
@app.route('/hook', methods=method_all)
|
||
def panel_hook():
|
||
# webhook接口
|
||
get = get_input()
|
||
if not os.path.exists('plugin/webhook'):
|
||
return abort(404)
|
||
public.package_path_append('plugin/webhook')
|
||
import webhook_main
|
||
return public.getJson(webhook_main.webhook_main().RunHook(get))
|
||
|
||
|
||
@app.route('/install', methods=method_all)
|
||
def install():
|
||
# 初始化面板接口
|
||
if public.is_spider(): return abort(404)
|
||
if not os.path.exists('install.pl'): return redirect('/login')
|
||
if public.M('config').where("id=?", ('1',)).getField('status') == 1:
|
||
if os.path.exists('install.pl'): os.remove('install.pl')
|
||
session.clear()
|
||
return redirect('/login')
|
||
ret_login = os.path.join('/', admin_path)
|
||
if admin_path == '/' or admin_path == '/bt': ret_login = '/login'
|
||
session['admin_path'] = False
|
||
session['login'] = False
|
||
if request.method == method_get[0]:
|
||
if not os.path.exists('install.pl'): return redirect(ret_login)
|
||
data = {}
|
||
data['status'] = os.path.exists('install.pl')
|
||
data['username'] = public.GetRandomString(8).lower()
|
||
return render_template('install.html', data=data)
|
||
|
||
elif request.method == method_post[0]:
|
||
if not os.path.exists('install.pl'): return redirect(ret_login)
|
||
get = get_input()
|
||
if not hasattr(get, 'bt_username'):
|
||
return public.get_msg_gettext('The user name cannot be empty!')
|
||
if not get.bt_username:
|
||
return public.get_msg_gettext('The user name cannot be empty!')
|
||
if not hasattr(get, 'bt_password1'):
|
||
return public.get_msg_gettext('Password can not be blank!')
|
||
if not get.bt_password1:
|
||
return public.get_msg_gettext('Password can not be blank!')
|
||
if get.bt_password1 != get.bt_password2:
|
||
return public.get_msg_gettext(
|
||
'The passwords entered twice do not match, please re-enter!')
|
||
public.M('users').where("id=?", (1,)).save(
|
||
'username,password',
|
||
(get.bt_username,
|
||
public.password_salt(public.md5(get.bt_password1.strip()),
|
||
uid=1)))
|
||
os.remove('install.pl')
|
||
public.M('config').where("id=?", ('1',)).setField('status', 1)
|
||
data = {}
|
||
data['status'] = os.path.exists('install.pl')
|
||
data['username'] = get.bt_username
|
||
return render_template('install.html', data=data)
|
||
|
||
|
||
# ==================================================#
|
||
|
||
# ======================公共方法区域START============================#
|
||
|
||
|
||
def get_dir_down(filename, token, find):
|
||
# 获取分享目录信息
|
||
import files
|
||
args = public.dict_obj()
|
||
args.path = filename
|
||
args.share = True
|
||
to_path = filename.replace(find['filename'], '').strip('/')
|
||
|
||
if request.args.get('play') == 'true':
|
||
pdata = files.files().get_videos(args)
|
||
return public.GetJson(pdata), json_header
|
||
else:
|
||
pdata = files.files().GetDir(args)
|
||
pdata['token'] = token
|
||
pdata['ps'] = find['ps']
|
||
pdata['src_path'] = find['filename']
|
||
pdata['to_path'] = to_path
|
||
if find['expire'] < (time.time() + (86400 * 365 * 10)):
|
||
pdata['expire'] = public.format_date(times=find['expire'])
|
||
else:
|
||
pdata['expire'] = public.get_msg_gettext('Never Expires')
|
||
pdata['filename'] = (find['filename'].split('/')[-1] + '/' +
|
||
to_path).strip('/')
|
||
return render_template('down.html', data=pdata, to_size=public.to_size)
|
||
|
||
|
||
def get_phpmyadmin_dir():
|
||
# 获取phpmyadmin目录
|
||
path = public.GetConfigValue('setup_path') + '/phpmyadmin'
|
||
if not os.path.exists(path): return None
|
||
phpport = '888'
|
||
try:
|
||
import re
|
||
if session['webserver'] == 'nginx':
|
||
filename = public.GetConfigValue(
|
||
'setup_path') + '/nginx/conf/nginx.conf'
|
||
conf = public.readFile(filename)
|
||
rep = r"listen\s+([0-9]+)\s*;"
|
||
rtmp = re.search(rep, conf)
|
||
if rtmp:
|
||
phpport = rtmp.groups()[0]
|
||
if session['webserver'] == 'apache':
|
||
filename = public.GetConfigValue(
|
||
'setup_path') + '/apache/conf/extra/httpd-vhosts.conf'
|
||
conf = public.readFile(filename)
|
||
rep = r"Listen\s+([0-9]+)\s*\n"
|
||
rtmp = re.search(rep, conf)
|
||
if rtmp:
|
||
phpport = rtmp.groups()[0]
|
||
if session['webserver'] == 'openlitespeed':
|
||
filename = public.GetConfigValue(
|
||
'setup_path') + '/panel/vhost/openlitespeed/listen/888.conf'
|
||
public.writeFile('/tmp/2', filename)
|
||
conf = public.readFile(filename)
|
||
rep = r"address\s*\*\:\s*(\d+)"
|
||
rtmp = re.search(rep, conf)
|
||
if rtmp:
|
||
phpport = rtmp.groups()[0]
|
||
except:
|
||
pass
|
||
|
||
for filename in os.listdir(path):
|
||
filepath = path + '/' + filename
|
||
if os.path.isdir(filepath):
|
||
if filename[0:10] == 'phpmyadmin':
|
||
return str(filename), phpport
|
||
return None
|
||
|
||
|
||
class run_exec:
|
||
# 模块访问对像
|
||
def run(self, toObject, defs, get):
|
||
result = None
|
||
|
||
if not get.action in defs:
|
||
return public.ReturnJson(
|
||
False, 'Specific parameters are invalid!'), json_header
|
||
# if get.action == 'change':
|
||
# public.print_log("查询 666 toObject --{}".format(vars(toObject)))
|
||
# # {'mail': <send_mail.send_mail object at 0x7f9fc98737d0>, '_config__mail_list': []}
|
||
# public.print_log("查询 666 get.action --{}".format(get.action))
|
||
result = getattr(toObject, get.action)(get)
|
||
if not hasattr(get, 'html') and not hasattr(get, 's_module'):
|
||
r_type = type(result)
|
||
if r_type in [Response, Resp]: return result
|
||
result = public.GetJson(result), json_header
|
||
|
||
if g.is_aes:
|
||
result = public.aes_encrypt(result[0], g.aes_key), json_header
|
||
|
||
return result
|
||
|
||
|
||
def check_csrf():
|
||
# CSRF校验
|
||
if app.config['DEBUG']: return True
|
||
http_token = request.headers.get('x-http-token')
|
||
if not http_token: return False
|
||
if http_token != public.get_csrf_sess_html_token_value(): return False
|
||
return True
|
||
|
||
|
||
def publicObject(toObject, defs, action=None, get=None, is_csrf=True):
|
||
try:
|
||
# 模块访问前置检查
|
||
if is_csrf and public.get_csrf_sess_html_token_value() and session.get('login', None):
|
||
if not check_csrf():
|
||
return public.ReturnJson(False, 'INIT_CSRF_ERR'), json_header
|
||
|
||
if not get:
|
||
get = get_input()
|
||
if action: get.action = action
|
||
|
||
if hasattr(get, 'path'):
|
||
get.path = get.path.replace('//', '/').replace('\\', '/')
|
||
if get.path.find('./') != -1:
|
||
return public.ReturnJson(False, 'Unsafe path'), json_header
|
||
if get.path.find('->') != -1:
|
||
get.path = get.path.split('->')[0].strip()
|
||
get.path = public.xssdecode(get.path)
|
||
if hasattr(get, 'filename'):
|
||
get.filename = public.xssdecode(get.filename)
|
||
|
||
if hasattr(get, 'sfile'):
|
||
get.sfile = get.sfile.replace('//', '/').replace('\\', '/')
|
||
get.sfile = public.xssdecode(get.sfile)
|
||
if hasattr(get, 'dfile'):
|
||
get.dfile = get.dfile.replace('//', '/').replace('\\', '/')
|
||
get.dfile = public.xssdecode(get.dfile)
|
||
|
||
if hasattr(toObject, 'site_path_check'):
|
||
if not toObject.site_path_check(get):
|
||
return public.ReturnJson(
|
||
False, "Overstepping one authority!"), json_header
|
||
return run_exec().run(toObject, defs, get)
|
||
except Exception as e:
|
||
return error_500(e)
|
||
|
||
|
||
def check_login(http_token=None):
|
||
# 检查是否登录面板
|
||
if cache.get('dologin'): return False
|
||
if 'login' in session:
|
||
loginStatus = session['login']
|
||
if loginStatus and http_token:
|
||
if public.get_csrf_sess_html_token_value() != http_token:
|
||
return False
|
||
return loginStatus
|
||
return False
|
||
|
||
|
||
def get_pd():
|
||
# 获取授权信息
|
||
return public.get_pd()
|
||
|
||
|
||
def send_authenticated():
|
||
# 发送http认证信息
|
||
request_host = public.GetHost()
|
||
result = Response(
|
||
'', 401,
|
||
{'WWW-Authenticate': 'Basic realm="%s"' % request_host.strip()})
|
||
if not 'login' in session and not 'admin_auth' in session: session.clear()
|
||
return result
|
||
|
||
|
||
# 取端口
|
||
def FtpPort():
|
||
# 获取FTP端口
|
||
if session.get('port'): return
|
||
import re
|
||
try:
|
||
file = public.GetConfigValue(
|
||
'setup_path') + '/pure-ftpd/etc/pure-ftpd.conf'
|
||
conf = public.readFile(file)
|
||
rep = r"\n#?\s*Bind\s+[0-9]+\.[0-9]+\.[0-9]+\.+[0-9]+,([0-9]+)"
|
||
port = re.search(rep, conf).groups()[0]
|
||
except:
|
||
port = '21'
|
||
session['port'] = port
|
||
|
||
|
||
def is_login(result):
|
||
# 判断是否登录2
|
||
if 'login' in session:
|
||
if session['login'] == True:
|
||
pass
|
||
return result
|
||
|
||
|
||
# js随机数模板使用,用于不更新版本号时更新前端文件不需要用户强制刷新浏览器
|
||
def get_js_random():
|
||
js_random = public.readFile('data/js_random.pl')
|
||
if not js_random or js_random == '1':
|
||
js_random = public.GetRandomString(16)
|
||
public.writeFile('data/js_random.pl', js_random)
|
||
return js_random
|
||
|
||
|
||
# 获取输入数据
|
||
def get_input():
|
||
data = public.dict_obj()
|
||
exludes = ['blob']
|
||
|
||
try:
|
||
for key in request.args.keys():
|
||
data.set(key, str(request.args.get(key, '')))
|
||
except:
|
||
pass
|
||
|
||
try:
|
||
for key in request.form.keys():
|
||
if key in exludes:
|
||
continue
|
||
|
||
data.set(key, str(request.form.get(key, '')))
|
||
except Exception as ex:
|
||
try:
|
||
post = request.form.to_dict()
|
||
for key in post.keys():
|
||
if key in exludes: continue
|
||
data.set(key, str(post[key]))
|
||
except:
|
||
pass
|
||
|
||
# 获取json数据
|
||
if request.is_json:
|
||
try:
|
||
json_data = request.get_json()
|
||
for k in json_data.keys():
|
||
data[k] = json_data[k]
|
||
except:
|
||
pass
|
||
|
||
if 'form_data' in g:
|
||
try:
|
||
for k in g.form_data.keys():
|
||
data.set(k, str(g.form_data[k]))
|
||
except:
|
||
pass
|
||
|
||
if not hasattr(data, 'data'):
|
||
data.data = []
|
||
|
||
return data
|
||
|
||
|
||
# 取数据对象
|
||
def get_input_data(data):
|
||
pdata = public.dict_obj()
|
||
for key in data.keys():
|
||
pdata[key] = str(data[key])
|
||
return pdata
|
||
|
||
|
||
# 检查Token
|
||
def check_token(data):
|
||
# 已作废
|
||
pluginPath = 'plugin/safelogin/token.pl'
|
||
if not os.path.exists(pluginPath): return False
|
||
from urllib import unquote
|
||
from binascii import unhexlify
|
||
from json import loads
|
||
|
||
result = unquote(unhexlify(data))
|
||
token = public.readFile(pluginPath).strip()
|
||
|
||
result = loads(result)
|
||
if not result: return False
|
||
if result['token'] != token: return False
|
||
return result
|
||
|
||
|
||
# ======================公共方法区域END============================#
|
||
|
||
# --------------------- websocket START -------------------------- #
|
||
|
||
|
||
@sockets.route('/workorder_client')
|
||
def workorder_client(ws):
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
|
||
get = ws.receive()
|
||
get = json.loads(get)
|
||
if not check_csrf_websocket(ws, get):
|
||
return
|
||
|
||
import panelWorkorder
|
||
toObject = panelWorkorder.panelWorkorder()
|
||
get = get_input()
|
||
toObject.client(ws, get)
|
||
|
||
|
||
|
||
@sockets.route('/ws_panel')
|
||
def ws_panel(ws):
|
||
'''
|
||
@name 面板接口ws入口
|
||
@author hwliang<2021-07-24>
|
||
@param ws<ws_parameter> websocket会话对像
|
||
@return void
|
||
'''
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
|
||
get = ws.receive()
|
||
get = json.loads(get)
|
||
if not check_csrf_websocket(ws, get): return
|
||
|
||
while True:
|
||
pdata = ws.receive()
|
||
if pdata == '{}': break
|
||
data = json.loads(pdata)
|
||
get = public.to_dict_obj(data)
|
||
get._ws = ws
|
||
p = threading.Thread(target=ws_panel_thread, args=(get,))
|
||
p.start()
|
||
|
||
|
||
def ws_panel_thread(get):
|
||
'''
|
||
@name 面板管理ws线程
|
||
@author hwliang<2021-07-24>
|
||
@param get<dict> 请求参数
|
||
@return void
|
||
'''
|
||
|
||
if not hasattr(get, 'ws_callback'):
|
||
get._ws.send(
|
||
public.getJson(public.return_status_code(1001, 'ws_callback')))
|
||
return
|
||
if not hasattr(get, 'mod_name'):
|
||
get._ws.send(
|
||
public.getJson(public.return_status_code(1001, 'mod_name')))
|
||
return
|
||
if not hasattr(get, 'def_name'):
|
||
get._ws.send(
|
||
public.getJson(public.return_status_code(1001, 'def_name')))
|
||
return
|
||
get.mod_name = get.mod_name.strip()
|
||
get.def_name = get.def_name.strip()
|
||
check_str = '{}{}'.format(get.mod_name, get.def_name)
|
||
if not re.match(r"^\w+$", check_str) or get.mod_name in [
|
||
'public', 'common', 'db', 'db_mysql', 'downloadFile', 'jobs'
|
||
]:
|
||
get._ws.send(
|
||
public.getJson(
|
||
public.return_status_code(
|
||
1000, 'Unsafe mod_name, def_name parameter content')))
|
||
return
|
||
|
||
mod_file = '{}/{}.py'.format(public.get_class_path(), get.mod_name)
|
||
|
||
if not os.path.exists(mod_file):
|
||
get._ws.send(
|
||
public.getJson(
|
||
public.return_status_code(
|
||
1000, 'Specified module {} does not exist'.format(
|
||
get.mod_name))))
|
||
return
|
||
_obj = public.get_script_object(mod_file)
|
||
if not _obj:
|
||
get._ws.send(
|
||
public.getJson(
|
||
public.return_status_code(
|
||
1000, 'Specified module {} does not exist'.format(
|
||
get.mod_name))))
|
||
return
|
||
_cls = getattr(_obj, get.mod_name)
|
||
if not _cls:
|
||
get._ws.send(
|
||
public.getJson(
|
||
public.return_status_code(
|
||
1000,
|
||
'The {} object was not found in the {} module'.format(
|
||
get.mod_name, get.mod_name))))
|
||
return
|
||
_def = getattr(_cls(), get.def_name)
|
||
if not _def:
|
||
get._ws.send(
|
||
public.getJson(
|
||
public.return_status_code(
|
||
1000,
|
||
'The {} object was not found in the {} module'.format(
|
||
get.mod_name, get.def_name))))
|
||
return
|
||
result = {'callback': get.ws_callback, 'result': _def(get)}
|
||
get._ws.send(public.getJson(result))
|
||
|
||
|
||
@sockets.route('/ws_project')
|
||
def ws_project(ws):
|
||
'''
|
||
@name 项目管理ws入口
|
||
@author hwliang<2021-07-24>
|
||
@param ws<ws_parameter> websocket会话对像
|
||
@return void
|
||
'''
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
get = ws.receive()
|
||
get = json.loads(get)
|
||
if not check_csrf_websocket(ws, get): return
|
||
|
||
from panelProjectController import ProjectController
|
||
project_obj = ProjectController()
|
||
while True:
|
||
pdata = ws.receive()
|
||
if pdata in '{}': break
|
||
get = public.to_dict_obj(json.loads(pdata))
|
||
get._ws = ws
|
||
p = threading.Thread(target=ws_project_thread, args=(project_obj, get))
|
||
p.start()
|
||
|
||
|
||
# docker模块内用到的ws
|
||
@sockets.route('/ws_model')
|
||
def ws_model(ws):
|
||
'''
|
||
@name 模型控制器ws入口
|
||
@author hwliang<2021-07-24>
|
||
@param ws<ws_parameter> websocket会话对像
|
||
@return void
|
||
'''
|
||
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
get = ws.receive()
|
||
get = json.loads(get)
|
||
|
||
if not check_csrf_websocket(ws, get): return
|
||
|
||
from panelController import Controller
|
||
model_obj = Controller()
|
||
while True:
|
||
pdata = ws.receive()
|
||
if pdata in ['{}', {}, None, '']:
|
||
ws.send(json.dumps(public.return_status_code(1000, '请求参数不能为空')))
|
||
break
|
||
try:
|
||
get = public.to_dict_obj(json.loads(pdata))
|
||
except:
|
||
request.form = {
|
||
"error": pdata
|
||
}
|
||
raise Exception('json load error !')
|
||
get._ws = ws
|
||
get.model_index = get.model_index.strip()
|
||
p = threading.Thread(target=ws_model_thread, args=(model_obj, get))
|
||
p.start()
|
||
|
||
def ws_mod_thread(_obj, get):
|
||
'''
|
||
@name 模型控制器ws线程
|
||
@author hwliang<2021-07-24>
|
||
@param _obj<Controller> 控制器对像
|
||
@param get<dict> 请求参数
|
||
@return void
|
||
'''
|
||
mod_result = _obj.model(get)
|
||
if mod_result is None:
|
||
return
|
||
try:
|
||
result = {'callback': get.ws_callback, 'result': _obj.model(get)}
|
||
get._ws.send(public.getJson(result))
|
||
except:
|
||
return
|
||
|
||
|
||
def ws_model_thread(_obj, get):
|
||
'''
|
||
@name 模型控制器ws线程
|
||
@author hwliang<2021-07-24>
|
||
@param _obj<Controller> 控制器对像
|
||
@param get<dict> 请求参数
|
||
@return void
|
||
'''
|
||
if not hasattr(get, 'ws_callback'):
|
||
get._ws.send(
|
||
public.getJson(public.return_status_code(1001, 'ws_callback')))
|
||
return
|
||
result = {'callback': get.ws_callback, 'result': _obj.model(get)}
|
||
get._ws.send(public.getJson(result))
|
||
|
||
|
||
def ws_project_thread(_obj, get):
|
||
'''
|
||
@name 项目管理ws线程
|
||
@author hwliang<2021-07-24>
|
||
@param _obj<ProjectController> 项目管理控制器对像
|
||
@param get<dict> 请求参数
|
||
@return void
|
||
'''
|
||
if not hasattr(get, 'ws_callback'):
|
||
get._ws.send(
|
||
public.getJson(public.return_status_code(1001, 'ws_callback')))
|
||
return
|
||
result = {'callback': get.ws_callback, 'result': _obj.model(get)}
|
||
get._ws.send(public.getJson(result))
|
||
|
||
|
||
import subprocess
|
||
|
||
sock_pids = {}
|
||
|
||
|
||
@sockets.route('/sock_shell')
|
||
def sock_shell(ws):
|
||
'''
|
||
@name 执行指定命令,实时输出命令执行结果
|
||
@author hwliang<2021-07-19>
|
||
@return void
|
||
|
||
示例:
|
||
p = new WebSocket('ws://192.168.1.247:8888/sock_shell')
|
||
p.send('ping www.yakpanel.com -c 100')
|
||
'''
|
||
comReturn = comm.local()
|
||
if comReturn:
|
||
ws.send(str(comReturn))
|
||
return
|
||
kill_closed()
|
||
get = ws.receive()
|
||
get = json.loads(get)
|
||
if not check_csrf_websocket(ws, get): return
|
||
|
||
t = None
|
||
try:
|
||
while True:
|
||
cmdstring = ws.receive()
|
||
if cmdstring in ['stop', 'error'] or not cmdstring:
|
||
break
|
||
t = threading.Thread(target=sock_recv, args=(cmdstring, ws))
|
||
t.start()
|
||
kill_closed()
|
||
except:
|
||
kill_closed()
|
||
|
||
|
||
def kill_closed():
|
||
'''
|
||
@name 关闭已关闭的连接
|
||
@author hwliang<2021-07-24>
|
||
@return void
|
||
'''
|
||
global sock_pids
|
||
import psutil
|
||
pids = psutil.pids()
|
||
keys = sock_pids.copy().keys()
|
||
for pid in keys:
|
||
if hasattr(sock_pids[pid], 'closed'):
|
||
is_closed = sock_pids[pid].closed
|
||
else:
|
||
is_closed = not sock_pids[pid].connected
|
||
|
||
logging.debug("PID: {} , sock_stat: {}".format(pid, is_closed))
|
||
if not is_closed: continue
|
||
|
||
if pid in pids:
|
||
try:
|
||
p = psutil.Process(pid)
|
||
for cp in p.children():
|
||
cp.kill()
|
||
p.kill()
|
||
logging.debug("killed: {}".format(pid))
|
||
sock_pids.pop(pid)
|
||
except:
|
||
pass
|
||
else:
|
||
sock_pids.pop(pid)
|
||
|
||
|
||
def sock_recv(cmdstring, ws):
|
||
global sock_pids
|
||
try:
|
||
p = subprocess.Popen(cmdstring + " 2>&1",
|
||
close_fds=True,
|
||
shell=True,
|
||
bufsize=4096,
|
||
stdout=subprocess.PIPE,
|
||
stderr=subprocess.PIPE)
|
||
sock_pids[p.pid] = ws
|
||
kill_closed()
|
||
while p.poll() == None:
|
||
send_line = p.stdout.readline().decode()
|
||
if not send_line or send_line.find('tail: ') != -1: continue
|
||
# ws.send(send_line)
|
||
# ws.send(p.stdout.read().decode())
|
||
if ws.connected:
|
||
ws.send(send_line)
|
||
if ws.connected:
|
||
ws.send(p.stdout.read().decode())
|
||
except:
|
||
kill_closed()
|
||
|
||
|
||
@app.route('/close_sock_shell', methods=method_all)
|
||
def close_sock_shell():
|
||
'''
|
||
@name 关闭指定命令
|
||
@author hwliang<2021-07-19>
|
||
@param cmdstring<string> 完整命令行
|
||
@return dict
|
||
示例:
|
||
$.post('/close_sock_shell',{cmdstring:'ping www.yakpanel.com -c 100'})
|
||
'''
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
args = get_input()
|
||
if not check_csrf():
|
||
return public.ReturnJson(False, 'INIT_CSRF_ERR'), json_header
|
||
cmdstring = args.cmdstring.strip()
|
||
skey = public.md5(cmdstring)
|
||
pid = cache.get(skey)
|
||
if not pid:
|
||
return json.dumps(
|
||
public.return_data(
|
||
False, [], error_msg='The specified sock has been terminated!')
|
||
), json_header
|
||
os.kill(pid, 9)
|
||
cache.delete(skey)
|
||
return json.dumps(public.return_data(True,
|
||
'Successful operation!')), json_header
|
||
|
||
|
||
def check_csrf_websocket(ws, args):
|
||
'''
|
||
@name 检查websocket是否被csrf攻击
|
||
@author hwliang<2021-07-24>
|
||
@param ws<WebSocket> websocket对像
|
||
@return void
|
||
'''
|
||
if g.is_aes: return True
|
||
if g.api_request: return True
|
||
if public.is_debug(): return True
|
||
is_success = True
|
||
if not 'x-http-token' in args:
|
||
is_success = False
|
||
|
||
if is_success:
|
||
if public.get_csrf_sess_html_token_value() != args['x-http-token']:
|
||
is_success = False
|
||
|
||
if not is_success:
|
||
ws.send('token error')
|
||
return False
|
||
|
||
return True
|
||
|
||
|
||
@sockets.route('/webssh')
|
||
def webssh(ws):
|
||
# YakPanel终端连接
|
||
comReturn = comm.local()
|
||
if comReturn:
|
||
ws.send(str(comReturn))
|
||
return
|
||
if not ws: return 'False'
|
||
get = ws.receive()
|
||
if not get: return
|
||
get = json.loads(get)
|
||
if not check_csrf_websocket(ws, get):
|
||
return
|
||
|
||
import ssh_terminal
|
||
sp = ssh_terminal.ssh_host_admin()
|
||
if 'host' in get:
|
||
ssh_info = {}
|
||
ssh_info['host'] = get['host'].strip()
|
||
if 'port' in get:
|
||
ssh_info['port'] = int(get['port'])
|
||
if 'username' in get:
|
||
ssh_info['username'] = get['username'].strip()
|
||
if 'password' in get:
|
||
ssh_info['password'] = get['password'].strip()
|
||
if 'pkey' in get:
|
||
ssh_info['pkey'] = get['pkey'].strip()
|
||
|
||
if get['host'] in ['127.0.0.1', 'localhost']:
|
||
if not 'password' in ssh_info:
|
||
ssh_info = sp.get_ssh_info('127.0.0.1')
|
||
if not ssh_info: ssh_info = sp.get_ssh_info('localhost')
|
||
if not ssh_info: ssh_info = {"host": "127.0.0.1"}
|
||
if not 'port' in ssh_info:
|
||
ssh_info['port'] = public.get_ssh_port()
|
||
else:
|
||
ssh_info = sp.get_ssh_info('127.0.0.1')
|
||
if not ssh_info: ssh_info = sp.get_ssh_info('localhost')
|
||
if not ssh_info: ssh_info = {"host": "127.0.0.1"}
|
||
ssh_info['port'] = public.get_ssh_port()
|
||
|
||
if not ssh_info['host'] in ['127.0.0.1', 'localhost']:
|
||
if not 'username' in ssh_info:
|
||
ssh_info = sp.get_ssh_info(ssh_info['host'])
|
||
if not ssh_info:
|
||
ws.send(
|
||
'The specified host information is not found, please add it again!'
|
||
)
|
||
return
|
||
p = ssh_terminal.ssh_terminal()
|
||
p.run(ws, ssh_info)
|
||
del (p)
|
||
if ws.connected:
|
||
ws.close()
|
||
return 'False'
|
||
|
||
|
||
@sockets.route(route_v2 + '/pyenv_webssh')
|
||
def python_env_ssh(ws):
|
||
# py环境终端
|
||
comReturn = comm.local()
|
||
if comReturn:
|
||
ws.send(str(comReturn))
|
||
return
|
||
if not ws:
|
||
return 'False'
|
||
get = ws.receive()
|
||
if not get:
|
||
return
|
||
get = json.loads(get)
|
||
if not check_csrf_websocket(ws, get):
|
||
return
|
||
|
||
import ssh_terminal_v2 as ssh_terminal
|
||
from projectModelV2.pythonModel import PyenvSshTerminal
|
||
|
||
sp = ssh_terminal.ssh_host_admin()
|
||
ssh_info = sp.get_ssh_info('127.0.0.1')
|
||
if not ssh_info: ssh_info = sp.get_ssh_info('localhost')
|
||
if not ssh_info: ssh_info = {"host": "127.0.0.1"}
|
||
ssh_info['port'] = public.get_ssh_port()
|
||
|
||
p = PyenvSshTerminal()
|
||
p.run(ws, ssh_info)
|
||
del p
|
||
if ws.connected:
|
||
ws.close()
|
||
return 'False'
|
||
|
||
# --------------------- websocket END -------------------------- #
|
||
|
||
|
||
@app.route("/daily", methods=method_all)
|
||
def daily():
|
||
"""面板日报数据"""
|
||
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
|
||
import panelDaily
|
||
toObject = panelDaily.panelDaily()
|
||
|
||
defs = ("get_app_usage", "get_daily_data", "get_daily_list")
|
||
result = publicObject(toObject, defs)
|
||
return result
|
||
|
||
|
||
@app.route('/phpmyadmin/<path:path_full>', methods=method_all)
|
||
def pma_proxy(path_full=None):
|
||
'''
|
||
@name phpMyAdmin代理
|
||
@author hwliang<2022-01-19>
|
||
@return Response
|
||
'''
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
cache_key = 'pmd_port_path'
|
||
pmd = cache.get(cache_key)
|
||
if not pmd:
|
||
pmd = get_phpmyadmin_dir()
|
||
if not pmd:
|
||
return 'phpMyAdmin is not installed, please go to the [App Store] page to install it!'
|
||
pmd = list(pmd)
|
||
cache.set(cache_key, pmd, 10)
|
||
panel_pool = 'http://'
|
||
if request.url_root[:5] == 'https':
|
||
panel_pool = 'https://'
|
||
import ajax
|
||
ssl_info = ajax.ajax().get_phpmyadmin_ssl(None)
|
||
if ssl_info['status']:
|
||
pmd[1] = ssl_info['port']
|
||
else:
|
||
panel_pool = 'http://'
|
||
|
||
proxy_url = '{}127.0.0.1:{}/{}/'.format(
|
||
panel_pool, pmd[1], pmd[0]) + request.full_path.replace(
|
||
'/phpmyadmin/', '')
|
||
from panelHttpProxy import HttpProxy
|
||
px = HttpProxy()
|
||
return px.proxy(proxy_url)
|
||
|
||
|
||
@app.route("/adminer/<path:path_full>", methods=method_all)
|
||
def adminer_proxy(path_full=None):
|
||
"""
|
||
@name adminer代理
|
||
@return Response
|
||
"""
|
||
comReturn = comm.local()
|
||
if comReturn:
|
||
return comReturn
|
||
try:
|
||
from adminer.manager import AdminerManager
|
||
manager = AdminerManager()
|
||
if not manager.is_install:
|
||
return 'Adminer is not install, please install it first!'
|
||
path, port = manager.adminer_dir_port
|
||
except public.HintException as e:
|
||
return str(e)
|
||
|
||
endpoint = request.full_path.replace("/adminer/", "")
|
||
proxy_url = f"http://127.0.0.1:{port}/{path}/{endpoint}"
|
||
from panelHttpProxy import HttpProxy
|
||
px = HttpProxy()
|
||
return px.proxy(proxy_url, True)
|
||
|
||
|
||
@app.route('/p/<int:port>', methods=method_all)
|
||
@app.route('/p/<int:port>/', methods=method_all)
|
||
@app.route('/p/<int:port>/<path:full_path>', methods=method_all)
|
||
def proxy_port(port, full_path=None):
|
||
'''
|
||
@name 代理指定端口
|
||
@author hwliang<2022-01-19>
|
||
@return Response
|
||
'''
|
||
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
full_path = request.full_path.replace('/p/{}/'.format(port),
|
||
'').replace('/p/{}'.format(port), '')
|
||
uri = '{}/{}'.format(port, full_path)
|
||
uri = uri.replace('//', '/')
|
||
proxy_url = 'http://127.0.0.1:{}'.format(uri)
|
||
from panelHttpProxy import HttpProxy
|
||
px = HttpProxy()
|
||
return px.proxy(proxy_url)
|
||
|
||
|
||
@app.route('/push', methods=method_all)
|
||
def push(pdata=None):
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import panelPush
|
||
toObject = panelPush.panelPush()
|
||
defs = ('set_push_status', 'get_push_msg_list', 'get_modules_list',
|
||
'install_module', 'uninstall_module', 'get_module_template',
|
||
'set_push_config', 'get_push_config', 'del_push_config',
|
||
'get_module_logs', 'get_module_config', 'get_push_list',
|
||
'get_push_logs')
|
||
result = publicObject(toObject, defs, None, pdata)
|
||
return result
|
||
|
||
# ===========================================================v2路由区start===========================================================#
|
||
# docker模块内用到的ws
|
||
@sockets.route(route_v2 + '/ws_model')
|
||
def ws_model_v2(ws):
|
||
'''
|
||
@name 模型控制器ws入口
|
||
@author hwliang<2021-07-24>
|
||
@param ws<ws_parameter> websocket会话对像
|
||
@return void
|
||
'''
|
||
# 开发时暂时注释----------------
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
|
||
get = ws.receive()
|
||
get = json.loads(get)
|
||
|
||
# 开发时暂时注释----------------
|
||
if not check_csrf_websocket(ws, get): return
|
||
|
||
from panelControllerV2 import Controller
|
||
model_obj = Controller()
|
||
while True:
|
||
pdata = ws.receive()
|
||
if pdata in ['{}', {}, None, '']:
|
||
ws.send(json.dumps(public.return_status_code(1000, 'The request parameter cannot be null')))
|
||
break
|
||
try:
|
||
get = public.to_dict_obj(json.loads(pdata))
|
||
except:
|
||
request.form = {
|
||
"error": pdata
|
||
}
|
||
raise Exception('json load error !')
|
||
get._ws = ws
|
||
get.model_index = get.model_index.strip()
|
||
p = threading.Thread(target=ws_model_thread, args=(model_obj, get))
|
||
p.start()
|
||
|
||
# 2024/2/19 上午 10:34 新场景模型控制器ws入口,无默认return
|
||
@sockets.route(route_v2 + '/ws_modsoc')
|
||
def ws_modsoc(ws):
|
||
'''
|
||
@name 新场景模型控制器ws入口
|
||
@author wzz<2024-02-19>
|
||
@param ws<ws_parameter> websocket会话对像
|
||
@return void
|
||
'''
|
||
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
get = ws.receive()
|
||
get = json.loads(get)
|
||
|
||
if not check_csrf_websocket(ws, get):
|
||
return
|
||
|
||
# try:
|
||
# from panelController import Controller
|
||
# model_obj = Controller()
|
||
from mod.modController import Controller
|
||
model_obj = Controller()
|
||
while True:
|
||
pdata = ws.receive()
|
||
if pdata in ['{}', {}, None, '']:
|
||
ws.send(json.dumps(public.return_status_code(1000, 'The request parameter cannot be empty')))
|
||
break
|
||
try:
|
||
get = public.to_dict_obj(json.loads(pdata))
|
||
except:
|
||
request.form = {
|
||
"error": pdata
|
||
}
|
||
raise Exception('json load error !')
|
||
get._ws = ws
|
||
get.model_index = "mod"
|
||
p = threading.Thread(target=ws_mod_thread_v2, args=(model_obj, get))
|
||
p.start()
|
||
# except:
|
||
# import traceback
|
||
# public.print_log(traceback.format_exc())
|
||
|
||
|
||
def ws_mod_thread_v2(_obj, get):
|
||
'''
|
||
@name 模型控制器ws线程
|
||
@author hwliang<2021-07-24>
|
||
@param _obj<Controller> 控制器对像
|
||
@param get<dict> 请求参数
|
||
@return void
|
||
'''
|
||
try:
|
||
mod_result = _obj.model(get)
|
||
if mod_result is None:
|
||
return
|
||
result = {'callback': get.get("ws_callback", get.get("callback", "")), 'result': mod_result}
|
||
if get._ws.connected:
|
||
get._ws.send(public.getJson(result))
|
||
|
||
except:
|
||
if public.is_debug():
|
||
public.print_error()
|
||
return
|
||
|
||
# ======================普通路由区start============================#
|
||
|
||
|
||
@app.route(route_v2 + '/', methods=method_all)
|
||
def home_v2():
|
||
# 面板首页
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
data = {}
|
||
data[public.to_string([112,
|
||
100])], data['pro_end'], data['ltd_end'] = get_pd()
|
||
data['siteCount'] = public.M('sites').count()
|
||
data['ftpCount'] = public.M('ftps').count()
|
||
data['databaseCount'] = public.M('databases').count()
|
||
data['lan'] = public.GetLan('index')
|
||
data['js_random'] = get_js_random()
|
||
return render_template('index.html', data=data)
|
||
|
||
|
||
@app.route(route_v2 + '/xterm', methods=method_all)
|
||
def xterm_v2():
|
||
# YakPanel终端管理
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
if request.method == method_get[0]:
|
||
import system_v2
|
||
data = system_v2.system().GetConcifInfo()
|
||
return render_template('xterm.html', data=data)
|
||
import ssh_terminal_v2
|
||
ssh_host_admin = ssh_terminal_v2.ssh_host_admin()
|
||
defs = ('get_host_list', 'get_host_find', 'modify_host', 'create_host',
|
||
'remove_host', 'set_sort', 'get_command_list', 'create_command',
|
||
'get_command_find', 'modify_command', 'remove_command', 'test_ssh_connect')
|
||
return publicObject(ssh_host_admin, defs, None)
|
||
|
||
|
||
@app.route(route_v2 + '/modify_password', methods=method_get)
|
||
def modify_password_v2():
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
# if not session.get('password_expire',False): return redirect('/',302)
|
||
data = {}
|
||
g.title = 'The password has expired, please change it!'
|
||
# return render_template('modify_password.html', data=data)
|
||
return render_template('index1.html', data=data)
|
||
|
||
@app.route(route_v2 + '/site', methods=method_all)
|
||
def site_v2(pdata=None):
|
||
# 网站管理
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
if request.method == method_get[0] and not pdata:
|
||
# data = {}
|
||
import system_v2
|
||
data = system_v2.system().GetConcifInfo()
|
||
data['isSetup'] = True
|
||
data['lan'] = public.getLan('site')
|
||
data['js_random'] = get_js_random()
|
||
if os.path.exists(public.GetConfigValue('setup_path') + '/nginx') == False \
|
||
and os.path.exists(public.GetConfigValue('setup_path') + '/apache') == False \
|
||
and os.path.exists('/usr/local/lsws/bin/lswsctrl') == False:
|
||
data['isSetup'] = False
|
||
return render_template('site.html', data=data)
|
||
|
||
import panel_site_v2
|
||
siteObject = panel_site_v2.panelSite()
|
||
defs = (
|
||
'get_auto_restart_rph',
|
||
'remove_auto_restart_rph',
|
||
'auto_restart_rph',
|
||
'check_del_data',
|
||
'upload_csv',
|
||
'create_website_multiple',
|
||
'del_redirect_multiple',
|
||
'del_proxy_multiple',
|
||
'delete_dir_auth_multiple',
|
||
'delete_dir_bind_multiple',
|
||
'delete_domain_multiple',
|
||
'set_site_etime_multiple',
|
||
'set_site_php_version_multiple',
|
||
'delete_website_multiple',
|
||
'set_site_status_multiple',
|
||
'get_site_list',
|
||
'get_site_err_log',
|
||
'get_site_domains',
|
||
'GetRedirectFile',
|
||
'SaveRedirectFile',
|
||
'DeleteRedirect',
|
||
'GetRedirectList',
|
||
'CreateRedirect',
|
||
'ModifyRedirect',
|
||
"set_error_redirect",
|
||
'set_dir_auth',
|
||
'delete_dir_auth',
|
||
'get_dir_auth',
|
||
'modify_dir_auth_pass',
|
||
'reset_wp_db',
|
||
'export_domains',
|
||
'import_domains',
|
||
'GetSiteLogs',
|
||
'clear_logs',
|
||
'download_logs',
|
||
'GetSiteDomains',
|
||
'GetSecurity',
|
||
'SetSecurity',
|
||
'ProxyCache',
|
||
'CloseToHttps',
|
||
'HttpToHttps',
|
||
'SetEdate',
|
||
'SetRewriteTel',
|
||
'GetCheckSafe',
|
||
'CheckSafe',
|
||
'GetDefaultSite',
|
||
'SetDefaultSite',
|
||
'CloseTomcat',
|
||
'SetTomcat',
|
||
'apacheAddPort',
|
||
'AddSite',
|
||
'GetPHPVersion',
|
||
'SetPHPVersion',
|
||
'DeleteSite',
|
||
'AddDomain',
|
||
'DelDomain',
|
||
'GetDirBinding',
|
||
'AddDirBinding',
|
||
'GetDirRewrite',
|
||
'DelDirBinding',
|
||
'get_site_types',
|
||
'add_site_type',
|
||
'remove_site_type',
|
||
'modify_site_type_name',
|
||
'set_site_type',
|
||
'UpdateRulelist',
|
||
'SetSiteRunPath',
|
||
'GetSiteRunPath',
|
||
'SetPath',
|
||
'SetIndex',
|
||
'GetIndex',
|
||
'GetDirUserINI',
|
||
'SetDirUserINI',
|
||
'GetRewriteList',
|
||
'SetSSL',
|
||
'SetSSLConf',
|
||
'CreateLet',
|
||
'CloseSSLConf',
|
||
'GetSSL',
|
||
'SiteStart',
|
||
'SiteStop',
|
||
'Set301Status',
|
||
'Get301Status',
|
||
'CloseLimitNet',
|
||
'SetLimitNet',
|
||
'GetLimitNet',
|
||
'RemoveProxy',
|
||
'GetProxyList',
|
||
'GetProxyDetals',
|
||
'CreateProxy',
|
||
'ModifyProxy',
|
||
'GetProxyFile',
|
||
'SaveProxyFile',
|
||
'ToBackup',
|
||
'DelBackup',
|
||
'GetSitePHPVersion',
|
||
'logsOpen',
|
||
'GetLogsStatus',
|
||
'CloseHasPwd',
|
||
'SetHasPwd',
|
||
'GetHasPwd',
|
||
'GetDnsApi',
|
||
'SetDnsApi',
|
||
'reset_wp_password',
|
||
'is_update',
|
||
'purge_all_cache',
|
||
'set_fastcgi_cache',
|
||
'update_wp',
|
||
'get_wp_username',
|
||
'get_language',
|
||
'deploy_wp',
|
||
# 网站管理新增
|
||
'test_domains_api',
|
||
'site_rname',
|
||
'get_wp_versions',
|
||
'AddWPSite',
|
||
'get_wp_configurations',
|
||
'save_wp_configurations',
|
||
'wp_backup_list',
|
||
'wp_backup',
|
||
'wp_restore',
|
||
'wp_remove_backup',
|
||
'get_wp_security_info',
|
||
'open_wp_file_protection',
|
||
"wordpress_vulnerabilities_scan",
|
||
"ignore_vuln",
|
||
"get_ignore_vuln",
|
||
"set_auth_scan",
|
||
"get_auth_scan_status",
|
||
"wordpress_vulnerabilities_time",
|
||
'close_wp_file_protection',
|
||
'get_wp_file_info',
|
||
'open_wp_firewall_protection',
|
||
'close_wp_firewall_protection',
|
||
'get_wp_firewall_info',
|
||
'wp_migrate_from_website_to_wptoolkit',
|
||
'wp_can_migrate_from_website_to_wptoolkit',
|
||
'wp_create_with_aap_bak',
|
||
'wp_create_with_plesk_or_cpanel_bak',
|
||
'wp_clone',
|
||
'wp_integrity_check',
|
||
'wp_reinstall_files',
|
||
'wp_plugin_list',
|
||
'wp_install_plugin',
|
||
'wp_installed_plugins',
|
||
'wp_update_plugin',
|
||
'wp_set_plugin_auto_update',
|
||
'wp_set_plugin_status',
|
||
'wp_uninstall_plugin',
|
||
'wp_theme_list',
|
||
'wp_install_theme',
|
||
'wp_installed_themes',
|
||
'wp_update_theme',
|
||
'wp_set_theme_auto_update',
|
||
'wp_switch_theme',
|
||
'wp_uninstall_theme',
|
||
'wp_all_sites',
|
||
'wp_set_list',
|
||
'wp_create_set',
|
||
'wp_remove_set',
|
||
'wp_get_items_from_set',
|
||
'wp_add_items_to_set',
|
||
'wp_update_item_state_with_set',
|
||
'wp_remove_items_from_set',
|
||
'wp_install_with_set',
|
||
'wp_remote_add',
|
||
'wp_remote_add_manually',
|
||
'wp_remote_remove',
|
||
'wp_remote_sites',
|
||
'wp_add_onekey_database',
|
||
'set_restart_task',
|
||
'get_restart_task',
|
||
'set_https_mode',
|
||
'get_https_mode',
|
||
'get_cron_scanin_info',
|
||
'set_cron_scanin_info',
|
||
'wp_create_with_manual_bak',
|
||
'set_wp_site_type',
|
||
'add_wp_site_type',
|
||
'edit_wp_site_type',
|
||
'del_wp_site_type',
|
||
'set_wp_tool',
|
||
'get_wp_tool',
|
||
'get_wp_debug_log',
|
||
'get_wp_sites',
|
||
'wp_copy_data',
|
||
'get_source_tables',
|
||
'get_wp_progress',
|
||
'set_wp_maintenance',
|
||
'get_wp_maintenance',
|
||
'get_site_maintenance',
|
||
'set_site_maintenance',
|
||
'get_wp_security_status',
|
||
'wp_manual_upload',
|
||
'get_cdn_ip',
|
||
'set_site_global',
|
||
'get_site_global',
|
||
'site_performance_test',
|
||
# 新增多服务
|
||
'set_default_site_conf',
|
||
'get_multi_webservice_status',
|
||
'switch_multi_webservice_status',
|
||
'switch_webservice',
|
||
'get_current_webservice',
|
||
'multi_service_check_repair',
|
||
'website_rollback',
|
||
'service_install_count',
|
||
# 项目网站分类管理, 除开php
|
||
'batch_set_site_type',
|
||
'get_project_site_types',
|
||
'add_project_site_type',
|
||
'modify_project_site_type',
|
||
'remove_project_site_type',
|
||
)
|
||
return publicObject(siteObject, defs, None, pdata)
|
||
|
||
@app.route(route_v2 + '/git', methods=method_all)
|
||
def git_tools(pdata=None):
|
||
# git管理
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import git_tools
|
||
gitObject = git_tools.GitTools()
|
||
defs = (
|
||
'get_git_version',
|
||
'get_ssh_key',
|
||
'add_key_repository',
|
||
'get_deploy_sh',
|
||
'save_deploy_sh',
|
||
'get_deploy_records',
|
||
'manual_deploy_site',
|
||
'get_site_deploy_log',
|
||
'get_deploy_script',
|
||
'git_rollback',
|
||
'get_site_git_conf',
|
||
'save_site_git_conf',
|
||
'del_site_git',
|
||
'auto_deploy',
|
||
'refresh_webhook_url',
|
||
'update_deploy_sh',
|
||
'del_script',
|
||
'get_webhook_log',
|
||
'clear_webhook_log',
|
||
'generate_yakpanel_ed25519_key',
|
||
'get_remote_branches',
|
||
'change_repository_key',
|
||
'import_existing_repository',
|
||
'get_git_directory'
|
||
)
|
||
return publicObject(gitObject, defs, None, pdata)
|
||
|
||
@app.route(route_v2 + '/ftp', methods=method_all)
|
||
def ftp_v2(pdata=None):
|
||
# FTP管理
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
if request.method == method_get[0] and not pdata:
|
||
FtpPort()
|
||
import system_v2
|
||
data = system_v2.system().GetConcifInfo()
|
||
data['isSetup'] = True
|
||
data['js_random'] = get_js_random()
|
||
if os.path.exists(public.GetConfigValue('setup_path') +
|
||
'/pure-ftpd') == False:
|
||
data['isSetup'] = False
|
||
data['lan'] = public.GetLan('ftp')
|
||
return render_template('ftp.html', data=data)
|
||
import ftp_v2
|
||
ftpObject = ftp_v2.ftp()
|
||
defs = ('AddUser', 'DeleteUser', 'SetUserPassword', 'SetStatus', 'setPort',
|
||
'set_user_home', 'get_login_logs', 'get_action_logs',
|
||
'set_ftp_logs')
|
||
return publicObject(ftpObject, defs, None, pdata)
|
||
|
||
|
||
@app.route(route_v2 + '/database', methods=method_all)
|
||
def database_v2(pdata=None):
|
||
# 数据库管理
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
if request.method == method_get[0] and not pdata:
|
||
import ajax_v2
|
||
from panelPlugin import panelPlugin
|
||
session['phpmyadminDir'] = False
|
||
if panelPlugin().get_phpmyadmin_stat():
|
||
pmd = get_phpmyadmin_dir()
|
||
if pmd:
|
||
session['phpmyadminDir'] = 'http://' + public.GetHost(
|
||
) + ':' + pmd[1] + '/' + pmd[0]
|
||
ajax_v2.ajax().set_phpmyadmin_session()
|
||
import system_v2
|
||
data = system_v2.system().GetConcifInfo()
|
||
data['isSetup'] = os.path.exists(
|
||
public.GetConfigValue('setup_path') + '/mysql/bin')
|
||
data['mysql_root'] = public.M('config').where(
|
||
'id=?', (1,)).getField('mysql_root')
|
||
data['lan'] = public.GetLan('database')
|
||
data['js_random'] = get_js_random()
|
||
return render_template('database.html', data=data)
|
||
import database_v2
|
||
databaseObject = database_v2.database()
|
||
defs = (
|
||
'GetdataInfo',
|
||
'check_del_data',
|
||
'get_database_size',
|
||
'GetInfo',
|
||
'ReTable',
|
||
'OpTable',
|
||
'AlTable',
|
||
'GetSlowLogs',
|
||
'GetRunStatus',
|
||
'SetDbConf',
|
||
'GetDbStatus',
|
||
'BinLog',
|
||
'GetErrorLog',
|
||
'GetMySQLInfo',
|
||
'SetDataDir',
|
||
'SetMySQLPort',
|
||
'AddCloudDatabase',
|
||
'AddDatabase',
|
||
'DeleteDatabase',
|
||
'SetupPassword',
|
||
'ResDatabasePassword',
|
||
'ToBackup',
|
||
'GetBackupSize',
|
||
'GetImportSize',
|
||
'GetImportLog',
|
||
'DelBackup',
|
||
'AddCloudServer',
|
||
'GetCloudServer',
|
||
'RemoveCloudServer',
|
||
'ModifyCloudServer',
|
||
'InputSql',
|
||
'SyncToDatabases',
|
||
'SyncGetDatabases',
|
||
'GetDatabaseAccess',
|
||
'SetDatabaseAccess',
|
||
'get_mysql_user',
|
||
'check_mysql_ssl_status',
|
||
'write_ssl_to_mysql',
|
||
'GetdataInfo',
|
||
'GetBackup',
|
||
'GetMysqlUser',
|
||
'GetDatabasesList',
|
||
'AddMysqlUser',
|
||
'DelMysqlUser',
|
||
'ChangeUserPass',
|
||
'GetUserGrants',
|
||
'GetUserHostDbGrant',
|
||
'AddUserGrants',
|
||
'DelUserGrants',
|
||
'GetmvDataDirSpeed',
|
||
'GetMySQLBinlogs',
|
||
'mysql_oom_adj',
|
||
)
|
||
return publicObject(databaseObject, defs, None, pdata)
|
||
|
||
|
||
@app.route(route_v2 + '/acme', methods=method_all)
|
||
def acme_v2(pdata=None):
|
||
# Let's 证书管理
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import acme_v2
|
||
acme_v2_object = acme_v2.acme_v2()
|
||
defs = ('get_orders', 'remove_order', 'get_order_find', 'revoke_order',
|
||
'create_order', 'get_account_info', 'set_account_info',
|
||
'update_zip', 'get_cert_init_api', 'get_auths', 'auth_domain',
|
||
'check_auth_status', 'download_cert', 'apply_cert', 'renew_cert',
|
||
'apply_cert_api', 'apply_dns_auth')
|
||
return publicObject(acme_v2_object, defs, None, pdata)
|
||
|
||
|
||
@app.route(route_v2 + '/api', methods=method_all)
|
||
def api_v2(pdata=None):
|
||
# APP使用的API接口管理
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import panel_api_v2
|
||
api_object = panel_api_v2.panelApi()
|
||
defs = ('get_token', 'check_bind', 'get_bind_status', 'get_apps',
|
||
'add_bind_app', 'remove_bind_app', 'set_token', 'get_tmp_token',
|
||
'get_app_bind_status', 'login_for_app')
|
||
return publicObject(api_object, defs, None, pdata)
|
||
|
||
|
||
@app.route(route_v2 + '/control', methods=method_all)
|
||
def control_v2(pdata=None):
|
||
# 监控页面
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import system_v2
|
||
data = system_v2.system().GetConcifInfo()
|
||
data['lan'] = public.GetLan('control')
|
||
data['js_random'] = get_js_random()
|
||
return render_template('control.html', data=data)
|
||
|
||
|
||
@app.route(route_v2 + '/logs', methods=method_all)
|
||
def logs_v2(pdata=None):
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
if request.method == method_get[0] and not pdata:
|
||
data = {}
|
||
data['lan'] = public.GetLan('soft')
|
||
data['show_workorder'] = not os.path.exists('data/not_workorder.pl')
|
||
return render_template('logs.html', data=data)
|
||
|
||
|
||
@app.route(route_v2 + '/firewall', methods=method_all)
|
||
def firewall_v2(pdata=None):
|
||
# 安全页面
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
if request.method == method_get[0] and not pdata:
|
||
import system_v2
|
||
data = system_v2.system().GetConcifInfo()
|
||
data['lan'] = public.GetLan('firewall')
|
||
data['js_random'] = get_js_random()
|
||
return render_template('firewall.html', data=data)
|
||
import firewalls_v2
|
||
firewallObject = firewalls_v2.firewalls()
|
||
defs = ('GetList', 'AddDropAddress', 'DelDropAddress', 'FirewallReload',
|
||
'SetFirewallStatus', 'AddAcceptPort', 'DelAcceptPort',
|
||
'SetSshStatus', 'SetPing', 'SetSshPort', 'GetSshInfo',
|
||
'SetFirewallStatus')
|
||
return publicObject(firewallObject, defs, None, pdata)
|
||
|
||
@app.route(route_v2 + '/firewall/com/<def_name>', methods=method_all)
|
||
def firewall_v22(def_name, pdata=None):
|
||
if request.method not in ['GET', 'POST']: return
|
||
path_split = request.path.split("/")
|
||
if len(path_split) < 5: return
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
# from panelSafeControllerV2 import SafeController
|
||
from panelFireControllerV2 import FirewallController
|
||
project_obj = FirewallController()
|
||
defs = ('model',)
|
||
get = get_input()
|
||
get.action = 'model'
|
||
get.mod_name = path_split[3]
|
||
get.def_name = def_name
|
||
|
||
return publicObject(project_obj, defs, None, get)
|
||
|
||
|
||
@app.route(route_v2 + '/ssh_security', methods=method_all)
|
||
def ssh_security_v2(pdata=None):
|
||
# SSH安全
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
if request.method == method_get[0] and not pdata and not request.args.get(
|
||
'action', '') in ['download_key']:
|
||
data = {}
|
||
data['lan'] = public.GetLan('firewall')
|
||
data['js_random'] = get_js_random()
|
||
return render_template('firewall.html', data=data)
|
||
import ssh_security_v2
|
||
firewallObject = ssh_security_v2.ssh_security()
|
||
is_csrf = True
|
||
if request.args.get('action', '') in ['download_key']: is_csrf = False
|
||
defs = ('san_ssh_security', 'set_password', 'set_sshkey', 'stop_key',
|
||
'get_config', 'download_key', 'stop_password', 'get_key',
|
||
'return_ip', 'add_return_ip', 'del_return_ip', 'start_jian',
|
||
'stop_jian', 'get_jian', 'get_logs', 'set_root', 'stop_root',
|
||
'start_auth_method', 'stop_auth_method', 'get_auth_method',
|
||
'check_so_file', 'get_so_file', 'get_pin', 'set_login_send',
|
||
'get_login_send', 'get_msg_push_list', 'clear_login_send', 'set_root_password',
|
||
'get_sshd_anti_logs', 'set_anti_conf', 'get_anti_conf', 'del_ban_ip')
|
||
return publicObject(firewallObject, defs, None, pdata, is_csrf)
|
||
|
||
|
||
@app.route(route_v2 + '/monitor', methods=method_all)
|
||
def panel_monitor_v2(pdata=None):
|
||
# 云控统计信息
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import monitor_v2
|
||
dataObject = monitor_v2.Monitor()
|
||
defs = ('get_spider', 'get_exception', 'get_request_count_qps',
|
||
'load_and_up_flow', 'get_request_count_by_hour')
|
||
return publicObject(dataObject, defs, None, pdata)
|
||
|
||
@app.route(route_v2 + '/site_monitor', methods=method_all)
|
||
def monitor(pdata=None):
|
||
# 网站统计预览
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
from projectModelV2 import monitorModel
|
||
dataObject = monitorModel.main()
|
||
defs = ('get_overview', 'get_site_overview')
|
||
return publicObject(dataObject, defs, None, pdata)
|
||
|
||
@app.route(route_v2 + '/san', methods=method_all)
|
||
def san_baseline_v2(pdata=None):
|
||
# 云控安全扫描
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import san_baseline_v2
|
||
dataObject = san_baseline_v2.san_baseline()
|
||
defs = ('start', 'get_api_log', 'get_resut', 'get_ssh_errorlogin',
|
||
'repair', 'repair_all')
|
||
return publicObject(dataObject, defs, None, pdata)
|
||
|
||
|
||
@app.route(route_v2 + '/password', methods=method_all)
|
||
def panel_password_v2(pdata=None):
|
||
# 云控密码管理
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import password_v2
|
||
dataObject = password_v2.password()
|
||
defs = ('set_root_password', 'get_mysql_root', 'set_mysql_password',
|
||
'set_panel_password', 'SetPassword', 'SetSshKey', 'StopKey',
|
||
'GetConfig', 'StopPassword', 'GetKey', 'get_databses',
|
||
'rem_mysql_pass', 'set_mysql_access', "get_panel_username")
|
||
return publicObject(dataObject, defs, None, pdata)
|
||
|
||
|
||
@app.route(route_v2 + '/warning', methods=method_all)
|
||
def panel_warning_v2(pdata=None):
|
||
# 首页安全警告
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
if public.get_csrf_html_token_key() in session and 'login' in session:
|
||
if not check_csrf():
|
||
return public.ReturnJson(False, 'INIT_CSRF_ERR'), json_header
|
||
get = get_input()
|
||
ikey = 'warning_list'
|
||
import panel_warning_v2
|
||
dataObject = panel_warning_v2.panelWarning()
|
||
if get.action == 'get_list':
|
||
result = cache.get(ikey)
|
||
if not result or 'force' in get:
|
||
result = json.loads('{"ignore":[],"risk":[],"security":[]}')
|
||
try:
|
||
defs = ("get_list",)
|
||
result = publicObject(dataObject, defs, None, pdata)
|
||
cache.set(ikey, result, 3600)
|
||
return result
|
||
except:
|
||
pass
|
||
if hasattr(get, 'open') and get.open == "1":
|
||
public.set_module_logs("panel_warning_v2", "get_list")
|
||
return result
|
||
|
||
defs = ('get_list', 'set_ignore', 'check_find', 'check_cve',
|
||
'set_vuln_ignore', 'get_scan_bar', 'get_tmp_result',
|
||
'kill_get_list','get_res_list')
|
||
|
||
if get.action in ['set_ignore', 'check_find', 'set_vuln_ignore']:
|
||
cache.delete(ikey)
|
||
return publicObject(dataObject, defs, None, pdata)
|
||
|
||
# ----------------------------------------- 安全模块路由区 start----------------------------------------
|
||
|
||
@app.route(route_v2 + '/safecloud', methods=method_all)
|
||
def safecloud(pdata=None):
|
||
# 安全
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
from projectModelV2.safecloudModel import main
|
||
toObject = main()
|
||
defs = ('get_safe_overview','get_pending_alarm_trend','get_security_trend',
|
||
'get_security_dynamic','set_config','get_safecloud_list',
|
||
'get_webshell_result','get_config','deal_webshell_file','set_alarm_config',
|
||
'webshell_detection','ignore_file','get_ignored_list','del_ignored')
|
||
return publicObject(toObject, defs, None, pdata)
|
||
|
||
# 避免加密后变成单例模式
|
||
from safeModel.reportModel import main as report_main
|
||
@app.route(route_v2 + '/safe/report', methods=method_all)
|
||
def report(pdata=None):
|
||
# 安全报告
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
toObject = report_main()
|
||
defs = ('get_report')
|
||
return publicObject(toObject, defs, None, pdata)
|
||
|
||
@app.route(route_v2 + '/scanning', methods=method_all)
|
||
def scanning(pdata=None):
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
from projectModelV2.scanningModel import main
|
||
toObject = main()
|
||
defs = ('get_vuln_info', 'startScan')
|
||
return publicObject(toObject, defs, None, pdata)
|
||
|
||
@app.route(route_v2 + '/safe_detect', methods=method_all)
|
||
def safe_detect(pdata=None):
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
from projectModelV2.safe_detectModel import main
|
||
toObject = main()
|
||
defs = ('get_safe_count')
|
||
return publicObject(toObject, defs, None, pdata)
|
||
|
||
# ----------------------------------------- 安全模块路由区 end----------------------------------------
|
||
|
||
@app.route(route_v2 + '/bak', methods=method_all)
|
||
def backup_bak_v2(pdata=None):
|
||
# 云控备份服务
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import backup_bak_v2
|
||
dataObject = backup_bak_v2.backup_bak()
|
||
defs = ('get_sites', 'get_databases', 'backup_database', 'backup_site',
|
||
'backup_path', 'get_database_progress', 'get_site_progress',
|
||
'down', 'get_down_progress', 'download_path', 'backup_site_all',
|
||
'get_all_site_progress', 'backup_date_all',
|
||
'get_all_date_progress')
|
||
return publicObject(dataObject, defs, None, pdata)
|
||
|
||
|
||
@app.route(route_v2 + '/abnormal', methods=method_all)
|
||
def abnormal_v2(pdata=None):
|
||
# 云控系统统计
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import abnormal_v2
|
||
dataObject = abnormal_v2.abnormal()
|
||
defs = ('mysql_server', 'mysql_cpu', 'mysql_count', 'php_server',
|
||
'php_conn_max', 'php_cpu', 'CPU', 'Memory', 'disk',
|
||
'not_root_user', 'start')
|
||
return publicObject(dataObject, defs, None, pdata)
|
||
|
||
|
||
@app.route(route_v2 + '/project/<mod_name>/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/project/<mod_name>/<def_name>/<stype>', methods=method_all)
|
||
def project_v2(mod_name, def_name, stype=None):
|
||
# ps: classV2 projectModelV2 动态调用
|
||
if request.method not in ['GET', 'POST']: return
|
||
if not mod_name: return abort(404)
|
||
if not def_name: return abort(404)
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
from panelProjectControllerV2 import ProjectController
|
||
project_obj = ProjectController()
|
||
defs = ('model',)
|
||
get = get_input()
|
||
get.action = 'model'
|
||
get.mod_name = mod_name
|
||
get.def_name = def_name
|
||
get.stype = stype
|
||
if stype == 'html':
|
||
return project_obj.model(get)
|
||
return publicObject(project_obj, defs, None, get)
|
||
|
||
|
||
@app.route(route_v2 + '/msg/<mod_name>/<def_name>', methods=method_all)
|
||
def msgcontroller_v2(mod_name, def_name):
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
from MsgControllerV2 import MsgController
|
||
project_obj = MsgController()
|
||
defs = ('model',)
|
||
get = get_input()
|
||
get.action = 'model'
|
||
get.mod_name = mod_name
|
||
get.def_name = def_name
|
||
return publicObject(project_obj, defs, None, get)
|
||
|
||
|
||
# @app.route(route_v2 + '/docker', methods=method_all)
|
||
# def docker_v2(pdata=None):
|
||
# comReturn = comm.local()
|
||
# if comReturn: return comReturn
|
||
# if request.method == method_get[0]:
|
||
# import system_v2
|
||
# data = system_v2.system().GetConcifInfo()
|
||
# data['js_random'] = get_js_random()
|
||
# data['lan'] = public.GetLan('files')
|
||
# return render_template('docker.html', data=data)
|
||
|
||
# @app.route(route_v2 + '/docker', methods=method_all)
|
||
@app.route(route_v2 + '/btdocker/app/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/btdocker/backup/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/btdocker/container/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/btdocker/compose/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/btdocker/dkgroup/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/btdocker/image/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/btdocker/network/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/btdocker/proxy/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/btdocker/project/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/btdocker/registry/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/btdocker/setup/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/btdocker/site/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/btdocker/status/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/btdocker/volume/<def_name>', methods=method_all)
|
||
def docker_v2(def_name):
|
||
if request.method not in ['GET', 'POST']: return
|
||
path_split = request.path.split("/")
|
||
if len(path_split) < 5: return
|
||
get = get_input()
|
||
get.action = 'model'
|
||
get.model_index = 'btDocker'
|
||
get.mod_name = path_split[3]
|
||
get.def_name = def_name
|
||
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
# p_path = public.get_plugin_path() + '/' + path_split[2]
|
||
# if os.path.exists(p_path):
|
||
# return panel_other(get.model_index, get.mod_name, def_name)
|
||
from panelDockerControllerV2 import DockerController
|
||
controller_obj = DockerController()
|
||
defs = ('model',)
|
||
return publicObject(controller_obj, defs, None, get)
|
||
|
||
|
||
@app.route(route_v2 + '/dbmodel/<mod_name>/<def_name>', methods=method_all)
|
||
def dbmodel_v2(mod_name, def_name):
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
from panelDatabaseControllerV2 import DatabaseController
|
||
database_obj = DatabaseController()
|
||
defs = ('model',)
|
||
get = get_input()
|
||
get.action = 'model'
|
||
get.mod_name = mod_name
|
||
get.def_name = def_name
|
||
|
||
return publicObject(database_obj, defs, None, get)
|
||
|
||
|
||
@app.route(route_v2 + '/files', methods=method_all)
|
||
def files_v2(pdata=None):
|
||
# 文件管理
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
if request.method == method_get[0] and not request.args.get(
|
||
'path') and not pdata:
|
||
import system_v2
|
||
data = system_v2.system().GetConcifInfo()
|
||
data['recycle_bin'] = os.path.exists('data/recycle_bin.pl')
|
||
data['lan'] = public.GetLan('files')
|
||
data['js_random'] = get_js_random()
|
||
return render_template('files.html', data=data)
|
||
import files_v2
|
||
filesObject = files_v2.files()
|
||
defs = ('files_search', 'files_replace', 'get_replace_logs',
|
||
'get_images_resize', 'add_files_rsync', 'get_file_attribute',
|
||
'get_file_hash', 'CreateLink', 'get_progress', 'restore_website',
|
||
'fix_permissions', 'get_all_back', 'restore_path_permissions',
|
||
'del_path_premissions', 'get_path_premissions',
|
||
'back_path_permissions', 'upload_file_exists', 'CheckExistsFiles',
|
||
'GetExecLog', 'GetSearch', 'ExecShell', 'GetExecShellMsg',
|
||
'exec_git', 'exec_composer', 'create_download_url', 'UploadFile',
|
||
'GetDir','GetDirNew', 'CreateFile', 'CreateDir', 'DeleteDir', 'DeleteFile',
|
||
'get_download_url_list', 'remove_download_url',
|
||
'modify_download_url', 'CopyFile', 'CopyDir', 'MvFile',
|
||
'GetFileBody', 'SaveFileBody', 'Zip', 'UnZip',
|
||
'get_download_url_find', 'set_file_ps', 'SearchFiles', 'upload',
|
||
'read_history', 're_history', 'auto_save_temp',
|
||
'get_auto_save_body', 'get_videos', 'GetFileAccess',
|
||
'SetFileAccess', 'GetDirSize', 'SetBatchData', 'BatchPaste',
|
||
'install_rar', 'get_path_size', 'DownloadFile', 'GetTaskSpeed',
|
||
'CloseLogs', 'InstallSoft', 'UninstallSoft', 'SaveTmpFile',
|
||
'get_composer_version', 'exec_composer', 'update_composer',
|
||
'GetTmpFile', 'del_files_store', 'add_files_store',
|
||
'get_files_store', 'del_files_store_types',
|
||
'add_files_store_types', 'exec_git', 'RemoveTask', 'ActionTask',
|
||
'Re_Recycle_bin', 'Get_Recycle_bin', 'Del_Recycle_bin',
|
||
'Close_Recycle_bin', 'Recycle_bin', 'file_webshell_check',
|
||
'dir_webshell_check', 'files_search', 'files_replace',
|
||
'get_replace_logs', 'get_sql_backup', 'test_path', 'upload_files_exists',
|
||
'file_history', 'file_history_list', 'del_file_history','del_history')
|
||
|
||
return publicObject(filesObject, defs, None, pdata)
|
||
|
||
|
||
@app.route(route_v2 + '/crontab', methods=method_all)
|
||
@app.route(route_v2 + '/crontab/<action>', methods=method_all)
|
||
@app.route(route_v2 + '/crontab_ifame', methods=method_all)
|
||
def crontab_v2(pdata=None,action=None):
|
||
# 计划任务
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
if request.method == method_get[0] and not pdata and not request.args:
|
||
import system
|
||
data = system.system().GetConcifInfo()
|
||
data['lan'] = public.GetLan('crontab')
|
||
data['js_random'] = get_js_random()
|
||
if request.path in ['/crontab_ifame']:
|
||
return render_template('crontab.html', data=data)
|
||
return render_template('index1.html', data=data)
|
||
|
||
import class_v2.crontab_v2 as crontab
|
||
crontabObject = crontab.crontab()
|
||
defs = (
|
||
'set_cron_status_all', 'get_zone', 'get_domain', 'cancel_top', 'set_task_top', 'GetCrontab', 'AddCrontab',
|
||
'GetDataList', 'GetLogs', 'DelLogs', 'download_logs', 'clear_logs',
|
||
'DelCrontab', 'StartTask', 'set_cron_status', 'get_crond_find', 'set_atuo_start_syssafe',
|
||
'modify_crond', 'get_backup_list', 'check_url_connecte', 'cloud_backup_download',
|
||
'GetDatabases', 'get_crontab_types', 'add_crontab_type', 'remove_crontab_type',
|
||
'modify_crontab_type_name', 'set_crontab_type', 'export_crontab_to_json', 'import_crontab_from_json',
|
||
'set_rotate_log', 'get_rotate_log_config', 'get_restart_project_config', 'set_restart_project',
|
||
'get_system_user_list','get_databases', 'get_auto_config', 'set_auto_config'
|
||
)
|
||
return publicObject(crontabObject, defs, None, pdata)
|
||
|
||
|
||
@app.route(route_v2 + '/soft', methods=method_all)
|
||
def soft_v2(pdata=None):
|
||
# 软件商店页面
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import system_v2
|
||
data = system_v2.system().GetConcifInfo()
|
||
data['lan'] = public.GetLan('soft')
|
||
data['js_random'] = get_js_random()
|
||
return render_template('soft.html', data=data)
|
||
|
||
|
||
@app.route(route_v2 + '/config', methods=method_all)
|
||
def config_v2(pdata=None):
|
||
# 面板设置页面
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
|
||
if request.method == method_get[0] and not pdata:
|
||
import system_v2, wxapp_v2, config_v2
|
||
c_obj = config_v2.config()
|
||
data = system_v2.system().GetConcifInfo()
|
||
data['lan'] = public.GetLan('config')
|
||
try:
|
||
data['wx'] = wxapp_v2.wxapp().get_user_info(None)['msg']
|
||
except:
|
||
data['wx'] = 'INIT_WX_NOT_BIND'
|
||
data['api'] = ''
|
||
data['ipv6'] = ''
|
||
sess_out_path = 'data/session_timeout.pl'
|
||
if not os.path.exists(sess_out_path):
|
||
public.writeFile(sess_out_path, '86400')
|
||
s_time_tmp = public.readFile(sess_out_path)
|
||
if not s_time_tmp: s_time_tmp = '0'
|
||
data['session_timeout'] = int(s_time_tmp)
|
||
if c_obj.get_ipv6_listen(None): data['ipv6'] = 'checked'
|
||
if c_obj.get_token(None)['open']: data['api'] = 'checked'
|
||
data['basic_auth'] = c_obj.get_basic_auth_stat(None)
|
||
data['status_code'] = c_obj.get_not_auth_status()
|
||
data['basic_auth']['value'] = public.getMsg('CLOSED')
|
||
if data['basic_auth']['open']:
|
||
data['basic_auth']['value'] = public.getMsg('OPENED')
|
||
data['debug'] = ''
|
||
data['js_random'] = get_js_random()
|
||
if app.config['DEBUG']: data['debug'] = 'checked'
|
||
data['is_local'] = ''
|
||
if public.is_local(): data['is_local'] = 'checked'
|
||
data['public_key'] = public.get_rsa_public_key().replace("\n", "")
|
||
return render_template('config.html', data=data)
|
||
import config_v2
|
||
defs = (
|
||
'send_by_telegram',
|
||
'set_empty',
|
||
'set_backup_notification',
|
||
'get_panel_ssl_status',
|
||
'set_file_deny',
|
||
'del_file_deny',
|
||
'get_file_deny',
|
||
'set_improvement',
|
||
'get_httpd_access_log_format_parameter',
|
||
'set_httpd_format_log_to_website',
|
||
'get_httpd_access_log_format',
|
||
'del_httpd_access_log_format',
|
||
'add_httpd_access_log_format',
|
||
'get_nginx_access_log_format_parameter',
|
||
'set_format_log_to_website',
|
||
'get_nginx_access_log_format',
|
||
'del_nginx_access_log_format',
|
||
'set_click_logs',
|
||
'get_node_config',
|
||
'add_nginx_access_log_format',
|
||
'get_ols_private_cache_status',
|
||
'get_ols_value',
|
||
'set_ols_value',
|
||
'set_node_config',
|
||
'get_ols_private_cache',
|
||
'get_ols_static_cache',
|
||
'set_ols_static_cache',
|
||
'switch_ols_private_cache',
|
||
'set_ols_private_cache',
|
||
'set_coll_open',
|
||
'get_qrcode_data',
|
||
'check_two_step',
|
||
'set_two_step_auth',
|
||
'create_user',
|
||
'remove_user',
|
||
'modify_user',
|
||
'get_key',
|
||
'get_php_session_path',
|
||
'set_php_session_path',
|
||
'get_cert_source',
|
||
'get_users',
|
||
'set_request_iptype',
|
||
'set_local',
|
||
'set_debug',
|
||
'get_panel_error_logs',
|
||
'clean_panel_error_logs',
|
||
'get_menu_list',
|
||
'set_hide_menu_list',
|
||
'get_basic_auth_stat',
|
||
'set_basic_auth',
|
||
'get_cli_php_version',
|
||
'get_tmp_token',
|
||
'get_temp_login',
|
||
'set_temp_login',
|
||
'remove_temp_login',
|
||
'clear_temp_login',
|
||
'set_site_total_setup',
|
||
'get_temp_login_logs',
|
||
'set_cli_php_version',
|
||
'DelOldSession',
|
||
'GetSessionCount',
|
||
'SetSessionConf',
|
||
'set_not_auth_status',
|
||
'GetSessionConf',
|
||
'get_ipv6_listen',
|
||
'set_ipv6_status',
|
||
'GetApacheValue',
|
||
'SetApacheValue',
|
||
'install_msg_module',
|
||
'GetNginxValue',
|
||
'SetNginxValue',
|
||
'get_token',
|
||
'set_token',
|
||
'set_admin_path',
|
||
'is_pro',
|
||
'set_msg_config',
|
||
'get_php_config',
|
||
'get_config',
|
||
'SavePanelSSL',
|
||
'GetPanelSSL',
|
||
'GetPHPConf',
|
||
'SetPHPConf',
|
||
'uninstall_msg_module',
|
||
'GetPanelList',
|
||
'AddPanelInfo',
|
||
'SetPanelInfo',
|
||
'DelPanelInfo',
|
||
'ClickPanelInfo',
|
||
'SetPanelSSL',
|
||
'get_msg_configs',
|
||
'SetTemplates',
|
||
'Set502',
|
||
'setPassword',
|
||
'setUsername',
|
||
'setPanel',
|
||
'clearBackup',
|
||
'setPathInfo',
|
||
'setPHPMaxSize',
|
||
'get_msg_fun',
|
||
'getFpmConfig',
|
||
'setFpmConfig',
|
||
'setPHPMaxTime',
|
||
'syncDate',
|
||
'setPHPDisable',
|
||
'SetControl',
|
||
'get_settings2',
|
||
'del_tg_info',
|
||
'set_tg_bot',
|
||
'ClosePanel',
|
||
'AutoUpdatePanel',
|
||
'SetPanelLock',
|
||
'return_mail_list',
|
||
'del_mail_list',
|
||
'add_mail_address',
|
||
'user_mail_send',
|
||
'get_user_mail',
|
||
'set_dingding',
|
||
'get_dingding',
|
||
'get_settings',
|
||
'user_stmp_mail_send',
|
||
'user_dingding_send',
|
||
'get_login_send',
|
||
'set_login_send',
|
||
'clear_login_send',
|
||
'get_login_log',
|
||
'login_ipwhite',
|
||
'set_ssl_verify',
|
||
'get_ssl_verify',
|
||
'get_password_config',
|
||
'set_password_expire',
|
||
'set_password_safe',
|
||
'setlastPassword',
|
||
'get_module_template',
|
||
# 新增nps评分
|
||
'write_nps_new',
|
||
'get_nps_new',
|
||
"check_nps",
|
||
'get_translations',
|
||
'get_login_translations',
|
||
# 语言包 测试接口
|
||
# 'get_language',
|
||
# 'get_languageinfo',
|
||
'set_language',
|
||
'download_language',
|
||
'upload_language',
|
||
# 设置ua限制
|
||
'set_ua',
|
||
'get_limit_ua',
|
||
'modify_ua',
|
||
'delete_ua',
|
||
'set_cdn_status',
|
||
'set_auto_favicon',
|
||
'set_theme',
|
||
# 旧主题
|
||
# 'set_panel_asset',
|
||
# 'get_panel_asset',
|
||
'get_alarm_services', # 服务
|
||
# 主题
|
||
'get_panel_theme',
|
||
'set_panel_theme',
|
||
'update_panel_theme',
|
||
'validate_theme_file',
|
||
'import_theme_config',
|
||
'export_theme_config',
|
||
)
|
||
return publicObject(config_v2.config(), defs, None, pdata)
|
||
|
||
|
||
@app.route(route_v2 + '/ajax', methods=method_all)
|
||
def ajax_v2(pdata=None):
|
||
# 面板系统服务状态接口
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import ajax_v2
|
||
ajaxObject = ajax_v2.ajax()
|
||
defs = ('get_lines', 'php_info', 'change_phpmyadmin_ssl_port',
|
||
'set_phpmyadmin_ssl', 'get_phpmyadmin_ssl', 'get_pd',
|
||
'check_user_auth', 'to_not_beta', 'get_beta_logs', 'get_version_logs','apple_beta',
|
||
'GetApacheStatus', 'GetCloudHtml', 'get_pay_type',
|
||
'get_load_average', 'GetOpeLogs', 'GetFpmLogs', 'GetFpmSlowLogs',
|
||
'SetMemcachedCache', 'GetMemcachedStatus', 'GetRedisStatus',
|
||
'GetWarning', 'SetWarning', 'CheckLogin', 'GetSpeed', 'GetAd',
|
||
'phpSort', 'ToPunycode', 'GetBetaStatus', 'SetBeta',
|
||
'setPHPMyAdmin', 'delClose', 'KillProcess', 'GetPHPInfo',
|
||
'GetQiniuFileList', 'get_process_tops', 'get_process_cpu_high',
|
||
'UninstallLib', 'InstallLib', 'SetQiniuAS', 'GetQiniuAS',
|
||
'GetLibList', 'GetProcessList', 'GetNetWorkList', 'GetNginxStatus',
|
||
'GetPHPStatus', 'GetTaskCount', 'GetSoftList', 'GetNetWorkIo',
|
||
'GetDiskIo', 'GetCpuIo', 'CheckInstalled', 'UpdatePanel',
|
||
'GetInstalled', 'GetPHPConfig', 'SetPHPConfig', 'log_analysis',
|
||
'speed_log', 'get_result', 'get_detailed', 'ignore_version')
|
||
|
||
return publicObject(ajaxObject, defs, None, pdata)
|
||
|
||
|
||
@app.route(route_v2 + '/system', methods=method_all)
|
||
def system_v2(pdata=None):
|
||
# 面板系统状态接口
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import system_v2
|
||
sysObject = system_v2.system()
|
||
defs = ('get_io_info', 'UpdatePro', 'GetAllInfo', 'GetNetWorkApi',
|
||
'GetLoadAverage', 'ClearSystem', 'GetNetWorkOld', 'GetNetWork',
|
||
'GetDiskInfo', 'GetCpuInfo', 'GetBootTime', 'GetSystemVersion',
|
||
'GetMemInfo', 'GetSystemTotal', 'GetConcifInfo', 'ServiceAdmin',
|
||
'ReWeb', 'RestartServer', 'ReMemory', 'RepPanel', 'mark_reboot_read')
|
||
return publicObject(sysObject, defs, None, pdata)
|
||
|
||
|
||
@app.route(route_v2 + '/deployment', methods=method_all)
|
||
def deployment_v2(pdata=None):
|
||
# 一键部署接口
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import plugin_deployment_v2
|
||
sysObject = plugin_deployment_v2.plugin_deployment()
|
||
defs = ('GetList', 'AddPackage', 'DelPackage', 'SetupPackage', 'GetSpeed',
|
||
'GetPackageOther')
|
||
return publicObject(sysObject, defs, None, pdata)
|
||
|
||
|
||
@app.route(route_v2 + '/data', methods=method_all)
|
||
@app.route(route_v2 + '/panel_data', methods=method_all)
|
||
def panel_data_v2(pdata=None):
|
||
# 从数据库获取数据接口
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import data_v2
|
||
dataObject = data_v2.data()
|
||
defs = ('setPs', 'getData', 'getFind', 'getKey', 'getSiteWafConfig', 'getSiteThirtyTotal','get_wp_classification','get_wp_site_list','get_aacloud_data')
|
||
return publicObject(dataObject, defs, None, pdata)
|
||
|
||
# todo 计划调整
|
||
@app.route(route_v2 + '/ssl', methods=method_all)
|
||
def ssl_v2(pdata=None):
|
||
# 商业SSL证书申请接口
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import panel_ssl_v2
|
||
toObject = panel_ssl_v2.panelSSL()
|
||
defs = (
|
||
'check_url_txt',
|
||
'RemoveCert',
|
||
'renew_lets_ssl',
|
||
'SetCertToSite',
|
||
'GetCertList',
|
||
'SaveCert',
|
||
'GetCert',
|
||
'GetCertName',
|
||
'again_verify',
|
||
'DelToken',
|
||
'GetToken',
|
||
'GetToken_New',
|
||
'GetUserInfo',
|
||
'GetOrderList',
|
||
'GetDVSSL',
|
||
'Completed',
|
||
'SyncOrder',
|
||
'download_cert',
|
||
'set_cert',
|
||
'cancel_cert_order',
|
||
'get_order_list',
|
||
'get_order_find',
|
||
'apply_order_pay',
|
||
'get_pay_status',
|
||
'apply_order',
|
||
'get_verify_info',
|
||
'get_verify_result',
|
||
'get_product_list',
|
||
'set_verify_info',
|
||
'GetSSLInfo',
|
||
'downloadCRT',
|
||
'GetSSLProduct',
|
||
'Renew_SSL',
|
||
'Get_Renew_SSL',
|
||
# 新增 购买证书对接接口
|
||
'get_product_list_v2',
|
||
'apply_cert_order_pay',
|
||
'get_cert_admin',
|
||
'apply_order_ca',
|
||
'apply_cert_install_pay',
|
||
'verify_mail_any',
|
||
)
|
||
get = get_input()
|
||
|
||
if get.action == 'download_cert':
|
||
from io import BytesIO
|
||
import base64
|
||
result = toObject.download_cert(get)
|
||
fp = BytesIO(base64.b64decode(result['res']['data']))
|
||
return send_file(fp,
|
||
download_name=result['res']['filename'],
|
||
as_attachment=True,
|
||
mimetype='application/zip')
|
||
result = publicObject(toObject, defs, get.action, get)
|
||
return result
|
||
|
||
@app.route(route_v2 + '/overview', methods=method_all)
|
||
def overview_v2(pdata=None):
|
||
# 首页overview管理
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
from overviewV2.api import OverViewApi
|
||
defs = (
|
||
"overview_template",
|
||
"get_overview",
|
||
"add_overview",
|
||
"set_overview",
|
||
"del_overview",
|
||
"sort_overview",
|
||
"get_overview_window",
|
||
)
|
||
return publicObject(OverViewApi(), defs, None, pdata)
|
||
|
||
@app.route(route_v2 + "/business_ssl", methods=method_all)
|
||
def business_ssl(pdata=None):
|
||
# 商业SSL证书申请接口
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
from ssl_domainModelV2.business_ssl import BusinessSSL
|
||
toObject = BusinessSSL()
|
||
defs = (
|
||
'apply_cert_install_pay',
|
||
'get_verify_result',
|
||
'download_cert',
|
||
'get_order_list',
|
||
'get_order_find',
|
||
'get_product_list',
|
||
'apply_cert_order_pay',
|
||
'get_cert_admin',
|
||
'apply_order_ca',
|
||
'check_domain_suitable',
|
||
'list_business_ssl',
|
||
'renew_cert_order',
|
||
'check_url_txt',
|
||
'again_verify',
|
||
)
|
||
get = get_input()
|
||
|
||
if get.action != "download_cert":
|
||
result = publicObject(toObject, defs, get.action, get)
|
||
return result
|
||
|
||
from io import BytesIO
|
||
import base64
|
||
result = toObject.download_cert(get)
|
||
# {'success': False, 'res': '[code: 0] no data [file: /www/wwwroot/192.168.1.139/app/Api/Cert/controllers/Cert.php] [line: 955]', 'nonce': 1706498844}
|
||
fp = BytesIO(base64.b64decode(result['res']['data']))
|
||
return send_file(
|
||
fp,
|
||
download_name=result['res']['filename'],
|
||
as_attachment=True,
|
||
mimetype='application/zip',
|
||
)
|
||
|
||
|
||
@app.route(route_v2 + '/ssl_domain', methods=method_all)
|
||
def domain_v2(pdata=None):
|
||
# 域名管理
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
from ssl_domainModelV2.api import DomainObject
|
||
defs = (
|
||
"get_sites",
|
||
"sync_dns_info",
|
||
"get_dns_support",
|
||
"create_dns_api",
|
||
"delete_dns_api",
|
||
"list_dns_api",
|
||
"edit_dns_api",
|
||
"list_dns_record",
|
||
"create_dns_record",
|
||
"delete_dns_record",
|
||
"edit_dns_record",
|
||
"list_domain_details",
|
||
"list_ssl_info",
|
||
"download_cert",
|
||
# "renew_cert",
|
||
"one_cilck_renew",
|
||
"renew_cert_process",
|
||
"manual_apply_check",
|
||
"manual_apply_vaild",
|
||
"apply_new_ssl",
|
||
"upload_cert",
|
||
"switch_auto_renew",
|
||
"switch_ssl_alarm",
|
||
"cert_domain_list",
|
||
"cert_deploy_sites",
|
||
"cert_deploy_mails",
|
||
"cert_deploy_panel",
|
||
"remove_cert",
|
||
"check_domain_automatic",
|
||
"ssl_tasks_status",
|
||
"get_panel_domain",
|
||
"set_panel_domain_ssl",
|
||
"mail_record_check",
|
||
)
|
||
return publicObject(DomainObject(), defs, None, pdata)
|
||
|
||
@app.route(route_v2 + '/ssl_dns', methods=method_all)
|
||
def ssl_dns_v2(pdata=None):
|
||
# aaDns管理
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
from ssl_dnsV2.api import DnsApiObject
|
||
defs = (
|
||
"install_pdns",
|
||
"get_status",
|
||
"change_status",
|
||
"add_zone",
|
||
"del_zone",
|
||
"set_nameserver",
|
||
"get_nameserver",
|
||
"get_soa",
|
||
"set_soa",
|
||
"get_logger",
|
||
"clear_logger",
|
||
"add_dmarc",
|
||
"add_dkim_spf",
|
||
"dns_checker",
|
||
"fix_zone",
|
||
"set_ttl_batch",
|
||
)
|
||
return publicObject(DnsApiObject(), defs, None, pdata)
|
||
|
||
@app.route(route_v2 + '/dns_api', methods=method_all)
|
||
def dns_api_v2(pdata=None):
|
||
# dns api 开放, 子面板调用
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
from ssl_domainModelV2.external_api import SubPanelApi
|
||
defs = (
|
||
"account_create_record",
|
||
"account_list_ssl_info",
|
||
"account_domain_provider",
|
||
)
|
||
return publicObject(SubPanelApi(), defs, None, pdata)
|
||
|
||
|
||
|
||
@app.route(route_v2 + '/adminer_manager', methods=method_all)
|
||
def adminer_manager_v2(pdata=None):
|
||
comReturn = comm.local()
|
||
if comReturn:
|
||
return comReturn
|
||
from adminer.api import AdminerApi
|
||
defs = (
|
||
"support_versions",
|
||
"install",
|
||
"repair",
|
||
"uninstall",
|
||
"get_status",
|
||
"switch_php",
|
||
"switch_port",
|
||
)
|
||
return publicObject(AdminerApi(), defs, None, pdata)
|
||
|
||
@app.route(route_v2 + '/task', methods=method_all)
|
||
def task_v2(pdata=None):
|
||
# 后台任务接口
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import panel_task_v2
|
||
toObject = panel_task_v2.bt_task()
|
||
defs = ('get_task_lists', 'remove_task', 'get_task_find',
|
||
"get_task_log_by_id")
|
||
result = publicObject(toObject, defs, None, pdata)
|
||
return result
|
||
|
||
|
||
@app.route(route_v2 + '/plugin', methods=method_all)
|
||
def plugin_v2(pdata=None):
|
||
# 插件系统接口
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import panel_plugin_v2
|
||
pluginObject = panel_plugin_v2.panelPlugin()
|
||
defs = ('get_usually_plugin', 'check_install_limit', 'set_score',
|
||
'get_score', 'update_zip', 'input_zip', 'export_zip', 'add_index',
|
||
'remove_index', 'sort_index', 'install_plugin', 'uninstall_plugin',
|
||
'get_soft_find', 'get_index_list', 'get_soft_list',
|
||
'get_cloud_list', 'check_deps', 'flush_cache', 'GetCloudWarning',
|
||
'install', 'unInstall', 'getPluginList', 'getPluginInfo',
|
||
'get_make_args', 'add_make_args', 'getPluginStatus',
|
||
'setPluginStatus', 'a', 'getCloudPlugin', 'getConfigHtml',
|
||
'savePluginSort', 'del_make_args', 'set_make_args', 'get_download_speed')
|
||
return publicObject(pluginObject, defs, None, pdata)
|
||
|
||
|
||
@app.route(route_v2 + '/wxapp', methods=method_all)
|
||
@app.route(route_v2 + '/panel_wxapp', methods=method_all)
|
||
def panel_wxapp_v2(pdata=None):
|
||
# 微信小程序绑定接口
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import wxapp_v2
|
||
toObject = wxapp_v2.wxapp()
|
||
defs = ('blind', 'get_safe_log', 'blind_result', 'get_user_info',
|
||
'blind_del', 'blind_qrcode')
|
||
result = publicObject(toObject, defs, None, pdata)
|
||
return result
|
||
|
||
|
||
@app.route(route_v2 + '/auth', methods=method_all)
|
||
def auth_v2(pdata=None):
|
||
# 面板认证接口
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import panel_auth_v2
|
||
toObject = panel_auth_v2.panelAuth()
|
||
defs = ('free_trial', 'renew_product_auth', 'auth_activate',
|
||
'get_product_auth', 'get_product_auth_all','get_stripe_session_id',
|
||
'get_re_order_status_plugin', 'create_plugin_other_order',
|
||
'get_order_stat', 'get_voucher_plugin','get_voucher_plugin_all',
|
||
'create_order_voucher_plugin', 'get_product_discount_by',
|
||
'get_re_order_status', 'create_order_voucher', 'create_order',
|
||
'get_order_status', 'get_voucher', 'flush_pay_status',
|
||
'create_serverid', 'check_serverid', 'get_plugin_list',
|
||
'check_plugin', 'get_buy_code', 'check_pay_status',
|
||
'get_renew_code', 'check_renew_code', 'get_business_plugin',
|
||
'get_ad_list', 'check_plugin_end', 'get_plugin_price',
|
||
'get_plugin_remarks', 'get_paypal_session_id',
|
||
'check_paypal_status', 'get_wx_order_status', 'get_apply_copon',
|
||
'get_coupon_list', 'ignore_coupon_time', 'set_user_adviser', 'rest_unbind_count',
|
||
'unbind_authorization', 'get_all_voucher_plugin', 'get_pay_unbind_count',
|
||
'get_coupons', 'get_credits', 'create_with_credit_by_panel', 'get_last_paid_time',
|
||
'get_all_coupons', 'detect_order_status',
|
||
'get_expand_pack_prices',
|
||
)
|
||
result = publicObject(toObject, defs, None, pdata)
|
||
return result
|
||
|
||
|
||
@app.route(route_v2 + '/download', methods=method_get)
|
||
def download_v2():
|
||
# 文件下载接口
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
filename = request.args.get('filename')
|
||
if filename.find('|') != -1:
|
||
filename = filename.split('|')[1]
|
||
if not filename:
|
||
return public.ReturnJson(False, "INIT_ARGS_ERR"), json_header
|
||
|
||
if filename in [
|
||
'alioss', 'qiniu', 'upyun', 'txcos', 'ftp', 'msonedrive',
|
||
'gcloud_storage', 'gdrive', 'aws_s3', 'obs', 'bos'
|
||
]:
|
||
return panel_cloud(False)
|
||
|
||
# === 限定下载根目录 ===
|
||
import html
|
||
filepath = html.unescape(filename.replace('\x00', ''))
|
||
if '..' in filepath.split('/') or '..' in filepath.split('\\'):
|
||
return public.ReturnJson(False, "INVALID PATH"), json_header
|
||
filename = os.path.abspath(filepath)
|
||
if not os.path.exists(filename):
|
||
return public.ReturnJson(False, "File not exists"), json_header
|
||
if os.path.isdir(filename):
|
||
return public.ReturnJson(False, "The catalog is not downloadable"), json_header
|
||
|
||
try:
|
||
import stat
|
||
file_stat = os.stat(filename)
|
||
if stat.S_ISSOCK(file_stat.st_mode):
|
||
return public.ReturnJson(False, "Unix domain socket files are not downloadable"), json_header
|
||
elif stat.S_ISCHR(file_stat.st_mode):
|
||
return public.ReturnJson(False, "Character device files cannot be downloaded"), json_header
|
||
elif stat.S_ISBLK(file_stat.st_mode):
|
||
return public.ReturnJson(False, "Block device files are not downloadable"), json_header
|
||
elif stat.S_ISFIFO(file_stat.st_mode):
|
||
return public.ReturnJson(False, "FIFO pipeline files are not downloadable"), json_header
|
||
except:
|
||
pass
|
||
|
||
if request.args.get('play') == 'true':
|
||
import panelVideo
|
||
start, end = panelVideo.get_range(request)
|
||
g.return_message = True
|
||
return panelVideo.partial_response(filename, start, end)
|
||
else:
|
||
mimetype = "application/octet-stream"
|
||
extName = filename.split('.')[-1]
|
||
if extName in ['png', 'gif', 'jpeg', 'jpg']: mimetype = None
|
||
public.WriteLog("TYPE_FILE", 'FILE_DOWNLOAD',
|
||
(filename, public.GetClientIp()))
|
||
g.return_message = True
|
||
if not os.path.exists(filename):
|
||
return public.ReturnJson(False, "File not exists"), json_header
|
||
return send_file(filename,
|
||
mimetype=mimetype,
|
||
as_attachment=True,
|
||
etag=True,
|
||
conditional=True,
|
||
download_name=os.path.basename(filename),
|
||
max_age=0)
|
||
|
||
|
||
@app.route(route_v2 + '/cloud', methods=method_all)
|
||
def panel_cloud_v2(is_csrf=True):
|
||
# 从对像存储下载备份文件接口
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
if is_csrf:
|
||
if not check_csrf():
|
||
return public.ReturnJson(False, 'INIT_CSRF_ERR'), json_header
|
||
get = get_input()
|
||
_filename = get.filename
|
||
plugin_name = ""
|
||
if _filename.find('|') != -1:
|
||
plugin_name = get.filename.split('|')[1]
|
||
else:
|
||
plugin_name = get.filename
|
||
|
||
if not os.path.exists('plugin/' + plugin_name + '/' + plugin_name +
|
||
'_main.py'):
|
||
return public.returnJson(
|
||
False, 'The specified plugin does not exist!'), json_header
|
||
public.package_path_append('plugin/' + plugin_name)
|
||
plugin_main = __import__(plugin_name + '_main')
|
||
public.mod_reload(plugin_main)
|
||
tmp = eval("plugin_main.%s_main()" % plugin_name)
|
||
if not hasattr(tmp, 'download_file'):
|
||
return public.returnJson(
|
||
False,
|
||
'Specified plugin has no file download function!'), json_header
|
||
download_url = tmp.download_file(get.name)
|
||
if plugin_name == 'ftp':
|
||
if download_url.find("ftp") != 0:
|
||
download_url = "ftp://" + download_url
|
||
else:
|
||
if download_url.find('http') != 0:
|
||
download_url = 'http://' + download_url
|
||
|
||
if "toserver" in get and get.toserver == "true":
|
||
download_dir = "/tmp/"
|
||
if "download_dir" in get:
|
||
download_dir = get.download_dir
|
||
local_file = os.path.join(download_dir, get.name)
|
||
|
||
input_from_local = False
|
||
if "input_from_local" in get:
|
||
input_from_local = True if get.input_from_local == "true" else False
|
||
|
||
if input_from_local:
|
||
if os.path.isfile(local_file):
|
||
return {
|
||
"status": True,
|
||
"msg":
|
||
"The file already exists and will be restored locally.",
|
||
"task_id": -1,
|
||
"local_file": local_file
|
||
}
|
||
from panel_task_v2 import bt_task
|
||
task_obj = bt_task()
|
||
task_id = task_obj.create_task('Download file', 1, download_url,
|
||
local_file)
|
||
return {
|
||
"status": True,
|
||
"msg": "The download task was created successfully",
|
||
"local_file": local_file,
|
||
"task_id": task_id
|
||
}
|
||
|
||
return redirect(download_url)
|
||
|
||
|
||
@app.route(route_v2 + '/btwaf_error', methods=method_get)
|
||
def btwaf_error_v2():
|
||
# 图标
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
get = get_input()
|
||
p_path = os.path.join('/www/server/panel/plugin/', "btwaf")
|
||
if not os.path.exists(p_path):
|
||
if get.name == 'btwaf' and get.fun == 'index':
|
||
return render_template('error3.html', data={})
|
||
return render_template('error3.html', data={})
|
||
|
||
|
||
@app.route(route_v2 + '/favicon.ico', methods=method_get)
|
||
def send_favicon_v2():
|
||
# 图标
|
||
comReturn = comm.local()
|
||
if comReturn: return abort(404)
|
||
s_file = '/www/server/panel/YakPanel/static/favicon.ico'
|
||
if not os.path.exists(s_file): return abort(404)
|
||
return send_file(s_file, conditional=True, etag=True)
|
||
|
||
|
||
@app.route(route_v2 + '/rspamd', defaults={'path': ''}, methods=method_all)
|
||
@app.route(route_v2 + '/rspamd/<path:path>', methods=method_all)
|
||
def proxy_rspamd_requests_v2(path):
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
param = str(request.url).split('?')
|
||
param = "" if len(param) < 2 else param[-1]
|
||
import requests
|
||
headers = {}
|
||
for h in request.headers.keys():
|
||
headers[h] = request.headers[h]
|
||
if request.method == "GET":
|
||
if re.search(r"\.(js|css)$", path):
|
||
return send_file('/usr/share/rspamd/www/rspamd/' + path,
|
||
conditional=True,
|
||
etag=True)
|
||
if path == "/":
|
||
return send_file('/usr/share/rspamd/www/rspamd/',
|
||
conditional=True,
|
||
etag=True)
|
||
url = "http://127.0.0.1:11334/rspamd/" + path + "?" + param
|
||
for i in [
|
||
'stat', 'auth', 'neighbours', 'list_extractors',
|
||
'list_transforms', 'graph', 'maps', 'actions', 'symbols',
|
||
'history', 'errors', 'check_selector', 'saveactions',
|
||
'savesymbols', 'getmap'
|
||
]:
|
||
if i in path:
|
||
url = "http://127.0.0.1:11334/" + path + "?" + param
|
||
req = requests.get(url, headers=headers, stream=True)
|
||
return Resp(stream_with_context(req.iter_content()),
|
||
content_type=req.headers['content-type'])
|
||
else:
|
||
url = "http://127.0.0.1:11334/" + path
|
||
for i in request.form.keys():
|
||
data = '{}='.format(i)
|
||
# public.writeFile('/tmp/2',data+"\n","a+")
|
||
req = requests.post(url, data=data, headers=headers, stream=True)
|
||
|
||
return Resp(stream_with_context(req.iter_content()),
|
||
content_type=req.headers['content-type'])
|
||
|
||
|
||
@app.route(route_v2 + '/tips', methods=method_get)
|
||
def tips_v2():
|
||
# 提示页面
|
||
comReturn = comm.local()
|
||
if comReturn: return abort(404)
|
||
get = get_input()
|
||
if len(get.get_items().keys()) > 1: return abort(404)
|
||
return render_template('tips.html')
|
||
|
||
|
||
# ======================普通路由区end============================#
|
||
|
||
# ======================严格排查区域start============================#
|
||
|
||
|
||
@app.route(route_v2 + '/login', methods=method_all)
|
||
@app.route(route_v2 + route_path, methods=method_all)
|
||
@app.route(route_v2 + route_path + '/', methods=method_all)
|
||
def login_v2():
|
||
# 面板登录接口
|
||
if os.path.exists('install.pl'): return redirect('/install')
|
||
global admin_check_auth, admin_path, route_path
|
||
is_auth_path = False
|
||
if admin_path != '/bt' and os.path.exists(
|
||
admin_path_file) and not 'admin_auth' in session:
|
||
is_auth_path = True
|
||
# 登录输入验证
|
||
if request.method == method_post[0]:
|
||
if is_auth_path:
|
||
g.auth_error = True
|
||
return public.error_not_login(None)
|
||
v_list = ['username', 'password', 'code', 'vcode', 'cdn_url']
|
||
for v in v_list:
|
||
if v in ['username', 'password']: continue
|
||
pv = request.form.get(v, '').strip()
|
||
if v == 'cdn_url':
|
||
if len(pv) > 32:
|
||
return public.return_msg_gettext(
|
||
False, 'Wrong parameter length!'), json_header
|
||
if not re.match(r"^[\w\.-]+$", pv):
|
||
public.return_msg_gettext(
|
||
False, 'Wrong parameter format!'), json_header
|
||
continue
|
||
|
||
if not pv: continue
|
||
p_len = 32
|
||
if v == 'code': p_len = 4
|
||
if v == 'vcode': p_len = 6
|
||
if len(pv) != p_len:
|
||
if v == 'code':
|
||
return public.returnJson(
|
||
False, 'Verification code length error!'), json_header
|
||
return public.returnJson(
|
||
False, 'Wrong parameter length!'), json_header
|
||
if not re.match(r"^\w+$", pv):
|
||
return public.returnJson(
|
||
False, 'Wrong parameter format!'), json_header
|
||
|
||
for n in request.form.keys():
|
||
if not n in v_list:
|
||
return public.returnJson(
|
||
False,
|
||
'There can be no extra parameters in the login parameters'
|
||
), json_header
|
||
|
||
get = get_input()
|
||
import user_login_v2
|
||
if hasattr(get, 'tmp_token'):
|
||
result = user_login_v2.userlogin().request_tmp(get)
|
||
return is_login(result)
|
||
# 过滤爬虫
|
||
if public.is_spider(): return abort(404)
|
||
if hasattr(get, 'dologin'):
|
||
login_path = '/login'
|
||
if not 'login' in session: return redirect(login_path)
|
||
if os.path.exists(admin_path_file): login_path = route_path
|
||
if session['login'] != False:
|
||
session['login'] = False
|
||
cache.set('dologin', True)
|
||
public.write_log_gettext(
|
||
'Logout', 'Client: {}, has manually exited the panel',
|
||
(public.GetClientIp() + ":" +
|
||
str(request.environ.get('REMOTE_PORT')),))
|
||
if 'tmp_login_expire' in session:
|
||
s_file = 'data/session/{}'.format(session['tmp_login_id'])
|
||
if os.path.exists(s_file):
|
||
os.remove(s_file)
|
||
token_key = public.get_csrf_html_token_key()
|
||
if token_key in session:
|
||
del (session[token_key])
|
||
session.clear()
|
||
sess_file = 'data/sess_files/' + public.get_sess_key()
|
||
if os.path.exists(sess_file):
|
||
try:
|
||
os.remove(sess_file)
|
||
except:
|
||
pass
|
||
sess_tmp_file = public.get_full_session_file()
|
||
if os.path.exists(sess_tmp_file): os.remove(sess_tmp_file)
|
||
g.dologin = True
|
||
return redirect(public.get_admin_path())
|
||
|
||
if is_auth_path:
|
||
if route_path != request.path and route_path + '/' != request.path:
|
||
referer = request.headers.get('Referer', 'err')
|
||
referer_tmp = referer.split('/')
|
||
referer_path = referer_tmp[-1]
|
||
if referer_path == '':
|
||
referer_path = referer_tmp[-2]
|
||
if route_path != '/' + referer_path:
|
||
g.auth_error = True
|
||
# return render_template('autherr.html')
|
||
return public.error_not_login(None)
|
||
|
||
session['admin_auth'] = True
|
||
comReturn = common.panelSetup().init()
|
||
if comReturn: return comReturn
|
||
|
||
if request.method == method_post[0]:
|
||
result = userlogin.userlogin().request_post(get)
|
||
return is_login(result)
|
||
|
||
if request.method == method_get[0]:
|
||
result = userlogin.userlogin().request_get(get)
|
||
if result:
|
||
return result
|
||
data = {}
|
||
data['lan'] = public.GetLan('login')
|
||
data['hosts'] = '[]'
|
||
hosts_file = 'plugin/static_cdn/hosts.json'
|
||
if os.path.exists(hosts_file):
|
||
data['hosts'] = public.get_cdn_hosts()
|
||
if type(data['hosts']) == dict:
|
||
data['hosts'] = '[]'
|
||
else:
|
||
data['hosts'] = json.dumps(data['hosts'])
|
||
data['app_login'] = os.path.exists('data/app_login.pl')
|
||
public.cache_set(
|
||
public.Md5(
|
||
uuid.UUID(int=uuid.getnode()).hex[-12:] +
|
||
public.GetClientIp()), 'check', 360)
|
||
|
||
# 生成登录token
|
||
last_key = 'last_login_token'
|
||
# -----------
|
||
last_time_key = 'last_login_token_time'
|
||
s_time = int(time.time())
|
||
if last_key in session and last_time_key in session:
|
||
# 10秒内不重复生成token
|
||
if s_time - session[last_time_key] > 10:
|
||
session[last_key] = public.GetRandomString(32)
|
||
session[last_time_key] = s_time
|
||
else:
|
||
session[last_key] = public.GetRandomString(32)
|
||
session[last_time_key] = s_time
|
||
|
||
data[last_key] = session[last_key]
|
||
data['public_key'] = public.get_rsa_public_key()
|
||
return render_template('login.html', data=data)
|
||
|
||
|
||
@app.route(route_v2 + '/close', methods=method_get)
|
||
def close_v2():
|
||
# 面板已关闭页面
|
||
if not os.path.exists('data/close.pl'): return redirect('/')
|
||
data = {}
|
||
data['lan'] = public.getLan('close')
|
||
return render_template('close.html', data=data)
|
||
|
||
|
||
@app.route(route_v2 + '/get_app_bind_status', methods=method_all)
|
||
def get_app_bind_status_v2(pdata=None):
|
||
# APP绑定状态查询
|
||
if not public.check_app('app_bind'): return abort(404)
|
||
get = get_input()
|
||
if len(get.get_items().keys()) > 2: return 'There are meaningless parameters!'
|
||
v_list = ['bind_token', 'data']
|
||
for n in get.get_items().keys():
|
||
if not n in v_list:
|
||
return public.returnJson(
|
||
False, 'There can be no redundant parameters'), json_header
|
||
import panel_api_v2
|
||
api_object = panel_api_v2.panelApi()
|
||
return json.dumps(api_object.get_app_bind_status(get_input())), json_header
|
||
|
||
|
||
@app.route(route_v2 + '/check_bind', methods=method_all)
|
||
def check_bind_v2(pdata=None):
|
||
# APP绑定查询
|
||
if not public.check_app('app_bind'): return abort(404)
|
||
get = get_input()
|
||
if len(get.get_items().keys()) > 4: return 'There are meaningless parameters!'
|
||
v_list = ['bind_token', 'client_brand', 'client_model', 'data']
|
||
for n in get.get_items().keys():
|
||
if not n in v_list:
|
||
return public.returnJson(
|
||
False, 'There can be no redundant parameters'), json_header
|
||
import panel_api_v2
|
||
api_object = panel_api_v2.panelApi()
|
||
return json.dumps(api_object.check_bind(get_input())), json_header
|
||
|
||
|
||
@app.route(route_v2 + '/code', methods=method_get)
|
||
def code_v2():
|
||
if not 'code' in session: return ''
|
||
if not session['code']: return ''
|
||
# 获取图片验证码
|
||
try:
|
||
import vilidate_v2
|
||
except:
|
||
public.ExecShell("btpip install Pillow -I")
|
||
return "Pillow not install!"
|
||
vie = vilidate_v2.vieCode()
|
||
codeImage = vie.GetCodeImage(80, 4)
|
||
if sys.version_info[0] == 2:
|
||
try:
|
||
from cStringIO import StringIO
|
||
except:
|
||
from StringIO import StringIO
|
||
out = StringIO()
|
||
else:
|
||
from io import BytesIO
|
||
out = BytesIO()
|
||
codeImage[0].save(out, "png")
|
||
cache.set("codeStr", public.md5("".join(codeImage[1]).lower()), 180)
|
||
cache.set("codeOut", 1, 0.1)
|
||
out.seek(0)
|
||
return send_file(out, mimetype='image/png', max_age=0)
|
||
|
||
|
||
@app.route(route_v2 + '/down/<token>', methods=method_all)
|
||
def down_v2(token=None, fname=None):
|
||
# 文件分享对外接口
|
||
try:
|
||
if public.M('download_token').count() == 0: return abort(404)
|
||
fname = request.args.get('fname')
|
||
if fname:
|
||
if (len(fname) > 256): return abort(404)
|
||
if fname: fname = fname.strip('/')
|
||
if not token: return abort(404)
|
||
if len(token) > 48: return abort(404)
|
||
char_list = [
|
||
'\\', '/', ':', '*', '?', '"', '<', '>', '|', ';', '&', '`'
|
||
]
|
||
for char in char_list:
|
||
if char in token: return abort(404)
|
||
if not request.args.get('play') in ['true', None, '']:
|
||
return abort(404)
|
||
args = get_input()
|
||
v_list = ['fname', 'play', 'file_password', 'data']
|
||
for n in args.get_items().keys():
|
||
if not n in v_list:
|
||
return public.returnJson(
|
||
False, 'There can be no redundant parameters'), json_header
|
||
if not re.match(r"^[\w\.]+$", token): return abort(404)
|
||
find = public.M('download_token').where('token=?', (token,)).find()
|
||
|
||
if not find: return abort(404)
|
||
if time.time() > int(find['expire']): return abort(404)
|
||
|
||
if not os.path.exists(find['filename']): return abort(404)
|
||
if find['password'] and not token in session:
|
||
if 'file_password' in args:
|
||
if not re.match(r"^\w+$", args.file_password):
|
||
return public.ReturnJson(False,
|
||
'Wrong password!'), json_header
|
||
if re.match(r"^\d+$", args.file_password):
|
||
args.file_password = str(int(args.file_password))
|
||
args.file_password += ".0"
|
||
if args.file_password != str(find['password']):
|
||
return public.ReturnJson(False,
|
||
'Wrong password!'), json_header
|
||
session[token] = 1
|
||
session['down'] = True
|
||
else:
|
||
pdata = {
|
||
"to_path": "",
|
||
"src_path": find['filename'],
|
||
"password": True,
|
||
"filename": find['filename'].split('/')[-1],
|
||
"ps": find['ps'],
|
||
"total": find['total'],
|
||
"token": find['token'],
|
||
"expire": public.format_date(times=find['expire'])
|
||
}
|
||
session['down'] = True
|
||
return render_template('down.html', data=pdata)
|
||
|
||
if not find['password']:
|
||
session['down'] = True
|
||
session[token] = 1
|
||
|
||
if session[token] != 1:
|
||
return abort(404)
|
||
|
||
filename = find['filename']
|
||
if fname:
|
||
filename = os.path.join(filename, fname)
|
||
if not public.path_safe_check(fname, False): return abort(404)
|
||
if os.path.isdir(filename):
|
||
return get_dir_down(filename, token, find)
|
||
else:
|
||
if os.path.isdir(filename):
|
||
return get_dir_down(filename, token, find)
|
||
|
||
if request.args.get('play') == 'true':
|
||
import panel_video_v2
|
||
start, end = panel_video_v2.get_range(request)
|
||
return panel_video_v2.partial_response(filename, start, end)
|
||
else:
|
||
mimetype = "application/octet-stream"
|
||
extName = filename.split('.')[-1]
|
||
if extName in ['png', 'gif', 'jpeg', 'jpg']: mimetype = None
|
||
b_name = os.path.basename(filename)
|
||
return send_file(filename,
|
||
mimetype=mimetype,
|
||
as_attachment=True,
|
||
download_name=b_name,
|
||
max_age=0)
|
||
except:
|
||
return abort(404)
|
||
|
||
|
||
@app.route(route_v2 + '/database/mongodb/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/database/pgsql/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/database/redis/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/database/sqlite/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/database/sqlserver/<def_name>', methods=method_all)
|
||
def databaseModel_v2(def_name):
|
||
if request.method not in ['GET', 'POST']: return
|
||
path_split = request.path.split("/")
|
||
if len(path_split) < 5: return
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
from panelDatabaseControllerV2 import DatabaseController
|
||
project_obj = DatabaseController()
|
||
defs = ('model',)
|
||
get = get_input()
|
||
get.action = 'model'
|
||
get.mod_name = path_split[3]
|
||
get.def_name = def_name
|
||
return publicObject(project_obj, defs, None, get)
|
||
|
||
|
||
# 系统安全模型页面
|
||
# @app.route(route_v2+'/safe/<mod_name>/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/safe/firewall/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/safe/freeip/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/safe/ips/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/safe/security/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/safe/ssh/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/safe/syslog/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/safe/serversafe/<def_name>', methods=method_all)
|
||
def safeModel_v2(def_name):
|
||
if request.method not in ['GET', 'POST']: return
|
||
path_split = request.path.split("/")
|
||
if len(path_split) < 5: return
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
from panelSafeControllerV2 import SafeController
|
||
project_obj = SafeController()
|
||
defs = ('model',)
|
||
get = get_input()
|
||
get.action = 'model'
|
||
get.mod_name = path_split[3]
|
||
get.def_name = def_name
|
||
|
||
return publicObject(project_obj, defs, None, get)
|
||
|
||
|
||
# 通用模型路由
|
||
@app.route(route_v2 + '/panel/binlog/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/panel/bt_check/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/panel/clear/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/panel/content/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/panel/docker/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/panel/go/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/panel/java/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/panel/nodejs/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/panel/other/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/panel/php/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/panel/python/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/panel/quota/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/panel/quota/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/panel/safe_detect/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/panel/scanning/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/panel/start_content/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/panel/totle_db/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/panel/webscanning/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/panel/public/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/monitor/process_management/<def_name>',
|
||
methods=method_all)
|
||
@app.route(route_v2 + '/monitor/soft/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/files/down/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/files/gz/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/files/logs/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/files/rar/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/files/search/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/files/size/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/files/upload/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/files/zip/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/logs/ftp/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/logs/panel/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/logs/site/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/crontab/trigger/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/crontab/script/<def_name>', methods=method_all)
|
||
def allModule_v2(def_name):
|
||
if request.method not in ['GET', 'POST']: return
|
||
path_split = request.path.split("/")
|
||
if len(path_split) < 4: return
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
p_path = public.get_plugin_path() + '/' + path_split[2]
|
||
|
||
defs = ('model',)
|
||
get = get_input()
|
||
get.model_index = path_split[2]
|
||
get.action = 'model'
|
||
get.mod_name = path_split[3]
|
||
get.def_name = def_name
|
||
if not request.path.startswith(route_v2 + '/monitor/') and os.path.exists(p_path):
|
||
return panel_other(get.model_index, get.mod_name, def_name)
|
||
|
||
from panelControllerV2 import Controller
|
||
controller_obj = Controller()
|
||
return publicObject(controller_obj, defs, None, get)
|
||
|
||
|
||
@app.route(route_v2 + '/public', methods=method_all)
|
||
def panel_public_v2():
|
||
get = get_input()
|
||
if len("{}".format(get.get_items())) > 1024 * 32:
|
||
return 'ERROR'
|
||
|
||
# 获取ping测试
|
||
if 'get_ping' in get:
|
||
try:
|
||
import panel_ping_v2
|
||
p = panel_ping_v2.Test()
|
||
get = p.check(get)
|
||
if not get: return 'ERROR'
|
||
result = getattr(p, get['act'])(get)
|
||
result_type = type(result)
|
||
if str(result_type).find('Response') != -1: return result
|
||
return public.getJson(result), json_header
|
||
except:
|
||
return abort(404)
|
||
|
||
if public.cache_get(
|
||
public.Md5(
|
||
uuid.UUID(int=uuid.getnode()).hex[-12:] +
|
||
public.GetClientIp())) != 'check':
|
||
return abort(404)
|
||
global admin_check_auth, admin_path, route_path, admin_path_file
|
||
if admin_path != '/bt' and os.path.exists(
|
||
admin_path_file) and not 'admin_auth' in session:
|
||
return abort(404)
|
||
v_list = ['fun', 'name', 'filename', 'data', 'secret_key']
|
||
for n in get.get_items().keys():
|
||
if not n in v_list:
|
||
return abort(404)
|
||
|
||
get.client_ip = public.GetClientIp()
|
||
num_key = get.client_ip + '_wxapp'
|
||
if not public.get_error_num(num_key, 10):
|
||
return public.return_msg_gettext(
|
||
False,
|
||
'10 consecutive authentication failures are prohibited for 1 hour')
|
||
if not hasattr(get, 'name'): get.name = ''
|
||
if not hasattr(get, 'fun'): return abort(404)
|
||
if not public.path_safe_check("%s/%s" % (get.name, get.fun)):
|
||
return abort(404)
|
||
if get.fun in ['login_qrcode', 'is_scan_ok', 'set_login']:
|
||
# 检查是否验证过安全入口
|
||
if admin_path != '/bt' and os.path.exists(
|
||
admin_path_file) and not 'admin_auth' in session:
|
||
return abort(404)
|
||
# 验证是否绑定了设备
|
||
if not public.check_app('app'):
|
||
return public.return_msg_gettext(False, 'Unbound user')
|
||
import wxapp_v2
|
||
pluwx = wxapp_v2.wxapp()
|
||
checks = pluwx._check(get)
|
||
if type(checks) != bool or not checks:
|
||
public.set_error_num(num_key)
|
||
return public.getJson(checks), json_header
|
||
data = public.getJson(eval('pluwx.' + get.fun + '(get)'))
|
||
return data, json_header
|
||
else:
|
||
return abort(404)
|
||
|
||
|
||
@app.route(route_v2 + '/<name>/<fun>', methods=method_all)
|
||
@app.route(route_v2 + '/<name>/<fun>/<path:stype>', methods=method_all)
|
||
def panel_other_v2(name=None, fun=None, stype=None):
|
||
|
||
# 插件接口
|
||
if public.is_error_path():
|
||
return redirect('/error', 302)
|
||
if not name: return abort(404)
|
||
if not re.match(r"^[\w\-]+$", name): return abort(404)
|
||
if fun and not re.match(r"^[\w\-\.]+$", fun): return abort(404)
|
||
if name != "mail_sys" or fun != "send_mail_http.json":
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
if not stype:
|
||
tmp = fun.split('.')
|
||
fun = tmp[0]
|
||
if len(tmp) == 1: tmp.append('')
|
||
stype = tmp[1]
|
||
if fun:
|
||
if name == 'btwaf' and fun == 'index':
|
||
pass
|
||
elif name == 'firewall' and fun == 'get_file':
|
||
pass
|
||
elif fun == 'static':
|
||
pass
|
||
elif stype == 'html':
|
||
pass
|
||
else:
|
||
if public.get_csrf_cookie_token_key(
|
||
) in session and 'login' in session:
|
||
if not check_csrf():
|
||
return public.ReturnJson(
|
||
False,
|
||
'CSRF calibration failed, please login again'
|
||
), json_header
|
||
args = None
|
||
else:
|
||
p_path = public.get_plugin_path() + '/' + name
|
||
if not os.path.exists(p_path): return abort(404)
|
||
args = get_input()
|
||
args_list = [
|
||
'mail_from', 'password', 'mail_to', 'subject', 'content',
|
||
'subtype', 'data'
|
||
]
|
||
for k in args.get_items():
|
||
if not k in args_list: return abort(404)
|
||
|
||
is_accept = False
|
||
if not fun: fun = 'index.html'
|
||
if not stype:
|
||
tmp = fun.split('.')
|
||
fun = tmp[0]
|
||
if len(tmp) == 1: tmp.append('')
|
||
stype = tmp[1]
|
||
|
||
if not name: name = 'coll'
|
||
if not public.path_safe_check("%s/%s/%s" % (name, fun, stype)):
|
||
return abort(404)
|
||
if name.find('./') != -1 or not re.match(r"^[\w-]+$", name):
|
||
return abort(404)
|
||
if not name:
|
||
return public.returnJson(
|
||
False, 'Please pass in the plug-in name!'), json_header
|
||
p_path = public.get_plugin_path() + '/' + name
|
||
if not os.path.exists(p_path):
|
||
if name == 'btwaf' and fun == 'index':
|
||
pdata = {}
|
||
import panel_plugin_v2
|
||
plu_panel = panel_plugin_v2.panelPlugin()
|
||
plugin_list = plu_panel.get_cloud_list()
|
||
if not 'pro' in plugin_list: plugin_list['pro'] = -1
|
||
for p in plugin_list['list']:
|
||
if p['name'] in ['btwaf']:
|
||
if p['endtime'] != 0 and p['endtime'] < time.time():
|
||
pdata['error_msg'] = 1
|
||
break
|
||
return render_template('error3.html', data=pdata)
|
||
return abort(404)
|
||
|
||
# 是否响插件应静态文件
|
||
if fun == 'static':
|
||
if stype.find('./') != -1 or not os.path.exists(p_path + '/static'):
|
||
return abort(404)
|
||
s_file = p_path + '/static/' + stype
|
||
if s_file.find('..') != -1: return abort(404)
|
||
if not re.match(r"^[\w\./-]+$", s_file): return abort(404)
|
||
if not public.path_safe_check(s_file): return abort(404)
|
||
if not os.path.exists(s_file): return abort(404)
|
||
return send_file(s_file, conditional=True, etag=True)
|
||
|
||
# 准备参数
|
||
if not args: args = get_input()
|
||
args.client_ip = public.GetClientIp()
|
||
args.fun = fun
|
||
|
||
# 初始化插件对象
|
||
try:
|
||
|
||
import PluginLoader
|
||
try:
|
||
args.s = fun
|
||
data = PluginLoader.plugin_run(name, fun, args)
|
||
if isinstance(data, dict):
|
||
if 'status' in data and data['status'] == False and 'msg' in data:
|
||
if isinstance(data['msg'], str):
|
||
if data['msg'].find('加载失败') != -1 or data['msg'].find('Traceback ') == 0:
|
||
raise public.PanelError(data['msg'])
|
||
except Exception as ex:
|
||
if name == 'btwaf' and fun == 'index' and str(ex).find('未购买') != -1:
|
||
return render_template('error3.html', data={})
|
||
return public.get_error_object(None, plugin_name=name)
|
||
|
||
r_type = type(data)
|
||
if r_type in [Response, Resp]:
|
||
return data
|
||
|
||
# 处理响应
|
||
if stype == 'json': # 响应JSON
|
||
# 兼容btwaf插件 v2调用改返回 v2/btwaf/xxx.json
|
||
if name == 'btwaf':
|
||
# public.print_log("插件调用22---{}".format(type(data)))
|
||
if type(data) == dict:
|
||
if 'msg' in data:
|
||
status = 0 if data['status'] else -1
|
||
data = public.return_message(status, 0, data['msg'])
|
||
else:
|
||
data = public.return_message(0, 0, data)
|
||
else:
|
||
data = public.return_message(0, 0, data)
|
||
return public.getJson(data), json_header
|
||
elif stype == 'html': # 使用模板
|
||
t_path_root = p_path + '/templates/'
|
||
t_path = t_path_root + fun + '.html'
|
||
if not os.path.exists(t_path):
|
||
return public.returnJson(
|
||
False,
|
||
'The specified template does not exist!'), json_header
|
||
t_body = public.readFile(t_path)
|
||
|
||
# 处理模板包含
|
||
rep = r'{%\s?include\s"(.+)"\s?%}'
|
||
includes = re.findall(rep, t_body)
|
||
for i_file in includes:
|
||
filename = p_path + '/templates/' + i_file
|
||
i_body = 'ERROR: File ' + filename + ' does not exists.'
|
||
if os.path.exists(filename):
|
||
i_body = public.readFile(filename)
|
||
t_body = re.sub(rep.replace('(.+)', i_file), i_body, t_body)
|
||
|
||
return render_template_string(t_body, data=data)
|
||
else: # 直接响应插件返回值,可以是任意flask支持的响应类型
|
||
r_type = type(data)
|
||
if r_type == dict:
|
||
if name == 'btwaf' and 'msg' in data:
|
||
return render_template('error3.html',
|
||
data={"error_msg": data['msg']})
|
||
return public.returnJson(
|
||
False,
|
||
public.getMsg('Bad return type [{}]').format(r_type)), json_header
|
||
# public.getMsg('PUBLIC_ERR_RETURN')), json_header
|
||
if name == 'btwaf':
|
||
data = public.return_message(0, 0, data)
|
||
return data
|
||
except:
|
||
return public.get_error_info()
|
||
|
||
@app.route(route_v2 + '/hook', methods=method_all)
|
||
def panel_hook_v2():
|
||
# webhook接口
|
||
get = get_input()
|
||
if not os.path.exists('plugin/webhook'):
|
||
return abort(404)
|
||
public.package_path_append('plugin/webhook')
|
||
import webhook_main
|
||
return public.getJson(webhook_main.webhook_main().RunHook(get))
|
||
|
||
|
||
@app.route(route_v2 + '/install', methods=method_all)
|
||
def install_v2():
|
||
# 初始化面板接口
|
||
if public.is_spider(): return abort(404)
|
||
if not os.path.exists('install.pl'): return redirect('/login')
|
||
if public.M('config').where("id=?", ('1',)).getField('status') == 1:
|
||
if os.path.exists('install.pl'): os.remove('install.pl')
|
||
session.clear()
|
||
return redirect('/login')
|
||
ret_login = os.path.join('/', admin_path)
|
||
if admin_path == '/' or admin_path == '/bt': ret_login = '/login'
|
||
session['admin_path'] = False
|
||
session['login'] = False
|
||
if request.method == method_get[0]:
|
||
if not os.path.exists('install.pl'): return redirect(ret_login)
|
||
data = {}
|
||
data['status'] = os.path.exists('install.pl')
|
||
data['username'] = public.GetRandomString(8).lower()
|
||
return render_template('install.html', data=data)
|
||
|
||
elif request.method == method_post[0]:
|
||
if not os.path.exists('install.pl'): return redirect(ret_login)
|
||
get = get_input()
|
||
if not hasattr(get, 'bt_username'):
|
||
return public.get_msg_gettext('The user name cannot be empty!')
|
||
if not get.bt_username:
|
||
return public.get_msg_gettext('The user name cannot be empty!')
|
||
if not hasattr(get, 'bt_password1'):
|
||
return public.get_msg_gettext('Password can not be blank!')
|
||
if not get.bt_password1:
|
||
return public.get_msg_gettext('Password can not be blank!')
|
||
if get.bt_password1 != get.bt_password2:
|
||
return public.get_msg_gettext(
|
||
'The passwords entered twice do not match, please re-enter!')
|
||
public.M('users').where("id=?", (1,)).save(
|
||
'username,password',
|
||
(get.bt_username,
|
||
public.password_salt(public.md5(get.bt_password1.strip()),
|
||
uid=1)))
|
||
os.remove('install.pl')
|
||
public.M('config').where("id=?", ('1',)).setField('status', 1)
|
||
data = {}
|
||
data['status'] = os.path.exists('install.pl')
|
||
data['username'] = get.bt_username
|
||
return render_template('install.html', data=data)
|
||
|
||
|
||
# --------------------- websocket START -------------------------- #
|
||
|
||
|
||
@sockets.route(route_v2 + '/workorder_client')
|
||
def workorder_client_v2(ws):
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
|
||
get = ws.receive()
|
||
get = json.loads(get)
|
||
if not check_csrf_websocket(ws, get):
|
||
return
|
||
|
||
import panelWorkorder
|
||
toObject = panelWorkorder.panelWorkorder()
|
||
get = get_input()
|
||
toObject.client(ws, get)
|
||
|
||
|
||
@sockets.route(route_v2 + '/ws_panel')
|
||
def ws_panel_v2(ws):
|
||
'''
|
||
@name 面板接口ws入口
|
||
@author hwliang<2021-07-24>
|
||
@param ws<ws_parameter> websocket会话对像
|
||
@return void
|
||
'''
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
|
||
get = ws.receive()
|
||
get = json.loads(get)
|
||
if not check_csrf_websocket(ws, get): return
|
||
|
||
while True:
|
||
pdata = ws.receive()
|
||
if pdata == '{}': break
|
||
data = json.loads(pdata)
|
||
get = public.to_dict_obj(data)
|
||
get._ws = ws
|
||
p = threading.Thread(target=ws_panel_thread_v2, args=(get,))
|
||
p.start()
|
||
|
||
|
||
def ws_panel_thread_v2(get):
|
||
'''
|
||
@name 面板管理ws线程
|
||
@author hwliang<2021-07-24>
|
||
@param get<dict> 请求参数
|
||
@return void
|
||
'''
|
||
|
||
if not hasattr(get, 'ws_callback'):
|
||
get._ws.send(
|
||
public.getJson(public.return_status_code(1001, 'ws_callback')))
|
||
return
|
||
if not hasattr(get, 'mod_name'):
|
||
get._ws.send(
|
||
public.getJson(public.return_status_code(1001, 'mod_name')))
|
||
return
|
||
if not hasattr(get, 'def_name'):
|
||
get._ws.send(
|
||
public.getJson(public.return_status_code(1001, 'def_name')))
|
||
return
|
||
get.mod_name = get.mod_name.strip()
|
||
get.def_name = get.def_name.strip()
|
||
check_str = '{}{}'.format(get.mod_name, get.def_name)
|
||
if not re.match(r"^\w+$", check_str) or get.mod_name in [
|
||
'public', 'common', 'db', 'db_mysql', 'downloadFile', 'jobs'
|
||
]:
|
||
get._ws.send(
|
||
public.getJson(
|
||
public.return_status_code(
|
||
1000, 'Unsafe mod_name, def_name parameter content')))
|
||
return
|
||
|
||
mod_file = '{}/{}.py'.format(public.get_class_path(), get.mod_name)
|
||
if not os.path.exists(mod_file):
|
||
get._ws.send(
|
||
public.getJson(
|
||
public.return_status_code(
|
||
1000, 'Specified module {} does not exist'.format(
|
||
get.mod_name))))
|
||
return
|
||
_obj = public.get_script_object(mod_file)
|
||
if not _obj:
|
||
get._ws.send(
|
||
public.getJson(
|
||
public.return_status_code(
|
||
1000, 'Specified module {} does not exist'.format(
|
||
get.mod_name))))
|
||
return
|
||
_cls = getattr(_obj, get.mod_name)
|
||
if not _cls:
|
||
get._ws.send(
|
||
public.getJson(
|
||
public.return_status_code(
|
||
1000,
|
||
'The {} object was not found in the {} module'.format(
|
||
get.mod_name, get.mod_name))))
|
||
return
|
||
_def = getattr(_cls(), get.def_name)
|
||
if not _def:
|
||
get._ws.send(
|
||
public.getJson(
|
||
public.return_status_code(
|
||
1000,
|
||
'The {} object was not found in the {} module'.format(
|
||
get.mod_name, get.def_name))))
|
||
return
|
||
result = {'callback': get.ws_callback, 'result': _def(get)}
|
||
get._ws.send(public.getJson(result))
|
||
|
||
|
||
@sockets.route(route_v2 + '/ws_project')
|
||
def ws_project_v2(ws):
|
||
'''
|
||
@name 项目管理ws入口
|
||
@author hwliang<2021-07-24>
|
||
@param ws<ws_parameter> websocket会话对像
|
||
@return void
|
||
'''
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
get = ws.receive()
|
||
get = json.loads(get)
|
||
if not check_csrf_websocket(ws, get): return
|
||
|
||
from panelProjectControllerV2 import ProjectController
|
||
project_obj = ProjectController()
|
||
while True:
|
||
pdata = ws.receive()
|
||
if pdata in '{}': break
|
||
get = public.to_dict_obj(json.loads(pdata))
|
||
get._ws = ws
|
||
p = threading.Thread(target=ws_project_thread_v2,
|
||
args=(project_obj, get))
|
||
p.start()
|
||
|
||
|
||
def ws_project_thread_v2(_obj, get):
|
||
'''
|
||
@name 项目管理ws线程
|
||
@author hwliang<2021-07-24>
|
||
@param _obj<ProjectController> 项目管理控制器对像
|
||
@param get<dict> 请求参数
|
||
@return void
|
||
'''
|
||
if not hasattr(get, 'ws_callback'):
|
||
get._ws.send(
|
||
public.getJson(public.return_status_code(1001, 'ws_callback')))
|
||
return
|
||
result = {'callback': get.ws_callback, 'result': _obj.model(get)}
|
||
get._ws.send(public.getJson(result))
|
||
|
||
|
||
sock_pids = {}
|
||
|
||
|
||
@sockets.route(route_v2 + '/sock_shell')
|
||
def sock_shell_v2(ws):
|
||
'''
|
||
@name 执行指定命令,实时输出命令执行结果
|
||
@author hwliang<2021-07-19>
|
||
@return void
|
||
|
||
示例:
|
||
p = new WebSocket('ws://192.168.1.247:8888/sock_shell')
|
||
p.send('ping www.yakpanel.com -c 100')
|
||
'''
|
||
comReturn = comm.local()
|
||
if comReturn:
|
||
ws.send(str(comReturn))
|
||
return
|
||
kill_closed_v2()
|
||
get = ws.receive()
|
||
get = json.loads(get)
|
||
if not check_csrf_websocket(ws, get): return
|
||
|
||
t = None
|
||
try:
|
||
while True:
|
||
cmdstring = ws.receive()
|
||
if cmdstring in ['stop', 'error'] or not cmdstring:
|
||
break
|
||
t = threading.Thread(target=sock_recv, args=(cmdstring, ws))
|
||
t.start()
|
||
kill_closed_v2()
|
||
except:
|
||
kill_closed_v2()
|
||
|
||
|
||
def kill_closed_v2():
|
||
'''
|
||
@name 关闭已关闭的连接
|
||
@author hwliang<2021-07-24>
|
||
@return void
|
||
'''
|
||
global sock_pids
|
||
import psutil
|
||
pids = psutil.pids()
|
||
keys = sock_pids.copy().keys()
|
||
for pid in keys:
|
||
if hasattr(sock_pids[pid], 'closed'):
|
||
is_closed = sock_pids[pid].closed
|
||
else:
|
||
is_closed = not sock_pids[pid].connected
|
||
|
||
logging.debug("PID: {} , sock_stat: {}".format(pid, is_closed))
|
||
if not is_closed: continue
|
||
|
||
if pid in pids:
|
||
try:
|
||
p = psutil.Process(pid)
|
||
for cp in p.children():
|
||
cp.kill()
|
||
p.kill()
|
||
logging.debug("killed: {}".format(pid))
|
||
sock_pids.pop(pid)
|
||
except:
|
||
pass
|
||
else:
|
||
sock_pids.pop(pid)
|
||
|
||
|
||
@app.route(route_v2 + '/close_sock_shell', methods=method_all)
|
||
def close_sock_shell_v2():
|
||
'''
|
||
@name 关闭指定命令
|
||
@author hwliang<2021-07-19>
|
||
@param cmdstring<string> 完整命令行
|
||
@return dict
|
||
示例:
|
||
$.post('/close_sock_shell',{cmdstring:'ping www.yakpanel.com -c 100'})
|
||
'''
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
args = get_input()
|
||
if not check_csrf():
|
||
return public.ReturnJson(False, 'INIT_CSRF_ERR'), json_header
|
||
cmdstring = args.cmdstring.strip()
|
||
skey = public.md5(cmdstring)
|
||
pid = cache.get(skey)
|
||
if not pid:
|
||
return json.dumps(
|
||
public.return_data(
|
||
False, [], error_msg='The specified sock has been terminated!')
|
||
), json_header
|
||
os.kill(pid, 9)
|
||
cache.delete(skey)
|
||
return json.dumps(public.return_data(True,
|
||
'Successful operation!')), json_header
|
||
|
||
|
||
@sockets.route(route_v2 + '/webssh')
|
||
def webssh_v2(ws):
|
||
# YakPanel终端连接
|
||
comReturn = comm.local()
|
||
if comReturn:
|
||
ws.send(str(comReturn))
|
||
return
|
||
if not ws: return 'False'
|
||
get = ws.receive()
|
||
if not get: return
|
||
get = json.loads(get)
|
||
if not check_csrf_websocket(ws, get):
|
||
return
|
||
# public.print_log("传入信息-get:{}".format(get))
|
||
import ssh_terminal_v2
|
||
sp = ssh_terminal_v2.ssh_host_admin()
|
||
if 'host' in get:
|
||
ssh_info = {}
|
||
ssh_info['host'] = get['host'].strip()
|
||
if 'port' in get:
|
||
ssh_info['port'] = int(get['port'])
|
||
if 'username' in get:
|
||
ssh_info['username'] = get['username'].strip()
|
||
if 'password' in get:
|
||
ssh_info['password'] = get['password'].strip()
|
||
if 'pkey' in get:
|
||
ssh_info['pkey'] = get['pkey'].strip()
|
||
|
||
if get['host'] in ['127.0.0.1', 'localhost']:
|
||
if not 'password' in ssh_info:
|
||
ssh_info = sp.get_ssh_info('127.0.0.1')
|
||
if not ssh_info: ssh_info = sp.get_ssh_info('localhost')
|
||
if not ssh_info: ssh_info = {"host": "127.0.0.1"}
|
||
if not 'port' in ssh_info:
|
||
ssh_info['port'] = public.get_ssh_port()
|
||
#当密码和key都为空的时候
|
||
if not 'password' in ssh_info and not 'pkey' in ssh_info:
|
||
import ssh_security_v2
|
||
sshobject = ssh_security_v2.ssh_security()
|
||
ssh_info['pkey'] = sshobject.get_key(get).get("message",{}).get("result","")
|
||
else:
|
||
# public.print_log("无host")
|
||
ssh_info = sp.get_ssh_info('127.0.0.1')
|
||
if not ssh_info: ssh_info = sp.get_ssh_info('localhost')
|
||
if not ssh_info: ssh_info = {"host": "127.0.0.1"}
|
||
ssh_info['port'] = public.get_ssh_port()
|
||
|
||
if not ssh_info['host'] in ['127.0.0.1', 'localhost']:
|
||
if not 'username' in ssh_info:
|
||
ssh_info = sp.get_ssh_info(ssh_info['host'])
|
||
if not ssh_info:
|
||
ws.send(
|
||
'The specified host information is not found, please add it again!'
|
||
)
|
||
return
|
||
p = ssh_terminal_v2.ssh_terminal()
|
||
# public.print_log("传入信息----ws:{}---ssh_info:{}".format(ws, ssh_info))
|
||
p.run(ws, ssh_info)
|
||
del (p)
|
||
if ws.connected:
|
||
ws.close()
|
||
return 'False'
|
||
|
||
|
||
# --------------------- websocket END -------------------------- #
|
||
|
||
|
||
@app.route(route_v2 + "/daily", methods=method_all)
|
||
def daily_v2():
|
||
"""面板日报数据"""
|
||
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
|
||
import panelDaily
|
||
toObject = panelDaily.panelDaily()
|
||
|
||
defs = ("get_app_usage", "get_daily_data", "get_daily_list")
|
||
result = publicObject(toObject, defs)
|
||
return result
|
||
|
||
|
||
@app.route(route_v2 + '/phpmyadmin/<path:path_full>', methods=method_all)
|
||
def pma_proxy_v2(path_full=None):
|
||
'''
|
||
@name phpMyAdmin代理
|
||
@author hwliang<2022-01-19>
|
||
@return Response
|
||
'''
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
cache_key = 'pmd_port_path'
|
||
pmd = cache.get(cache_key)
|
||
if not pmd:
|
||
pmd = get_phpmyadmin_dir()
|
||
if not pmd:
|
||
return 'phpMyAdmin is not installed, please go to the [App Store] page to install it!'
|
||
pmd = list(pmd)
|
||
cache.set(cache_key, pmd, 10)
|
||
panel_pool = 'http://'
|
||
if request.url_root[:5] == 'https':
|
||
panel_pool = 'https://'
|
||
import ajax
|
||
ssl_info = ajax.ajax().get_phpmyadmin_ssl(None)
|
||
if ssl_info['status']:
|
||
pmd[1] = ssl_info['port']
|
||
else:
|
||
panel_pool = 'http://'
|
||
|
||
proxy_url = '{}127.0.0.1:{}/{}/'.format(
|
||
panel_pool, pmd[1], pmd[0]) + request.full_path.replace(
|
||
'/phpmyadmin/', '')
|
||
from panel_http_proxy_v2 import HttpProxy
|
||
px = HttpProxy()
|
||
return px.proxy(proxy_url)
|
||
|
||
|
||
@app.route(route_v2 + '/p/<int:port>', methods=method_all)
|
||
@app.route(route_v2 + '/p/<int:port>/', methods=method_all)
|
||
@app.route(route_v2 + '/p/<int:port>/<path:full_path>', methods=method_all)
|
||
def proxy_port_v2(port, full_path=None):
|
||
'''
|
||
@name 代理指定端口
|
||
@author hwliang<2022-01-19>
|
||
@return Response
|
||
'''
|
||
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
full_path = request.full_path.replace('/p/{}/'.format(port),
|
||
'').replace('/p/{}'.format(port), '')
|
||
uri = '{}/{}'.format(port, full_path)
|
||
uri = uri.replace('//', '/')
|
||
proxy_url = 'http://127.0.0.1:{}'.format(uri)
|
||
from panel_http_proxy_v2 import HttpProxy
|
||
px = HttpProxy()
|
||
return px.proxy(proxy_url)
|
||
|
||
|
||
@app.route(route_v2 + '/push', methods=method_all)
|
||
def push_v2(pdata=None):
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import panel_push_v2
|
||
toObject = panel_push_v2.panelPush()
|
||
defs = ('set_push_status', 'get_push_msg_list', 'get_modules_list',
|
||
'install_module', 'uninstall_module', 'get_module_template',
|
||
'set_push_config', 'get_push_config', 'del_push_config',
|
||
'get_module_logs', 'get_module_config', 'get_push_list',
|
||
'get_push_logs')
|
||
result = publicObject(toObject, defs, None, pdata)
|
||
return result
|
||
|
||
|
||
# 2024/1/24 上午 11:42 新场景模型的路由
|
||
@app.route(route_v2 + '/mod/<name>/<sub_name>/<fun>', methods=method_all)
|
||
@app.route(route_v2 + '/mod/<name>/<sub_name>/<fun>/<path:stype>', methods=method_all)
|
||
def panel_mod_v2(name=None, sub_name=None, fun=None, stype=None):
|
||
"""
|
||
@name 新场景模型的路由
|
||
@param "data":{"参数名":""} <数据类型> 参数描述
|
||
@return dict{"status":True/False,"msg":"提示信息"}
|
||
|
||
PS: mod/project/... 动态调用
|
||
name为场景名称,如push、proxy、docker, java, php等
|
||
sub_name为该场景下子模块名称,如push下的 msgconfMod、taskMod等, 不带Mod
|
||
fun为调用 main类的方法
|
||
stype为响应类型,json或html,默认json
|
||
"""
|
||
# 取消 /mod/ 路由下的强制绑定账号限制 2025/06/19
|
||
# if not public.is_bind():
|
||
# return redirect('/bind', 302)
|
||
if public.is_error_path():
|
||
return redirect('/error', 302)
|
||
|
||
if not name: return abort(404)
|
||
if not sub_name: return abort(404)
|
||
if sub_name.endswith('Mod'): sub_name = sub_name[:-3]
|
||
if not re.match(r"^[\w\-]+$", name): return abort(404)
|
||
if not re.match(r"^[\w\-]+$", sub_name): return abort(404)
|
||
if fun and not re.match(r"^[\w\-.]+$", fun): return abort(404)
|
||
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
if not stype:
|
||
tmp = fun.split('.')
|
||
fun = tmp[0]
|
||
if len(tmp) == 1: tmp.append('')
|
||
stype = tmp[1]
|
||
if fun:
|
||
if public.get_csrf_cookie_token_key() in session and 'login' in session:
|
||
if not check_csrf():
|
||
return public.ReturnJson(False, 'INIT_CSRF_ERR'), json_header
|
||
|
||
args = get_mod_input()
|
||
|
||
if not fun: fun = 'index.html'
|
||
if not stype:
|
||
tmp = fun.split('.')
|
||
fun = tmp[0]
|
||
if len(tmp) == 1: tmp.append('')
|
||
stype = tmp[1]
|
||
|
||
if not name: name = 'coll'
|
||
if not public.path_safe_check("%s/%s/%s/%s" % (name, sub_name, fun, stype)):
|
||
return abort(404)
|
||
if name.find('./') != -1 or not re.match(r"^[\w-]+$", name):
|
||
return abort(404)
|
||
if sub_name.find('./') != -1 or not re.match(r"^[\w-]+$", sub_name):
|
||
return abort(404)
|
||
if not name:
|
||
return public.returnJson(False, 'PLUGIN_INPUT_ERR'), json_header
|
||
|
||
args.client_ip = public.GetClientIp()
|
||
|
||
# 初始化新场景模型对象
|
||
try:
|
||
from mod.modController import Controller
|
||
controller_obj = Controller()
|
||
defs = ('model',)
|
||
args.model_index = "mod"
|
||
args.action = 'model'
|
||
args.mod_name = name
|
||
args.sub_mod_name = sub_name
|
||
args.def_name = fun
|
||
data = publicObject(controller_obj, defs, None, args)
|
||
r_type = type(data)
|
||
if r_type in [Response, Resp]:
|
||
return data
|
||
|
||
p_path = public.get_mod_path() + '/' + name
|
||
# 处理响应
|
||
if stype == 'json': # 响应JSON
|
||
return public.getJson(data), json_header
|
||
elif stype == 'html': # 使用模板
|
||
t_path_root = p_path + '/templates/'
|
||
t_path = t_path_root + fun + '.html'
|
||
if not os.path.exists(t_path):
|
||
return public.returnJson(False,
|
||
'PLUGIN_NOT_TEMPLATE'), json_header
|
||
t_body = public.readFile(t_path)
|
||
# 处理模板包含
|
||
rep = r'{%\s?include\s"(.+)"\s?%}'
|
||
includes = re.findall(rep, t_body)
|
||
for i_file in includes:
|
||
filename = p_path + '/templates/' + i_file
|
||
i_body = 'ERROR: File ' + filename + ' does not exists.'
|
||
if os.path.exists(filename):
|
||
i_body = public.readFile(filename)
|
||
t_body = re.sub(rep.replace('(.+)', i_file), i_body, t_body)
|
||
return render_template_string(t_body, data=data)
|
||
else: # 直接响应插件返回值,可以是任意flask支持的响应类型
|
||
r_type = type(data)
|
||
if r_type == dict:
|
||
if name == 'btwaf' and 'msg' in data:
|
||
return render_template('error3.html',
|
||
data={"error_msg": data['msg']})
|
||
return public.returnJson(
|
||
False,
|
||
public.getMsg('Bad return type [{}]').format(r_type)), json_header
|
||
return data
|
||
except:
|
||
if not 'login' in session: return abort(404)
|
||
return public.get_error_object(None, plugin_name=name)
|
||
|
||
@app.route(route_v2 + '/check_auth', methods=method_all)
|
||
def check_auth_v2(pdata=None):
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
if os.path.exists('data/.is_pro.pl'):
|
||
return public.return_message(0,0,'true')
|
||
return public.return_message(-1,0,'false')
|
||
|
||
@app.route('/bind', methods=method_get)
|
||
def bind():
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
if public.is_bind(): return redirect('/', 302)
|
||
data = {}
|
||
data['lan'] = public.GetLan('index_new')
|
||
# g.title = '请先绑定YakPanel帐号'
|
||
return render_template('index_new.html', data=data)
|
||
|
||
@app.route(route_v2 + '/breaking_through', methods=method_all)
|
||
def breaking_through_v2(pdata=None):
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import breaking_through
|
||
breakingObject = breaking_through.main()
|
||
get = get_input()
|
||
defs = (
|
||
'set_config',
|
||
'get_config',
|
||
'get_history_record',
|
||
'set_history_record_limit',
|
||
'clear_history_record_limit',
|
||
'get_black_white',
|
||
'add_black_white',
|
||
'modify_black_white',
|
||
'del_balck_white',
|
||
'check_local_ip_white',
|
||
'panel_ip_white',
|
||
'get_protected_services',
|
||
'get_linux_users',
|
||
'get_compiler_info',
|
||
'add_user_to_compiler',
|
||
'del_user_to_compiler',
|
||
'set_compiler_status',
|
||
)
|
||
return publicObject(breakingObject, defs, None, pdata)
|
||
|
||
@app.route(route_v2 + '/virtual/<def_name>', methods=method_all)
|
||
@app.route(route_v2 + '/yakpanelsub/<def_name>', methods=method_all)
|
||
@app.route('/yakpanelsub/<def_name>', methods=method_all)
|
||
def virtualModel_v2(def_name):
|
||
if request.method in ['GET'] and request.path.startswith('/yakpanelsub'):
|
||
return index_new(request.path)
|
||
path_split = request.path.split("/")
|
||
if len(path_split) < 3: return
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import public.PluginLoader as plugin_loader
|
||
mod_file = '{}/class_v2/virtualModelV2/virtualModel.py'.format(public.get_panel_path())
|
||
plugin_class = plugin_loader.get_module(mod_file)
|
||
plugin_object = getattr(plugin_class,"main")()
|
||
get= get_input()
|
||
if def_name.endswith('.json'):
|
||
def_name = def_name[:-5]
|
||
result = getattr(plugin_object,def_name)(get)
|
||
return result
|
||
|
||
|
||
@app.route(route_v2 + '/campaign/<def_name>', methods=method_all)
|
||
def campaign_v2(def_name: str):
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
from power_mta import actions
|
||
return getattr(actions, def_name)(get_input())
|
||
|
||
|
||
@app.route(route_v2 + '/userRegister', methods=method_all)
|
||
def userRegister_v2():
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
import userRegister_v2
|
||
reg = userRegister_v2.userRegister()
|
||
defs = ('toRegister',)
|
||
|
||
return publicObject(reg, defs, None, None)
|
||
# ===========================================================v2路由区end===========================================================#
|
||
|
||
@app.route('/v2/install_finish', methods=method_post)
|
||
def install_finish():
|
||
with open('{}/data/install_finished.mark'.format(public.get_panel_path()), 'w') as fp:
|
||
fp.write('True')
|
||
return public.return_message(0, 0, 'Successfully')
|
||
|
||
|
||
@app.route('/v2/wp/login/<int:site_id>', methods=method_get)
|
||
@app.route('/v2/wp/login/<int:site_id>/<wp_site_type>', methods=method_get)
|
||
def wp_login(site_id: int, wp_site_type: str = 'local'):
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
|
||
if site_id < 1:
|
||
return public.gettext_msg('Invalid site_id')
|
||
|
||
# wp_site_type
|
||
# 1 Local
|
||
# 2 Remote
|
||
if wp_site_type.lower() == 'local':
|
||
from wp_toolkit import wpmgr
|
||
return wpmgr(site_id).auto_login()
|
||
elif wp_site_type.lower() == 'remote':
|
||
from wp_toolkit import wpmgr_remote
|
||
return wpmgr_remote(site_id).auto_login()
|
||
else:
|
||
return public.gettext_msg('Invalid site_type')
|
||
|
||
|
||
@app.route('/v2/pmta/<enc_str>', methods=method_get)
|
||
def mail_campaign_handler(enc_str: str):
|
||
g.api_request = True
|
||
try:
|
||
from power_mta.maillog_stat import campaign_event_handler
|
||
return campaign_event_handler(enc_str)
|
||
except:
|
||
public.print_error()
|
||
return public.lang('Server has been crashed')
|
||
|
||
|
||
# 获取新场景模型的传参数据
|
||
def get_mod_input():
|
||
'''
|
||
@name # 获取新场景模型的传参数据
|
||
@author wzz <2024/1/24 上午 11:42>
|
||
@param "data":{"参数名":""} <数据类型> 参数描述
|
||
@return dict{"status":True/False,"msg":"提示信息"}
|
||
'''
|
||
data = public.dict_obj()
|
||
exludes = ['blob']
|
||
for key in request.args.keys():
|
||
data.set(key, str(request.args.get(key, '')))
|
||
|
||
if request.is_json:
|
||
for key in request.get_json().keys():
|
||
data.set(key, str(request.get_json()[key]))
|
||
else:
|
||
try:
|
||
for key in request.form.keys():
|
||
if key in exludes: continue
|
||
data.set(key, str(request.form.get(key, '')))
|
||
except:
|
||
try:
|
||
post = request.form.to_dict()
|
||
for key in post.keys():
|
||
if key in exludes: continue
|
||
data.set(key, str(post[key]))
|
||
except:
|
||
pass
|
||
|
||
if 'form_data' in g:
|
||
for k in g.form_data.keys():
|
||
data.set(k, str(g.form_data[k]))
|
||
|
||
if not hasattr(data, 'data'): data.data = []
|
||
return data
|
||
|
||
|
||
# ----------------------------------------- 无登录校验路由 start----------------------------------------
|
||
|
||
# 邮局调用退订接口
|
||
@app.route('/mailUnsubscribe', methods=method_all)
|
||
def mailUnsubscribe():
|
||
# 插件判断
|
||
if not os.path.exists('/www/server/panel/plugin/mail_sys/mail_send_bulk.py') or not os.path.exists('/www/vmail/postfixadmin.db'):
|
||
return abort(404)
|
||
|
||
g.is_aes = False
|
||
import mailUnsubscribe
|
||
reg = mailUnsubscribe.mailUnsubscribe()
|
||
defs = ('Unsubscribe', 'get_mail_type_list')
|
||
return publicObject(reg, defs, None, None)
|
||
|
||
|
||
# 新增登录设置语言
|
||
@app.route('/userLang', methods=method_all)
|
||
def userLang():
|
||
if public.cache_get(
|
||
public.Md5(
|
||
uuid.UUID(int=uuid.getnode()).hex[-12:] +
|
||
public.GetClientIp())) != 'check':
|
||
|
||
return abort(404)
|
||
|
||
global admin_check_auth, admin_path, route_path, admin_path_file
|
||
if admin_path != '/bt' and os.path.exists(
|
||
admin_path_file) and not 'admin_auth' in session:
|
||
return abort(404)
|
||
|
||
g.is_aes = False
|
||
import userLang
|
||
reg = userLang.userLang()
|
||
defs = ('get_language', 'set_language')
|
||
return publicObject(reg, defs, None, None)
|
||
|
||
|
||
# =========================================================== Google OAuth2.0 start ===========================================================#
|
||
|
||
@app.route('/google/redirect', methods=method_get)
|
||
def google_redirect():
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
|
||
nonce = public.md5(str(time.time()) + public.GetClientIp())
|
||
session['google_nonce'] = nonce
|
||
|
||
redirect_url = public.httpPost('{}/google/redirect'.format(public.OfficialApiBase()), headers={
|
||
'X-Forwarded-For': public.GetClientIp(),
|
||
}, data={
|
||
'redirect_url': 'https://{}{}{}'.format(public.GetHost(), ':{}'.format(str(public.ReadFile('data/port.pl')).strip()) if os.path.exists('data/port.pl') else '', '/google/callback'),
|
||
'nonce': nonce,
|
||
'from_panel': 1,
|
||
})
|
||
|
||
return redirect(redirect_url)
|
||
|
||
|
||
@app.route('/google/callback', methods=method_get)
|
||
def google_callback():
|
||
comReturn = comm.local()
|
||
if comReturn: return comReturn
|
||
|
||
get = get_input()
|
||
|
||
# validate nonce
|
||
if 'google_nonce' not in session or not session['google_nonce'] or 'nonce' not in get or not get.nonce or session['google_nonce'] != get.nonce:
|
||
return abort(403)
|
||
|
||
# remove nonce
|
||
session['google_nonce'] = None
|
||
session.pop('google_nonce', None)
|
||
|
||
bind = 'data/bind.pl'
|
||
if os.path.exists(bind): os.remove(bind)
|
||
userinfo = json.loads(public.base64url_decode(get.user_data))
|
||
userinfo['token'] = get.token
|
||
# 用户信息写入文件
|
||
public.writeFile('data/userInfo.json', json.dumps(userinfo))
|
||
session['focre_cloud'] = True
|
||
return redirect('/')
|
||
|
||
# ========================================================== Google OAuth2.0 end ===========================================================#
|
||
|
||
|
||
# ----------------------------------------- 无登录校验路由 end----------------------------------------
|
||
|
||
|
||
# 初始化CDN的配置
|
||
def init_cdn_config(app):
|
||
config_path = '/www/server/panel/config/cdn.conf'
|
||
config_dir = os.path.dirname(config_path)
|
||
# 确保配置目录存在
|
||
if not os.path.exists(config_dir):
|
||
app.config['CDN_PROXY'] = False
|
||
return
|
||
if not os.path.exists(config_path):
|
||
app.config['CDN_PROXY'] = False
|
||
with open(config_path, 'w') as f:
|
||
f.write('CDN_PROXY=False')
|
||
return
|
||
with open(config_path, 'r') as f:
|
||
content = f.read().strip()
|
||
if content == 'CDN_PROXY=True':
|
||
app.config['CDN_PROXY'] = True
|
||
else:
|
||
app.config['CDN_PROXY'] = False
|
||
return
|
||
|
||
init_cdn_config(app)
|