Browse Source

fix send mail bug,perfect auth

honmaple 8 years ago
parent
commit
74bf8f6acc

+ 5 - 5
forums/admin/user.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-12-17 11:57:11 (CST)
-# Last Update:星期三 2017-3-29 20:21:42 (CST)
+# Last Update:星期六 2017-4-1 21:21:49 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -30,10 +30,10 @@ class UserView(BaseView):
     column_editable_list = ['is_confirmed', 'is_superuser']
     form_columns = ('username', 'email', 'password', 'is_confirmed',
                     'is_superuser')
-    inline_models = (UserInfo, UserSetting)
-    form_extra_fields = {
-        'password': PasswordField('Password', [DataRequired()])
-    }
+    # inline_models = (UserInfo, UserSetting)
+    # form_extra_fields = {
+    #     'password': PasswordField('Password', [DataRequired()])
+    # }
 
 
 class UserInfoView(BaseView):

+ 14 - 12
forums/api/auth/views.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-10-28 10:26:10 (CST)
-# Last Update:星期三 2017-3-29 11:24:40 (CST)
+# Last Update:星期六 2017-4-1 21:59:10 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -115,29 +115,31 @@ class ForgetView(MethodView):
         password = ''.join(sample(ascii_letters + digits, 12))
         user.set_password(password)
         user.save()
-        self.email(user)
-        return HTTPResponse(HTTPResponse.NORMAL_STATUS).to_response()
-
-    def email(self, user):
-        html = render_template(
-            'templet/forget.html', confirm_url=user.password)
+        html = render_template('templet/forget.html', confirm_url=password)
         subject = "Please update your password in time"
-        user.send_email(html, subject)
+        user.send_email(html=html, subject=subject)
+        return HTTPResponse(HTTPResponse.NORMAL_STATUS).to_response()
 
 
 class ConfirmView(MethodView):
     @login_required
     def post(self):
-        if current_user.is_confirmed:
+        user = request.user
+        if user.is_confirmed:
             return HTTPResponse(
                 HTTPResponse.AUTH_USER_IS_CONFIRMED).to_response()
-        token = current_user.email_token()
+        if not user.email_is_allowed:
+            msg = "user isn't allowed to send email"
+            return HTTPResponse(
+                HTTPResponse.AUTH_USER_IS_CONFIRMED, message=msg).to_response()
+        token = user.email_token
         confirm_url = url_for(
             'auth.confirm_token', token=token, _external=True)
         html = render_template('templet/email.html', confirm_url=confirm_url)
         subject = _("Please confirm  your email")
-        current_user.send_email(html, subject)
-        return HTTPResponse(HTTPResponse.NORMAL_STATUS).to_response()
+        user.send_email(html=html, subject=subject)
+        return HTTPResponse(
+            HTTPResponse.NORMAL_STATUS, message='send success').to_response()
 
 
 class ConfirmTokenView(MethodView):

+ 27 - 51
forums/api/user/models.py

@@ -6,16 +6,13 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-12-15 21:09:08 (CST)
-# Last Update:星期六 2017-4-1 20:28:26 (CST)
+# Last Update:星期六 2017-4-1 22:4:46 (CST)
 #          By:
 # Description:
 # **************************************************************************
-from datetime import datetime
-from threading import Thread
-
+from datetime import datetime, timedelta
 from flask import current_app
 from flask_login import UserMixin, current_user
-from flask_mail import Message
 from itsdangerous import BadSignature, SignatureExpired, URLSafeTimedSerializer
 from pytz import all_timezones
 from sqlalchemy import event
@@ -23,6 +20,7 @@ from sqlalchemy.orm import object_session
 from werkzeug.security import check_password_hash, generate_password_hash
 
 from flask_maple.models import ModelMixin
+from flask_maple.mail import MailMixin
 from forums.count import Count
 from forums.extension import db, mail
 from forums.common.records import load_online_sign_users
@@ -33,7 +31,7 @@ user_follower = db.Table(
     db.Column('follower_id', db.Integer, db.ForeignKey('users.id')))
 
 
