Merge pull request #19 from Fluffy-Bean/beta

Beta
This commit is contained in:
Michał Gdula 2023-06-26 14:20:29 +01:00 committed by GitHub
commit 7633c823e8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 215 additions and 49 deletions

View file

@ -1,4 +1,8 @@
{$THE_FRONT_ROOMS_DOMAIN}
reverse_proxy tfr:8000
encode gzip
reverse_proxy tfr:8000 {
header_up X-Forwarded-For {http.request.remote.addr}
header_up X-Real-IP {http.request.remote.addr}
}
encode gzip

View file

@ -1,5 +1,6 @@
import uuid
import os
import re
from PIL import Image
from flask import Blueprint, request, render_template, flash, redirect, url_for
@ -14,7 +15,7 @@ from .config import (
UPLOAD_DIR,
UPLOAD_RESOLUTION,
)
from .models import Users, Sessions, Scores, ProfileTags, PasswordReset
from .models import Users, Sessions, Scores
from .extensions import db
@ -38,16 +39,28 @@ def get_settings():
@blueprint.route("/settings", methods=["POST"])
@login_required
def post_settings():
# This is the worst part of this entire project
# This gotta go :sobbing:
username = request.form.get("username", "").strip()
email = request.form.get("email", "").strip()
password = request.form.get("password", "").strip()
discord = request.form.get("discord", "").strip()
twitter = request.form.get("twitter", "").strip()
twitch = request.form.get("twitch", "").strip()
youtube = request.form.get("youtube", "").strip()
twitter_regex = re.compile(r"^(?!.*\.\.)(?!.*\.$)[\w.]{1,15}$")
twitch_regex = re.compile("^(?=.{4,25}$)(?!_)(?!.*[_.]{2})[a-zA-Z0-9._]+$")
youtube_regex = re.compile("^(?!.*[._]{2})[a-zA-Z0-9._-]{1,50}$")
error = []
user = Users.query.filter_by(username=current_user.username).first()
if not check_password_hash(user.password, password):
flash("Password is incorrect!", "error")
return redirect(url_for("account.settings"))
return redirect(url_for("account.get_settings"))
if "file" in request.files and request.files["file"].filename:
picture = request.files["file"]
@ -96,15 +109,33 @@ def post_settings():
else:
error.append("Email is invalid!")
if discord:
user.discord = discord
if twitter:
if twitter_regex.match(twitter):
user.twitter = twitter
else:
error.append("Twitter is invalid!")
if twitch:
if twitch_regex.match(twitch):
user.twitch = twitch
else:
error.append("Twitch is invalid!")
if youtube:
if youtube_regex.match(youtube):
user.youtube = youtube
else:
error.append("YouTube is invalid!")
if error:
for err in error:
flash(err, "error")
return redirect(url_for("account.settings"))
return redirect(url_for("account.get_settings"))
db.session.commit()
flash("Successfully updated account!", "success")
return redirect(url_for("account.settings"))
return redirect(url_for("account.get_settings"))
@blueprint.route("/password", methods=["GET"])
@ -175,8 +206,6 @@ def post_delete_account():
db.session.query(Sessions).filter_by(user_id=current_user.id).delete()
db.session.query(Scores).filter_by(user_id=current_user.id).delete()
db.session.query(ProfileTags).filter_by(user_id=current_user.id).delete()
db.session.query(PasswordReset).filter_by(user_id=current_user.id).delete()
db.session.delete(user)
db.session.commit()

View file

@ -61,26 +61,26 @@ def post():
return "Score is not valid!"
try:
float(score)
int(difficulty)
score = float(score)
difficulty = int(difficulty)
except TypeError:
return "Invalid score and difficulty must be valid numbers!"
if int(difficulty) not in GAME_DIFFICULTIES:
if 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!"
if score < 10:
return "Score is impossible!"
session_data = Sessions.query.filter_by(auth_key=session_key).first()
if not session_data:
return "Authentication failed!"
score_upload = Scores(
score=float(score),
difficulty=int(difficulty),
score=score,
difficulty=difficulty,
version=version,
user_id=session_data.user_id,
)

View file

@ -77,4 +77,4 @@ def login():
login_user(user, remember=True)
flash("Successfully logged in!", "success")
return redirect(url_for("account.settings"))
return redirect(url_for("account.get_settings"))

View file

@ -39,3 +39,5 @@ port = 5432
SQLALCHEMY_DATABASE_URI = f"postgresql+psycopg2://{user}:{password}@{host}:{port}/{db}"
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_POOL_RECYCLE = 621
LOGS_DIR = "/data/logs"

