diff --git a/CHANGES.md b/CHANGES.md index 9407b30..48ffabb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,15 @@ ## 版本更新记录 -### v.0.5.0 2020-05-02 +### v0.5.1 2020-05-08 + +- 优化个人中心交互体验; +- 添加站点异常报错的日志功能; +- 优化项目结构; +- 修改文档页面新增删除文档功能; +- 优化首页样式排版; +- 后台支持设置全站强制登录; + +### v0.5.0 2020-05-02 - MrDoc正式中文取名:觅道文档; - 文档编辑器添加Markdown折叠功能; diff --git a/MrDoc/__init__.py b/MrDoc/__init__.py index e69de29..a8fb230 100644 --- a/MrDoc/__init__.py +++ b/MrDoc/__init__.py @@ -0,0 +1,10 @@ +from loguru import logger +from django.conf import settings +import os + +LOG_DIR = os.path.join(settings.BASE_DIR,'log') + +if os.path.exists(LOG_DIR) is False: + os.makedirs(LOG_DIR) + +logger.add(os.path.join(LOG_DIR,'{time}.log'),rotation='1 days',retention='30 days',encoding='utf-8') \ No newline at end of file diff --git a/MrDoc/settings.py b/MrDoc/settings.py index 2485796..3116de0 100644 --- a/MrDoc/settings.py +++ b/MrDoc/settings.py @@ -12,10 +12,16 @@ https://docs.djangoproject.com/en/2.1/ref/settings/ """ import os +from configparser import ConfigParser # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +# 配置文件和数据文件目录 +CONFIG_DIR = os.path.join(BASE_DIR, 'config') +CONFIG = ConfigParser() +CONFIG.read(os.path.join(CONFIG_DIR,'config.ini'),encoding='utf-8') + # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/ @@ -24,9 +30,9 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) SECRET_KEY = '5&71mt9@^58zdg*_!t(x6g14q*@84d%ptr%%s6e0l50zs0we3d' # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = False +DEBUG = CONFIG.getboolean('site','debug') -VERSIONS = '0.5.0' +VERSIONS = '0.5.1' ALLOWED_HOSTS = ['*'] @@ -54,6 +60,7 @@ MIDDLEWARE = [ 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'app_admin.middleware.require_login_middleware.RequiredLoginMiddleware', ] ROOT_URLCONF = 'MrDoc.urls' @@ -62,8 +69,8 @@ TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [ - os.path.join(BASE_DIR,'template') - ], + os.path.join(BASE_DIR,'template') + ], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ @@ -81,17 +88,37 @@ WSGI_APPLICATION = 'MrDoc.wsgi.application' # Database -# https://docs.djangoproject.com/en/2.1/ref/settings/#databases +# 数据库配置 +# https://docs.djangoproject.com/en/2.2/ref/settings/#databases -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), - 'OPTIONS':{ - 'timeout':20, +DATABASE_MAP = { + 'sqlite':'django.db.backends.sqlite3', + 'mysql':'django.db.backends.mysql', + 'postgresql':'django.db.backends.postgresql_psycopg2', + 'oracle':'django.db.backends.oracle', +} + +if CONFIG['database']['engine'] == 'sqlite': + DATABASES = { + 'default': { + 'ENGINE': DATABASE_MAP[CONFIG['database']['engine']], + 'NAME': os.path.join(CONFIG_DIR, 'db.sqlite3'), + 'OPTIONS':{ + 'timeout':20, + } + } + } +else: + DATABASES = { + 'default': { + 'ENGINE': DATABASE_MAP[CONFIG['database']['engine']], + 'NAME': CONFIG['database']['name'], + 'USER': CONFIG['database']['user'], + 'PASSWORD': CONFIG['database']['password'], + 'HOST': CONFIG['database']['host'], + 'PORT': CONFIG['database']['port'], } } -} # Password validation diff --git a/README.md b/README.md index c89a849..4cc629a 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ - 支持**文集协作**功能,一个文集可以拥有一个创建者和多个协作者,可灵活选择协作权限; - 支持**文档历史版本**功能,可以查看和对比历史版本与现有版本的差异,恢复某个历史版本为当前版本; -当前版本为:**v0.5.0**,版本发布时间为**2020-05-02** +当前版本为:**v0.5.1**,版本发布时间为**2020-05-08** 完整更新记录详见:[CHANGES.md](./CHANGES.md) @@ -55,7 +55,7 @@ `MrDoc`基于`Python`语言的`Django Web`框架配合前端的`LayUI`、`JQuery`等库进行开发。 -`MrDoc`在`Python3.6` + `Django 2.2`上进行开发,并且在Django 2.1、2.2和Python3.5、3.6、3.7上测试运行良好,在其他环境下运行MrDoc不排除有未知的异常。。 +`MrDoc`在`Python 3.6` + `Django 2.2`上进行开发,并且在Django 2.1、2.2和Python3.5、3.6、3.7上测试运行良好,在其他环境下运行MrDoc不排除有未知的异常。。 ## 简明安装教程 @@ -70,43 +70,32 @@ pip install -r requirements.txt 如果有配置其他数据库的需求,请首先按照Django官方的[数据库支持说明](https://docs.djangoproject.com/zh-hans/2.2/ref/databases/),安装特定数据库的Python绑定库, -然后在/MrDoc/MrDoc目录下打开settings.py文件,在约80行的位置,将如下代码: +然后在/MrDoc/config目录下打开conig.ini文件,根据自己的数据库信息进行修改: ```python -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), - } -} +# engine,指定数据库类型,接受sqlite、mysql、oracle、postgresql +engine = sqlite +# name表示数据库的名称 +# name = db_name +# user表示数据库用户名 +# user = db_user +# password表示数据库用户密码 +# password = db_pwd +# host表示数据库主机地址 +# host = db_host +# port表示数据库端口 +# port = db_port ``` 按照自己数据库的信息,将其修改如下格式,下面以MySQL为例: -```python -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.mysql', # 使用的数据库后端 - 'NAME': 'mrdoc', # 数据库名 - 'USER':'root', # 数据库用户 - 'PASSWORD':'123456789', # 数据库用户密码 - 'HOST':'', # 数据库主机地址 - 'PORT':'3306', # 数据库端口 - } -} -``` - ### 3、初始化数据库 在安装完所需的第三方库并配置好数据库信息之后,我们需要对数据库进行初始化。 -在项目路径下打开命令行界面,运行如下命令生成数据库迁移: +在项目路径下打开命令行界面,运行如下命令生成和执行数据库迁移: ``` -python manage.py makemigrations -``` -接着,运行如下命令执行数据库迁移: -``` -python manage.py migrate +python manage.py makemigrations && python manage.py migrate ``` 执行完毕之后,数据库就初始化完成了。 @@ -120,14 +109,11 @@ python manage.py createsuperuser 在完成上述步骤之后,即可运行使用MrDoc。 在测试环境中,可以使用Django自带的服务器运行MrDoc,其命令为: + ``` python manage.py runserver ``` -## 使用说明文档 - -详见MrDoc使用文档: [http://mrdoc.zmister.com](http://mrdoc.zmister.com) - ## 问题提交和反馈 ### 1、提交issue diff --git a/app_admin/check_code.py b/app_admin/check_code.py index 9760f3f..281b53e 100644 --- a/app_admin/check_code.py +++ b/app_admin/check_code.py @@ -2,13 +2,18 @@ # coding:utf-8 # 生成验证码图片 import random +import os from PIL import Image, ImageDraw, ImageFont, ImageFilter +from django.conf import settings _letter_cases = "abcdefghjkmnpqrstuvwxy" # 小写字母,去除可能干扰的i,l,o,z _upper_cases = _letter_cases.upper() # 大写字母 _numbers = ''.join(map(str, range(3, 10))) # 数字 init_chars = ''.join((_letter_cases, _upper_cases, _numbers)) +static_path = os.path.join(settings.BASE_DIR,"static") # 静态文件路径 +font_path = os.path.join(static_path,"MONACO.TTF") # 字体路径 + def create_validate_code(size=(120, 30), chars=init_chars, img_type="GIF", @@ -16,7 +21,7 @@ def create_validate_code(size=(120, 30), bg_color=(255, 255, 255), fg_color=(0, 0, 255), font_size=18, - font_type="MONACO.TTF", + font_type=font_path, length=4, draw_lines=True, n_line=(1, 2), diff --git a/app_admin/middleware/require_login_middleware.py b/app_admin/middleware/require_login_middleware.py new file mode 100644 index 0000000..3d8ace8 --- /dev/null +++ b/app_admin/middleware/require_login_middleware.py @@ -0,0 +1,50 @@ +# coding:utf-8 +# @文件: require_login_middleware.py +# @创建者:州的先生 +# #日期:2020/5/8 +# 博客地址:zmister.com + +from app_admin.models import SysSetting +from django.contrib.auth.decorators import login_required +import re + + +class RequiredLoginMiddleware(): + def __init__(self, get_response): + self.get_response = get_response + # 设置排除URL + compile_tuple = (r'/user/login(.*)$', r'/user/logout(.*)$', r'/user/register(.*)$',r'/user/check_code(.*)$') + self.exceptions = tuple(re.compile(url) for url in compile_tuple) + + def __call__(self, request): + response = self.get_response(request) + return response + + def process_view(self, request, view_func, view_args, view_kwargs): + # 登陆用户不理会 + if request.user.is_authenticated: + return None + + try: + # 获取数据库的设置值 + data = SysSetting.objects.get(name='require_login').value + # 如果设置值为on,表示开启了验证 + if data == 'on': + is_exceptions = False + # 遍历排除列表 + for url in self.exceptions: + # 如果当前url匹配到排除列表,不理会 + if url.match(request.path): + # print('排除URL:',request.path) + is_exceptions = True + if is_exceptions: + return None + else: + # print("验证URL:",request.path) + return login_required(view_func)(request, *view_args, **view_kwargs) + # 否则,不理会 + else: + return None + except: + # 如果查询异常,说明数据库无此设置值,不理会 + return None \ No newline at end of file diff --git a/app_admin/views.py b/app_admin/views.py index 8b58400..764a3e8 100644 --- a/app_admin/views.py +++ b/app_admin/views.py @@ -13,6 +13,7 @@ from app_doc.models import * from app_admin.models import * from app_admin.utils import * import traceback +from loguru import logger # 返回验证码图片 @@ -30,12 +31,14 @@ def check_code(request): except Exception as e: if settings.DEBUG: print(traceback.print_exc()) + logger.exception("生成验证码图片异常") return HttpResponse("请求异常:{}".format(repr(e))) # 登录视图 def log_in(request): if request.method == 'GET': + # 登录用户访问登录页面自动跳转到首页 if request.user.is_authenticated: return redirect('/') else: @@ -62,12 +65,15 @@ def log_in(request): except Exception as e: if settings.DEBUG: print(traceback.print_exc()) + logger.exception("登录异常") return HttpResponse('请求出错') # 注册视图 @open_register +@logger.catch() def register(request): + # 如果登录用户访问注册页面,跳转到首页 if request.user.is_authenticated: return redirect('/') else: @@ -146,6 +152,7 @@ def log_out(request): except Exception as e: if settings.DEBUG: print(traceback.print_exc()) + logger.exception("注销异常") return redirect(request.META['HTTP_REFERER']) @@ -175,11 +182,13 @@ def forget_pwd(request): except Exception as e: if settings.DEBUG: print(traceback.print_exc()) + logger.exception("修改密码异常") errormsg = "验证码错误" return render(request,'forget_pwd.html',locals()) # 发送电子邮箱验证码 +@logger.catch() def send_email_vcode(request): if request.method == 'POST': email = request.POST.get('email',None) @@ -205,9 +214,12 @@ def send_email_vcode(request): else: return JsonResponse({'status':False,'data':'电子邮箱不存在!'}) + else: + return JsonResponse({'status':False,'data':'方法错误'}) # 管理员后台首页 - 用户管理 @superuser_only +@logger.catch() def admin_user(request): if request.method == 'GET': # user_list = User.objects.all() @@ -235,10 +247,13 @@ def admin_user(request): } table_data.append(item) return JsonResponse({'status':True,'data':table_data}) + else: + return JsonResponse({'status':False,'data':'方法错误'}) # 管理员后台首页 - 创建用户 @superuser_only +@logger.catch() def admin_create_user(request): if request.method == 'POST': username = request.POST.get('username','') # 接收用户名参数 @@ -263,6 +278,7 @@ def admin_create_user(request): # 管理员后台 - 修改密码 @superuser_only +@logger.catch() def admin_change_pwd(request): if request.method == 'POST': try: @@ -288,6 +304,7 @@ def admin_change_pwd(request): # 管理员后台 - 删除用户 @superuser_only +@logger.catch() def admin_del_user(request): if request.method == 'POST': try: @@ -303,6 +320,7 @@ def admin_del_user(request): # 管理员后台 - 文集管理 @superuser_only +@logger.catch() def admin_project(request): if request.method == 'GET': search_kw = request.GET.get('kw','') @@ -334,6 +352,7 @@ def admin_project(request): # 管理员后台 - 修改文集权限 @superuser_only +@logger.catch() def admin_project_role(request,pro_id): pro = Project.objects.get(id=pro_id) if request.method == 'GET': @@ -368,6 +387,7 @@ def admin_project_role(request,pro_id): # 管理员后台 - 文档管理 @superuser_only +@logger.catch() def admin_doc(request): if request.method == 'GET': kw = request.GET.get('kw','') @@ -397,6 +417,7 @@ def admin_doc(request): # 管理员后台 - 文档模板管理 @superuser_only +@logger.catch() def admin_doctemp(request): if request.method == 'GET': kw = request.GET.get('kw','') @@ -426,6 +447,7 @@ def admin_doctemp(request): # 管理员后台 - 注册邀请码管理 @superuser_only +@logger.catch() def admin_register_code(request): # 返回注册邀请码管理页面 if request.method == 'GET': @@ -482,6 +504,7 @@ def admin_register_code(request): # 普通用户修改密码 @login_required() +@logger.catch() def change_pwd(request): if request.method == 'POST': try: @@ -506,6 +529,7 @@ def change_pwd(request): # 管理员后台 - 应用设置 @superuser_only +@logger.catch() def admin_setting(request): email_settings = SysSetting.objects.filter(types="email") if email_settings.count() == 6: @@ -522,6 +546,7 @@ def admin_setting(request): # 基础设置 if types == 'basic': close_register = request.POST.get('close_register',None) # 禁止注册 + require_login = request.POST.get('require_login',None) # 全站登录 static_code = request.POST.get('static_code',None) # 统计代码 ad_code = request.POST.get('ad_code',None) # 广告位1 ad_code_2 = request.POST.get('ad_code_2',None) # 广告位2 @@ -530,9 +555,14 @@ def admin_setting(request): enable_register_code = request.POST.get('enable_register_code',None) # 注册邀请码 enable_project_report = request.POST.get('enable_project_report',None) # 文集导出 # 更新开放注册状态 + SysSetting.objects.update_or_create( + name='require_login', + defaults={'value':require_login,'types':'basic'} + ) + # 更新全站登录状态 SysSetting.objects.update_or_create( name='close_register', - defaults={'value':close_register,'types':'basic'} + defaults={'value': close_register, 'types': 'basic'} ) # 更新统计代码状态 SysSetting.objects.update_or_create( diff --git a/app_api/views.py b/app_api/views.py index ed19ecc..60aea4e 100644 --- a/app_api/views.py +++ b/app_api/views.py @@ -10,6 +10,7 @@ import time,hashlib import traceback,json from django.conf import settings from app_doc.util_upload_img import upload_generation_dir,base_img_upload +from loguru import logger # MrDoc 基于用户的Token访问API模块 @@ -22,6 +23,10 @@ def manage_token(request): token = UserToken.objects.get(user=request.user).token # 查询用户Token except ObjectDoesNotExist: token = '你还没有生成过Token!' + except: + if settings.DEBUG: + print(traceback.print_exc()) + logger.exception("Token管理页面异常") return render(request,'app_api/manage_token.html',locals()) elif request.method == 'POST': try: @@ -40,6 +45,7 @@ def manage_token(request): except: if settings.DEBUG: print(traceback.print_exc()) + logger.exception("用户Token生成异常") return JsonResponse({'status':False,'data':'生成出错,请重试!'}) @@ -61,6 +67,11 @@ def get_projects(request): return JsonResponse({'status':True,'data':project_list}) except ObjectDoesNotExist: return JsonResponse({'status':False,'data':'token无效'}) + except: + if settings.DEBUG: + print(traceback.print_exc()) + logger.exception("token获取文集异常") + return JsonResponse({'status':False,'data':'系统异常'}) # 新建文档 @@ -89,7 +100,11 @@ def create_doc(request): return JsonResponse({'status':False,'data':'非法请求'}) except ObjectDoesNotExist: return JsonResponse({'status': False, 'data': 'token无效'}) - + except: + if settings.DEBUG: + print(traceback.print_exc()) + logger.exception("token创建文档异常") + return JsonResponse({'status':False,'data':'系统异常'}) # 上传图片 @csrf_exempt @@ -113,4 +128,5 @@ def upload_img(request): except: if settings.DEBUG: print(traceback.print_exc()) + logger.exception("token上传图片异常") return JsonResponse({'success':0,'data':'上传出错'}) \ No newline at end of file diff --git a/app_doc/views.py b/app_doc/views.py index 066f448..1d52e58 100644 --- a/app_doc/views.py +++ b/app_doc/views.py @@ -10,6 +10,7 @@ from app_doc.models import Project,Doc,DocTemp from django.contrib.auth.models import User from django.db.models import Q from django.db import transaction +from loguru import logger import datetime import traceback import re @@ -23,7 +24,8 @@ def validateTitle(title): new_title = re.sub(rstr, "_", title) # 替换为下划线 return new_title - +# 文集列表(首页) +@logger.catch('文集列表获取异常') def project_list(request): kw = request.GET.get('kw','') # 搜索词 sort = request.GET.get('sort',0) # 排序,0表示按时间升序排序,1表示按时间降序排序,默认为0 @@ -1411,7 +1413,7 @@ def manage_image(request): image_list = Image.objects.filter(user=request.user,group_id=None) # 查询指定分组的图片 else: image_list = Image.objects.filter(user=request.user,group_id=g_id) # 查询指定分组的图片 - paginator = Paginator(image_list, 20) + paginator = Paginator(image_list, 18) page = request.GET.get('page', 1) try: images = paginator.page(page) diff --git a/config/config.ini b/config/config.ini new file mode 100644 index 0000000..2e2388d --- /dev/null +++ b/config/config.ini @@ -0,0 +1,17 @@ +[site] +# True表示开启站点调试模式,False表示关闭站点调试模式 +debug = False + +[database] +# engine,指定数据库类型,接受sqlite、mysql、oracle、postgresql +engine = sqlite +# name表示数据库的名称 +# name = db_name +# user表示数据库用户名 +# user = db_user +# password表示数据库用户密码 +# password = db_pwd +# host表示数据库主机地址 +# host = db_host +# port表示数据库端口 +# port = db_port \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 422d9d7..fce411c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,5 @@ django==2.2.12 beautifulsoup4==4.8.2 lxml pillow==6.2.2 -pyppeteer==0.0.25 \ No newline at end of file +pyppeteer==0.0.25 +loguru==0.4.1 \ No newline at end of file diff --git a/template/app_admin/admin_setting.html b/template/app_admin/admin_setting.html index 4449a93..6a70019 100644 --- a/template/app_admin/admin_setting.html +++ b/template/app_admin/admin_setting.html @@ -30,11 +30,11 @@