diff --git a/Caddy/Caddyfile b/Caddy/Caddyfile index b7a9dd1..b8b54af 100644 --- a/Caddy/Caddyfile +++ b/Caddy/Caddyfile @@ -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 diff --git a/TFR/server/account.py b/TFR/server/account.py index badb41b..fdd3235 100644 --- a/TFR/server/account.py +++ b/TFR/server/account.py @@ -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() diff --git a/TFR/server/api.py b/TFR/server/api.py index 8bcd790..edf645b 100644 --- a/TFR/server/api.py +++ b/TFR/server/api.py @@ -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, ) diff --git a/TFR/server/auth.py b/TFR/server/auth.py index e880801..0e6d548 100644 --- a/TFR/server/auth.py +++ b/TFR/server/auth.py @@ -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")) diff --git a/TFR/server/config.py b/TFR/server/config.py index 9700d34..d92a7bf 100644 --- a/TFR/server/config.py +++ b/TFR/server/config.py @@ -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" diff --git a/TFR/server/extensions.py b/TFR/server/extensions.py index 9916969..f1f8a30 100644 --- a/TFR/server/extensions.py +++ b/TFR/server/extensions.py @@ -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() diff --git a/TFR/server/models.py b/TFR/server/models.py index 70b6551..04d6485 100644 --- a/TFR/server/models.py +++ b/TFR/server/models.py @@ -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) diff --git a/TFR/server/static/js/search.js b/TFR/server/static/js/search.js index 4cc1638..bed45aa 100644 --- a/TFR/server/static/js/search.js +++ b/TFR/server/static/js/search.js @@ -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); diff --git a/TFR/server/static/sass/block.sass b/TFR/server/static/sass/block.sass index c40accd..7ff7126 100644 --- a/TFR/server/static/sass/block.sass +++ b/TFR/server/static/sass/block.sass @@ -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 diff --git a/TFR/server/static/sass/profile-settings.sass b/TFR/server/static/sass/profile-settings.sass index 4b613ac..4b1c0ca 100644 --- a/TFR/server/static/sass/profile-settings.sass +++ b/TFR/server/static/sass/profile-settings.sass @@ -4,7 +4,7 @@ gap: 1rem .picture - margin: 0 + margin: 0 0 auto 0 width: 10rem position: relative diff --git a/TFR/server/static/sass/style.sass b/TFR/server/static/sass/style.sass index 716e30b..ecaebfe 100644 --- a/TFR/server/static/sass/style.sass +++ b/TFR/server/static/sass/style.sass @@ -52,7 +52,7 @@ body margin: 0 auto padding: 0 - width: 800px + width: 900px min-height: 100vh position: relative diff --git a/TFR/server/templates/macros/input.html b/TFR/server/templates/macros/input.html index 1f44540..b81d14c 100644 --- a/TFR/server/templates/macros/input.html +++ b/TFR/server/templates/macros/input.html @@ -11,7 +11,7 @@ type="{{ type }}" name="{{ name }}" id="{{ id }}" - value="{{ value }}" + {% if value %}value="{{ value }}"{% endif %} {% if required %}required{% endif %} minlength="{{ minlength }}" > diff --git a/TFR/server/templates/views/account_settings.html b/TFR/server/templates/views/account_settings.html index e92eda7..a53646e 100644 --- a/TFR/server/templates/views/account_settings.html +++ b/TFR/server/templates/views/account_settings.html @@ -18,6 +18,9 @@
Devices and games that you logged into. If you're looking to log out all website users, reset your password instead.
diff --git a/TFR/server/templates/views/scores.html b/TFR/server/templates/views/scores.html index 3580bb3..51c9e7e 100644 --- a/TFR/server/templates/views/scores.html +++ b/TFR/server/templates/views/scores.html @@ -42,10 +42,23 @@ {% else %}