From c57ebef076438c83a0e408227341398249566ff1 Mon Sep 17 00:00:00 2001 From: stilbruch Date: Wed, 23 Mar 2022 12:30:23 -0500 Subject: User registration and login now works --- TODO | 1 + app/app.py | 10 ++++++- app/database.py | 19 -------------- app/forms.py | 13 ++++++++++ app/login.py | 55 --------------------------------------- app/routes.py | 56 +++++++++++++++++++++++++++++++++------- app/tables/user.py | 25 ++++++++++++++++++ app/templates/user/login.html | 16 +++++++----- app/templates/user/register.html | 21 ++++++++++++--- 9 files changed, 121 insertions(+), 95 deletions(-) create mode 100644 TODO delete mode 100644 app/database.py create mode 100644 app/forms.py delete mode 100644 app/login.py create mode 100644 app/tables/user.py diff --git a/TODO b/TODO new file mode 100644 index 0000000..f81209d --- /dev/null +++ b/TODO @@ -0,0 +1 @@ +Add fa icons to base diff --git a/app/app.py b/app/app.py index 8f6fafe..3051934 100644 --- a/app/app.py +++ b/app/app.py @@ -1,16 +1,24 @@ from flask import Flask from flask_sqlalchemy import SQLAlchemy +from flask_login import LoginManager # Setup app before doing imports app = Flask(__name__) app.config['TEMPLATES_AUTO_RELOAD'] = True app.config['SECRET_KEY'] = "super duper secret" # FIXME: do not use in prod -app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://../strenghty.db' +app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///../strengthy.db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False +# Setup flask-login +login_manager = LoginManager(app) +login_manager.init_app(app) + # Setup SQLAlchemy # https://flask-sqlalchemy.palletsprojects.com/en/2.x/quickstart/#a-minimal-application db = SQLAlchemy(app) +# TODO: do in script +import tables.user +db.create_all() # Load routes import routes diff --git a/app/database.py b/app/database.py deleted file mode 100644 index e23aa5d..0000000 --- a/app/database.py +++ /dev/null @@ -1,19 +0,0 @@ -import sqlite3 -from app import app -from flask import g - -# constants -DATABASE_FILE = "../strengthy.db" - -# Called when an "appcontext" is closed, usually a request is finished -@app.teardown_appcontext -def close_db_conn(exception): - db = getattr(g, '_database', None) - if db is not None: - db.close() - -def database_get(): - db = getattr(g, '_database', None) - if db is None: - db = g._database = sqlite3.connect(DATABASE_FILE) - return db diff --git a/app/forms.py b/app/forms.py new file mode 100644 index 0000000..12e4bf8 --- /dev/null +++ b/app/forms.py @@ -0,0 +1,13 @@ +from flask_wtf import FlaskForm +from wtforms import StringField, PasswordField, BooleanField +from wtforms.validators import DataRequired, Email + +class LoginForm(FlaskForm): + username = StringField("username", validators=[DataRequired()]) + password = PasswordField("password", validators=[DataRequired()]) + #remember_me = BooleanField() + +class RegisterForm(FlaskForm): + username = StringField("username", validators=[DataRequired()]) + password = PasswordField("password", validators=[DataRequired()]) + email = StringField("email", validators=[DataRequired(), Email()]) diff --git a/app/login.py b/app/login.py deleted file mode 100644 index 6d4b4a2..0000000 --- a/app/login.py +++ /dev/null @@ -1,55 +0,0 @@ -from app import app -from database import database_get -from flask_login import LoginManager, UserMixin, current_user -from flask_wtf import FlaskForm -from wtforms import StringField, PasswordField, BooleanField -from wtforms.validators import DataRequired, Email - -# https://python.plainenglish.io/implementing-flask-login-with-hash-password-888731c88a99 - -# Forms classes for flask_wtf -class LoginForm(FlaskForm): - username = StringField("username", validators=[DataRequired()]) - password = PasswordField("password", validators=[DataRequired()]) - remember_me = BooleanField() - -class RegisterForm(FlaskForm): - username = StringField("username", validators=[DataRequired()]) - password = PasswordField("password", validators=[DataRequired()]) - name = StringField("name") - email = StringField("email", validators=[DataRequired(), Email()]) - -# User class for flask_login -class User(UserMixin): - def __init__(self, id, email, password_hash): - self.id = unicode(id) - self.email = email - self.password_hash = password_hash - self.authenticated = False - - def is_authenticated(self): - return self.authenticated - - def is_active(self): - return True - - def is_anonymous(self): - return False - - def get_id(self): - return self.id - -login_manager = LoginManager() -login_manager.init_app(app) - -@login_manager.user_loader -def load_user(uid): - conn = database_get() - curs = conn.cursor() - curs.execute("SELECT * FROM users WHERE id = (?)", [uid]) - row = curs.fetchone() - - if row is None: - return None - else: - return User(int(row[0]), row[1], row[2]) diff --git a/app/routes.py b/app/routes.py index fdf65e3..ca556ad 100644 --- a/app/routes.py +++ b/app/routes.py @@ -1,20 +1,56 @@ -from app import app -from database import database_get -from flask import render_template -from login import * +from app import app, db +from flask import render_template, redirect, url_for, flash +from flask_login import login_user +from forms import LoginForm, RegisterForm +from tables.user import User @app.route("/", methods=["GET"]) def index(): return render_template('base/index.html') -@app.route("/login", methods=["GET"]) +@app.route("/home", methods=["GET"]) +def home(): + return render_template('base/home.html') + +@app.route("/login", methods=['GET', 'POST']) def login(): - return render_template('user/login.html') + form = LoginForm() + username = form.username.data + password = form.password.data + + if form.validate_on_submit(): + # Valid submission + user = User.query.filter_by(username=username).first() + + # TODO: show user if login succeeded + if user and user.verify_password(password): + login_user(user) + return redirect(url_for('home')) + else: + flash("invalid login") + return redirect(url_for('register')) -@app.route("/register", methods=["GET"]) + return render_template('user/login.html', form=form) + +@app.route("/register", methods=['GET', 'POST']) def register(): form = RegisterForm() + # Load data from form + username = form.username.data + password = form.password.data + email = form.email.data + + print("register") + + if form.validate_on_submit(): + # Valid submission + user = User.query.filter_by(username=username).first() + if not user: + # No user with this username + user = User(username, password, email) + db.session.add(user) + db.session.commit() - #if form.validate_on_submit(): - # # TODO: make sure username isnt taken somehow - return render_template('user/register.html') + return redirect(url_for('login')) + else: + return render_template('user/register.html', form=form) diff --git a/app/tables/user.py b/app/tables/user.py new file mode 100644 index 0000000..d384fdb --- /dev/null +++ b/app/tables/user.py @@ -0,0 +1,25 @@ +from app import db, login_manager +from flask_login import UserMixin +from werkzeug.security import generate_password_hash, check_password_hash + +@login_manager.user_loader +def get_user(user_id): + return User.query.get(user_id) + +class User(db.Model, UserMixin): + __tablename__ = 'users' + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String(80), unique=True, nullable=False) + password = db.Column(db.String(80)) + email = db.Column(db.String(120), unique=True, nullable=False) + + def __init__(self, username, password, email): + self.username = username + self.password = generate_password_hash(password) + self.email = email + + def __repr__(self): + return f'' + + def verify_password(self, pwd): + return check_password_hash(self.password, pwd) diff --git a/app/templates/user/login.html b/app/templates/user/login.html index 7a671c1..9aaf33c 100644 --- a/app/templates/user/login.html +++ b/app/templates/user/login.html @@ -3,26 +3,30 @@ {% block content %}