diff --git a/TFR/Dockerfile b/TFR/Dockerfile index 840cc19..0f25da0 100644 --- a/TFR/Dockerfile +++ b/TFR/Dockerfile @@ -5,7 +5,7 @@ EXPOSE 8000 WORKDIR /data RUN mkdir /storage&& \ apk update && \ - apk add python3 py3-pip postgresql-client + apk --no-cache add python3 py3-pip postgresql-client COPY requirements.txt requirements.txt RUN pip install --no-cache-dir -r requirements.txt diff --git a/TFR/run.sh b/TFR/run.sh index a75e82e..353db65 100644 --- a/TFR/run.sh +++ b/TFR/run.sh @@ -1,15 +1,15 @@ #!/bin/sh # Wait for database to start -until pg_isready -d $DB_NAME -h $DB_HOST -U $DB_USER +until pg_isready -d "$DB_NAME" -h "$DB_HOST" -U "$DB_USER" do - echo "Waiting for database to start... (5s)" - sleep 5 + echo "Waiting for database to start... (3s)" + sleep 3 done echo "Database is ready!" -# Check if migrastions folder exists +# Check if migrations folder exists if [ ! -d "/data/storage/migrations" ]; then echo "Creating tables..." @@ -17,7 +17,7 @@ then fi # Check if there are any changes to the database -if ! $(flask --app server db check | grep -q "No changes in schema detected."); +if ! flask --app server db check | grep "No changes in schema detected."; then echo "Database changes detected! Migrating..." flask --app server db migrate diff --git a/TFR/server/account.py b/TFR/server/account.py index f94bdfb..457df68 100644 --- a/TFR/server/account.py +++ b/TFR/server/account.py @@ -21,160 +21,167 @@ from .extensions import db blueprint = Blueprint("account", __name__, url_prefix="/account") -@blueprint.route("/settings", methods=["GET", "POST"]) +@blueprint.route("/settings", methods=["GET"]) @login_required -def settings(): - if request.method == "POST": - username = request.form.get("username", "").strip() - email = request.form.get("email", "").strip() - password = request.form.get("password", "").strip() - error = [] +def get_settings(): + action = request.args.get("action", None) - user = Users.query.filter_by(username=current_user.username).first() + if action == "logout": + logout_user() + flash("Successfully logged out!", "success") + return redirect(url_for("views.index")) - if not check_password_hash(user.password, password): - flash("Password is incorrect!", "error") - return redirect(url_for("account.settings")) + sessions = Sessions.query.filter_by(user_id=current_user.id).all() + return render_template("views/account_settings.html", sessions=sessions) - if "file" in request.files and request.files["file"].filename: - picture = request.files["file"] - file_ext = picture.filename.split(".")[-1].lower() - file_name = f"{user.id}.{file_ext}" - if file_ext not in UPLOAD_EXTENSIONS: - error.append("Picture is not a valid image!") - if picture.content_length > UPLOAD_MAX_SIZE: - error.append( - f"Picture must be less than {UPLOAD_EXTENSIONS / 1000000}MB!" - ) +@blueprint.route("/settings", methods=["POST"]) +@login_required +def post_settings(): + username = request.form.get("username", "").strip() + email = request.form.get("email", "").strip() + password = request.form.get("password", "").strip() + error = [] - image = Image.open(picture.stream) + user = Users.query.filter_by(username=current_user.username).first() - # 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: - flash(err, "error") - return redirect(url_for("account.settings")) - - if user.picture: - os.remove(os.path.join(UPLOAD_DIR, user.picture)) - - user.picture = 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): - user.username = username - else: - error.append("Username is invalid!") - if email: - if USER_EMAIL_REGEX.match(email): - user.email = email - else: - error.append("Email is invalid!") - - if error: - for err in error: - flash(err, "error") - return redirect(url_for("account.settings")) - - db.session.commit() - - flash("Successfully updated account!", "success") + if not check_password_hash(user.password, password): + flash("Password is incorrect!", "error") return redirect(url_for("account.settings")) - else: - action = request.args.get("action", None) - if action == "logout": - logout_user() - flash("Successfully logged out!", "success") - return redirect(url_for("views.index")) + if "file" in request.files and request.files["file"].filename: + picture = request.files["file"] + file_ext = picture.filename.split(".")[-1].lower() + file_name = f"{user.id}.{file_ext}" - sessions = Sessions.query.filter_by(user_id=current_user.id).all() - return render_template("views/account_settings.html", sessions=sessions) + if file_ext not in UPLOAD_EXTENSIONS: + error.append("Picture is not a valid image!") + if picture.content_length > UPLOAD_MAX_SIZE: + error.append(f"Picture must be less than {UPLOAD_MAX_SIZE}MB!") + image = Image.open(picture.stream) -@blueprint.route("/reset-password", methods=["GET", "POST"]) -@login_required -def password_reset(): - if request.method == "POST": - current = request.form.get("current", "").strip() - new = request.form.get("new", "").strip() - confirm = request.form.get("confirm", "").strip() - error = [] - - user = Users.query.filter_by(username=current_user.username).first() - - if not current or not new or not confirm: - error.append("Please fill out all fields!") - if not check_password_hash(user.password, current): - error.append("Current password is incorrect!") - if len(new) < 8: - error.append( - "New password is too short! Must be at least 8 characters long." + # 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 new != confirm: - error.append("New passwords do not match!") if error: for err in error: flash(err, "error") - return redirect(url_for("account.password_reset")) + return redirect(url_for("account.settings")) - user.password = generate_password_hash(new, method="scrypt") - user.alt_id = str(uuid.uuid4()) - db.session.commit() + if user.picture: + os.remove(os.path.join(UPLOAD_DIR, user.picture)) - flash("Successfully changed password!", "success") - logout_user() - return redirect(url_for("auth.auth")) - else: - return render_template("views/reset_password.html") + user.picture = 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): + user.username = username + else: + error.append("Username is invalid!") + if email: + if USER_EMAIL_REGEX.match(email): + user.email = email + else: + error.append("Email is invalid!") + + if error: + for err in error: + flash(err, "error") + return redirect(url_for("account.settings")) + + db.session.commit() + + flash("Successfully updated account!", "success") + return redirect(url_for("account.settings")) -@blueprint.route("/delete-account", methods=["GET", "POST"]) +@blueprint.route("/password", methods=["GET"]) @login_required -def delete_account(): - if request.method == "POST": - username = request.form.get("username", "").strip() - password = request.form.get("password", "").strip() - error = [] +def get_password_reset(): + return render_template("views/reset_password.html") - user = Users.query.filter_by(username=current_user.username).first() - if username != user.username: - error.append("Username does not match!") - if not password: - error.append("Please fill out all fields!") - if not check_password_hash(user.password, password): - error.append("Password is incorrect!") +@blueprint.route("/password", methods=["POST"]) +@login_required +def post_password_reset(): + current = request.form.get("current", "").strip() + new = request.form.get("new", "").strip() + confirm = request.form.get("confirm", "").strip() + error = [] - if error: - for err in error: - flash(err, "error") - return redirect(url_for("account.delete_account")) + user = Users.query.filter_by(username=current_user.username).first() - 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() + if not current or not new or not confirm: + error.append("Please fill out all fields!") + if not check_password_hash(user.password, current): + error.append("Current password is incorrect!") + if len(new) < 8: + error.append( + "New password is too short! Must be at least 8 characters long." + ) + if new != confirm: + error.append("New passwords do not match!") - flash("Successfully deleted account!", "success") - logout_user() - return redirect(url_for("auth.auth")) - else: - return render_template("views/delete_account.html") + if error: + for err in error: + flash(err, "error") + return redirect(url_for("account.password_reset")) + + user.password = generate_password_hash(new, method="scrypt") + user.alt_id = str(uuid.uuid4()) + db.session.commit() + + flash("Successfully changed password!", "success") + logout_user() + return redirect(url_for("auth.auth")) + + +@blueprint.route("/delete-account", methods=["GET"]) +@login_required +def get_delete_account(): + return render_template("views/delete_account.html") + + +@blueprint.route("/delete", methods=["POST"]) +@login_required +def post_delete_account(): + username = request.form.get("username", "").strip() + password = request.form.get("password", "").strip() + error = [] + + user = Users.query.filter_by(username=current_user.username).first() + + if username != user.username: + error.append("Username does not match!") + if not password: + error.append("Please fill out all fields!") + if not check_password_hash(user.password, password): + error.append("Password is incorrect!") + + if error: + for err in error: + flash(err, "error") + return redirect(url_for("account.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() + + flash("Successfully deleted account!", "success") + logout_user() + return redirect(url_for("auth.auth")) \ No newline at end of file diff --git a/TFR/server/config.py b/TFR/server/config.py index 522dae0..9700d34 100644 --- a/TFR/server/config.py +++ b/TFR/server/config.py @@ -6,7 +6,7 @@ SECRET_KEY = getenv("FLASK_KEY") UPLOAD_DIR = "/data/uploads" UPLOAD_EXTENSIONS = ["png", "jpg", "jpeg", "gif", "webp"] -UPLOAD_RESOLUTION = 512 +UPLOAD_RESOLUTION = 169 UPLOAD_MAX_SIZE = 3 * 1024 * 1024 # 3MB GAME_VERSION = "alpha" diff --git a/TFR/server/static/js/search.js b/TFR/server/static/js/search.js index 70d782b..c2fd1c0 100644 --- a/TFR/server/static/js/search.js +++ b/TFR/server/static/js/search.js @@ -1,7 +1,7 @@ function showHint() { const search = document.querySelector('#search'); const searchPos = search.getBoundingClientRect(); - let hint = document.querySelector('.search-hint'); + const hint = document.querySelector('.search-hint'); hint.style.width = `${search.offsetWidth}px`; hint.style.left = `${searchPos.left}px`; @@ -12,7 +12,7 @@ function showHint() { function hideHint() { - let hint = document.querySelector('.search-hint'); + const hint = document.querySelector('.search-hint'); hint.style.display = 'none'; } @@ -20,7 +20,7 @@ function hideHint() { function updateHint() { const search = document.querySelector('#search'); const searchPos = search.getBoundingClientRect(); - let hint = document.querySelector('.search-hint'); + const hint = document.querySelector('.search-hint'); hint.style.width = `${search.offsetWidth}px`; hint.style.left = `${searchPos.left}px`; @@ -30,7 +30,7 @@ function updateHint() { function getSearch() { let search = document.querySelector('#search').value; - let hint = document.querySelector('.search-hint'); + const hint = document.querySelector('.search-hint'); if (search.length === 0) { hint.innerHTML = '
Start typing to see results...
'; @@ -68,7 +68,7 @@ function getSearch() { window.onload = () => { let typingTimer; - let search = document.querySelector('#search'); + const search = document.querySelector('#search'); if (search === null) { return; diff --git a/TFR/server/templates/navigation.html b/TFR/server/templates/navigation.html index e349325..ab14234 100644 --- a/TFR/server/templates/navigation.html +++ b/TFR/server/templates/navigation.html @@ -5,7 +5,7 @@ {% if current_user.is_authenticated %} - + {{ current_user.username }} {% if not current_user.email %}{% endif %} diff --git a/TFR/server/templates/views/account_settings.html b/TFR/server/templates/views/account_settings.html index 5a450fb..e92eda7 100644 --- a/TFR/server/templates/views/account_settings.html +++ b/TFR/server/templates/views/account_settings.html @@ -4,7 +4,7 @@ {% block content %}