Merge pull request #508 from liangliangyy/dev

增加新功能
sh_branch
且听风吟 4 years ago committed by GitHub
commit 7e96de0d42
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -12,24 +12,23 @@
@file: blog_signals.py
@time: 2017/8/12 上午10:18
"""
import django
import _thread
import logging
import django.dispatch
from django.dispatch import receiver
from django.conf import settings
from django.contrib.admin.models import LogEntry
from DjangoBlog.utils import get_current_site
from django.core.mail import EmailMultiAlternatives
from django.db.models.signals import post_save
from django.contrib.auth.signals import user_logged_in, user_logged_out, user_login_failed
from django.contrib.auth.signals import user_logged_in, user_logged_out
from DjangoBlog.utils import cache, send_email, expire_view_cache, delete_sidebar_cache, delete_view_cache
from DjangoBlog.spider_notify import SpiderNotify
from oauth.models import OAuthUser
from blog.models import Article, Category, Tag, Links, SideBar, BlogSettings
from comments.models import Comment
from comments.utils import send_comment_email
import _thread
import logging
from DjangoBlog.utils import get_current_site
from DjangoBlog.utils import cache, expire_view_cache, delete_sidebar_cache, delete_view_cache
from DjangoBlog.spider_notify import SpiderNotify
logger = logging.getLogger(__name__)
@ -61,7 +60,7 @@ def send_email_signal_handler(sender, **kwargs):
result = msg.send()
log.send_result = result > 0
except Exception as e:
logger.error(e)
logger.error(f"失败邮箱号: {emailto}, {e}")
log.send_result = False
log.save()

@ -2,28 +2,21 @@
# encoding: utf-8
"""
@version: ??
@author: liangliangyy
@license: MIT Licence
@contact: liangliangyy@gmail.com
@site: https://www.lylinux.net/
@software: PyCharm
@file: utils.py
@time: 2017/1/19 上午2:30
"""
from django.core.cache import cache
from django.contrib.sites.models import Site
import logging
import os
import random
import string
import uuid
from hashlib import sha256
import mistune
import requests
from django.contrib.sites.models import Site
from django.core.cache import cache
from mistune import escape, escape_link
from pygments import highlight
from pygments.lexers import get_lexer_by_name
from pygments.formatters import html
import logging
import requests
import uuid
import os
from pygments.lexers import get_lexer_by_name
logger = logging.getLogger(__name__)
@ -187,6 +180,11 @@ def send_email(emailto, title, content):
content=content)
def generate_code() -> str:
"""生成随机数验证码"""
return ''.join(random.sample(string.digits, 6))
def parse_dict_to_url(dict):
from urllib.parse import quote
url = '&'.join(['{}={}'.format(quote(k, safe='/'), quote(v, safe='/'))

@ -12,11 +12,14 @@
@file: forms.py
@time: 2016/11/20 下午3:16
"""
from django import forms
from django.contrib.auth import get_user_model, password_validation
from django.contrib.auth.forms import AuthenticationForm, UserCreationForm
from django.forms import widgets
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from django.forms import widgets
from . import utils
from .models import BlogUser
class LoginForm(AuthenticationForm):
@ -50,3 +53,79 @@ class RegisterForm(UserCreationForm):
class Meta:
model = get_user_model()
fields = ("username", "email")
class ForgetPasswordForm(forms.Form):
new_password1 = forms.CharField(
label="新密码",
widget=forms.PasswordInput(
attrs={
"class": "form-control",
'placeholder': "密码"
}
),
)
new_password2 = forms.CharField(
label="确认密码",
widget=forms.PasswordInput(
attrs={
"class": "form-control",
'placeholder': "确认密码"
}
),
)
email = forms.EmailField(
label='邮箱',
widget=forms.TextInput(
attrs={
'class': 'form-control',
'placeholder': "邮箱"
}
),
)
code = forms.CharField(
label='验证码',
widget=forms.TextInput(
attrs={
'class': 'form-control',
'placeholder': "验证码"
}
),
)
def clean_new_password2(self):
password1 = self.data.get("new_password1")
password2 = self.data.get("new_password2")
if password1 and password2 and password1 != password2:
raise ValidationError("两次密码不一致")
password_validation.validate_password(password2)
return password2
def clean_email(self):
user_email = self.cleaned_data.get("email")
if not BlogUser.objects.filter(
email=user_email
).exists():
# todo 这里的报错提示可以判断一个邮箱是不是注册过,如果不想暴露可以修改
raise ValidationError("未找到邮箱对应的用户")
return user_email
def clean_code(self):
code = self.cleaned_data.get("code")
error = utils.verify(
email=self.cleaned_data.get("email"),
code=code,
)
if error:
raise ValidationError(error)
return code
class ForgetPasswordCodeForm(forms.Form):
email = forms.EmailField(
label="邮箱号"
)

