mirror of
https://github.com/midoks/mdserver-web.git
synced 2026-10-10 06:39:25 +08:00
update
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
<div class="bgw mtb15 pd15 tab-view-box firewall-tab-view safe" style="overflow: hidden;">
|
||||
|
||||
<div class="mask_layer" style="display:none;">
|
||||
<div class="prompt_description">当前未安装本地服务器,<a class="btlink install_server" href="/soft">点击安装</a></div>
|
||||
<div class="prompt_description">当前未安装本地服务器,<a class="btlink install_server" href="/soft/index">点击安装</a></div>
|
||||
</div>
|
||||
|
||||
<!-- mysql start -->
|
||||
|
||||
+43
-8
@@ -10,17 +10,23 @@
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import time
|
||||
import uuid
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
|
||||
from flask import Flask
|
||||
|
||||
from flask_socketio import SocketIO, emit, send
|
||||
from flask import Flask, abort, request, current_app, session, url_for
|
||||
from flask import Flask, abort, current_app, session, url_for
|
||||
from flask import Blueprint, render_template
|
||||
from flask import render_template_string
|
||||
|
||||
from flask import request
|
||||
from flask import Response
|
||||
|
||||
|
||||
from flask_migrate import Migrate
|
||||
from flask_caching import Cache
|
||||
from werkzeug.local import LocalProxy
|
||||
@@ -44,7 +50,9 @@ socketio = SocketIO(manage_session=False, async_mode='threading',
|
||||
|
||||
app = Flask(__name__, template_folder='templates/default')
|
||||
|
||||
# app.debug = True
|
||||
# 缓存配置
|
||||
cache = Cache(config={'CACHE_TYPE': 'simple'})
|
||||
cache.init_app(app, config={'CACHE_TYPE': 'simple'})
|
||||
|
||||
# 静态文件配置
|
||||
from whitenoise import WhiteNoise
|
||||
@@ -63,14 +71,11 @@ app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=31)
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = mw.getSqitePrefix()+config.SQLITE_PATH+"?timeout=20" # 使用 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)
|
||||
Migrate(app, sys_db)
|
||||
|
||||
|
||||
# 检查数据库是否存在。如果没有就创建它。
|
||||
setup_db_required = False
|
||||
if not os.path.isfile(config.SQLITE_PATH):
|
||||
@@ -89,6 +94,11 @@ with app.app_context():
|
||||
setup.init_admin_user()
|
||||
setup.init_option()
|
||||
|
||||
app.config['BASIC_AUTH_OPEN'] = False
|
||||
with app.app_context():
|
||||
basic_auth = model.getOptionByJson('basic_auth', default={'open':False})
|
||||
if basic_auth['open']:
|
||||
app.config['BASIC_AUTH_OPEN'] = True
|
||||
|
||||
|
||||
# 加载模块
|
||||
@@ -98,11 +108,36 @@ for module in get_submodules():
|
||||
if app.blueprints.get(module.name) is None:
|
||||
app.register_blueprint(module)
|
||||
|
||||
def sendAuthenticated():
|
||||
# 发送http认证信息
|
||||
request_host = mw.getHostAddr()
|
||||
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
|
||||
|
||||
@app.before_request
|
||||
def requestCheck():
|
||||
# print("hh")
|
||||
pass
|
||||
# 自定义basic auth认证
|
||||
if app.config['BASIC_AUTH_OPEN']:
|
||||
basic_auth = model.getOptionByJson('basic_auth', default={'open':False})
|
||||
if not basic_auth['open']:
|
||||
return
|
||||
|
||||
auth = request.authorization
|
||||
if request.path in ['/download', '/hook', '/down']:
|
||||
return
|
||||
if not auth:
|
||||
return sendAuthenticated()
|
||||
|
||||
# print(auth.username.strip(),auth.password.strip())
|
||||
salt = basic_auth['salt']
|
||||
basic_user = mw.md5(auth.username.strip() + salt)
|
||||
basic_pwd = mw.md5(auth.password.strip() + salt)
|
||||
if basic_user != basic_auth['basic_user'] or basic_pwd != basic_auth['basic_pwd']:
|
||||
return sendAuthenticated()
|
||||
|
||||
|
||||
|
||||
@app.after_request
|
||||
def requestAfter(response):
|
||||
|
||||
@@ -29,11 +29,41 @@ blueprint = Blueprint('dashboard', __name__, url_prefix='/', template_folder='..
|
||||
def index():
|
||||
return render_template('default/index.html')
|
||||
|
||||
# 安全路径
|
||||
@blueprint.route('/<path>',endpoint='admin_safe_path',methods=['GET'])
|
||||
def admin_safe_path(path):
|
||||
db_path = model.getOption('admin_path')
|
||||
if db_path == path:
|
||||
return render_template('default/login.html')
|
||||
|
||||
unauthorized_status = model.getOption('unauthorized_status')
|
||||
if unauthorized_status == '0':
|
||||
return render_template('default/path.html')
|
||||
return Response(status=int(unauthorized_status))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# 定义登录入口相关方法
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
# 登录页: 当设置了安全路径,本页失效。
|
||||
@blueprint.route('/login')
|
||||
def login():
|
||||
signout = request.args.get('signout', '')
|
||||
if signout == 'True':
|
||||
session.clear()
|
||||
session['login'] = False
|
||||
session['overdue'] = 0
|
||||
|
||||
db_path = model.getOption('admin_path')
|
||||
if db_path == '':
|
||||
return render_template('default/login.html')
|
||||
else:
|
||||
unauthorized_status = model.getOption('unauthorized_status')
|
||||
if unauthorized_status == '0':
|
||||
return render_template('default/path.html')
|
||||
return Response(status=int(unauthorized_status))
|
||||
|
||||
# 验证码
|
||||
@blueprint.route('/code')
|
||||
def code():
|
||||
@@ -47,15 +77,6 @@ def code():
|
||||
img = Response(out.getvalue(), headers={'Content-Type': 'image/png'})
|
||||
return make_response(img)
|
||||
|
||||
@blueprint.route('/login')
|
||||
def login():
|
||||
signout = request.args.get('signout', '')
|
||||
if signout == 'True':
|
||||
session.clear()
|
||||
session['login'] = False
|
||||
session['overdue'] = 0
|
||||
return render_template('default/login.html')
|
||||
|
||||
# 检查是否登录
|
||||
@blueprint.route('/check_login',methods=['GET','POST'])
|
||||
def check_login():
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
import re
|
||||
import json
|
||||
import os
|
||||
|
||||
from flask import Blueprint, render_template
|
||||
from flask import request
|
||||
@@ -17,6 +19,7 @@ from admin import model
|
||||
from admin.user_login_check import panel_login_required
|
||||
|
||||
import core.mw as mw
|
||||
import utils.config as utils_config
|
||||
|
||||
# 默认页面
|
||||
blueprint = Blueprint('setting', __name__, url_prefix='/setting', template_folder='../../templates')
|
||||
@@ -25,8 +28,6 @@ blueprint = Blueprint('setting', __name__, url_prefix='/setting', template_folde
|
||||
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():
|
||||
@@ -106,5 +107,61 @@ def set_admin_path():
|
||||
|
||||
|
||||
|
||||
# 设置BasicAuth认证
|
||||
@blueprint.route('/set_basic_auth', endpoint='set_basic_auth', methods=['POST'])
|
||||
@panel_login_required
|
||||
def set_basic_auth():
|
||||
basic_user = request.form.get('basic_user', '').strip()
|
||||
basic_pwd = request.form.get('basic_pwd', '').strip()
|
||||
basic_open = request.form.get('is_open', '').strip()
|
||||
|
||||
__file = mw.getCommonFile()
|
||||
path = __file['basic_auth']
|
||||
|
||||
is_open = True
|
||||
if basic_open == 'false':
|
||||
is_open = False
|
||||
|
||||
if basic_open == 'false':
|
||||
model.setOption('basic_auth', json.dumps({'open':False}))
|
||||
mw.writeLog('面板设置', '设置BasicAuth状态为: %s' % is_open)
|
||||
return mw.returnData(True, '删除BasicAuth成功!')
|
||||
|
||||
if basic_user == '' or basic_pwd == '':
|
||||
return mw.returnData(False, '用户和密码不能为空!')
|
||||
|
||||
salt = mw.getRandomString(6)
|
||||
data = {}
|
||||
data['salt'] = salt
|
||||
data['basic_user'] = mw.md5(basic_user + salt)
|
||||
data['basic_pwd'] = mw.md5(basic_pwd + salt)
|
||||
data['open'] = is_open
|
||||
|
||||
model.setOption('basic_auth', json.dumps(data))
|
||||
mw.writeLog('面板设置', '设置BasicAuth状态为: %s' % is_open)
|
||||
return mw.returnData(True, '设置成功!')
|
||||
|
||||
|
||||
# 默认站点目录
|
||||
@blueprint.route('/set_status_code', endpoint='set_status_code', methods=['POST'])
|
||||
@panel_login_required
|
||||
def set_status_code():
|
||||
status_code = request.form.get('status_code', '').strip()
|
||||
if re.match(r"^\d+$", status_code):
|
||||
status_code = int(status_code)
|
||||
if status_code != 0:
|
||||
if status_code < 100 or status_code > 999:
|
||||
return mw.returnData(False, '状态码范围错误!!')
|
||||
else:
|
||||
return mw.returnData(False, '状态码范围错误!')
|
||||
|
||||
info = utils_config.getUnauthStatus(code=str(status_code))
|
||||
model.setOption('unauthorized_status', str(status_code))
|
||||
mw.writeLog('面板设置', '将未授权响应状态码设置为:{0}:{1}'.format(status_code,info['text']))
|
||||
return mw.returnData(True, '设置成功!')
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
# Author: midoks <midoks@163.com>
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
import json
|
||||
|
||||
from flask import request
|
||||
|
||||
from admin import model
|
||||
@@ -22,9 +24,16 @@ def init_option():
|
||||
|
||||
# 后台面板是否关闭
|
||||
model.setOption('admin_close', 'no')
|
||||
|
||||
# 未认证状态码
|
||||
model.setOption('unauthorized_status', '0')
|
||||
|
||||
# 调式模式,默认关闭
|
||||
model.setOption('debug', 'close')
|
||||
|
||||
# basic auth 配置
|
||||
model.setOption('basic_auth', json.dumps({'open':False}))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -8,18 +8,26 @@
|
||||
# Author: midoks <midoks@163.com>
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
|
||||
from flask import render_template
|
||||
from flask import Response
|
||||
|
||||
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):
|
||||
if not isLogined():
|
||||
return {'status':False, 'msg':'未登录/登录过期'}
|
||||
unauthorized_status = model.getOption('unauthorized_status')
|
||||
if unauthorized_status == '0':
|
||||
return render_template('default/path.html')
|
||||
return Response(status=int(unauthorized_status))
|
||||
|
||||
return func(*args, **kwargs)
|
||||
return wrapper
|
||||
+1
-1
@@ -26,7 +26,7 @@ from version import APP_VERSION, APP_RELEASE, APP_REVISION, APP_SUFFIX
|
||||
DEBUG = False
|
||||
|
||||
# 配置数据库连接池大小。将其设置为0将删除任何限制
|
||||
CONFIG_DATABASE_CONNECTION_POOL_SIZE = 1
|
||||
CONFIG_DATABASE_CONNECTION_POOL_SIZE = 20
|
||||
# 允许溢出超过连接池大小的连接数
|
||||
CONFIG_DATABASE_CONNECTION_MAX_OVERFLOW = 100
|
||||
|
||||
|
||||
+15
-1
@@ -137,12 +137,26 @@ def getDateFromNow(tf_format="%Y-%m-%d %H:%M:%S", time_zone="Asia/Shanghai"):
|
||||
time.tzset()
|
||||
return time.strftime(tf_format, time.localtime())
|
||||
|
||||
|
||||
def getDataFromInt(val):
|
||||
time_format = '%Y-%m-%d %H:%M:%S'
|
||||
time_str = time.localtime(val)
|
||||
return time.strftime(time_format, time_str)
|
||||
|
||||
def getCommonFile():
|
||||
# 统一默认配置文件
|
||||
base_dir = getPanelDir()+'/'
|
||||
data = {
|
||||
'debug' : base_dir+'data/debug.pl', # DEBUG文件
|
||||
'close' : base_dir+'data/close.pl', # 识别关闭面板文件
|
||||
'basic_auth' : base_dir+'data/basic_auth.json', # 面板Basic验证
|
||||
'ipv6' : base_dir+'data/ipv6.pl', # ipv6识别文件
|
||||
'bind_domain' : base_dir+'data/bind_domain.pl', # 面板域名绑定
|
||||
'auth_secret': base_dir+'data/auth_secret.pl', # 二次验证密钥
|
||||
'ssl': base_dir+'ssl/choose.pl', # ssl设置
|
||||
}
|
||||
return data
|
||||
|
||||
|
||||
def toSize(size, middle='') -> str:
|
||||
"""
|
||||
字节单位转换
|
||||
|
||||
@@ -1152,7 +1152,7 @@ function setStatusCode(o){
|
||||
yes:function(index){
|
||||
var loadT = layer.msg("正在设置未认证时的响应状态", { icon: 16, time: 0, shade: [0.3, '#000'] });
|
||||
var status_code = $('select[name="status_code"]').val();
|
||||
$.post('/config/set_status_code', { status_code: status_code }, function (rdata) {
|
||||
$.post('/setting/set_status_code', { status_code: status_code }, function (rdata) {
|
||||
showMsg(rdata.msg, function(){
|
||||
layer.close(index);
|
||||
layer.close(loadT);
|
||||
@@ -1362,7 +1362,7 @@ function setBasicAuth(){
|
||||
$('.save_auth_cfg').click(function(){
|
||||
var basic_user = $('input[name="basic_user"]').val();
|
||||
var basic_pwd = $('input[name="basic_pwd"]').val();
|
||||
$.post('/config/set_basic_auth', {'basic_user':basic_user,'basic_pwd':basic_pwd},function(rdata){
|
||||
$.post('/setting/set_basic_auth', {'basic_user':basic_user,'basic_pwd':basic_pwd},function(rdata){
|
||||
showMsg(rdata.msg, function(){
|
||||
window.location.reload();
|
||||
} ,{icon:rdata.status?1:2}, 2000);
|
||||
|
||||
@@ -121,7 +121,8 @@
|
||||
|
||||
<p class="mtb15">
|
||||
<span class="set-tit text-right" title="BasicAuth认证" style="float: left;">BasicAuth认证</span>
|
||||
<input class="btswitch btswitch-ios" id="cfg_basic_auth" type="checkbox" {{data['basic_auth']}}/>
|
||||
<input class="btswitch btswitch-ios" id="cfg_basic_auth" type="checkbox"
|
||||
{% if data['basic_auth']['open'] %}checked{% endif %}/>
|
||||
<label class="btswitch-btn ml5" for="cfg_basic_auth" style="float: left;margin-top:4px;" onclick="setBasicAuth()"></label>
|
||||
<span class="set-info c7">为面板增加一道基于BasicAuth的认证服务,有效防止面板被扫描</span>
|
||||
</p>
|
||||
@@ -136,8 +137,8 @@
|
||||
|
||||
<p class="mtb15">
|
||||
<span class="set-tit text-right" title="未认证响应状态">未认证响应状态</span>
|
||||
<input name="status_code" class="inputtxt bt-input-text disable" type="text" value="{{data['status_code_msg']}}" disabled>
|
||||
<button type="button" class="btn btn-success btn-sm ml5" data-code="{{data['status_code']}}" onclick="setStatusCode(this)">设置</button>
|
||||
<input name="status_code" class="inputtxt bt-input-text disable" type="text" value="{{data['unauthorized_status']['text']}}" disabled>
|
||||
<button type="button" class="btn btn-success btn-sm ml5" data-code="{{data['unauthorized_status']['code']}}" onclick="setStatusCode(this)">设置</button>
|
||||
<span class="set-info c7">用于在未登录且未正确输入安全入口时的响应,可用于隐藏面板特征</span>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -12,6 +12,31 @@ from admin import model
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
def getUnauthStatus(
|
||||
code: str | None = '0'
|
||||
):
|
||||
code = str(code)
|
||||
data = {}
|
||||
data['code'] = code
|
||||
if code == '0':
|
||||
data['text'] = "默认-安全入口错误提示"
|
||||
elif code == '400':
|
||||
data['text'] = "400-客户端请求错误"
|
||||
elif code == '401':
|
||||
data['text'] = "401-未授权访问"
|
||||
elif code == '403':
|
||||
data['text'] = "403-拒绝访问"
|
||||
elif code == '404':
|
||||
data['text'] = "404-页面不存在"
|
||||
elif code == '408':
|
||||
data['text'] = "408-客户端超时"
|
||||
elif code == '416':
|
||||
data['text'] = "416-无效的请求"
|
||||
else:
|
||||
data['code'] = '0'
|
||||
data['text'] = "默认-安全入口错误提示"
|
||||
return data
|
||||
|
||||
def getGlobalVar():
|
||||
'''
|
||||
获取全局变量
|
||||
@@ -26,6 +51,11 @@ def getGlobalVar():
|
||||
data['site_count'] = model.getSitesCount()
|
||||
data['port'] = mw.getHostPort()
|
||||
|
||||
# 获取未认证状态信息
|
||||
unauthorized_status = model.getOption('unauthorized_status', default='0')
|
||||
data['unauthorized_status'] = getUnauthStatus(code=unauthorized_status)
|
||||
data['basic_auth'] = model.getOptionByJson('basic_auth', default={'open':False})
|
||||
|
||||
# 服务器时间
|
||||
sformat = 'date +"%Y-%m-%d %H:%M:%S %Z %z"'
|
||||
data['systemdate'] = mw.execShell(sformat)[0].strip()
|
||||
|
||||
Reference in New Issue
Block a user