Make Posts id autoincrement, make working posts page on home which shows the pumpkins and comment, reduce bloat by making a lightbtn setup function, fix XSS

This commit is contained in:
csd4ni3l
2025-10-24 18:01:50 +02:00
parent ed274a24e4
commit ab44901dc0
4 changed files with 78 additions and 42 deletions

28
app.py
View File

@@ -1,10 +1,11 @@
from flask import Flask, Response, render_template, redirect, url_for, g, request from flask import Flask, Response, render_template, redirect, url_for, g, request, json
from flask_login import LoginManager, login_required from flask_login import LoginManager, login_required
from datetime import datetime
from pattern import Pattern from pattern import Pattern
import sqlite3, os, flask_login, dotenv, secrets import sqlite3, os, flask_login, dotenv, secrets, html
if os.path.exists(".env"): if os.path.exists(".env"):
dotenv.load_dotenv(".env") dotenv.load_dotenv(".env")
@@ -31,8 +32,8 @@ def get_db():
db.execute(""" db.execute("""
CREATE TABLE IF NOT EXISTS Posts ( CREATE TABLE IF NOT EXISTS Posts (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE, username TEXT NOT NULL,
comment TEXT NOT NULL, comment TEXT NOT NULL,
pattern TEXT NOT NULL, pattern TEXT NOT NULL,
creation_time INTEGER NOT NULL creation_time INTEGER NOT NULL
@@ -69,13 +70,23 @@ def main():
cur = get_db().cursor() cur = get_db().cursor()
cur.execute("SELECT * FROM Posts LIMIT 20") cur.execute("SELECT * FROM Posts LIMIT 15")
posts = cur.fetchall() posts = cur.fetchall()
cur.close() cur.close()
return render_template("index.jinja2", username=username, posts=posts) for post in posts:
if not isinstance(post[0], int):
print("Post ID is not int. Exiting for safety.")
return "Post ID is not int. Exiting for safety."
if not isinstance(post[4], int):
print("Post Timestamp is not int. Exiting for safety.")
return "Post Timestamp is not int. Exiting for safety."
new_posts = [[post[0], html.escape(post[1], quote=True), html.escape(post[2], quote=True), [html.escape(f"{pos[0]},{pos[1]}", quote=True) for pos in json.loads(post[3])], datetime.fromtimestamp(post[4]).strftime('%Y-%m-%d %H:%M:%S')] for post in posts]
return render_template("index.jinja2", username=username, posts=new_posts, grid_size=os.getenv("GRID_SIZE", 15))
@app.route("/countdown") @app.route("/countdown")
def countdown(): def countdown():
@@ -120,7 +131,10 @@ def register():
return render_template("register.jinja2", grid_size=os.getenv("GRID_SIZE", 15)) return render_template("register.jinja2", grid_size=os.getenv("GRID_SIZE", 15))
elif request.method == "POST": elif request.method == "POST":
username = request.form["username"] if request.form["username"] != html.escape(request.form["username"], quote=True):
return "No XSS please"
username = html.escape(request.form["username"], quote=True)
pattern = Pattern.from_str(request.form["pattern"]) pattern = Pattern.from_str(request.form["pattern"])
cur = get_db().cursor() cur = get_db().cursor()

View File

@@ -151,6 +151,23 @@ function unlight_pumpkin(ctx, cell_size, currentPattern) {
} }
} }
function setup_lightbtn(ctx, cell_size, lightbtn_id, pattern) {
let lit = { value: false };
document.getElementById(lightbtn_id).addEventListener('click', function(event) {
if (lit.value) {
lit.value = false;
unlight_pumpkin(ctx, cell_size, pattern);
}
else {
lit.value = true;
light_pumpkin(ctx, cell_size, pattern);
}
});
return lit;
}
function setup_pumpkin(canvas_id, clearbtn_id, lightbtn_id, form_id, pattern_field_id, grid_size, allow_drawing=true) { function setup_pumpkin(canvas_id, clearbtn_id, lightbtn_id, form_id, pattern_field_id, grid_size, allow_drawing=true) {
const canvas = document.getElementById(canvas_id); const canvas = document.getElementById(canvas_id);
const ctx = canvas.getContext('2d'); const ctx = canvas.getContext('2d');
@@ -160,29 +177,20 @@ function setup_pumpkin(canvas_id, clearbtn_id, lightbtn_id, form_id, pattern_fie
const CELL_SIZE = canvas.width / GRID_SIZE; const CELL_SIZE = canvas.width / GRID_SIZE;
let currentPattern = []; let currentPattern = [];
let lit = { value: false };
img.onload = () => { img.onload = () => {
clearCanvas(ctx, canvas, img, GRID_SIZE, currentPattern); clearCanvas(ctx, canvas, img, GRID_SIZE, currentPattern);
}; };
if (allow_drawing) { if (allow_drawing) {
let lit = false; lit = setup_lightbtn(ctx, CELL_SIZE, lightbtn_id, currentPattern);
document.getElementById(lightbtn_id).addEventListener('click', function(event) {
if (lit) {
lit = false;
unlight_pumpkin(ctx, CELL_SIZE, currentPattern);
}
else {
lit = true;
light_pumpkin(ctx, CELL_SIZE, currentPattern);
}
});
let drawing = false; let drawing = false;
canvas.addEventListener('mousedown', () => { drawing = true; }); canvas.addEventListener('mousedown', () => { drawing = true; });
canvas.addEventListener('mouseup', () => { drawing = false; }); canvas.addEventListener('mouseup', () => { drawing = false; });
canvas.addEventListener('mousemove', (e) => draw(e, ctx, CELL_SIZE, drawing, canvas, currentPattern, lit)); canvas.addEventListener('mousemove', (e) => draw(e, ctx, CELL_SIZE, drawing, canvas, currentPattern, lit.value));
canvas.addEventListener('click', (e) => {draw(e, ctx, CELL_SIZE, true, canvas, currentPattern, lit)}); canvas.addEventListener('click', (e) => {draw(e, ctx, CELL_SIZE, true, canvas, currentPattern, lit.value)});
document.getElementById(clearbtn_id).addEventListener('click', () => clearCanvas(ctx, canvas, img, GRID_SIZE, currentPattern)); document.getElementById(clearbtn_id).addEventListener('click', () => clearCanvas(ctx, canvas, img, GRID_SIZE, currentPattern));
document.getElementById(form_id).addEventListener('submit', function(event) { document.getElementById(form_id).addEventListener('submit', function(event) {

View File

@@ -90,18 +90,7 @@ img.addEventListener('load', function() {
setInterval(update, 1500); setInterval(update, 1500);
}); });
setup_lightbtn(ctx, CELL_SIZE, "lightBtn", pattern.slice(0, pattern.length - last_days));
let lit = false;
document.getElementById("lightBtn").addEventListener('click', function(event) {
if (lit) {
lit = false;
unlight_pumpkin(ctx, CELL_SIZE, pattern.slice(0, pattern.length - last_days));
}
else {
lit = true;
light_pumpkin(ctx, CELL_SIZE, pattern.slice(0, pattern.length - last_days));
}
});
</script> </script>
{% endblock body %} {% endblock body %}

View File

@@ -18,15 +18,40 @@
{% endblock %} {% endblock %}
{% block body %} {% block body %}
<div class="position-absolute top-50 start-50 translate-middle text-center"> <div class="text-center mt-3">
<h1>Posts</h1>
</div>
<div class="container mt-4">
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
{% for post in posts %} {% for post in posts %}
<div class="card" style="width: 18rem;"> <div class="col">
<canvas class="card-img-top"></canvas> <div class="card h-100">
<canvas id="pumpkin-canvas-{{ post.0 }}" width="600" height="600" class="card-img-top"></canvas>
<div class="card-body"> <div class="card-body">
<h5 class="card-title">{{ post.1 }}: {{ post.3 }}</h5> <h5 class="card-title">{{ post.2 }}</h5>
<p class="card-text">{{ post.2 }}</p> <p class="card-text">By: {{ post.1 }}<br>Created at: {{ post.4 }}</p>
<button type="button" id="lightBtn-{{ post.0 }}" class="btn btn-warning">Light!</button>
</div>
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
</div>
<script>
let CELL_SIZE = 0;
{% for post in posts %}
const [ctx_{{ post.0 }}, canvas_{{ post.0 }}, img_{{ post.0 }}] = setup_pumpkin("pumpkin-canvas-{{ post.0 }}", null, null, null, null, {{ grid_size }}, false);
let lit_{{ post.0 }} = false;
let pattern_{{ post.0 }} = {{ post.3 }};
CELL_SIZE = canvas_{{ post.0 }}.width / 15;
img_{{ post.0 }}.addEventListener('load', function() {
unlight_pumpkin(ctx_{{ post.0 }}, CELL_SIZE, pattern_{{ post.0 }});
});
setup_lightbtn(ctx_{{ post.0 }}, CELL_SIZE, "lightBtn-{{ post.0 }}", pattern_{{ post.0 }})
{% endfor %}
</script>
{% endblock body %} {% endblock body %}