390 lines
15 KiB
Python
390 lines
15 KiB
Python
|
|
# coding: utf-8
|
||
|
|
# -------------------------------------------------------------------
|
||
|
|
# YakPanel
|
||
|
|
# -------------------------------------------------------------------
|
||
|
|
# Copyright (c) 2014-2099 YakPanel(www.yakpanel.com) All rights reserved.
|
||
|
|
# -------------------------------------------------------------------
|
||
|
|
# Author: yakpanel
|
||
|
|
# -------------------------------------------------------------------
|
||
|
|
|
||
|
|
# ------------------------------
|
||
|
|
# overview app
|
||
|
|
# ------------------------------
|
||
|
|
import json
|
||
|
|
import os
|
||
|
|
from typing import Callable
|
||
|
|
|
||
|
|
import public
|
||
|
|
from overviewV2.base import OverViewBase, DISPLAY
|
||
|
|
from public.exceptions import HintException
|
||
|
|
from public.validate import Param
|
||
|
|
|
||
|
|
public.sys_path_append("class_v2/")
|
||
|
|
|
||
|
|
PANEL_PATH = public.get_panel_path()
|
||
|
|
|
||
|
|
|
||
|
|
class OverViewApi(OverViewBase):
|
||
|
|
PLUGIN_DIR = os.path.join(PANEL_PATH, "plugin")
|
||
|
|
TEMPLATE = os.path.join(PANEL_PATH, "config/overview_template.json")
|
||
|
|
SETTING = os.path.join(PANEL_PATH, "config/overview_setting.json")
|
||
|
|
|
||
|
|
def __init__(self):
|
||
|
|
overview_setting = []
|
||
|
|
if not os.path.isfile(self.SETTING):
|
||
|
|
try:
|
||
|
|
temp_data = json.loads(public.readFile(self.TEMPLATE))
|
||
|
|
except Exception as e:
|
||
|
|
public.print_log(f"error read overview template: {e}")
|
||
|
|
temp_data = []
|
||
|
|
id = 0
|
||
|
|
for temp in temp_data:
|
||
|
|
for option in temp.get("option", []):
|
||
|
|
if option.get("name") in [
|
||
|
|
"sites", "ftps", "databases", "security",
|
||
|
|
]:
|
||
|
|
option["id"] = id
|
||
|
|
option["template"] = temp["template"]
|
||
|
|
option["params"] = [
|
||
|
|
p["option"][0] for p in option.get("params", []) if p.get("option")
|
||
|
|
]
|
||
|
|
id += 1
|
||
|
|
overview_setting.append(option)
|
||
|
|
public.writeFile(self.SETTING, json.dumps(overview_setting))
|
||
|
|
|
||
|
|
def _check_plugin_install(self, overview: dict):
|
||
|
|
if overview.get("type") == "plugin":
|
||
|
|
if not overview.get("name"):
|
||
|
|
pass
|
||
|
|
if not os.path.exists(os.path.join(self.PLUGIN_DIR, overview["name"])):
|
||
|
|
raise HintException(f"[{overview.get('name')}] Plugin Not Installed!")
|
||
|
|
|
||
|
|
def _dynamic_info(self, overview: dict, replace_title: bool = True) -> dict:
|
||
|
|
# icon desc
|
||
|
|
if DISPLAY.get(overview["name"]):
|
||
|
|
overview["icon"] = DISPLAY[overview["name"]]["icon"]
|
||
|
|
overview["desc"] = DISPLAY[overview["name"]]["desc"]
|
||
|
|
# route
|
||
|
|
router_inplace = ["sites", "databases"]
|
||
|
|
try:
|
||
|
|
if overview.get("name") in router_inplace and len(overview.get("params", [])) == 1:
|
||
|
|
point = overview["params"][0].get("source")
|
||
|
|
if not point:
|
||
|
|
return overview
|
||
|
|
if point == "mongodb":
|
||
|
|
point = "mongo"
|
||
|
|
|
||
|
|
if point == "all":
|
||
|
|
if overview["name"] == "sites":
|
||
|
|
point = "php"
|
||
|
|
elif overview["name"] == "databases":
|
||
|
|
point = "mysql"
|
||
|
|
|
||
|
|
overview["source"]["href"] = f'{overview["source"]["href"]}/{point}'
|
||
|
|
except Exception:
|
||
|
|
import traceback
|
||
|
|
public.print_log(traceback.format_exc())
|
||
|
|
|
||
|
|
# replace Title
|
||
|
|
try:
|
||
|
|
if replace_title and overview.get("name") == "monitor":
|
||
|
|
params = overview.get("params", [])
|
||
|
|
act = params[-1].get("name", "monitor")
|
||
|
|
overview["title"] = f"Monitor - {act}"
|
||
|
|
|
||
|
|
if replace_title and overview.get("name") == "sites":
|
||
|
|
params = overview.get("params", [])
|
||
|
|
act = params[-1].get("name", "php")
|
||
|
|
overview["title"] = f"Site - {act}"
|
||
|
|
except Exception:
|
||
|
|
import traceback
|
||
|
|
public.print_log(traceback.format_exc())
|
||
|
|
|
||
|
|
return overview
|
||
|
|
|
||
|
|
def overview_template(self, get):
|
||
|
|
"""
|
||
|
|
@name 获取概览模板
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
temp_data = json.loads(public.readFile(self.TEMPLATE))
|
||
|
|
except Exception as e:
|
||
|
|
raise HintException(e)
|
||
|
|
|
||
|
|
select_option_dict = {
|
||
|
|
"site_all": public.M("sites").field("name").select()
|
||
|
|
}
|
||
|
|
for template in temp_data:
|
||
|
|
template_option = template.get("option", [])
|
||
|
|
for i in range(len(template_option) - 1, -1, -1):
|
||
|
|
option = template_option[i]
|
||
|
|
# display
|
||
|
|
option = self._dynamic_info(option, replace_title=False)
|
||
|
|
|
||
|
|
if not option.get("status", False):
|
||
|
|
if option.get("type") == "plugin": # 插件
|
||
|
|
plugin_path = os.path.join(self.PLUGIN_DIR, option["name"])
|
||
|
|
option["status"] = os.path.exists(plugin_path)
|
||
|
|
|
||
|
|
# 填充参数选项数据
|
||
|
|
option_params = option.get("params", [])
|
||
|
|
for params in option_params:
|
||
|
|
# select_option 监控报表, 提供选择站点
|
||
|
|
select_option = params.get("select_option")
|
||
|
|
if select_option is not None and select_option_dict.get(select_option) is not None:
|
||
|
|
params["option"] = select_option_dict.get(select_option)
|
||
|
|
for site in params["option"]:
|
||
|
|
site["source"] = site["name"]
|
||
|
|
|
||
|
|
return public.success_v2(temp_data)
|
||
|
|
|
||
|
|
def get_overview(self, get):
|
||
|
|
"""
|
||
|
|
@name 获取概览
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
overview_setting = public.readFile(self.SETTING) or "[]"
|
||
|
|
overview_setting = json.loads(overview_setting)
|
||
|
|
if not isinstance(overview_setting, list):
|
||
|
|
raise Exception("overview_setting format error")
|
||
|
|
if len(overview_setting) == 0:
|
||
|
|
raise Exception("overview_setting is empty")
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("get_overview error info: {}".format(ex))
|
||
|
|
public.ExecShell("rm -f {}".format(self.SETTING))
|
||
|
|
overview_setting = []
|
||
|
|
|
||
|
|
func_dict = {
|
||
|
|
"sites": self._base,
|
||
|
|
"ftps": self._base,
|
||
|
|
"databases": self._base,
|
||
|
|
"security": self._security,
|
||
|
|
"ssh_log": self._ssh_log,
|
||
|
|
"ssl": self._ssl,
|
||
|
|
"corn": self._corn,
|
||
|
|
"btwaf": self._btwaf,
|
||
|
|
"monitor": self._monitor,
|
||
|
|
"tamper_core": self._tamper_core,
|
||
|
|
"alarm_logs": self._alarm_logs,
|
||
|
|
}
|
||
|
|
|
||
|
|
nlist = []
|
||
|
|
for overview in overview_setting:
|
||
|
|
overview["value"] = []
|
||
|
|
# display
|
||
|
|
overview = self._dynamic_info(overview)
|
||
|
|
params_list = overview.get("params", [])
|
||
|
|
|
||
|
|
if overview.get("status", False):
|
||
|
|
func: Callable[[str, list], list] = func_dict.get(overview["name"])
|
||
|
|
if func is not None:
|
||
|
|
overview["value"] = func(overview["name"], params_list)
|
||
|
|
|
||
|
|
nlist.append(overview)
|
||
|
|
return public.success_v2(overview_setting)
|
||
|
|
|
||
|
|
def get_overview_window(self, get):
|
||
|
|
"""
|
||
|
|
params = {
|
||
|
|
"model": "ssl",
|
||
|
|
"source": "all"
|
||
|
|
}
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("model").String().Require(),
|
||
|
|
Param("source").String().Require(),
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter(),
|
||
|
|
])
|
||
|
|
|
||
|
|
if get.model == "ssl" and get.source not in [
|
||
|
|
"all", "normal", "renew_fail", "expirin_soon"
|
||
|
|
]:
|
||
|
|
return public.fail_v2(
|
||
|
|
"[ssl] source value mustbe one of 'all', 'normal', 'renew_fail', 'expirin_soon'"
|
||
|
|
)
|
||
|
|
|
||
|
|
if get.model == "alarm_log" and get.source not in [
|
||
|
|
"all", "handled", "unhandled"
|
||
|
|
]:
|
||
|
|
return public.fail_v2(
|
||
|
|
"[alarm_log] source value mustbe one of 'all', 'handled', 'unhandled'"
|
||
|
|
)
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("get_overview_window error info: {}".format(ex))
|
||
|
|
return public.fail_v2(str(ex))
|
||
|
|
|
||
|
|
data = []
|
||
|
|
if get.model == "ssl":
|
||
|
|
if get.source != "all":
|
||
|
|
data = self._base_ssl(get.source)
|
||
|
|
else:
|
||
|
|
data = []
|
||
|
|
for tag in ["expirin_soon", "renew_fail", "normal"]:
|
||
|
|
data.extend(self._base_ssl(tag))
|
||
|
|
sorted(data, key=lambda x: x.get("id", 0))
|
||
|
|
return public.success_v2(data)
|
||
|
|
|
||
|
|
def add_overview(self, get):
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("overview").String().Require(),
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter(),
|
||
|
|
])
|
||
|
|
overview = json.loads(get.overview)
|
||
|
|
overview_setting = public.readFile(self.SETTING) or "[]"
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("add_overview error info: {}".format(ex))
|
||
|
|
return public.fail_v2(str(ex))
|
||
|
|
self._check_plugin_install(overview)
|
||
|
|
try:
|
||
|
|
overview_setting = json.loads(overview_setting)
|
||
|
|
except json.JSONDecodeError:
|
||
|
|
overview_setting = []
|
||
|
|
|
||
|
|
if len(overview_setting) >= 6:
|
||
|
|
return public.fail_v2("Overview Full, Max 6 Items!")
|
||
|
|
|
||
|
|
if overview.get("value") is not None:
|
||
|
|
del overview["value"]
|
||
|
|
|
||
|
|
max_id = 0
|
||
|
|
for over in overview_setting:
|
||
|
|
if over["name"] == overview["name"]:
|
||
|
|
return public.success_v2(public.lang("Overview Exist!"))
|
||
|
|
|
||
|
|
if int(over["id"]) > max_id:
|
||
|
|
max_id = int(over["id"])
|
||
|
|
|
||
|
|
overview["id"] = max_id + 1
|
||
|
|
overview_setting.append(overview)
|
||
|
|
|
||
|
|
public.writeFile(self.SETTING, json.dumps(overview_setting))
|
||
|
|
return public.success_v2(public.lang("Overview Add Successfully!"))
|
||
|
|
|
||
|
|
def set_overview(self, get):
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("overview").String().Require(),
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter(),
|
||
|
|
])
|
||
|
|
overview = json.loads(get.overview)
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("set_overview error info: {}".format(ex))
|
||
|
|
return public.fail_v2(str(ex))
|
||
|
|
self._check_plugin_install(overview)
|
||
|
|
overview_setting = []
|
||
|
|
if isinstance(overview, list):
|
||
|
|
overview_setting = overview
|
||
|
|
elif isinstance(overview, dict):
|
||
|
|
try:
|
||
|
|
overview_setting = public.readFile(self.SETTING)
|
||
|
|
overview_setting = json.loads(overview_setting)
|
||
|
|
except Exception as err:
|
||
|
|
public.print_log("set_overview readFile error info: {}".format(err))
|
||
|
|
overview_setting = []
|
||
|
|
|
||
|
|
if overview.get("value") is not None:
|
||
|
|
del overview["value"]
|
||
|
|
|
||
|
|
for idx in range(len(overview_setting)):
|
||
|
|
over = overview_setting[idx]
|
||
|
|
if int(over["id"]) == int(overview["id"]):
|
||
|
|
overview_setting[idx] = overview
|
||
|
|
break
|
||
|
|
else:
|
||
|
|
return public.fail_v2("Overview Not Found!")
|
||
|
|
|
||
|
|
public.writeFile(self.SETTING, json.dumps(overview_setting))
|
||
|
|
return public.success_v2(public.lang("Overview Set Successfully!"))
|
||
|
|
|
||
|
|
def del_overview(self, get):
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("overview_id").Integer().Require(),
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter(),
|
||
|
|
])
|
||
|
|
overview_setting = public.readFile(self.SETTING)
|
||
|
|
overview_setting = json.loads(overview_setting)
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("del_overview error info: {}".format(ex))
|
||
|
|
return public.fail_v2(str(ex))
|
||
|
|
|
||
|
|
if len(overview_setting) <= 1:
|
||
|
|
return public.fail_v2("Overview At Least One Item!")
|
||
|
|
|
||
|
|
overview_id = int(get.overview_id)
|
||
|
|
for idx in range(len(overview_setting) - 1, -1, -1):
|
||
|
|
over = overview_setting[idx]
|
||
|
|
if int(over["id"]) == overview_id:
|
||
|
|
del overview_setting[idx]
|
||
|
|
break
|
||
|
|
|
||
|
|
public.writeFile(self.SETTING, json.dumps(overview_setting))
|
||
|
|
return public.success_v2(public.lang("Overview Delete Successfully!"))
|
||
|
|
|
||
|
|
def sort_overview(self, get):
|
||
|
|
try:
|
||
|
|
get.validate([
|
||
|
|
Param("sort_ids").String().Require(),
|
||
|
|
], [
|
||
|
|
public.validate.trim_filter(),
|
||
|
|
])
|
||
|
|
sort_ids = json.loads(get.sort_ids)
|
||
|
|
overview_setting = public.readFile(self.SETTING)
|
||
|
|
overview_setting = json.loads(overview_setting)
|
||
|
|
except Exception as ex:
|
||
|
|
public.print_log("sort_overview error info: {}".format(ex))
|
||
|
|
return public.fail_v2(str(ex))
|
||
|
|
|
||
|
|
new_overview_setting = []
|
||
|
|
for pk in sort_ids:
|
||
|
|
for over in overview_setting:
|
||
|
|
if int(over["id"]) == int(pk):
|
||
|
|
new_overview_setting.append(over)
|
||
|
|
break
|
||
|
|
|
||
|
|
public.writeFile(self.SETTING, json.dumps(new_overview_setting))
|
||
|
|
return public.success_v2(public.lang("Successfully!"))
|
||
|
|
|
||
|
|
|
||
|
|
def update_overview():
|
||
|
|
# 同步模板中的更新, 同时移除不存在的项
|
||
|
|
try:
|
||
|
|
api = OverViewApi()
|
||
|
|
template_data = json.loads(public.readFile(api.TEMPLATE))
|
||
|
|
settings = json.loads(public.readFile(api.SETTING))
|
||
|
|
new_settings = []
|
||
|
|
for setting in settings:
|
||
|
|
for template in template_data:
|
||
|
|
for op in template.get("option", []):
|
||
|
|
if setting["name"] == op["name"]:
|
||
|
|
try:
|
||
|
|
new_settings.append(
|
||
|
|
{
|
||
|
|
"title": op["title"],
|
||
|
|
"name": setting["name"],
|
||
|
|
"status": setting.get("status", False),
|
||
|
|
"type": op["type"],
|
||
|
|
"source": op.get("source", {}),
|
||
|
|
"params": setting["params"],
|
||
|
|
"icon": op.get("icon", ""),
|
||
|
|
"desc": op.get("desc", ""),
|
||
|
|
"id": setting["id"],
|
||
|
|
"template": op.get("template", ""),
|
||
|
|
}
|
||
|
|
)
|
||
|
|
except Exception as e:
|
||
|
|
public.print_log("update_overview item error:".format(str(e)))
|
||
|
|
continue
|
||
|
|
public.writeFile(OverViewApi.SETTING, json.dumps(new_settings))
|
||
|
|
except:
|
||
|
|
pass
|
||
|
|
|
||
|
|
|
||
|
|
update_overview()
|