Deepsource Python issues

This commit is contained in:
Michał Gdula 2023-06-24 21:47:13 +00:00
parent e0fc37d48a
commit 4d912d314c
10 changed files with 162 additions and 156 deletions

View file

@ -5,7 +5,7 @@ EXPOSE 8000
WORKDIR /data WORKDIR /data
RUN mkdir /storage&& \ RUN mkdir /storage&& \
apk update && \ apk update && \
apk add python3 py3-pip postgresql-client apk --no-cache add python3 py3-pip postgresql-client
COPY requirements.txt requirements.txt COPY requirements.txt requirements.txt
RUN pip install --no-cache-dir -r requirements.txt RUN pip install --no-cache-dir -r requirements.txt

View file

@ -1,15 +1,15 @@
#!/bin/sh #!/bin/sh
# Wait for database to start # 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 do
echo "Waiting for database to start... (5s)" echo "Waiting for database to start... (3s)"
sleep 5 sleep 3
done done
echo "Database is ready!" echo "Database is ready!"
# Check if migrastions folder exists # Check if migrations folder exists
if [ ! -d "/data/storage/migrations" ]; if [ ! -d "/data/storage/migrations" ];
then then
echo "Creating tables..." echo "Creating tables..."
@ -17,7 +17,7 @@ then
fi fi
# Check if there are any changes to the database # 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 then
echo "Database changes detected! Migrating..." echo "Database changes detected! Migrating..."
flask --app server db migrate flask --app server db migrate

View file

@ -21,160 +21,167 @@ from .extensions import db
blueprint = Blueprint("account", __name__, url_prefix="/account") blueprint = Blueprint("account", __name__, url_prefix="/account")
@blueprint.route("/settings", methods=["GET", "POST"]) @blueprint.route("/settings", methods=["GET"])
@login_required @login_required
def settings(): def get_settings():
if request.method == "POST": action = request.args.get("action", None)
username = request.form.get("username", "").strip()
email = request.form.get("email", "").strip()
password = request.form.get("password", "").strip()
error = []
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): sessions = Sessions.query.filter_by(user_id=current_user.id).all()
flash("Password is incorrect!", "error") return render_template("views/account_settings.html", sessions=sessions)
return redirect(url_for("account.settings"))
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: @blueprint.route("/settings", methods=["POST"])
error.append("Picture is not a valid image!") @login_required
if picture.content_length > UPLOAD_MAX_SIZE: def post_settings():
error.append( username = request.form.get("username", "").strip()
f"Picture must be less than {UPLOAD_EXTENSIONS / 1000000}MB!" 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 not check_password_hash(user.password, password):
if file_ext != "gif": flash("Password is incorrect!", "error")
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")
return redirect(url_for("account.settings")) return redirect(url_for("account.settings"))
else:
action = request.args.get("action", None)
if action == "logout": if "file" in request.files and request.files["file"].filename:
logout_user() picture = request.files["file"]
flash("Successfully logged out!", "success") file_ext = picture.filename.split(".")[-1].lower()
return redirect(url_for("views.index")) file_name = f"{user.id}.{file_ext}"
sessions = Sessions.query.filter_by(user_id=current_user.id).all() if file_ext not in UPLOAD_EXTENSIONS:
return render_template("views/account_settings.html", sessions=sessions) 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"]) # Resizing gifs is more work than it's worth
@login_required if file_ext != "gif":
def password_reset(): image_x, image_y = image.size
if request.method == "POST": image.thumbnail(
current = request.form.get("current", "").strip() (min(image_x, UPLOAD_RESOLUTION), min(image_y, UPLOAD_RESOLUTION))
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."
) )
if new != confirm:
error.append("New passwords do not match!")
if error: if error:
for err in error: for err in error:
flash(err, "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") if user.picture:
user.alt_id = str(uuid.uuid4()) os.remove(os.path.join(UPLOAD_DIR, user.picture))
db.session.commit()
flash("Successfully changed password!", "success") user.picture = file_name
logout_user()
return redirect(url_for("auth.auth")) if file_ext == "gif":
else: image.save(os.path.join(UPLOAD_DIR, file_name), save_all=True)
return render_template("views/reset_password.html") 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 @login_required
def delete_account(): def get_password_reset():
if request.method == "POST": return render_template("views/reset_password.html")
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: @blueprint.route("/password", methods=["POST"])
error.append("Username does not match!") @login_required
if not password: def post_password_reset():
error.append("Please fill out all fields!") current = request.form.get("current", "").strip()
if not check_password_hash(user.password, password): new = request.form.get("new", "").strip()
error.append("Password is incorrect!") confirm = request.form.get("confirm", "").strip()
error = []
if error: user = Users.query.filter_by(username=current_user.username).first()
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() if not current or not new or not confirm:
db.session.query(Scores).filter_by(user_id=current_user.id).delete() error.append("Please fill out all fields!")
db.session.query(ProfileTags).filter_by(user_id=current_user.id).delete() if not check_password_hash(user.password, current):
db.session.query(PasswordReset).filter_by(user_id=current_user.id).delete() error.append("Current password is incorrect!")
db.session.delete(user) if len(new) < 8:
db.session.commit() 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") if error:
logout_user() for err in error:
return redirect(url_for("auth.auth")) flash(err, "error")
else: return redirect(url_for("account.password_reset"))
return render_template("views/delete_account.html")
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"))

