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
|
||||
|
||||
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 flask_login import login_required, login_user, logout_user, current_user
|
||||
from werkzeug.security import generate_password_hash, check_password_hash
|
||||
|
||||
from server.extensions import cache, login_manager, db
|
||||
from server.models import Users
|
||||
from server.extensions import db
|
||||
from server.models import Users, Tokens
|
||||
|
||||
|
||||
blueprint = Blueprint('auth', __name__)
|
||||
|
@ -15,10 +15,12 @@ blueprint = Blueprint('auth', __name__)
|
|||
def auth():
|
||||
return render_template('auth.html')
|
||||
|
||||
|
||||
@blueprint.route('/account', methods=['GET'])
|
||||
@login_required
|
||||
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'])
|
||||
|
@ -46,11 +48,44 @@ def register():
|
|||
flash(err, "error")
|
||||
return redirect(url_for("auth.auth"))
|
||||
|
||||
register_user = Users(
|
||||
username=username,
|
||||
password=generate_password_hash(password, method="sha256"),
|
||||
)
|
||||
register_user = Users(username=username, password=generate_password_hash(password, method="scrypt"))
|
||||
db.session.add(register_user)
|
||||
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
|
||||
"""
|
||||
from uuid import uuid4
|
||||
from flask_login import UserMixin
|
||||
from server.extensions import db
|
||||
|
||||
|
||||
|
@ -19,17 +20,17 @@ class Scores(db.Model):
|
|||
username = db.Column(db.String(32), nullable=True)
|
||||
|
||||
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(
|
||||
db.DateTime,
|
||||
nullable=False,
|
||||
server_default=db.func.utcnow(),
|
||||
server_default=db.func.now(),
|
||||
)
|
||||
|
||||
scorer = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
|
||||
|
||||
|
||||
class Users(db.Model):
|
||||
class Users(db.Model, UserMixin):
|
||||
"""
|
||||
User table
|
||||
"""
|
||||
|
@ -39,12 +40,11 @@ class Users(db.Model):
|
|||
alt_id = db.Column(db.String, nullable=False, unique=True, default=str(uuid4()))
|
||||
|
||||
username = db.Column(db.String(32), unique=True, nullable=False)
|
||||
email = db.Column(db.String, unique=True, nullable=False)
|
||||
password = db.Column(db.String(128), nullable=False)
|
||||
password = db.Column(db.String, nullable=False)
|
||||
joined_at = db.Column(
|
||||
db.DateTime,
|
||||
nullable=False,
|
||||
server_default=db.func.utcnow(), # pylint: disable=E1102
|
||||
server_default=db.func.now(),
|
||||
)
|
||||
|
||||
scores = db.relationship('Scores', backref='user', lazy=True)
|
||||
|
@ -61,10 +61,10 @@ class Tokens(db.Model):
|
|||
__tablename__ = "tokens"
|
||||
|
||||
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()))
|
||||
created_at = db.Column(
|
||||
db.DateTime,
|
||||
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)
|
||||
text-decoration: none
|
||||
background-color: transparent
|
||||
background-color: RGBA($color, 0.02)
|
||||
color: RGB($color)
|
||||
border-radius: 2px
|
||||
// box-shadow: 0 0 0 0 RGBA($color, 0)
|
||||
transition: background-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out, transform 0.2s ease-in-out
|
||||
transition: background-color 0.2s ease-in-out, transform 0.1s ease-in-out
|
||||
|
||||
&:hover
|
||||
background-color: RGBA($color, 0.3)
|
||||
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')
|
||||
|
||||
|
@ -79,41 +77,44 @@ header
|
|||
background-color: RGBA($darkBlue, 0.7)
|
||||
|
||||
> img
|
||||
margin-bottom: 2rem
|
||||
margin-bottom: 1rem
|
||||
width: 100%
|
||||
height: auto
|
||||
text-align: center
|
||||
|
||||
> nav
|
||||
margin: 0
|
||||
padding: 0
|
||||
nav
|
||||
margin-top: 0.3rem
|
||||
padding: 0
|
||||
|
||||
height: 3rem
|
||||
display: flex
|
||||
flex-direction: row
|
||||
justify-content: center
|
||||
|
||||
display: flex
|
||||
flex-direction: row
|
||||
justify-content: center
|
||||
> span
|
||||
width: 100%
|
||||
|
||||
> span
|
||||
width: 100%
|
||||
> a
|
||||
margin: auto 0.15rem
|
||||
padding: 0.5rem 1rem
|
||||
|
||||
> a
|
||||
margin: auto 0.25rem
|
||||
padding: 0.5rem 1rem
|
||||
text-decoration: none
|
||||
white-space: nowrap
|
||||
font-size: 0.9em
|
||||
|
||||
text-decoration: none
|
||||
white-space: nowrap
|
||||
color: RGB($primary)
|
||||
|
||||
color: RGB($primary)
|
||||
&.button
|
||||
@include button($white)
|
||||
|
||||
&.button
|
||||
@include button($white)
|
||||
&.primary
|
||||
@include button($primary)
|
||||
|
||||
&.primary
|
||||
@include button($primary)
|
||||
&.secondary
|
||||
@include button($secondary)
|
||||
|
||||
&.secondary
|
||||
@include button($secondary)
|
||||
> i
|
||||
font-size: 1.25em
|
||||
display: block
|
||||
|
||||
.flash
|
||||
display: flex
|
||||
|
@ -160,7 +161,7 @@ main
|
|||
color: RGB($white)
|
||||
|
||||
.auth
|
||||
margin: auto 0
|
||||
margin-bottom: 1rem
|
||||
padding: 1rem
|
||||
|
||||
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 %}
|
||||
<div class="auth">
|
||||
<h2>Login</h2>
|
||||
<form action="" method="POST">
|
||||
<input type="text" name="username" placeholder="Username | Email" required>
|
||||
<form action="{{ url_for('auth.login') }}" method="POST">
|
||||
<input type="text" name="username" placeholder="Username" required>
|
||||
<input type="password" name="password" placeholder="Password" required>
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<p style="text-align: center;">or</p>
|
||||
|
||||
<div class="auth">
|
||||
<h2>Register</h2>
|
||||
<form action="{{ url_for('auth.register') }}" method="POST">
|
||||
<input type="text" name="username" placeholder="Username" required>
|
||||
<input type="password" name="password" placeholder="Password" required>
|
||||
<button type="submit" class="secondary">Register</button>
|
||||
<button type="submit">Register</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -5,30 +5,33 @@
|
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Front Rooms Highscores</title>
|
||||
<script src="https://unpkg.com/@phosphor-icons/web"></script>
|
||||
{% assets "styles" %}
|
||||
<link rel="stylesheet" href="{{ ASSET_URL }}" type="text/css">
|
||||
{% endassets %}
|
||||
</head>
|
||||
<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">
|
||||
<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>
|
||||
<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>
|
||||
<a href="{{ url_for('views.index') }}" class="button"><i class="ph ph-house"></i></a>
|
||||
<a href="{{ url_for('views.about') }}" class="button"><i class="ph ph-info"></i></a>
|
||||
<a href="#" class="button"><i class="ph ph-download-simple"></i></a>
|
||||
|
||||
<span></span> <!-- This is a spacer -->
|
||||
<!-- This is a spacer -->
|
||||
<span></span>
|
||||
|
||||
{% 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 %}
|
||||
<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 %}
|
||||
</nav>
|
||||
|
||||
<!-- Secondary nav bar for page specific content -->
|
||||
{% block nav %}{% endblock %}
|
||||
</header>
|
||||
|
||||
<!-- This is where the flash messages will be displayed -->
|
||||
|
|
|
@ -1,4 +1,16 @@
|
|||
{% 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 %}
|
||||
{% if scores %}
|
||||
<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 wtforms import StringField, IntegerField
|
||||
from wtforms.validators import DataRequired
|
||||
|
@ -20,7 +20,7 @@ class ScoreForm(FlaskForm):
|
|||
|
||||
|
||||
@blueprint.route('/')
|
||||
@cache.cached(timeout=60)
|
||||
# @cache.cached(timeout=60)
|
||||
def index():
|
||||
difficulty = request.args.get('diff', 0)
|
||||
|
||||
|
@ -32,6 +32,11 @@ def index():
|
|||
return render_template('scores.html', top_scores=top_scores)
|
||||
|
||||
|
||||
@blueprint.route('/about')
|
||||
def about():
|
||||
return render_template('about.html')
|
||||
|
||||
|
||||
|
||||
@blueprint.route('/post', methods=['POST'])
|
||||
def post():
|
||||
|
|
Loading…
Add table
Reference in a new issue