diff --git a/app_admin/decorators.py b/app_admin/decorators.py index cbb5d0400a2654cfd5fb4f6a3af643194681f35a..b65f1af02be420d32adf323de4570aba6fb936ae 100644 --- a/app_admin/decorators.py +++ b/app_admin/decorators.py @@ -1,12 +1,14 @@ -from django.core.exceptions import PermissionDenied # 权限拒绝异常 -from django.http import Http404,JsonResponse +from django.core.exceptions import PermissionDenied # 权限拒绝异常 +from django.http import Http404, JsonResponse from app_admin.models import SysSetting from app_api.models import UserToken from django import VERSION as django_version + # 超级管理员用户需求 def superuser_only(function): """限制视图只有超级管理员能够访问""" + def _inner(request, *args, **kwargs): if request.user.is_authenticated: if not request.user.is_superuser: @@ -17,10 +19,12 @@ def superuser_only(function): return _inner + # 开放注册需求 def open_register(function): '''只有开放注册才能访问''' - def _inner(request,*args,**kwargs): + + def _inner(request, *args, **kwargs): try: status = SysSetting.objects.get(name='close_register') except: @@ -32,21 +36,23 @@ def open_register(function): return _inner + # 请求头验证 def check_headers(function): - def _inner(request,*args,**kwargs): + def _inner(request, *args, **kwargs): metas = request.META # if 'HTTP_COOKIE' not in metas: # raise Http404 if 'HTTP_USER_AGENT' not in metas: raise Http404 return function(request, *args, **kwargs) + return _inner # 开放前台文集导出 def allow_report_file(function): - def _inner(request,*args,**kwargs): + def _inner(request, *args, **kwargs): try: status = SysSetting.objects.get(name='enable_project_report') except: @@ -57,8 +63,8 @@ def allow_report_file(function): return function(request, *args, **kwargs) else: raise Http404 - return _inner + return _inner # Token头验证 # def check_token(function): @@ -77,4 +83,4 @@ def allow_report_file(function): # return JsonResponse({'data':'无效Token'}) # else: # metas = request.META -# return _inner \ No newline at end of file +# return _inner diff --git a/app_admin/migrations/0009_cookieplant_plant.py b/app_admin/migrations/0009_cookieplant_plant.py new file mode 100644 index 0000000000000000000000000000000000000000..4131f613ad29e0f2ea2a8cbff7bb677a9f17d86a --- /dev/null +++ b/app_admin/migrations/0009_cookieplant_plant.py @@ -0,0 +1,45 @@ +# Generated by Django 2.2.13 on 2021-02-04 15:06 + +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_admin', '0008_useroptions'), + ] + + operations = [ + migrations.CreateModel( + name='Plant', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('plant_code', models.CharField(max_length=10, unique=True, verbose_name='平台Cookie')), + ('plant_name', models.CharField(max_length=500, unique=True, verbose_name='平台名称')), + ('status', models.IntegerField(default=1, verbose_name='平台状态')), + ('create_time', models.DateTimeField(auto_now=True, verbose_name='创建时间')), + ], + options={ + 'verbose_name': '平台管理', + 'verbose_name_plural': '平台管理', + }, + ), + migrations.CreateModel( + name='CookiePlant', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('cookie', models.TextField(unique=True, verbose_name='平台Cookie')), + ('status', models.IntegerField(default=1, verbose_name='cookie状态')), + ('create_time', models.DateTimeField(auto_now=True, verbose_name='创建时间')), + ('create_user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('plant', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app_admin.Plant')), + ], + options={ + 'verbose_name': '平台cookie管理', + 'verbose_name_plural': '平台cookie管理', + }, + ), + ] diff --git a/app_admin/migrations/0010_auto_20210204_1621.py b/app_admin/migrations/0010_auto_20210204_1621.py new file mode 100644 index 0000000000000000000000000000000000000000..017a7d1b2e1b959df3e028a802de756609aeb707 --- /dev/null +++ b/app_admin/migrations/0010_auto_20210204_1621.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.13 on 2021-02-04 16:21 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('app_admin', '0009_cookieplant_plant'), + ] + + operations = [ + migrations.AlterField( + model_name='plant', + name='plant_code', + field=models.CharField(max_length=10, unique=True, verbose_name='平台id'), + ), + ] diff --git a/app_admin/models.py b/app_admin/models.py index 0d000a4af26c972e1b59749a201e067f4bfd4f27..5ad8346e64b68da4bf631aa02ee08055a94381be 100644 --- a/app_admin/models.py +++ b/app_admin/models.py @@ -4,9 +4,9 @@ from django.contrib.auth.models import User # 系统设置项模型 class SysSetting(models.Model): - name = models.CharField(verbose_name="项目",max_length=50,primary_key=True) - value = models.TextField(verbose_name="内容",null=True,blank=True) - types = models.CharField(verbose_name="类型",max_length=10,default="basic") + name = models.CharField(verbose_name="项目", max_length=50, primary_key=True) + value = models.TextField(verbose_name="内容", null=True, blank=True) + types = models.CharField(verbose_name="类型", max_length=10, default="basic") def __str__(self): return self.name @@ -18,9 +18,9 @@ class SysSetting(models.Model): # 用户选项配置 class UserOptions(models.Model): - user = models.ForeignKey(User,on_delete=models.CASCADE) + user = models.ForeignKey(User, on_delete=models.CASCADE) # 用户配置的编辑器选项,1表示Editormd编辑器,2表示Vditor编辑器,默认为1 - editor_mode = models.IntegerField(default=1,verbose_name="编辑器选项") + editor_mode = models.IntegerField(default=1, verbose_name="编辑器选项") def __str__(self): return self.user @@ -33,34 +33,69 @@ class UserOptions(models.Model): # 电子邮件验证码模型 class EmaiVerificationCode(models.Model): email_name = models.EmailField(verbose_name="电子邮箱") - verification_type = models.CharField(verbose_name="验证码类型",max_length=50) - verification_code = models.CharField(verbose_name="验证码",max_length=10) - create_time = models.DateTimeField(verbose_name="创建时间",auto_now_add=True) + verification_type = models.CharField(verbose_name="验证码类型", max_length=50) + verification_code = models.CharField(verbose_name="验证码", max_length=10) + create_time = models.DateTimeField(verbose_name="创建时间", auto_now_add=True) expire_time = models.DateTimeField(verbose_name="过期时间") def __str__(self): - return "{}:{}".format(self.verification_type,self.email_name) + return "{}:{}".format(self.verification_type, self.email_name) class Meta: verbose_name = '电子邮件验证码' verbose_name_plural = verbose_name + # 用户注册邀请码模型 class RegisterCode(models.Model): - code = models.CharField(verbose_name="注册邀请码",max_length=10,unique=True) + code = models.CharField(verbose_name="注册邀请码", max_length=10, unique=True) # 注册码的有效注册数量,表示注册码最多能够被使用多少次,默认为1 - all_cnt = models.IntegerField(verbose_name="有效注册数量",default=1) + all_cnt = models.IntegerField(verbose_name="有效注册数量", default=1) # 注册码的已使用数量,其值小于等于有效注册数量,默认为0 - used_cnt = models.IntegerField(verbose_name='已使用数量',default=0) + used_cnt = models.IntegerField(verbose_name='已使用数量', default=0) # 注册码状态:0表示数据已满,1表示有效,默认为1 - status = models.IntegerField(verbose_name="注册码状态",default=1) - user_list = models.CharField(verbose_name="使用此注册码的用户",default='',max_length=500,blank=True,null=True) - create_user = models.ForeignKey(User,on_delete=models.CASCADE) - create_time = models.DateTimeField(auto_now=True,verbose_name='创建时间') + status = models.IntegerField(verbose_name="注册码状态", default=1) + user_list = models.CharField(verbose_name="使用此注册码的用户", default='', max_length=500, blank=True, null=True) + create_user = models.ForeignKey(User, on_delete=models.CASCADE) + create_time = models.DateTimeField(auto_now=True, verbose_name='创建时间') def __str__(self): return self.code class Meta: verbose_name = '注册邀请码' - verbose_name_plural = verbose_name \ No newline at end of file + verbose_name_plural = verbose_name + +# 用户各平台 模型 +class Plant(models.Model): + plant_code = models.CharField(verbose_name="平台id", max_length=10, unique=True) + plant_name = models.CharField(verbose_name="平台名称", max_length=500, unique=True) + # 平台状态:0表示不可用,1表示可用,默认为1 + status = models.IntegerField(verbose_name="平台状态", default=1) + create_time = models.DateTimeField(auto_now=True, verbose_name='创建时间') + + def __str__(self): + return self.plant_code + + class Meta: + verbose_name = '平台管理' + verbose_name_plural = verbose_name + + +# 用户各平台 cookie模型 +class CookiePlant(models.Model): + cookie = models.TextField(verbose_name="平台Cookie", unique=True) + # Cookie状态:0表示失效,1表示有效,默认为1 + status = models.IntegerField(verbose_name="cookie状态", default=1) + create_user = models.ForeignKey(User, on_delete=models.CASCADE) + plant = models.ForeignKey(Plant, on_delete=models.CASCADE) + create_time = models.DateTimeField(auto_now=True, verbose_name='创建时间') + + def __str__(self): + return self.cookie + + class Meta: + verbose_name = '平台cookie管理' + verbose_name_plural = verbose_name + + diff --git a/app_admin/urls.py b/app_admin/urls.py index f4021c863012abbb9a19e7f0eebd863fba892c21..525518028312cebd46a4fd920db63f3ac655ba08 100644 --- a/app_admin/urls.py +++ b/app_admin/urls.py @@ -24,5 +24,7 @@ urlpatterns = [ path('admin_center/',views.admin_center,name="admin_center"), # 后台管理 path('admin/center_menu/',views.admin_center_menu,name="admin_center_menu"), # 后台管理菜单数据 path('admin_overview/',views.admin_overview,name="admin_overview"), # 后台管理仪表盘 + path('admin_plant_manage/',views.admin_plant_manage,name="admin_plant_manage"), # 发布平台管理 + path('admin_cookie_manage/',views.admin_cookie_manage,name="admin_cookie_manage"), # 发布平台 cookie 管理 ] \ No newline at end of file diff --git a/app_admin/views.py b/app_admin/views.py index c5806222c4ba5600dc2586489855651451578d0e..6b45a633dbf4093148ce273220f4e373baaeb45a 100644 --- a/app_admin/views.py +++ b/app_admin/views.py @@ -1,12 +1,14 @@ # coding:utf-8 -from django.shortcuts import render,redirect -from django.http.response import JsonResponse,HttpResponse,Http404 -from django.contrib.auth import authenticate,login,logout # 认证相关方法 -from django.contrib.auth.models import User # Django默认用户模型 -from django.contrib.auth.decorators import login_required # 登录需求装饰器 -from django.views.decorators.http import require_http_methods,require_GET,require_POST # 视图请求方法装饰器 -from django.core.paginator import Paginator,PageNotAnInteger,EmptyPage,InvalidPage # 后端分页 -from app_admin.decorators import superuser_only,open_register +import json + +from django.shortcuts import render, redirect +from django.http.response import JsonResponse, HttpResponse, Http404 +from django.contrib.auth import authenticate, login, logout # 认证相关方法 +from django.contrib.auth.models import User # Django默认用户模型 +from django.contrib.auth.decorators import login_required # 登录需求装饰器 +from django.views.decorators.http import require_http_methods, require_GET, require_POST # 视图请求方法装饰器 +from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage, InvalidPage # 后端分页 +from app_admin.decorators import superuser_only, open_register from django.core.exceptions import ObjectDoesNotExist from django.db.models import Q from django.urls import reverse @@ -15,6 +17,7 @@ import requests from app_doc.models import * from app_admin.models import * from app_admin.utils import * +from app_doc.spider.csdn_publish import CSDNPublish import traceback from loguru import logger import re @@ -44,16 +47,16 @@ def log_in(request): if request.user.is_authenticated: return redirect('/') else: - return render(request,'login.html',locals()) + return render(request, 'login.html', locals()) elif request.method == 'POST': try: - username = request.POST.get('username','') - pwd = request.POST.get('password','') + username = request.POST.get('username', '') + pwd = request.POST.get('password', '') if username != '' and pwd != '': - user = authenticate(username=username,password=pwd) + user = authenticate(username=username, password=pwd) if user is not None: if user.is_active: - login(request,user) + login(request, user) return redirect('/') else: errormsg = '用户被禁用!' @@ -78,41 +81,41 @@ def register(request): return redirect('/') else: if request.method == 'GET': - return render(request,'register.html',locals()) + return render(request, 'register.html', locals()) elif request.method == 'POST': - username = request.POST.get('username',None) - email = request.POST.get('email',None) - password = request.POST.get('password',None) - checkcode = request.POST.get("check_code",None) - register_code = request.POST.get("register_code",None) + username = request.POST.get('username', None) + email = request.POST.get('email', None) + password = request.POST.get('password', None) + checkcode = request.POST.get("check_code", None) + register_code = request.POST.get("register_code", None) is_register_code = SysSetting.objects.filter(types='basic', name='enable_register_code', value='on') - if is_register_code.count() > 0: # 开启了注册码设置 + if is_register_code.count() > 0: # 开启了注册码设置 try: - register_code_value = RegisterCode.objects.get(code=register_code,status=1) + register_code_value = RegisterCode.objects.get(code=register_code, status=1) except ObjectDoesNotExist: errormsg = '注册码无效!' return render(request, 'register.html', locals()) # 判断是否输入了用户名、邮箱和密码 if username and email and password: - if '@'in email: + if '@' in email: email_exit = User.objects.filter(email=email) username_exit = User.objects.filter(username=username) - if email_exit.count() > 0: # 验证电子邮箱 + if email_exit.count() > 0: # 验证电子邮箱 errormsg = '此电子邮箱已被注册!' return render(request, 'register.html', locals()) - elif username_exit.count() > 0: # 验证用户名 + elif username_exit.count() > 0: # 验证用户名 errormsg = '用户名已被使用!' return render(request, 'register.html', locals()) - elif re.match('^[0-9a-z]+$',username) is False: + elif re.match('^[0-9a-z]+$', username) is False: errormsg = '用户名只能为英文数字组合' return render(request, 'register.html', locals()) elif len(username) < 5: errormsg = '用户名必须大于等于5位!' return render(request, 'register.html', locals()) - elif len(password) < 6: # 验证密码长度 + elif len(password) < 6: # 验证密码长度 errormsg = '密码必须大于等于6位!' return render(request, 'register.html', locals()) - elif checkcode != request.session['CheckCode'].lower(): # 验证验证码 + elif checkcode != request.session['CheckCode'].lower(): # 验证验证码 errormsg = "验证码错误" return render(request, 'register.html', locals()) else: @@ -123,19 +126,19 @@ def register(request): user = authenticate(username=username, password=password) # 注册码数据更新 if is_register_code.count() > 0: - r_all_cnt = register_code_value.all_cnt # 注册码的最大使用次数 - r_used_cnt = register_code_value.used_cnt + 1 # 更新注册码的已使用次数 - r_use_user = register_code_value.user_list # 注册码的使用用户 - if r_used_cnt >= r_all_cnt: # 如果注册码已使用次数大于等于注册码的最大使用次数,则注册码失效 + r_all_cnt = register_code_value.all_cnt # 注册码的最大使用次数 + r_used_cnt = register_code_value.used_cnt + 1 # 更新注册码的已使用次数 + r_use_user = register_code_value.user_list # 注册码的使用用户 + if r_used_cnt >= r_all_cnt: # 如果注册码已使用次数大于等于注册码的最大使用次数,则注册码失效 RegisterCode.objects.filter(code=register_code).update( - status=0,# 注册码状态设为失效 - used_cnt = r_used_cnt, # 更新注册码的已使用次数 - user_list = r_use_user + email + ',', + status=0, # 注册码状态设为失效 + used_cnt=r_used_cnt, # 更新注册码的已使用次数 + user_list=r_use_user + email + ',', ) else: RegisterCode.objects.filter(code=register_code).update( - used_cnt=r_used_cnt, # 更新注册码的已使用次数 - user_list = r_use_user + email + ',', + used_cnt=r_used_cnt, # 更新注册码的已使用次数 + user_list=r_use_user + email + ',', ) if user.is_active: login(request, user) @@ -163,15 +166,15 @@ def log_out(request): # 忘记密码 def forget_pwd(request): if request.method == 'GET': - return render(request,'forget_pwd.html',locals()) + return render(request, 'forget_pwd.html', locals()) elif request.method == 'POST': - email = request.POST.get("email",None) # 邮箱 - vcode = request.POST.get("vcode",None) # 验证码 - new_pwd= request.POST.get('password',None) # 密码 + email = request.POST.get("email", None) # 邮箱 + vcode = request.POST.get("vcode", None) # 验证码 + new_pwd = request.POST.get('password', None) # 密码 new_pwd_confirm = request.POST.get('confirm_password') # 查询验证码和邮箱是否匹配 try: - data = EmaiVerificationCode.objects.get(email_name=email,verification_code=vcode,verification_type='忘记密码') + data = EmaiVerificationCode.objects.get(email_name=email, verification_code=vcode, verification_type='忘记密码') expire_time = data.expire_time if expire_time > datetime.datetime.now(): user = User.objects.get(email=email) @@ -189,14 +192,14 @@ def forget_pwd(request): except Exception as e: logger.exception("修改密码异常") errormsg = "验证码或邮箱错误" - return render(request,'forget_pwd.html',locals()) + return render(request, 'forget_pwd.html', locals()) # 发送电子邮箱验证码 @logger.catch() def send_email_vcode(request): if request.method == 'POST': - email = request.POST.get('email',None) + email = request.POST.get('email', None) is_email = User.objects.filter(email=email) if is_email.count() != 0: vcode_str = generate_vcode() @@ -208,19 +211,19 @@ def send_email_vcode(request): expire_time = now_time + datetime.timedelta(minutes=30) # 创建数据库记录 EmaiVerificationCode.objects.create( - email_name = email, - verification_type = '忘记密码', - verification_code = vcode_str, - expire_time = expire_time + email_name=email, + verification_type='忘记密码', + verification_code=vcode_str, + expire_time=expire_time ) - return JsonResponse({'status':True,'data':'发送成功'}) + return JsonResponse({'status': True, 'data': '发送成功'}) else: - return JsonResponse({'status':False,'data':'发送验证码出错,请重试!'}) + return JsonResponse({'status': False, 'data': '发送验证码出错,请重试!'}) else: - return JsonResponse({'status':False,'data':'电子邮箱不存在!'}) + return JsonResponse({'status': False, 'data': '电子邮箱不存在!'}) else: - return JsonResponse({'status':False,'data':'方法错误'}) + return JsonResponse({'status': False, 'data': '方法错误'}) # 后台管理 - 仪表盘 @@ -230,9 +233,9 @@ def admin_overview(request): # 用户数 user_cnt = User.objects.all().count() # 文集数 - pro_cnt = Project.objects.all().count() # 文集总数 + pro_cnt = Project.objects.all().count() # 文集总数 # 文档数 - doc_cnt = Doc.objects.all().count() # 文档总数 + doc_cnt = Doc.objects.all().count() # 文档总数 total_tag_cnt = Tag.objects.filter(create_user=request.user).count() img_cnt = Image.objects.filter(user=request.user).count() attachment_cnt = Attachment.objects.filter(user=request.user).count() @@ -240,10 +243,11 @@ def admin_overview(request): doc_active_list = Doc.objects.all().order_by('-modify_time')[:5] # 个人文集列表 pro_list = Project.objects.filter(create_user=request.user).order_by('-create_time') - return render(request,'app_admin/admin_overview.html',locals()) + return render(request, 'app_admin/admin_overview.html', locals()) else: pass + # 后台管理 - 用户管理 @superuser_only @logger.catch() @@ -252,31 +256,31 @@ def admin_user(request): # user_list = User.objects.all() return render(request, 'app_admin/admin_user.html', locals()) elif request.method == 'POST': - username = request.POST.get('username','') + username = request.POST.get('username', '') if username == '': user_data = User.objects.all().values_list( - 'id','last_login','is_superuser','username','email','date_joined','is_active','first_name' + 'id', 'last_login', 'is_superuser', 'username', 'email', 'date_joined', 'is_active', 'first_name' ) else: user_data = User.objects.filter(username__icontains=username).values_list( - 'id','last_login','is_superuser','username','email','date_joined','is_active','first_name' + 'id', 'last_login', 'is_superuser', 'username', 'email', 'date_joined', 'is_active', 'first_name' ) table_data = [] for i in list(user_data): item = { - 'id':i[0], - 'last_login':i[1], - 'is_superuser':i[2], - 'username':i[3], - 'email':i[4], - 'date_joined':i[5], - 'is_active':i[6], - 'first_name':i[7] + 'id': i[0], + 'last_login': i[1], + 'is_superuser': i[2], + 'username': i[3], + 'email': i[4], + 'date_joined': i[5], + 'is_active': i[6], + 'first_name': i[7] } table_data.append(item) - return JsonResponse({'status':True,'data':table_data}) + return JsonResponse({'status': True, 'data': table_data}) else: - return JsonResponse({'status':False,'data':'方法错误'}) + return JsonResponse({'status': False, 'data': '方法错误'}) # 后台管理 - 创建用户 @@ -284,18 +288,18 @@ def admin_user(request): @logger.catch() def admin_create_user(request): if request.method == 'POST': - username = request.POST.get('username','') # 接收用户名参数 - email = request.POST.get('email','') # 接收email参数 - password = request.POST.get('password','') # 接收密码参数 - user_type = request.POST.get('user_type',0) # 用户类型 0为普通用户,1位管理员 + username = request.POST.get('username', '') # 接收用户名参数 + email = request.POST.get('email', '') # 接收email参数 + password = request.POST.get('password', '') # 接收密码参数 + user_type = request.POST.get('user_type', 0) # 用户类型 0为普通用户,1位管理员 if username != '' and password != '' and email != '' and \ - '@' in email and re.match(r'^[0-9a-z]',username) and len(username) >= 5 : + '@' in email and re.match(r'^[0-9a-z]', username) and len(username) >= 5: # 不允许电子邮箱重复 - if User.objects.filter(email = email).count() > 0: - return JsonResponse({'status':False,'data':'电子邮箱不可重复'}) + if User.objects.filter(email=email).count() > 0: + return JsonResponse({'status': False, 'data': '电子邮箱不可重复'}) # 不允许重复的用户名 - if User.objects.filter(username = username).count() > 0: - return JsonResponse({'status': False,'data':'用户名不可重复'}) + if User.objects.filter(username=username).count() > 0: + return JsonResponse({'status': False, 'data': '用户名不可重复'}) try: if user_type == 0: user = User.objects.create_user( @@ -311,11 +315,11 @@ def admin_create_user(request): email=email ) user.save() - return JsonResponse({'status':True}) + return JsonResponse({'status': True}) except Exception as e: - return JsonResponse({'status':False,'data':'系统异常'}) + return JsonResponse({'status': False, 'data': '系统异常'}) else: - return JsonResponse({'status':False,'data':'请检查参数'}) + return JsonResponse({'status': False, 'data': '请检查参数'}) else: return HttpResponse('方法不允许') @@ -326,24 +330,24 @@ def admin_create_user(request): def admin_change_pwd(request): if request.method == 'POST': try: - user_id = request.POST.get('user_id',None) - password = request.POST.get('password',None) - password2 = request.POST.get('password2',None) + user_id = request.POST.get('user_id', None) + password = request.POST.get('password', None) + password2 = request.POST.get('password2', None) if user_id and password: if password == password2: user = User.objects.get(id=int(user_id)) user.set_password(password) user.save() - return JsonResponse({'status':True,'data':'修改成功'}) + return JsonResponse({'status': True, 'data': '修改成功'}) else: - return JsonResponse({'status':False,'data':'两个密码不一致'}) + return JsonResponse({'status': False, 'data': '两个密码不一致'}) else: - return JsonResponse({'status':False,'data':'参数错误'}) + return JsonResponse({'status': False, 'data': '参数错误'}) except Exception as e: print(repr(e)) - return JsonResponse({'status':False,'data':'请求错误'}) + return JsonResponse({'status': False, 'data': '请求错误'}) else: - return JsonResponse({'status':False,'data':'方法错误'}) + return JsonResponse({'status': False, 'data': '方法错误'}) # 后台管理 - 删除用户 @@ -352,21 +356,21 @@ def admin_change_pwd(request): def admin_del_user(request): if request.method == 'POST': try: - user_id = request.POST.get('user_id',None) # 获取用户ID - user = User.objects.get(id=int(user_id)) # 获取用户 - colloas = ProjectCollaborator.objects.filter(user=user) # 获取参与协作的文集 + user_id = request.POST.get('user_id', None) # 获取用户ID + user = User.objects.get(id=int(user_id)) # 获取用户 + colloas = ProjectCollaborator.objects.filter(user=user) # 获取参与协作的文集 # 遍历用户参与协作的文集 for colloa in colloas: # 查询出用户协作创建的文档,修改作者为文集所有者 Doc.objects.filter( - top_doc=colloa.project.id,create_user=user + top_doc=colloa.project.id, create_user=user ).update(create_user=colloa.project.create_user) user.delete() - return JsonResponse({'status':True,'data':'删除成功'}) + return JsonResponse({'status': True, 'data': '删除成功'}) except Exception as e: - return JsonResponse({'status':False,'data':'删除出错'}) + return JsonResponse({'status': False, 'data': '删除出错'}) else: - return JsonResponse({'status':False,'data':'方法错误'}) + return JsonResponse({'status': False, 'data': '方法错误'}) # 后台管理 - 文集管理 @@ -374,7 +378,7 @@ def admin_del_user(request): @logger.catch() def admin_project(request): if request.method == 'GET': - return render(request,'app_admin/admin_project.html',locals()) + return render(request, 'app_admin/admin_project.html', locals()) elif request.method == 'POST': kw = request.POST.get('kw', '') page = request.POST.get('page', 1) @@ -403,8 +407,8 @@ def admin_project(request): 'role': project.role, 'role_value': project.role_value, 'colla_total': ProjectCollaborator.objects.filter(project=project).count(), - 'is_top':project.is_top, - 'create_user':project.create_user.username, + 'is_top': project.is_top, + 'create_user': project.create_user.username, 'create_time': project.create_time, 'modify_time': project.modify_time } @@ -417,30 +421,31 @@ def admin_project(request): } return JsonResponse(resp_data) + # 后台管理 - 修改文集权限 @superuser_only @logger.catch() -def admin_project_role(request,pro_id): +def admin_project_role(request, pro_id): pro = Project.objects.get(id=pro_id) if request.method == 'GET': - return render(request,'app_admin/admin_project_role.html',locals()) + return render(request, 'app_admin/admin_project_role.html', locals()) elif request.method == 'POST': - role_type = request.POST.get('role','') + role_type = request.POST.get('role', '') if role_type != '': - if int(role_type) in [0,1]:# 公开或私密 + if int(role_type) in [0, 1]: # 公开或私密 Project.objects.filter(id=int(pro_id)).update( - role = role_type, - modify_time = datetime.datetime.now() + role=role_type, + modify_time=datetime.datetime.now() ) - if int(role_type) == 2: # 指定用户可见 - role_value = request.POST.get('tagsinput','') + if int(role_type) == 2: # 指定用户可见 + role_value = request.POST.get('tagsinput', '') Project.objects.filter(id=int(pro_id)).update( role=role_type, - role_value = role_value, - modify_time = datetime.datetime.now() + role_value=role_value, + modify_time=datetime.datetime.now() ) - if int(role_type) == 3: # 访问码可见 - role_value = request.POST.get('viewcode','') + if int(role_type) == 3: # 访问码可见 + role_value = request.POST.get('viewcode', '') Project.objects.filter(id=int(pro_id)).update( role=role_type, role_value=role_value, @@ -451,6 +456,7 @@ def admin_project_role(request,pro_id): else: return Http404 + # 后台管理 - 控制文集置顶状态 @superuser_only @require_POST @@ -463,10 +469,10 @@ def admin_project_istop(request): else: is_top = False Project.objects.filter(id=project_id).update(is_top=is_top) - return JsonResponse({'status':True}) + return JsonResponse({'status': True}) except: logger.exception("置顶文集出错") - return JsonResponse({'status':False,'data':'执行出错'}) + return JsonResponse({'status': False, 'data': '执行出错'}) # 后台管理 - 文档管理 @@ -483,7 +489,7 @@ def admin_doc(request): draft_doc_cnt = Doc.objects.filter(status=0).count() # 所有文档数量 all_cnt = published_doc_cnt + draft_doc_cnt - return render(request,'app_admin/admin_doc.html',locals()) + return render(request, 'app_admin/admin_doc.html', locals()) elif request.method == 'POST': kw = request.POST.get('kw', '') project = request.POST.get('project', '') @@ -543,13 +549,14 @@ def admin_doc(request): item = { 'id': doc.id, 'name': doc.name, + 'plant_list': doc.plant_list, 'parent': Doc.objects.get(id=doc.parent_doc).name if doc.parent_doc != 0 else '无', 'project_id': Project.objects.get(id=doc.top_doc).id, 'project_name': Project.objects.get(id=doc.top_doc).name, 'status': doc.status, 'editor_mode': doc.editor_mode, 'open_children': doc.open_children, - 'create_user':doc.create_user.username, + 'create_user': doc.create_user.username, 'create_time': doc.create_time, 'modify_time': doc.modify_time } @@ -568,7 +575,7 @@ def admin_doc(request): @logger.catch() def admin_doctemp(request): if request.method == 'GET': - kw = request.GET.get('kw','') + kw = request.GET.get('kw', '') if kw == '': doctemp_list = DocTemp.objects.all() paginator = Paginator(doctemp_list, 10) @@ -590,7 +597,7 @@ def admin_doctemp(request): except EmptyPage: doctemps = paginator.page(paginator.num_pages) doctemps.kw = kw - return render(request,'app_admin/admin_doctemp.html',locals()) + return render(request, 'app_admin/admin_doctemp.html', locals()) # 后台管理 - 注册邀请码管理 @@ -608,15 +615,15 @@ def admin_register_code(request): codes = paginator.page(1) except EmptyPage: codes = paginator.page(paginator.num_pages) - return render(request,'app_admin/admin_register_code.html',locals()) + return render(request, 'app_admin/admin_register_code.html', locals()) elif request.method == 'POST': - types = request.POST.get('types',None) + types = request.POST.get('types', None) if types is None: - return JsonResponse({'status':False,'data':'参数错误'}) + return JsonResponse({'status': False, 'data': '参数错误'}) # types表示注册码操作的类型,1表示新增、2表示删除 if int(types) == 1: try: - all_cnt = int(request.POST.get('all_cnt',1)) # 注册码的最大使用次数 + all_cnt = int(request.POST.get('all_cnt', 1)) # 注册码的最大使用次数 if all_cnt <= 0: return JsonResponse({'status': False, 'data': '最大使用次数不可为负数'}) is_code = False @@ -624,34 +631,34 @@ def admin_register_code(request): code_str = '0123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM' random_code = ''.join(random.sample(code_str, k=10)) random_code_used = RegisterCode.objects.filter(code=random_code).count() - if random_code_used > 0: # 已存在此注册码,继续生成一个注册码 + if random_code_used > 0: # 已存在此注册码,继续生成一个注册码 is_code = False - else:# 数据库中不存在此注册码,跳出循环 + else: # 数据库中不存在此注册码,跳出循环 is_code = True # 创建一个注册码 RegisterCode.objects.create( - code = random_code, - all_cnt = all_cnt, - create_user = request.user + code=random_code, + all_cnt=all_cnt, + create_user=request.user ) - return JsonResponse({'status':True,'data':'新增成功'}) + return JsonResponse({'status': True, 'data': '新增成功'}) except Exception as e: logger.exception("生成注册码异常") - return JsonResponse({'status': False,'data':'系统异常'}) + return JsonResponse({'status': False, 'data': '系统异常'}) elif int(types) == 2: - code_id = request.POST.get('code_id',None) + code_id = request.POST.get('code_id', None) try: register_code = RegisterCode.objects.get(id=int(code_id)) register_code.delete() - return JsonResponse({'status':True,'data':'删除成功'}) + return JsonResponse({'status': True, 'data': '删除成功'}) except ObjectDoesNotExist: - return JsonResponse({'status':False,'data':'注册码不存在'}) + return JsonResponse({'status': False, 'data': '注册码不存在'}) except: - return JsonResponse({'status':False,'data':'系统异常'}) + return JsonResponse({'status': False, 'data': '系统异常'}) else: - return JsonResponse({'status':False,'data':'类型错误'}) + return JsonResponse({'status': False, 'data': '类型错误'}) else: - return JsonResponse({'status': False,'data':'方法错误'}) + return JsonResponse({'status': False, 'data': '方法错误'}) # 普通用户修改密码 @@ -660,21 +667,21 @@ def admin_register_code(request): def change_pwd(request): if request.method == 'POST': try: - password = request.POST.get('password',None) - password2 = request.POST.get('password2',None) + password = request.POST.get('password', None) + password2 = request.POST.get('password2', None) print(password, password2) - if password and password== password2: + if password and password == password2: if len(password) >= 6: user = User.objects.get(id=request.user.id) user.set_password(password) user.save() - return JsonResponse({'status':True,'data':'修改成功'}) + return JsonResponse({'status': True, 'data': '修改成功'}) else: - return JsonResponse({'status':False,'data':'密码不得少于6位数'}) + return JsonResponse({'status': False, 'data': '密码不得少于6位数'}) else: - return JsonResponse({'status':False,'data':'两个密码不一致'}) + return JsonResponse({'status': False, 'data': '两个密码不一致'}) except Exception as e: - return JsonResponse({'status':False,'data':'修改出错'}) + return JsonResponse({'status': False, 'data': '修改出错'}) else: return HttpResponse('方法错误') @@ -692,27 +699,27 @@ def admin_setting(request): email_ssl = email_settings.get(name="smtp_ssl") email_pwd = email_settings.get(name="pwd") if request.method == 'GET': - return render(request,'app_admin/admin_setting.html',locals()) + return render(request, 'app_admin/admin_setting.html', locals()) elif request.method == 'POST': - types = request.POST.get('type',None) + types = request.POST.get('type', None) # 基础设置 if types == 'basic': - site_name = request.POST.get('site_name',None) # 站点名称 + site_name = request.POST.get('site_name', None) # 站点名称 site_sub_name = request.POST.get('site_sub_name', None) # 站点子标题 site_keywords = request.POST.get('site_keywords', None) # 站点关键词 site_desc = request.POST.get('site_desc', None) # 站点描述 beian_code = request.POST.get('beian_code', None) # 备案号 - index_project_sort = request.POST.get('index_project_sort','1') # 首页文集默认排序 - 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 + index_project_sort = request.POST.get('index_project_sort', '1') # 首页文集默认排序 + 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 ad_code_3 = request.POST.get('ad_code_3', None) # 广告位3 - enbale_email = request.POST.get("enable_email",None) # 启用邮箱 - img_scale = request.POST.get('img_scale',None) # 图片缩略 - enable_register_code = request.POST.get('enable_register_code',None) # 注册邀请码 - enable_project_report = request.POST.get('enable_project_report',None) # 文集导出 + enbale_email = request.POST.get("enable_email", None) # 启用邮箱 + img_scale = request.POST.get('img_scale', None) # 图片缩略 + 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='index_project_sort', @@ -721,7 +728,7 @@ def admin_setting(request): # 更新开放注册状态 SysSetting.objects.update_or_create( name='require_login', - defaults={'value':require_login,'types':'basic'} + defaults={'value': require_login, 'types': 'basic'} ) # 更新全站登录状态 SysSetting.objects.update_or_create( @@ -730,13 +737,13 @@ def admin_setting(request): ) # 更新统计代码状态 SysSetting.objects.update_or_create( - name = 'static_code', - defaults={'value':static_code,'types':'basic'} + name='static_code', + defaults={'value': static_code, 'types': 'basic'} ) # 更新广告代码状态 SysSetting.objects.update_or_create( - name = 'ad_code', - defaults={'value':ad_code,'types':'basic'} + name='ad_code', + defaults={'value': ad_code, 'types': 'basic'} ) SysSetting.objects.update_or_create( name='ad_code_2', @@ -750,7 +757,7 @@ def admin_setting(request): # 更新备案号 SysSetting.objects.update_or_create( name='beian_code', - defaults={'value':beian_code,'types':'basic'} + defaults={'value': beian_code, 'types': 'basic'} ) # 更新站点名称 SysSetting.objects.update_or_create( @@ -785,32 +792,32 @@ def admin_setting(request): ) # 更新注册码启停状态 SysSetting.objects.update_or_create( - name = 'enable_register_code', - defaults= {'value': enable_register_code, 'types':'basic'} + name='enable_register_code', + defaults={'value': enable_register_code, 'types': 'basic'} ) # 更新文集导出状态 SysSetting.objects.update_or_create( - name = 'enable_project_report', - defaults={'value':enable_project_report,'types':'basic'} + name='enable_project_report', + defaults={'value': enable_project_report, 'types': 'basic'} ) - return render(request,'app_admin/admin_setting.html',locals()) + return render(request, 'app_admin/admin_setting.html', locals()) # 邮箱设置 elif types == 'email': # 读取上传的参数 - emailer = request.POST.get("send_emailer",None) - host = request.POST.get("smtp_host",None) - port = request.POST.get("smtp_port",None) - username = request.POST.get("smtp_username",None) - pwd = request.POST.get("smtp_pwd",None) - ssl = request.POST.get("smtp_ssl",None) + emailer = request.POST.get("send_emailer", None) + host = request.POST.get("smtp_host", None) + port = request.POST.get("smtp_port", None) + username = request.POST.get("smtp_username", None) + pwd = request.POST.get("smtp_pwd", None) + ssl = request.POST.get("smtp_ssl", None) # 对密码进行加密 pwd = enctry(pwd) if emailer != None: # 更新发件箱 SysSetting.objects.update_or_create( - name = 'send_emailer', - defaults={"value":emailer,"types":'email'} + name='send_emailer', + defaults={"value": emailer, "types": 'email'} ) if host != None: # 更新邮箱主机 @@ -849,14 +856,14 @@ def admin_setting(request): email_username = email_settings.get(name="username") email_ssl = email_settings.get(name="smtp_ssl") email_pwd = email_settings.get(name="pwd") - return render(request, 'app_admin/admin_setting.html',locals()) + return render(request, 'app_admin/admin_setting.html', locals()) # 文档全局设置 elif types == 'doc': # iframe白名单 - iframe_whitelist = request.POST.get('iframe_whitelist','') + iframe_whitelist = request.POST.get('iframe_whitelist', '') SysSetting.objects.update_or_create( - name = 'iframe_whitelist', - defaults = {'value':iframe_whitelist,'types':'doc'} + name='iframe_whitelist', + defaults={'value': iframe_whitelist, 'types': 'doc'} ) # 上传图片大小 img_size = request.POST.get('img_size', 10) @@ -874,13 +881,13 @@ def admin_setting(request): ) # 附件格式白名单 - attachment_suffix = request.POST.get('attachment_suffix','') + attachment_suffix = request.POST.get('attachment_suffix', '') SysSetting.objects.update_or_create( - name = 'attachment_suffix', - defaults = {'value':attachment_suffix,'types':'doc'} + name='attachment_suffix', + defaults={'value': attachment_suffix, 'types': 'doc'} ) # 附件大小 - attachment_size = request.POST.get('attachment_size',50) + attachment_size = request.POST.get('attachment_size', 50) try: if int(attachment_size) == 0: attachment_size = 50 @@ -899,14 +906,198 @@ def admin_setting(request): # 检测版本更新 def check_update(request): url = 'https://gitee.com/api/v5/repos/zmister/MrDoc/tags' - resp = requests.get(url,timeout=5).json() - return JsonResponse({'status':True,'data':resp[-1]}) + resp = requests.get(url, timeout=5).json() + return JsonResponse({'status': True, 'data': resp[-1]}) # 后台管理 @superuser_only def admin_center(request): - return render(request,'app_admin/admin_center.html',locals()) + return render(request, 'app_admin/admin_center.html', locals()) + + +# 后台管理 - 发布平台管理 +@superuser_only +@logger.catch() +def admin_plant_manage(request): + # 发布平台管理页面 + if request.method == 'GET': + register_codes = Plant.objects.all() + for i in register_codes: + print(i.status) + # print(register_codes) + paginator = Paginator(register_codes, 10) + page = request.GET.get('page', 1) + try: + codes = paginator.page(page) + except PageNotAnInteger: + codes = paginator.page(1) + except EmptyPage: + codes = paginator.page(paginator.num_pages) + print(codes) + return render(request, 'app_admin/admin_plant_manage.html', locals()) + elif request.method == 'POST': + types = request.POST.get('types', None) + if types is None: + return JsonResponse({'status': False, 'data': '参数错误'}) + # types表示注册码操作的类型,1表示新增、2表示修改 + if int(types) == 1: + try: + plant_name = request.POST.get('plant_name') # 注册码的最大使用次数 + random_code = None + is_code = False + while is_code is False: + code_str = '0123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM' + random_code = ''.join(random.sample(code_str, k=5)) + random_code_used = Plant.objects.filter(plant_code=random_code).count() + if random_code_used > 0: # 已存在此code + is_code = False + else: # 数据库中不存在此码,跳出循环 + is_code = True + # 创建一个注册码 + Plant.objects.create( + plant_code=random_code, + plant_name=plant_name, + status=1 + ) + return JsonResponse({'status': True, 'data': '新增成功'}) + except Exception as e: + logger.exception("新增发布平台异常") + return JsonResponse({'status': False, 'data': '系统异常'}) + elif int(types) == 2: + code_id = request.POST.get('code_id', None) + status = request.POST.get('status') + print('============', status) + try: + if int(status) == 1: + status = 0 + else: + status = 1 + print('更新后:', status) + Plant.objects.filter(id=int(code_id)).update(status=status) + # print(register_code) + return JsonResponse({'status': True, 'data': '操作成功'}) + except ObjectDoesNotExist: + return JsonResponse({'status': False, 'data': '当前平台不存在'}) + except: + print(traceback.format_exc()) + return JsonResponse({'status': False, 'data': '系统异常'}) + elif int(types) == 3: # 删除 + code_id = request.POST.get('code_id', None) + try: + Plant.objects.filter(id=int(code_id)).delete() + # print(register_code) + return JsonResponse({'status': True, 'data': '删除成功'}) + except ObjectDoesNotExist: + return JsonResponse({'status': False, 'data': '当前平台不存在'}) + except: + print(traceback.format_exc()) + return JsonResponse({'status': False, 'data': '系统异常'}) + + else: + return JsonResponse({'status': False, 'data': '类型错误'}) + else: + return JsonResponse({'status': False, 'data': '方法错误'}) + + +# 后台管理 - 发布平台cookie管理 +@superuser_only +@logger.catch() +def admin_cookie_manage(request): + # 发布平台cookie管理页面 + if request.method == 'GET': + plant_list = Plant.objects.all() + register_codes = CookiePlant.objects.all() + # print(register_codes) + paginator = Paginator(register_codes, 10) + page = request.GET.get('page', 1) + try: + codes = paginator.page(page) + except PageNotAnInteger: + codes = paginator.page(1) + except EmptyPage: + codes = paginator.page(paginator.num_pages) + return render(request, 'app_admin/admin_cookie_manage.html', locals()) + elif request.method == 'POST': + types = request.POST.get('types', None) + if types is None: + return JsonResponse({'status': False, 'data': '参数错误'}) + # types表示操作的类型,1表示新增、2表示修改 + if int(types) == 1: + try: + plant_cookie = request.POST.get('plant_cookie') # cookie + plant_id = request.POST.get('plant_id') # 平台id + plant = Plant.objects.filter(id=int(plant_id)) + # 创建一个注册码 + CookiePlant.objects.create( + cookie=plant_cookie, + plant=plant[0], + status=1, + create_user=request.user + ) + return JsonResponse({'status': True, 'data': '新增成功'}) + except Exception as e: + logger.exception("新增发布平台异常") + return JsonResponse({'status': False, 'data': '系统异常'}) + elif int(types) == 2: # 更新 cookie + code_id = request.POST.get('code_id', None) + cookie = request.POST.get('cookie') + try: + + CookiePlant.objects.filter(id=int(code_id)).update(status=1, cookie=cookie) + # print(register_code) + return JsonResponse({'status': True, 'data': '操作成功'}) + except ObjectDoesNotExist: + return JsonResponse({'status': False, 'data': '当前平台不存在'}) + except: + print(traceback.format_exc()) + return JsonResponse({'status': False, 'data': '系统异常'}) + elif int(types) == 3: # 发布文章 + code_id = request.POST.get('code_id', None) + doc_id = request.POST.get('doc_id', None) + doc_tags = request.POST.get('tags', None) + try: + doc = Doc.objects.filter(id=int(doc_id)) + doc_create_user = doc[0].create_user + doc_name = doc[0].name + doc_pre_content = doc[0].pre_content + # 已发布平台 + doc_plant_list = doc[0].plant_list + doc_content = doc[0].content + cookie = CookiePlant.objects.filter(id=int(code_id)) + cookie_user = cookie[0].create_user + plant_cookie = cookie[0].cookie + plant_name = cookie[0].plant.plant_name + if plant_name in doc_plant_list: + return JsonResponse({'status': False, 'data': f'当前文章在 {plant_name} 平台已发布'}) + if doc_create_user == cookie_user and cookie_user == request.user: + result = CSDNPublish().publish_content(tags=doc_tags, markdowncontent=doc_pre_content, + content=doc_content, cookie=cookie, title=doc_name) + if result: + result_json = json.loads(result) + code = result_json.get('code') + msg = result_json.get('msg') + publish_url = result_json.get('data').get('url') + if code == 200 and msg == 'success' and publish_url: + if doc_plant_list: + plant_list = doc_plant_list + ',' + plant_name + else: + plant_list = plant_name + doc.update(plant_list=plant_list) + # print(register_code) + return JsonResponse({'status': True, 'data': '发布成功'}) + return JsonResponse({'status': False, 'data': '发布失败'}) + else: + return JsonResponse({'status': False, 'data': '只能发布自己的文章哦!'}) + except ObjectDoesNotExist: + return JsonResponse({'status': False, 'data': '当前平台不存在'}) + except: + print(traceback.format_exc()) + return JsonResponse({'status': False, 'data': '系统异常'}) + else: + return JsonResponse({'status': False, 'data': '类型错误'}) + else: + return JsonResponse({'status': False, 'data': '方法错误'}) # 后台管理菜单 @@ -961,6 +1152,20 @@ def admin_center_menu(request): "icon": "layui-icon layui-icon-set", "href": reverse('sys_setting'), }, + { + "id": 8, + "title": "发布平台管理", + "type": 1, + "icon": "layui-icon layui-icon-user", + "href": reverse('admin_plant_manage'), + }, + { + "id": 9, + "title": "发布平台 cookie 管理", + "type": 1, + "icon": "layui-icon layui-icon-user", + "href": reverse('admin_cookie_manage'), + }, { "id": "common", "title": "使用帮助", @@ -984,4 +1189,4 @@ def admin_center_menu(request): }] } ] - return JsonResponse(menu_data,safe=False) + return JsonResponse(menu_data, safe=False) diff --git a/app_doc/models.py b/app_doc/models.py index a4b7ce1875f29cb517c43e915bd8a2ade3185ac4..8d938729f74d4c32a4f012f92845019ec7bd4ecc 100644 --- a/app_doc/models.py +++ b/app_doc/models.py @@ -4,17 +4,17 @@ from django.contrib.auth.models import User # 文集模型 class Project(models.Model): - name = models.CharField(verbose_name="文集名称",max_length=50) - icon= models.CharField(verbose_name="文集图标",max_length=50,blank=True,null=True,default=None) + name = models.CharField(verbose_name="文集名称", max_length=50) + icon = models.CharField(verbose_name="文集图标", max_length=50, blank=True, null=True, default=None) intro = models.TextField(verbose_name="介绍") # 文集权限说明:0表示公开,1表示私密,2表示指定用户可见,3表示访问码可见 默认公开 - role = models.IntegerField(choices=((0,0),(1,1),(2,2),(3,3)), default=0,verbose_name="文集权限") - role_value = models.TextField(verbose_name="文集权限值",blank=True,null=True) - is_watermark = models.BooleanField(verbose_name="水印状态",default=False) - watermark_type = models.IntegerField(verbose_name="水印类型",default=1) # 1表示文字水印 2表示图片水印 - watermark_value = models.CharField(verbose_name="水印内容",null=True,blank=True,default='',max_length=250) - is_top = models.BooleanField(verbose_name="是否置顶",default=False) - create_user = models.ForeignKey(User,on_delete=models.CASCADE) + role = models.IntegerField(choices=((0, 0), (1, 1), (2, 2), (3, 3)), default=0, verbose_name="文集权限") + role_value = models.TextField(verbose_name="文集权限值", blank=True, null=True) + is_watermark = models.BooleanField(verbose_name="水印状态", default=False) + watermark_type = models.IntegerField(verbose_name="水印类型", default=1) # 1表示文字水印 2表示图片水印 + watermark_value = models.CharField(verbose_name="水印内容", null=True, blank=True, default='', max_length=250) + is_top = models.BooleanField(verbose_name="是否置顶", default=False) + create_user = models.ForeignKey(User, on_delete=models.CASCADE) create_time = models.DateTimeField(auto_now_add=True) modify_time = models.DateTimeField(auto_now=True) @@ -29,18 +29,18 @@ class Project(models.Model): from django.urls import reverse return reverse("pro_index", kwargs={ - "pro_id":self.pk} + "pro_id": self.pk} ) # 文集协作模型 class ProjectCollaborator(models.Model): - project = models.ForeignKey(Project,on_delete=models.CASCADE) # 文集 - user = models.ForeignKey(User,on_delete=models.CASCADE) # 用户 + project = models.ForeignKey(Project, on_delete=models.CASCADE) # 文集 + user = models.ForeignKey(User, on_delete=models.CASCADE) # 用户 # 用户的协作模式:0表示可新建文档,可修改、删除自己新建的文档,1表示可新建文档,可删除自己创建的文档、可修改所有文档 - role = models.IntegerField(choices=((0,0),(1,1)),default=0,verbose_name='协作模式') - create_time = models.DateTimeField(auto_now=True,verbose_name='添加时间') - modify_time = models.DateTimeField(auto_now_add=True,verbose_name='修改时间') + role = models.IntegerField(choices=((0, 0), (1, 1)), default=0, verbose_name='协作模式') + create_time = models.DateTimeField(auto_now=True, verbose_name='添加时间') + modify_time = models.DateTimeField(auto_now_add=True, verbose_name='修改时间') def __str__(self): return self.project @@ -52,7 +52,7 @@ class ProjectCollaborator(models.Model): # 文集目录模型 class ProjectToc(models.Model): - project = models.ForeignKey(Project,on_delete=models.CASCADE) + project = models.ForeignKey(Project, on_delete=models.CASCADE) value = models.TextField(verbose_name="文集文档层级目录") def __str__(self): @@ -65,21 +65,22 @@ class ProjectToc(models.Model): # 文档模型 class Doc(models.Model): - name = models.CharField(verbose_name="文档标题",max_length=50) - pre_content = models.TextField(verbose_name="编辑内容",null=True,blank=True) - content = models.TextField(verbose_name="文档内容",null=True,blank=True) - parent_doc = models.IntegerField(default=0,verbose_name="上级文档") - top_doc = models.IntegerField(default=0,verbose_name="所属项目") - sort = models.IntegerField(verbose_name='排序',default=99) - create_user = models.ForeignKey(User,on_delete=models.CASCADE) + name = models.CharField(verbose_name="文档标题", max_length=50) + pre_content = models.TextField(verbose_name="编辑内容", null=True, blank=True) + content = models.TextField(verbose_name="文档内容", null=True, blank=True) + parent_doc = models.IntegerField(default=0, verbose_name="上级文档") + top_doc = models.IntegerField(default=0, verbose_name="所属项目") + sort = models.IntegerField(verbose_name='排序', default=99) + create_user = models.ForeignKey(User, on_delete=models.CASCADE) create_time = models.DateTimeField(auto_now_add=True) modify_time = models.DateTimeField(auto_now=True) # 文档状态说明:0表示草稿状态,1表示发布状态,2表示删除状态 - status = models.IntegerField(choices=((0,0),(1,1)),default=1,verbose_name='文档状态') + status = models.IntegerField(choices=((0, 0), (1, 1)), default=1, verbose_name='文档状态') + plant_list = models.CharField(default='', verbose_name='已发布平台', max_length=1000) # 编辑器模式:1表示Editormd编辑器,2表示Vditor编辑器,3表示iceEditor编辑器 - editor_mode = models.IntegerField(default=1,verbose_name='编辑器模式') - open_children = models.BooleanField(default=False,verbose_name="展开下级目录") - show_children = models.BooleanField(verbose_name="显示下级文档",default=False) + editor_mode = models.IntegerField(default=1, verbose_name='编辑器模式') + open_children = models.BooleanField(default=False, verbose_name="展开下级目录") + show_children = models.BooleanField(verbose_name="显示下级文档", default=False) def __str__(self): return self.name @@ -88,7 +89,7 @@ class Doc(models.Model): verbose_name = '文档' verbose_name_plural = verbose_name indexes = [ - models.Index(fields=['top_doc','parent_doc','status']), + models.Index(fields=['top_doc', 'parent_doc', 'status']), models.Index(fields=['sort']), ] # ordering = ['-create_time','sort'] @@ -98,15 +99,15 @@ class Doc(models.Model): return reverse("doc", kwargs={ "pro_id": str(self.top_doc), - "doc_id":self.pk} + "doc_id": self.pk} ) # 文档历史模型 class DocHistory(models.Model): - doc = models.ForeignKey(Doc,on_delete=models.CASCADE) - pre_content = models.TextField(verbose_name='文档历史编辑内容',null=True,blank=True) - create_user = models.ForeignKey(User,on_delete=models.SET_NULL,null=True) + doc = models.ForeignKey(Doc, on_delete=models.CASCADE) + pre_content = models.TextField(verbose_name='文档历史编辑内容', null=True, blank=True) + create_user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True) create_time = models.DateTimeField(auto_now=True) def __str__(self): @@ -119,9 +120,9 @@ class DocHistory(models.Model): # 文档模板模型 class DocTemp(models.Model): - name = models.CharField(verbose_name="模板名称",max_length=50) + name = models.CharField(verbose_name="模板名称", max_length=50) content = models.TextField(verbose_name="文档模板") - create_user = models.ForeignKey(User,on_delete=models.CASCADE) + create_user = models.ForeignKey(User, on_delete=models.CASCADE) create_time = models.DateTimeField(auto_now_add=True) modify_time = models.DateTimeField(auto_now=True) @@ -132,17 +133,18 @@ class DocTemp(models.Model): verbose_name = '文档模板' verbose_name_plural = verbose_name + # 文档分享模型 class DocShare(models.Model): - token = models.CharField(verbose_name="分享Token",max_length=100,blank=True,null=True) - doc = models.ForeignKey(Doc,on_delete=models.CASCADE) # 文档 + token = models.CharField(verbose_name="分享Token", max_length=100, blank=True, null=True) + doc = models.ForeignKey(Doc, on_delete=models.CASCADE) # 文档 # 文档分享类型,0表示公开分享,1表示私密分享 - share_type = models.IntegerField(choices=((0,0),(1,1)),default=0) + share_type = models.IntegerField(choices=((0, 0), (1, 1)), default=0) # 文档分享码,当分享类型为私密分享时,需要验证分享码 - share_value = models.CharField(max_length=10,verbose_name="分享码",blank=True,null=True) + share_value = models.CharField(max_length=10, verbose_name="分享码", blank=True, null=True) # 分享有效期,默认为0,表示永久有效,值为整数,表示创建时间起多久过期 # effective_time = models.IntegerField(default=0,blank=True,null=True) - is_enable = models.BooleanField(default=True,verbose_name="启用状态") + is_enable = models.BooleanField(default=True, verbose_name="启用状态") create_time = models.DateTimeField(auto_now_add=True) def __str__(self): @@ -152,10 +154,11 @@ class DocShare(models.Model): verbose_name = '文档分享' verbose_name_plural = verbose_name + # 标签模板 class Tag(models.Model): - name = models.CharField(verbose_name='标签名',max_length=10) - create_user = models.ForeignKey(User,on_delete=models.CASCADE) + name = models.CharField(verbose_name='标签名', max_length=10) + create_user = models.ForeignKey(User, on_delete=models.CASCADE) def __str__(self): return self.name @@ -167,12 +170,12 @@ class Tag(models.Model): # 文档标签 class DocTag(models.Model): - tag = models.ForeignKey(Tag,on_delete=models.CASCADE) - doc = models.ForeignKey(Doc,on_delete=models.CASCADE) + tag = models.ForeignKey(Tag, on_delete=models.CASCADE) + doc = models.ForeignKey(Doc, on_delete=models.CASCADE) create_time = models.DateTimeField(auto_now_add=True) def __str__(self): - return "{}-{}".format(self.tag.name,self.doc.name) + return "{}-{}".format(self.tag.name, self.doc.name) class Meta: verbose_name = '文档标签' @@ -181,9 +184,9 @@ class DocTag(models.Model): # 文集导出模型 class ProjectReport(models.Model): - project = models.OneToOneField(Project,unique=True,on_delete=models.CASCADE) + project = models.OneToOneField(Project, unique=True, on_delete=models.CASCADE) # 允许导出,默认为0-允许,1-不允许 - allow_epub = models.IntegerField(default=0,verbose_name="前台导出EPUB") + allow_epub = models.IntegerField(default=0, verbose_name="前台导出EPUB") allow_pdf = models.IntegerField(default=0, verbose_name="前台导出PDF") def __str__(self): @@ -197,7 +200,8 @@ class ProjectReport(models.Model): # 文集导出文件模型 class ProjectReportFile(models.Model): project = models.ForeignKey(Project, on_delete=models.CASCADE) # 外键关联文集 - file_type = models.CharField(choices=(('epub', 'epub'), ('pdf', 'pdf'), ('docx', 'docx')), verbose_name='文件类型',max_length=10) + file_type = models.CharField(choices=(('epub', 'epub'), ('pdf', 'pdf'), ('docx', 'docx')), verbose_name='文件类型', + max_length=10) file_name = models.CharField(max_length=100, verbose_name='文件名称') file_path = models.CharField(max_length=250, verbose_name='文件路径') create_time = models.DateTimeField(auto_now_add=True) @@ -212,8 +216,8 @@ class ProjectReportFile(models.Model): # 图片分组模型 class ImageGroup(models.Model): - user = models.ForeignKey(User,on_delete=models.CASCADE) - group_name = models.CharField(verbose_name="图片分组",max_length=50,default="默认分组") + user = models.ForeignKey(User, on_delete=models.CASCADE) + group_name = models.CharField(verbose_name="图片分组", max_length=50, default="默认分组") def __str__(self): return self.group_name @@ -225,13 +229,13 @@ class ImageGroup(models.Model): # 图片模型 class Image(models.Model): - user = models.ForeignKey(User,on_delete=models.CASCADE) - file_path = models.CharField(verbose_name="图片路径",max_length=250) - file_name = models.CharField(verbose_name="图片名称",max_length=250,null=True,blank=True) - group = models.ForeignKey(ImageGroup,on_delete=models.SET_NULL,null=True,verbose_name="图片分组") - remark = models.CharField(verbose_name="图片备注",null=True,blank=True,max_length=250,default="图片描述") - create_time = models.DateTimeField(verbose_name='创建时间',auto_now_add=True) - modify_time = models.DateTimeField(verbose_name='修改时间',auto_now=True) + user = models.ForeignKey(User, on_delete=models.CASCADE) + file_path = models.CharField(verbose_name="图片路径", max_length=250) + file_name = models.CharField(verbose_name="图片名称", max_length=250, null=True, blank=True) + group = models.ForeignKey(ImageGroup, on_delete=models.SET_NULL, null=True, verbose_name="图片分组") + remark = models.CharField(verbose_name="图片备注", null=True, blank=True, max_length=250, default="图片描述") + create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) + modify_time = models.DateTimeField(verbose_name='修改时间', auto_now=True) class Meta: verbose_name = '素材图片' @@ -240,10 +244,10 @@ class Image(models.Model): # 附件模型 class Attachment(models.Model): - file_name = models.CharField(max_length=200,verbose_name="附件名",default='mrdoc_附件.zip') - file_size = models.CharField(max_length=100,verbose_name="附件大小",blank=True,null=True) - file_path = models.FileField(upload_to='attachment/%Y/%m/',verbose_name='附件') - user = models.ForeignKey(User,on_delete=models.CASCADE,) + file_name = models.CharField(max_length=200, verbose_name="附件名", default='mrdoc_附件.zip') + file_size = models.CharField(max_length=100, verbose_name="附件大小", blank=True, null=True) + file_path = models.FileField(upload_to='attachment/%Y/%m/', verbose_name='附件') + user = models.ForeignKey(User, on_delete=models.CASCADE, ) create_time = models.DateTimeField(auto_now_add=True) def __str__(self): @@ -259,7 +263,7 @@ class MyCollect(models.Model): # 收藏类型 1表示文档 2表示文集 collect_type = models.IntegerField(verbose_name="收藏类型") collect_id = models.IntegerField(verbose_name="收藏对象ID") - create_user = models.ForeignKey(User,on_delete=models.CASCADE) + create_user = models.ForeignKey(User, on_delete=models.CASCADE) create_time = models.DateTimeField(auto_now_add=True) def __str__(self): @@ -267,4 +271,4 @@ class MyCollect(models.Model): class Meta: verbose_name = '我的收藏' - verbose_name_plural = verbose_name \ No newline at end of file + verbose_name_plural = verbose_name diff --git a/app_doc/spider/__init__.py b/app_doc/spider/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..59c217dc03e219823f91f57a38760f96dfe7f386 --- /dev/null +++ b/app_doc/spider/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# @Time : 2021/2/4 17:40 +# @Author : zj +# @File : __init__.py.py +# @Project : MrDoc \ No newline at end of file diff --git a/app_doc/spider/csdn_publish.py b/app_doc/spider/csdn_publish.py new file mode 100644 index 0000000000000000000000000000000000000000..040d91b34244bd43a806db61600776e4ef2e17c4 --- /dev/null +++ b/app_doc/spider/csdn_publish.py @@ -0,0 +1,172 @@ +# -*- coding: utf-8 -*- +# @Time : 2021/2/4 17:22 +# @Author : zj +# @File : csdn_publish.py +# @Project : spider_publish +import os +import requests +import hmac +import base64 +import execjs +from hashlib import sha256 +from app_doc.spider.utils.tools import headers_to_dict + + +class CSDNPublish: + def __init__(self): + self.sess = requests.session() + """ + uuid_tt_dd=10_18833398380-1596515218828-152372; UserName=zhao_5352269; UserInfo=e0b82274578c40409ca83aede18a20f4; UserToken=e0b82274578c40409ca83aede18a20f4; UserNick=%E8%8F%9C%E9%B8%9F%E8%B5%B7%E9%A3%9Elo; AU=9AB; UN=zhao_5352269; BT=1601348093447; p_uid=U010000; Hm_up_6bcd52f51e9b3dce32bec4a3997715ac=%7B%22islogin%22%3A%7B%22value%22%3A%221%22%2C%22scope%22%3A1%7D%2C%22isonline%22%3A%7B%22value%22%3A%221%22%2C%22scope%22%3A1%7D%2C%22isvip%22%3A%7B%22value%22%3A%220%22%2C%22scope%22%3A1%7D%2C%22uid_%22%3A%7B%22value%22%3A%22zhao_5352269%22%2C%22scope%22%3A1%7D%7D; Hm_ct_6bcd52f51e9b3dce32bec4a3997715ac=6525*1*10_18833398380-1596515218828-152372!5744*1*zhao_5352269; dc_sid=482b3952bb515cb166592117297e4acf; c_segment=4; c_first_ref=www.baidu.com; __gads=ID=22bbcb1d0212b93b-229c4fe6dcc40031:T=1606112428:RT=1606112428:R:S=ALNI_MaWUYyB3I7CCvOO0YMrlLXy200jQg; aliyun_webUmidToken=T2gATnBVya6ECNeA8rDbSZsdysruWAHY1WB2EfZmJZb02aZgkYwohuayKvg0iWVw7hJzx1fy0RIXNSwpKo2wuJ28; is_advert=1; c_first_page=https%3A//www.csdn.net/tags/Ntzacg4sMDY0MS1ibG9n.html; Hm_lvt_6bcd52f51e9b3dce32bec4a3997715ac=1612439006,1612439102,1612439311,1612439351; log_Id_click=26; dc_session_id=10_1612495651487.187227; announcement-new=%7B%22isLogin%22%3Atrue%2C%22announcementUrl%22%3A%22https%3A%2F%2Fblog.csdn.net%2Fblogdevteam%2Farticle%2Fdetails%2F112280974%3Futm_source%3Dgonggao_0107%22%2C%22announcementCount%22%3A0%2C%22announcementExpire%22%3A3600000%7D; c_page_id=default; firstDie=1; log_Id_view=293; c_pref=https%3A//mp.csdn.net/console/home%3Fspm%3D1001.2101.3001.4503; dc_tos=qo1dm6; log_Id_pv=113; Hm_lpvt_6bcd52f51e9b3dce32bec4a3997715ac=1612495663 + """ + def publish_content(self, tags, markdowncontent, content, cookie, title): + x_ca_nonce, signature = self._de_sign() + header = f""" + accept: */* + accept-encoding: gzip, deflate, br + accept-language: zh-CN,zh;q=0.9 + content-type: application/json + cookie: {cookie} + origin: https://editor.csdn.net + pragma: no-cache + referer: https://editor.csdn.net/ + user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36 + x-ca-key: 203803574 + x-ca-nonce: {x_ca_nonce} + x-ca-signature: {signature} + x-ca-signature-headers: x-ca-key,x-ca-nonce + """ + url = 'https://bizapi.csdn.net/blog-console-api/v3/mdeditor/saveArticle' + form_data = { + "title": title, + "markdowncontent": markdowncontent, + "content": content, + "readType": "public", + "tags": tags, + "status": 0, + "categories": "", + "type": "original", + "original_link": "", + "authorized_status": False, + "not_auto_saved": "1", + "source": "pc_mdeditor" + } + res = self.sess.post(url=url, json=form_data, headers=headers_to_dict(header)) + return res.text + + def _de_sign(self): + path = os.getcwd() + path_js = path + '/app_doc/spider/web_js/csdn_x_ca.js' + x_ca_nonce = execjs.compile(open(path_js, 'r', encoding='utf8').read()).call('f') + appsecret = '9znpamsyl2c7cdrr9sas0le9vbc3r6ba' + md5 = '' + text_sign = '' + text_sign += "POST" + '\n' + text_sign += "*/*" + '\n' + text_sign += md5 + '\n' + text_sign += "application/json" + '\n' + text_sign += "" + '\n' + text_sign += "x-ca-key:203803574" + '\n' + text_sign += f"x-ca-nonce:{x_ca_nonce}" + '\n' + text_sign += "/blog-console-api/v3/mdeditor/saveArticle" + + appsecret = appsecret.encode('utf-8') # 秘钥 + data = text_sign.encode('utf-8') # 加密数据 + signature = base64.b64encode(hmac.new(appsecret, data, digestmod=sha256).digest()).decode('utf8') + return x_ca_nonce, signature + + +if __name__ == '__main__': + markdown_content = """ + #### 企业经营分析页面(获取主营业务) + +http://stockpage.10jqka.com.cn/300937/operate/ + +真正的数据在 + +http://basic.10jqka.com.cn/300937/operate.html + +![image-20210127192742612](http://img.andrewblog.cn/workqiniu/image-20210127192742612.png-gg) + +这个链接 + +直接加上整个请求头,请求几次之后就会失效。 + +经过测试发现是因为 `cookie` 中包含参数 `v=A0-7YHAXc1csJXcrWyzMcU4D3uhadKOWPcinimFc677FMGGeaUQz5k2YNi9y` 这个参数每隔几秒就会重新更新一次,旧的就会失效 + +下面就是找这个值改变的地方。 + +- 直接删除所有的 `cookie`,然后刷新浏览器 + +- 一般设置 `cookie`, 都是 `setCookie`, 直接搜。打断点。 + +- 因为这个 `cookie` 每隔几秒就会更新,所以就是看那个能起作用,就是哪个 `js`(方法有点蠢,可以直接 `hook cookie` 当 `cookie` 发生变化的时候就会进入 `debugger`) + + **经过测试就在 `chameleon.min.js` 中 **。 + + ![image-20210127193438327](http://img.andrewblog.cn/workqiniu/image-20210127193438327.png-gg) + +在 `setCookie` 的时候发现,**t** 也就是 `cookie` 已经生成,所以真正的就是上面一步 + +开始调试 + +![image-20210127194003309](http://img.andrewblog.cn/workqiniu/image-20210127194003309.png-gg) + +。。。。 + +一通调试 + +最后选择直接用 `selenium` 打开,每次失效了之后获取一次 `cookie`. 完美解决 + +`js` 看着也不难,就是逆向或者去解析太麻烦。所以还是 `selenium` 解决。 + +还原前 + +![image-20210127195549309](http://img.andrewblog.cn/workqiniu/image-20210127195549309.png-gg) + +`AST` 还原后的一部分 + +![image-20210127195456106](http://img.andrewblog.cn/workqiniu/image-20210127195456106.png-gg) + +已经看着好多了,在手动替换一下,应该还是可以的。这个 `js` 看这挺简单。 + +不过还是没有 `selenium` 方便快捷 + +![image-20210127195909811](http://img.andrewblog.cn/workqiniu/image-20210127195909811.png-gg) + """ + content = """ +