-class User(db.Model, UserMixin, ModelMixin):
+class User(db.Model, UserMixin, ModelMixin, MailMixin):
     __tablename__ = 'users'
     id = db.Column(db.Integer, primary_key=True)
     username = db.Column(db.String(49), unique=True, nullable=False)
@@ -97,6 +95,25 @@ class User(db.Model, UserMixin, ModelMixin):
     def message_count(self, value):
         return Count.user_message_count(self.id, value)
 
+    @property
+    def send_email_time(self):
+        # return self.receive_messages.filter_by(status='0').count()
+        return Count.user_email_time(self.id)
+
+    @send_email_time.setter
+    def send_email_time(self, value):
+        return Count.user_email_time(self.id, value)
+
+    @property
+    def email_is_allowed(self):
+        t = self.send_email_time
+        t = datetime.strptime(t, '%Y-%m-%d %H:%M:%S')
+        now = datetime.now()
+        if t + timedelta(hours=3) < now:
+            self.send_email_time = now.strftime('%Y-%m-%d %H:%M:%S')
+            return True
+        return False
+
     def __str__(self):
         return self.username
 
@@ -109,51 +126,6 @@ class User(db.Model, UserMixin, ModelMixin):
     def check_password(self, raw_password):
         return check_password_hash(self.password, raw_password)
 
-    def send_async_email(self, msg):
-        app = current_app._get_current_object()
-        with app.app_context():
-            mail.send(msg)
-
-    def send_email(self,
-                   subject='',
-                   recipients=None,
-                   body=None,
-                   html=None,
-                   **kwargs):
-        if recipients is None:
-            recipients = self.email
-        if not isinstance(recipients, list):
-            recipients = [recipients]
-        msg = Message(subject=subject, recipients=recipients, html=html)
-        thr = Thread(target=self.send_async_email, args=[msg])
-        thr.start()
-
-    @property
-    def email_token(self):
-        config = current_app.config
-        secret_key = config.setdefault('SECRET_KEY')
-        salt = config.setdefault('SECURITY_PASSWORD_SALT')
-        serializer = URLSafeTimedSerializer(secret_key)
-        token = serializer.dumps(self.email, salt=salt)
-        return token
-
-    @staticmethod
-    def check_email_token(token, max_age=1800):
-        config = current_app.config
-        secret_key = config.setdefault('SECRET_KEY')
-        salt = config.setdefault('SECURITY_PASSWORD_SALT')
-        serializer = URLSafeTimedSerializer(secret_key)
-        try:
-            email = serializer.loads(token, salt=salt, max_age=max_age)
-        except BadSignature:
-            return False
-        except SignatureExpired:
-            return False
-        user = User.query.filter_by(email=email).first()
-        if user is None:
-            return False
-        return user
-
     @property
     def token(self):
         config = current_app.config
@@ -180,6 +152,10 @@ class User(db.Model, UserMixin, ModelMixin):
             return False
         return user
 
+    def send_email(self, *args, **kwargs):
+        kwargs.update(recipients=[self.email])
+        mail.send_email(*args, **kwargs)
+
 
 class UserInfo(db.Model, ModelMixin):
     __tablename__ = 'userinfo'

+ 2 - 3
forums/api/user/urls.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-12-15 22:24:23 (CST)
-# Last Update:星期六 2017-3-25 20:52:36 (CST)
+# Last Update:星期六 2017-4-1 21:36:46 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -28,8 +28,7 @@ followings = UserFollowingListView.as_view('following')
 
 site.add_url_rule('', view_func=user_list)
 site.add_url_rule('/<username>', view_func=user)
-site.add_url_rule('/<username>', view_func=topics)
-site.add_url_rule('/<username>/topics', view_func=user)
+site.add_url_rule('/<username>/topics', view_func=topics)
 site.add_url_rule('/<username>/replies', view_func=replies)
 site.add_url_rule('/<username>/collects', view_func=collects)
 site.add_url_rule('/<username>/followers', view_func=followers)

