Merge pull request #8 from Fluffy-Bean/beta

Beta
This commit is contained in:
Michał Gdula 2023-06-24 13:55:23 +01:00 committed by GitHub
commit 6472d6969c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 122 additions and 80 deletions

View file

@ -11,3 +11,4 @@ Flask-Caching
libsass-bin
jsmin
cssmin
timeago

View file

@ -1,5 +1,3 @@
from random import randint
from flask import Flask, render_template, abort
from flask_assets import Bundle
from werkzeug.exceptions import HTTPException
@ -53,8 +51,6 @@ def error_page(err):
abort(500)
return (
render_template(
"error.html", error=err.code, msg=err.description, image=str(randint(1, 3))
),
render_template("error.html", error=err.code, msg=err.description),
err.code,
)

View file

@ -1,5 +1,4 @@
import uuid
import re
import os
from PIL import Image
@ -29,9 +28,6 @@ def settings():
username = request.form.get("username", "").strip()
email = request.form.get("email", "").strip()
password = request.form.get("password", "").strip()
user_regex = re.compile(USER_REGEX)
email_regex = re.compile(USER_EMAIL_REGEX)
error = []
user = Users.query.filter_by(username=current_user.username).first()
@ -42,7 +38,7 @@ def settings():
if "file" in request.files and request.files['file'].filename:
picture = request.files["file"]
file_ext = picture.filename.split(".")[-1]
file_ext = picture.filename.split(".")[-1].lower()
file_name = f"{user.id}.{file_ext}"
if file_ext not in UPLOAD_EXTENSIONS:
@ -51,11 +47,14 @@ def settings():
error.append(f"Picture must be less than {UPLOAD_EXTENSIONS / 1000000}MB!")
image = Image.open(picture.stream)
image_x, image_y = image.size
image.thumbnail((
min(image_x, UPLOAD_RESOLUTION),
min(image_y, UPLOAD_RESOLUTION)
))
# Resizing gifs is more work than it's worth
if file_ext != "gif":
image_x, image_y = image.size
image.thumbnail((
min(image_x, UPLOAD_RESOLUTION),
min(image_y, UPLOAD_RESOLUTION)
))
if error:
for err in error:
@ -66,16 +65,21 @@ def settings():
os.remove(os.path.join(UPLOAD_DIR, user.picture))
user.picture = file_name
image.save(os.path.join(UPLOAD_DIR, file_name))
if file_ext == "gif":
image.save(os.path.join(UPLOAD_DIR, file_name), save_all=True)
else:
image.save(os.path.join(UPLOAD_DIR, file_name))
image.close()
if username:
if user_regex.match(username):
if USER_REGEX.match(username):
user.username = username
else:
error.append("Username is invalid!")
if email:
if email_regex.match(email):
if USER_EMAIL_REGEX.match(email):
user.email = email
else:
error.append("Email is invalid!")

View file

