From eb3f78ef4a3db43ee594d323095bf201b7af1937 Mon Sep 17 00:00:00 2001 From: Mr Chen Date: Wed, 30 Oct 2024 00:55:34 +0800 Subject: [PATCH] update --- plugins/task_manager/task_manager_index.py | 8 +++- web/admin/__init__.py | 12 +++++- web/admin/common.py | 37 ++++++++++++++++++ web/admin/crontab/__init__.py | 2 + web/admin/dashboard/__init__.py | 37 ++++++++++++++++-- web/admin/files/__init__.py | 13 ++++++- web/admin/firewall/__init__.py | 3 ++ web/admin/model/__init__.py | 2 + web/admin/model/user.py | 42 +++++++++++++++++++++ web/admin/monitor/__init__.py | 5 ++- web/admin/setting/__init__.py | 9 +++++ web/admin/setup/option.py | 4 ++ web/admin/site/__init__.py | 9 +++-- web/admin/soft/__init__.py | 2 + web/admin/system/__init__.py | 14 ++++++- web/admin/task/__init__.py | 5 +++ web/admin/user_login_check.py | 10 ++++- web/core/mw.py | 16 ++++++++ web/static/fonts/2.ttf | Bin 0 -> 28328 bytes web/templates/default/layout.html | 22 +++++------ web/utils/__init__.py | 4 +- web/utils/vilidate.py | 2 +- 22 files changed, 229 insertions(+), 29 deletions(-) create mode 100644 web/admin/common.py create mode 100644 web/admin/model/user.py create mode 100755 web/static/fonts/2.ttf diff --git a/plugins/task_manager/task_manager_index.py b/plugins/task_manager/task_manager_index.py index a864e3c4a..9790b19b9 100755 --- a/plugins/task_manager/task_manager_index.py +++ b/plugins/task_manager/task_manager_index.py @@ -11,8 +11,12 @@ import json from typing import List, Dict -sys.path.append(os.getcwd() + "/class/core") -import mw +try: + sys.path.append(os.getcwd() + "/class/core") + import mw +except Exception as e: + import core.mw as mw + app_debug = False if mw.isAppleSystem(): diff --git a/web/admin/__init__.py b/web/admin/__init__.py index b1c51c334..12786430f 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -20,13 +20,17 @@ from flask_socketio import SocketIO, emit, send from flask import Flask, abort, request, current_app, session, url_for from flask import Blueprint, render_template from flask import render_template_string -from flask_migrate import Migrate +from flask_migrate import Migrate +from flask_caching import Cache from werkzeug.local import LocalProxy -from admin.model import db as sys_db +from admin import model from admin import setup +from admin.model import db as sys_db + + import core.mw as mw import config import utils.config as utils_config @@ -59,6 +63,9 @@ app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=31) app.config['SQLALCHEMY_DATABASE_URI'] = mw.getSqitePrefix()+config.SQLITE_PATH # 使用 SQLite 数据库 app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True +# 缓存配置 +cache = Cache(config={'CACHE_TYPE': 'simple'}) +cache.init_app(app, config={'CACHE_TYPE': 'simple'}) # 初始化db sys_db.init_app(app) @@ -91,6 +98,7 @@ for module in get_submodules(): if app.blueprints.get(module.name) is None: app.register_blueprint(module) + @app.before_request def requestCheck(): # print("hh") diff --git a/web/admin/common.py b/web/admin/common.py new file mode 100644 index 000000000..c1692c081 --- /dev/null +++ b/web/admin/common.py @@ -0,0 +1,37 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import time + +from admin import model +from admin import session + +def isLogined(): + if 'login' in session and session['login'] == True and 'username' in session: + username = session['username'] + info = model.getUserByName(username) + if info is None: + return False + + # print(userInfo) + if info['name'] != session['username']: + return False + + now_time = int(time.time()) + + if 'overdue' in session and now_time > session['overdue']: + # 自动续期 + session['overdue'] = int(time.time()) + 7 * 24 * 60 * 60 + return False + + if 'tmp_login_expire' in session and now_time > int(session['tmp_login_expire']): + session.clear() + return False + return True \ No newline at end of file diff --git a/web/admin/crontab/__init__.py b/web/admin/crontab/__init__.py index 203b78685..6162e7272 100644 --- a/web/admin/crontab/__init__.py +++ b/web/admin/crontab/__init__.py @@ -12,6 +12,7 @@ from flask import Blueprint, render_template from flask import request +from admin.user_login_check import panel_login_required from admin.model import Crontab blueprint = Blueprint('crontab', __name__, url_prefix='/crontab', template_folder='../../templates/default') @@ -21,6 +22,7 @@ def index(): # 插件列表 @blueprint.route('/list', endpoint='list', methods=['GET','POST']) +@panel_login_required def list(): page = request.args.get('p', 1) size = 10 diff --git a/web/admin/dashboard/__init__.py b/web/admin/dashboard/__init__.py index 426c65905..0d9ece5bb 100644 --- a/web/admin/dashboard/__init__.py +++ b/web/admin/dashboard/__init__.py @@ -8,14 +8,24 @@ # Author: midoks # --------------------------------------------------------------------------------- +import io +import time from flask import Blueprint, render_template +from flask import make_response +from flask import Response from flask import request,g +from admin.common import isLogined +from admin.user_login_check import panel_login_required +from admin import cache,session +from admin import model import core.mw as mw + blueprint = Blueprint('dashboard', __name__, url_prefix='/', template_folder='../../templates') @blueprint.route('/') +@panel_login_required def index(): return render_template('default/index.html') @@ -24,6 +34,19 @@ def index(): # 定义登录入口相关方法 # --------------------------------------------------------------------------------- +# 验证码 +@blueprint.route('/code') +def code(): + import utils.vilidate as vilidate + vie = vilidate.vieCode() + codeImage = vie.GetCodeImage(80, 4) + out = io.BytesIO() + codeImage[0].save(out, "png") + session['code'] = mw.md5(''.join(codeImage[1]).lower()) + + img = Response(out.getvalue(), headers={'Content-Type': 'image/png'}) + return make_response(img) + @blueprint.route('/login') def login(): return render_template('default/login.html') @@ -31,7 +54,9 @@ def login(): # 检查是否登录 @blueprint.route('/check_login',methods=['GET','POST']) def check_login(): - return "0" + if isLogined(): + return "true" + return "false" # 执行登录操作 @blueprint.route('/do_login', endpoint='do_login', methods=['POST']) @@ -41,7 +66,11 @@ def do_login(): password = request.form.get('password', '').strip() code = request.form.get('code', '').strip() - print(g) - print(username, password, code ) + if 'code' in session: + if session['code'] != mw.md5(code): + return mw.returnData(False, code_msg) - return mw.returnData(False, '面板已经关闭!') + print(username, password, code) + print(cache,session) + + return mw.returnData(1, '登录成功,正在跳转...') diff --git a/web/admin/files/__init__.py b/web/admin/files/__init__.py index 31ae28f9f..bd14375c8 100644 --- a/web/admin/files/__init__.py +++ b/web/admin/files/__init__.py @@ -15,23 +15,26 @@ from flask import request from admin import model - +from admin.user_login_check import panel_login_required import core.mw as mw import utils.file as file blueprint = Blueprint('files', __name__, url_prefix='/files', template_folder='../../templates/default') @blueprint.route('/index', endpoint='index') +@panel_login_required def index(): return render_template('files.html') # 获取文件内容 @blueprint.route('/get_body', endpoint='get_file_body', methods=['POST']) +@panel_login_required def get_file_body(): path = request.form.get('path', '') return file.getFileBody(path) # 获取文件内容 @blueprint.route('/save_body', endpoint='save_body', methods=['POST']) +@panel_login_required def save_body(): path = request.form.get('path', '') data = request.form.get('data', '') @@ -56,6 +59,7 @@ def save_body(): # 获取文件内容(最新行数) @blueprint.route('/get_last_body', endpoint='get_file_last_body', methods=['POST']) +@panel_login_required def get_file_last_body(): path = request.form.get('path', '') line = request.form.get('line', '100') @@ -72,6 +76,7 @@ def get_file_last_body(): # 获取文件列表 @blueprint.route('/get_dir', endpoint='get_dir', methods=['POST']) +@panel_login_required def get_dir(): path = request.form.get('path', '') if not os.path.exists(path): @@ -92,6 +97,7 @@ def get_dir(): # 获取站点日志目录 @blueprint.route('/get_dir_size', endpoint='get_dir_size', methods=['POST']) +@panel_login_required def get_dir_size(): path = request.form.get('path', '') size = file.getDirSize(path) @@ -100,6 +106,7 @@ def get_dir_size(): # 删除文件 @blueprint.route('/delete', endpoint='delete', methods=['POST']) +@panel_login_required def delete(): path = request.form.get('path', '') return file.fileDelete(path) @@ -107,6 +114,7 @@ def delete(): # 删除文件 @blueprint.route('/delete_dir', endpoint='delete_dir', methods=['POST']) +@panel_login_required def delete_dir(): path = request.form.get('path', '') return file.dirDelete(path) @@ -114,17 +122,20 @@ def delete_dir(): # 回收站文件 @blueprint.route('/get_recycle_bin', endpoint='get_recycle_bin', methods=['POST']) +@panel_login_required def get_recycle_bin(): return file.getRecycleBin() # 回收站文件恢复 @blueprint.route('/re_recycle_bin', endpoint='re_recycle_bin', methods=['POST']) +@panel_login_required def re_recycle_bin(): path = request.form.get('path', '') return file.reRecycleBin(path) # 回收站文件 @blueprint.route('/recycle_bin', endpoint='recycle_bin', methods=['POST']) +@panel_login_required def recycle_bin(): return file.toggleRecycleBin() diff --git a/web/admin/firewall/__init__.py b/web/admin/firewall/__init__.py index e3d694fd6..9ed6f057e 100644 --- a/web/admin/firewall/__init__.py +++ b/web/admin/firewall/__init__.py @@ -19,6 +19,7 @@ import core.mw as mw blueprint = Blueprint('firewall', __name__, url_prefix='/firewall', template_folder='../../templates/default') @blueprint.route('/index', endpoint='index') +@panel_login_required def index(): return render_template('firewall.html') @@ -51,12 +52,14 @@ def get_list(): # 获取站点日志目录 @blueprint.route('/get_www_path', endpoint='get_www_path', methods=['POST']) +@panel_login_required def get_www_path(): path = mw.getLogsDir() return {'path': path} # 获取站点日志目录 @blueprint.route('/get_ssh_info', endpoint='get_ssh_info', methods=['POST']) +@panel_login_required def get_ssh_info(): pass diff --git a/web/admin/model/__init__.py b/web/admin/model/__init__.py index 170532902..2537c467b 100644 --- a/web/admin/model/__init__.py +++ b/web/admin/model/__init__.py @@ -21,3 +21,5 @@ from .task import addTask from .task import getTaskCount,getTaskUnexecutedCount,getTaskList,getTaskFirstByRun from .task import setTaskStatus,setTaskData +from .user import getUserByName,isLoginCheck + diff --git a/web/admin/model/user.py b/web/admin/model/user.py new file mode 100644 index 000000000..74542f525 --- /dev/null +++ b/web/admin/model/user.py @@ -0,0 +1,42 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +from admin.model import db, Users + +import core.mw as mw + +def getUserByName(name, +) -> None: + ''' + 获取用户信息通过用户名 + ''' + item = Users.query.filter(Users.name==name).first() + if item is None: + return None + row = {} + row['id'] = item.id + row['name'] = item.name + row['password'] = item.password + row['login_ip'] = item.login_ip + row['login_time'] = item.login_time + row['phone'] = item.phone + row['email'] = item.email + row['add_time'] = item.add_time + row['update_time'] = item.update_time + return row + +def isLoginCheck(username, password) -> bool: + info = getUserByName(data['username']) + if info is None: + return False + + if info['password'] == mw.md5(password): + return True + return False diff --git a/web/admin/monitor/__init__.py b/web/admin/monitor/__init__.py index c8258caef..0a540268a 100644 --- a/web/admin/monitor/__init__.py +++ b/web/admin/monitor/__init__.py @@ -11,7 +11,10 @@ from flask import Blueprint, render_template +from admin.user_login_check import panel_login_required + blueprint = Blueprint('monitor', __name__, url_prefix='/monitor', template_folder='../../templates/default') @blueprint.route('/index', endpoint='index') +@panel_login_required def index(): - return render_template('monitor.html',) \ No newline at end of file + return render_template('monitor.html') \ No newline at end of file diff --git a/web/admin/setting/__init__.py b/web/admin/setting/__init__.py index 30f24ec68..a7fece270 100644 --- a/web/admin/setting/__init__.py +++ b/web/admin/setting/__init__.py @@ -14,23 +14,28 @@ from flask import Blueprint, render_template from flask import request from admin import model +from admin.user_login_check import panel_login_required + import core.mw as mw # 默认页面 blueprint = Blueprint('setting', __name__, url_prefix='/setting', template_folder='../../templates') @blueprint.route('/index', endpoint='index') +@panel_login_required def index(): return render_template('default/setting.html') @blueprint.route('/get_panel_list', endpoint='get_panel_list', methods=['POST']) +@panel_login_required def get_panel_list(): return [] # 设置面板名称 @blueprint.route('/set_webname', endpoint='set_webname', methods=['POST']) +@panel_login_required def set_webname(): webname = request.form.get('webname', '') src_webname = model.getOption('title') @@ -40,6 +45,7 @@ def set_webname(): # 设置服务器IP @blueprint.route('/set_ip', endpoint='set_ip', methods=['POST']) +@panel_login_required def set_ip(): host_ip = request.form.get('host_ip', '') src_host_ip = model.getOption('server_ip') @@ -49,6 +55,7 @@ def set_ip(): # 默认备份目录 @blueprint.route('/set_backup_dir', endpoint='set_backup_dir', methods=['POST']) +@panel_login_required def set_backup_dir(): backup_path = request.form.get('backup_path', '') src_backup_path = model.getOption('backup_path') @@ -58,6 +65,7 @@ def set_backup_dir(): # 默认站点目录 @blueprint.route('/set_www_dir', endpoint='set_www_dir', methods=['POST']) +@panel_login_required def set_www_dir(): sites_path = request.form.get('sites_path', '') src_sites_path = model.getOption('sites_path') @@ -68,6 +76,7 @@ def set_www_dir(): # 设置安全入口 @blueprint.route('/set_admin_path', endpoint='set_admin_path', methods=['POST']) +@panel_login_required def set_admin_path(): admin_path = request.form.get('admin_path', '') admin_path_sensitive = [ diff --git a/web/admin/setup/option.py b/web/admin/setup/option.py index c941b71d5..3bbb4c575 100644 --- a/web/admin/setup/option.py +++ b/web/admin/setup/option.py @@ -20,6 +20,10 @@ def init_option(): model.setOption('recycle_bin', 'open') model.setOption('template', 'default') + # 调式模式,默认关闭 + model.setOption('debug', 'close') + + # 开启后台任务 # model.setOption('run_bg_task', 'close') diff --git a/web/admin/site/__init__.py b/web/admin/site/__init__.py index 5abe21b76..12ad0468f 100644 --- a/web/admin/site/__init__.py +++ b/web/admin/site/__init__.py @@ -12,10 +12,11 @@ from flask import Blueprint, render_template from flask import request -from utils.mwplugin import MwPlugin - -import core.mw as mw from admin.model import Sites +from admin.user_login_check import panel_login_required + +from utils.mwplugin import MwPlugin +import core.mw as mw blueprint = Blueprint('site', __name__, url_prefix='/site', template_folder='../../templates/default') @blueprint.route('/index', endpoint='index') @@ -24,6 +25,7 @@ def index(): # 插件列表 @blueprint.route('/list', endpoint='list', methods=['GET','POST']) +@panel_login_required def list(): p = request.form.get('p', '1') limit = request.form.get('limit', '10') @@ -55,5 +57,6 @@ def list(): return data @blueprint.route('/get_site_types', endpoint='get_site_types',methods=['POST']) +@panel_login_required def get_site_types(): return [] \ No newline at end of file diff --git a/web/admin/soft/__init__.py b/web/admin/soft/__init__.py index def11c495..b0b73c465 100644 --- a/web/admin/soft/__init__.py +++ b/web/admin/soft/__init__.py @@ -11,6 +11,8 @@ from flask import Blueprint, render_template +from admin.user_login_check import panel_login_required + blueprint = Blueprint('soft', __name__, url_prefix='/soft', template_folder='../../templates/default') @blueprint.route('/index', endpoint='index') def index(): diff --git a/web/admin/system/__init__.py b/web/admin/system/__init__.py index 6b2239b81..0f5779504 100644 --- a/web/admin/system/__init__.py +++ b/web/admin/system/__init__.py @@ -12,14 +12,18 @@ from flask import Blueprint, render_template from flask import request +from admin.user_login_check import panel_login_required +import admin.model.option as option + import core.mw as mw import utils.system as sys -import admin.model.option as option + blueprint = Blueprint('system', __name__, url_prefix='/system', template_folder='../../templates') # 获取系统的统计信息 @blueprint.route('/system_total', endpoint='system_total') +@panel_login_required def system_total(): data = sys.getMemInfo() cpu = sys.getCpuInfo(interval=1) @@ -32,6 +36,7 @@ def system_total(): # 获取系统的网络流量信息 @blueprint.route('/network', endpoint='network') +@panel_login_required def network(): stat = {} stat['cpu'] = sys.getCpuInfo() @@ -43,12 +48,14 @@ def network(): # 获取系统的磁盘信息 @blueprint.route('/disk_info', endpoint='disk_info') +@panel_login_required def disk_info(): data = sys.getDiskInfo() return data # 获取系统的负载统计信息 @blueprint.route('/get_load_average', endpoint='get_load_average', methods=['GET']) +@panel_login_required def get_load_average(): start = request.args.get('start', '') end = request.args.get('end', '') @@ -62,6 +69,7 @@ def get_load_average(): # 获取系统的磁盘IO统计信息 @blueprint.route('/get_disk_io', endpoint='get_disk_io', methods=['GET']) +@panel_login_required def get_disk_io(): start = request.args.get('start', '') end = request.args.get('end', '') @@ -73,6 +81,7 @@ def get_disk_io(): # 获取系统的CPU/IO统计信息 @blueprint.route('/get_cpu_io', endpoint='get_cpu_io', methods=['GET']) +@panel_login_required def get_cpu_io(): start = request.args.get('start', '') end = request.args.get('end', '') @@ -87,6 +96,7 @@ def get_cpu_io(): # 获取系统网络IO统计信息 @blueprint.route('/get_network_io', endpoint='get_network_io', methods=['GET']) +@panel_login_required def get_network_io(): start = request.args.get('start', '') end = request.args.get('end', '') @@ -100,6 +110,7 @@ def get_network_io(): # 获取系统网络IO统计信息 @blueprint.route('/set_control', endpoint='set_control', methods=['POST']) +@panel_login_required def set_control(): stype = request.form.get('type', '') day = request.form.get('day', '') @@ -153,6 +164,7 @@ def set_control(): # 升级检测 @blueprint.route('/update_server', endpoint='update_server') +@panel_login_required def update_server(): panel_type = request.args.get('type', 'check') version = request.args.get('version', '') diff --git a/web/admin/task/__init__.py b/web/admin/task/__init__.py index 94063ed0b..2cb6ea08e 100644 --- a/web/admin/task/__init__.py +++ b/web/admin/task/__init__.py @@ -14,6 +14,7 @@ from flask import request from admin import model from admin.model import db,Tasks +from admin.user_login_check import panel_login_required import core.mw as mw @@ -21,11 +22,13 @@ blueprint = Blueprint('task', __name__, url_prefix='/task', template_folder='../ @blueprint.route('/count', endpoint='task_count') +@panel_login_required def task_count(): return str(model.getTaskUnexecutedCount()) @blueprint.route('/list', endpoint='list', methods=['POST']) +@panel_login_required def list(): p = request.form.get('p', '1') limit = request.form.get('limit', '10').strip() @@ -53,12 +56,14 @@ def list(): return data @blueprint.route('/get_exec_log', endpoint='get_exec_log', methods=['POST']) +@panel_login_required def get_exec_log(): file = mw.getPanelTaskLog() return mw.getLastLine(file, 100) @blueprint.route('/get_task_speed', endpoint='get_task_speed', methods=['POST']) +@panel_login_required def get_task_speed(): count = model.getTaskUnexecutedCount() if count == 0: diff --git a/web/admin/user_login_check.py b/web/admin/user_login_check.py index 85a730a1c..5cc84df46 100644 --- a/web/admin/user_login_check.py +++ b/web/admin/user_login_check.py @@ -10,10 +10,16 @@ from functools import wraps +from admin import model +from admin import session +from admin.common import isLogined + def panel_login_required(func): - + @wraps(func) def wrapper(*args, **kwargs): - # print('panel_login_required', args, kwargs) + if not isLogined(): + return {'status':False, 'msg':'未登录/登录过期'} + return func(*args, **kwargs) return wrapper \ No newline at end of file diff --git a/web/core/mw.py b/web/core/mw.py index ebd52bd5f..c657071b1 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -422,6 +422,22 @@ def getLastLine(path, num, p=1): return "\n".join(data) +# 获取系统温度 +def getSystemDeviceTemperature(): + import psutil + if not hasattr(psutil, "sensors_temperatures"): + return False, "platform not supported" + temps = psutil.sensors_temperatures() + if not temps: + return False, "can't read any temperature" + for name, entries in temps.items(): + for entry in entries: + return True, entry.label + # print("%-20s %s °C (high = %s °C, critical = %s °C)" % ( + # entry.label or name, entry.current, entry.high, + # entry.critical)) + return False, "" + def getPage(args, result='1,2,3,4,5,8'): data = getPageObject(args, result) return data[0] diff --git a/web/static/fonts/2.ttf b/web/static/fonts/2.ttf new file mode 100755 index 0000000000000000000000000000000000000000..3a452b68f3baa612539ad43cd427725e96f44100 GIT binary patch literal 28328 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 D\ + type: 1, + title: "【AD】求恰饭", + closeBtn: 1, + area: '300px;', + shade: 0.8, + id: 'panel_ad_list_mode', + btnAlign: 'c', + moveType: 1, + content: '
\ \ \ \ \ \
商家优惠码
mdserver-web
\ -
' - ,success: function(layero){ + ', + success: function(layero){ } }); } diff --git a/web/utils/__init__.py b/web/utils/__init__.py index 5ab1bb9b0..dfba09b52 100644 --- a/web/utils/__init__.py +++ b/web/utils/__init__.py @@ -10,4 +10,6 @@ import os -IS_WIN = (os.name == 'nt') \ No newline at end of file +IS_WIN = (os.name == 'nt') + + diff --git a/web/utils/vilidate.py b/web/utils/vilidate.py index c275bfe76..a2d8ba363 100755 --- a/web/utils/vilidate.py +++ b/web/utils/vilidate.py @@ -30,7 +30,7 @@ class vieCode: __inCurve = True # 是否画干扰线 __inNoise = True # 是否画干扰点 __type = 2 # 验证码类型 1、纯字母 2、数字字母混合 - __fontPatn = 'class/fonts/2.ttf' # 字体 + __fontPatn = 'static/fonts/2.ttf' # 字体 def GetCodeImage(self, size=80, length=4): '''获取验证码图片