+ 2 - 2
forums/common/response.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-10-25 21:07:00 (CST)
-# Last Update:星期六 2017-4-1 18:51:49 (CST)
+# Last Update:星期六 2017-4-1 22:6:39 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -52,7 +52,7 @@ class HTTPResponse(object):
                  description='',
                  pageinfo=None):
         self.status = status
-        self.message = self.STATUS_DESCRIPTION.get(status)
+        self.message = message or self.STATUS_DESCRIPTION.get(status)
         self.data = data
         self.description = description
         self.pageinfo = pageinfo

+ 11 - 1
forums/common/views.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2017-03-13 13:29:37 (CST)
-# Last Update:星期三 2017-3-29 13:54:44 (CST)
+# Last Update:星期六 2017-4-1 22:15:52 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -14,6 +14,12 @@ from flask import request, current_app, flash, redirect, url_for
 from flask.views import MethodView
 from flask_login import login_required, current_user
 from forums.permission import confirm_permission
+from forums.extension import cache
+
+
+def cache_key():
+    key = request.url
+    return 'view:%s' % key
 
 
 def is_confirmed(func):
@@ -41,6 +47,10 @@ class BaseMethodView(MethodView):
             number = per_page
         return page, number
 
+    @cache.cached(timeout=180, key_prefix=cache_key)
+    def dispatch_request(self, *args, **kwargs):
+        return super(BaseMethodView, self).dispatch_request(*args, **kwargs)
+
 
 class IsAuthMethodView(BaseMethodView):
     decorators = [login_required]

+ 8 - 1
forums/count.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2017-03-29 21:28:52 (CST)
-# Last Update:星期六 2017-4-1 20:47:11 (CST)
+# Last Update:星期六 2017-4-1 22:5:53 (CST)
 #          By:
 # Description: 一些统计信息
 # **************************************************************************
@@ -87,3 +87,10 @@ class Count(object):
         if clear:
             redis_data.hset(key, 'message', 0)
         return redis_data.hget(key, 'message') or 0
+
+    @classmethod
+    def user_email_time(cls, userId, value=None):
+        key = 'count:user:%s' % str(userId)
+        if value is not None:
+            redis_data.hset(key, 'email', value)
+        return redis_data.hget(key, 'email') or '2015-1-1 1:1:1'

+ 2 - 2
forums/extension.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-10-25 21:57:10 (CST)
-# Last Update:星期三 2017-3-29 21:25:57 (CST)
+# Last Update:星期六 2017-4-1 21:31:33 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -24,7 +24,7 @@ from flask_maple.bootstrap import Bootstrap
 from flask_maple.error import Error
 from flask_maple.captcha import Captcha
 from flask_maple.redis import Redis
-from flask_mail import Mail
+from flask_maple.mail import Mail
 from flask_cache import Cache
 from flask_principal import Principal
 from flask_login import LoginManager

+ 7 - 1
manager.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-10-25 22:08:39 (CST)
-# Last Update:星期六 2017-4-1 18:47:53 (CST)
+# Last Update:星期六 2017-4-1 21:34:3 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -68,6 +68,12 @@ def babel_compile():
 
 
 @manager.option('-u', '--username', dest='username')
+def delete_user(username):
+    user = User.query.filter_by(username=username).first()
+    user.delete()
+
+
+@manager.option('-u', '--username', dest='username')
 @manager.option('-e', '--email', dest='email')
 @manager.option('-w', '--password', dest='password')
 def create_user(username, email, password):

+ 3 - 3
static/assets/home.js

