diff --git a/.gitignore b/.gitignore index f04c230a6..63aba0e20 100644 --- a/.gitignore +++ b/.gitignore @@ -185,3 +185,4 @@ plugins/goedge-node plugins/goedge-happy plugins/dztasks +data/default_new.pl diff --git a/class/core/config_api.py b/class/core/config_api.py index bec9e2e46..2d2bda247 100755 --- a/class/core/config_api.py +++ b/class/core/config_api.py @@ -42,7 +42,7 @@ class config_api: 'bind_domain' : 'data/bind_domain.pl', # 面板域名绑定 'unauth_status' : 'data/unauthorized_status.pl', # URL路径未成功显示状态 'auth_secret': 'data/auth_secret.pl', # 二次验证密钥 - 'ssl':'ssl/choose.pl', # ssl设置 + 'ssl':'ssl/choose.pl', # ssl设置 'hook_database' : 'data/hook_database.json', # 数据库钩子 'hook_menu' : 'data/hook_menu.json', # 菜单钩子 'hook_global_static' : 'data/hook_global_static.json', # 静态文件钩子 diff --git a/class/core/system_api.py b/class/core/system_api.py index 391c3588c..0d7cf4537 100755 --- a/class/core/system_api.py +++ b/class/core/system_api.py @@ -717,7 +717,6 @@ class system_api: self.cache[iokey] = {'info':diskio_group,'time':mtime} except Exception as e: - # print(mw.getTracebackInfo()) pass return diskInfo diff --git a/plugins/gitea/index.py b/plugins/gitea/index.py index cffc4908b..8aff8694e 100755 --- a/plugins/gitea/index.py +++ b/plugins/gitea/index.py @@ -220,7 +220,7 @@ def getDbConfValue(): rep_scope = r"\[database\](.*?)\[" tmp = re.findall(rep_scope, content, re.S) - rep = '(\w*)\s*=\s*(.*)' + rep = '(\\w*)\\s*=\\s*(.*)' tmp = re.findall(rep, tmp[0]) r = {} for x in range(len(tmp)): diff --git a/web/admin/__init__.py b/web/admin/__init__.py index f41ca350e..12b07fd22 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -8,7 +8,11 @@ # Author: midoks # --------------------------------------------------------------------------------- +import os import sys +import uuid +import logging +from datetime import timedelta from flask import Flask from flask_socketio import SocketIO, emit, send @@ -20,6 +24,7 @@ from flask_migrate import Migrate from werkzeug.local import LocalProxy from admin.model import db as sys_db +from admin import setup import core.mw as mw import setting @@ -33,25 +38,46 @@ socketio = SocketIO(manage_session=False, async_mode='threading', app = Flask(__name__, template_folder='templates/default') +# app.debug = True # 静态文件配置 from whitenoise import WhiteNoise app.wsgi_app = WhiteNoise(app.wsgi_app, root="../web/static/", prefix="static/", max_age=604800) +# session配置 +app.secret_key = uuid.UUID(int=uuid.getnode()).hex[-12:] +# app.config['sessions'] = dict() +app.config['SESSION_PERMANENT'] = True +app.config['SESSION_USE_SIGNER'] = True +app.config['SESSION_KEY_PREFIX'] = 'MW_:' +app.config['SESSION_COOKIE_NAME'] = "MW_VER_1" +app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=31) + # db的配置 -app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///'+root_dir+'/example.log' # 使用 SQLite 数据库 +app.config['SQLALCHEMY_DATABASE_URI'] = mw.getSqitePrefix()+setting.SQLITE_PATH # 使用 SQLite 数据库 app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True + # 初始化db sys_db.init_app(app) Migrate(app, sys_db) +# 检查数据库是否存在。如果没有就创建它。 +setup_db_required = False +if not os.path.isfile(setting.SQLITE_PATH): + setup_db_required = True + +# with app.app_context(): +# sys_db.create_all() + with app.app_context(): - sys_db.create_all() + if setup_db_required: + sys_db.create_all() +# 初始化用户信息 +with app.app_context(): + setup.init_admin_user() -# print(mw.getRunDir()) -# print(app.config['SQLALCHEMY_DATABASE_URI']) # 加载模块 @@ -61,6 +87,9 @@ for module in get_submodules(): if app.blueprints.get(module.name) is None: app.register_blueprint(module) +@app.before_request +def requestCheck(): + print("hh") @app.after_request def requestAfter(response): @@ -127,6 +156,8 @@ def inject_global_variables(): # return jsonify(result) + + # Log the startup app.logger.info('########################################################') app.logger.info('Starting %s v%s...', setting.APP_NAME, setting.APP_VERSION) diff --git a/web/admin/dashboard/__init__.py b/web/admin/dashboard/__init__.py index 5cb7172a5..8812c3527 100644 --- a/web/admin/dashboard/__init__.py +++ b/web/admin/dashboard/__init__.py @@ -10,13 +10,38 @@ from flask import Blueprint, render_template +from flask import request,g -blueprint = Blueprint('dashboard', __name__, url_prefix='/', template_folder='../../templates/default') +import core.mw as mw + +blueprint = Blueprint('dashboard', __name__, url_prefix='/', template_folder='../../templates') @blueprint.route('/') def index(): - return render_template('index.html', data = {}) + return render_template('default/index.html', data = {}) + + +# --------------------------------------------------------------------------------- +# 定义登录入口相关方法 +# --------------------------------------------------------------------------------- +@blueprint.route('/login') +def login(): + return render_template('default/login.html', data = {}) +# 检查是否登录 @blueprint.route('/check_login',methods=['GET','POST']) def check_login(): return "0" + +# 执行登录操作 +@blueprint.route('/do_login', endpoint='do_login', methods=['POST']) +def do_login(): + + username = request.form.get('username', '').strip() + password = request.form.get('password', '').strip() + code = request.form.get('code', '').strip() + + print(g) + print(username, password, code ) + + return mw.returnData(False, '面板已经关闭!') diff --git a/web/admin/logs/__init__.py b/web/admin/logs/__init__.py index 12ed5986c..4602416c4 100644 --- a/web/admin/logs/__init__.py +++ b/web/admin/logs/__init__.py @@ -11,7 +11,22 @@ from flask import Blueprint, render_template -blueprint = Blueprint('logs', __name__, url_prefix='/logs', template_folder='../../templates/default') +from admin.user_login_check import panel_login_required + +blueprint = Blueprint('logs', __name__, url_prefix='/logs', template_folder='../../templates') @blueprint.route('/index', endpoint='index') def index(): - return render_template('logs.html', data={}) \ No newline at end of file + return render_template('default/logs.html', data={}) + + +# 日志列表 +@blueprint.route('/get_log_list', endpoint='get_log_list', methods=['POST']) +@panel_login_required +def get_log_list(): + return [] + +# 审计日志列表 +@blueprint.route('/get_audit_logs_files', endpoint='get_audit_logs_files', methods=['POST']) +@panel_login_required +def get_audit_logs_files(): + return [] \ No newline at end of file diff --git a/web/admin/model/__init__.py b/web/admin/model/__init__.py index 324094677..f07394047 100644 --- a/web/admin/model/__init__.py +++ b/web/admin/model/__init__.py @@ -17,11 +17,11 @@ SCHEMA_VERSION = 1 db = SQLAlchemy( - engine_options={ - 'pool_size': setting.CONFIG_DATABASE_CONNECTION_POOL_SIZE, - 'max_overflow': setting.CONFIG_DATABASE_CONNECTION_MAX_OVERFLOW - } - ) + engine_options={ + 'pool_size': setting.CONFIG_DATABASE_CONNECTION_POOL_SIZE, + 'max_overflow': setting.CONFIG_DATABASE_CONNECTION_MAX_OVERFLOW + } +) class Version(db.Model): @@ -42,11 +42,12 @@ class Option(db.Model): __tablename__ = 'option' id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") name = db.Column(db.String(128), unique=True, nullable=False, comment="配置名称") + type = db.Column(db.String(50), unique=False, nullable=False, default='common',comment="计划类型:common/hook/web") value = db.Column(db.TEXT, unique=False, nullable=False, comment="内容") -class User(db.Model): +class Users(db.Model): """定义登录用户""" - __tablename__ = 'user' + __tablename__ = 'users' id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") name = db.Column(db.String(128), unique=True, nullable=False, comment="用户名") password = db.Column(db.String(128), unique=False, nullable=False, comment="密码") @@ -54,6 +55,8 @@ class User(db.Model): login_time = db.Column(db.String(128), unique=False, nullable=True, comment="登录时间") phone = db.Column(db.String(20), unique=False, nullable=False,comment="手机") email = db.Column(db.String(320), nullable=False, comment="邮件") + add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") + update_time = db.Column(db.TEXT, nullable=False, comment="更新时间") class Crontab(db.Model): """定义计划任务""" @@ -73,7 +76,7 @@ class Crontab(db.Model): save = db.Column(db.Integer, unique=False, nullable=True, default=3,comment="备份数量") status = db.Column(db.Integer, unique=False, nullable=True, default=1, comment="状态") add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") - update_time = db.Column(db.TEXT, nullable=False, comment="添加时间") + update_time = db.Column(db.TEXT, nullable=False, comment="更新时间") class Logs(db.Model): """定义日志""" @@ -88,8 +91,8 @@ class Firewall(db.Model): """定义防火墙""" __tablename__ = 'firewall' id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") - port = db.Column(db.Integer(), unique=True, nullable=False, comment="端口") - protocol = db.Column(db.Integer(), unique=True, nullable=False, comment="协议/tcp/udp") + port = db.Column(db.Integer(), unique=False, nullable=False, comment="端口") + protocol = db.Column(db.Integer(), unique=False, nullable=False, comment="协议/tcp/udp") ps = db.Column(db.TEXT, unique=False, nullable=False, comment="备注") add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") update_time = db.Column(db.TEXT, nullable=False, comment="更新时间") @@ -106,6 +109,84 @@ class Backup(db.Model): size = db.Column(db.Integer(), unique=False, nullable=False, comment="大小") add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") +class Sites(db.Model): + """定义计划任务""" + __tablename__ = 'sites' + id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") + name = db.Column(db.TEXT, unique=False, nullable=False, comment="网站名称") + path = db.Column(db.TEXT, unique=False, nullable=False, comment="网站路径") + index = db.Column(db.TEXT, unique=False, nullable=False, comment="") + ps = db.Column(db.TEXT, unique=False, nullable=False, comment="备注") + edate = db.Column(db.TEXT, unique=False, nullable=False, comment="到期时间") + type_id = db.Column(db.TEXT, unique=False, nullable=False, comment="分类ID") + status = db.Column(db.Integer(), unique=False, nullable=True, default=1, comment="状态") + add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") + update_time = db.Column(db.TEXT, nullable=False, comment="更新时间") + +class SiteType(db.Model): + """定义站点类型""" + __tablename__ = 'site_type' + id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") + name = db.Column(db.TEXT, unique=False, nullable=False, comment="类型名称") + add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") + update_time = db.Column(db.TEXT, nullable=False, comment="更新时间") + +class Domain(db.Model): + """定义站点域名""" + __tablename__ = 'domain' + id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") + pid = db.Column(db.Integer(), unique=False, nullable=False, comment="站点ID") + name = db.Column(db.TEXT, unique=False, nullable=False, comment="域名") + port = db.Column(db.Integer(), unique=False, nullable=False, comment="端口") + add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") + +class Binding(db.Model): + """定义站点绑定""" + __tablename__ = 'binding' + id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") + pid = db.Column(db.Integer(), unique=False, nullable=False, comment="站点ID") + domain = db.Column(db.TEXT, unique=False, nullable=False, comment="域名") + path = db.Column(db.TEXT, unique=False, nullable=False, comment="路径") + port = db.Column(db.Integer(), unique=False, nullable=False, comment="端口") + add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") + + +class Tasks(db.Model): + """定义任务""" + __tablename__ = 'tasks' + id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") + name = db.Column(db.TEXT, unique=False, nullable=False, comment="任务名称") + type = db.Column(db.TEXT, unique=False, nullable=False, comment="域名") + execstr = db.Column(db.TEXT, unique=False, nullable=False, comment="返回内容") + start = db.Column(db.Integer(), unique=False, nullable=True, comment="开始执行时间") + end = db.Column(db.Integer(), unique=False, nullable=True, comment="结束执行时间") + status = db.Column(db.Integer(), unique=False, nullable=True, default=1, comment="状态") + add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") + + +class TempLogin(db.Model): + """定义临时登录""" + __tablename__ = 'temp_login' + id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") + token = db.Column(db.TEXT, unique=False, nullable=False, comment="token") + salt = db.Column(db.TEXT, unique=False, nullable=False, comment="salt") + state = db.Column(db.Integer(), unique=False, nullable=False, comment="状态") + login_time = db.Column(db.Integer(), unique=False, nullable=True, comment="登录时间") + login_addr = db.Column(db.Integer(), unique=False, nullable=True, comment="登录地址") + logout_time = db.Column(db.Integer(), unique=False, nullable=True, comment="登出时间") + expire = db.Column(db.Integer(), unique=False, nullable=True, comment="过期时间") + add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") + +class Panel(db.Model): + """定义其他面板""" + __tablename__ = 'panel' + id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") + title = db.Column(db.TEXT, unique=False, nullable=False, comment="标题") + url = db.Column(db.TEXT, unique=False, nullable=False, comment="地址") + username = db.Column(db.Integer(), unique=False, nullable=False, comment="用户名") + password = db.Column(db.Integer(), unique=False, nullable=True, comment="密码") + click = db.Column(db.Integer(), unique=False, nullable=True, comment="点击次数") + add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") diff --git a/web/admin/plugins/__init__.py b/web/admin/plugins/__init__.py index b88bdfe55..f86489305 100644 --- a/web/admin/plugins/__init__.py +++ b/web/admin/plugins/__init__.py @@ -28,6 +28,7 @@ def index(): # 初始化检查,首页提示选择安装 @blueprint.route('/init', endpoint='init', methods=['POST']) +@panel_login_required def init(): plugin_names = { 'openresty': '1.25.3', @@ -38,6 +39,13 @@ def init(): } return [] +# 首页软件展示 +@blueprint.route('/index_list', endpoint='index_list', methods=['GET','POST']) +@panel_login_required +def index_list(): + pg = MwPlugin.instance() + return pg.getIndexList() + # 插件列表 @blueprint.route('/list', endpoint='list', methods=['GET']) @panel_login_required @@ -102,7 +110,6 @@ def run(): pg = MwPlugin.instance() data = pg.run(name, func, version, args, script) - if data[1] == '': r = mw.returnData(True, "OK", data[0].strip()) else: diff --git a/web/admin/setup/__init__.py b/web/admin/setup/__init__.py new file mode 100644 index 000000000..f5e9e23db --- /dev/null +++ b/web/admin/setup/__init__.py @@ -0,0 +1,11 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +from .user import * \ No newline at end of file diff --git a/web/admin/setup/user.py b/web/admin/setup/user.py new file mode 100644 index 000000000..d0dea27dc --- /dev/null +++ b/web/admin/setup/user.py @@ -0,0 +1,39 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +from flask import request + +from admin.model import db, Users + +import core.mw as mw + +# 初始化用户信息 +def init_admin_user(): + data = Users.query.filter_by(id=1).first() + if not data: + name = mw.getRandomString(8).lower() + password = mw.getRandomString(8).lower() + file_pass_pl = mw.getPanelDataDir() + '/default_new.pl' + mw.writeFile(file_pass_pl, password) + insert_time = mw.formatDate() + login_ip = '127.0.0.1' + add_user = Users( + name=name, + password=mw.md5(password), + login_ip=login_ip, + login_time=insert_time, + phone='', + email='', + add_time=insert_time, + update_time=insert_time) + db.session.add(add_user) + db.session.commit() + db.session.close() + return True \ No newline at end of file diff --git a/web/admin/submodules.py b/web/admin/submodules.py index a3bc1502f..29954e7f7 100644 --- a/web/admin/submodules.py +++ b/web/admin/submodules.py @@ -20,6 +20,7 @@ from .plugins import blueprint as PluginsModule from .crontab import blueprint as CrontabModule from .firewall import blueprint as FirewallModule from .control import blueprint as ControlModule +from .system import blueprint as SystemModule def get_submodules(): return [ @@ -33,5 +34,6 @@ def get_submodules(): CrontabModule, FirewallModule, ControlModule, + SystemModule, ConfigModule, ] diff --git a/web/admin/system/__init__.py b/web/admin/system/__init__.py new file mode 100644 index 000000000..a06677c7a --- /dev/null +++ b/web/admin/system/__init__.py @@ -0,0 +1,58 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + + +from flask import Blueprint, render_template +from flask import request + +import core.mw as mw +import utils.system as sys + +blueprint = Blueprint('system', __name__, url_prefix='/system', template_folder='../../templates') + +# 取系统的统计信息 +@blueprint.route('/system_total', endpoint='system_total') +def system_total(): + data = sys.getMemInfo() + cpu = sys.getCpuInfo(interval=1) + data['cpuNum'] = cpu[1] + data['cpuRealUsed'] = cpu[0] + data['time'] = sys.getBootTime() + data['system'] = sys.getSystemVersion() + data['version'] = '0.0.1' + return data + +# 取系统的网络流量信息 +@blueprint.route('/network', endpoint='network') +def network(): + stat = {} + stat['cpu'] = sys.getCpuInfo() + stat['load'] = sys.getLoadAverage() + stat['mem'] = sys.getMemInfo() + stat['iostat'] = sys.stats().disk() + stat['network'] = sys.stats().network() + return stat + +# 取系统的磁盘信息 +@blueprint.route('/disk_info', endpoint='disk_info') +def disk_info(): + data = sys.getDiskInfo() + return data + +# 升级检测 +@blueprint.route('/update_server', endpoint='update_server') +def update_server(): + panel_type = request.args.get('type', 'check') + version = request.args.get('version', '') + + return mw.returnData(False, '已经是最新,无需更新!') + + + diff --git a/web/admin/user_login_check.py b/web/admin/user_login_check.py index b8c2256db..742d0127c 100644 --- a/web/admin/user_login_check.py +++ b/web/admin/user_login_check.py @@ -11,10 +11,9 @@ from functools import wraps def panel_login_required(func): - - print('panel_login_required',func) - + @wraps(func) def wrapper(*args, **kwargs): + print('panel_login_required', args, kwargs) return func(*args, **kwargs) return wrapper \ No newline at end of file diff --git a/web/branding.py b/web/branding.py index 9979842d8..e6dd762b9 100644 --- a/web/branding.py +++ b/web/branding.py @@ -14,5 +14,8 @@ APP_NAME = 'mdserver-web' APP_ICON = 'icon' +APP_LOG_NAME = 'panel' +APP_SQLITE_NAME = 'panel' + # Copyright string for display in the app APP_COPYRIGHT = 'Copyright (C) 2018-∞ All rights reserved.' \ No newline at end of file diff --git a/web/core/mw.py b/web/core/mw.py index 41cebc27b..749c2e832 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -116,11 +116,11 @@ def getUniqueId(): return unique_id def returnData(status, msg, data=None): + if data == None: + return {'status': status, 'msg': msg} return {'status': status, 'msg': msg, 'data': data} def returnJson(status, msg, data=None): - if data == None: - return getJson({'status': status, 'msg': msg}) return getJson({'status': status, 'msg': msg, 'data': data}) def readFile(filename): @@ -163,6 +163,52 @@ def systemdCfgDir(): # local test return "/tmp" + +def formatDate(format="%Y-%m-%d %H:%M:%S", times=None): + # 格式化指定时间戳 + if not times: + times = int(time.time()) + time_local = time.localtime(times) + return time.strftime(format, time_local) + + +def strfToTime(sdate): + # 转换时间 + import time + return time.strftime('%Y-%m-%d', time.strptime(sdate, '%b %d %H:%M:%S %Y %Z')) + + +def md5(content): + # 生成MD5 + try: + m = hashlib.md5() + m.update(content.encode("utf-8")) + return m.hexdigest() + except Exception as ex: + return False + + +def getFileMd5(filename): + # 文件的MD5值 + if not os.path.isfile(filename): + return False + + myhash = hashlib.md5() + f = file(filename, 'rb') + while True: + b = f.read(8096) + if not b: + break + myhash.update(b) + f.close() + return myhash.hexdigest() + + +def getClientIp(): + from flask import request + return request.remote_addr.replace('::ffff:', '') + + def getJson(data): import json return json.dumps(data) @@ -240,6 +286,41 @@ def getSqitePrefix(): prefix = 'sqlite:////' return prefix +def getCpuType(): + cpuType = '' + if isAppleSystem(): + cmd = "system_profiler SPHardwareDataType | grep 'Processor Name' | awk -F ':' '{print $2}'" + cpuinfo = execShell(cmd) + return cpuinfo[0].strip() + + current_os = getOs() + if current_os.startswith('freebsd'): + cmd = "sysctl -a | egrep -i 'hw.model' | awk -F ':' '{print $2}'" + cpuinfo = execShell(cmd) + return cpuinfo[0].strip() + + # 取CPU类型 + cpuinfo = open('/proc/cpuinfo', 'r').read() + rep = "model\\s+name\\s+:\\s+(.+)" + tmp = re.search(rep, cpuinfo, re.I) + if tmp: + cpuType = tmp.groups()[0] + else: + cpuinfo = execShell('LANG="en_US.UTF-8" && lscpu')[0] + rep = "Model\\s+name:\\s+(.+)" + tmp = re.search(rep, cpuinfo, re.I) + if tmp: + cpuType = tmp.groups()[0] + return cpuType + + +def getInfo(msg, args=()): + # 取提示消息 + for i in range(len(args)): + rep = '{' + str(i + 1) + '}' + msg = msg.replace(rep, args[i]) + return msg + def getLastLine(path, num, p=1): pyVersion = sys.version_info[0] try: diff --git a/web/setting.py b/web/setting.py index 313aaebc8..892224459 100644 --- a/web/setting.py +++ b/web/setting.py @@ -18,7 +18,9 @@ import logging import os import sys -from branding import APP_NAME, APP_ICON, APP_COPYRIGHT +import core.mw as mw + +from branding import APP_NAME, APP_ICON, APP_COPYRIGHT, APP_LOG_NAME, APP_SQLITE_NAME from version import APP_VERSION, APP_RELEASE, APP_REVISION, APP_SUFFIX DEBUG = False @@ -28,6 +30,31 @@ CONFIG_DATABASE_CONNECTION_POOL_SIZE = 5 # 允许溢出超过连接池大小的连接数 CONFIG_DATABASE_CONNECTION_MAX_OVERFLOW = 100 +# 应用程序日志级别-其中之一: +# CRITICAL 50 +# ERROR 40 +# WARNING 30 +# SQL 25 +# INFO 20 +# DEBUG 10 +# NOTSET 0 +CONSOLE_LOG_LEVEL = logging.WARNING +FILE_LOG_LEVEL = logging.WARNING + +DATA_DIR = mw.getPanelDataDir() + +# 日志文件名。这将进入数据目录,服务器模式下的非Windows平台除外。 +LOG_FILE = os.path.join(mw.getMWLogs(), APP_LOG_NAME + '.log') + +# 日志旋转设置日志文件将根据LOG_ROTATION_SIZE和LOG_ROTATION_AGE的值进行切换。 +# 旋转的文件将以格式命名Y-m-d_H-M-S +LOG_ROTATION_SIZE = 10 # MBs +LOG_ROTATION_AGE = 1440 # minutes +LOG_ROTATION_MAX_LOG_FILES = 90 # 要保留的最大备份数 + +# 用于存储用户帐户和设置的SQLite数据库的默认路径。 +# 此默认设置将文件放置在与此相同的目录中 配置文件,但会生成一个在整个应用程序中使用的绝对路径。 +SQLITE_PATH = os.path.join(DATA_DIR, APP_SQLITE_NAME + '.db') DEFAULT_SERVER = '127.0.0.1' diff --git a/web/templates/default/login.html b/web/templates/default/login.html index aaf5f544a..fb471f309 100755 --- a/web/templates/default/login.html +++ b/web/templates/default/login.html @@ -135,7 +135,7 @@
- +
@@ -145,7 +145,7 @@
-
{% if session['code'] %}{% endif %}
+
{% if session['code'] %}{% endif %}
@@ -205,9 +205,9 @@ $('#login-button').click(function(){ var data = 'username='+username+'&password='+password+'&code='+code; var loadT = layer.msg("正在登录中...",{icon:16,time:0,shade: [0.3, '#000']}); $.post('/do_login',data,function(rdata){ - console.log(rdata); + console.log('do_login',rdata); layer.close(loadT); - if(status < 0){ + if(!rdata.status || rdata.status < 0){ if(username == 'admin' && rdata.msg.indexOf('用户名') != -1){ rdata.msg += ',
获取默认用户和密码命令: mw default'; } diff --git a/web/utils/__init__.py b/web/utils/__init__.py new file mode 100644 index 000000000..5ab1bb9b0 --- /dev/null +++ b/web/utils/__init__.py @@ -0,0 +1,13 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os + +IS_WIN = (os.name == 'nt') \ No newline at end of file diff --git a/web/utils/mwplugin.py b/web/utils/mwplugin.py index bdc412d75..0412cd5d6 100644 --- a/web/utils/mwplugin.py +++ b/web/utils/mwplugin.py @@ -104,6 +104,46 @@ class MwPlugin(object): mw.writeFile(self.__index, '[]') self.__index_data = json.loads(mw.readFile(self.__index)) + def getIndexList(self): + if not os.path.exists(self.__index): + mw.writeFile(self.__index, '[]') + + indexList = json.loads(mw.readFile(self.__index)) + plist = [] + for i in indexList: + tmp = i.split('-') + tmp_len = len(tmp) + plugin_name = tmp[0] + plugin_ver = tmp[1] + if tmp_len > 2: + tmpArr = tmp[0:tmp_len - 1] + plugin_name = '-'.join(tmpArr) + plugin_ver = tmp[tmp_len - 1] + + read_json_file = self.__plugin_dir + '/' + plugin_name + '/info.json' + if os.path.exists(read_json_file): + content = mw.readFile(read_json_file) + try: + data = json.loads(content) + data = self.makeCoexistList(data) + for index in range(len(data)): + if data[index]['coexist']: + if data[index]['versions'] == plugin_ver or plugin_ver in data[index]['versions']: + data[index]['display'] = True + plist.append(data[index]) + continue + else: + data[index]['display'] = True + plist.append(data[index]) + + except Exception as e: + print('getIndexList:', mw.getTracebackInfo()) + + # 使用gevent模式时,无法使用多进程 + # plist = self.checkStatusMProcess(plist) + plist = self.checkStatusMThreads(plist) + return plist + # 插件搜索匹配 def searchKey(self, info, keyword: str | None = None, diff --git a/web/utils/system/__init__.py b/web/utils/system/__init__.py new file mode 100644 index 000000000..0d5869331 --- /dev/null +++ b/web/utils/system/__init__.py @@ -0,0 +1,292 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import sys +import time +import math +import psutil + + +import core.mw as mw + +def getDiskInfo(): + # 取磁盘分区信息 + temp = mw.execShell("df -h -P|grep '/'|grep -v tmpfs | grep -v devfs")[0] + tempInodes = mw.execShell("df -i -P|grep '/'|grep -v tmpfs | grep -v devfs")[0] + temp1 = temp.split('\n') + tempInodes1 = tempInodes.split('\n') + diskInfo = [] + n = 0 + cuts = ['/mnt/cdrom', '/boot', '/boot/efi', '/dev', + '/dev/shm', '/zroot', '/run/lock', '/run', '/run/shm', '/run/user'] + for tmp in temp1: + n += 1 + inodes = tempInodes1[n - 1].split() + disk = tmp.split() + if len(disk) < 5: + continue + if disk[1].find('M') != -1: + continue + if disk[1].find('K') != -1: + continue + if len(disk[5].split('/')) > 4: + continue + if disk[5] in cuts: + continue + arr = {} + arr['path'] = disk[5] + tmp1 = [disk[1], disk[2], disk[3], disk[4]] + arr['size'] = tmp1 + arr['inodes'] = [inodes[1], inodes[2], inodes[3], inodes[4]] + diskInfo.append(arr) + return diskInfo + +class stats: + cache = {} + + # 磁盘统计 + def disk(self): + iokey = 'disk_stat' + diskInfo = {} + diskInfo['ALL'] = {} + diskInfo['ALL']['read_count'] = 0 + diskInfo['ALL']['write_count'] = 0 + diskInfo['ALL']['read_bytes'] = 0 + diskInfo['ALL']['write_bytes'] = 0 + diskInfo['ALL']['read_time'] = 0 + diskInfo['ALL']['write_time'] = 0 + diskInfo['ALL']['read_merged_count'] = 0 + diskInfo['ALL']['write_merged_count'] = 0 + + try: + diskio = None + if iokey in self.cache: + diskio = self.cache[iokey] + + mtime = int(time.time()) + if not diskio: + diskio = {} + diskio['info'] = None + diskio['time'] = mtime + + diskio_cache = diskio['info'] + stime = mtime - diskio['time'] + if not stime: stime = 1 + + diskio_group = psutil.disk_io_counters(perdisk=True) + if not diskio_cache: + diskio_cache = diskio_group + + for disk_name in diskio_group.keys(): + diskInfo[disk_name] = {} + # print('disk_name',disk_name) + # print(diskio_group[disk_name].write_time , diskio_cache[disk_name].write_time) + # print(diskio_group[disk_name].write_count , diskio_cache[disk_name].write_count) + + diskInfo[disk_name]['read_count'] = int((diskio_group[disk_name].read_count - diskio_cache[disk_name].read_count) / stime) + diskInfo[disk_name]['write_count'] = int((diskio_group[disk_name].write_count - diskio_cache[disk_name].write_count) / stime) + diskInfo[disk_name]['read_bytes'] = int((diskio_group[disk_name].read_bytes - diskio_cache[disk_name].read_bytes) / stime) + diskInfo[disk_name]['write_bytes'] = int((diskio_group[disk_name].write_bytes - diskio_cache[disk_name].write_bytes) / stime) + diskInfo[disk_name]['read_time'] = int((diskio_group[disk_name].read_time - diskio_cache[disk_name].read_time) / stime) + diskInfo[disk_name]['write_time'] = int((diskio_group[disk_name].write_time - diskio_cache[disk_name].write_time) / stime) + + if 'read_merged_count' in diskio_group[disk_name] and 'read_merged_count' in diskio_cache[disk_name]: + diskInfo[disk_name]['read_merged_count'] = int((diskio_group[disk_name].read_merged_count - diskio_cache[disk_name].read_merged_count) / stime) + if 'write_merged_count' in diskio_group[disk_name] and 'write_merged_count' in diskio_cache[disk_name]: + diskInfo[disk_name]['write_merged_count'] = int((diskio_group[disk_name].write_merged_count - diskio_cache[disk_name].write_merged_count) / stime) + + diskInfo['ALL']['read_count'] += diskInfo[disk_name]['read_count'] + diskInfo['ALL']['write_count'] += diskInfo[disk_name]['write_count'] + diskInfo['ALL']['read_bytes'] += diskInfo[disk_name]['read_bytes'] + diskInfo['ALL']['write_bytes'] += diskInfo[disk_name]['write_bytes'] + if diskInfo['ALL']['read_time'] < diskInfo[disk_name]['read_time']: + diskInfo['ALL']['read_time'] = diskInfo[disk_name]['read_time'] + if diskInfo['ALL']['write_time'] < diskInfo[disk_name]['write_time']: + diskInfo['ALL']['write_time'] = diskInfo[disk_name]['write_time'] + + if 'read_merged_count' in diskInfo[disk_name] and 'read_merged_count' in diskInfo[disk_name]: + diskInfo['ALL']['read_merged_count'] += diskInfo[disk_name]['read_merged_count'] + if 'write_merged_count' in diskInfo[disk_name] and 'write_merged_count' in diskInfo[disk_name]: + diskInfo['ALL']['write_merged_count'] += diskInfo[disk_name]['write_merged_count'] + + self.cache[iokey] = {'info':diskio_group,'time':mtime} + except Exception as e: + pass + + return diskInfo + + # 网络统计 + def network(self): + netInfo = {} + + netInfo['ALL'] = {} + netInfo['ALL']['up'] = 0 + netInfo['ALL']['down'] = 0 + netInfo['ALL']['upTotal'] = 0 + netInfo['ALL']['downTotal'] = 0 + netInfo['ALL']['upPackets'] = 0 + netInfo['ALL']['downPackets'] = 0 + + mtime = time.time() + iokey = 'net_stat' + netio = None + if iokey in self.cache: + netio = self.cache[iokey] + + if not netio: + netio = {} + netio['info'] = None + netio['all_io'] = None + netio['time'] = mtime + + stime = mtime - netio['time'] + if not stime: stime = 1 + + # print("new:",stime) + netio_group = psutil.net_io_counters(pernic=True).keys() + + netio_cache = netio['info'] + allio_cache = netio['all_io'] + if not netio_cache: + netio_cache = {} + + netio_group_t = {} + for name in netio_group: + netInfo[name] = {} + + io_data = psutil.net_io_counters(pernic=True).get(name) + if not name in netio_cache: + netio_cache[name] = io_data + + netio_group_t[name] = io_data + + netInfo[name]['up'] = round(float((io_data[0] - netio_cache[name][0]) / stime), 2) + netInfo[name]['down'] = round(float((io_data[1] - netio_cache[name][1])/ stime), 2) + + netInfo[name]['upTotal'] = io_data[0] + netInfo[name]['downTotal'] = io_data[1] + netInfo[name]['upPackets'] = io_data[2] + netInfo[name]['downPackets'] = io_data[3] + + all_io = psutil.net_io_counters()[:4] + if not allio_cache: + allio_cache = all_io + + netInfo['ALL']['up'] = round(float((all_io[0] - allio_cache[0]) /stime), 2) + netInfo['ALL']['down'] = round(float((all_io[1] - allio_cache[1]) /stime), 2) + netInfo['ALL']['upTotal'] = all_io[0] + netInfo['ALL']['downTotal'] = all_io[1] + netInfo['ALL']['upPackets'] = all_io[2] + netInfo['ALL']['downPackets'] = all_io[3] + + self.cache[iokey] = {'info':netio_group_t,'all_io':all_io,'time':mtime} + return netInfo + + + +def getLoadAverage(): + c = os.getloadavg() + data = {} + data['one'] = round(float(c[0]), 2) + data['five'] = round(float(c[1]), 2) + data['fifteen'] = round(float(c[2]), 2) + data['max'] = psutil.cpu_count() * 2 + data['limit'] = data['max'] + data['safe'] = data['max'] * 0.75 + return data + +def getSystemVersion(): + # 取操作系统版本 + current_os = mw.getOs() + # sys_temper = self.getSystemDeviceTemperature() + # print(sys_temper) + # mac + if current_os == 'darwin': + data = mw.execShell('sw_vers')[0] + data_list = data.strip().split("\n") + mac_version = '' + for x in data_list: + xlist = x.split("\t") + mac_version += xlist[len(xlist)-1] + ' ' + + arch_ver = mw.execShell("arch") + return mac_version + " (" + arch_ver[0].strip() + ")" + + # freebsd + if current_os.startswith('freebsd'): + version = mw.execShell( + "cat /etc/*-release | grep PRETTY_NAME | awk -F = '{print $2}' | awk -F '\"' '{print $2}'") + arch_ver = mw.execShell( + "sysctl -a | egrep -i 'hw.machine_arch' | awk -F ':' '{print $2}'") + return version[0].strip() + " (" + arch_ver[0].strip() + ")" + + redhat_series = '/etc/redhat-release' + if os.path.exists(redhat_series): + version = mw.readFile('/etc/redhat-release') + version = version.replace('release ', '').strip() + + arch_ver = mw.execShell("arch") + return version + " (" + arch_ver[0].strip() + ")" + + os_series = '/etc/os-release' + if os.path.exists(os_series): + version = mw.execShell( + "cat /etc/*-release | grep PRETTY_NAME | awk -F = '{print $2}' | awk -F '\"' '{print $2}'") + + arch_ver = mw.execShell("arch") + return version[0].strip() + " (" + arch_ver[0].strip() + ")" + +def getBootTime(): + # 取系统启动时间 + uptime = mw.readFile('/proc/uptime') + if uptime == False: + start_time = psutil.boot_time() + run_time = time.time() - start_time + else: + run_time = uptime.split()[0] + tStr = float(run_time) + min = tStr / 60 + hours = min / 60 + days = math.floor(hours / 24) + hours = math.floor(hours - (days * 24)) + min = math.floor(min - (days * 60 * 24) - (hours * 60)) + return mw.getInfo('已不间断运行: {1}天{2}小时{3}分钟', (str(int(days)), str(int(hours)), str(int(min)))) + +def getCpuInfo(interval=1): + # 取CPU信息 + cpuCount = psutil.cpu_count() + cpuLogicalNum = psutil.cpu_count(logical=False) + used = psutil.cpu_percent(interval=interval) + cpuLogicalNum = 0 + if os.path.exists('/proc/cpuinfo'): + c_tmp = mw.readFile('/proc/cpuinfo') + d_tmp = re.findall("physical id.+", c_tmp) + cpuLogicalNum = len(set(d_tmp)) + + used_all = psutil.cpu_percent(percpu=True) + cpu_name = mw.getCpuType() + " * {}".format(cpuLogicalNum) + return used, cpuCount, used_all, cpu_name, cpuCount, cpuLogicalNum + +def getMemInfo(): + # 取内存信息 + mem = psutil.virtual_memory() + if sys.platform == 'darwin': + memInfo = {'memTotal': mem.total} + memInfo['memRealUsed'] = memInfo['memTotal'] * (mem.percent / 100) + else: + memInfo = { + 'memTotal': mem.total, + 'memFree': mem.free, + 'memBuffers': mem.buffers, + 'memCached': mem.cached + } + memInfo['memRealUsed'] = memInfo['memTotal'] - memInfo['memFree'] - memInfo['memBuffers'] - memInfo['memCached'] + return memInfo \ No newline at end of file diff --git a/web/utils/vilidate.py b/web/utils/vilidate.py new file mode 100755 index 000000000..c275bfe76 --- /dev/null +++ b/web/utils/vilidate.py @@ -0,0 +1,143 @@ +# coding: utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +# --------------------------------------------------------------------------------- +# 验证码操作 +# --------------------------------------------------------------------------------- + + +import random +import math + +from PIL import Image, ImageDraw, ImageFont, ImageFilter + +class vieCode: + __fontSize = 20 # 字体大小 + __width = 120 # 画布宽度 + __heigth = 45 # 画布高度 + __length = 4 # 验证码长度 + __draw = None # 画布 + __img = None # 图片资源 + __code = None # 验证码字符 + __str = None # 自定义验证码字符集 + __inCurve = True # 是否画干扰线 + __inNoise = True # 是否画干扰点 + __type = 2 # 验证码类型 1、纯字母 2、数字字母混合 + __fontPatn = 'class/fonts/2.ttf' # 字体 + + def GetCodeImage(self, size=80, length=4): + '''获取验证码图片 + @param int size 验证码大小 + @param int length 验证码长度 + ''' + # 准备基础数据 + self.__length = length + self.__fontSize = size + self.__width = self.__fontSize * self.__length + self.__heigth = int(self.__fontSize * 1.5) + + # 生成验证码图片 + self.__createCode() + self.__createImage() + self.__createNoise() + self.__printString() + self.__cerateFilter() + + return self.__img, self.__code + + def __cerateFilter(self): + '''模糊处理''' + self.__img = self.__img.filter(ImageFilter.BLUR) + filter = ImageFilter.ModeFilter(8) + self.__img = self.__img.filter(filter) + + def __createCode(self): + '''创建验证码字符''' + # 是否自定义字符集合 + if not self.__str: + # 源文本 + number = "3456789" + srcLetter = "qwertyuipasdfghjkzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM" + srcUpper = srcLetter.upper() + if self.__type == 1: + self.__str = number + else: + self.__str = srcLetter + srcUpper + number + + # 构造验证码 + self.__code = random.sample(self.__str, self.__length) + + def __createImage(self): + '''创建画布''' + bgColor = (random.randint(200, 255), random.randint( + 200, 255), random.randint(200, 255)) + self.__img = Image.new('RGB', (self.__width, self.__heigth), bgColor) + self.__draw = ImageDraw.Draw(self.__img) + + def __createNoise(self): + '''画干扰点''' + if not self.__inNoise: + return + font = ImageFont.truetype(self.__fontPatn, int(self.__fontSize / 1.5)) + for i in range(5): + # 杂点颜色 + noiseColor = (random.randint(150, 200), random.randint( + 150, 200), random.randint(150, 200)) + putStr = random.sample(self.__str, 2) + for j in range(2): + # 绘杂点 + size = (random.randint(-10, self.__width), + random.randint(-10, self.__heigth)) + self.__draw.text(size, putStr[j], font=font, fill=noiseColor) + pass + + def __createCurve(self): + '''画干扰线''' + if not self.__inCurve: + return + x = y = 0 + + # 计算曲线系数 + a = random.uniform(1, self.__heigth / 2) + b = random.uniform(-self.__width / 4, self.__heigth / 4) + f = random.uniform(-self.__heigth / 4, self.__heigth / 4) + t = random.uniform(self.__heigth, self.__width * 2) + xend = random.randint(self.__width / 2, self.__width * 2) + w = (2 * math.pi) / t + + # 画曲线 + color = (random.randint(30, 150), random.randint( + 30, 150), random.randint(30, 150)) + for x in range(xend): + if w != 0: + for k in range(int(self.__heigth / 10)): + y = a * math.sin(w * x + f) + b + self.__heigth / 2 + i = int(self.__fontSize / 5) + while i > 0: + px = x + i + py = y + i + k + self.__draw.point((px, py), color) + i -= i + + def __printString(self): + '''打印验证码字符串''' + font = ImageFont.truetype(self.__fontPatn, self.__fontSize) + x = 0 + # 打印字符到画板 + for i in range(self.__length): + # 设置字体随机颜色 + color = (random.randint(30, 150), random.randint( + 30, 150), random.randint(30, 150)) + # 计算座标 + x = random.uniform(self.__fontSize * i * 0.95, + self.__fontSize * i * 1.1) + y = self.__fontSize * random.uniform(0.3, 0.5) + # 打印字符 + self.__draw.text((x, y), self.__code[i], font=font, fill=color)