@ -1,13 +1,13 @@
from django.conf import settings
from django.test import Client, RequestFactory, TestCase
from blog.models import Article, Category, Tag
from django.contrib.auth import get_user_model
from DjangoBlog.utils import delete_view_cache, delete_sidebar_cache
from accounts.models import BlogUser
from django.urls import reverse
from DjangoBlog.utils import *
from django.conf import settings
from django.utils import timezone
from DjangoBlog.utils import *
from accounts.models import BlogUser
from blog.models import Article, Category
from . import utils
# Create your tests here.
@ -15,6 +15,12 @@ class AccountTest(TestCase):
def setUp(self):
self.client = Client()
self.factory = RequestFactory()
self.blog_user = BlogUser.objects.create_user(
username="test",
email="admin@admin.com",
password="12345678"
)
self.new_test = "xxx123--="
def test_validate_account(self):
site = get_current_site().domain
@ -111,3 +117,101 @@ class AccountTest(TestCase):
response = self.client.get(article.get_admin_url())
self.assertIn(response.status_code, [301, 302, 200])
def test_verify_email_code(self):
to_email = "admin@admin.com"
code = generate_code()
utils.set_code(to_email, code)
utils.send_verify_email(to_email, code)
err = utils.verify("admin@admin.com", code)
self.assertEqual(err, None)
err = utils.verify("admin@123.com", code)
self.assertEqual(type(err), str)
def test_forget_password_email_code_success(self):
resp = self.client.post(
path=reverse("account:forget_password_code"),
data=dict(email="admin@admin.com")
)
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp.content.decode("utf-8"), "ok")
def test_forget_password_email_code_fail(self):
resp = self.client.post(
path=reverse("account:forget_password_code"),
data=dict()
)
self.assertEqual(resp.content.decode("utf-8"), "错误的邮箱")
resp = self.client.post(
path=reverse("account:forget_password_code"),
data=dict(email="admin@com")
)
self.assertEqual(resp.content.decode("utf-8"), "错误的邮箱")
def test_forget_password_email_success(self):
code = generate_code()
utils.set_code(self.blog_user.email, code)
data = dict(
new_password1=self.new_test,
new_password2=self.new_test,
email=self.blog_user.email,
code=code,
)
resp = self.client.post(
path=reverse("account:forget_password"),
data=data
)
self.assertEqual(resp.status_code, 302)
# 验证用户密码是否修改成功
blog_user = BlogUser.objects.filter(
email=self.blog_user.email,
).first() # type: BlogUser
self.assertNotEqual(blog_user, None)
self.assertEqual(blog_user.check_password(data["new_password1"]), True)
def test_forget_password_email_not_user(self):
data = dict(
new_password1=self.new_test,
new_password2=self.new_test,
email="123@123.com",
code="123456",
)
resp = self.client.post(
path=reverse("account:forget_password"),
data=data
)
self.assertEqual(resp.status_code, 200)
self.assertFormError(
response=resp,
form="form",
field="email",
errors="未找到邮箱对应的用户"
)
def test_forget_password_email_code_error(self):
code = generate_code()
utils.set_code(self.blog_user.email, code)
data = dict(
new_password1=self.new_test,
new_password2=self.new_test,
email=self.blog_user.email,
code="111111",
)
resp = self.client.post(
path=reverse("account:forget_password"),
data=data
)
self.assertEqual(resp.status_code, 200)
self.assertFormError(
response=resp,
form="form",
field="code",
errors="验证码错误"
)

