diff --git a/class/core/logs_api.py b/class/core/logs_api.py index beb2cdb57..128d9a5dd 100644 --- a/class/core/logs_api.py +++ b/class/core/logs_api.py @@ -173,14 +173,12 @@ class logs_api: def __to_date2(self, date_str): tmp = date_str.split() - s_date = str(tmp[-1]) + '-' + self.__months.get(tmp[1], - tmp[1]) + '-' + tmp[2] + ' ' + tmp[3] + s_date = str(tmp[-1]) + '-' + self.__months.get(tmp[1], tmp[1]) + '-' + tmp[2] + ' ' + tmp[3] return s_date def __to_date3(self, date_str): tmp = date_str.split() - s_date = str(datetime.now().year) + '-' + \ - self.__months.get(tmp[1], tmp[1]) + '-' + tmp[2] + ' ' + tmp[3] + s_date = str(datetime.now().year) + '-' + self.__months.get(tmp[1], tmp[1]) + '-' + tmp[2] + ' ' + tmp[3] return s_date def __to_date4(self, date_str): diff --git a/web/admin/files/__init__.py b/web/admin/files/__init__.py index ca5939136..92bf0283f 100644 --- a/web/admin/files/__init__.py +++ b/web/admin/files/__init__.py @@ -21,8 +21,8 @@ def index(): return render_template('files.html', data={}) # 获取文件内容 -@blueprint.route('/get_body', endpoint='getFileBody', methods=['POST']) -def getFileBody(): +@blueprint.route('/get_body', endpoint='get_file_body', methods=['POST']) +def get_file_body(): path = request.form.get('path', '') if not os.path.exists(path): @@ -55,8 +55,8 @@ def getFileBody(): return mw.returnData(True, 'OK', data) # 获取文件内容 -@blueprint.route('/save_body', endpoint='saveBody', methods=['POST']) -def saveBody(): +@blueprint.route('/save_body', endpoint='save_body', methods=['POST']) +def save_body(): path = request.form.get('path', '') data = request.form.get('data', '') encoding = request.form.get('encoding', '') @@ -79,8 +79,8 @@ def saveBody(): return mw.returnData(False, '文件保存错误:' + str(ex)) # 获取文件内容(最新行数) -@blueprint.route('/get_last_body', endpoint='getFileLastBody', methods=['POST']) -def getFileLastBody(): +@blueprint.route('/get_last_body', endpoint='get_file_last_body', methods=['POST']) +def get_file_last_body(): path = request.form.get('path', '') line = request.form.get('line', '100') @@ -94,6 +94,29 @@ def getFileLastBody(): return mw.returnData(False, '无法正确读取文件!' + str(ex)) +# 获取文件列表 +@blueprint.route('/get_dir', endpoint='get_dir', methods=['POST']) +def get_dir(): + path = request.form.get('path', '') + if not os.path.exists(path): + path = mw.getRootDir() + "/wwwroot" + search = request.args.get('search', '').strip().lower() + search_all = request.args.get('all', '').strip().lower() + page = request.args.get('p', '1').strip().lower() + row = request.args.get('row', '10') + order = request.form.get('order', '') + + print(path,search,search_all,page,row,order) + if not os.path.exists(path): + return mw.returnData(False, '文件不存在', (path,)) + + try: + data = mw.getLastLine(path, int(line)) + return mw.returnData(True, 'OK', data) + except Exception as ex: + return mw.returnData(False, '无法正确读取文件!' + str(ex)) + + diff --git a/web/admin/logs/__init__.py b/web/admin/logs/__init__.py index 4602416c4..5e343c67d 100644 --- a/web/admin/logs/__init__.py +++ b/web/admin/logs/__init__.py @@ -10,9 +10,15 @@ from flask import Blueprint, render_template +from flask import request from admin.user_login_check import panel_login_required +from admin.model import db, Logs +import core.mw as mw +import utils.adult_log as adult_log + +# 日志页面 blueprint = Blueprint('logs', __name__, url_prefix='/logs', template_folder='../../templates') @blueprint.route('/index', endpoint='index') def index(): @@ -23,10 +29,60 @@ def index(): @blueprint.route('/get_log_list', endpoint='get_log_list', methods=['POST']) @panel_login_required def get_log_list(): - return [] + p = request.form.get('p', '1').strip() + size = request.form.get('limit', '10').strip() + search = request.form.get('search', '').strip() + + count = Logs.query.filter_by().count() + if search != '': + pagination = Logs.query.filter_by(Logs.type.like(search) or Logs.log.like(search)).paginate(page=int(p), per_page=int(size)) + else: + pagination = Logs.query.filter_by().paginate(page=int(p), per_page=int(size)) + + rows = [] + for item in pagination.items: + t = {} + t['id'] = item.id + t['type'] = item.type + t['log'] = item.log + t['uid'] = item.uid + t['add_time'] = item.add_time + rows.append(t) + + data = {} + data['data'] = rows + data['page'] = mw.getPage({'count':count,'tojs':'getLogs','p':p}) + return data + +# 日志清空 +@blueprint.route('/del_panel_logs', endpoint='del_panel_logs', methods=['POST']) +@panel_login_required +def del_panel_logs(): + db.session.query(Logs).filter(Logs.id > 0).delete() + # db.session.execute('truncate table logs;') + db.session.commit() + mw.writeLog('面板设置', '面板操作日志已清空!') + return mw.returnData(True, '面板操作日志已清空!') -# 审计日志列表 +# 系统审计日志列表 @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 + logs_file = adult_log.getAuditLogsFiles() + return logs_file + +# 系统审计日志列表 +@blueprint.route('/get_audit_file', endpoint='get_audit_file', methods=['POST']) +@panel_login_required +def get_audit_file(): + name = request.form.get('log_name', '').strip() + return adult_log.getAuditLogsName(name) + + + + + + + + + diff --git a/web/admin/model/logs.py b/web/admin/model/logs.py new file mode 100644 index 000000000..8f7eef5e1 --- /dev/null +++ b/web/admin/model/logs.py @@ -0,0 +1,26 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + + +import json + +from admin.model import db, Logs +import core.mw as mw + +def add(type, log, uid=1) -> str: + add_time = mw.formatDate() + add_logs = Logs( + uid=uid, + log=log, + type=type, + add_time=add_time) + db.session.add(add_logs) + db.session.commit() + return True \ No newline at end of file diff --git a/web/core/mw.py b/web/core/mw.py index 5d878e4ad..d53ac07bb 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -453,4 +453,29 @@ def isNumber(s): return False +def writeLog(stype, msg, args=()): + # 写日志 + uid = 1 + try: + from flask import session + if 'uid' in session: + uid = session['uid'] + except Exception as e: + print(str(e)) + # pass + # writeFileLog(getTracebackInfo()) + return writeDbLog(stype, msg, args, uid) + +def writeDbLog(stype, msg, args=(), uid=1): + try: + import admin.model.logs as logs + + format_msg = getInfo(msg, args) + logs.add(stype, format_msg, uid) + # mdate = time.strftime('%Y-%m-%d %X', time.localtime()) + return True + except Exception as e: + print("writeDbLog:"+str(e)) + return False + diff --git a/web/static/app/logs.js b/web/static/app/logs.js index c61f3d7b1..18f46c210 100644 --- a/web/static/app/logs.js +++ b/web/static/app/logs.js @@ -185,7 +185,7 @@ function getLogs(page,search) { " + data.data[i].id + "\ " + data.data[i].type + "\ " + data.data[i].log + "\ - " + data.data[i].addtime + "\ + " + data.data[i].add_time + "\ "; } $("#operationLog tbody").html(body); diff --git a/web/templates/default/logs.html b/web/templates/default/logs.html index ac3e0b876..1a08635c2 100644 --- a/web/templates/default/logs.html +++ b/web/templates/default/logs.html @@ -145,7 +145,7 @@
diff --git a/web/utils/adult_log.py b/web/utils/adult_log.py new file mode 100644 index 000000000..f1b4e4a80 --- /dev/null +++ b/web/utils/adult_log.py @@ -0,0 +1,356 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +from datetime import datetime + +import core.mw as mw + +__months = {'Jan': '01', 'Feb': '02', 'Mar': '03', 'Apr': '04', 'May': '05', 'Jun': '06', + 'Jul': '07', 'Aug': '08', 'Sep': '09', 'Sept': '09', 'Oct': '10', 'Nov': '11', 'Dec': '12'} + +def getAuditLogsFiles(): + log_dir = '/var/log' + log_files = [] + for log_file in os.listdir(log_dir): + log_suffix = log_file.split('.')[-1:] + if log_suffix[0] in ['gz', 'xz', 'bz2', 'asl']: + continue + + if log_file in ['.', '..']: + continue + + filename = os.path.join(log_dir, log_file) + if not os.path.exists(filename): + continue + + if os.path.isfile(filename): + file_size = os.path.getsize(filename) + if not file_size: + continue + + tmp = { + 'name': log_file, + 'size': file_size, + 'log_file': filename, + 'title': getLogsTitle(log_file), + 'uptime': os.path.getmtime(filename) + } + + log_files.append(tmp) + else: + for next_name in os.listdir(filename): + if next_name[-3:] in ['.gz', '.xz']: + continue + next_file = os.path.join(filename, next_name) + if not os.path.isfile(next_file): + continue + file_size = os.path.getsize(next_file) + if not file_size: + continue + log_name = '{}/{}'.format(log_file, next_name) + tmp = { + 'name': log_name, + 'size': file_size, + 'log_file': next_file, + 'title': getLogsTitle(log_name), + 'uptime': os.path.getmtime(next_file) + } + log_files.append(tmp) + log_files = sorted(log_files, key=lambda x: x['name'], reverse=True) + return log_files + +def __to_date2(date_str): + tmp = date_str.split() + s_date = str(tmp[-1]) + '-' + __months.get(tmp[1], tmp[1]) + '-' + tmp[2] + ' ' + tmp[3] + return s_date + +def __to_date3(date_str): + tmp = date_str.split() + s_date = str(datetime.now().year) + '-' + __months.get(tmp[1], tmp[1]) + '-' + tmp[2] + ' ' + tmp[3] + return s_date + +def __to_date4(date_str): + tmp = date_str.split() + s_date = str(datetime.now().year) + '-' + __months.get(tmp[0], tmp[0]) + '-' + tmp[1] + ' ' + tmp[2] + return s_date + +def parseAuditFile(log_name, result): + log_list = [] + is_string = True + for _line in result.split("\n"): + if not _line.strip(): + continue + if log_name.find('sa/sa') == -1: + if _line[:3] in __months: + _msg = _line[16:] + _tmp = _msg.split(": ") + _act = '' + if len(_tmp) > 1: + _act = _tmp[0] + _msg = _tmp[1] + else: + _msg = _tmp[0] + _line = { + "时间": __to_date4(_line[:16].strip()), + "角色": _act, + "事件": _msg + } + is_string = False + elif _line[:2] in ['19', '20', '21', '22', '23', '24']: + _msg = _line[19:] + _tmp = _msg.split(" ") + _act = _tmp[1] + _msg = ' '.join(_tmp[2:]) + _line = { + "时间": _line[:19].strip(), + "角色": _act, + "事件": _msg + } + is_string = False + elif log_name.find('alternatives') == 0: + _tmp = _line.split(": ") + _last = _tmp[0].split(" ") + _act = _last[0] + _msg = ' '.join(_tmp[1:]) + _line = { + "时间": ' '.join(_last[1:]).strip(), + "角色": _act, + "事件": _msg + } + is_string = False + else: + if not is_string: + if type(_line) != dict: + continue + + log_list.append(_line) + return log_list + +def getAuditLast(self, log_name): + # 获取日志 + cmd = '''LANG=en_US.UTF-8 last -n 200 -x -f {} |grep -v 127.0.0.1|grep -v " begins"'''.format( + '/var/log/' + log_name) + result = mw.execShell(cmd) + lastlog_list = [] + for _line in result[0].split("\n"): + if not _line: + continue + tmp = {} + sp_arr = _line.split() + tmp['用户'] = sp_arr[0] + if sp_arr[0] == 'runlevel': + tmp['来源'] = sp_arr[4] + tmp['端口'] = ' '.join(sp_arr[1:4]) + tmp['时间'] = __to_date3(' '.join(sp_arr[5:])) + ' ' + ' '.join(sp_arr[-2:]) + elif sp_arr[0] in ['reboot', 'shutdown']: + tmp['来源'] = sp_arr[3] + tmp['端口'] = ' '.join(sp_arr[1:3]) + if sp_arr[-3] == '-': + tmp['时间'] = __to_date3( + ' '.join(sp_arr[4:])) + ' ' + ' '.join(sp_arr[-3:]) + else: + tmp['时间'] = __to_date3( + ' '.join(sp_arr[4:])) + ' ' + ' '.join(sp_arr[-2:]) + elif sp_arr[1] in ['tty1', 'tty', 'tty2', 'tty3', 'hvc0', 'hvc1', 'hvc2'] or len(sp_arr) == 9: + tmp['来源'] = '' + tmp['端口'] = sp_arr[1] + tmp['时间'] = __to_date3(' '.join(sp_arr[2:])) + ' ' + ' '.join(sp_arr[-3:]) + else: + tmp['来源'] = sp_arr[2] + tmp['端口'] = sp_arr[1] + tmp['时间'] = __to_date3(' '.join(sp_arr[3:])) + ' ' + ' '.join(sp_arr[-3:]) + + # tmp['_line'] = _line + lastlog_list.append(tmp) + # lastlog_list = sorted(lastlog_list,key=lambda x:x['时间'],reverse=True) + return mw.returnData(True, 'ok!', lastlog_list) + +def getAuditLastLog(self): + cmd = '''LANG=en_US.UTF-8 lastlog|grep -v Username''' + result = mw.execShell(cmd) + lastlog_list = [] + for _line in result[0].split("\n"): + if not _line: + continue + tmp = {} + sp_arr = _line.split() + tmp['用户'] = sp_arr[0] + # tmp['_line'] = _line + if _line.find('Never logged in') != -1: + tmp['最后登录时间'] = '0' + tmp['最后登录来源'] = '-' + tmp['最后登录端口'] = '-' + lastlog_list.append(tmp) + continue + tmp['最后登录来源'] = sp_arr[2] + tmp['最后登录端口'] = sp_arr[1] + tmp['最后登录时间'] = __to_date2(' '.join(sp_arr[3:])) + + lastlog_list.append(tmp) + lastlog_list = sorted(lastlog_list, key=lambda x: x['最后登录时间'], reverse=True) + for i in range(len(lastlog_list)): + if lastlog_list[i]['最后登录时间'] == '0': + lastlog_list[i]['最后登录时间'] = '从未登录过' + return mw.returnData(True, 'ok!', lastlog_list) + +def parseAuditFileLine(log_name, _line): + is_string = True + if log_name.find('sa/sa') == -1: + if _line[:3] in __months: + _msg = _line[16:] + _tmp = _msg.split(": ") + # print('tmp: ',_tmp) + _act = '' + if len(_tmp) > 1: + _act = _tmp[0] + _msg = _tmp[1] + else: + _msg = _tmp[0] + _line = { + "时间": __to_date4(_line[:16].strip()), + "角色": _act, + "事件": _msg + } + is_string = False + elif _line[:2] in ['19', '20', '21', '22', '23', '24']: + # print(_line) + _msg = _line[19:] + _tmp = _msg.split(" ") + _act = _tmp[1] + _msg = ' '.join(_tmp[2:]) + _line = { + "时间": _line[:19].strip(), + "角色": _act, + "事件": _msg + } + is_string = False + elif log_name.find('alternatives') == 0: + _tmp = _line.split(": ") + _last = _tmp[0].split(" ") + _act = _last[0] + _msg = ' '.join(_tmp[1:]) + _line = { + "时间": ' '.join(_last[1:]).strip(), + "角色": _act, + "事件": _msg + } + is_string = False + else: + if not is_string: + if type(_line) != dict: + return _line + return _line + +def parseAuditFile(log_name, result): + log_list = [] + for _line in result.split("\n"): + if not _line.strip(): + continue + try: + _line = parseAuditFileLine(log_name, _line) + except Exception as e: + print(str(e)) + + log_list.append(_line) + return log_list + +def getAuditLogsName(log_name): + if log_name in ['wtmp', 'btmp', 'utmp'] or log_name.find('wtmp') == 0 or log_name.find('btmp') == 0 or log_name.find('utmp') == 0: + return getAuditLast(log_name) + + if log_name.find('lastlog') == 0: + return getAuditLastLog() + + if log_name.find('sa/sa') == 0: + if log_name.find('sa/sar') == -1: + return mw.execShell("sar -f /var/log/{}".format(log_name))[0] + log_dir = '/var/log' + log_file = log_dir + '/' + log_name + if not os.path.exists(log_file): + return mw.returnData(False, '日志文件不存在!') + result = mw.getLastLine(log_file, 100) + try: + log_list = parseAuditFile(log_name, result) + _string = [] + _dict = [] + _list = [] + for _line in log_list: + if isinstance(_line, str): + _string.append(_line.strip()) + elif isinstance(_line, dict): + _dict.append(_line) + elif isinstance(_line, list): + _list.append(_line) + else: + continue + _str_len = len(_string) + _dict_len = len(_dict) + _list_len = len(_list) + if _str_len > _dict_len + _list_len: + return "\n".join(_string) + elif _dict_len > _str_len + _list_len: + return mw.returnData(True, 'ok!', _dict) + else: + return mw.returnData(True, 'ok!', _list) + + except Exception as e: + # print(mw.getTracebackInfo()) + return mw.returnData(True, 'ok!', result) + +def getLogsTitle(log_name): + log_name = log_name.replace('.1', '') + if log_name in ['mw-update.log']: + return '面板更新日志' + if log_name in ['mw-install.log']: + return '面板安装日志' + if log_name in ['auth.log', 'secure'] or log_name.find('auth.') == 0: + return '授权日志' + if log_name in ['dmesg'] or log_name.find('dmesg') == 0: + return '内核缓冲区日志' + if log_name in ['syslog'] or log_name.find('syslog') == 0: + return '系统日志' + if log_name in ['rsyncd.log']: + return '远程同步日志' + if log_name in ['btmp']: + return '失败的登录记录' + if log_name in ['utmp', 'wtmp']: + return '登录和重启记录' + if log_name in ['lastlog']: + return '用户最后登录' + if log_name in ['yum.log']: + return 'yum包管理器日志' + if log_name in ['anaconda.log']: + return 'Anaconda日志' + if log_name in ['dpkg.log']: + return 'dpkg日志' + if log_name in ['daemon.log']: + return '系统后台守护进程日志' + if log_name in ['boot.log']: + return '启动日志' + if log_name in ['kern.log']: + return '内核日志' + if log_name in ['maillog', 'mail.log']: + return '邮件日志' + if log_name.find('Xorg') == 0: + return 'Xorg日志' + if log_name in ['cron.log']: + return '定时任务日志' + if log_name in ['alternatives.log']: + return '更新替代信息' + if log_name in ['debug']: + return '调试信息' + if log_name.find('apt') == 0: + return 'apt-get相关日志' + if log_name.find('installer') == 0: + return '系统安装相关日志' + if log_name in ['messages']: + return '综合日志' + return '{}日志'.format(log_name.split('.')[0]) \ No newline at end of file