aboutsummaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGertjan van den Burg <gertjanvandenburg@gmail.com>2019-03-18 16:57:31 +0000
committerGertjan van den Burg <gertjanvandenburg@gmail.com>2019-03-18 16:57:31 +0000
commita3184c5d142848b5147811ed246e39545e978805 (patch)
treec3b1d0e87dcbc2c8aad3ed531c81f9bc4117a992 /app
parentuse bootstrap (diff)
downloadAnnotateChange-a3184c5d142848b5147811ed246e39545e978805.tar.gz
AnnotateChange-a3184c5d142848b5147811ed246e39545e978805.zip
refactor
Diffstat (limited to 'app')
-rw-r--r--app/__init__.py115
-rw-r--r--app/auth/__init__.py7
-rw-r--r--app/auth/email.py22
-rw-r--r--app/auth/forms.py (renamed from app/forms.py)0
-rw-r--r--app/auth/routes.py (renamed from app/routes.py)53
-rw-r--r--app/email.py22
-rw-r--r--app/errors.py13
-rw-r--r--app/errors/__init__.py7
-rw-r--r--app/errors/handlers.py16
-rw-r--r--app/main/__init__.py9
-rw-r--r--app/main/routes.py12
-rw-r--r--app/models.py6
-rw-r--r--app/templates/auth/login.html16
-rw-r--r--app/templates/auth/register.html (renamed from app/templates/register.html)0
-rw-r--r--app/templates/auth/reset_password.html (renamed from app/templates/reset_password.html)0
-rw-r--r--app/templates/auth/reset_password_request.html (renamed from app/templates/reset_password_request.html)0
-rw-r--r--app/templates/base.html8
-rw-r--r--app/templates/email/reset_password.html2
-rw-r--r--app/templates/email/reset_password.txt2
-rw-r--r--app/templates/errors/404.html (renamed from app/templates/404.html)2
-rw-r--r--app/templates/errors/500.html (renamed from app/templates/500.html)2
-rw-r--r--app/templates/login.html29
22 files changed, 198 insertions, 145 deletions
diff --git a/app/__init__.py b/app/__init__.py
index 3ee019e..9908dd8 100644
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -9,55 +9,82 @@ from logging.handlers import SMTPHandler, RotatingFileHandler
from flask import Flask
from flask_bootstrap import Bootstrap
-from flask_sqlalchemy import SQLAlchemy
-from flask_migrate import Migrate
from flask_login import LoginManager
from flask_mail import Mail
+from flask_migrate import Migrate
+from flask_sqlalchemy import SQLAlchemy
from .config import Config
-app = Flask(__name__)
-app.config.from_object(Config)
-db = SQLAlchemy(app)
-migrate = Migrate(app, db)
-login = LoginManager(app)
-login.login_view = "login"
-mail = Mail(app)
-bootstrap = Bootstrap(app)
-
-from app import routes, models, errors
-
-if not app.debug:
- if app.config["MAIL_SERVER"]:
- auth = None
- if app.config["MAIL_USERNAME"] or app.config["MAIL_PASSWORD"]:
- auth = (app.config["MAIL_USERNAME"], app.config["MAIL_PASSWORD"])
- secure = None
- if app.config["MAIL_USE_TLS"]:
- secure = ()
- mail_handler = SMTPHandler(
- mailhost=(app.config["MAIL_SERVER"], app.config["MAIL_PORT"]),
- fromaddr="no-reply@" + app.config["MAIL_SERVER"],
- toaddrs=app.config["ADMINS"],
- subject="AnnotateChange Failure",
- credentials=auth,
- secure=secure,
+db = SQLAlchemy()
+migrate = Migrate()
+login = LoginManager()
+login.login_view = "auth.login"
+mail = Mail()
+bootstrap = Bootstrap()
+
+
+def create_app(config_class=Config):
+ app = Flask(__name__)
+ app.config.from_object(config_class)
+
+ db.init_app(app)
+ migrate.init_app(app, db)
+ login.init_app(app)
+ mail.init_app(app)
+ bootstrap.init_app(app)
+
+ from app.errors import bp as errors_bp
+
+ app.register_blueprint(errors_bp)
+
+ from app.auth import bp as auth_bp
+
+ app.register_blueprint(auth_bp, url_prefix="/auth")
+
+ from app.main import bp as main_bp
+
+ app.register_blueprint(main_bp)
+
+ if not app.debug:
+ if app.config["MAIL_SERVER"]:
+ auth = None
+ if app.config["MAIL_USERNAME"] or app.config["MAIL_PASSWORD"]:
+ auth = (
+ app.config["MAIL_USERNAME"],
+ app.config["MAIL_PASSWORD"],
+ )
+ secure = None
+ if app.config["MAIL_USE_TLS"]:
+ secure = ()
+ mail_handler = SMTPHandler(
+ mailhost=(app.config["MAIL_SERVER"], app.config["MAIL_PORT"]),
+ fromaddr="no-reply@" + app.config["MAIL_SERVER"],
+ toaddrs=app.config["ADMINS"],
+ subject="AnnotateChange Failure",
+ credentials=auth,
+ secure=secure,
+ )
+ mail_handler.setLevel(logging.ERROR)
+ app.logger.addHandler(mail_handler)
+
+ if not os.path.exists("logs"):
+ os.mkdir("logs")
+ file_handler = RotatingFileHandler(
+ "logs/annotatechange.log", maxBytes=10240, backupCount=10
)
- mail_handler.setLevel(logging.ERROR)
- app.logger.addHandler(mail_handler)
-
- if not os.path.exists("logs"):
- os.mkdir("logs")
- file_handler = RotatingFileHandler(
- "logs/annotatechange.log", maxBytes=10240, backupCount=10
- )
- file_handler.setFormatter(
- logging.Formatter(
- "%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]"
+ file_handler.setFormatter(
+ logging.Formatter(
+ "%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]"
+ )
)
- )
- file_handler.setLevel(logging.INFO)
- app.logger.addHandler(file_handler)
+ file_handler.setLevel(logging.INFO)
+ app.logger.addHandler(file_handler)
+
+ app.logger.setLevel(logging.INFO)
+ app.logger.info("AnnotateChange startup")
+
+ return app
+
- app.logger.setLevel(logging.INFO)
- app.logger.info("AnnotateChange startup")
+from app import models
diff --git a/app/auth/__init__.py b/app/auth/__init__.py
new file mode 100644
index 0000000..c938693
--- /dev/null
+++ b/app/auth/__init__.py
@@ -0,0 +1,7 @@
+# -*- coding: utf-8 -*-
+
+from flask import Blueprint
+
+bp = Blueprint('auth', __name__)
+
+from app.auth import routes
diff --git a/app/auth/email.py b/app/auth/email.py
new file mode 100644
index 0000000..9f6f9d0
--- /dev/null
+++ b/app/auth/email.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+
+from threading import Thread
+
+from flask import current_app, render_template
+
+from app import mail
+
+
+def send_password_reset_email(user):
+ token = user.get_reset_password_token()
+ send_email(
+ "[AnnotateChange] Reset your password",
+ sender=current_app.config["ADMINS"][0],
+ recipients=[user.email],
+ text_body=render_template(
+ "email/reset_password.txt", user=user, token=token
+ ),
+ html_body=render_template(
+ "email/reset_password.html", user=user, token=token
+ ),
+ )
diff --git a/app/forms.py b/app/auth/forms.py
index 8f7662a..8f7662a 100644
--- a/app/forms.py
+++ b/app/auth/forms.py
diff --git a/app/routes.py b/app/auth/routes.py
index ba07a02..88ff7a0 100644
--- a/app/routes.py
+++ b/app/auth/routes.py
@@ -7,56 +7,49 @@ from flask_login import current_user, login_user, logout_user, login_required
from werkzeug.urls import url_parse
-from app import app
from app import db
+from app.auth import bp
-from app.forms import (
+from app.auth.forms import (
LoginForm,
RegistrationForm,
ResetPasswordRequestForm,
ResetPasswordForm,
)
from app.models import User
-from app.email import send_password_reset_email
+from app.auth.email import send_password_reset_email
-@app.route("/")
-@app.route("/index")
-@login_required
-def index():
- return render_template("index.html", title="Home")
-
-
-@app.route("/login", methods=("GET", "POST"))
+@bp.route("/login", methods=("GET", "POST"))
def login():
if current_user.is_authenticated:
current_user.last_active = datetime.datetime.utcnow()
db.session.commit()
- return redirect(url_for("index"))
+ return redirect(url_for("main.index"))
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.username.data).first()
if user is None or not user.check_password(form.password.data):
flash("Invalid username or password", category="error")
- return redirect(url_for("login"))
+ return redirect(url_for("auth.login"))
login_user(user, remember=form.remember_me.data)
next_page = request.args.get("next")
if not next_page or url_parse(next_page).netloc != "":
- next_page = url_for("index")
+ next_page = url_for("main.index")
return redirect(next_page)
- return render_template("login.html", title="Sign In", form=form)
+ return render_template("auth/login.html", title="Sign In", form=form)
-@app.route("/logout")
+@bp.route("/logout")
def logout():
logout_user()
- return redirect(url_for("index"))
+ return redirect(url_for("main.index"))
-@app.route("/register", methods=("GET", "POST"))
+@bp.route("/register", methods=("GET", "POST"))
def register():
if current_user.is_authenticated:
- return redirect(url_for("index"))
+ return redirect(url_for("main.index"))
form = RegistrationForm()
if form.validate_on_submit():
user = User(username=form.username.data, email=form.email.data)
@@ -64,37 +57,37 @@ def register():
db.session.add(user)
db.session.commit()
flash("Thank you, you are now a registered user!")
- return redirect(url_for("login"))
- return render_template("register.html", title="Register", form=form)
+ return redirect(url_for("auth.login"))
+ return render_template("auth/register.html", title="Register", form=form)
-@app.route("/reset_password_request", methods=("GET", "POST"))
+@bp.route("/reset_password_request", methods=("GET", "POST"))
def reset_password_request():
if current_user.is_authenticated:
- return redirect(url_for("index"))
+ return redirect(url_for("main.index"))
form = ResetPasswordRequestForm()
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user:
send_password_reset_email(user)
flash("Check your email for the instructions to reset your password.")
- return redirect(url_for("login"))
+ return redirect(url_for("auth.login"))
return render_template(
- "reset_password_request.html", title="Reset Password", form=form
+ "auth/reset_password_request.html", title="Reset Password", form=form
)
-@app.route("/reset_password/<token>", methods=("GET", "POST"))
+@bp.route("/reset_password/<token>", methods=("GET", "POST"))
def reset_password(token):
if current_user.is_authenticated:
- return redirect(url_for("index"))
+ return redirect(url_for("main.index"))
user = User.verify_reset_password_token(token)
if not user:
- return redirect(url_for("index"))
+ return redirect(url_for("main.index"))
form = ResetPasswordForm()
if form.validate_on_submit():
user.set_password(form.password.data)
db.session.commit()
flash("Your password has been reset.")
- return redirect(url_for("login"))
- return render_template("reset_password.html", form=form)
+ return redirect(url_for("auth.login"))
+ return render_template("auth/reset_password.html", form=form)
diff --git a/app/email.py b/app/email.py
index 8c71a50..7b97364 100644
--- a/app/email.py
+++ b/app/email.py
@@ -2,10 +2,9 @@
from threading import Thread
-from flask import render_template
+from flask import current_app
from flask_mail import Message
-from app import app
from app import mail
@@ -18,19 +17,6 @@ def send_email(subject, sender, recipients, text_body, html_body):
msg = Message(subject, sender=sender, recipients=recipients)
msg.body = text_body
msg.html = html_body
- Thread(target=send_async_email, args=(app, msg)).start()
-
-
-def send_password_reset_email(user):
- token = user.get_reset_password_token()
- send_email(
- "[AnnotateChange] Reset your password",
- sender=app.config["ADMINS"][0],
- recipients=[user.email],
- text_body=render_template(
- "email/reset_password.txt", user=user, token=token
- ),
- html_body=render_template(
- "email/reset_password.html", user=user, token=token
- ),
- )
+ Thread(
+ target=send_async_email, args=(current_app._get_current_object(), msg)
+ ).start()
diff --git a/app/errors.py b/app/errors.py
deleted file mode 100644
index f459038..0000000
--- a/app/errors.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from flask import render_template
-from app import app, db
-
-@app.errorhandler(404)
-def not_found_error(error):
- return render_template('404.html'), 404
-
-@app.errorhandler(500)
-def internal_error(error):
- db.session.rollback()
- return render_template('500.html'), 500
diff --git a/app/errors/__init__.py b/app/errors/__init__.py
new file mode 100644
index 0000000..8a85dca
--- /dev/null
+++ b/app/errors/__init__.py
@@ -0,0 +1,7 @@
+# -*- coding: utf-8 -*-
+
+from flask import Blueprint
+
+bp = Blueprint("errors", __name__)
+
+from app.errors import handlers
diff --git a/app/errors/handlers.py b/app/errors/handlers.py
new file mode 100644
index 0000000..6c2b1e7
--- /dev/null
+++ b/app/errors/handlers.py
@@ -0,0 +1,16 @@
+# -*- coding: utf-8 -*-
+
+from flask import render_template
+from app import db
+from app.errors import bp
+
+
+@bp.app_errorhandler(404)
+def not_found_error(error):
+ return render_template("errors/404.html"), 404
+
+
+@bp.app_errorhandler(500)
+def internal_error(error):
+ db.session.rollback()
+ return render_template("errors/500.html"), 500
diff --git a/app/main/__init__.py b/app/main/__init__.py
new file mode 100644
index 0000000..2cb605e
--- /dev/null
+++ b/app/main/__init__.py
@@ -0,0 +1,9 @@
+# -*- coding: utf-8 -*-
+
+from flask import Blueprint
+
+bp = Blueprint('main', __name__)
+
+from app.main import routes
+
+
diff --git a/app/main/routes.py b/app/main/routes.py
new file mode 100644
index 0000000..5d14fc6
--- /dev/null
+++ b/app/main/routes.py
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+
+from flask import render_template
+from flask_login import login_required
+
+from app.main import bp
+
+@bp.route("/")
+@bp.route("/index")
+@login_required
+def index():
+ return render_template("index.html", title="Home")
diff --git a/app/models.py b/app/models.py
index cb90fda..ed9138f 100644
--- a/app/models.py
+++ b/app/models.py
@@ -4,11 +4,11 @@ import datetime
import jwt
import time
+from flask import current_app
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
-from app import app
from app import db
from app import login
@@ -34,7 +34,7 @@ class User(UserMixin, db.Model):
def get_reset_password_token(self, expires_in=600):
return jwt.encode(
{"reset_password": self.id, "exp": time.time() + expires_in},
- app.config["SECRET_KEY"],
+ current_app.config["SECRET_KEY"],
algorithm="HS256",
).decode("utf-8")
@@ -42,7 +42,7 @@ class User(UserMixin, db.Model):
def verify_reset_password_token(token):
try:
_id = jwt.decode(
- token, app.config["SECRET_KEY"], algorithms=["HS256"]
+ token, current_app.config["SECRET_KEY"], algorithms=["HS256"]
)["reset_password"]
except:
return None
diff --git a/app/templates/auth/login.html b/app/templates/auth/login.html
new file mode 100644
index 0000000..27268b2
--- /dev/null
+++ b/app/templates/auth/login.html
@@ -0,0 +1,16 @@
+{% extends "base.html" %}
+{% import 'bootstrap/wtf.html' as wtf %}
+
+{% block app_content %}
+<h1>Sign In</h1>
+<div class="row">
+ <div class="col-md-4">
+ {{ wtf.quick_form(form) }}
+ </div>
+</div>
+<p>New User? <a href="{{ url_for('auth.register') }}">Click to Register!</a></p>
+<p>
+Forgot your password?
+<a href="{{ url_for('auth.reset_password_request') }}">Click here to reset it</a>
+</p>
+{% endblock %}
diff --git a/app/templates/register.html b/app/templates/auth/register.html
index c430b38..c430b38 100644
--- a/app/templates/register.html
+++ b/app/templates/auth/register.html
diff --git a/app/templates/reset_password.html b/app/templates/auth/reset_password.html
index cab00c9..cab00c9 100644
--- a/app/templates/reset_password.html
+++ b/app/templates/auth/reset_password.html
diff --git a/app/templates/reset_password_request.html b/app/templates/auth/reset_password_request.html
index c516141..c516141 100644
--- a/app/templates/reset_password_request.html
+++ b/app/templates/auth/reset_password_request.html
diff --git a/app/templates/base.html b/app/templates/base.html
index ee03e3d..8e51695 100644
--- a/app/templates/base.html
+++ b/app/templates/base.html
@@ -14,17 +14,17 @@
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
- <a class="navbar-brand" href="{{ url_for('index') }}">AnnotateChange</a>
+ <a class="navbar-brand" href="{{ url_for('main.index') }}">AnnotateChange</a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
- <li><a href="{{ url_for('index') }}">Home</a></li>
+ <li><a href="{{ url_for('main.index') }}">Home</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
{% if current_user.is_anonymous %}
- <li><a href="{{ url_for('login') }}">Login</a></li>
+ <li><a href="{{ url_for('auth.login') }}">Login</a></li>
{% else %}
- <li><a href="{{ url_for('logout') }}">Logout</a></li>
+ <li><a href="{{ url_for('auth.logout') }}">Logout</a></li>
{% endif %}
</ul>
</div>
diff --git a/app/templates/email/reset_password.html b/app/templates/email/reset_password.html
index f7403a5..5ce604d 100644
--- a/app/templates/email/reset_password.html
+++ b/app/templates/email/reset_password.html
@@ -1,7 +1,7 @@
<p>Dear {{ user.username }},</p>
<p>
To reset your password
- <a href="{{ url_for('reset_password', token=token, _external=True) }}">
+ <a href="{{ url_for('auth.reset_password', token=token, _external=True) }}">
click here
</a>.
</p>
diff --git a/app/templates/email/reset_password.txt b/app/templates/email/reset_password.txt
index da1330f..0f7b0e7 100644
--- a/app/templates/email/reset_password.txt
+++ b/app/templates/email/reset_password.txt
@@ -2,7 +2,7 @@ Dear {{ user.username }},
To reset your password click on the following link:
-{{ url_for('reset_password', token=token, _external=True) }}
+{{ url_for('auth.reset_password', token=token, _external=True) }}
If you have not requested a password reset then you can simply ignore this email.
diff --git a/app/templates/404.html b/app/templates/errors/404.html
index 51295c4..80f5bd6 100644
--- a/app/templates/404.html
+++ b/app/templates/errors/404.html
@@ -2,5 +2,5 @@
{% block app_content %}
<h1>Page Not Found</h1>
- <p><a href="{{ url_for('index') }}">Home</p>
+ <p><a href="{{ url_for('main.index') }}">Home</p>
{% endblock %}
diff --git a/app/templates/500.html b/app/templates/errors/500.html
index adda72b..5297e6a 100644
--- a/app/templates/500.html
+++ b/app/templates/errors/500.html
@@ -3,5 +3,5 @@
{% block app_content %}
<h1>An unexpected error has occurred</h1>
<p>The administrator has been notified, apologies for the inconvenience.</p>
- <p><a href="{{ url_for('index') }}">Home</p>
+ <p><a href="{{ url_for('main.index') }}">Home</p>
{% endblock %}
diff --git a/app/templates/login.html b/app/templates/login.html
deleted file mode 100644
index 9b862f6..0000000
--- a/app/templates/login.html
+++ /dev/null
@@ -1,29 +0,0 @@
-{% extends "base.html" %}
-
-{% block app_content %}
- <h1>Sign In</h1>
- <form action="" method="post" novalidate>
- {{ form.hidden_tag() }}
- <p>
- {{ form.username.label }}<br>
- {{ form.username(size=32) }}
- {% for error in form.username.errors %}
- <span style="color: red;">[{{ error }}]</span>
- {% endfor %}
- </p>
- <p>
- {{ form.password.label }}<br>
- {{ form.password(size=32) }}
- {% for error in form.username.errors %}
- <span style="color: red;">[{{ error }}]</span>
- {% endfor %}
- </p>
- <p>{{ form.remember_me() }} {{ form.remember_me.label }}</p>
- <p>{{ form.submit() }}</p>
- </form>
- <p>New User? <a href="{{ url_for('register') }}">Click to Register!</a></p>
- <p>
- Forgot your password?
- <a href="{{ url_for('reset_password_request') }}">Click here to reset it</a>
- </p>
-{% endblock %}