@ -14,10 +14,10 @@
"""
from django.conf.urls import url
from django.contrib.auth import views as auth_view
from django.urls import path
from . import views
from .forms import LoginForm
from . import views
app_name = "accounts"
@ -33,4 +33,11 @@ urlpatterns = [url(r'^login/$',
name='logout'),
path(r'account/result.html',
views.account_result,
name='result')]
name='result'),
url(r'^forget_password/$',
views.ForgetPasswordView.as_view(),
name='forget_password'),
url(r'^forget_password_code/$',
views.ForgetPasswordEmailCode.as_view(),
name='forget_password_code'),
]

@ -0,0 +1,45 @@
import typing
from datetime import timedelta
from django.core.cache import cache
from DjangoBlog.utils import send_email
_code_ttl = timedelta(minutes=5)
def send_verify_email(to_mail: str, code: str, subject: str = "邮件验证码"):
"""发送重设密码验证码
Args:
to_mail: 接受邮箱
subject: 邮件主题
code: 验证码
"""
html_content = f"您正在重设密码,验证码为:{code}, 5分钟内有效请妥善保管"
send_email([to_mail], subject, html_content)
def verify(email: str, code: str) -> typing.Optional[str]:
"""验证code是否有效
Args:
email: 请求邮箱
code: 验证码
Return:
如果有错误就返回错误str
Node:
这里的错误处理不太合理应该采用raise抛出
否测调用方也需要对error进行处理
"""
cache_code = get_code(email)
if cache_code != code:
return "验证码错误"
def set_code(email: str, code: str):
"""设置code"""
cache.set(email, code, _code_ttl.seconds)
def get_code(email: str) -> typing.Optional[str]:
"""获取code"""
return cache.get(email)

@ -1,24 +1,30 @@
from django.shortcuts import render
import logging
from .forms import RegisterForm, LoginForm
from django.contrib.auth import authenticate, login, logout
# from django.views.generic.edit import FormView
from django.views.generic import FormView, RedirectView
from django.conf import settings
from django.contrib import auth
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.contrib.auth import get_user_model
from django.shortcuts import get_object_or_404
from django.contrib.auth import logout
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth.hashers import make_password
from django.http import HttpResponseRedirect, HttpResponseForbidden
from django.http.request import HttpRequest
from django.http.response import HttpResponse
from django.shortcuts import get_object_or_404
from django.shortcuts import render
from django.urls import reverse
from django.contrib.auth.forms import AuthenticationForm, UserCreationForm
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.views.decorators.csrf import csrf_protect
from django.contrib import auth
from django.views.decorators.cache import never_cache
from django.shortcuts import redirect
from django.utils.decorators import method_decorator
from django.views.decorators.debug import sensitive_post_parameters
from django.utils.http import is_safe_url
from DjangoBlog.utils import send_email, get_sha256, get_current_site
from django.conf import settings
from django.views import View
from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_protect
from django.views.decorators.debug import sensitive_post_parameters
from django.views.generic import FormView, RedirectView
from DjangoBlog.utils import send_email, get_sha256, get_current_site, generate_code
from . import utils
from .forms import RegisterForm, LoginForm, ForgetPasswordForm, ForgetPasswordCodeForm
from .models import BlogUser
logger = logging.getLogger(__name__)
@ -62,7 +68,7 @@ class RegisterView(FormView):
content=content)
url = reverse('accounts:result') + \
'?type=register&id=' + str(user.id)
'?type=register&id=' + str(user.id)
return HttpResponseRedirect(url)
else:
return self.render_to_response({
@ -89,6 +95,7 @@ class LoginView(FormView):
template_name = 'account/login.html'
success_url = '/'
redirect_field_name = REDIRECT_FIELD_NAME
login_ttl = 2626560 # 一个月的时间
@method_decorator(sensitive_post_parameters('password'))
@method_decorator(csrf_protect)
@ -115,6 +122,8 @@ class LoginView(FormView):
logger.info(self.redirect_field_name)
auth.login(self.request, form.get_user())
if self.request.POST.get("remember"):
self.request.session.set_expiry(self.login_ttl)
return super(LoginView, self).form_valid(form)
# return HttpResponseRedirect('/')
else:
@ -126,8 +135,8 @@ class LoginView(FormView):
redirect_to = self.request.POST.get(self.redirect_field_name)
if not is_safe_url(
url=redirect_to, allowed_hosts=[
self.request.get_host()]):
url=redirect_to, allowed_hosts=[
self.request.get_host()]):
redirect_to = self.success_url
return redirect_to
@ -163,3 +172,32 @@ def account_result(request):
})
else:
return HttpResponseRedirect('/')
class ForgetPasswordView(FormView):
form_class = ForgetPasswordForm
template_name = 'account/forget_password.html'
def form_valid(self, form):
if form.is_valid():
blog_user = BlogUser.objects.filter(email=form.cleaned_data.get("email")).get()
blog_user.password = make_password(form.cleaned_data["new_password2"])
blog_user.save()
return HttpResponseRedirect('/login/')
else:
return self.render_to_response({'form': form})
class ForgetPasswordEmailCode(View):
def post(self, request: HttpRequest):
form = ForgetPasswordCodeForm(request.POST)
if not form.is_valid():
return HttpResponse("错误的邮箱")
to_email = form.cleaned_data["email"]
code = generate_code()
utils.send_verify_email(to_email, code)
utils.set_code(to_email, code)
return HttpResponse("ok")

@ -111,6 +111,7 @@ class TagAdmin(admin.ModelAdmin):
class CategoryAdmin(admin.ModelAdmin):
list_display = ('name', 'parent_category', 'index')
exclude = ('slug', 'last_mod_time', 'created_time')

@ -174,9 +174,10 @@ class Category(BaseModel):
null=True,
on_delete=models.CASCADE)
slug = models.SlugField(default='no-slug', max_length=60, blank=True)
index = models.IntegerField(default=0, verbose_name="权重排序-越大越靠前")
class Meta:
ordering = ['name']
ordering = ['-index']
verbose_name = "分类"
verbose_name_plural = verbose_name

@ -0,0 +1,9 @@
.button {
border: none;
padding: 4px 80px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
}

@ -0,0 +1,47 @@
let wait = 60;
function time(o) {
if (wait == 0) {
o.removeAttribute("disabled");
o.value = "获取验证码";
wait = 60
return false
} else {
o.setAttribute("disabled", true);
o.value = "重新发送(" + wait + ")";
wait--;
setTimeout(function () {
time(o)
},
1000)
}
}
document.getElementById("btn").onclick = function () {
let id_email = $("#id_email")
let token = $("*[name='csrfmiddlewaretoken']").val()
let ts = this
let myErr = $("#myErr")
$.ajax(
{
url: "/forget_password_code/",
type: "POST",
data: {
"email": id_email.val(),
"csrfmiddlewaretoken": token
},
success: function (result) {
if (result != "ok") {
myErr.remove()
id_email.after("<ul className='errorlist' id='myErr'><li>" + result + "</li></ul>")
return
}
myErr.remove()
time(ts)
},
error: function (e) {
alert("发送失败,请重试")
}
}
);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 B

@ -1,14 +1 @@
#!/usr/bin/env python
# encoding: utf-8
"""
@version: ??
@author: liangliangyy
@license: MIT Licence
@contact: liangliangyy@gmail.com
@site: https://www.lylinux.net/
@software: PyCharm
@file: __init__.py.py
@time: 2017/8/27 上午11:40
"""

@ -1,19 +1,6 @@
#!/usr/bin/env python
# encoding: utf-8
from haystack.query import SearchQuerySet
"""
@version: ??
@author: liangliangyy
@license: MIT Licence
@contact: liangliangyy@gmail.com
@site: https://www.lylinux.net/
@software: PyCharm
@file: blogapi.py
@time: 2017/8/27 上午11:40
"""
from blog.models import Article, Category, Tag
from haystack.query import EmptySearchQuerySet, SearchQuerySet
from blog.models import Article, Category
class BlogApi():

@ -1,30 +1,17 @@
#!/usr/bin/env python
# encoding: utf-8
"""
@version: ??
@author: liangliangyy
@license: MIT Licence
@contact: liangliangyy@gmail.com
@site: https://www.lylinux.net/
@software: PyCharm
@file: commonapi.py
@time: 2017/9/2 上午1:43
"""
import requests
import json
import logging
import requests
logger = logging.getLogger(__name__)
class TuLing():
class TuLing:
def __init__(self):
self.__key__ = '2f1446eb0321804291b0a1e217c25bb5'
self.__appid__ = 137762
def __build_req_url(self, content):
def _build_req_url(self, content):
return 'http://www.tuling123.com/openapi/api?key=%s&info=%s&userid=%s' % (
self.__key__, content, self.__appid__)
@ -34,7 +21,7 @@ class TuLing():
def getdata(self, content):
try:
requrl = self.__build_req_url(content)
requrl = self._build_req_url(content)
res = self.UserAgent(requrl).decode('utf-8')
jsons = json.loads(res, encoding='utf-8')

@ -0,0 +1,29 @@
{% extends 'share_layout/base_account.html' %}
{% load static %}
{% block content %}
<div class="container">
<h2 class="form-signin-heading text-center">忘记密码</h2>
<div class="card card-signin">
<img class="img-circle profile-img" src="{% static 'blog/img/avatar.png' %}" alt="">
<form class="form-signin" action="{% url 'account:forget_password' %}" method="post">
{% csrf_token %}
{{ form.non_field_errors }}
{% for field in form %}
{{ field }}
{{ field.errors }}
{% endfor %}
<input type="button" class="button" id="btn" value="获取验证码">
<button class="btn btn-lg btn-primary btn-block" type="submit">提交</button>
</form>
</div>
<p class="text-center">
<a href="/">Home Page</a>
|
<a href="{% url "account:login" %}">login page</a>
</p>
</div> <!-- /container -->
{% endblock %}

@ -25,7 +25,7 @@
<div class="checkbox">
{% comment %}<a class="pull-right">Need help?</a>{% endcomment %}
<label>
<input type="checkbox" value="remember-me"> Stay signed in
<input type="checkbox" value="remember-me" name="remember"> Stay signed in
</label>
</div>
{% load oauth_tags %}
@ -37,6 +37,8 @@
<a href="{% url "account:register" %}">Create Account</a>
|
<a href="/">Home Page</a>
|
<a href="{% url "account:forget_password" %}">忘记密码</a>
</p>
</div> <!-- /container -->

@ -11,6 +11,7 @@
<link rel="icon" href="../../favicon.ico">
<meta name="robots" content="noindex">
<title>{{ SITE_NAME }} | {{ SITE_DESCRIPTION }}</title>
<link href="{% static 'account/css/account.css' %}" rel="stylesheet">
{% load compress %}
{% compress css %}
<!-- Bootstrap core CSS -->
@ -41,4 +42,6 @@
<!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
</body>
<script type="text/javascript" src="{% static 'blog/js/jquery-3.6.0.min.js' %}"></script>
<script src="{% static 'account/js/account.js' %}" type="text/javascript"></script>
</html>
Loading…
Cancel
Save