diff --git a/CHANGES.md b/CHANGES.md index 48ffabb..c3f1a89 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,13 @@ ## 版本更新记录 +### v0.5.2 + +- 添加Chrome内核浏览器扩展 +- 添加API访问接口; +- 优化配置路径; +- 增强代码安全性; +- 优化个人中心图片、文档模板的默认排序; + ### v0.5.1 2020-05-08 - 优化个人中心交互体验; diff --git a/MrDoc/__init__.py b/MrDoc/__init__.py index a8fb230..e69de29 100644 --- a/MrDoc/__init__.py +++ b/MrDoc/__init__.py @@ -1,10 +0,0 @@ -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 3116de0..4ac9a24 100644 --- a/MrDoc/settings.py +++ b/MrDoc/settings.py @@ -13,6 +13,7 @@ https://docs.djangoproject.com/en/2.1/ref/settings/ import os from configparser import ConfigParser +from loguru import logger # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -22,6 +23,13 @@ CONFIG_DIR = os.path.join(BASE_DIR, 'config') CONFIG = ConfigParser() CONFIG.read(os.path.join(CONFIG_DIR,'config.ini'),encoding='utf-8') +# 日志文件配置 +LOG_DIR = os.path.join(BASE_DIR,'log') + +if os.path.exists(LOG_DIR) is False: + os.makedirs(LOG_DIR) + +logger.add(os.path.join(LOG_DIR,'error.log'),rotation='1 days',retention='30 days',encoding='utf-8') # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/ @@ -32,11 +40,10 @@ SECRET_KEY = '5&71mt9@^58zdg*_!t(x6g14q*@84d%ptr%%s6e0l50zs0we3d' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = CONFIG.getboolean('site','debug') -VERSIONS = '0.5.1' +VERSIONS = '0.5.2' ALLOWED_HOSTS = ['*'] - # Application definition INSTALLED_APPS = [ @@ -50,6 +57,7 @@ INSTALLED_APPS = [ 'app_doc', 'app_api', 'django.contrib.sitemaps', + 'rest_framework', ] MIDDLEWARE = [ @@ -80,6 +88,10 @@ TEMPLATES = [ 'django.contrib.messages.context_processors.messages', 'app_admin.context_processors.sys_setting', # 自定义系统设置上下文渲染 ], + 'libraries': { # 自定义的模板标签 + 'doc_filter' : 'app_doc.templatetags.doc_filter', + 'project_filter' : 'app_doc.templatetags.project_filter', + }, }, }, ] @@ -168,3 +180,15 @@ MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR,'media') +REST_FRAMEWORK = { + 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', + 'PAGE_SIZE': 10 +} + +# Chromium路径 +try: + CHROMIUM_DIR = CONFIG['chromium']['path'] + CHROMIUM_PATH = CHROMIUM_DIR + # CHROMIUM_PATH = os.path.join(CONFIG_DIR,CHROMIUM_DIR) # Windows便携版本使用config下的路径 +except: + CHROMIUM_PATH = None \ No newline at end of file diff --git a/MrDoc/urls.py b/MrDoc/urls.py index 9eff071..f872a1d 100644 --- a/MrDoc/urls.py +++ b/MrDoc/urls.py @@ -27,6 +27,7 @@ urlpatterns = [ path('',include('app_doc.urls')), # doc应用 path('user/',include('app_admin.urls'),), # admin应用 path('api/',include('app_api.urls')), # API应用 + path('api_app/',include('app_api.urls_app')), # App的API接口 re_path('^static/(?P.*)$',serve,{'document_root':settings.STATIC_ROOT}),# 静态文件 re_path('^media/(?P.*)$',serve,{'document_root':settings.MEDIA_ROOT}),# 媒体文件 path('sitemap.xml', views.index, {'sitemaps': sitemaps,'template_name':'sitemap/sitemap-index.xml'},name='sitemap',), # 站点地图索引 diff --git a/README.md b/README.md index 1664331..1ccc7b9 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,14 @@ ### 介绍 -`MrDoc`是基于`Python`开发的在线文档系统,适合作为个人和小型团队的文档、知识和笔记管理工具。 +`MrDoc`是基于`Python`开发的在线文档系统,适合作为个人和小型团队的文档、知识和笔记管理工具。致力于成为全平台(Web端、桌面端、移动端)的私有化在线文档部署方案。 + +目前涵盖: + +- Web端,; +- Chrome扩展; +- App端(开发中) +- Windows、Mac、Linux桌面端(开发中) ### 开源地址 @@ -140,6 +147,8 @@ python manage.py runserver ## 赞赏项目 -如果MrDoc对你有所帮助,欢迎给予开发者赞赏,助力项目更好发展。 +开源不易,需要鼓励,如果MrDoc觅道文档对你有所帮助,请给予一个`Star`。 + +欢迎给予开发者赞赏,助力项目更好发展。 ![](./captrue/mrdoc-zan.png) \ No newline at end of file diff --git a/app_admin/views.py b/app_admin/views.py index 764a3e8..4f3b63e 100644 --- a/app_admin/views.py +++ b/app_admin/views.py @@ -29,8 +29,6 @@ def check_code(request): request.session["CheckCode"] = code return HttpResponse(stream.getvalue()) except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("生成验证码图片异常") return HttpResponse("请求异常:{}".format(repr(e))) @@ -63,8 +61,6 @@ def log_in(request): errormsg = '用户名或密码错误!' return render(request, 'login.html', locals()) except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("登录异常") return HttpResponse('请求出错') @@ -150,8 +146,6 @@ def log_out(request): try: logout(request) except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("注销异常") return redirect(request.META['HTTP_REFERER']) @@ -180,8 +174,6 @@ def forget_pwd(request): errormsg = "验证码已过期" return render(request, 'forget_pwd.html', locals()) except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("修改密码异常") errormsg = "验证码错误" return render(request,'forget_pwd.html',locals()) diff --git a/app_api/auth_app.py b/app_api/auth_app.py new file mode 100644 index 0000000..4b7bb69 --- /dev/null +++ b/app_api/auth_app.py @@ -0,0 +1,52 @@ +# coding:utf-8 +# @文件: auth.py +# @创建者:州的先生 +# #日期:2020/5/11 +# 博客地址:zmister.com + +from django.contrib.auth.models import User +from rest_framework.authentication import BaseAuthentication +from rest_framework.exceptions import AuthenticationFailed +from app_api.models import * + + +class AppAuth(BaseAuthentication): + '''自定义认证类''' + + def authenticate(self, request): + token = request.query_params.get('token') + # print(token) + if token: + # 如果请求url中携带有token参数 + user_obj = AppUserToken.objects.filter(token=token).first() + if user_obj: + # print("ok") + # token 是有效的,返回一个元组 + return user_obj.user, token # request.user, request.auth + else: + # raise AuthenticationFailed('无效的token') + return None + else: + # raise AuthenticationFailed('请求的URL中必须携带token参数') + return None + + +class AppMustAuth(BaseAuthentication): + '''自定义认证类''' + + def authenticate(self, request): + token = request.query_params.get('token') + # print(token) + if token: + # 如果请求url中携带有token参数 + user_obj = AppUserToken.objects.filter(token=token).first() + if user_obj: + # print("ok") + # token 是有效的,返回一个元组 + return user_obj.user, token # request.user, request.auth + else: + raise AuthenticationFailed('无效的token') + # return None + else: + raise AuthenticationFailed('请求的URL中必须携带token参数') + # return None \ No newline at end of file diff --git a/app_api/migrations/0003_appusertoken.py b/app_api/migrations/0003_appusertoken.py new file mode 100644 index 0000000..b1cce29 --- /dev/null +++ b/app_api/migrations/0003_appusertoken.py @@ -0,0 +1,28 @@ +# Generated by Django 2.2.12 on 2020-05-11 20:56 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('app_api', '0002_auto_20200322_0929'), + ] + + operations = [ + migrations.CreateModel( + name='AppUserToken', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('token', models.CharField(max_length=250, unique=True, verbose_name='token值')), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': 'App用户Token', + 'verbose_name_plural': 'App用户Token', + }, + ), + ] diff --git a/app_api/models.py b/app_api/models.py index 84164d7..f7731ae 100644 --- a/app_api/models.py +++ b/app_api/models.py @@ -1,7 +1,7 @@ from django.db import models from django.contrib.auth.models import User -# Token模型 +# Token模型 - 用于浏览器扩展 class UserToken(models.Model): user = models.OneToOneField(User,on_delete=models.CASCADE) token = models.CharField(verbose_name="token值",max_length=250,unique=True) @@ -12,3 +12,15 @@ class UserToken(models.Model): class Meta: verbose_name = '用户Token' verbose_name_plural = verbose_name + +# AppToken模型 - 用于桌面、移动等各类 APP 应用 +class AppUserToken(models.Model): + user = models.OneToOneField(User, on_delete=models.CASCADE) + token = models.CharField(verbose_name="token值", max_length=250, unique=True) + + def __str__(self): + return self.user + + class Meta: + verbose_name = 'App用户Token' + verbose_name_plural = verbose_name \ No newline at end of file diff --git a/app_api/permissions_app.py b/app_api/permissions_app.py new file mode 100644 index 0000000..cd53f50 --- /dev/null +++ b/app_api/permissions_app.py @@ -0,0 +1,31 @@ +# coding:utf-8 +# @文件: permissions.py +# @创建者:州的先生 +# #日期:2020/5/11 +# 博客地址:zmister.com + +from rest_framework.permissions import BasePermission,SAFE_METHODS + + +class AppPermission(BasePermission): + message = '只有VIP才能访问' + + def has_permission(self, request, view): + # vip才有访问权限 + # request.user:当前经过认证的用户对象 + # 如果没有认证 request.user就是匿名用户 + if not request.auth: + # 认证没有通过 + return False + if request.user.vip: + return True + else: + return False + + def has_object_permission(self, request, view, obj): + + if request.method in SAFE_METHODS: + return True + + # 示例必须要有一个名为`owner`的属性 + return obj.owner == request.user \ No newline at end of file diff --git a/app_api/serializers_app.py b/app_api/serializers_app.py new file mode 100644 index 0000000..e496d67 --- /dev/null +++ b/app_api/serializers_app.py @@ -0,0 +1,44 @@ +# coding:utf-8 +# @文件: serializers_app.py +# @创建者:州的先生 +# #日期:2020/5/11 +# 博客地址:zmister.com + +from rest_framework.serializers import ModelSerializer +from app_doc.models import * + +# 文集序列化器 +class ProjectSerializer(ModelSerializer): + class Meta: + model = Project + fields = ('__all__') + +# 文档序列化器 +class DocSerializer(ModelSerializer): + class Meta: + model = Doc + fields = ('__all__') + +# 文档模板序列化器 +class DocTempSerializer(ModelSerializer): + class Meta: + model = DocTemp + fields = ('__all__') + +# 图片序列化器 +class ImageSerializer(ModelSerializer): + class Meta: + model = Image + fields = ('__all__') + +# 图片分组序列化器 +class ImageGroupSerializer(ModelSerializer): + class Meta: + model = ImageGroup + fields = ('__all__') + +# 附件序列化器 +class AttachmentSerializer(ModelSerializer): + class Meta: + model = Attachment + fields = ('__all__') \ No newline at end of file diff --git a/app_api/urls_app.py b/app_api/urls_app.py new file mode 100644 index 0000000..977b86a --- /dev/null +++ b/app_api/urls_app.py @@ -0,0 +1,17 @@ +# coding:utf-8 +# @文件: urls_app.py +# @创建者:州的先生 +# #日期:2020/5/11 +# 博客地址:zmister.com +from django.urls import path,re_path +from app_api import views_app + +urlpatterns = [ + path('login/',views_app.LoginView.as_view()),# 登录 + path('projects/',views_app.ProjectView.as_view()), # 文集 + path('docs/',views_app.DocView.as_view()), # 文档 + path('doctemps/',views_app.DocTempView.as_view()), # 文档模板 + path('images/',views_app.ImageView.as_view()), # 图片 + path('imggroups/',views_app.ImageGroupView.as_view()), # 图片分组 + path('attachments/',views_app.AttachmentView.as_view()), # 附件 +] \ No newline at end of file diff --git a/app_api/views.py b/app_api/views.py index 60aea4e..1bd1d8b 100644 --- a/app_api/views.py +++ b/app_api/views.py @@ -43,8 +43,6 @@ def manage_token(request): ) return JsonResponse({'status':True,'data':token_str}) except: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("用户Token生成异常") return JsonResponse({'status':False,'data':'生成出错,请重试!'}) @@ -68,8 +66,6 @@ def get_projects(request): except ObjectDoesNotExist: return JsonResponse({'status':False,'data':'token无效'}) except: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("token获取文集异常") return JsonResponse({'status':False,'data':'系统异常'}) @@ -101,8 +97,6 @@ def create_doc(request): except ObjectDoesNotExist: return JsonResponse({'status': False, 'data': 'token无效'}) except: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("token创建文档异常") return JsonResponse({'status':False,'data':'系统异常'}) @@ -126,7 +120,5 @@ def upload_img(request): except ObjectDoesNotExist: return JsonResponse({'success': 0, 'data': 'token无效'}) 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_api/views_app.py b/app_api/views_app.py new file mode 100644 index 0000000..8e5da22 --- /dev/null +++ b/app_api/views_app.py @@ -0,0 +1,829 @@ +# coding:utf-8 +# @文件: views_app.py +# @创建者:州的先生 +# #日期:2020/5/11 +# 博客地址:zmister.com + +from django.contrib.auth.models import User +from django.contrib.auth import authenticate +from django.core.exceptions import ObjectDoesNotExist +from django.db.models import Q +from django.conf import settings +from rest_framework.views import APIView +from app_api.models import AppUserToken +from rest_framework.response import Response +from rest_framework.pagination import PageNumberPagination +from app_doc.models import * +from app_api.serializers_app import * +from app_api.auth_app import AppAuth,AppMustAuth +from app_doc.views import validateTitle +from app_doc.util_upload_img import img_upload,base_img_upload +from loguru import logger +import datetime +import os + +''' +响应: + code:状态码 + data:数据 + +状态码: + 0:成功 + 1:资源未找到 + 2:无权访问 + 3:需要访问码 + 4:系统异常 + 5:参数不正确 + 6:需要登录 + +''' + +# 生成Token的函数 +def get_token_code(username): + """ + 根据用户名和时间戳来生成永不相同的token随机字符串 + :param username: 字符串格式的用户名 + :return: 字符串格式的Token + """ + + import time + import hashlib + + timestamp = str(time.time()) + m = hashlib.md5(username.encode("utf-8")) + # md5 要传入字节类型的数据 + m.update(timestamp.encode("utf-8")) + return m.hexdigest() # 将生成的随机字符串返回 + + +# 登陆视图 +class LoginView(APIView): + ''' + 登陆检测试图。 + 1,接收用户发过来的用户名和密码数据 + 2,校验用户密码是否正确 + - 成功就返回登陆成功,然后发Token + - 失败就返回错误提示 + ''' + + def post(self,request): + res = {"code":0} + # 从post 里面取数据 + # print(request.data) + username = request.data.get("username") + password = request.data.get("password") + # 查询用户是否存在、密码是否匹配 + user_obj = authenticate(username=username, password=password) + if user_obj: + if user_obj.is_active: + # 生成Token + token = get_token_code(username) + # 保存或更新token + AppUserToken.objects.update_or_create(defaults={"token": token}, user=user_obj) + # 将token返回给用户 + res["token"] = token + else: + res["error"] = '账号被禁用' + + else: + # 登陆失败 + res["code"] = 1 + res["error"] = "用户名或密码错误" + return Response(res) + + +# 文集视图 +class ProjectView(APIView): + authentication_classes = (AppAuth,) + # 获取文集 + def get(self,request): + pro_id = request.query_params.get('id',None) + if pro_id: + resp = dict() + # 获取文集信息 + project = Project.objects.get(id=int(pro_id)) + # 获取文集的协作用户信息 + # print(request.auth) + # print(request.user) + if request.auth: # 对登陆用户查询其协作文档信息 + colla_user = ProjectCollaborator.objects.filter(project=project, user=request.user).count() + else: + colla_user = 0 + + # 获取文集前台下载权限 + try: + allow_download = ProjectReport.objects.get(project=project) + except: + allow_download = False + + # 私密文集并且访问者非创建者非协作者 + if (project.role == 1) and (request.user != project.create_user) and (colla_user == 0): + # return Response({'code': 2, 'data': []}) + resp['code'] = 2 + # 指定用户可见文集 + elif project.role == 2: + user_list = project.role_value + if request.auth: # 认证用户判断是否在许可用户列表中 + if (request.user.username not in user_list) and \ + (request.user != project.create_user) and \ + (colla_user == 0): # 访问者不在指定用户之中 + resp['code'] = 2 + else: # 游客直接返回404 + resp['code'] = 2 + # 访问码可见 + elif project.role == 3: + # 浏览用户不为创建者、协作者 + if request.user != project.create_user and colla_user == 0: + viewcode = project.role_value + viewcode_name = 'viewcode-{}'.format(project.id) + r_viewcode = request.data.get(viewcode_name,0) # 获取访问码 + if viewcode != r_viewcode: # 访问码不等于文集访问码,跳转到访问码认证界面 + # return Response({'code': 3}) + resp['code'] = 3 + else: + serializer = ProjectSerializer(project) + resp = {'code': 0, 'data': serializer.data} + return Response(resp) + + else: + kw = request.query_params.get('kw', '') # 搜索词 + sort = request.query_params.get('sort', 0) # 排序,0表示按时间升序排序,1表示按时间降序排序,默认为0 + role = request.query_params.get('role', -1) # 筛选文集权限,默认为显示所有可显示的文集 + + # 是否排序 + if sort in ['', 0, '0']: + sort_str = '' + else: + sort_str = '-' + + # 是否搜索 + if kw == '': + is_kw = False + else: + is_kw = True + + # 是否认证 + if request.auth: + is_auth = True + else: + is_auth = False + + # 是否筛选 + if role in ['', -1, '-1']: + is_role = False + role_list = [0, 3] + else: + is_role = True + + # 没有搜索 and 认证用户 and 没有筛选 + if (is_kw is False) and (is_auth) and (is_role is False): + colla_list = [i.project.id for i in ProjectCollaborator.objects.filter(user=request.user)] # 用户的协作文集列表 + project_list = Project.objects.filter( + Q(role__in=role_list) | \ + Q(role=2, role_value__contains=str(request.user.username)) | \ + Q(create_user=request.user) | \ + Q(id__in=colla_list) + ).order_by("{}create_time".format(sort_str)) + + # 没有搜索 and 认证用户 and 有筛选 + elif (is_kw is False) and (is_auth) and (is_role): + if role in ['0', 0]: + project_list = Project.objects.filter(role=0).order_by("{}create_time".format(sort_str)) + elif role in ['1', 1]: + project_list = Project.objects.filter(create_user=request.user, role=1).order_by( + "{}create_time".format(sort_str)) + elif role in ['2', 2]: + project_list = Project.objects.filter(role=2, role_value__contains=str(request.user.username)).order_by( + "{}create_time".format(sort_str)) + elif role in ['3', 3]: + project_list = Project.objects.filter(role=3).order_by("{}create_time".format(sort_str)) + elif role in ['99', 99]: + colla_list = [i.project.id for i in ProjectCollaborator.objects.filter(user=request.user)] # 用户的协作文集列表 + project_list = Project.objects.filter(id__in=colla_list).order_by("{}create_time".format(sort_str)) + else: + return Response({'code':2,'data':[]}) + + # 没有搜索 and 游客 and 没有筛选 + elif (is_kw is False) and (is_auth is False) and (is_role is False): + project_list = Project.objects.filter(role__in=[0, 3]).order_by("{}create_time".format(sort_str)) + + # 没有搜索 and 游客 and 有筛选 + elif (is_kw is False) and (is_auth is False) and (is_role): + if role in ['0', 0]: + project_list = Project.objects.filter(role=0).order_by("{}create_time".format(sort_str)) + elif role in ['3', 3]: + project_list = Project.objects.filter(role=3).order_by("{}create_time".format(sort_str)) + else: + return Response({'code': 2, 'data': []}) + + # 有搜索 and 认证用户 and 没有筛选 + elif (is_kw) and (is_auth) and (is_role is False): + colla_list = [i.project.id for i in ProjectCollaborator.objects.filter(user=request.user)] # 用户的协作文集 + # 查询所有可显示的文集 + project_list = Project.objects.filter( + Q(role__in=[0, 3]) | \ + Q(role=2, role_value__contains=str(request.user.username)) | \ + Q(create_user=request.user) | \ + Q(id__in=colla_list), + Q(name__icontains=kw) | Q(intro__icontains=kw) + ).order_by('{}create_time'.format(sort_str)) + + # 有搜索 and 认证用户 and 有筛选 + elif (is_kw) and (is_auth) and (is_role): + if role in ['0', 0]: + project_list = Project.objects.filter( + Q(name__icontains=kw) | Q(intro__icontains=kw), + role=0 + ).order_by("{}create_time".format(sort_str)) + elif role in ['1', 1]: + project_list = Project.objects.filter( + Q(name__icontains=kw) | Q(intro__icontains=kw), + create_user=request.user + ).order_by("{}create_time".format(sort_str)) + elif role in ['2', 2]: + project_list = Project.objects.filter( + Q(name__icontains=kw) | Q(intro__icontains=kw), + role=2, + role_value__contains=str(request.user.username) + ).order_by("{}create_time".format(sort_str)) + elif role in ['3', 3]: + project_list = Project.objects.filter( + Q(name__icontains=kw) | Q(intro__icontains=kw), + role=3 + ).order_by("{}create_time".format(sort_str)) + elif role in ['99', 99]: + colla_list = [i.project.id for i in ProjectCollaborator.objects.filter(user=request.user)] # 用户的协作文集列表 + project_list = Project.objects.filter( + Q(name__icontains=kw) | Q(intro__icontains=kw), + id__in=colla_list + ).order_by("{}create_time".format(sort_str)) + else: + return Response({'code':1,'data':[]}) + + # 有搜索 and 游客 and 没有筛选 + elif (is_kw) and (is_auth is False) and (is_role is False): + project_list = Project.objects.filter( + Q(name__icontains=kw) | Q(intro__icontains=kw), + role__in=[0, 3] + ).order_by("{}create_time".format(sort_str)) + + # 有搜索 and 游客 and 有筛选 + elif (is_kw) and (is_auth is False) and (is_role): + if role in ['0', 0]: + project_list = Project.objects.filter( + Q(name__icontains=kw) | Q(intro__icontains=kw), + role=0 + ).order_by("{}create_time".format(sort_str)) + elif role in ['3', 3]: + project_list = Project.objects.filter( + Q(name__icontains=kw) | Q(intro__icontains=kw), + role=3 + ).order_by("{}create_time".format(sort_str)) + else: + return Response({'code':1,'data':[]}) + + page = PageNumberPagination() # 实例化一个分页器 + page_projects = page.paginate_queryset(project_list,request,view=self) # 进行分页查询 + serializer = ProjectSerializer(page_projects,many=True) # 对分页后的结果进行序列化处理 + resp = { + 'code':0, + 'data':serializer.data, + 'count':project_list.count() + } + return Response(resp) + + # 新增文集 + def post(self,request): + resp = dict() + if request.auth: + try: + name = request.data.get('pname', '') + name = validateTitle(name) + desc = request.data.get('desc', '') + role = request.data.get('role', 0) + role_list = ['0', '1', '2', '3', 0, 1, 2, 3] + if name != '': + project = Project.objects.create( + name=validateTitle(name), + intro=desc[:100], + create_user=request.user, + role=int(role) if role in role_list else 0 + ) + project.save() + resp = {'code':0,'data':{'id': project.id, 'name': project.name}} + return Response(resp) + else: + resp['code'] = 5 + resp['data'] = '参数不正确' + return Response(resp) + except Exception as e: + logger.exception("创建文集出错") + resp['code'] = 4 + resp['data'] = '系统异常请稍后再试' + return Response(resp) + else: + resp['code'] = 6 + resp['data'] = '请登录后操作' + return Response(resp) + + # 修改文集 + def put(self,request): + resp = dict() + if request.auth: + try: + pro_id = request.query_params.get('id', None) + project = Project.objects.get(id=pro_id) + # 验证用户有权限修改文集 + if (request.user == project.create_user) or request.user.is_superuser: + name = request.data.get('name', None) + content = request.data.get('desc', None) + role = request.data.get('role',None) + role_value = request.data.get('role_value',None) + project.name = validateTitle(name) + project.intro = content + project.role = role + project.role_value = role_value + project.save() + resp['code'] = 0 + resp['data'] = 'ok' + # return Response(resp) + else: + resp['code'] = 2 + resp['data'] = '非法请求' + # return Response(resp) + except ObjectDoesNotExist: + resp['code'] = 1 + resp['data'] = '资源未找到' + # return Response(resp) + except Exception as e: + logger.exception("修改文集出错") + resp['code'] = 4 + # return Response(resp) + else: + resp['code'] = 6 + + return Response(resp) + + # 删除文集 + def delete(self,request): + resp = dict() + if request.auth: + try: + pro_id = request.query_params.get('id', '') + if pro_id != '': + pro = Project.objects.get(id=pro_id) + if (request.user == pro.create_user) or request.user.is_superuser: + # 删除文集下的文档 + pro_doc_list = Doc.objects.filter(top_doc=int(pro_id)) + pro_doc_list.delete() + # 删除文集 + pro.delete() + resp['code'] = 0 + resp['data'] = 'ok' + # return Response(resp) + else: + resp['code'] = 2 + # return Response(resp) + else: + resp['code'] = 5 + resp['data'] = '参数错误' + # return Response(resp) + except ObjectDoesNotExist: + resp['code'] = 1 + resp['data'] = '资源未找到' + # return Response(resp) + except Exception as e: + logger.exception("API文集删除异常") + resp['code'] = 4 + # return Response(resp) + else: + resp['code'] = 6 + + return Response(resp) + + +# 文档视图 +class DocView(APIView): + authentication_classes = (AppAuth,) + + # 获取文档 + def get(self,request): + pro_id = request.query_params.get('pid','') + doc_id = request.query_params.get('did','') + + if pro_id != '' and doc_id != '': + # 获取文集信息 + project = Project.objects.get(id=int(pro_id)) + # 获取文集的协作用户信息 + if request.auth: + colla_user = ProjectCollaborator.objects.filter(project=project, user=request.user) + if colla_user.exists(): + colla_user_role = colla_user[0].role + colla_user = colla_user.count() + else: + colla_user = colla_user.count() + else: + colla_user = 0 + + # 私密文集且访问者非创建者、协作者 - 不能访问 + if (project.role == 1) and (request.user != project.create_user) and (colla_user == 0): + return Response({'code':2}) + # 指定用户可见文集 + elif project.role == 2: + user_list = project.role_value + if request.user.is_authenticated: # 认证用户判断是否在许可用户列表中 + if (request.user.username not in user_list) and \ + (request.user != project.create_user) and \ + (colla_user == 0): # 访问者不在指定用户之中,也不是协作者 + return Response({'code': 2}) + else: # 游客直接返回404 + return Response({'code': 2}) + # 访问码可见 + elif project.role == 3: + # 浏览用户不为创建者和协作者 - 需要访问码 + if (request.user != project.create_user) and (colla_user == 0): + viewcode = project.role_value + viewcode_name = 'viewcode-{}'.format(project.id) + r_viewcode = request.data.get(viewcode_name,0) # 获取访问码 + if viewcode != r_viewcode: # cookie中的访问码不等于文集访问码,跳转到访问码认证界面 + return Response({'code':3}) + + # 获取文档内容 + try: + doc = Doc.objects.get(id=int(doc_id), status=1) + serializer = DocSerializer(doc) + resp = {'code':0,'data':serializer.data} + return Response(resp) + except ObjectDoesNotExist: + return Response({'code':4}) + else: + return Response({'code':4}) + + # 新建文档 + def post(self, request): + try: + project = request.data.get('project','') + parent_doc = request.data.get('parent_doc','') + doc_name = request.data.get('doc_name','') + doc_content = request.data.get('content','') + pre_content = request.data.get('pre_content','') + sort = request.data.get('sort','') + status = request.data.get('status',1) + if project != '' and doc_name != '' and project != '-1': + # 验证请求者是否有文集的权限 + check_project = Project.objects.filter(id=project,create_user=request.user) + colla_project = ProjectCollaborator.objects.filter(project=project,user=request.user) + if check_project.count() > 0 or colla_project.count() > 0: + # 创建文档 + doc = Doc.objects.create( + name=doc_name, + content = doc_content, + pre_content= pre_content, + parent_doc= int(parent_doc) if parent_doc != '' else 0, + top_doc= int(project), + sort = sort if sort != '' else 99, + create_user=request.user, + status = status + ) + return Response({'code':0,'data':{'pro':project,'doc':doc.id}}) + else: + return Response({'code':2,'data':'无权操作此文集'}) + else: + return Response({'code':5,'data':'请确认文档标题、文集正确'}) + except Exception as e: + logger.exception("api新建文档异常") + return Response({'status':4,'data':'请求出错'}) + + # 修改文档 + def put(self, request): + try: + doc_id = request.data.get('doc_id','') # 文档ID + project = request.data.get('project', '') # 文集ID + parent_doc = request.data.get('parent_doc', '') # 上级文档ID + doc_name = request.data.get('doc_name', '') # 文档名称 + doc_content = request.data.get('content', '') # 文档内容 + pre_content = request.data.get('pre_content', '') # 文档Markdown格式内容 + sort = request.data.get('sort', '') # 文档排序 + status = request.data.get('status',1) # 文档状态 + + if doc_id != '' and project != '' and doc_name != '' and project != '-1': + doc = Doc.objects.get(id=doc_id) + pro_colla = ProjectCollaborator.objects.filter(project=project, user=request.user) + # 验证用户有权限修改文档 - 文档的创建者或文集的高级协作者 + if (request.user == doc.create_user) or (pro_colla[0].role == 1): + # 将现有文档内容写入到文档历史中 + DocHistory.objects.create( + doc = doc, + pre_content = doc.pre_content, + create_user = request.user + ) + # 更新文档内容 + Doc.objects.filter(id=int(doc_id)).update( + name=doc_name, + content=doc_content, + pre_content=pre_content, + parent_doc=int(parent_doc) if parent_doc != '' else 0, + sort=sort if sort != '' else 99, + modify_time = datetime.datetime.now(), + status = status + ) + return Response({'code': 0,'data':'修改成功'}) + else: + return Response({'code':2,'data':'未授权请求'}) + else: + return Response({'code': 5,'data':'参数错误'}) + except Exception as e: + logger.exception("api修改文档出错") + return Response({'code':4,'data':'请求出错'}) + + # 删除文档 + def delete(self, request): + try: + # 获取文档ID + doc_id = request.data.get('doc_id', None) + if doc_id: + # 查询文档 + try: + doc = Doc.objects.get(id=doc_id) + except ObjectDoesNotExist: + return Response({'code': 1, 'data': '文档不存在'}) + if request.user == doc.create_user: + # 删除 + doc.delete() + # 修改其子文档为顶级文档 + Doc.objects.filter(parent_doc=doc_id).update(parent_doc=0) + return Response({'code': 0, 'data': '删除完成'}) + else: + return Response({'code': 2, 'data': '非法请求'}) + else: + return Response({'code': 5, 'data': '参数错误'}) + except Exception as e: + logger.exception("api删除文档出错") + return Response({'code': 4, 'data': '请求出错'}) + + +# 文档模板视图 +class DocTempView(APIView): + authentication_classes = (AppAuth,) + + # 获取文档模板 + def get(self, request): + if request.auth: + temp_id = request.query_params.get('id','') + if temp_id != '': + doctemp = DocTemp.objects.get(id=int(temp_id)) + if request.user == doctemp.create_user: + serializer = DocTempSerializer(doctemp) + resp = {'code': 0, 'data': serializer.data} + else: + resp = {'code':2,'data':'无权操作'} + else: + doctemps = DocTemp.objects.filter(create_user=request.user) + page = PageNumberPagination() + page_doctemps = page.paginate_queryset(doctemps,request,view=self) + serializer = DocTempSerializer(page_doctemps,many=True) + resp = {'code':0,'data':serializer.data,'count':doctemps.count()} + return Response(resp) + else: + return Response({'code': 6, 'data': '请登录'}) + + def post(self, request): + try: + if request.auth: + name = request.data.get('name','') + content = request.data.get('content','') + if name != '': + doctemp = DocTemp.objects.create( + name = name, + content = content, + create_user=request.user + ) + doctemp.save() + return Response({'code':0,'data':'创建成功'}) + else: + return Response({'code':5,'data':'模板标题不能为空'}) + else: + return Response({'code':6,'data':'请登录'}) + except Exception as e: + logger.exception("api创建文档模板出错") + return Response({'code':4,'data':'请求出错'}) + + def put(self, request): + try: + doctemp_id = request.data.get('doctemp_id','') + name = request.data.get('name','') + content = request.data.get('content','') + if doctemp_id != '' and name !='': + doctemp = DocTemp.objects.get(id=doctemp_id) + # 验证请求用户为文档模板的创建者 + if request.user == doctemp.create_user: + doctemp.name = name + doctemp.content = content + doctemp.save() + return Response({'code':0,'data':'修改成功'}) + else: + return Response({'code':2,'data':'非法操作'}) + else: + return Response({'code':5,'data':'参数错误'}) + except Exception as e: + logger.exception("api修改文档模板出错") + return Response({'code':4,'data':'请求出错'}) + + def delete(self, request): + try: + doctemp_id = request.data.get('doctemp_id', '') + if doctemp_id != '': + doctemp = DocTemp.objects.get(id=doctemp_id) + if request.user == doctemp.create_user: + doctemp.delete() + return Response({'code': 0, 'data': '删除完成'}) + else: + return Response({'code': 2, 'data': '非法请求'}) + else: + return Response({'code': 5, 'data': '参数错误'}) + except Exception as e: + logger.exception("api删除文档模板出错") + return Response({'code': 4, 'data': '请求出错'}) + + +# 图片视图 +class ImageView(APIView): + authentication_classes = (AppAuth,) + + def get(self, request): + if request.auth: + g_id = int(request.query_params.get('group', 0)) # 图片分组id + if int(g_id) == 0: + image_list = Image.objects.filter(user=request.user) # 查询所有图片 + elif int(g_id) == -1: + image_list = Image.objects.filter(user=request.user, group_id=None) # 查询指定分组的图片 + else: + image_list = Image.objects.filter(user=request.user, group_id=g_id) # 查询指定分组的图片 + page = PageNumberPagination() + page_images = page.paginate_queryset(image_list,request,view=self) + serializer = ImageSerializer(page_images,many=True) + resp = {'code':0,'data':serializer.data,'count':image_list.count()} + return Response(resp) + else: + return Response({'code': 6, 'data': '请登录'}) + + # 上传 + def post(self, request): + img = request.data.get("api_img_upload", None) # 编辑器上传 + # manage_upload = request.data.get('manage_upload', None) # 图片管理上传 + dir_name = request.data.get('dirname', '') + base_img = request.data.get('base', None) + if img: # 上传普通图片文件 + result = img_upload(img, dir_name, request.user) + resp = {'code':0,'data':result['url']} + # elif manage_upload: + # result = img_upload(manage_upload, dir_name, request.user) + # resp = {'code': 0, 'data': result['url']} + elif base_img: # 上传base64编码图片 + result = base_img_upload(base_img, dir_name, request.user) + resp = {'code': 0, 'data': result['url']} + else: + resp = {"code": 5, "message": "出错信息"} + return Response(resp) + + # 删除 + def delete(self, request): + img_id = request.data.get('id', '') + img = Image.objects.get(id=img_id) + if img.user != request.user: + return Response({'code': 2, 'data': '未授权请求'}) + file_path = settings.BASE_DIR + img.file_path + is_exist = os.path.exists(file_path) + if is_exist: + os.remove(file_path) # 删除本地文件 + img.delete() # 删除记录 + return Response({'code': 0, 'data': 'ok'}) + + +# 图片分组视图 +class ImageGroupView(APIView): + authentication_classes = (AppMustAuth,) + + def get(self, request): + try: + group_list = [] + all_cnt = Image.objects.filter(user=request.user).count() + non_group_cnt = Image.objects.filter(group_id=None,user=request.user).count() + group_list.append({'group_name': '全部图片', 'group_cnt': all_cnt, 'group_id': 0}) + group_list.append({'group_name': '未分组', 'group_cnt': non_group_cnt, 'group_id': -1}) + groups = ImageGroup.objects.filter(user=request.user) # 查询所有分组 + for group in groups: + group_cnt = Image.objects.filter(group_id=group).count() + item = { + 'group_id': group.id, + 'group_name': group.group_name, + 'group_cnt': group_cnt + } + group_list.append(item) + return Response({'code': 0, 'data': group_list}) + except: + return Response({'code': 4, 'data': '出现错误'}) + + def post(self, request): + group_name = request.data.get('group_name', '') + if group_name not in ['', '默认分组', '未分组']: + ImageGroup.objects.create( + user=request.user, + group_name=group_name + ) + return Response({'code': 0, 'data': 'ok'}) + else: + return Response({'code': 5, 'data': '名称无效'}) + + def put(self, request): + group_name = request.data.get("group_name", '') + if group_name not in ['', '默认分组', '未分组']: + group_id = request.POST.get('group_id', '') + ImageGroup.objects.filter(id=group_id,user=request.user).update(group_name=group_name) + return Response({'code': 0, 'data': 'ok'}) + else: + return Response({'code': 5, 'data': '名称无效'}) + + def delete(self, request): + try: + group_id = request.data.get('group_id', '') + group = ImageGroup.objects.get(id=group_id, user=request.user) # 查询分组 + images = Image.objects.filter(group_id=group_id).update(group_id=None) # 移动图片到未分组 + group.delete() # 删除分组 + return Response({'code': 0, 'data': 'ok'}) + except: + return Response({'code': 4, 'data': '删除错误'}) + + +# 附件视图 +class AttachmentView(APIView): + authentication_classes = (AppAuth,) + + # 文件大小 字节转换 + def sizeFormat(size, is_disk=False, precision=2): + ''' + size format for human. + byte ---- (B) + kilobyte ---- (KB) + megabyte ---- (MB) + gigabyte ---- (GB) + terabyte ---- (TB) + petabyte ---- (PB) + exabyte ---- (EB) + zettabyte ---- (ZB) + yottabyte ---- (YB) + ''' + formats = ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] + unit = 1000.0 if is_disk else 1024.0 + if not (isinstance(size, float) or isinstance(size, int)): + raise TypeError('a float number or an integer number is required!') + if size < 0: + raise ValueError('number must be non-negative') + for i in formats: + size /= unit + if size < unit: + r = '{}{}'.format(round(size, precision), i) + return r + + def get(self, request): + attachment_list = [] + attachments = Attachment.objects.filter(user=request.user) + for a in attachments: + item = { + 'filename': a.file_name, + 'filesize': a.file_size, + 'filepath': a.file_path.name, + 'filetime': a.create_time + } + attachment_list.append(item) + return Response({'code': 0, 'data': attachment_list}) + + def post(self, request): + attachment = request.data.get('attachment_upload', None) + if attachment: + attachment_name = attachment.name + attachment_size = self.sizeFormat(attachment.size) + # 限制附件大小在50mb以内 + if attachment.size > 52428800: + return Response({'code': False, 'data': '文件大小超出限制'}) + # 限制附件为ZIP格式文件 + if attachment_name.endswith('.zip'): + a = Attachment.objects.create( + file_name=attachment_name, + file_size=attachment_size, + file_path=attachment, + user=request.user + ) + return Response({'code': 0, 'data': {'name': attachment_name, 'url': a.file_path.name}}) + else: + return Response({'code': 5, 'data': '不支持的格式'}) + else: + return Response({'code': 5, 'data': '无效文件'}) + + def delete(self, request): + attach_id = request.data.get('attach_id', '') + attachment = Attachment.objects.filter(id=attach_id, user=request.user) # 查询附件 + for a in attachment: # 遍历附件 + a.file_path.delete() # 删除文件 + attachment.delete() # 删除数据库记录 + return Response({'code': 0, 'data': 'ok'}) \ No newline at end of file diff --git a/app_doc/report_utils.py b/app_doc/report_utils.py index a284ba7..8509f4d 100644 --- a/app_doc/report_utils.py +++ b/app_doc/report_utils.py @@ -43,7 +43,21 @@ def geneta_js_img(html_path,img_path,types): 'seque':'.sequence-diagram' # 序列图 } async def main(): - browser = await launch(headless=True,handleSIGINT=False,handleSIGTERM=False,handleSIGHUP=False) + if settings.CHROMIUM_PATH: + browser = await launch( + executablePath=r'{}'.format(settings.CHROMIUM_PATH), + headless=True, + handleSIGINT=False, + handleSIGTERM=False, + handleSIGHUP=False + ) + else: + browser = await launch( + headless=True, + handleSIGINT=False, + handleSIGTERM=False, + handleSIGHUP=False + ) page = await browser.newPage() await page.goto('file://' + html_path, {'waitUntil': 'networkidle0'}) element = await page.querySelector(type_map[types]) @@ -66,13 +80,21 @@ def geneta_js_img(html_path,img_path,types): @logger.catch() def html_to_pdf(html_path,pdf_path): async def main(): - browser = await launch( - headless=True, - handleSIGINT=False, - handleSIGTERM=False, - handleSIGHUP=False, - ignoreHTTPSErrors = True, - ) + if settings.CHROMIUM_PATH: + browser = await launch( + executablePath=r'{}'.format(settings.CHROMIUM_PATH), + headless=True, + handleSIGINT=False, + handleSIGTERM=False, + handleSIGHUP=False + ) + else: + browser = await launch( + headless=True, + handleSIGINT=False, + handleSIGTERM=False, + handleSIGHUP=False + ) page = await browser.newPage() await page.goto('file://' + html_path, {'waitUntil': 'networkidle0'}) await page.pdf({ diff --git a/app_doc/views.py b/app_doc/views.py index 82135b6..393d94f 100644 --- a/app_doc/views.py +++ b/app_doc/views.py @@ -194,8 +194,7 @@ def create_project(request): else: return JsonResponse({'status':False,'data':'文集名称不能为空!'}) except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) + logger.exception("创建文集出错") return JsonResponse({'status':False,'data':'出现异常,请检查输入值!'}) @@ -252,8 +251,6 @@ def project_index(request,pro_id): return render(request,'app_doc/project_doc_search.html',locals()) return render(request, 'app_doc/project.html', locals()) except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("文集页访问异常") return render(request,'404.html') @@ -276,8 +273,6 @@ def modify_project(request): else: return JsonResponse({'status':False,'data':'非法请求'}) except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("修改文集出错") return JsonResponse({'status':False,'data':'请求出错'}) @@ -344,8 +339,6 @@ def check_viewcode(request): errormsg = "访问码错误" return render(request, 'app_doc/check_viewcode.html', locals()) except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("验证文集访问码出错") return render(request,'404.html') @@ -370,8 +363,6 @@ def del_project(request): else: return JsonResponse({'status':False,'data':'参数错误'}) except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("删除文集出错") return JsonResponse({'status':False,'data':'请求出错'}) @@ -405,8 +396,6 @@ def manage_project(request): pros = paginator.page(paginator.num_pages) return render(request,'app_doc/manage_project.html',locals()) except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("管理文集出错") return render(request,'404.html') @@ -499,8 +488,7 @@ def manage_project_collaborator(request,pro_id): pro_colla.delete() return JsonResponse({'status':True,'data':'删除成功'}) except: - if settings.DEBUG: - print(traceback.print_exc()) + logger.exception("删除协作者出错") return JsonResponse({'status':False,'data':'删除出错'}) # 修改协作权限 elif int(types) == 2: @@ -512,8 +500,7 @@ def manage_project_collaborator(request,pro_id): pro_colla.update(role=role) return JsonResponse({'status':True,'data':'修改成功'}) except: - if settings.DEBUG: - print(traceback.print_exc()) + logger.exception("修改协作权限出错") return JsonResponse({'status':False,'data':'修改失败'}) else: @@ -574,8 +561,6 @@ def doc(request,pro_id,doc_id): try: doc = Doc.objects.get(id=int(doc_id),status=1) except ObjectDoesNotExist: - if settings.DEBUG: - print(traceback.print_exc()) return render(request, '404.html') # 获取文集下一级文档 project_docs = Doc.objects.filter(top_doc=doc.top_doc, parent_doc=0, status=1).order_by('sort') @@ -583,8 +568,6 @@ def doc(request,pro_id,doc_id): else: return HttpResponse('参数错误') except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("文集浏览出错") return render(request,'404.html') @@ -602,8 +585,7 @@ def create_doc(request): doctemp_list = DocTemp.objects.filter(create_user=request.user).values('id','name','create_time') return render(request,'app_doc/create_doc.html',locals()) except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) + logger.exception("访问创建文档页面出错") return render(request,'404.html') elif request.method == 'POST': try: @@ -636,8 +618,7 @@ def create_doc(request): else: return JsonResponse({'status':False,'data':'请确认文档标题、文集正确'}) except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) + logger.exception("创建文档出错") return JsonResponse({'status':False,'data':'请求出错'}) else: return JsonResponse({'status':False,'data':'方法不允许'}) @@ -660,8 +641,6 @@ def modify_doc(request,doc_id): else: return render(request,'403.html') except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("修改文档页面访问出错") return render(request,'404.html') elif request.method == 'POST': @@ -702,8 +681,6 @@ def modify_doc(request,doc_id): else: return JsonResponse({'status': False,'data':'参数错误'}) except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("修改文档出错") return JsonResponse({'status':False,'data':'请求出错'}) @@ -732,8 +709,6 @@ def del_doc(request): else: return JsonResponse({'status':False,'data':'参数错误'}) except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("删除文档出错") return JsonResponse({'status':False,'data':'请求出错'}) @@ -851,8 +826,6 @@ def diff_doc(request,doc_id,his_id): else: return render(request, '403.html') except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("文档历史版本页面访问出错") return render(request, '404.html') @@ -870,8 +843,6 @@ def diff_doc(request,doc_id,his_id): else: return JsonResponse({'status':False,'data':'非法请求'}) except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("文档历史版本获取出错") return JsonResponse({'status':False,'data':'获取异常'}) @@ -894,8 +865,6 @@ def manage_doc_history(request,doc_id): historys = paginator.page(paginator.num_pages) return render(request, 'app_doc/manage_doc_history.html', locals()) except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("管理文档历史版本页面访问出错") return render(request, '404.html') elif request.method == 'POST': @@ -904,8 +873,6 @@ def manage_doc_history(request,doc_id): DocHistory.objects.filter(id=history_id,doc=doc_id,create_user=request.user).delete() return JsonResponse({'status':True,'data':'删除成功'}) except: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("操作文档历史版本出错") return JsonResponse({'status':False,'data':'出现异常'}) @@ -932,8 +899,6 @@ def create_doctemp(request): else: return JsonResponse({'status':False,'data':'模板标题不能为空'}) except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("创建文档模板出错") return JsonResponse({'status':False,'data':'请求出错'}) @@ -951,8 +916,6 @@ def modify_doctemp(request,doctemp_id): else: return HttpResponse('非法请求') except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("访问文档模板修改页面出错") return render(request, '404.html') elif request.method == 'POST': @@ -972,8 +935,6 @@ def modify_doctemp(request,doctemp_id): else: return JsonResponse({'status':False,'data':'参数错误'}) except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("修改文档模板出错") return JsonResponse({'status':False,'data':'请求出错'}) @@ -993,8 +954,6 @@ def del_doctemp(request): else: return JsonResponse({'status': False, 'data': '参数错误'}) except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("删除文档模板出错") return JsonResponse({'status':False,'data':'请求出错'}) @@ -1006,7 +965,10 @@ def manage_doctemp(request): try: search_kw = request.GET.get('kw', None) if search_kw: - doctemp_list = DocTemp.objects.filter(create_user=request.user,content__icontains=search_kw) + doctemp_list = DocTemp.objects.filter( + create_user=request.user, + content__icontains=search_kw + ).order_by('-modify_time') paginator = Paginator(doctemp_list, 10) page = request.GET.get('page', 1) try: @@ -1017,7 +979,7 @@ def manage_doctemp(request): doctemps = paginator.page(paginator.num_pages) doctemps.kw = search_kw else: - doctemp_list = DocTemp.objects.filter(create_user=request.user) + doctemp_list = DocTemp.objects.filter(create_user=request.user).order_by('-modify_time') paginator = Paginator(doctemp_list, 10) page = request.GET.get('page', 1) try: @@ -1028,8 +990,6 @@ def manage_doctemp(request): doctemps = paginator.page(paginator.num_pages) return render(request, 'app_doc/manage_doctemp.html', locals()) except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("管理文档模板页面访问出错") return render(request, '404.html') @@ -1046,8 +1006,6 @@ def get_doctemp(request): else: return JsonResponse({'status':False,'data':'参数错误'}) except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("获取指定文档模板出错") return JsonResponse({'status':False,'data':'请求出错'}) @@ -1163,8 +1121,6 @@ def report_md(request): else: return JsonResponse({'status':False,'data':'无权限'}) except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("导出文集MD文件出错") return JsonResponse({'status':False,'data':'文集不存在'}) @@ -1262,8 +1218,6 @@ def genera_project_file(request): return JsonResponse({'status': True, 'data': epub_file}) except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) return JsonResponse({'status': False, 'data': '生成出错'}) # 导出PDF elif report_type in ['pdf']: @@ -1296,8 +1250,6 @@ def genera_project_file(request): return JsonResponse({'status': True, 'data': pdf_file}) except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) return JsonResponse({'status': False, 'data': '生成出错'}) else: return JsonResponse({'status': False, 'data': '不支持的类型'}) @@ -1309,8 +1261,6 @@ def genera_project_file(request): return JsonResponse({'status':False,'data':'文集不存在'}) except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("生成文集文件出错") return JsonResponse({'status':False,'data':'系统异常'}) @@ -1387,8 +1337,6 @@ def report_file(request): # print(report_project) return JsonResponse({'status': True, 'data': report_project.file_path}) except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) return JsonResponse({'status': False, 'data': '导出出错'}) # 导出PDF elif report_type in ['pdf']: @@ -1400,8 +1348,6 @@ def report_file(request): # print(report_project) return JsonResponse({'status': True, 'data': report_project.file_path}) except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) return JsonResponse({'status': False, 'data': '导出出错'}) else: return JsonResponse({'status': False, 'data': '不支持的类型'}) @@ -1410,8 +1356,6 @@ def report_file(request): except ObjectDoesNotExist: return JsonResponse({'status':False,'data':'文集不存在'}) except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("获取文集前台导出文件出错") return JsonResponse({'status':False,'data':'系统异常'}) @@ -1428,11 +1372,11 @@ def manage_image(request): no_group_cnt = Image.objects.filter(user=request.user,group_id=None).count() # 获取所有未分组的图片数量 g_id = int(request.GET.get('group', 0)) # 图片分组id if int(g_id) == 0: - image_list = Image.objects.filter(user=request.user) # 查询所有图片 + image_list = Image.objects.filter(user=request.user).order_by('-create_time') # 查询所有图片 elif int(g_id) == -1: - image_list = Image.objects.filter(user=request.user,group_id=None) # 查询指定分组的图片 + image_list = Image.objects.filter(user=request.user,group_id=None).order_by('-create_time') # 查询指定分组的图片 else: - image_list = Image.objects.filter(user=request.user,group_id=g_id) # 查询指定分组的图片 + image_list = Image.objects.filter(user=request.user,group_id=g_id).order_by('-create_time') # 查询指定分组的图片 paginator = Paginator(image_list, 18) page = request.GET.get('page', 1) try: @@ -1444,8 +1388,6 @@ def manage_image(request): images.group = g_id return render(request,'app_doc/manage_image.html',locals()) except: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("图片素材管理出错") return render(request,'404.html') elif request.method == 'POST': @@ -1478,11 +1420,11 @@ def manage_image(request): if group_id is None: # return JsonResponse({'status':False,'data':'参数错误'}) elif int(group_id) == 0: - imgs = Image.objects.filter(user=request.user) + imgs = Image.objects.filter(user=request.user).order_by('-create_time') elif int(group_id) == -1: - imgs = Image.objects.filter(user=request.user,group_id=None) + imgs = Image.objects.filter(user=request.user,group_id=None).order_by('-create_time') else: - imgs = Image.objects.filter(user=request.user,group_id=group_id) + imgs = Image.objects.filter(user=request.user,group_id=group_id).order_by('-create_time') img_list = [] for img in imgs: item = { @@ -1496,8 +1438,6 @@ def manage_image(request): except ObjectDoesNotExist: return JsonResponse({'status':False,'data':'图片不存在'}) except: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("操作图片素材出错") return JsonResponse({'status':False,'data':'程序异常'}) @@ -1542,8 +1482,7 @@ def manage_img_group(request): group.delete() # 删除分组 return JsonResponse({'status':True,'data':'删除完成'}) except: - if settings.DEBUG: - print(traceback.print_exc()) + logger.exception("删除图片分组出错") return JsonResponse({'status':False,'data':'删除错误'}) # 获取分组 elif int(types) == 3: @@ -1564,8 +1503,7 @@ def manage_img_group(request): group_list.append(item) return JsonResponse({'status':True,'data':group_list}) except: - if settings.DEBUG: - print(traceback.print_exc()) + logger.exception("获取图片分组出错") return JsonResponse({'status':False,'data':'出现错误'}) @@ -1603,7 +1541,10 @@ def manage_attachment(request): try: search_kw = request.GET.get('kw', None) if search_kw: - attachment_list = Attachment.objects.filter(user=request.user,file_name__icontains=search_kw) + attachment_list = Attachment.objects.filter( + user=request.user, + file_name__icontains=search_kw + ).order_by('-create_time') paginator = Paginator(attachment_list, 15) page = request.GET.get('page', 1) try: @@ -1614,7 +1555,7 @@ def manage_attachment(request): attachments = paginator.page(paginator.num_pages) attachments.kw = search_kw else: - attachment_list = Attachment.objects.filter(user=request.user) + attachment_list = Attachment.objects.filter(user=request.user).order_by('-create_time') paginator = Paginator(attachment_list, 15) page = request.GET.get('page', 1) try: @@ -1625,8 +1566,6 @@ def manage_attachment(request): attachments = paginator.page(paginator.num_pages) return render(request, 'app_doc/manage_attachment.html', locals()) except Exception as e: - if settings.DEBUG: - print(traceback.print_exc()) logger.exception("附件管理访问出错") return render(request,'404.html') elif request.method == 'POST': diff --git a/config/config.ini b/config/config.ini index 2e2388d..a4f3cbb 100644 --- a/config/config.ini +++ b/config/config.ini @@ -14,4 +14,7 @@ engine = sqlite # host表示数据库主机地址 # host = db_host # port表示数据库端口 -# port = db_port \ No newline at end of file +# port = db_port +[chromium] +# path用于指定Chromium的路径,不指定则使用默认的 +# path = chromium_path \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index fce411c..f2631a9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,5 @@ beautifulsoup4==4.8.2 lxml pillow==6.2.2 pyppeteer==0.0.25 -loguru==0.4.1 \ No newline at end of file +loguru==0.4.1 +djangorestframework==3.8.2 \ No newline at end of file