企业经营分析页面(获取主营业务)

http://stockpage.10jqka.com.cn/300937/operate/

+

真正的数据在

+

http://basic.10jqka.com.cn/300937/operate.html

+

image-20210127192742612 +

这个链接

+

直接加上整个请求头,请求几次之后就会失效。

+

经过测试发现是因为 cookie 中包含参数 v=A0-7YHAXc1csJXcrWyzMcU4D3uhadKOWPcinimFc677FMGGeaUQz5k2YNi9y 这个参数每隔几秒就会重新更新一次,旧的就会失效

+

下面就是找这个值改变的地方。

+ +

setCookie 的时候发现,t 也就是 cookie 已经生成,所以真正的就是上面一步

+

开始调试

+

image-20210127194003309 +

。。。。

+

一通调试

+

最后选择直接用 selenium 打开,每次失效了之后获取一次 cookie. 完美解决

+

js 看着也不难,就是逆向或者去解析太麻烦。所以还是 selenium 解决。

+

还原前

+

image-20210127195549309 +

AST 还原后的一部分

+

image-20210127195456106 +

已经看着好多了,在手动替换一下,应该还是可以的。这个 js 看这挺简单。

+

不过还是没有 selenium 方便快捷

+

image-20210127195909811 + """ + cookie = """ + uuid_tt_dd=10_18833398380-1596515218828-152372; UserName=zhao_5352269; UserInfo=e0b82274578c40409ca83aede18a20f4; UserToken=e0b82274578c40409ca83aede18a20f4; UserNick=%E8%8F%9C%E9%B8%9F%E8%B5%B7%E9%A3%9Elo; AU=9AB; UN=zhao_5352269; BT=1601348093447; p_uid=U010000; Hm_up_6bcd52f51e9b3dce32bec4a3997715ac=%7B%22islogin%22%3A%7B%22value%22%3A%221%22%2C%22scope%22%3A1%7D%2C%22isonline%22%3A%7B%22value%22%3A%221%22%2C%22scope%22%3A1%7D%2C%22isvip%22%3A%7B%22value%22%3A%220%22%2C%22scope%22%3A1%7D%2C%22uid_%22%3A%7B%22value%22%3A%22zhao_5352269%22%2C%22scope%22%3A1%7D%7D; Hm_ct_6bcd52f51e9b3dce32bec4a3997715ac=6525*1*10_18833398380-1596515218828-152372!5744*1*zhao_5352269; dc_sid=482b3952bb515cb166592117297e4acf; c_segment=4; c_first_ref=www.baidu.com; __gads=ID=22bbcb1d0212b93b-229c4fe6dcc40031:T=1606112428:RT=1606112428:R:S=ALNI_MaWUYyB3I7CCvOO0YMrlLXy200jQg; aliyun_webUmidToken=T2gATnBVya6ECNeA8rDbSZsdysruWAHY1WB2EfZmJZb02aZgkYwohuayKvg0iWVw7hJzx1fy0RIXNSwpKo2wuJ28; is_advert=1; c_first_page=https%3A//www.csdn.net/tags/Ntzacg4sMDY0MS1ibG9n.html; Hm_lvt_6bcd52f51e9b3dce32bec4a3997715ac=1612439006,1612439102,1612439311,1612439351; log_Id_click=26; dc_session_id=10_1612495651487.187227; announcement-new=%7B%22isLogin%22%3Atrue%2C%22announcementUrl%22%3A%22https%3A%2F%2Fblog.csdn.net%2Fblogdevteam%2Farticle%2Fdetails%2F112280974%3Futm_source%3Dgonggao_0107%22%2C%22announcementCount%22%3A0%2C%22announcementExpire%22%3A3600000%7D; c_page_id=default; firstDie=1; log_Id_view=293; c_pref=https%3A//mp.csdn.net/console/home%3Fspm%3D1001.2101.3001.4503; dc_tos=qo1dm6; log_Id_pv=113; Hm_lpvt_6bcd52f51e9b3dce32bec4a3997715ac=1612495663 + """ + # CSDNPublish()._de_sign() + CSDNPublish().publish_content(tags='python,js', markdowncontent=markdown_content, + content=content, cookie=cookie, title='同花顺 web 请求 cookie v参数生成') \ No newline at end of file diff --git a/app_doc/spider/utils/__init__.py b/app_doc/spider/utils/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..1ddbe8c6cc9400f5ea525f565c7d664e05885019 --- /dev/null +++ b/app_doc/spider/utils/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# @Time : 2020/7/23 16:51 +# @Author : zj +# @File : __init__.py.py +# @Project : work_code \ No newline at end of file diff --git a/app_doc/spider/utils/db_conf.py b/app_doc/spider/utils/db_conf.py new file mode 100644 index 0000000000000000000000000000000000000000..e08b129899c44e017f1c7b5a9be2d30712b10e3e --- /dev/null +++ b/app_doc/spider/utils/db_conf.py @@ -0,0 +1,59 @@ +import uuid + + +# 数据库配置 +spider_area = {'host': 'rm-bp1lu35v2bgr2m23cyo.mysql.rds.aliyuncs.com', 'port': 3306, 'user': 'spider_base', + 'passwd': '86links@com!', 'db': 'spider_area','charset': 'utf8'} + +spider_base = {'host': 'rm-bp1lu35v2bgr2m23cyo.mysql.rds.aliyuncs.com', 'port': 3306, 'user': 'spider_base', + 'passwd': '86links@com!', 'db': 'spider_base','charset': 'utf8'} + +spider_base_qcc = {'host': 'rm-bp1lu35v2bgr2m23cyo.mysql.rds.aliyuncs.com', 'port': 3306, 'user': 'spider_base', + 'passwd': '86links@com!', 'db': 'qcc', 'charset': 'utf8'} + + +""" +mysql 表名 +""" +# # 全量企查查企业详情url列表 +TABLE_COMPANY_LIST = 'major_technology' +TABLE_CONTENT = 'major_technology_dynamic' + + +agents = [ + "Mozilla/5.0 (Linux; U; Android 2.3.6; en-us; Nexus S Build/GRK39F) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1", + "Avant Browser/1.2.789rel1 (http://www.avantbrowser.com)", + "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.0.249.0 Safari/532.5", + "Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/532.9 (KHTML, like Gecko) Chrome/5.0.310.0 Safari/532.9", + "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.514.0 Safari/534.7", + "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/534.14 (KHTML, like Gecko) Chrome/9.0.601.0 Safari/534.14", + "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.14 (KHTML, like Gecko) Chrome/10.0.601.0 Safari/534.14", + "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.20 (KHTML, like Gecko) Chrome/11.0.672.2 Safari/534.20", + "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.27 (KHTML, like Gecko) Chrome/12.0.712.0 Safari/534.27", + "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.24 Safari/535.1", + "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.120 Safari/535.2", + "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.36 Safari/535.7", + "Mozilla/5.0 (Windows; U; Windows NT 6.0 x64; en-US; rv:1.9pre) Gecko/2008072421 Minefield/3.0.2pre", + "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.10) Gecko/2009042316 Firefox/3.0.10", + "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.9.0.11) Gecko/2009060215 Firefox/3.0.11 (.NET CLR 3.5.30729)", + "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 GTB5", + "Mozilla/5.0 (Windows; U; Windows NT 5.1; tr; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8 ( .NET CLR 3.5.30729; .NET4.0E)", + "Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1", + "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0.1) Gecko/20100101 Firefox/4.0.1", + "Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0", + "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0a2) Gecko/20110622 Firefox/6.0a2", + "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:7.0.1) Gecko/20100101 Firefox/7.0.1", + "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0b4pre) Gecko/20100815 Minefield/4.0b4pre", + "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0 )", + "Mozilla/4.0 (compatible; MSIE 5.5; Windows 98; Win 9x 4.90)", + "Mozilla/5.0 (Windows; U; Windows XP) Gecko MultiZilla/1.6.1.0a", + "Mozilla/2.02E (Win95; U)", + "Mozilla/3.01Gold (Win95; I)", + "Mozilla/4.8 [en] (Windows NT 5.1; U)", + "Mozilla/5.0 (Windows; U; Win98; en-US; rv:1.4) Gecko Netscape/7.1 (ax)", + "HTC_Dream Mozilla/5.0 (Linux; U; Android 1.5; en-ca; Build/CUPCAKE) AppleWebKit/528.5 (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1", +] + + + + diff --git a/app_doc/spider/utils/db_pool.py b/app_doc/spider/utils/db_pool.py new file mode 100644 index 0000000000000000000000000000000000000000..3884d92db2bbe807274b4e4b785e199905a6ab54 --- /dev/null +++ b/app_doc/spider/utils/db_pool.py @@ -0,0 +1,130 @@ +import pymysql +import traceback +from DBUtils.PooledDB import PooledDB +import inspect + + +class DB_POOL(object): + # mysql_info:字典格式的数据库配置信息 + # + def __init__(self, mysql_info, min, max, n): + self.pool = PooledDB(creator=pymysql, mincached=min, maxcached=max, maxconnections=n, blocking=True, + host=mysql_info['host'], + user=mysql_info['user'], + passwd=mysql_info['passwd'], + db=mysql_info['db'], + port=mysql_info['port'], + charset=mysql_info['charset']) + + def sql_exe(self, sql): + conn = self.pool.connection() + cur = conn.cursor(cursor=pymysql.cursors.DictCursor) + try: + res = cur.execute(sql) + conn.commit() + data = cur.fetchall() + if data: + return data + else: + return res + except Exception as e: + print(e) + print('{}.{}错误'.format(self.__class__.__name__, inspect.stack()[1][3])) + conn.rollback() + return + finally: + cur.close() + conn.close() + + def sql_exe_zip(self, sql): + conn = self.pool.connection() + cur = conn.cursor() + try: + res = cur.execute(sql) + conn.commit() + data = cur.fetchall() + if data: + return data + else: + return res + except Exception as e: + print(e) + print('{}.{}错误'.format(self.__class__.__name__, inspect.stack()[1][3])) + conn.rollback() + return + finally: + cur.close() + conn.close() + + def sql_exe_ex(self, sql, *args): + conn = self.pool.connection() + cur = conn.cursor(cursor=pymysql.cursors.DictCursor) + try: + res = cur.execute(sql, *args) + conn.commit() + data = cur.fetchall() + if data: + return data + else: + return res + except Exception as e: + print(e) + print('{}.{}错误'.format(self.__class__.__name__, inspect.stack()[1][3])) + conn.rollback() + return + finally: + cur.close() + conn.close() + + def sql_exe_getlastid(self, sql, *args): + conn = self.pool.connection() + cur = conn.cursor(cursor=pymysql.cursors.DictCursor) + try: + res = cur.execute(sql, *args) + conn.commit() + last_id = cur.lastrowid + return res, last_id + # except pymysql.err.IntegrityError as e: + # print("当前数据已插入。。。,返回字符串") + # conn.rollback() + # return 6, 6 + except Exception as e: + print(e) + print('{}.{}错误'.format(self.__class__.__name__, inspect.stack()[1][3])) + conn.rollback() + return + finally: + cur.close() + conn.close() + + def sql_exe_many(self, sql, T): + conn = self.pool.connection() + cur = conn.cursor(cursor=pymysql.cursors.DictCursor) + try: + res = cur.executemany(sql, T) + conn.commit() + return res + except Exception as e: + print(e) + print(traceback.format_exc()) + print('{}.{}错误'.format(self.__class__.__name__, inspect.stack()[1][3])) + conn.rollback() + return + finally: + cur.close() + conn.close() + + def sql_exe_many_notry(self, sql, T): + conn = self.pool.connection() + cur = conn.cursor(cursor=pymysql.cursors.DictCursor) + res = cur.executemany(sql, T) + conn.commit() + cur.close() + conn.close() + return res + + def commit_data(self): + conn = self.pool.connection() + conn.cursor(cursor=pymysql.cursors.DictCursor) + conn.commit() + conn.close() diff --git a/app_doc/spider/utils/db_utils.py b/app_doc/spider/utils/db_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..5654084b1739696d9100e673f85c2006f4a70ca6 --- /dev/null +++ b/app_doc/spider/utils/db_utils.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +# @Time : 2020/7/23 16:51 +# @Author : zj +# @File : db_utils.py +# @Project : work_code +import pymysql +from DBUtils.PooledDB import PooledDB +import inspect + + + diff --git a/app_doc/spider/utils/pipeline.py b/app_doc/spider/utils/pipeline.py new file mode 100644 index 0000000000000000000000000000000000000000..f2cb05ea973d221859cfbf49ae2f4e6fe15a0849 --- /dev/null +++ b/app_doc/spider/utils/pipeline.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# @Time : 2020/8/5 12:14 +# @Author : zj +# @File : pipeline.py +# @Project : work_code +import traceback + +from utils.db_pool import DB_POOL +from utils.redis_pool import redis_pool +from utils.db_conf import spider_area, spider_base, TABLE_COMPANY_LIST, QCC_URL_KEY_SET, QCC_QUERY_PARAMS + +import datetime + + +class QccScrapyPipeline: + def __init__(self): + self.db_area = DB_POOL(spider_area, 5, 20, 10) + self.db_sb = DB_POOL(spider_base, 2, 10, 10) + self.count = 0 + self.list_count = 0 + self.redis = redis_pool(8) + + def process_item(self, item): + query_item = item.popitem() + sql_insert_tyc = 'insert ignore into {} (company_name, company_url, search_id, company_id' \ + ') values (%s, %s, %s, %s);'.format(TABLE_COMPANY_LIST) + + try: + res = self.db_sb.sql_exe_many(sql_insert_tyc, query_item[1]) + print('插入结果:', res) + except Exception as e: + print(traceback.format_exc()) + print('save_mysql_error:', e) + else: + print("************插入数据,返回的响应内容:", res) + if res or res == 0: + update_sql = f'update {QCC_QUERY_PARAMS} set is_pa=1 where ' \ + f'id = {query_item[0]}' + self.db_sb.sql_exe_ex(update_sql) + # redis_res = self.redis.sadd(QCC_URL_KEY_SET, "&^&".join(data)) + # print('redis_res:', redis_res) + self.list_count += len(query_item[1]) + print("总计获得总量%s" % self.list_count) + # else: + # print("插入失败", sql_insert_tyc, res) + return + + diff --git a/app_doc/spider/utils/proxy.zip b/app_doc/spider/utils/proxy.zip new file mode 100644 index 0000000000000000000000000000000000000000..f2861ff0f4b5c5d2f42c0f7959e3691465ce4f9a Binary files /dev/null and b/app_doc/spider/utils/proxy.zip differ diff --git a/app_doc/spider/utils/redis_pool.py b/app_doc/spider/utils/redis_pool.py new file mode 100644 index 0000000000000000000000000000000000000000..925df223519fe433a40061dfb271a99920f3b33e --- /dev/null +++ b/app_doc/spider/utils/redis_pool.py @@ -0,0 +1,45 @@ +import redis +from sshtunnel import SSHTunnelForwarder + + +# def redis_pool(ku): +# pool = redis.ConnectionPool(host='47.96.186.162', port=6379, password='86Links@com_Redis_dev%', +# decode_responses=True, db=ku) +# red = redis.Redis(connection_pool=pool, health_check_interval=30) +# +# return red + + +def redis_ssh_pool(ku): + server = SSHTunnelForwarder( + ssh_address_or_host="122.152.202.232", # ssh地址 + ssh_username="root", # ssh连接的用户名 + ssh_password="86links.com!", # ssh连接的密码 + remote_bind_address=("172.17.0.12", 6379)) + + server.start() + + pool = redis.ConnectionPool(host='127.0.0.1', port=server.local_bind_port, password='86links@com!',decode_responses=True, db=ku) + red = redis.Redis(connection_pool=pool, health_check_interval=30) + + return red + + +def redis_pool(ku): + """ + redis的链接会经常断开,重写链接redis的逻辑 + :return: + """ + while True: + try: + pool = redis.ConnectionPool(host='47.96.186.162', port=6379, password='86Links@com_Redis_dev%', + decode_responses=True, db=ku, health_check_interval=30) + red = redis.Redis(connection_pool=pool) + red.ping() + except Exception as e: + print('redis链接失败,正在尝试重连。。。') + continue + else: + return red + + diff --git a/app_doc/spider/utils/tools.py b/app_doc/spider/utils/tools.py new file mode 100644 index 0000000000000000000000000000000000000000..1a736ffbf5ab4a03f98846f5f2394e03fffc478b --- /dev/null +++ b/app_doc/spider/utils/tools.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +# @Time : 2020/7/28 14:33 +# @Author : zj +# @File : tools.py +# @Project : work_code +import requests + + +# RELOAD_COOKIE_URL = 'http://127.0.0.1:3000' +RELOAD_COOKIE_URL = 'http://47.99.142.145:3001/' + + +def cookie_to_dict(cookies): + row_cookie = cookies.split(';') + row_dict = dict() + for i in row_cookie: + if i == '': + continue + row = i.strip().split('=', 1) + row_dict[row[0].strip()] = row[1].strip() + return row_dict + + +def headers_to_dict(headers): + row_headear = headers.split('\n') + row_dict = dict() + for i in row_headear: + if i == '': + continue + row = i.strip().split(':', 1) + if len(row) == 0: + continue + if row[0] == '': + continue + row_dict[row[0].strip()] = row[1].strip() + return row_dict + + +def req_proxy(): + url_change = 'http://ip.dobel.cn/switch-ip' + response = requests.get(url_change, proxies=get_proxies(), timeout=1.5) + print('新代理:', response.text) + return response.text + + +def get_proxies(): + proxyHost = "http-dyn.abuyun.com" + proxyPort = "9020" + + # 代理隧道验证信息 + proxyUser = "HX2075H1W621AP2D" + proxyPass = "C4A669D901BC1BA0" + + # proxyHost = "http-proxy-t4.dobel.cn" + # proxyPort = "9180" + # + # # 代理隧道验证信息 + # proxyUser = "JSWLHTT7AGAMOR80" + # proxyPass = "D7caenRb" + + proxyMeta = "http://%(user)s:%(pass)s@%(host)s:%(port)s" % { + "host": proxyHost, + "port": proxyPort, + "user": proxyUser, + "pass": proxyPass, + } + + proxies = { + "http": proxyMeta, + "https": proxyMeta, + } + # print(proxies, '代理信息') + return proxies diff --git a/app_doc/spider/web_js/csdn_x_ca.js b/app_doc/spider/web_js/csdn_x_ca.js new file mode 100644 index 0000000000000000000000000000000000000000..b0c997ab8e12e92d1be930823d4cf2b11acca8d1 --- /dev/null +++ b/app_doc/spider/web_js/csdn_x_ca.js @@ -0,0 +1,8 @@ +f = function () { + return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (function (e) { + var n = 16 * Math.random() | 0 + , t = "x" === e ? n : 3 & n | 8; + return t.toString(16) + } + )) +}; diff --git a/app_doc/util_upload_img.py b/app_doc/util_upload_img.py index 9372d812772ed4affdfa27cb8afca681e4b58f11..e8d674b7f6316af978b58c3c8bfb57f2456f1d8a 100644 --- a/app_doc/util_upload_img.py +++ b/app_doc/util_upload_img.py @@ -2,9 +2,9 @@ from django.http import HttpResponse from django.conf import settings from django.views.decorators.csrf import csrf_exempt -from django.contrib.auth.decorators import login_required # 登录需求装饰器 -import datetime,time,json,base64,os,uuid -from app_doc.models import Image,ImageGroup +from django.contrib.auth.decorators import login_required # 登录需求装饰器 +import datetime, time, json, base64, os, uuid +from app_doc.models import Image, ImageGroup from app_admin.models import SysSetting import requests import random @@ -23,81 +23,82 @@ def upload_ice_img(request): # formData.append('upload_type', "files"); ################## try: - up_type = request.POST.get('upload_type','') - up_num = request.POST.get('upload_num','') - iceEditor_img = str(request.POST.get('iceEditor-img','')) + up_type = request.POST.get('upload_type', '') + up_num = request.POST.get('upload_num', '') + iceEditor_img = str(request.POST.get('iceEditor-img', '')) except: - pass + pass if up_type == "files": # 多文件上传功能,需要修改js文件 - res_dic = {'length':int(up_num)} - for i in range(0,int(up_num)): + res_dic = {'length': int(up_num)} + for i in range(0, int(up_num)): file_obj = request.FILES.get('file_' + str(i)) - result = ice_save_file(file_obj,request.user) + result = ice_save_file(file_obj, request.user) res_dic[i] = result elif iceEditor_img.lower().startswith('http'): - res_dic = ice_url_img_upload(iceEditor_img,request.user) + res_dic = ice_url_img_upload(iceEditor_img, request.user) else: # 粘贴上传和单文件上传 file_obj = request.FILES.get('file[]') - result = ice_save_file(file_obj,request.user) - res_dic = {0:result,"length":1,'other_msg':iceEditor_img} #一个文件,直接把文件数量固定了 + result = ice_save_file(file_obj, request.user) + res_dic = {0: result, "length": 1, 'other_msg': iceEditor_img} # 一个文件,直接把文件数量固定了 return HttpResponse(json.dumps(res_dic), content_type="application/json") -def ice_save_file(file_obj,user): + +def ice_save_file(file_obj, user): # 默认保留支持ice单文件上传功能,可以iceEditor中开启 - file_suffix = str(file_obj).split(".")[-1] # 提取图片格式 + file_suffix = str(file_obj).split(".")[-1] # 提取图片格式 # 允许上传文件类型,ice粘贴上传为blob - allow_suffix =["jpg", "jpeg", "gif", "png", "bmp", "webp","blob"] + allow_suffix = ["jpg", "jpeg", "gif", "png", "bmp", "webp", "blob"] # 判断附件格式 is_images = ["jpg", "jpeg", "gif", "png", "bmp", "webp"] - if file_suffix.lower() not in allow_suffix: + if file_suffix.lower() not in allow_suffix: return {"error": "文件格式不允许"} if file_suffix.lower() == 'blob': # 粘贴上传直接默认为png就行 - file_suffix = 'png' - # 接下来构造一个文件名,时间和随机10位字符串构成 + file_suffix = 'png' + # 接下来构造一个文件名,时间和随机10位字符串构成 relative_path = upload_generation_dir() name_time = time.strftime("%Y-%m-%d_%H%M%S_") name_join = "" - name_rand = name_join.join(random.sample('zyxwvutsrqponmlkjihgfedcba',10) ) + name_rand = name_join.join(random.sample('zyxwvutsrqponmlkjihgfedcba', 10)) - file_name = name_time + name_rand + "." + file_suffix + file_name = name_time + name_rand + "." + file_suffix path_file = relative_path + file_name path_file = settings.MEDIA_ROOT + path_file - #file_Url 是文件的url下发路径 - file_url = settings.MEDIA_URL + relative_path + file_name - + # file_Url 是文件的url下发路径 + file_url = settings.MEDIA_URL + relative_path + file_name + with open(path_file, 'wb') as f: for chunk in file_obj.chunks(): - f.write(chunk) # 保存文件 - if file_suffix.lower() in is_images: + f.write(chunk) # 保存文件 + if file_suffix.lower() in is_images: Image.objects.create( user=user, file_path=file_url, file_name=file_name, - remark="iceEditor上传" + remark="iceEditor上传" ) - - else : - #文件上传,暂时不屏蔽,如果需要正常使用此功能,是需要在iceeditor中修改的,mrdoc使用的是自定义脚本上传 + + else: + # 文件上传,暂时不屏蔽,如果需要正常使用此功能,是需要在iceeditor中修改的,mrdoc使用的是自定义脚本上传 Attachment.objects.create( user=user, file_path=file_url, file_name=file_name, - file_size=str(round(len(chunk)/1024,2))+"KB" + file_size=str(round(len(chunk) / 1024, 2)) + "KB" ) - return {"error":0, "name": str(file_obj),'url':file_url} + return {"error": 0, "name": str(file_obj), 'url': file_url} return {"error": "文件存储异常"} -# ice_url图片上传 -def ice_url_img_upload(url,user): +# ice_url图片上传 +def ice_url_img_upload(url, user): relative_path = upload_generation_dir() name_time = time.strftime("%Y-%m-%d_%H%M%S_") name_join = "" - name_rand = name_join.join(random.sample('zyxwvutsrqponmlkjihgfedcba',10) ) - file_name = name_time + name_rand + '.png' # 日期时间_随机字符串命名 + name_rand = name_join.join(random.sample('zyxwvutsrqponmlkjihgfedcba', 10)) + file_name = name_time + name_rand + '.png' # 日期时间_随机字符串命名 path_file = os.path.join(relative_path, file_name) path_file = settings.MEDIA_ROOT + path_file # print('文件路径:', path_file) @@ -117,11 +118,10 @@ def ice_url_img_upload(url,user): file_name=file_name, remark='iceurl粘贴上传', ) - resp_data = {"error":0, "name": file_name,'url':file_url} + resp_data = {"error": 0, "name": file_name, 'url': file_url} return resp_data - @login_required() @csrf_exempt def upload_img(request): @@ -129,17 +129,17 @@ def upload_img(request): # {"success": 0, "message": "出错信息"} # {"success": 1, "url": "图片地址"} ################## - img = request.FILES.get("editormd-image-file", None) # 编辑器上传 - manage_upload = request.FILES.get('manage_upload',None) # 图片管理上传 + img = request.FILES.get("editormd-image-file", None) # 编辑器上传 + manage_upload = request.FILES.get('manage_upload', None) # 图片管理上传 try: url_img = json.loads(request.body.decode())['url'] except: url_img = None - dir_name = request.POST.get('dirname','') - base_img = request.POST.get('base',None) - group_id = request.POST.get('group_id',0) + dir_name = request.POST.get('dirname', '') + base_img = request.POST.get('base', None) + group_id = request.POST.get('group_id', 0) - if int(group_id) not in [0,-1]: + if int(group_id) not in [0, -1]: try: group_id = ImageGroup.objects.get(id=group_id) except: @@ -149,19 +149,19 @@ def upload_img(request): # 上传普通图片文件 if img: - result = img_upload(img, dir_name,request.user) + result = img_upload(img, dir_name, request.user) # 图片管理上传 elif manage_upload: result = img_upload(manage_upload, dir_name, request.user, group_id=group_id) # 上传base64编码图片 elif base_img: - result = base_img_upload(base_img,dir_name,request.user) + result = base_img_upload(base_img, dir_name, request.user) # 上传图片URL地址 elif url_img: - if url_img.startswith("data:image"):# 以URL形式上传的BASE64编码图片 + if url_img.startswith("data:image"): # 以URL形式上传的BASE64编码图片 result = base_img_upload(url_img, dir_name, request.user) else: - result = url_img_upload(url_img,dir_name,request.user) + result = url_img_upload(url_img, dir_name, request.user) else: result = {"success": 0, "message": "上传出错"} return HttpResponse(json.dumps(result), content_type="application/json") @@ -170,7 +170,7 @@ def upload_img(request): # 目录创建 def upload_generation_dir(dir_name=''): today = datetime.datetime.today() - dir_name = dir_name + '/%d%02d/' %(today.year,today.month) + dir_name = dir_name + '/%d%02d/' % (today.year, today.month) # print("dir_name:",dir_name) if not os.path.exists(settings.MEDIA_ROOT + dir_name): # print("创建目录") @@ -181,8 +181,8 @@ def upload_generation_dir(dir_name=''): # 普通图片上传 def img_upload(files, dir_name, user, group_id=None): # 允许上传文件类型 - allow_suffix =["jpg", "jpeg", "gif", "png", "bmp", "webp"] - file_suffix = files.name.split(".")[-1] # 提取图片格式 + allow_suffix = ["jpg", "jpeg", "gif", "png", "bmp", "webp"] + file_suffix = files.name.split(".")[-1] # 提取图片格式 # 判断图片格式 if file_suffix.lower() not in allow_suffix: return {"success": 0, "message": "图片格式不正确"} @@ -198,31 +198,31 @@ def img_upload(files, dir_name, user, group_id=None): return {"success": 0, "message": "图片大小超出{}MB".format(allow_img_size / 1048576)} relative_path = upload_generation_dir(dir_name) - file_name = files.name.replace(file_suffix,'').replace('.','') + '_' +str(int(time.time())) + '.' + file_suffix - path_file=os.path.join(relative_path, file_name) + file_name = files.name.replace(file_suffix, '').replace('.', '') + '_' + str(int(time.time())) + '.' + file_suffix + path_file = os.path.join(relative_path, file_name) path_file = settings.MEDIA_ROOT + path_file # print('文件路径:',path_file) file_url = settings.MEDIA_URL + relative_path + file_name # print("文件URL:",file_url) with open(path_file, 'wb') as f: for chunk in files.chunks(): - f.write(chunk) # 保存文件 + f.write(chunk) # 保存文件 Image.objects.create( user=user, file_path=file_url, file_name=file_name, remark='本地上传', - group = group_id, + group=group_id, ) - return {"success": 1, "url": file_url,'message':'上传图片成功'} + return {"success": 1, "url": file_url, 'message': '上传图片成功'} # base64编码图片上传 -def base_img_upload(files,dir_name, user): - files_str = files.split(';base64,')[-1] # 截取图片正文 - files_base = base64.b64decode(files_str) # 进行base64编码 +def base_img_upload(files, dir_name, user): + files_str = files.split(';base64,')[-1] # 截取图片正文 + files_base = base64.b64decode(files_str) # 进行base64编码 relative_path = upload_generation_dir(dir_name) - file_name = str(datetime.datetime.today()).replace(':', '').replace(' ', '_').split('.')[0] + '.png' # 日期时间 + file_name = str(datetime.datetime.today()).replace(':', '').replace(' ', '_').split('.')[0] + '.png' # 日期时间 path_file = os.path.join(relative_path, file_name) path_file = settings.MEDIA_ROOT + path_file # print('文件路径:', path_file) @@ -231,17 +231,16 @@ def base_img_upload(files,dir_name, user): with open(path_file, 'wb') as f: f.write(files_base) # 保存文件 Image.objects.create( - user = user, - file_path = file_url, + user=user, + file_path=file_url, file_name=file_name, - remark = '粘贴上传', + remark='粘贴上传', ) return {"success": 1, "url": file_url, 'message': '上传图片成功'} # url图片上传 -def url_img_upload(url,dir_name,user): - +def url_img_upload(url, dir_name, user): relative_path = upload_generation_dir(dir_name) file_name = str(datetime.datetime.today()).replace(':', '').replace(' ', '_').split('.')[0] + '.png' # 日期时间 path_file = os.path.join(relative_path, file_name) @@ -264,12 +263,12 @@ def url_img_upload(url,dir_name,user): remark='粘贴上传', ) resp_data = { - 'msg': '', - 'code': 0, - 'data' : { - 'originalURL': url, - 'url': file_url - } + 'msg': '', + 'code': 0, + 'data': { + 'originalURL': url, + 'url': file_url } + } return resp_data - # return {"success": 1, "url": file_url, 'message': '上传图片成功'} \ No newline at end of file + # return {"success": 1, "url": file_url, 'message': '上传图片成功'} diff --git a/requirements.txt b/requirements.txt index fb0fce9d5c81a82edba738f8931583dc924a4a71..1108c490c10f654186d11d5da102697583ca0287 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -django==2.2.13 +django==2.1.* beautifulsoup4==4.8.2 lxml pillow==7.1.0 diff --git a/template/app_admin/admin_cookie_manage.html b/template/app_admin/admin_cookie_manage.html new file mode 100644 index 0000000000000000000000000000000000000000..05d3ef9efb78826090af19724f26e3128ca7b2b9 --- /dev/null +++ b/template/app_admin/admin_cookie_manage.html @@ -0,0 +1,309 @@ +{% extends 'app_admin/admin_base.html' %} +{% load static %} +{% load i18n %} +{% block title %}发布平台 cookie 管理{% endblock %} +{% block content %} +

