优化项目目录,新增后台设置强制登录功能,优化样式

This commit is contained in:
yangjian 2020-05-08 21:09:53 +08:00
parent 682fb7156b
commit 62d1c6c9e2
15 changed files with 215 additions and 57 deletions

View File

@ -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折叠功能

View File

@ -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')

View File

@ -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

View File

@ -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

View File

@ -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" # 小写字母去除可能干扰的iloz
_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),

View File

@ -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

View File

@ -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(

View File

@ -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':'上传出错'})

View File

@ -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)

17
config/config.ini Normal file
View File

@ -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

View File

@ -2,4 +2,5 @@ django==2.2.12
beautifulsoup4==4.8.2
lxml
pillow==6.2.2
pyppeteer==0.0.25
pyppeteer==0.0.25
loguru==0.4.1

View File

@ -30,11 +30,11 @@
</div>
<div class="layui-form-item">
<label class="layui-form-label">启用注册码</label>
<label class="layui-form-label">全站登录</label>
<div class="layui-input-inline">
<input type="checkbox" name="enable_register_code" lay-skin="switch" lay-text="开启|关闭" {% if enable_register_code %}checked{% endif %}>
<input type="checkbox" name="require_login" lay-skin="switch" lay-text="开启|关闭" {% if require_login %}checked{% endif %}>
</div>
<div class="layui-form-mid layui-word-aux">开启此选项,新用户注册将需要填写注册码</div>
<div class="layui-form-mid layui-word-aux">开启此选项,除注册、登录页面外的所有页面的访问都需要登录</div>
</div>
<div class="layui-form-item">

View File

@ -74,6 +74,9 @@
.layui-laypage .layui-laypage-curr .layui-laypage-em{
background-color: #1E9FFF !important;
}
.layui-form-select dl dd.layui-this{
background-color: #1E9FFF !important;
}
/* HTML预览样式 */
.markdown-body h1{
font-size: 1.7em;

View File

@ -285,7 +285,7 @@
data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
});
//解析Markdown为HTML
layer.load(1);
// layer.load(1);
editormd.markdownToHTML("content", {
htmlDecode : "style,script,iframe",
emoji : true, //emoji表情
@ -300,7 +300,7 @@
atLink : false,//禁用@链接
});
layer.closeAll("loading");
// layer.closeAll("loading");
//为当前页面的目录链接添加蓝色样式
$("nav li a").each(function (i) {
var $me = $(this);

View File

@ -78,7 +78,9 @@
<script src="{% static 'viewerjs/viewer.js' %}"></script>
<script>
var form = layui.form;
var flow = layui.flow;
// 懒加载图片
flow.lazyimg({elem:'img.image-list-i'});
//悬浮显示图片按钮
$(".image-list").mouseover(function(){
$(this).find(".opera-img-btn").show();