View file

@ -1,9 +1,21 @@
import logging
from os import path
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_assets import Environment
from flask_caching import Cache
from flask_login import LoginManager
from .config import LOGS_DIR
logger = logging
logger.getLogger(path.join(LOGS_DIR, "server.log"))
logger.basicConfig(
level=logging.DEBUG,
format="%(asctime)s %(levelname)s %(name)s %(threadName)s : %(message)s",
)
db = SQLAlchemy()
migrate = Migrate()
assets = Environment()

View file

@ -55,32 +55,28 @@ class Sessions(db.Model):
)
class PasswordReset(db.Model):
class TagJunction(db.Model):
"""
Password reset table
Tag Junction table
"""
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey("users.id", use_alter=True))
reset_key = db.Column(db.String, nullable=False, unique=True)
created_at = db.Column(
db.DateTime,
nullable=False,
server_default=db.func.now(),
)
tag_id = db.Column(db.Integer, db.ForeignKey("tags.id", use_alter=True))
class ProfileTags(db.Model):
class Tags(db.Model):
"""
Profile Tags table
"""
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey("users.id", use_alter=True))
users = db.relationship("TagJunction", backref=db.backref("tags", lazy=True))
tag = db.Column(db.String, nullable=False)
icon = db.Column(db.String)
color = db.Column(db.String)
class Users(db.Model, UserMixin):
@ -98,6 +94,11 @@ class Users(db.Model, UserMixin):
email = db.Column(db.String)
password = db.Column(db.String, nullable=False)
discord = db.Column(db.String)
twitter = db.Column(db.String)
twitch = db.Column(db.String)
youtube = db.Column(db.String)
joined_at = db.Column(
db.DateTime,
nullable=False,
@ -106,8 +107,7 @@ class Users(db.Model, UserMixin):
scores = db.relationship("Scores", backref=db.backref("users", lazy=True))
tokens = db.relationship("Sessions", backref=db.backref("users", lazy=True))
reset = db.relationship("PasswordReset", backref=db.backref("users", lazy=True))
tags = db.relationship("ProfileTags", backref=db.backref("users", lazy=True))
tags = db.relationship("TagJunction", backref=db.backref("users", lazy=True))
def get_id(self):
return str(self.alt_id)

View file

@ -54,7 +54,8 @@ function getSearch() {
el.innerHTML = user;
el.onmousedown = function (event) {
event.preventDefault();
search = user.toString();
search = document.querySelector('#search');
search.value = user.toString();
search.blur();
}
hint.appendChild(el);

View file

@ -60,11 +60,6 @@
gap: 0.5rem
h2
margin: 0
font-size: 1.3rem
color: RGB($white)
p
margin: 0
font-size: 1rem
@ -76,11 +71,82 @@
border: 0 solid transparent
border-bottom: 1px solid RGBA($white, 0.1)
@media (max-width: 621px)
.account-block
flex-direction: column
.profile-title
display: flex
flex-direction: row
justify-content: space-between
gap: 0.5rem
> img
h2
margin: 0
font-size: 1.3rem
color: RGB($white)
.profile-tag
margin: auto 0
padding: 0.2rem 0.3rem
font-size: 0.8rem
color: RGB($black)
background-color: RGB($white)
border-radius: 2px
> img
width: 0.9rem
height: 0.9rem
margin-right: 0.2rem
.profile-links
display: flex
flex-direction: row
flex-wrap: wrap
gap: 0.5rem
> .discord, > .twitter, > .twitch, > .youtube
margin: 0
padding: 0.2rem 0.3rem
width: 2rem
height: 2rem
display: flex
flex-direction: row
align-items: center
justify-content: center
text-decoration: none
color: RGB($white)
border-radius: 2px
border: 0 solid transparent
> i
font-size: 1.3rem
&:hover
cursor: pointer
filter: brightness(1.2)
> .discord
background-color: #5865F2
> .twitter
background-color: #1DA1F2
> .twitch
background-color: #9146FF
> .youtube
background-color: #FF0000
color: RGB($white)
@media (max-width: 550px)
.account-block
// flex-direction: column
> img
margin: 0 auto
width: 15rem
height: 15rem
width: 5rem
height: 5rem

View file

@ -4,7 +4,7 @@
gap: 1rem
.picture
margin: 0
margin: 0 0 auto 0
width: 10rem
position: relative

View file

@ -52,7 +52,7 @@ body
margin: 0 auto
padding: 0
width: 800px
width: 900px
min-height: 100vh
position: relative

View file

@ -11,7 +11,7 @@
type="{{ type }}"
name="{{ name }}"
id="{{ id }}"
value="{{ value }}"
{% if value %}value="{{ value }}"{% endif %}
{% if required %}required{% endif %}
minlength="{{ minlength }}"
>

View file

@ -18,6 +18,9 @@
<div class="other">
{{ text(id="profile-username", name="username", value=current_user.username) }}
{{ text(id="profile-email", name="email") }}
<span style="height: 100%"></span>
{{ text(id="profile-password", name="password", type="password", required=True, minlength=8) }}
<span style="height: 100%"></span>
@ -28,6 +31,28 @@
</form>
</div>
<div class="block">
<h2 style="margin-bottom: 1rem;">Linked Socials</h2>
<form action="{{ url_for('account.post_settings') }}" method="POST" enctype="multipart/form-data">
<div class="profile-settings">
<div class="other">
{{ text(id="socials-discord", name="discord", value=current_user.discord) }}
{{ text(id="socials-twitter", name="twitter", value=current_user.twitter) }}
{{ text(id="socials-twitch", name="twitch", value=current_user.twitch) }}
{{ text(id="socials-youtube", name="youtube", value=current_user.youtube) }}
<span style="height: 100%"></span>
{{ text(id="socials-password", name="password", type="password", required=True, minlength=8) }}
<span style="height: 100%"></span>
<button type="submit" class="button primary">Save changes</button>
</div>
</div>
</form>
</div>
<div class="block">
<h2>Sessions</h2>
<p>Devices and games that you logged into. If you're looking to log out all website users, reset your password instead.</p>

View file

@ -42,10 +42,23 @@
{% else %}
<img src="{{ url_for('static', filename='images/pfp.png') }}" alt="Profile picture">
{% endif %}
<div class="other">
<h2>{{ user.username }}</h2>
<hr>
<div class="profile-title">
<h2>{{ user.username }}</h2>
{% for tag in tags %}<span class="profile-tag" style="background-color:{{ tag.color }};">{{ tag.tag }}</span>{% endfor %}
</div>
<p>Joined {{ user.joined_at|timesince }}</p>
<hr>
<div class="profile-links">
{% if user.discord %}<button class="discord"><i class="ph ph-discord-logo"></i></button>{% endif %}
{% if user.twitter %}<a href="https://twitter.com/{{ user.twitter }}" class="twitter"><i class="ph ph-twitter-logo"></i></a>{% endif %}
{% if user.twitch %}<a href="https://twitch.com/{{ user.twitch }}" class="twitch"><i class="ph ph-twitch-logo"></i></a>{% endif %}
{% if user.youtube %}<a href="https://youtube.com/{{ user.youtube }}" class="youtube"><i class="ph ph-youtube-logo"></i></a>{% endif %}
</div>
</div>
</div>
{% endif %}

View file

@ -1,7 +1,7 @@
from flask import Blueprint, request, render_template, abort
from sqlalchemy import func
from .models import Scores, Users
from .models import Scores, Users, TagJunction, Tags
from .config import GAME_VERSION, MAX_TOP_SCORES
from .extensions import db
@ -15,6 +15,7 @@ def index():
ver_arg = request.args.get("ver", GAME_VERSION).strip()
user_arg = request.args.get("user", "").strip()
user = None
tags = None
scores = db.session.query(Scores).filter_by(difficulty=diff_arg)
@ -33,6 +34,13 @@ def index():
)
else:
user = Users.query.filter_by(username=user_arg).first()
# get all tags from the junction table and add them to a list of tags
tags = (
db.session.query(Tags)
.join(TagJunction)
.filter(TagJunction.user_id == user.id)
.all()
)
if user:
scores = scores.filter_by(user_id=user.id)
else:
@ -41,7 +49,12 @@ def index():
scores = scores.order_by(Scores.score.asc()).limit(MAX_TOP_SCORES).all()
return render_template(
"views/scores.html", scores=scores, diff=int(diff_arg), ver=ver_arg, user=user
"views/scores.html",
scores=scores,
diff=int(diff_arg),
ver=ver_arg,
user=user,
tags=tags,
)

View file

@ -38,6 +38,7 @@ services:
volumes:
- ./TFR/storage/migrations:/data/migrations
- ./TFR/storage/uploads:/data/uploads
- ./TFR/storage/logs:/data/logs
environment:
FLASK_KEY: ${THE_FRONT_ROOMS_SECRETE_KEY}
DB_USER: ${POSTGRES_USER}