Add base template and unuasble style

This commit is contained in:
Michał Gdula 2023-05-05 02:11:10 +03:00
parent 8bf194f936
commit 0af071992b
10 changed files with 94 additions and 53 deletions

View file

@ -0,0 +1,20 @@
import os
# Purely to make the code a bit more readable
def env(key):
return os.getenv(key)
SECRET_KEY = env('FLASK_KEY')
BEARER_TOKEN = env('BEARER_TOKEN')
user = env('DB_USER')
password = env('DB_PASSWORD')
host = env('DB_HOST')
database = env('DB_NAME')
SQLALCHEMY_DATABASE_URI = f"postgresql+psycopg2://{user}:{password}@{host}:5432/{database}"
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_POOL_RECYCLE = 621
MIGRATION_DIR = '/data/storage/migrations'
INSTANCE_DIR = '/data/storage/instance'

View file

@ -0,0 +1,7 @@
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_caching import Cache
db = SQLAlchemy()
migrate = Migrate()
cache = Cache(config={'CACHE_TYPE': 'simple'})

View file

@ -0,0 +1,43 @@
"""
Database models for the server
"""
from extensions import db
class Scores(db.Model):
"""
Post table
"""
__tablename__ = "scores"
id = db.Column(db.Integer, primary_key=True)
score = db.Column(db.Integer, nullable=False)
difficulty = db.Column(db.String, nullable=False)
achievements = db.Column(db.String, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
scored_at = db.Column(
db.DateTime,
nullable=False,
server_default=db.func.now(),
)
class Users(db.Model):
"""
User table
"""
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
steam_uuid = db.Column(db.String, unique=True, nullable=False)
steam_name = db.Column(db.String, nullable=False)
scores = db.relationship('Scores', backref='user', lazy=True)
creation_data = db.Column(
db.DateTime,
nullable=False,
server_default=db.func.now(),
)

View file

@ -0,0 +1,29 @@
#!/bin/sh
# Wait for database to start
until pg_isready -d $DB_NAME -h $DB_HOST -U $DB_USER
do
echo "Waiting for database to start... (5s)"
sleep 5
done
echo "Database is ready!"
# Check if migrastions folder exists
if [ ! -d "/data/storage/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);
then
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

View file

@ -0,0 +1,17 @@
import os
from flask import Flask
from extensions import db, migrate, cache
from config import MIGRATION_DIR, INSTANCE_DIR
from views import blueprint
app = Flask(__name__, instance_path=INSTANCE_DIR)
app.config.from_pyfile('config.py')
db.init_app(app)
migrate.init_app(app, db, directory=MIGRATION_DIR)
cache.init_app(app)
with app.app_context():
db.create_all()
app.register_blueprint(blueprint)

View file

@ -0,0 +1,53 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<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>
<style>
@import url('https://fonts.cdnfonts.com/css/cmu-serif');
* {
font-family: 'CMU Serif', serif;
}
body, html {
margin: 0;
padding: 0;
min-height: 100vh;
display: flex;
background-color: #000;
color: #fff;
}
.app {
margin: auto;
padding: 1rem;
text-align: center;
min-width: 621px;
border: 3px solid yellow;
}
</style>
</head>
<body>
<div class="app">
<h1>Front Rooms Highscores</h1>
<p>Created by Bradley, Mia, Bartek & Michał</p>
<nav>
<ul>
<li><a href="Easy">Easy</a></li>
<li><a href="Normal">Normal</a></li>
<li><a href="Hard">Hard</a></li>
</ul>
</nav>
{% block content %}{% endblock %}
</div>
</body>
</html>

View file

@ -0,0 +1,29 @@
{% extends "base.html" %}
{% block content %}
{% if show_sub %}
<nav>
<ul>
<li><a href="Level1">Level 1</a></li>
<li><a href="Level2">Level 2</a></li>
<li><a href="Level3">Level 3</a></li>
</ul>
</nav>
{% endif %}
<table>
<tr>
<th>Score</th>
<th>Difficulty</th>
<th>Achievements</th>
<th>Player</th>
</tr>
{% for score in top_scores %}
<tr>
<td>{{ score.score }}</td>
<td>{{ score.difficulty }}</td>
<td>{{ score.achievements }}</td>
<td>{{ score.user.steam_name }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}

View file

@ -0,0 +1,62 @@
from flask import Blueprint, jsonify, request, render_template
from flask_wtf import FlaskForm
from wtforms import StringField, IntegerField
from wtforms.validators import DataRequired
from models import Scores, Users
from extensions import db, cache
from config import BEARER_TOKEN
blueprint = Blueprint('views', __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('/', methods=['GET'])
@cache.cached(timeout=60)
def index():
top_scores = Scores.query.order_by(Scores.score.desc()).limit(10).all()
return render_template('scores.html', top_scores=top_scores, show_sub=True)
@blueprint.route('/post', methods=['POST'])
def post():
form = ScoreForm()
if not form:
return "Invalid form", 400
if request.headers.get('Authentication') != 'Bearer ' + BEARER_TOKEN:
return "Invalid authentication", 401
if not isinstance(form.score.data, int):
return "Score must be an integer", 400
if form.score.data < 0:
return "Score must be greater than 0", 400
if form.difficulty.data not in ['easy', 'medium', 'hard']:
return "Invalid difficulty", 400
user = Users.query.filter_by(steam_uuid=form.playerId.data).first()
if not user:
user = Users(
steam_uuid=form.playerId.data,
steam_name=form.playerName.data,
)
db.session.add(user)
db.session.commit()
score = Scores(
score=form.score.data,
difficulty=form.difficulty.data,
achievements=form.achievements.data,
user_id=user.id,
)
db.session.add(score)
db.session.commit()
return jsonify({'message': 'Success!'})