This commit is contained in:
Michał Gdula 2023-05-07 16:24:34 +01:00
parent 62945c943d
commit ebdef07840
7 changed files with 222 additions and 127 deletions

View file

@ -1,6 +1,7 @@
from flask import Flask from flask import Flask
from flask_assets import Bundle 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 from server import views, auth
app = Flask(__name__) app = Flask(__name__)
@ -12,6 +13,9 @@ migrate.init_app(app, db)
with app.app_context(): with app.app_context():
db.create_all() db.create_all()
login_manager.init_app(app)
login_manager.login_view = "auth.auth"
assets.init_app(app) assets.init_app(app)
styles = Bundle("style.sass", filters="libsass, cssmin", output="gen/styles.css", depends="style.sass") styles = Bundle("style.sass", filters="libsass, cssmin", output="gen/styles.css", depends="style.sass")
assets.register("styles", styles) assets.register("styles", styles)
@ -19,3 +23,8 @@ assets.register("styles", styles)
cache.init_app(app) cache.init_app(app)
app.register_blueprint(views.blueprint) app.register_blueprint(views.blueprint)
app.register_blueprint(auth.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()

View file

@ -1,25 +1,56 @@
from flask import Blueprint, jsonify, request, render_template import re
from flask_wtf import FlaskForm
from wtforms import StringField, IntegerField
from wtforms.validators import DataRequired
from server.models import Scores, Users, Tokens from flask import Blueprint, render_template, request, flash, redirect, url_for
from server.extensions import db, cache from flask_login import current_user, login_required
from server.config import BEARER_TOKEN from werkzeug.security import generate_password_hash
from server.extensions import cache, login_manager, db
from server.models import Users
blueprint = Blueprint('auth', __name__) 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']) @blueprint.route('/auth', methods=['GET'])
@cache.cached(timeout=60)
def auth(): def auth():
return render_template('auth.html') 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"))

View file

@ -2,8 +2,10 @@ from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate from flask_migrate import Migrate
from flask_assets import Environment from flask_assets import Environment
from flask_caching import Cache from flask_caching import Cache
from flask_login import LoginManager
db = SQLAlchemy() db = SQLAlchemy()
migrate = Migrate() migrate = Migrate()
assets = Environment() assets = Environment()
cache = Cache(config={'CACHE_TYPE': 'simple'}) cache = Cache(config={'CACHE_TYPE': 'simple'})
login_manager = LoginManager()

View file

@ -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))} @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}

View file

