diff --git a/server/__init__.py b/server/__init__.py index 5fece3f..9b6042e 100644 --- a/server/__init__.py +++ b/server/__init__.py @@ -1,6 +1,7 @@ from flask import Flask from flask_assets import Bundle -from server.extensions import db, migrate, cache, assets +from server.extensions import db, migrate, cache, assets, login_manager +from server.models import Users from server import views, auth app = Flask(__name__) @@ -12,6 +13,9 @@ migrate.init_app(app, db) with app.app_context(): db.create_all() +login_manager.init_app(app) +login_manager.login_view = "auth.auth" + assets.init_app(app) styles = Bundle("style.sass", filters="libsass, cssmin", output="gen/styles.css", depends="style.sass") assets.register("styles", styles) @@ -19,3 +23,8 @@ assets.register("styles", styles) cache.init_app(app) app.register_blueprint(views.blueprint) app.register_blueprint(auth.blueprint) + + +@login_manager.user_loader +def load_user(user_id): + return Users.query.filter_by(alt_id=user_id).first() diff --git a/server/auth.py b/server/auth.py index 6bddd68..bc660f4 100644 --- a/server/auth.py +++ b/server/auth.py @@ -1,25 +1,56 @@ -from flask import Blueprint, jsonify, request, render_template -from flask_wtf import FlaskForm -from wtforms import StringField, IntegerField -from wtforms.validators import DataRequired +import re -from server.models import Scores, Users, Tokens -from server.extensions import db, cache -from server.config import BEARER_TOKEN +from flask import Blueprint, render_template, request, flash, redirect, url_for +from flask_login import current_user, login_required +from werkzeug.security import generate_password_hash + +from server.extensions import cache, login_manager, db +from server.models import Users blueprint = Blueprint('auth', __name__) -class ScoreForm(FlaskForm): - playerName = StringField('Player Name', validators=[DataRequired()]) - playerId = StringField('Player ID', validators=[DataRequired()]) - score = IntegerField('Score', validators=[DataRequired()]) - difficulty = StringField('Difficulty', validators=[DataRequired()]) - achievements = StringField('Achievements', validators=[DataRequired()]) - - @blueprint.route('/auth', methods=['GET']) -@cache.cached(timeout=60) def auth(): - return render_template('auth.html') \ No newline at end of file + return render_template('auth.html') + +@blueprint.route('/account', methods=['GET']) +@login_required +def account(): + return render_template('account.html') + + +@blueprint.route('/register', methods=['POST']) +def register(): + # Get the form data + username = request.form["username"].strip() + password = request.form["password"].strip() + username_regex = re.compile(r"\b[A-Za-z0-9._-]+\b") + + error = [] + + # Validate the form + if not username or not username_regex.match(username): + error.append("Username is empty or invalid! Must be alphanumeric, and can contain ._-") + if not password: + error.append("Password is empty!") + elif len(password) < 8: + error.append("Password is too short! Must be at least 8 characters long.") + if Users.query.filter_by(username=username).first(): + error.append("Username already exists!") + + # If there are errors, return them + if error: + for err in error: + flash(err, "error") + return redirect(url_for("auth.auth")) + + register_user = Users( + username=username, + password=generate_password_hash(password, method="sha256"), + ) + db.session.add(register_user) + db.session.commit() + + return redirect(url_for("view.index")) \ No newline at end of file diff --git a/server/extensions.py b/server/extensions.py index d361aa8..54ce5fe 100644 --- a/server/extensions.py +++ b/server/extensions.py @@ -2,8 +2,10 @@ from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate from flask_assets import Environment from flask_caching import Cache +from flask_login import LoginManager db = SQLAlchemy() migrate = Migrate() assets = Environment() cache = Cache(config={'CACHE_TYPE': 'simple'}) +login_manager = LoginManager() diff --git a/server/static/gen/styles.css b/server/static/gen/styles.css index 2356861..eba9d0b 100644 --- a/server/static/gen/styles.css +++ b/server/static/gen/styles.css @@ -1 +1 @@ -@import url("https://fonts.googleapis.com/css2?family=Merriweather:ital,wght@0,300;0,400;0,700;0,900;1,300;1,400;1,700&display=swap");:root{--black:21,21,21;--white:232,227,227;--primary:213,214,130;--secondary:185,77,77;--gold:255,222,70;--silver:229,220,206;--bronze:193,145,69;--darkBlue:9,9,39}*{box-sizing:border-box;font-family:'Merriweather',serif}html{margin:0;padding:0}body{margin:0;padding:0;display:flex;flex-direction:row;background-color:RGB(var(--darkBlue));color:RGB(var(--white))}.background{width:100%;height:100%;object-fit:cover;position:absolute;z-index:1}.app{margin:0 auto;padding:2rem;width:800px;min-height:100vh;position:relative;display:flex;flex-direction:column;background-color:rgba(var(--darkBlue),0.9);z-index:2}.app>table{width:100%}.app .center-text{height:100%;display:flex;flex-direction:column;justify-content:center;align-items:center}.app .center-text>h2{margin:0;text-align:center;font-size:2em;color:RGB(var(--white))}.app .center-text>p{margin:0;text-align:center;font-size:1em;color:RGB(var(--white))}.app .auth{margin:auto 1rem;padding:1rem;display:flex;flex-direction:column;background-color:rgba(var(--darkBlue),0.5);border-radius:2px}.app .auth>h2{margin:0 0 1rem 0;font-size:1.3em;color:RGB(var(--white))}.title{margin-bottom:2rem;width:100%;height:auto;text-align:center}.subtitle{margin-bottom:1rem;padding:0;text-align:center;font-weight:bolder;font-size:1.2em;color:RGB(var(--secondary))}.subtitle>span{padding:0 .1rem;color:transparent;background:RGB(var(--secondary))}nav{margin:0;padding:0;height:3rem;display:flex;flex-direction:row;justify-content:center}nav>span{width:100%}nav>a{margin:auto .25rem;padding:.5rem 1rem;text-decoration:none;white-space:nowrap;color:RGB(var(--primary))}nav>a.button{text-decoration:none;background-color:transparent;color:RGB(var(--white));border-radius:2px;transition:background-color .2s ease-in-out,box-shadow .2s ease-in-out,transform .2s ease-in-out}nav>a.button:hover{background-color:RGBA(var(--white),0.3);transform:translateY(-0.1rem)}nav>a.button.primary{text-decoration:none;background-color:transparent;color:RGB(var(--primary));border-radius:2px;transition:background-color .2s ease-in-out,box-shadow .2s ease-in-out,transform .2s ease-in-out}nav>a.button.primary:hover{background-color:RGBA(var(--primary),0.3);transform:translateY(-0.1rem)}nav>a.button.secondary{text-decoration:none;background-color:transparent;color:RGB(var(--secondary));border-radius:2px;transition:background-color .2s ease-in-out,box-shadow .2s ease-in-out,transform .2s ease-in-out}nav>a.button.secondary:hover{background-color:RGBA(var(--secondary),0.3);transform:translateY(-0.1rem)}form{display:flex;flex-direction:column}form>input{margin:0 0 1rem 0;padding:.5rem 1rem;border:1px solid RGB(var(--white));border-radius:2px;background-color:RGB(var(--darkBlue));color:RGB(var(--white))}form>input:focus{outline:none;border-color:RGB(var(--primary))}form>input.error{border-color:RGB(var(--secondary))}form>button{margin:0;padding:.5rem 1rem;font-weight:bolder;border:transparent;border-radius:2px;background-color:RGB(var(--primary));color:RGB(var(--black))}form>button:focus-visible,form>button:hover{outline:none;background-color:RGBA(var(--primary),0.3);color:RGB(var(--primary))}form>button.disabled{pointer-events:none;opacity:.5}form>button.secondary{background-color:RGB(var(--secondary));color:RGB(var(--black))}form>button.secondary:focus-visible,form>button.secondary:hover{background-color:RGBA(var(--secondary),0.3);color:RGB(var(--secondary))} \ No newline at end of file +@import url("https://fonts.googleapis.com/css2?family=Merriweather:ital,wght@0,300;0,400;0,700;0,900;1,300;1,400;1,700&display=swap");:root{--black:21,21,21;--white:232,227,227;--primary:213,214,130;--secondary:185,77,77;--gold:255,222,70;--silver:229,220,206;--bronze:193,145,69;--darkBlue:9,9,39}*{box-sizing:border-box;font-family:'Merriweather',serif}html{margin:0;padding:0}body{margin:0;padding:0;display:flex;flex-direction:row;background-color:RGB(var(--darkBlue));color:RGB(var(--white))}.background{width:100%;height:100%;object-fit:cover;position:absolute;z-index:1}.app{margin:0 auto;padding:0;width:800px;min-height:100vh;position:relative;display:flex;flex-direction:column;background-color:rgba(var(--darkBlue),0.7);z-index:2}.app>table{width:100%}header{padding:1rem;background-color:RGBA(var(--darkBlue),0.7)}header>img{margin-bottom:2rem;width:100%;height:auto;text-align:center}header>nav{margin:0;padding:0;height:3rem;display:flex;flex-direction:row;justify-content:center}header>nav>span{width:100%}header>nav>a{margin:auto .25rem;padding:.5rem 1rem;text-decoration:none;white-space:nowrap;color:RGB(var(--primary))}header>nav>a.button{text-decoration:none;background-color:transparent;color:RGB(var(--white));border-radius:2px;transition:background-color .2s ease-in-out,box-shadow .2s ease-in-out,transform .2s ease-in-out}header>nav>a.button:hover{background-color:RGBA(var(--white),0.3);transform:translateY(-0.1rem)}header>nav>a.button.primary{text-decoration:none;background-color:transparent;color:RGB(var(--primary));border-radius:2px;transition:background-color .2s ease-in-out,box-shadow .2s ease-in-out,transform .2s ease-in-out}header>nav>a.button.primary:hover{background-color:RGBA(var(--primary),0.3);transform:translateY(-0.1rem)}header>nav>a.button.secondary{text-decoration:none;background-color:transparent;color:RGB(var(--secondary));border-radius:2px;transition:background-color .2s ease-in-out,box-shadow .2s ease-in-out,transform .2s ease-in-out}header>nav>a.button.secondary:hover{background-color:RGBA(var(--secondary),0.3);transform:translateY(-0.1rem)}.flash{display:flex;flex-direction:column;justify-content:center;align-items:center}.flash>p{margin:.4rem 0 0;padding:.75rem 1rem;width:100%;position:relative;border-left:RGB(var(--secondary)) .25rem solid;background-color:RGB(var(--darkBlue));color:RGB(var(--secondary))}main{padding:1rem;height:100%;display:flex;flex-direction:column}main .center-text{height:100%;display:flex;flex-direction:column;justify-content:center;align-items:center}main .center-text>h2{margin:0;text-align:center;font-size:2em;color:RGB(var(--white))}main .center-text>p{margin:0;text-align:center;font-size:1em;color:RGB(var(--white))}main .auth{margin:auto 0;padding:1rem;display:flex;flex-direction:column;background-color:rgba(var(--darkBlue),0.7);border-radius:2px}main .auth>h2{margin:0 0 1rem 0;font-size:1.3em;color:RGB(var(--white))}main form{display:flex;flex-direction:column}main form>input{margin:0 0 1rem 0;padding:.75rem 1rem;border:1px solid RGB(var(--white));border-radius:2px;background-color:RGB(var(--darkBlue));color:RGB(var(--white))}main form>input:focus{outline:none;border-color:RGB(var(--primary))}main form>input.error{border-color:RGB(var(--secondary))}main form>button{margin:0;padding:.75rem 1rem;font-weight:bolder;border:transparent;border-radius:2px;background-color:RGB(var(--primary));color:RGB(var(--black))}main form>button:focus-visible,main form>button:hover{outline:none;background-color:RGBA(var(--primary),0.3);color:RGB(var(--primary))}main form>button.disabled{pointer-events:none;opacity:.5}main form>button.secondary{background-color:RGB(var(--secondary));color:RGB(var(--black))}main form>button.secondary:focus-visible,main form>button.secondary:hover{background-color:RGBA(var(--secondary),0.3);color:RGB(var(--secondary))}footer{padding:.5rem 1rem;width:100%;display:flex;flex-direction:row;background-color:RGBA(var(--darkBlue),0.7)}footer>p{margin:0;width:100%;text-align:center;font-size:.8em;white-space:nowrap;color:RGB(var(--white))}footer>p>a{color:RGB(var(--secondary));text-decoration:none}footer>p>a:hover{text-decoration:underline} \ No newline at end of file diff --git a/server/static/style.sass b/server/static/style.sass index f3bf3d5..596505d 100644 --- a/server/static/style.sass +++ b/server/static/style.sass @@ -58,7 +58,7 @@ body .app margin: 0 auto - padding: 2rem + padding: 0 width: 800px min-height: 100vh @@ -67,12 +67,78 @@ body display: flex flex-direction: column - background-color: rgba($darkBlue, 0.9) + background-color: rgba($darkBlue, 0.7) z-index: 2 > table width: 100% +header + padding: 1rem + + background-color: RGBA($darkBlue, 0.7) + + > img + margin-bottom: 2rem + width: 100% + height: auto + text-align: center + + > nav + margin: 0 + padding: 0 + + height: 3rem + + display: flex + flex-direction: row + justify-content: center + + > span + width: 100% + + > a + margin: auto 0.25rem + padding: 0.5rem 1rem + + text-decoration: none + white-space: nowrap + + color: RGB($primary) + + &.button + @include button($white) + + &.primary + @include button($primary) + + &.secondary + @include button($secondary) + +.flash + display: flex + flex-direction: column + justify-content: center + align-items: center + + > p + margin: 0.4rem 0 0 + padding: 0.75rem 1rem + + width: 100% + position: relative + + border-left: RGB($secondary) 0.25rem solid + background-color: RGB($darkBlue) + color: RGB($secondary) + +main + padding: 1rem + height: 100% + + display: flex + flex-direction: column + .center-text height: 100% @@ -94,13 +160,13 @@ body color: RGB($white) .auth - margin: auto 1rem + margin: auto 0 padding: 1rem display: flex flex-direction: column - background-color: rgba($darkBlue, 0.5) + background-color: rgba($darkBlue, 0.7) border-radius: 2px > h2 @@ -108,105 +174,74 @@ body font-size: 1.3em color: RGB($white) -.title - margin-bottom: 2rem - width: 100% - height: auto - text-align: center + form + display: flex + flex-direction: column -.subtitle - margin-bottom: 1rem - padding: 0 + > input + margin: 0 0 1rem 0 + padding: 0.75rem 1rem - text-align: center - font-weight: bolder - font-size: 1.2em + border: 1px solid RGB($white) + border-radius: 2px - color: RGB($secondary) + background-color: RGB($darkBlue) + color: RGB($white) - > span - padding: 0 0.1rem + &:focus + outline: none + border-color: RGB($primary) - color: transparent - background: RGB($secondary) + &.error + border-color: RGB($secondary) -nav - margin: 0 - padding: 0 + > button + margin: 0 + padding: 0.75rem 1rem - height: 3rem + font-weight: bolder - display: flex - flex-direction: row - justify-content: center + border: transparent + border-radius: 2px - > span - width: 100% - - > a - margin: auto 0.25rem - padding: 0.5rem 1rem - - text-decoration: none - white-space: nowrap - - color: RGB($primary) - - &.button - @include button($white) - - &.primary - @include button($primary) - - &.secondary - @include button($secondary) - -form - display: flex - flex-direction: column - - > input - margin: 0 0 1rem 0 - padding: 0.5rem 1rem - - border: 1px solid RGB($white) - border-radius: 2px - - background-color: RGB($darkBlue) - color: RGB($white) - - &:focus - outline: none - border-color: RGB($primary) - - &.error - border-color: RGB($secondary) - - > button - margin: 0 - padding: 0.5rem 1rem - - font-weight: bolder - - border: transparent - border-radius: 2px - - background-color: RGB($primary) - color: RGB($black) - - &:focus-visible, &:hover - outline: none - background-color: RGBA($primary, 0.3) - color: RGB($primary) - - &.disabled - pointer-events: none - opacity: 0.5 - - &.secondary - background-color: RGB($secondary) + background-color: RGB($primary) color: RGB($black) &:focus-visible, &:hover - background-color: RGBA($secondary, 0.3) - color: RGB($secondary) \ No newline at end of file + outline: none + background-color: RGBA($primary, 0.3) + color: RGB($primary) + + &.disabled + pointer-events: none + opacity: 0.5 + + &.secondary + background-color: RGB($secondary) + color: RGB($black) + + &:focus-visible, &:hover + background-color: RGBA($secondary, 0.3) + color: RGB($secondary) + +footer + padding: 0.5rem 1rem + width: 100% + display: flex + flex-direction: row + background-color: RGBA($darkBlue, 0.7) + + > p + margin: 0 + width: 100% + text-align: center + font-size: 0.8em + white-space: nowrap + color: RGB($white) + + > a + color: RGB($secondary) + text-decoration: none + + &:hover + text-decoration: underline \ No newline at end of file diff --git a/server/templates/auth.html b/server/templates/auth.html index 216d28a..b31b6e4 100644 --- a/server/templates/auth.html +++ b/server/templates/auth.html @@ -13,9 +13,8 @@

Register

-
+ -
diff --git a/server/templates/base.html b/server/templates/base.html index 5bb6314..049be4e 100644 --- a/server/templates/base.html +++ b/server/templates/base.html @@ -12,20 +12,39 @@ The Front Rooms pause menu
- The Front Rooms logo - + + + +
+ {% for message in get_flashed_messages() %} +

{{ message }}

+ {% endfor %} +
+ +
+ {% block content %}{% endblock %} +
+ +
\ No newline at end of file