mirror of
https://github.com/Fluffy-Bean/GameExpo23.git
synced 2025-05-14 14:22:16 +00:00
commit
7633c823e8
16 changed files with 215 additions and 49 deletions
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
gap: 1rem
|
||||
|
||||
.picture
|
||||
margin: 0
|
||||
margin: 0 0 auto 0
|
||||
width: 10rem
|
||||
|
||||
position: relative
|
||||
|
|
|
@ -52,7 +52,7 @@ body
|
|||
margin: 0 auto
|
||||
padding: 0
|
||||
|
||||
width: 800px
|
||||
width: 900px
|
||||
min-height: 100vh
|
||||
|
||||
position: relative
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
type="{{ type }}"
|
||||
name="{{ name }}"
|
||||
id="{{ id }}"
|
||||
value="{{ value }}"
|
||||
{% if value %}value="{{ value }}"{% endif %}
|
||||
{% if required %}required{% endif %}
|
||||
minlength="{{ minlength }}"
|
||||
>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -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}
|
||||
|
|
Loading…
Add table
Reference in a new issue