-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 2a13fc6
Showing
143 changed files
with
4,397 additions
and
0 deletions.
There are no files selected for viewing
Binary file not shown.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
#! /usr/bin/env python3 | ||
import os | ||
from app import create_app, db | ||
from app.models import User, Role, Permission | ||
from flask.ext.script import Manager, Shell | ||
|
||
|
||
app = create_app(os.getenv('FLASK_CONFIG') or 'default') | ||
manager = Manager(app) | ||
|
||
|
||
def make_shell_context(): | ||
return dict(app=app, db=db, User=User, Role=Role, Permission=Permission) | ||
|
||
manager.add_command('shell', Shell(make_context=make_shell_context)) | ||
|
||
|
||
@manager.command | ||
def test(coverage=False): | ||
if coverage and not os.environ.get('FLASK_COVERAGE'): | ||
import sys | ||
os.environ['FLASK_COVERAGE'] = '1' | ||
os.execvp(sys.executable, [sys.executable] + sys.argv) | ||
import unittest | ||
tests = unittest.TestLoader().discover('tests') | ||
unittest.TextTestRunner(verbosity=2).run(tests) | ||
|
||
if __name__ == '__main__': | ||
manager.run() |
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
#! /usr/bin/env python3 | ||
|
||
import pymysql | ||
from flask import Flask | ||
from flask.ext.bootstrap import Bootstrap | ||
from flask.ext.moment import Moment | ||
from flask.ext.sqlalchemy import SQLAlchemy | ||
from flask.ext.login import LoginManager | ||
from flask.ext.mail import Mail | ||
from config import config | ||
from flask.ext.pagedown import PageDown | ||
|
||
pymysql.install_as_MySQLdb() | ||
|
||
bootstrap = Bootstrap() | ||
moment = Moment() | ||
mail = Mail() | ||
db = SQLAlchemy() | ||
pagedown = PageDown() | ||
login_manager = LoginManager() | ||
login_manager.session_protection = 'strong' | ||
login_manager.login_view = 'auth.login' | ||
|
||
|
||
def create_app(config_name): | ||
app = Flask(__name__) | ||
app.config.from_object(config[config_name]) | ||
config[config_name].init_app(app) | ||
|
||
bootstrap.init_app(app) | ||
moment.init_app(app) | ||
mail.init_app(app) | ||
db.init_app(app) | ||
pagedown.init_app(app) | ||
login_manager.init_app(app) | ||
|
||
# 注册蓝本 | ||
from .main import main as main_blueprint | ||
app.register_blueprint(main_blueprint) | ||
|
||
from .auth import auth as auth_blueprint | ||
# url_prefix 指定路由前缀 /auth/login | ||
app.register_blueprint(auth_blueprint, url_prefix='/auth') | ||
|
||
return app | ||
|
||
|
||
from app.main import views |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from flask import Blueprint | ||
|
||
auth = Blueprint('auth', __name__) | ||
|
||
from . import views |
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
from flask.ext.wtf import Form | ||
from wtforms import StringField, SubmitField, PasswordField, BooleanField | ||
from wtforms.validators import DataRequired, Length, Email, Regexp, EqualTo | ||
from wtforms import ValidationError | ||
from ..models import User | ||
|
||
|
||
class LoginForm(Form): | ||
email = StringField('邮箱', validators=[DataRequired(), Length(1, 64), Email()]) | ||
password = PasswordField('密码', validators=[DataRequired()]) | ||
remember_me = BooleanField('记住我') | ||
submit = SubmitField('登陆') | ||
|
||
|
||
class RegistrationForm(Form): | ||
username = StringField('用户名', validators=[DataRequired(), Length(1, 64), Regexp('^(?!_)(?!.*?_$)[a-zA-Z0-9_' | ||
'\u4e00-\u9fa5]+$', 0, | ||
message='非法用户名')]) | ||
email = StringField('邮箱', validators=[DataRequired(), Length(1, 64), Email()]) | ||
password = PasswordField('密码', validators=[DataRequired(), EqualTo('cfm_password', message='密码必须一致')]) | ||
cfm_password = PasswordField('确认密码', validators=[DataRequired()]) | ||
submit = SubmitField('注册') | ||
|
||
def validate_email(self, field): | ||
if User.query.filter_by(email=field.data).first(): | ||
raise ValidationError('邮箱已经注册') | ||
|
||
def validate_username(self, field): | ||
if User.query.filter_by(username=field.data).first(): | ||
raise ValidationError('用户名已经存在') | ||
|
||
|
||
class ChangePasswordForm(Form): | ||
old_password = StringField('旧密码', validators=[DataRequired()]) | ||
new_password = PasswordField('新密码', validators=[DataRequired(), EqualTo('cfm_new_password', message='密码必须一致')]) | ||
cfm_new_password = PasswordField('确认新密码', validators=[DataRequired()]) | ||
submit = SubmitField('保存') | ||
|
||
|
||
class ChangeEmailForm(Form): | ||
password = PasswordField('密码', validators=[DataRequired()]) | ||
new_email = StringField('邮箱', validators=[DataRequired(), Length(1, 64), Email()]) | ||
submit = SubmitField('保存') | ||
|
||
def validate_new_email(self, field): | ||
if User.query.filter_by(email=field.data).first(): | ||
raise ValidationError('邮箱已经被注册') | ||
|
||
|
||
class ResetPasswordForm(Form): | ||
email = StringField('Email', validators=[DataRequired(), Length(1, 64),Email()]) | ||
new_password = PasswordField('新密码', validators=[DataRequired(), EqualTo('cfm_new_password', message='密码必须一致')]) | ||
cfm_password = PasswordField('确认新密码', validators=[DataRequired]) | ||
submit = SubmitField('确认') | ||
|
||
def validate_email(self, field): | ||
if User.query.filter_by(email=field.data).first() is None: | ||
raise ValidationError('Unknown email address.') | ||
|
||
|
||
class PasswordResetRequestForm(Form): | ||
email = StringField('Email', validators=[DataRequired(), Length(1, 64), Email()]) | ||
submit = SubmitField('Reset Password') | ||
|
||
|
||
class ChangeUsernameForm(Form): | ||
username = StringField('用户名', validators=[DataRequired(), Length(1, 64), Regexp('^(?!_)(?!.*?_$)[a-zA-Z0-9_' | ||
'\u4e00-\u9fa5]+$', 0, | ||
message='非法用户名')]) | ||
submit = SubmitField('保存') | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
from flask import render_template, redirect, request, url_for, flash | ||
from . import auth | ||
from flask.ext.login import login_user, login_required, logout_user, current_user | ||
from ..models import User | ||
from .forms import LoginForm, RegistrationForm, ChangePasswordForm, ChangeEmailForm, ResetPasswordForm, \ | ||
PasswordResetRequestForm, ChangeUsernameForm | ||
from .. import db | ||
from ..email import send_email | ||
|
||
|
||
@auth.route('/login', methods=['GET', 'POST']) | ||
def login(): | ||
form = LoginForm() | ||
if form.validate_on_submit(): | ||
user = User.query.filter_by(email=form.email.data).first() | ||
if user is not None and user.verify_password(form.password.data): | ||
login_user(user, form.remember_me.data) | ||
return redirect(request.args.get('next') or url_for('main.index', name=user.username)) | ||
flash('Invalid username or password.') | ||
return render_template('auth/login.html', form=form) | ||
|
||
|
||
@auth.route('/logout') | ||
@login_required | ||
def logout(): | ||
logout_user() | ||
flash('You have been logged out.') | ||
return redirect(url_for('main.index')) | ||
|
||
|
||
@auth.route('/register', methods=['GET', 'POST']) | ||
def register(): | ||
form = RegistrationForm() | ||
if form.validate_on_submit(): | ||
user = User(email=form.email.data, password=form.password.data, | ||
username=form.username.data) | ||
db.session.add(user) | ||
db.session.commit() | ||
token = user.generate_confirmation_token() | ||
send_email(user.email, 'Confirm Your Account', 'auth/email/confirm', user=user, token=token) | ||
flash('A confirmation email has been sent to you by email.') | ||
return redirect(url_for('auth.login')) | ||
return render_template('auth/register.html', form=form) | ||
|
||
|
||
@auth.route('/confirm/<token>') | ||
@login_required | ||
def confirm(token): | ||
if current_user.email_confirm: | ||
return redirect(url_for('main.index')) | ||
if current_user.confirm(token): | ||
flash('You have confirmed your account. Thanks!') | ||
else: | ||
flash('The confirmation link is invalid or has expired.') | ||
return redirect(url_for('main.index')) | ||
|
||
|
||
@auth.before_app_request | ||
def before_request(): | ||
if current_user.is_authenticated: | ||
current_user.ping() | ||
if not current_user.email_confirm and request.endpoint[:5] != 'auth.' \ | ||
and request.endpoint != 'static': | ||
return redirect(url_for('auth.unconfirmed')) | ||
|
||
|
||
@auth.route('/unconfirmed') | ||
def unconfirmed(): | ||
if current_user.is_anonymous or current_user.email_confirm: | ||
return redirect(url_for('main.index')) | ||
return render_template('auth/unconfirmed.html') | ||
|
||
|
||
@auth.route('/confirm') | ||
@login_required | ||
def resend_confirmation(): | ||
token = current_user.generate_confirmation_token() | ||
send_email(current_user.email, 'Confirm Your Account', 'auth/email/confirm', user=current_user, token=token) | ||
flash('A new confirmation email has been sent to you by email') | ||
return redirect(url_for('main.index')) | ||
|
||
|
||
@auth.route('/change-password', methods=['GET', 'POST']) | ||
@login_required | ||
def change_password(): | ||
form = ChangePasswordForm() | ||
if form.validate_on_submit(): | ||
if current_user.verify_password(form.old_password.data): | ||
current_user.password = form.new_password.data | ||
db.session.add(current_user) | ||
flash('Your password has been updated.') | ||
return redirect(url_for('main.index')) | ||
else: | ||
flash('密码错误') | ||
return render_template('auth/change_password.html', form=form) | ||
|
||
|
||
@auth.route('/change-email', methods=['GET', 'POST']) | ||
@login_required | ||
def change_email_request(): | ||
form = ChangeEmailForm() | ||
if form.validate_on_submit(): | ||
if current_user.verify_password(form.password.data): | ||
new_email = form.new_email.data | ||
token = current_user.generate_email_change_token(new_email) | ||
send_email(new_email, 'Confirm your email address', 'auth/email/change_email', user=current_user, | ||
token=token) | ||
flash('An email with instructions to confirm your new email ' | ||
'address has been sent to you.') | ||
return redirect(url_for('main.index')) | ||
else: | ||
flash('Invalid email or password.') | ||
return render_template('auth/change_email.html', form=form) | ||
|
||
|
||
@auth.route('/change-email/<token>') | ||
@login_required | ||
def change_email(token): | ||
if current_user.change_email(token): | ||
flash('Your email address has been updated.') | ||
else: | ||
flash('Invalid request.') | ||
return redirect(url_for('main.index')) | ||
|
||
|
||
@auth.route('/reset', methods=['GET', 'POST']) | ||
@login_required | ||
def password_reset_request(): | ||
if not current_user.is_anonymous: | ||
return redirect(url_for('main.index')) | ||
form = PasswordResetRequestForm() | ||
if form.validate_on_submit(): | ||
user = User.query.filter_by(email=form.email.data).first() | ||
if user: | ||
token = user.generate_reset_token() | ||
send_email(user.email, 'Reset Your Password', | ||
'auth/email/reset_password', | ||
user=user, token=token, | ||
next=request.args.get('next')) | ||
flash('An email with instructions to reset your password has been ' | ||
'sent to you.') | ||
return redirect(url_for('auth.login')) | ||
return render_template('auth/reset_password.html', form=form) | ||
|
||
|
||
@auth.route('/reset/<token>', methods=['GET', 'POST']) | ||
def password_reset(token): | ||
if not current_user.is_anonymous: | ||
return redirect(url_for('main.index')) | ||
form = ResetPasswordForm() | ||
if form.validate_on_submit(): | ||
user = User.query.filter_by(email=form.email.data).first() | ||
if user is None: | ||
return redirect(url_for('main.index')) | ||
if user.reset_password(token, form.new_password.data): | ||
flash('Your password has been updated.') | ||
return redirect(url_for('auth.login')) | ||
else: | ||
return redirect(url_for('main.index')) | ||
return render_template('auth/reset_password.html', form=form) | ||
|
||
|
||
@auth.route('/change-username', methods=['GET', 'POST']) | ||
@login_required | ||
def change_username(): | ||
form = ChangeUsernameForm() | ||
if form.validate_on_submit(): | ||
current_user.username = form.username.data | ||
db.session.add(current_user) | ||
flash('Your password has been updated.') | ||
return redirect(url_for('main.index')) | ||
return render_template('auth/change_username.html', form=form) |
Oops, something went wrong.