+
+
+
+ 发布平台 cookie 管理 + +
+
+
+
+
+ + + + + + +
+ {#
#} + {# #} + {#
#} + {# #} + {#
#} + {#
#} +
+
+
+ + + + + + + + + + + + + + + + + + + + + {% for code in codes %} + + + + + + + + + {% endfor %} + +
平台cookie平台名称状态创建用户创建时间操作
{{ code.cookie }}{{ code.plant.plant_name }} + {% if code.status == 1 %} + 可用 + {% elif code.status == 0 %} + 不可用 + {% endif %} + {{ code.create_time }}{{ code.create_user }} + + + + {# #} + {# 更新#} + {# #} + +
+
+ +
+ +
+
+
+{% endblock %} + +{% block custom_script %} + + + + + + + + +{% endblock %} \ No newline at end of file diff --git a/template/app_admin/admin_doc.html b/template/app_admin/admin_doc.html index 87e6b36710c23aca5826359d2e7e9c9e7933cc79..c778460438ba0ad1687d81b15f61c01f5b7b5a21 100644 --- a/template/app_admin/admin_doc.html +++ b/template/app_admin/admin_doc.html @@ -142,6 +142,7 @@ {type: 'checkbox',width:20}, {title: '文档名称',field: 'name',align: 'left',templet:"#doc-name",minWidth:280}, {title: '状态',field: 'status',align: 'left',templet:"#doc-status",width:90}, + {title: '已分发平台',field: 'plant_list',align: 'left',templet:"#doc-plant-list",width:90}, {title: '上级',field: 'parent',align: 'left',}, {title: '文集',field: 'project_name',align: 'left',templet:"#project-role"}, {title: '编辑模式',field: 'editor_mode',align: 'left',templet:"#editor-mode",width:90}, diff --git a/template/app_admin/admin_plant_manage.html b/template/app_admin/admin_plant_manage.html new file mode 100644 index 0000000000000000000000000000000000000000..7907bc14d99f1694646f5996cda82f9e91efbe00 --- /dev/null +++ b/template/app_admin/admin_plant_manage.html @@ -0,0 +1,201 @@ +{% extends 'app_admin/admin_base.html' %} +{% load static %} +{% load i18n %} +{% block title %}发布平台管理{% endblock %} +{% block content %} +
+
+
+
+ 发布平台管理 + +
+
+
+
+
+ + + + + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + {% for code in codes %} + + + + + + + + {% endfor %} + +
平台id平台名称状态创建时间操作
{{ code.plant_code }}{{ code.plant_name }} + {% if code.status == 1 %} + 可用 + {% elif code.status == 0 %} + 不可用 + {% endif %} + {{ code.create_time }} +
+ + + {% if code.status == 1 %} + + {% elif code.status == 0 %} + + {% endif %} + + 删除 + + + +
+
+
+ +
+ +
+
+
+{% endblock %} + +{% block custom_script %} + +{% endblock %} \ No newline at end of file diff --git a/template/app_doc/editor/create_doc.html b/template/app_doc/editor/create_doc.html index 15722f598de3ee178ace3d71701c7059482790e4..f51d1c45448f1411511e7291ac441498d10ab4f8 100644 --- a/template/app_doc/editor/create_doc.html +++ b/template/app_doc/editor/create_doc.html @@ -126,6 +126,9 @@ + @@ -339,6 +342,9 @@ $("#pub_doc").click(function(){ createDoc(1); }) + $("#pub_doc_csdn").click(function(){ + createDoc(1); + }) $("#save_doc").click(function(){ createDoc(0); }) diff --git a/whoosh_index/MAIN_WRITELOCK b/whoosh_index/MAIN_WRITELOCK new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391