mirror of
https://github.com/Xevion/runnerspace.git
synced 2025-12-07 09:16:21 -06:00
Migrate app to use WTForms for auth form validation
- Not finished yet, major styling breakage in this commit - Also encapsulated GET & POST requests of /login and /signup routes into one route.
This commit is contained in:
2
app.py
2
app.py
@@ -44,7 +44,7 @@ def create_app():
|
||||
from routes import blueprint as routes_blueprint
|
||||
app.register_blueprint(routes_blueprint)
|
||||
|
||||
from forms import blueprint as forms_blueprint
|
||||
from route_forms import blueprint as forms_blueprint
|
||||
app.register_blueprint(forms_blueprint)
|
||||
|
||||
@app.errorhandler(404)
|
||||
|
||||
33
auth.py
33
auth.py
@@ -1,4 +1,4 @@
|
||||
from flask import Blueprint, flash, redirect, request, url_for
|
||||
from flask import Blueprint, flash, redirect, request, url_for, render_template
|
||||
from flask_login import login_required, login_user, logout_user, current_user
|
||||
from werkzeug.security import check_password_hash, generate_password_hash
|
||||
|
||||
@@ -14,7 +14,7 @@ def edit_profile_post(username: str):
|
||||
form = EditProfileForm(request.form)
|
||||
user = User.query.filter_by(username=username).first_or_404()
|
||||
|
||||
if current_user.is_admin or user.id == current_user.id
|
||||
if current_user.is_admin or user.id == current_user.id:
|
||||
if form.validate():
|
||||
user.about_me = form.about_me.data
|
||||
db.session.commit()
|
||||
@@ -22,38 +22,45 @@ def edit_profile_post(username: str):
|
||||
return redirect(url_for('main.view_user', username=user.username))
|
||||
|
||||
|
||||
@blueprint.route('/login', methods=['POST'])
|
||||
def login_post():
|
||||
@blueprint.route('/login', methods=['GET', 'POST'])
|
||||
def login():
|
||||
form = LoginForm(request.form)
|
||||
user = User.query.filter_by(username=form.data.username).first()
|
||||
|
||||
if request.method == 'POST' and form.validate():
|
||||
user = User.query.filter_by(username=form.username.data).first()
|
||||
|
||||
# check if the user actually exists, and compare password given
|
||||
if not user or not check_password_hash(user.password, form.password.data):
|
||||
flash('Please check your login details and try again.')
|
||||
return redirect(url_for('main.login'))
|
||||
return redirect(url_for('auth.login'))
|
||||
|
||||
login_user(user, remember=form.remember.data)
|
||||
return redirect(url_for('main.index'))
|
||||
return redirect(url_for('auth.index'))
|
||||
|
||||
return render_template('pages/auth/login.html', form=form)
|
||||
|
||||
|
||||
@blueprint.route('/signup', methods=['POST'])
|
||||
def signup_post():
|
||||
@blueprint.route('/signup', methods=['GET', 'POST'])
|
||||
def signup():
|
||||
# validate and add user to db
|
||||
form = RegistrationForm(request.form)
|
||||
|
||||
if request.method == 'POST' and form.validate():
|
||||
user = User.query.filter_by(username=form.username.data).first() # Check if the username is already in use
|
||||
if user: # redirect back to sign-up page
|
||||
flash('This username is already in use.')
|
||||
return redirect(url_for('main.signup'))
|
||||
return redirect(url_for('auth.signup'))
|
||||
|
||||
# Create new user with form data
|
||||
new_user = User(username=form.username.data, name=form.name.data, password=generate_password_hash(form.password.data, method='sha256'))
|
||||
|
||||
new_user = User(username=form.username.data, name=form.name.data,
|
||||
password=generate_password_hash(form.password.data, method='sha256'))
|
||||
# Add new user to db
|
||||
db.session.add(new_user)
|
||||
db.session.commit()
|
||||
|
||||
return redirect(url_for('main.login'))
|
||||
return redirect(url_for('auth.login'))
|
||||
|
||||
return render_template('pages/auth/signup.html', form=form)
|
||||
|
||||
|
||||
@blueprint.route('/logout')
|
||||
|
||||
74
forms.py
74
forms.py
@@ -1,63 +1,31 @@
|
||||
from flask import Blueprint, flash, redirect, request, url_for
|
||||
from flask_login import current_user, login_required
|
||||
from profanity_filter import ProfanityFilter
|
||||
|
||||
from database import db
|
||||
from models import User, Post, Comment
|
||||
|
||||
blueprint = Blueprint('forms', __name__)
|
||||
pf = ProfanityFilter()
|
||||
from wtforms import Form, BooleanField, StringField, PasswordField, validators
|
||||
|
||||
|
||||
@blueprint.route('/user/<username>/edit', methods=['POST'])
|
||||
@login_required
|
||||
def edit_profile_post(username):
|
||||
user = db.session.query(User).filter_by(username=username).first_or_404()
|
||||
|
||||
# Allow admins to edit profiles, but deny other users
|
||||
if not current_user.is_admin and current_user.id != user.id:
|
||||
return redirect(url_for('main.user', username=username))
|
||||
|
||||
user.about_me = request.form.get('about-me', user.about_me)
|
||||
user.name = request.form.get('name', user.name)
|
||||
db.session.commit()
|
||||
|
||||
flash('Successfully updated profile.')
|
||||
return redirect(url_for('main.edit_user', username=username))
|
||||
class RegistrationForm(Form):
|
||||
username = StringField('Username', [validators.Length(min=4, max=25)])
|
||||
name = StringField('Name', [validators.Length(min=2, max=35)])
|
||||
password = PasswordField('New Password', [
|
||||
validators.DataRequired(),
|
||||
validators.EqualTo('confirm', message='Passwords must match')
|
||||
])
|
||||
confirm = PasswordField('Repeat Password')
|
||||
accept_tos = BooleanField('I accept the TOS', [validators.DataRequired()])
|
||||
|
||||
|
||||
@blueprint.route('/feed/new', methods=['POST'])
|
||||
@login_required
|
||||
def new_post():
|
||||
post_text = request.form.get('text')
|
||||
|
||||
post = Post(author=current_user.id, text=post_text)
|
||||
db.session.add(post)
|
||||
db.session.commit()
|
||||
|
||||
return redirect(url_for('main.view_post', post_id=post.id))
|
||||
class LoginForm(Form):
|
||||
username = StringField('Username', [validators.DataRequired()])
|
||||
password = StringField('Password', [validators.DataRequired()])
|
||||
remember_me = BooleanField('Remember Me', [validators.Optional()])
|
||||
|
||||
|
||||
@blueprint.route('/feed/<post_id>/comment', methods=['POST'])
|
||||
@login_required
|
||||
def add_comment(post_id: int):
|
||||
post = Post.query.get_or_404(post_id)
|
||||
class EditProfileForm(Form):
|
||||
name = RegistrationForm.name
|
||||
about_me = StringField('About Me', [validators.Optional()])
|
||||
|
||||
comment_text: str = request.form.get('comment-text')
|
||||
|
||||
if len(comment_text) > 50:
|
||||
flash('Cannot have more than 50 characters of text.')
|
||||
return redirect(url_for('main.view_post', post_id=post_id))
|
||||
elif len(comment_text) < 5:
|
||||
flash('Your comment must have at least 5 characters of text.')
|
||||
return redirect(url_for('main.view_post', post_id=post_id))
|
||||
class NewPostForm(Form):
|
||||
text = StringField('Text', [validators.Length(min=15, max=1000)])
|
||||
|
||||
if not pf.is_clean(comment_text):
|
||||
flash('Sorry, profanity is not allowed on runnerspace.')
|
||||
return redirect(url_for('main.view_post', post_id=post_id))
|
||||
|
||||
comment = Comment(post=post.id, author=current_user.id, text=comment_text)
|
||||
db.session.add(comment)
|
||||
db.session.commit()
|
||||
|
||||
return redirect(url_for('main.view_post', post_id=post.id))
|
||||
class NewCommentForm(Form):
|
||||
text = StringField('Text', [validators.Length(min=5, max=50)])
|
||||
|
||||
61
route_forms.py
Normal file
61
route_forms.py
Normal file
@@ -0,0 +1,61 @@
|
||||
from flask import Blueprint, flash, redirect, request, url_for
|
||||
from flask_login import current_user, login_required
|
||||
from profanity_filter import ProfanityFilter
|
||||
from forms import RegistrationForm, EditProfileForm, NewPostForm, NewCommentForm
|
||||
from database import db
|
||||
from models import User, Post, Comment
|
||||
|
||||
blueprint = Blueprint('forms', __name__)
|
||||
pf = ProfanityFilter()
|
||||
|
||||
|
||||
@blueprint.route('/user/<username>/edit', methods=['POST'])
|
||||
@login_required
|
||||
def edit_profile_post(username):
|
||||
user = db.session.query(User).filter_by(username=username).first_or_404()
|
||||
|
||||
# Allow admins to edit profiles, but deny other users
|
||||
if not current_user.is_admin and current_user.id != user.id:
|
||||
return redirect(url_for('main.view_user', username=username))
|
||||
|
||||
form = RegistrationForm(request.form)
|
||||
if form.validate():
|
||||
user.about_me = form.about_me.data
|
||||
user.name = form.name.data
|
||||
db.session.commit()
|
||||
|
||||
flash('Successfully updated profile.')
|
||||
return redirect(url_for('main.edit_user', username=username))
|
||||
|
||||
|
||||
@blueprint.route('/feed/new', methods=['POST'])
|
||||
@login_required
|
||||
def new_post():
|
||||
form = NewPostForm(request.form)
|
||||
|
||||
if form.validate():
|
||||
post = Post(author=current_user.id, text=form.text.data)
|
||||
db.session.add(post)
|
||||
db.session.commit()
|
||||
|
||||
return redirect(url_for('main.view_post', post_id=post.id))
|
||||
else:
|
||||
redirect(url_for('main.feed'))
|
||||
|
||||
|
||||
@blueprint.route('/feed/<post_id>/comment', methods=['POST'])
|
||||
@login_required
|
||||
def add_comment(post_id: int):
|
||||
post = Post.query.get_or_404(post_id)
|
||||
form = NewCommentForm(request.form)
|
||||
|
||||
if form.validate():
|
||||
if not pf.is_clean(form.text.data):
|
||||
flash('Sorry, profanity is not allowed on runnerspace.')
|
||||
return redirect(url_for('main.view_post', post_id=post_id))
|
||||
|
||||
comment = Comment(post=post.id, author=current_user.id, text=form.text.data)
|
||||
db.session.add(comment)
|
||||
db.session.commit()
|
||||
|
||||
return redirect(url_for('main.view_post', post_id=post.id))
|
||||
10
routes.py
10
routes.py
@@ -68,7 +68,6 @@ def edit_user(username: str):
|
||||
return render_template('pages/user_edit.html', user=user)
|
||||
return redirect(url_for('main.view_user', username=username))
|
||||
|
||||
|
||||
# @blueprint.route('/blogs')
|
||||
# def blogs():
|
||||
# return render_template('pages/blogs.html')
|
||||
@@ -77,12 +76,3 @@ def edit_user(username: str):
|
||||
# @blueprint.route('/groups')
|
||||
# def groups():
|
||||
# return render_template('pages/groups.html')
|
||||
|
||||
@blueprint.route('/login', methods=['GET'])
|
||||
def login():
|
||||
return render_template('pages/auth/login.html')
|
||||
|
||||
|
||||
@blueprint.route('/signup', methods=['GET'])
|
||||
def signup():
|
||||
return render_template('pages/auth/signup.html')
|
||||
|
||||
@@ -131,10 +131,10 @@ form.login-form {
|
||||
border: 1px solid darkblue;
|
||||
padding: 0.7em;
|
||||
}
|
||||
form .field {
|
||||
form field {
|
||||
padding: 4px;
|
||||
}
|
||||
form .field .checkbox {
|
||||
form field .checkbox {
|
||||
margin-left: auto;
|
||||
}
|
||||
form button {
|
||||
|
||||
@@ -154,7 +154,7 @@ form {
|
||||
padding: 0.7em;
|
||||
}
|
||||
|
||||
.field {
|
||||
field {
|
||||
padding: 4px;
|
||||
|
||||
.checkbox {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<a href="{{ url_for('main.edit_user', username=current_user.username) }}"><i class="fas fa-cog fa-1x"></i></a>
|
||||
| <a href="{{ url_for('auth.logout') }}">Logout</a>
|
||||
{% else %}
|
||||
<a href="{{ url_for('main.login') }}">Login</a> or <a href="{{ url_for('main.signup') }}">Sign-up</a>!
|
||||
<a href="{{ url_for('auth.login') }}">Login</a> or <a href="{{ url_for('auth.signup') }}">Sign-up</a>!
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{% macro render_field(field) %}
|
||||
<dt>{{ field.label }}
|
||||
<dd>{{ field(**kwargs)|safe }}
|
||||
<field>
|
||||
<label>{{ field.label }}</label>
|
||||
{{ field(**kwargs)|safe }}
|
||||
{% if field.errors %}
|
||||
<ul class=errors>
|
||||
{% for error in field.errors %}
|
||||
@@ -8,5 +9,6 @@
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</dd>
|
||||
<br>
|
||||
</field>
|
||||
{% endmacro %}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{% extends 'layouts/index.html' %}
|
||||
{% from 'macros.html' import render_field %}
|
||||
|
||||
{% block content %}
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% if messages %}
|
||||
@@ -7,29 +9,14 @@
|
||||
</span>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
<form method="POST" action="{{ url_for('auth.login_post') }}" class="login-form">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
Username
|
||||
<input class="input is-large" type="text" name="username" placeholder="Username" autofocus="true">
|
||||
</div>
|
||||
</div>
|
||||
<form method="POST" action="{{ url_for('auth.login') }}" class="login-form">
|
||||
{{ render_field(form.username) }}
|
||||
{{ render_field(form.password) }}
|
||||
{{ render_field(form.remember_me) }}
|
||||
<p><input type=submit value=Login>
|
||||
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
Password
|
||||
<input class="input is-large" type="password" name="password" placeholder="Password">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox">
|
||||
Remember me
|
||||
</label>
|
||||
</div>
|
||||
<button class="button">Login</button>
|
||||
</form>
|
||||
<p class="form-subtext">
|
||||
Don't have a login? <a href="{{ url_for('main.signup') }}">Sign-up</a> instead!
|
||||
Don't have a login? <a href="{{ url_for('auth.signup') }}">Sign-up</a> instead!
|
||||
</p>
|
||||
{% endblock content %}
|
||||
|
||||
@@ -1,45 +1,19 @@
|
||||
{% extends 'layouts/index.html' %}
|
||||
{% from "macros.html" import render_field %}
|
||||
|
||||
{% block content %}
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% if messages %}
|
||||
<span class="error-message">
|
||||
{{ messages[0] }}. Go to <a href="{{ url_for('main.login') }}">login page</a>.
|
||||
</span>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<form method="POST" action="{{ url_for('auth.signup_post') }}" class="login-form">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
Username
|
||||
<input class="input" type="text" name="username" placeholder="Username" autofocus>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
Name
|
||||
<input class="input" type="text" name="name" placeholder="Name" autofocus>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
Password
|
||||
<input class="input" type="password" name="password" placeholder="Password">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
Confirm Password
|
||||
<input class="input" type="password" name="confirm" placeholder="Confirm Password">
|
||||
</div>
|
||||
</div>
|
||||
<button>Sign Up</button>
|
||||
<form method=post class="login-form">
|
||||
<dl>
|
||||
{{ render_field(form.username) }}
|
||||
{{ render_field(form.name) }}
|
||||
{{ render_field(form.password) }}
|
||||
{{ render_field(form.confirm) }}
|
||||
{{ render_field(form.accept_tos) }}
|
||||
</dl>
|
||||
<p><input type=submit value=Register>
|
||||
</form>
|
||||
|
||||
<p class="form-subtext">
|
||||
Already have a login? <a href="{{ url_for('main.login') }}">Login</a> instead!
|
||||
Already have a login? <a href="{{ url_for('auth.login') }}">Login</a> instead!
|
||||
</p>
|
||||
{% endblock content %}
|
||||
|
||||
Reference in New Issue
Block a user