Add missing GRID_SIZE option to example env and decrease to 15, add profile page with username and pattern change, add logout, fix clear buttons submitting, add Posts table for later use, prevent timing attacks during pattern equal check

This commit is contained in:
csd4ni3l
2025-10-23 15:51:22 +02:00
parent cc7f906000
commit 810b925387
9 changed files with 382 additions and 15 deletions

248
templates/profile.jinja2 Normal file
View File

@@ -0,0 +1,248 @@
{% extends "base.jinja2" %}
{% block title %}Loginween Profile{% endblock %}
{% block nav %}
<li class="nav-item">
<a class="nav-link" href="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/posts">Posts</a>
</li>
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="/profile">Profile</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/logout">Logout</a>
</li>
{% endblock %}
{% block body %}
<div class="container my-4">
<div class="card shadow-sm bg-dark text-light border-secondary">
<div class="card-body">
<h2 class="card-title">Profile Overview {% if not logged_in_account %} of {{ username}} {% endif %}</h2>
{% if logged_in_account %}
<p class="mb-1">Logged in as: {{ username }}</p>
{% endif %}
</div>
</div>
{% if logged_in_account %}
<div class="card shadow-sm mt-4 bg-dark text-light border-secondary">
<div class="card-body">
<h4>Change Username</h4>
<form method="POST" action="/change_username">
<div class="mb-3">
<label for="newusername" class="form-label">New Username</label>
<input type="text" class="form-control bg-secondary text-light" id="newusername" name="new_username" placeholder="Enter new username..." required>
</div>
<button type="submit" class="btn btn-primary">Update Username</button>
</form>
</div>
</div>
<div class="card shadow-sm mt-4 bg-dark text-light border-secondary">
<div class="card-body">
<h4>Change Pattern</h4>
<div class="mt-3"></div>
<form id="change_pattern_form" method="POST" action="/change_pattern">
<div class="mb-3 d-block">
<h5>Current</h5>
<input type="hidden" name="current_pattern" id="current_pattern_field">
<canvas id="current_pumpkin_canvas" width="600" height="600" class="my-3"></canvas>
<div class="buttons">
<button type="button" id="currentclearBtn" class="btn btn-danger">Clear</button>
</div>
</div>
<div class="mt-3"></div>
<div class="mb-3 d-block">
<h5>New</h5>
<input type="hidden" name="new_pattern" id="new_pattern_field">
<canvas id="new_pumpkin_canvas" width="600" height="600" class="my-3"></canvas>
<div class="buttons">
<button type="button" id="newclearBtn" class="btn btn-danger">Clear</button>
</div>
</div>
<button type="submit" class="btn btn-primary">Update Pattern</button>
</form>
</div>
</div>
<div class="card shadow-sm mt-4 border-danger bg-dark text-light">
<div class="card-body">
<h4 class="text-danger">Danger Zone</h4>
<p class="text-muted">These actions cannot be undone!</p>
<form method="POST" action="/delete_account" class="d-inline">
<button type="submit" class="btn btn-danger me-2" onclick="return confirm('Are you sure you want to delete your account?');">Delete Account</button>
</form>
</div>
</div>
{% endif %}
</div>
<script type="module">
const canvas = document.getElementById('current_pumpkin_canvas');
const ctx = canvas.getContext('2d');
const img = new Image();
img.src = '/static/pumpkin.png';
const GRID_SIZE = {{ grid_size }};
const CELL_SIZE = canvas.width / GRID_SIZE;
img.onload = () => {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
drawGrid();
};
let drawing = false;
let currentPattern = [];
let savedPattern = null;
canvas.addEventListener('mousedown', () => { drawing = true; });
canvas.addEventListener('mouseup', () => { drawing = false; });
canvas.addEventListener('mousemove', draw);
function draw(e) {
if (!drawing) return;
var rect = canvas.getBoundingClientRect();
var x = e.clientX - rect.left;
var y = e.clientY - rect.top;
var gridX = Math.floor(x / CELL_SIZE);
var gridY = Math.floor(y / CELL_SIZE);
var cellX = gridX * CELL_SIZE + CELL_SIZE / 3;
var cellY = gridY * CELL_SIZE + CELL_SIZE / 3;
var pixel = ctx.getImageData(cellX, cellY, 1, 1).data;
if (pixel[0] >= 254 && (pixel[1] >= 124 && pixel[1] <= 126)) {
var key = `${gridX},${gridY}`;
if (!currentPattern.includes(key)) {
currentPattern.push(key);
ctx.fillStyle = 'black';
ctx.fillRect(cellX - CELL_SIZE / 3, cellY - CELL_SIZE / 3, CELL_SIZE, CELL_SIZE);
}
}
}
function drawGrid() {
ctx.strokeStyle = 'rgba(0, 0, 0, 0.6)';
ctx.lineWidth = 1;
for (let i = 0; i <= GRID_SIZE; i++) {
const pos = i * CELL_SIZE;
ctx.beginPath();
ctx.moveTo(pos, 0);
ctx.lineTo(pos, canvas.height);
ctx.stroke();
ctx.beginPath()
ctx.moveTo(0, pos);
ctx.lineTo(canvas.width, pos);
ctx.stroke()
}
}
document.getElementById('currentclearBtn').addEventListener('click', clearCanvas);
function clearCanvas() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
drawGrid()
currentPattern = [];
}
document.getElementById("change_pattern_form").addEventListener('submit', function(event) {
document.getElementById('current_pattern_field').value = JSON.stringify(currentPattern);
});
</script>
<script type="module">
const canvas = document.getElementById('new_pumpkin_canvas');
const ctx = canvas.getContext('2d');
const img = new Image();
img.src = '/static/pumpkin.png';
const GRID_SIZE = {{ grid_size }};
const CELL_SIZE = canvas.width / GRID_SIZE;
img.onload = () => {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
drawGrid();
};
let drawing = false;
let currentPattern = [];
let savedPattern = null;
canvas.addEventListener('mousedown', () => { drawing = true; });
canvas.addEventListener('mouseup', () => { drawing = false; });
canvas.addEventListener('mousemove', draw);
function draw(e) {
if (!drawing) return;
var rect = canvas.getBoundingClientRect();
var x = e.clientX - rect.left;
var y = e.clientY - rect.top;
var gridX = Math.floor(x / CELL_SIZE);
var gridY = Math.floor(y / CELL_SIZE);
var cellX = gridX * CELL_SIZE + CELL_SIZE / 3;
var cellY = gridY * CELL_SIZE + CELL_SIZE / 3;
var pixel = ctx.getImageData(cellX, cellY, 1, 1).data;
if (pixel[0] >= 254 && (pixel[1] >= 124 && pixel[1] <= 126)) {
var key = `${gridX},${gridY}`;
if (!currentPattern.includes(key)) {
currentPattern.push(key);
ctx.fillStyle = 'black';
ctx.fillRect(cellX - CELL_SIZE / 3, cellY - CELL_SIZE / 3, CELL_SIZE, CELL_SIZE);
}
}
}
function drawGrid() {
ctx.strokeStyle = 'rgba(0, 0, 0, 0.6)';
ctx.lineWidth = 1;
for (let i = 0; i <= GRID_SIZE; i++) {
const pos = i * CELL_SIZE;
ctx.beginPath();
ctx.moveTo(pos, 0);
ctx.lineTo(pos, canvas.height);
ctx.stroke();
ctx.beginPath()
ctx.moveTo(0, pos);
ctx.lineTo(canvas.width, pos);
ctx.stroke()
}
}
document.getElementById('newclearBtn').addEventListener('click', clearCanvas);
function clearCanvas() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
drawGrid()
currentPattern = [];
}
document.getElementById("change_pattern_form").addEventListener('submit', function(event) {
document.getElementById('new_pattern_field').value = JSON.stringify(currentPattern);
});
</script>
{% endblock body %}