@@ -12,9 +12,9 @@ else{$("#error").text(response.message);}}}
 $('button#login').click(function(){$.ajax({type:"POST",url:url.login,data:JSON.stringify({username:$('#username').val(),password:$('#password').val(),captcha:$("#captcha").val(),remember:$("#remember").is(':checked')}),contentType:'application/json;charset=UTF-8',success:function(response){return AuthCallBack(response);}});});$('button#register').click(function(){$.ajax({type:"POST",url:url.register,data:JSON.stringify({username:$('#username').val(),email:$('#email').val(),password:$('#password').val(),captcha:$("#captcha").val()}),contentType:'application/json;charset=UTF-8',success:function(response){return AuthCallBack(response);}});});$('button#forget').click(function(){$.ajax({type:"POST",url:url.forget,data:JSON.stringify({email:$('#email').val(),captcha:$("#captcha").val()}),contentType:'application/json;charset=UTF-8',success:function(response){return AuthCallBack(response);}});});});function loadFile(event){var _file=document.getElementById("avatar");var i=_file.value.lastIndexOf('.');var len=_file.value.length;var extEndName=_file.value.substring(i+1,len);var extName="JPG,PNG";if(extName.indexOf(extEndName.toUpperCase())==-1){alert("您只能上传"+extName+"格式的文件");$('#avatar').val('');}else{var reader=new FileReader();reader.onload=function(){var icon='<i class="fa fa-exchange"></i>'+'\n';var img='<img src="'+reader.result+'" title="avatar" class="avatar img-circle">';$("#show-avatar").html(icon+img);};reader.readAsDataURL(event.target.files[0]);}}
 function getQueryParams(k){var p={};location.search.replace(/[?&]+([^=&]+)=([^&]*)/gi,function(s,k,v){p[k]=v;});return k?p[k]:p;}
 function SortFuntion(){var within=$('select#within').val();var orderby=$('select#orderby').val();var desc=$('select#desc').val();var params=getQueryParams();params.within=within;params.orderby=orderby;params.desc=desc;window.location.href=window.location.pathname+'?'+$.param(params);}
-$(document).ready(function(){$('select#within').change(function(){SortFuntion();});$('select#orderby').change(function(){SortFuntion();});$('select#desc').change(function(){SortFuntion();});$('span#email-confirm').click(function(){$.ajax({type:"POST",url:"/confirm-email",data:JSON.stringify({}),contentType:'application/json;charset=UTF-8',success:function(result){if(result.judge===true)
-{alert(result.error);}else
-{alert(result.error);}}});});});function dispatch(){var q=document.getElementById("search");if(q.value!==""){var url='https://www.google.com/search?q=site:forums.honmaple.org%20'+q.value;if(navigator.userAgent.indexOf('iPad')>-1||navigator.userAgent.indexOf('iPod')>-1||navigator.userAgent.indexOf('iPhone')>-1){location.href=url;}else{window.open(url,"_blank");}
+$(document).ready(function(){$('select#within').change(function(){SortFuntion();});$('select#orderby').change(function(){SortFuntion();});$('select#desc').change(function(){SortFuntion();});$('span#email-confirm').click(function(){$.ajax({type:"POST",url:"/confirm",data:JSON.stringify({}),contentType:'application/json;charset=UTF-8',success:function(response){if(response.status==='200')
+{alert(response.message);}else
+{alert(response.message);}}});});});function dispatch(){var q=document.getElementById("search");if(q.value!==""){var url='https://www.google.com/search?q=site:forums.honmaple.org%20'+q.value;if(navigator.userAgent.indexOf('iPad')>-1||navigator.userAgent.indexOf('iPod')>-1||navigator.userAgent.indexOf('iPhone')>-1){location.href=url;}else{window.open(url,"_blank");}
 return false;}else{return false;}}
 function Follow(obj,data,url){if(obj.hasClass('active')){$.ajax({type:"DELETE",url:url,data:data,contentType:'application/json;charset=UTF-8',success:function(response){if(response.status==='200')
 {obj.text('关注').removeClass('active');}else

+ 5 - 5
static/styles/forums.js

@@ -26,17 +26,17 @@ $(document).ready(function(){
   $('span#email-confirm').click(function(){
     $.ajax ({
       type : "POST",
-      url : "/confirm-email",
+      url : "/confirm",
       data:JSON.stringify({
       }),
       contentType: 'application/json;charset=UTF-8',
-      success: function(result) {
-        if (result.judge === true)
+      success: function(response) {
+        if (response.status === '200')
         {
-          alert(result.error);
+          alert(response.message);
         } else
         {
-          alert(result.error);
+          alert(response.message);
         }}
     });
   });