@ -58,7 +58,7 @@ body
.app .app
margin: 0 auto margin: 0 auto
padding: 2rem padding: 0
width: 800px width: 800px
min-height: 100vh min-height: 100vh
@ -67,70 +67,24 @@ body
display: flex display: flex
flex-direction: column flex-direction: column
background-color: rgba($darkBlue, 0.9) background-color: rgba($darkBlue, 0.7)
z-index: 2 z-index: 2
> table > table
width: 100% width: 100%
.center-text header
height: 100%
display: flex
flex-direction: column
justify-content: center
align-items: center
> h2
margin: 0
text-align: center
font-size: 2em
color: RGB($white)
> p
margin: 0
text-align: center
font-size: 1em
color: RGB($white)
.auth
margin: auto 1rem
padding: 1rem padding: 1rem
display: flex background-color: RGBA($darkBlue, 0.7)
flex-direction: column
background-color: rgba($darkBlue, 0.5) > img
border-radius: 2px
> h2
margin: 0 0 1rem 0
font-size: 1.3em
color: RGB($white)
.title
margin-bottom: 2rem margin-bottom: 2rem
width: 100% width: 100%
height: auto height: auto
text-align: center text-align: center
.subtitle > nav
margin-bottom: 1rem
padding: 0
text-align: center
font-weight: bolder
font-size: 1.2em
color: RGB($secondary)
> span
padding: 0 0.1rem
color: transparent
background: RGB($secondary)
nav
margin: 0 margin: 0
padding: 0 padding: 0
@ -161,13 +115,72 @@ nav
&.secondary &.secondary
@include button($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%
display: flex
flex-direction: column
justify-content: center
align-items: center
> h2
margin: 0
text-align: center
font-size: 2em
color: RGB($white)
> p
margin: 0
text-align: center
font-size: 1em
color: RGB($white)
.auth
margin: auto 0
padding: 1rem
display: flex
flex-direction: column
background-color: rgba($darkBlue, 0.7)
border-radius: 2px
> h2
margin: 0 0 1rem 0
font-size: 1.3em
color: RGB($white)
form form
display: flex display: flex
flex-direction: column flex-direction: column
> input > input
margin: 0 0 1rem 0 margin: 0 0 1rem 0
padding: 0.5rem 1rem padding: 0.75rem 1rem
border: 1px solid RGB($white) border: 1px solid RGB($white)
border-radius: 2px border-radius: 2px
@ -184,7 +197,7 @@ form
> button > button
margin: 0 margin: 0
padding: 0.5rem 1rem padding: 0.75rem 1rem
font-weight: bolder font-weight: bolder
@ -210,3 +223,25 @@ form
&:focus-visible, &:hover &:focus-visible, &:hover
background-color: RGBA($secondary, 0.3) background-color: RGBA($secondary, 0.3)
color: RGB($secondary) 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

View file

@ -13,9 +13,8 @@
<div class="auth"> <div class="auth">
<h2>Register</h2> <h2>Register</h2>
<form action="" method="POST"> <form action="{{ url_for('auth.register') }}" method="POST">
<input type="text" name="username" placeholder="Username" required> <input type="text" name="username" placeholder="Username" required>
<input type="email" name="email" placeholder="Email - Optional">
<input type="password" name="password" placeholder="Password" required> <input type="password" name="password" placeholder="Password" required>
<button type="submit" class="secondary">Register</button> <button type="submit" class="secondary">Register</button>
</form> </form>

View file

@ -12,7 +12,8 @@
<body> <body>
<img src="{{ url_for("static", filename="bg.png") }}" alt="The Front Rooms pause menu" class="background"> <img src="{{ url_for("static", filename="bg.png") }}" alt="The Front Rooms pause menu" class="background">
<div class="app"> <div class="app">
<img src="{{ url_for("static", filename="title.png") }}" alt="The Front Rooms logo" class="title"> <header>
<img src="{{ url_for("static", filename="title.png") }}" alt="The Front Rooms logo" class="title" height="60px">
<nav> <nav>
<a href="{{ url_for('views.index', diff=0) }}" class="button">Level 1</a> <a href="{{ url_for('views.index', diff=0) }}" class="button">Level 1</a>
<a href="{{ url_for('views.index', diff=1) }}" class="button">Level 2</a> <a href="{{ url_for('views.index', diff=1) }}" class="button">Level 2</a>
@ -22,10 +23,28 @@
<span></span> <!-- This is a spacer --> <span></span> <!-- This is a spacer -->
{% if current_user.is_authenticated %}
<a href="{{ url_for('auth.account') }}" class="button secondary">Account</a>
{% else %}
<a href="{{ url_for('auth.auth') }}" class="button secondary">Login</a> <a href="{{ url_for('auth.auth') }}" class="button secondary">Login</a>
{% endif %}
</nav> </nav>
</header>
<!-- This is where the flash messages will be displayed -->
<div class="flash">
{% for message in get_flashed_messages() %}
<p>{{ message }}</p>
{% endfor %}
</div>
<main>
{% block content %}{% endblock %} {% block content %}{% endblock %}
</main>
<footer>
<p>By Project Redacted | <a href="https://github.com/Fluffy-Bean/GameExpo23">Server Source</a></p>
</footer>
</div> </div>
</body> </body>
</html> </html>