@ -1,4 +1,3 @@
import re
import shortuuid
from flask import Blueprint, request, jsonify, send_from_directory
@ -9,6 +8,9 @@ from werkzeug.utils import secure_filename
from .models import Scores, Sessions, Users
from .extensions import db
from .config import (
GAME_VERSION,
GAME_VERSIONS,
GAME_DIFFICULTY,
GAME_DIFFICULTIES,
MAX_SEARCH_RESULTS,
USER_REGEX,
@ -49,8 +51,8 @@ def tokens():
@blueprint.route("/post", methods=["POST"])
def post():
session_key = request.form.get("session", "").strip()
version = request.form.get("version", "alpha").strip()
difficulty = request.form.get("difficulty", 0)
version = request.form.get("version", GAME_VERSION).strip()
difficulty = request.form.get("difficulty", GAME_DIFFICULTY)
score = request.form.get("score", 0)
if not session_key:
@ -66,6 +68,8 @@ def post():
if int(difficulty) not in GAME_DIFFICULTIES:
return "Invalid difficulty!"
if version not in GAME_VERSIONS:
return "Invalid version!"
# This is a fix for a bug in the game that we dunno how to actually fix
# if score < 10:
# return "Score is impossible!"
@ -110,9 +114,8 @@ def login():
username = request.form.get("username", "").strip()
password = request.form.get("password", "").strip()
device = request.form.get("device", "Unknown").strip()
username_regex = re.compile(USER_REGEX)
if not username or not username_regex.match(username) or not password:
if not username or not USER_REGEX.match(username) or not password:
return "Username or Password is incorrect!", 400
user = Users.query.filter_by(username=username).first()

View file

@ -1,4 +1,3 @@
import re
import uuid
from flask import Blueprint, render_template, request, flash, redirect, url_for
@ -24,12 +23,10 @@ def register():
username = request.form.get("username", None).strip()
password = request.form.get("password", None).strip()
confirm = request.form.get("confirm", None).strip()
username_regex = re.compile(USER_REGEX)
error = []
# Validate the form
if not username or not username_regex.match(username):
if not username or not USER_REGEX.match(username):
error.append("Username is invalid! Must be alphanumeric, and can contain ._-")
if not password or len(password) < 8:
error.append("Password is too short! Must be at least 8 characters long.")
@ -61,11 +58,10 @@ def login():
# Get the form data
username = request.form.get("username", None).strip()
password = request.form.get("password", None).strip()
username_regex = re.compile(USER_REGEX)
error = []
# Validate the form
if not username or not username_regex.match(username) or not password:
if not username or not USER_REGEX.match(username) or not password:
error.append("Username or Password is incorrect!")
user = Users.query.filter_by(username=username).first()

View file

@ -1,35 +1,41 @@
from os import getenv
import re
SECRET_KEY = getenv("FLASK_KEY")
UPLOAD_DIR = "/data/uploads"
UPLOAD_EXTENSIONS = ["png", "jpg", "jpeg", "gif"]
UPLOAD_EXTENSIONS = ["png", "jpg", "jpeg", "gif", "webp"]
UPLOAD_RESOLUTION = 512
UPLOAD_MAX_SIZE = 3 * 1024 * 1024 # 3MB
GAME_VERSION = "alpha"
GAME_VERSIONS = ["alpha"]
GAME_DIFFICULTIES = [0, 1, 2, 3, 4]
GAME_DIFFICULTY = 0
USER_REGEX = r"\b[A-Za-z0-9._-]+\b"
USER_EMAIL_REGEX = r"[^@]+@[^@]+\.[^@]+"
GAME_VERSIONS = {
"alpha": "Alpha",
"alpha-expo": "Alpha (Expo Build)",
}
GAME_DIFFICULTIES = {
0: "Easy - Level 1",
1: "Easy - Level 2",
2: "Easy - Level 3",
3: "Medium",
4: "Hard",
}
USER_REGEX = re.compile(r"\b[A-Za-z0-9._-]+\b")
USER_EMAIL_REGEX = re.compile(r"[^@]+@[^@]+\.[^@]+")
MAX_TOP_SCORES = 15
MAX_SEARCH_RESULTS = 5
# Postgres
SECRET_KEY = getenv("FLASK_KEY")
user = getenv("DB_USER")
password = getenv("DB_PASSWORD")
host = getenv("DB_HOST")
db = getenv("DB_NAME")
port = 5432
SQLALCHEMY_DATABASE_URI = f"postgresql+psycopg2://{user}:{password}@{host}:5432/{db}"
SQLALCHEMY_DATABASE_URI = f"postgresql+psycopg2://{user}:{password}@{host}:{port}/{db}"
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_POOL_RECYCLE = 621
"""
# SQLite
SECRET_KEY = "dev"
SQLALCHEMY_DATABASE_URI = "sqlite:///tfr.db"
"""

View file

@ -1,4 +1,5 @@
import datetime
import timeago
from flask import Blueprint
@ -11,3 +12,8 @@ def format_result(dttm):
time = datetime.timedelta(seconds=int(dttm[0]))
microtime = dttm[1][:3]
return f"{time}:{microtime}"
@blueprint.app_template_filter()
def timesince(dttm):
return timeago.format(dttm, datetime.datetime.now())

Binary file not shown.

Before

Width:  |  Height:  |  Size: 570 KiB

After

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 708 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

After

Width:  |  Height:  |  Size: 351 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View file

@ -12,6 +12,7 @@
flex-direction: column
border: 1px solid RGBA($white, 0.1)
box-sizing: content-box
> img
height: 10rem
@ -67,7 +68,11 @@
gap: 0.5rem
> .text-input
margin: 0 !important
margin: 0
> button
margin: 0 0 0 auto
width: auto
@media (max-width: 621px)
.profile-settings
@ -80,3 +85,7 @@
> img
height: 13rem
width: 13rem
.other > button
margin: 0
width: 100%

View file

@ -90,9 +90,16 @@ body
padding: 0.5rem
text-align: center
&.player
color: RGB($secondary)
text-shadow: 0 0 5px RGBA($secondary, 0.7)
> a
color: RGB($white)
text-decoration: none
&#you
color: RGB($primary)
text-shadow: 0 0 5px RGBA($primary, 0.4)
&:hover
text-decoration: underline
> i
padding: 0.25rem

View file

@ -3,6 +3,6 @@
<div class="center-text">
<h2>{{ error }}</h2>
<p>{{ msg }}</p>
<image src="{{ url_for('static', filename='images/error/' + image + '.jpg') }}" alt="Error">
<image src="{{ url_for('static', filename='images/error.png') }}" alt="Error">
</div>
{% endblock %}

View file

@ -3,6 +3,8 @@
<h2>What is The Front Rooms?</h2>
<p>The Front Rooms is a game based on The Backrooms Genre of games.</p>
<img src="{{ url_for('static', filename='images/controls.png') }}" alt="Drawing of keyboard displaying controls" width="500" height="500">
<h2>Is my data secured?</h2>
<p>Yes, all passwords and emails are hashed and salted, and at no point stored in plain text.</p>
{% endblock %}

View file

@ -8,9 +8,9 @@
<div class="profile-settings">
<div class="picture">
{% if current_user.picture %}
<img src="{{ url_for('api.upload_dir', filename=current_user.picture) }}" alt="Profile picture">
<img src="{{ url_for('api.upload_dir', filename=current_user.picture) }}" alt="Profile picture" id="picture-preview">
{% else %}
<img src="{{ url_for('static', filename='images/pfp.png') }}" alt="Profile picture">
<img src="{{ url_for('static', filename='images/pfp.png') }}" alt="Profile picture" id="picture-preview">
{% endif %}
<label for="profile-picture">Profile Picture</label>
<input type="file" name="file" id="profile-picture">
@ -60,9 +60,26 @@
</div>
{% if not current_user.email %}
<script>
<script>
{% if not current_user.email %}
addFlashMessage("No Email set. If you loose your account, it will not be possible to recover it!", "error")
</script>
{% endif %}
{% endif %}
// Adjusted from https://stackoverflow.com/a/3814285/14885829
document.getElementById('profile-picture').onchange = (event) => {
let tgt = event.target || window.event.srcElement,
files = tgt.files;
if (FileReader && files && files.length) {
let fr = new FileReader();
fr.onload = () => {
document.getElementById('picture-preview').src = fr.result;
}
fr.readAsDataURL(files[0]);
}
else {
addFlashMessage("Your browser could not show a preview of your profile picture!", "error")
}
}
</script>
{% endblock %}

View file

@ -4,24 +4,23 @@
<nav>
<form method="GET" action="{{ url_for('views.index') }}" class="compact">
<select name="diff" class="button">
<option value="0" {% if diff==0 %}selected{% endif %}>Level 1</option>
<option value="1" {% if diff==1 %}selected{% endif %}>Level 2</option>
<option value="2" {% if diff==2 %}selected{% endif %}>Level 3</option>
<option value="3" {% if diff==3 %}selected{% endif %}>Normal</option>
<option value="4" {% if diff==4 %}selected{% endif %}>Hard</option>
{% for difficulty in config["GAME_DIFFICULTIES"] %}
<option value="{{ difficulty }}" {% if diff==difficulty %}selected{% endif %}>
{{ config["GAME_DIFFICULTIES"][difficulty] }}
</option>
{% endfor %}
</select>
<select name="ver" class="button">
{% for game_version in config["GAME_VERSIONS"] %}
<option
value="{{ game_version }}"
{% if ver==game_version %}selected{% endif %}
>{{ game_version }}</option>
{% for version in config["GAME_VERSIONS"] %}
<option value="{{ version }}" {% if ver==version %}selected{% endif %}>
{{ config["GAME_VERSIONS"][version] }}
</option>
{% endfor %}
</select>
<span class="text-input">
<label for="search">Username</label>
<label for="search" style="min-width:auto">Username</label>
<input
type="text"
name="user"
@ -45,11 +44,8 @@
{% endif %}
<div class="other">
<h2>{{ user.username }}</h2>
<p>Joined {{ user.joined_at.strftime('%Y-%m-%d') }}</p>
<hr>
<p>Best score: {{ scores[0].score | format_result }}</p>
<p>Joined {{ user.joined_at|timesince }}</p>
</div>
</div>
{% endif %}
@ -58,7 +54,7 @@
<div class="table">
<table>
<tr>
<th></th>
<th>Pos</th>
<th>Name</th>
<th>Time Set</th>
<th>Submitted</th>
@ -74,14 +70,13 @@
{% else %}
<td>{{ loop.index }}</td>
{% endif %}
{% if score.users.id == current_user.id %}
<td class="player">{{ score.users.username }}</td>
{% else %}
<td>{{ score.users.username }}</td>
{% endif %}
<td>{{ score.score | format_result }}</td>
<td>
<a href="{{ url_for('views.index', user=score.users.username) }}"
{% if score.users.id == current_user.id %}id="you"{% endif %}>
{{ score.users.username }}
</a>
</td>
<td>{{ score.score|format_result }}</td>
<td>{{ score.scored_at.strftime('%Y-%m-%d') }}</td>
</tr>
{% endfor %}
@ -90,7 +85,7 @@
{% else %}
<div class="center-text">
<h2>No scores</h2>
<p>Go set some!</p>
<p>We searched far and wide, but nothing was found</p>
</div>
{% endif %}
{% endblock %}