View file

@ -6,7 +6,7 @@ SECRET_KEY = getenv("FLASK_KEY")
UPLOAD_DIR = "/data/uploads" UPLOAD_DIR = "/data/uploads"
UPLOAD_EXTENSIONS = ["png", "jpg", "jpeg", "gif", "webp"] UPLOAD_EXTENSIONS = ["png", "jpg", "jpeg", "gif", "webp"]
UPLOAD_RESOLUTION = 512 UPLOAD_RESOLUTION = 169
UPLOAD_MAX_SIZE = 3 * 1024 * 1024 # 3MB UPLOAD_MAX_SIZE = 3 * 1024 * 1024 # 3MB
GAME_VERSION = "alpha" GAME_VERSION = "alpha"

View file

@ -1,7 +1,7 @@
function showHint() { function showHint() {
const search = document.querySelector('#search'); const search = document.querySelector('#search');
const searchPos = search.getBoundingClientRect(); const searchPos = search.getBoundingClientRect();
let hint = document.querySelector('.search-hint'); const hint = document.querySelector('.search-hint');
hint.style.width = `${search.offsetWidth}px`; hint.style.width = `${search.offsetWidth}px`;
hint.style.left = `${searchPos.left}px`; hint.style.left = `${searchPos.left}px`;
@ -12,7 +12,7 @@ function showHint() {
function hideHint() { function hideHint() {
let hint = document.querySelector('.search-hint'); const hint = document.querySelector('.search-hint');
hint.style.display = 'none'; hint.style.display = 'none';
} }
@ -20,7 +20,7 @@ function hideHint() {
function updateHint() { function updateHint() {
const search = document.querySelector('#search'); const search = document.querySelector('#search');
const searchPos = search.getBoundingClientRect(); const searchPos = search.getBoundingClientRect();
let hint = document.querySelector('.search-hint'); const hint = document.querySelector('.search-hint');
hint.style.width = `${search.offsetWidth}px`; hint.style.width = `${search.offsetWidth}px`;
hint.style.left = `${searchPos.left}px`; hint.style.left = `${searchPos.left}px`;
@ -30,7 +30,7 @@ function updateHint() {
function getSearch() { function getSearch() {
let search = document.querySelector('#search').value; let search = document.querySelector('#search').value;
let hint = document.querySelector('.search-hint'); const hint = document.querySelector('.search-hint');
if (search.length === 0) { if (search.length === 0) {
hint.innerHTML = '<p>Start typing to see results...</p>'; hint.innerHTML = '<p>Start typing to see results...</p>';
@ -68,7 +68,7 @@ function getSearch() {
window.onload = () => { window.onload = () => {
let typingTimer; let typingTimer;
let search = document.querySelector('#search'); const search = document.querySelector('#search');
if (search === null) { if (search === null) {
return; return;

View file

@ -5,7 +5,7 @@
<span class="spacer"></span> <span class="spacer"></span>
{% if current_user.is_authenticated %} {% if current_user.is_authenticated %}
<a href="{{ url_for('account.settings') }}" class="button primary"> <a href="{{ url_for('account.get_settings') }}" class="button primary">
{{ current_user.username }} {{ current_user.username }}
{% if not current_user.email %}<i class="ph ph-warning" style="margin-left: 0.2rem !important;"></i>{% endif %} {% if not current_user.email %}<i class="ph ph-warning" style="margin-left: 0.2rem !important;"></i>{% endif %}
</a> </a>

View file

@ -4,7 +4,7 @@
{% block content %} {% block content %}
<div class="block"> <div class="block">
<h2 style="margin-bottom: 1rem;">Profile Settings</h2> <h2 style="margin-bottom: 1rem;">Profile Settings</h2>
<form action="{{ url_for('account.settings') }}" method="POST" enctype="multipart/form-data"> <form action="{{ url_for('account.post_settings') }}" method="POST" enctype="multipart/form-data">
<div class="profile-settings"> <div class="profile-settings">
<div class="picture"> <div class="picture">
{% if current_user.picture %} {% if current_user.picture %}
@ -34,7 +34,7 @@
<div class="table"> <div class="table">
<table> <table>
<tr> <tr>
<th></th> <th>Options</th>
<th>Device</th> <th>Device</th>
<th>Created</th> <th>Created</th>
<th>Last Used</th> <th>Last Used</th>
@ -44,7 +44,7 @@
<td><button onclick="yeetSession({{ session.id }})" class="button secondary"><i class="ph ph-trash"></i></button></td> <td><button onclick="yeetSession({{ session.id }})" class="button secondary"><i class="ph ph-trash"></i></button></td>
<td>{{ session.device_type }}</td> <td>{{ session.device_type }}</td>
<td>{{ session.created_at.strftime('%Y-%m-%d') }}</td> <td>{{ session.created_at.strftime('%Y-%m-%d') }}</td>
<td>{{ session.last_used.strftime('%Y-%m-%d') }}</td> <td>{{ session.last_used|timesince }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
@ -54,9 +54,9 @@
<div class="block secondary"> <div class="block secondary">
<h2>Danger Zone</h2> <h2>Danger Zone</h2>
<p>Be careful!</p> <p>Be careful!</p>
<a href="{{ url_for('account.delete_account') }}" class="button secondary" style="margin-bottom: 0.5rem">Delete Account</a> <a href="{{ url_for('account.get_delete_account') }}" class="button secondary" style="margin-bottom: 0.5rem">Delete Account</a>
<a href="{{ url_for('account.password_reset') }}" class="button secondary" style="margin-bottom: 0.5rem">Reset Password</a> <a href="{{ url_for('account.get_password_reset') }}" class="button secondary" style="margin-bottom: 0.5rem">Reset Password</a>
<a href="{{ url_for('account.settings', action='logout') }}" class="button secondary">Logout</a> <a href="{{ url_for('account.get_settings', action='logout') }}" class="button secondary">Logout</a>
</div> </div>
@ -67,8 +67,7 @@
// Adjusted from https://stackoverflow.com/a/3814285/14885829 // Adjusted from https://stackoverflow.com/a/3814285/14885829
document.getElementById('profile-picture').onchange = (event) => { document.getElementById('profile-picture').onchange = (event) => {
let tgt = event.target || window.event.srcElement, let files = event.target.files;
files = tgt.files;
if (FileReader && files && files.length) { if (FileReader && files && files.length) {
let fr = new FileReader(); let fr = new FileReader();
@ -78,7 +77,7 @@
fr.readAsDataURL(files[0]); fr.readAsDataURL(files[0]);
} }
else { else {
addFlashMessage("Your browser could not show a preview of your profile picture!", "error") addFlashMessage("Your browser could not show a preview of your profile picture!", "error");
} }
} }
</script> </script>

View file

@ -8,7 +8,7 @@
Deleting your account will delete <span style="color: rgb(var(--secondary)) !important;">EVERYTHING</span> on your account, including <span style="color: rgb(var(--secondary)) !important;">ALL</span> your ever submitted scores. Deleting your account will delete <span style="color: rgb(var(--secondary)) !important;">EVERYTHING</span> on your account, including <span style="color: rgb(var(--secondary)) !important;">ALL</span> your ever submitted scores.
There is <span style="color: rgb(var(--secondary)) !important;">NO WAY</span> to recover your account from this, are you sure you want todo this? There is <span style="color: rgb(var(--secondary)) !important;">NO WAY</span> to recover your account from this, are you sure you want todo this?
</p> </p>
<form action="{{ url_for('account.delete_account') }}" method="POST"> <form action="{{ url_for('account.post_delete_account') }}" method="POST">
{{ text(id="username", name="username", required=True) }} {{ text(id="username", name="username", required=True) }}
{{ text(id="password", name="password", type="password", required=True) }} {{ text(id="password", name="password", type="password", required=True) }}
<button type="submit" class="button secondary">Delete account forever</button> <button type="submit" class="button secondary">Delete account forever</button>

View file

@ -5,7 +5,7 @@
<div class="block secondary"> <div class="block secondary">
<h2>Password Reset</h2> <h2>Password Reset</h2>
<p>Forgotten your current password? Go here [insert password reset tool link]</p> <p>Forgotten your current password? Go here [insert password reset tool link]</p>
<form action="{{ url_for('account.password_reset') }}" method="POST"> <form action="{{ url_for('account.post_password_reset') }}" method="POST">
{{ text(id="current-password", name="current", type="password", required=True) }} {{ text(id="current-password", name="current", type="password", required=True) }}
{{ text(id="new-password", name="new", type="password", required=True) }} {{ text(id="new-password", name="new", type="password", required=True) }}
{{ text(id="confirm-password", name="confirm", type="password", required=True) }} {{ text(id="confirm-password", name="confirm", type="password", required=True) }}

View file

@ -9,7 +9,7 @@ from .extensions import db
blueprint = Blueprint("views", __name__) blueprint = Blueprint("views", __name__)
@blueprint.route("/") @blueprint.route("/", methods=["GET"])
def index(): def index():
diff_arg = request.args.get("diff", 0) diff_arg = request.args.get("diff", 0)
ver_arg = request.args.get("ver", GAME_VERSION).strip() ver_arg = request.args.get("ver", GAME_VERSION).strip()
@ -45,6 +45,6 @@ def index():
) )
@blueprint.route("/about") @blueprint.route("/about", methods=["GET"])
def about(): def about():
return render_template("views/about.html") return render_template("views/about.html")