mirror of
https://github.com/Project-Redacted/Highscores-Server.git
synced 2025-05-14 07:32:15 +00:00
Add account page and login system
This commit is contained in:
parent
ebdef07840
commit
ffba2b3b7b
11 changed files with 154 additions and 63 deletions
22
run.sh
Executable file
22
run.sh
Executable file
|
@ -0,0 +1,22 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Check if migrations folder exists
|
||||||
|
if [ ! -d "./migrations" ];
|
||||||
|
then
|
||||||
|
echo "Creating tables..."
|
||||||
|
flask --app server db init
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if there are any changes to the database
|
||||||
|
if flask --app server db check | grep "No changes detected";
|
||||||
|
then
|
||||||
|
echo "No database changes detected"
|
||||||
|
else
|
||||||
|
echo "Database changes detected! Migrating..."
|
||||||
|
flask --app server db migrate
|
||||||
|
flask --app server db upgrade
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Start server!!!!
|
||||||
|
echo "Starting server..."
|
||||||
|
# gunicorn --bind highscore:8080 server:app
|
|
@ -1,11 +1,11 @@
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from flask import Blueprint, render_template, request, flash, redirect, url_for
|
from flask import Blueprint, render_template, request, flash, redirect, url_for
|
||||||
from flask_login import current_user, login_required
|
from flask_login import login_required, login_user, logout_user, current_user
|
||||||
from werkzeug.security import generate_password_hash
|
from werkzeug.security import generate_password_hash, check_password_hash
|
||||||
|
|
||||||
from server.extensions import cache, login_manager, db
|
from server.extensions import db
|
||||||
from server.models import Users
|
from server.models import Users, Tokens
|
||||||
|
|
||||||
|
|
||||||
blueprint = Blueprint('auth', __name__)
|
blueprint = Blueprint('auth', __name__)
|
||||||
|
@ -15,10 +15,12 @@ blueprint = Blueprint('auth', __name__)
|
||||||
def auth():
|
def auth():
|
||||||
return render_template('auth.html')
|
return render_template('auth.html')
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/account', methods=['GET'])
|
@blueprint.route('/account', methods=['GET'])
|
||||||
@login_required
|
@login_required
|
||||||
def account():
|
def account():
|
||||||
return render_template('account.html')
|
token_list = Tokens.query.filter_by(holder=current_user.id).all()
|
||||||
|
return render_template('account.html', token_list=token_list)
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/register', methods=['POST'])
|
@blueprint.route('/register', methods=['POST'])
|
||||||
|
@ -46,11 +48,44 @@ def register():
|
||||||
flash(err, "error")
|
flash(err, "error")
|
||||||
return redirect(url_for("auth.auth"))
|
return redirect(url_for("auth.auth"))
|
||||||
|
|
||||||
register_user = Users(
|
register_user = Users(username=username, password=generate_password_hash(password, method="scrypt"))
|
||||||
username=username,
|
|
||||||
password=generate_password_hash(password, method="sha256"),
|
|
||||||
)
|
|
||||||
db.session.add(register_user)
|
db.session.add(register_user)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
return redirect(url_for("view.index"))
|
flash("Successfully registered!", "success")
|
||||||
|
return redirect(url_for("auth.auth"))
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route('/login', methods=['POST'])
|
||||||
|
def login():
|
||||||
|
# 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) or not password:
|
||||||
|
error.append("Username or Password is incorrect!")
|
||||||
|
|
||||||
|
user = Users.query.filter_by(username=username).first()
|
||||||
|
|
||||||
|
if not user or not check_password_hash(user.password, password):
|
||||||
|
error.append("Username or Password is incorrect!")
|
||||||
|
|
||||||
|
# If there are errors, return them
|
||||||
|
if error:
|
||||||
|
for err in error:
|
||||||
|
flash(err, "error")
|
||||||
|
return redirect(url_for("auth.account"))
|
||||||
|
|
||||||
|
login_user(user, remember=True)
|
||||||
|
return redirect(url_for("views.index"))
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route('/logout', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
def logout():
|
||||||
|
logout_user()
|
||||||
|
return redirect(url_for("views.index"))
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
Database models for the server
|
Database models for the server
|
||||||
"""
|
"""
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
from flask_login import UserMixin
|
||||||
from server.extensions import db
|
from server.extensions import db
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,17 +20,17 @@ class Scores(db.Model):
|
||||||
username = db.Column(db.String(32), nullable=True)
|
username = db.Column(db.String(32), nullable=True)
|
||||||
|
|
||||||
score = db.Column(db.Float, nullable=False)
|
score = db.Column(db.Float, nullable=False)
|
||||||
difficulty = db.Column(db.String, nullable=False)
|
difficulty = db.Column(db.Integer, nullable=False)
|
||||||
scored_at = db.Column(
|
scored_at = db.Column(
|
||||||
db.DateTime,
|
db.DateTime,
|
||||||
nullable=False,
|
nullable=False,
|
||||||
server_default=db.func.utcnow(),
|
server_default=db.func.now(),
|
||||||
)
|
)
|
||||||
|
|
||||||
scorer = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
|
scorer = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
|
||||||
|
|
||||||
|
|
||||||
class Users(db.Model):
|
class Users(db.Model, UserMixin):
|
||||||
"""
|
"""
|
||||||
User table
|
User table
|
||||||
"""
|
"""
|
||||||
|
@ -39,12 +40,11 @@ class Users(db.Model):
|
||||||
alt_id = db.Column(db.String, nullable=False, unique=True, default=str(uuid4()))
|
alt_id = db.Column(db.String, nullable=False, unique=True, default=str(uuid4()))
|
||||||
|
|
||||||
username = db.Column(db.String(32), unique=True, nullable=False)
|
username = db.Column(db.String(32), unique=True, nullable=False)
|
||||||
email = db.Column(db.String, unique=True, nullable=False)
|
password = db.Column(db.String, nullable=False)
|
||||||
password = db.Column(db.String(128), nullable=False)
|
|
||||||
joined_at = db.Column(
|
joined_at = db.Column(
|
||||||
db.DateTime,
|
db.DateTime,
|
||||||
nullable=False,
|
nullable=False,
|
||||||
server_default=db.func.utcnow(), # pylint: disable=E1102
|
server_default=db.func.now(),
|
||||||
)
|
)
|
||||||
|
|
||||||
scores = db.relationship('Scores', backref='user', lazy=True)
|
scores = db.relationship('Scores', backref='user', lazy=True)
|
||||||
|
@ -61,10 +61,10 @@ class Tokens(db.Model):
|
||||||
__tablename__ = "tokens"
|
__tablename__ = "tokens"
|
||||||
|
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
holder = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||||
token = db.Column(db.String, nullable=False, unique=True, default=str(uuid4()))
|
token = db.Column(db.String, nullable=False, unique=True, default=str(uuid4()))
|
||||||
created_at = db.Column(
|
created_at = db.Column(
|
||||||
db.DateTime,
|
db.DateTime,
|
||||||
nullable=False,
|
nullable=False,
|
||||||
server_default=db.func.utcnow(), # pylint: disable=E1102
|
server_default=db.func.now(),
|
||||||
)
|
)
|
||||||
holder = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
|
||||||
|
|
|
@ -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: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}
|
@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:1rem;width:100%;height:auto;text-align:center}nav{margin-top:.3rem;padding:0;display:flex;flex-direction:row;justify-content:center}nav>span{width:100%}nav>a{margin:auto .15rem;padding:.5rem 1rem;text-decoration:none;white-space:nowrap;font-size:.9em;color:RGB(var(--primary))}nav>a.button{text-decoration:none;background-color:RGBA(var(--white),0.02);color:RGB(var(--white));border-radius:2px;transition:background-color .2s ease-in-out,transform .1s 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:RGBA(var(--primary),0.02);color:RGB(var(--primary));border-radius:2px;transition:background-color .2s ease-in-out,transform .1s 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:RGBA(var(--secondary),0.02);color:RGB(var(--secondary));border-radius:2px;transition:background-color .2s ease-in-out,transform .1s ease-in-out}nav>a.button.secondary:hover{background-color:RGBA(var(--secondary),0.3);transform:translateY(-0.1rem)}nav>a>i{font-size:1.25em;display:block}.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-bottom:1rem;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}
|
|
@ -9,16 +9,14 @@ $darkBlue: var(--darkBlue)
|
||||||
|
|
||||||
@mixin button($color)
|
@mixin button($color)
|
||||||
text-decoration: none
|
text-decoration: none
|
||||||
background-color: transparent
|
background-color: RGBA($color, 0.02)
|
||||||
color: RGB($color)
|
color: RGB($color)
|
||||||
border-radius: 2px
|
border-radius: 2px
|
||||||
// box-shadow: 0 0 0 0 RGBA($color, 0)
|
transition: background-color 0.2s ease-in-out, transform 0.1s ease-in-out
|
||||||
transition: background-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out, transform 0.2s ease-in-out
|
|
||||||
|
|
||||||
&:hover
|
&:hover
|
||||||
background-color: RGBA($color, 0.3)
|
background-color: RGBA($color, 0.3)
|
||||||
transform: translateY(-0.1rem)
|
transform: translateY(-0.1rem)
|
||||||
// box-shadow: 0 0.1rem 0.4rem 0.1rem RGBA($color, 0.2)
|
|
||||||
|
|
||||||
@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')
|
@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')
|
||||||
|
|
||||||
|
@ -79,41 +77,44 @@ header
|
||||||
background-color: RGBA($darkBlue, 0.7)
|
background-color: RGBA($darkBlue, 0.7)
|
||||||
|
|
||||||
> img
|
> img
|
||||||
margin-bottom: 2rem
|
margin-bottom: 1rem
|
||||||
width: 100%
|
width: 100%
|
||||||
height: auto
|
height: auto
|
||||||
text-align: center
|
text-align: center
|
||||||
|
|
||||||
> nav
|
nav
|
||||||
margin: 0
|
margin-top: 0.3rem
|
||||||
padding: 0
|
padding: 0
|
||||||
|
|
||||||
height: 3rem
|
display: flex
|
||||||
|
flex-direction: row
|
||||||
|
justify-content: center
|
||||||
|
|
||||||
display: flex
|
> span
|
||||||
flex-direction: row
|
width: 100%
|
||||||
justify-content: center
|
|
||||||
|
|
||||||
> span
|
> a
|
||||||
width: 100%
|
margin: auto 0.15rem
|
||||||
|
padding: 0.5rem 1rem
|
||||||
|
|
||||||
> a
|
text-decoration: none
|
||||||
margin: auto 0.25rem
|
white-space: nowrap
|
||||||
padding: 0.5rem 1rem
|
font-size: 0.9em
|
||||||
|
|
||||||
text-decoration: none
|
color: RGB($primary)
|
||||||
white-space: nowrap
|
|
||||||
|
|
||||||
color: RGB($primary)
|
&.button
|
||||||
|
@include button($white)
|
||||||
|
|
||||||
&.button
|
&.primary
|
||||||
@include button($white)
|
@include button($primary)
|
||||||
|
|
||||||
&.primary
|
&.secondary
|
||||||
@include button($primary)
|
@include button($secondary)
|
||||||
|
|
||||||
&.secondary
|
> i
|
||||||
@include button($secondary)
|
font-size: 1.25em
|
||||||
|
display: block
|
||||||
|
|
||||||
.flash
|
.flash
|
||||||
display: flex
|
display: flex
|
||||||
|
@ -160,7 +161,7 @@ main
|
||||||
color: RGB($white)
|
color: RGB($white)
|
||||||
|
|
||||||
.auth
|
.auth
|
||||||
margin: auto 0
|
margin-bottom: 1rem
|
||||||
padding: 1rem
|
padding: 1rem
|
||||||
|
|
||||||
display: flex
|
display: flex
|
||||||
|
|
5
server/templates/about.html
Normal file
5
server/templates/about.html
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block content %}
|
||||||
|
<h2>What is The Front Rooms?</h2>
|
||||||
|
<h2>Project Redacted</h2>
|
||||||
|
{% endblock %}
|
10
server/templates/account.html
Normal file
10
server/templates/account.html
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block content %}
|
||||||
|
<h2>Hello, {{ current_user.username }}!</h2>
|
||||||
|
<a href="{{ url_for('auth.logout') }}">Logout</a>
|
||||||
|
|
||||||
|
<h2>Tokens</h2>
|
||||||
|
{% for token in token_list %}
|
||||||
|
<p>{{ token.token }}</p>
|
||||||
|
{% endfor %}
|
||||||
|
{% endblock %}
|
|
@ -2,21 +2,19 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="auth">
|
<div class="auth">
|
||||||
<h2>Login</h2>
|
<h2>Login</h2>
|
||||||
<form action="" method="POST">
|
<form action="{{ url_for('auth.login') }}" method="POST">
|
||||||
<input type="text" name="username" placeholder="Username | Email" required>
|
<input type="text" name="username" placeholder="Username" required>
|
||||||
<input type="password" name="password" placeholder="Password" required>
|
<input type="password" name="password" placeholder="Password" required>
|
||||||
<button type="submit">Login</button>
|
<button type="submit">Login</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p style="text-align: center;">or</p>
|
|
||||||
|
|
||||||
<div class="auth">
|
<div class="auth">
|
||||||
<h2>Register</h2>
|
<h2>Register</h2>
|
||||||
<form action="{{ url_for('auth.register') }}" 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="password" name="password" placeholder="Password" required>
|
<input type="password" name="password" placeholder="Password" required>
|
||||||
<button type="submit" class="secondary">Register</button>
|
<button type="submit">Register</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -5,30 +5,33 @@
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Front Rooms Highscores</title>
|
<title>Front Rooms Highscores</title>
|
||||||
|
<script src="https://unpkg.com/@phosphor-icons/web"></script>
|
||||||
{% assets "styles" %}
|
{% assets "styles" %}
|
||||||
<link rel="stylesheet" href="{{ ASSET_URL }}" type="text/css">
|
<link rel="stylesheet" href="{{ ASSET_URL }}" type="text/css">
|
||||||
{% endassets %}
|
{% endassets %}
|
||||||
</head>
|
</head>
|
||||||
<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">
|
||||||
<header>
|
<header>
|
||||||
<img src="{{ url_for("static", filename="title.png") }}" alt="The Front Rooms logo" class="title" height="60px">
|
<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') }}" class="button"><i class="ph ph-house"></i></a>
|
||||||
<a href="{{ url_for('views.index', diff=1) }}" class="button">Level 2</a>
|
<a href="{{ url_for('views.about') }}" class="button"><i class="ph ph-info"></i></a>
|
||||||
<a href="{{ url_for('views.index', diff=2) }}" class="button">Level 3</a>
|
<a href="#" class="button"><i class="ph ph-download-simple"></i></a>
|
||||||
<a href="{{ url_for('views.index', diff=3) }}" class="button">Normal</a>
|
|
||||||
<a href="{{ url_for('views.index', diff=4) }}" class="button">Hard</a>
|
|
||||||
|
|
||||||
<span></span> <!-- This is a spacer -->
|
<!-- This is a spacer -->
|
||||||
|
<span></span>
|
||||||
|
|
||||||
{% if current_user.is_authenticated %}
|
{% if current_user.is_authenticated %}
|
||||||
<a href="{{ url_for('auth.account') }}" class="button secondary">Account</a>
|
<a href="{{ url_for('auth.account') }}" class="button primary">{{ current_user.username }}</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{{ url_for('auth.auth') }}" class="button secondary">Login</a>
|
<a href="{{ url_for('auth.auth') }}" class="button primary"><i class="ph ph-identification-card"></i></a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
<!-- Secondary nav bar for page specific content -->
|
||||||
|
{% block nav %}{% endblock %}
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- This is where the flash messages will be displayed -->
|
<!-- This is where the flash messages will be displayed -->
|
||||||
|
|
|
@ -1,4 +1,16 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
{% block nav %}
|
||||||
|
<nav>
|
||||||
|
<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=2) }}" class="button">Level 3</a>
|
||||||
|
<a href="{{ url_for('views.index', diff=3) }}" class="button">Normal</a>
|
||||||
|
<a href="{{ url_for('views.index', diff=4) }}" class="button">Hard</a>
|
||||||
|
|
||||||
|
<!-- This is a spacer -->
|
||||||
|
<span></span>
|
||||||
|
</nav>
|
||||||
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% if scores %}
|
{% if scores %}
|
||||||
<table>
|
<table>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from flask import Blueprint, jsonify, request, render_template
|
from flask import Blueprint, request, render_template
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import StringField, IntegerField
|
from wtforms import StringField, IntegerField
|
||||||
from wtforms.validators import DataRequired
|
from wtforms.validators import DataRequired
|
||||||
|
@ -20,7 +20,7 @@ class ScoreForm(FlaskForm):
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/')
|
@blueprint.route('/')
|
||||||
@cache.cached(timeout=60)
|
# @cache.cached(timeout=60)
|
||||||
def index():
|
def index():
|
||||||
difficulty = request.args.get('diff', 0)
|
difficulty = request.args.get('diff', 0)
|
||||||
|
|
||||||
|
@ -32,6 +32,11 @@ def index():
|
||||||
return render_template('scores.html', top_scores=top_scores)
|
return render_template('scores.html', top_scores=top_scores)
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route('/about')
|
||||||
|
def about():
|
||||||
|
return render_template('about.html')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/post', methods=['POST'])
|
@blueprint.route('/post', methods=['POST'])
|
||||||
def post():
|
def post():
|
||||||
|
|
Loading…
Add table
Reference in a new issue