500&404 errors
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -1,2 +1,4 @@
 | 
				
			|||||||
.venv
 | 
					.venv
 | 
				
			||||||
.env
 | 
					.env
 | 
				
			||||||
 | 
					db.sqlite
 | 
				
			||||||
 | 
					flask_session
 | 
				
			||||||
							
								
								
									
										25
									
								
								dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					FROM python:alpine
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Set the working directory
 | 
				
			||||||
 | 
					WORKDIR /app
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Copy the requirements file into the container
 | 
				
			||||||
 | 
					COPY requirements.txt .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Install the required packages
 | 
				
			||||||
 | 
					RUN pip install --no-cache-dir -r requirements.txt
 | 
				
			||||||
 | 
					RUN pip install gunicorn
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Copy the rest of the application code into the container
 | 
				
			||||||
 | 
					COPY src src
 | 
				
			||||||
 | 
					COPY templates templates
 | 
				
			||||||
 | 
					COPY static static
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Expose the port the app runs on
 | 
				
			||||||
 | 
					EXPOSE 5000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Set environment variables
 | 
				
			||||||
 | 
					ENV FLASK_APP=main.py
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# run the application
 | 
				
			||||||
 | 
					ENTRYPOINT [ "gunicorn",  "-b", ":5000", "--access-logfile", "-", "--error-logfile", "-", "src.main:app" ]
 | 
				
			||||||
							
								
								
									
										4
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					python-dotenv
 | 
				
			||||||
 | 
					flask-session
 | 
				
			||||||
 | 
					requests
 | 
				
			||||||
 | 
					flask
 | 
				
			||||||
@@ -18,6 +18,8 @@ class Database:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def insert_snake(self, name, score):
 | 
					    def insert_snake(self, name, score):
 | 
				
			||||||
        old_score = self.get_snake_score(name)
 | 
					        old_score = self.get_snake_score(name)
 | 
				
			||||||
 | 
					        print(f"Old score for {name}: {old_score}")
 | 
				
			||||||
 | 
					        print(f"New score for {name}: {score}")
 | 
				
			||||||
        if old_score is not None and score <= old_score:
 | 
					        if old_score is not None and score <= old_score:
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
@@ -28,7 +30,7 @@ class Database:
 | 
				
			|||||||
        self.connection.commit()
 | 
					        self.connection.commit()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_snake_score(self, name):
 | 
					    def get_snake_score(self, name):
 | 
				
			||||||
        self.cursor.execute('SELECT score FROM snake WHERE name = ?', (name,))
 | 
					        self.cursor.execute('SELECT score FROM snake WHERE name = ? ORDER BY score DESC LIMIT 1', (name,))
 | 
				
			||||||
        result = self.cursor.fetchone()
 | 
					        result = self.cursor.fetchone()
 | 
				
			||||||
        return result[0] if result else None
 | 
					        return result[0] if result else None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										92
									
								
								src/main.py
									
									
									
									
									
								
							
							
						
						@@ -1,8 +1,12 @@
 | 
				
			|||||||
from flask import Flask, request, render_template
 | 
					from flask import Flask, request, render_template, send_from_directory
 | 
				
			||||||
from flask_session import Session
 | 
					from flask_session import Session
 | 
				
			||||||
from dotenv import load_dotenv
 | 
					from dotenv import load_dotenv
 | 
				
			||||||
from os import getenv as env
 | 
					from os import getenv as env
 | 
				
			||||||
import logging, database, requests
 | 
					import logging, requests
 | 
				
			||||||
 | 
					try:
 | 
				
			||||||
 | 
					    import src.database as database
 | 
				
			||||||
 | 
					except ImportError:
 | 
				
			||||||
 | 
					    import database
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
 | 
					logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
 | 
				
			||||||
@@ -11,8 +15,9 @@ load_dotenv()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
app = Flask(
 | 
					app = Flask(
 | 
				
			||||||
    __name__,
 | 
					    __name__,
 | 
				
			||||||
    template_folder=env('TEMPLATE_FOLDER', default='templates'),
 | 
					    template_folder=env('TEMPLATE_FOLDER', default='../templates'),
 | 
				
			||||||
    static_folder=env('STATIC_FOLDER', default='static'),
 | 
					    static_folder=env('STATIC_FOLDER', default='../static'),
 | 
				
			||||||
 | 
					    static_url_path=env('STATIC_URL_PATH', default='/static')
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
app.config["SESSION_PERMANENT"] = True
 | 
					app.config["SESSION_PERMANENT"] = True
 | 
				
			||||||
app.config["SESSION_TYPE"] = "filesystem"
 | 
					app.config["SESSION_TYPE"] = "filesystem"
 | 
				
			||||||
@@ -28,48 +33,73 @@ def index():
 | 
				
			|||||||
    return render_template('index.html')
 | 
					    return render_template('index.html')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@app.route('/snake/submit', methods=['POST'])
 | 
					@app.route('/robots.txt')
 | 
				
			||||||
def snake_submit():
 | 
					@app.route('/sitemap.xml')
 | 
				
			||||||
    data = request.json
 | 
					@app.route('/favicon.ico')
 | 
				
			||||||
    if not data or 'name' not in data or 'score' not in data:
 | 
					def web_stuffs():
 | 
				
			||||||
        logging.error("Invalid data received: %s", data)
 | 
					    return send_from_directory(
 | 
				
			||||||
        return {'error': 'Invalid data'}, 400
 | 
					        app.static_folder,
 | 
				
			||||||
 | 
					        request.path[1:],
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    name = data.get('name', '')
 | 
					
 | 
				
			||||||
    cap = data.get('cap', '')
 | 
					@app.route('/404')
 | 
				
			||||||
    score = data.get('score', -1)
 | 
					@app.errorhandler(404)
 | 
				
			||||||
 | 
					def not_found():
 | 
				
			||||||
 | 
					    unformatted_scores = db.get_snake_scores()
 | 
				
			||||||
 | 
					    scores = [{'position': i + 1, 'name': score[1], 'score': score[2]} for i, score in enumerate(unformatted_scores)]
 | 
				
			||||||
 | 
					    return render_template('404.html', scores=scores)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@app.route('/404/submit', methods=['POST'])
 | 
				
			||||||
 | 
					def snake_submit():
 | 
				
			||||||
 | 
					    unformatted_scores = db.get_snake_scores()
 | 
				
			||||||
 | 
					    scores = [{'position': i + 1, 'name': score[1], 'score': score[2]} for i, score in enumerate(unformatted_scores)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    data = request.form
 | 
				
			||||||
 | 
					    username = data.get('username', '').strip()
 | 
				
			||||||
 | 
					    score = data.get('snake-score', '').strip()
 | 
				
			||||||
 | 
					    token = data.get('cap-token', '').strip()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if not username or not score or not token:
 | 
				
			||||||
 | 
					        logging.error("Missing required fields: username=%s, score=%s, token=%s", username, score, token)
 | 
				
			||||||
 | 
					        return render_template('404.html', scores=scores, error='Missing required fields'), 400
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        score = int(score)
 | 
				
			||||||
 | 
					    except ValueError:
 | 
				
			||||||
 | 
					        logging.error("Invalid score value: %s", score)
 | 
				
			||||||
 | 
					        return render_template('404.html', scores=scores, error='Invalid score value'), 400
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if score <= 0 or score > 10000 or len(username) < 3 or len(username) > 20:
 | 
				
			||||||
 | 
					        logging.error("Invalid score or username length: score=%s, username=%s", score, username)
 | 
				
			||||||
 | 
					        return render_template('404.html', scores=scores, error='Invalid score or username length'), 400
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    cap_response = requests.post(
 | 
					    cap_response = requests.post(
 | 
				
			||||||
        env('CAP_VERIFY_URL', default='https://<instance_url>/<key_id>/siteverify'),
 | 
					        env('CAP_VERIFY_URL', default='https://<instance_url>/<key_id>/siteverify'),
 | 
				
			||||||
        json={
 | 
					        json={
 | 
				
			||||||
            'secret': env('CAP_SECRET', default=''),
 | 
					            'secret': env('CAP_SECRET', default=''),
 | 
				
			||||||
            'response': cap,
 | 
					            'response': token,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if cap_response.status_code != 200 or not cap_response.json().get('success', "false") != "true":
 | 
					    if cap_response.status_code != 200 or not cap_response.json().get('success', "false") != "true":
 | 
				
			||||||
        logging.error("Captcha verification failed: %s", cap_response.json())
 | 
					        logging.error("Captcha verification failed: %s", cap_response.json())
 | 
				
			||||||
        return {'error': 'Captcha verification failed'}, 400
 | 
					        return render_template('404.html', scores=scores, error='Captcha verification failed'), 400
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    if not isinstance(name, str) or not isinstance(score, int):
 | 
					    db.insert_snake(name=username, score=int(score))
 | 
				
			||||||
        logging.error("Invalid data types: name=%s, score=%s", type(name), type(score))
 | 
					    logging.info("Snake submitted: name=%s, score=%d", username, score)
 | 
				
			||||||
        return {'error': 'Invalid data types'}, 400
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if not name or score <= 0 or score > 10000:
 | 
					 | 
				
			||||||
        logging.error("Invalid name or score: name=%s, score=%s", name, score)
 | 
					 | 
				
			||||||
        return {'error': 'Invalid name or score'}, 400
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    db.insert_snake(name, score)
 | 
					 | 
				
			||||||
    logging.info("Snake submitted: name=%s, score=%d", name, score)
 | 
					 | 
				
			||||||
    return {'success': True, 'message': 'Snake submitted successfully'}, 200
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@app.errorhandler(404)
 | 
					 | 
				
			||||||
def page_not_found(e):
 | 
					 | 
				
			||||||
    logging.error("Page not found: %s", request.path)
 | 
					 | 
				
			||||||
    unformatted_scores = db.get_snake_scores()
 | 
					    unformatted_scores = db.get_snake_scores()
 | 
				
			||||||
    scores = [{'position': i + 1, 'name': score[1], 'score': score[2]} for i, score in enumerate(unformatted_scores)]
 | 
					    scores = [{'position': i + 1, 'name': score[1], 'score': score[2]} for i, score in enumerate(unformatted_scores)]
 | 
				
			||||||
    return render_template('404.html', scores=scores), 404
 | 
					    return render_template('404.html', scores=scores, success='Score submitted successfully!')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@app.route('/500')
 | 
				
			||||||
 | 
					@app.errorhandler(500)
 | 
				
			||||||
 | 
					def internal_error(error="An internal server error occurred."):
 | 
				
			||||||
 | 
					    logging.error("Internal server error: %s", error)
 | 
				
			||||||
 | 
					    return render_template('500.html'), 500
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == '__main__':
 | 
					if __name__ == '__main__':
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,151 +0,0 @@
 | 
				
			|||||||
const canvas = document.getElementById('snakeCanvas');
 | 
					 | 
				
			||||||
const ctx = canvas.getContext('2d');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const gridSize = 15;
 | 
					 | 
				
			||||||
const scale = 200;
 | 
					 | 
				
			||||||
canvas.width = gridSize * scale;
 | 
					 | 
				
			||||||
canvas.height = gridSize * scale;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let snake = [{ x: 10, y: 10 }];
 | 
					 | 
				
			||||||
let direction = { x: 0, y: 0 };
 | 
					 | 
				
			||||||
let food = { x: 5, y: 5 };
 | 
					 | 
				
			||||||
let score = 0;
 | 
					 | 
				
			||||||
let gameOver = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let token = null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function draw() {
 | 
					 | 
				
			||||||
    ctx.clearRect(0, 0, canvas.width, canvas.height);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // draw grid of checkerboard pattern
 | 
					 | 
				
			||||||
    for (let x = 0; x < gridSize; x++) {
 | 
					 | 
				
			||||||
        for (let y = 0; y < gridSize; y++) {
 | 
					 | 
				
			||||||
            ctx.fillStyle = (x + y) % 2 === 0 ? 'darkgray' : 'grey';
 | 
					 | 
				
			||||||
            ctx.fillRect(x * scale, y * scale, scale, scale);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Draw snake
 | 
					 | 
				
			||||||
    snake.forEach(segment => {
 | 
					 | 
				
			||||||
        ctx.fillStyle = (snake.indexOf(segment) % 2 === 0) ? 'green' : 'darkgreen';
 | 
					 | 
				
			||||||
        ctx.fillRect(segment.x * scale, segment.y * scale, scale, scale);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Draw food
 | 
					 | 
				
			||||||
    ctx.fillStyle = 'red';
 | 
					 | 
				
			||||||
    ctx.fillRect(food.x * scale, food.y * scale, scale, scale);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function update() {
 | 
					 | 
				
			||||||
    if (gameOver) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Move snake
 | 
					 | 
				
			||||||
    const head = { x: snake[0].x + direction.x, y: snake[0].y + direction.y };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Add new head
 | 
					 | 
				
			||||||
    snake.unshift(head);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Check for food collision
 | 
					 | 
				
			||||||
    if (head.x === food.x && head.y === food.y) {
 | 
					 | 
				
			||||||
        score += 10; // Increase score
 | 
					 | 
				
			||||||
        placeFood();
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        snake.pop(); // Remove tail if no food eaten
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Check for wall collision
 | 
					 | 
				
			||||||
    if (head.x < 0 || head.x >= gridSize || head.y < 0 || head.y >= gridSize) {
 | 
					 | 
				
			||||||
        gameOver = true;
 | 
					 | 
				
			||||||
        alert(`Game Over! Your score: ${score}`);
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Check for self collision
 | 
					 | 
				
			||||||
    for (let i = 1; i < snake.length; i++) {
 | 
					 | 
				
			||||||
        if (head.x === snake[i].x && head.y === snake[i].y) {
 | 
					 | 
				
			||||||
            gameOver = true;
 | 
					 | 
				
			||||||
            alert(`Game Over! Your score: ${score}`);
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function placeFood() {
 | 
					 | 
				
			||||||
    do {
 | 
					 | 
				
			||||||
        food.x = Math.floor(Math.random() * gridSize);
 | 
					 | 
				
			||||||
        food.y = Math.floor(Math.random() * gridSize);
 | 
					 | 
				
			||||||
    } while (snake.some(segment => segment.x === food.x && segment.y === food.y));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function changeDirection(event) {
 | 
					 | 
				
			||||||
    switch (event.key) {
 | 
					 | 
				
			||||||
        case 'w':
 | 
					 | 
				
			||||||
            if (direction.y === 0) direction = { x: 0, y: -1 };
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        case 's':
 | 
					 | 
				
			||||||
            if (direction.y === 0) direction = { x: 0, y: 1 };
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        case 'a':
 | 
					 | 
				
			||||||
            if (direction.x === 0) direction = { x: -1, y: 0 };
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        case 'd':
 | 
					 | 
				
			||||||
            if (direction.x === 0) direction = { x: 1, y: 0 };
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function gameLoop() {
 | 
					 | 
				
			||||||
    if (!gameOver) {
 | 
					 | 
				
			||||||
        update();
 | 
					 | 
				
			||||||
        draw();
 | 
					 | 
				
			||||||
        setTimeout(gameLoop, 100);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
document.addEventListener('keydown', changeDirection);
 | 
					 | 
				
			||||||
// Start the game loop
 | 
					 | 
				
			||||||
gameLoop();
 | 
					 | 
				
			||||||
// Initial draw
 | 
					 | 
				
			||||||
draw();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const widget = document.querySelector("#cap");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
widget.addEventListener("solve", function (e) {
 | 
					 | 
				
			||||||
    token = e.detail.token;
 | 
					 | 
				
			||||||
    console.log("Captcha solved, token:", token);
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Function to submit score to the server
 | 
					 | 
				
			||||||
document.getElementById("snakeForm").addEventListener('submit', function(event) {
 | 
					 | 
				
			||||||
    event.preventDefault();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!token) {
 | 
					 | 
				
			||||||
        alert('Please complete the CAPTCHA before submitting your score.');
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // post request to server with score
 | 
					 | 
				
			||||||
    fetch('/snake/submit', {
 | 
					 | 
				
			||||||
        method: 'POST',
 | 
					 | 
				
			||||||
        headers: {
 | 
					 | 
				
			||||||
            'Content-Type': 'application/json'
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        body: JSON.stringify({
 | 
					 | 
				
			||||||
            score: score,
 | 
					 | 
				
			||||||
            name: document.getElementById('name').value,
 | 
					 | 
				
			||||||
            cap: token
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
    }).then(response => {
 | 
					 | 
				
			||||||
        if (response.ok) {
 | 
					 | 
				
			||||||
            alert('Score submitted successfully!');
 | 
					 | 
				
			||||||
            window.location.reload();
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            alert('Failed to submit score, check console for details.');
 | 
					 | 
				
			||||||
            response.json().then(data => console.error(data));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }).catch(error => {
 | 
					 | 
				
			||||||
        console.error('Error submitting score:', error);
 | 
					 | 
				
			||||||
        alert('Error submitting score, check console for details.');
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
| 
		 Before Width: | Height: | Size: 743 KiB After Width: | Height: | Size: 743 KiB  | 
| 
		 Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB  | 
| 
		 Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB  | 
@@ -1,7 +1,8 @@
 | 
				
			|||||||
canvas#snakeCanvas {
 | 
					canvas#snakeCanvas {
 | 
				
			||||||
    padding: 15px;
 | 
					    margin: 15px;
 | 
				
			||||||
    width: 100%;
 | 
					 | 
				
			||||||
    box-sizing: border-box;
 | 
					    box-sizing: border-box;
 | 
				
			||||||
 | 
					    border: 2px solid var(--secondary-background-color);
 | 
				
			||||||
 | 
					    border-radius: 10px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
form {
 | 
					form {
 | 
				
			||||||
@@ -77,3 +78,36 @@ form button[type="submit"] {
 | 
				
			|||||||
#snakeLeaderboard li:nth-child(even) {
 | 
					#snakeLeaderboard li:nth-child(even) {
 | 
				
			||||||
    background-color: var(--secondary-background-color);
 | 
					    background-color: var(--secondary-background-color);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					dialog {
 | 
				
			||||||
 | 
					    width: 90%;
 | 
				
			||||||
 | 
					    max-width: 500px;
 | 
				
			||||||
 | 
					    padding: 20px;
 | 
				
			||||||
 | 
					    background-color: var(--background-color);
 | 
				
			||||||
 | 
					    color: var(--text-color);
 | 
				
			||||||
 | 
					    border: 2px solid var(--secondary-background-color);
 | 
				
			||||||
 | 
					    border-radius: 8px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					dialog button {
 | 
				
			||||||
 | 
					    padding: 10px;
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    box-sizing: border-box;
 | 
				
			||||||
 | 
					    border: 2px solid var(--secondary-background-color);
 | 
				
			||||||
 | 
					    border-radius: 6px;
 | 
				
			||||||
 | 
					    background-color: var(--secondary-background-color-but-slightly-transparent);
 | 
				
			||||||
 | 
					    color: var(--text-color);
 | 
				
			||||||
 | 
					    font-weight: bold;
 | 
				
			||||||
 | 
					    cursor: pointer;
 | 
				
			||||||
 | 
					    margin-top: 10px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					dialog h2 {
 | 
				
			||||||
 | 
					    margin: 0;
 | 
				
			||||||
 | 
					    font-size: 1.5rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					dialog p {
 | 
				
			||||||
 | 
					    margin: 0;
 | 
				
			||||||
 | 
					    font-size: 1rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										8
									
								
								static/css/500.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					.bluescreen {
 | 
				
			||||||
 | 
					    background-color: #0077D6;
 | 
				
			||||||
 | 
					    color: #fff;
 | 
				
			||||||
 | 
					    padding:40px;
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					    flex-direction: column;
 | 
				
			||||||
 | 
					    gap: 20px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								static/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 240 KiB  | 
							
								
								
									
										174
									
								
								static/js/snake.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,174 @@
 | 
				
			|||||||
 | 
					const canvas = document.getElementById('snakeCanvas');
 | 
				
			||||||
 | 
					const ctx = canvas.getContext('2d');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const gridSize = 20;
 | 
				
			||||||
 | 
					const tileSize = 100;
 | 
				
			||||||
 | 
					const snakeSize = 60;
 | 
				
			||||||
 | 
					const foodSize = 80;
 | 
				
			||||||
 | 
					canvas.width = gridSize * tileSize;
 | 
				
			||||||
 | 
					canvas.height = gridSize * tileSize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let snake = [{ x: 10, y: 10 }, { x: 10, y: 11 }, { x: 10, y: 12 }];
 | 
				
			||||||
 | 
					let direction = { x: 0, y: 0 };
 | 
				
			||||||
 | 
					let food = { x: Math.floor(Math.random() * gridSize), y: Math.floor(Math.random() * gridSize) };
 | 
				
			||||||
 | 
					let score = 0;
 | 
				
			||||||
 | 
					let gameOver = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function draw() {
 | 
				
			||||||
 | 
					    ctx.clearRect(0, 0, canvas.width, canvas.height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // draw grid of checkerboard pattern
 | 
				
			||||||
 | 
					    for (let x = 0; x < gridSize; x++) {
 | 
				
			||||||
 | 
					        for (let y = 0; y < gridSize; y++) {
 | 
				
			||||||
 | 
					            ctx.fillStyle = (x + y) % 2 === 0 ? 
 | 
				
			||||||
 | 
					            getComputedStyle(document.documentElement).getPropertyValue('--background-color') : 
 | 
				
			||||||
 | 
					            getComputedStyle(document.documentElement).getPropertyValue('--secondary-background-color');
 | 
				
			||||||
 | 
					            ctx.fillRect(x * tileSize, y * tileSize, tileSize, tileSize);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Draw snake
 | 
				
			||||||
 | 
					    snake.forEach(segment => {
 | 
				
			||||||
 | 
					        let nextVec = { x: 0, y: 0 };
 | 
				
			||||||
 | 
					        // if there is a segment after the current segment
 | 
				
			||||||
 | 
					        if (snake.indexOf(segment) < snake.length - 1) {
 | 
				
			||||||
 | 
					            const nextSegment = snake[snake.indexOf(segment) + 1];
 | 
				
			||||||
 | 
					            nextVec.x = nextSegment.x - segment.x;
 | 
				
			||||||
 | 
					            nextVec.y = nextSegment.y - segment.y;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ctx.fillStyle = getComputedStyle(document.documentElement).getPropertyValue('--primary-color');
 | 
				
			||||||
 | 
					        if (nextVec.x === 0 && nextVec.y === 0) {
 | 
				
			||||||
 | 
					            ctx.fillRect(
 | 
				
			||||||
 | 
					                segment.x * tileSize + (tileSize - snakeSize) / 2,
 | 
				
			||||||
 | 
					                segment.y * tileSize + (tileSize - snakeSize) / 2,
 | 
				
			||||||
 | 
					                snakeSize,
 | 
				
			||||||
 | 
					                snakeSize
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        } else if (nextVec.x > 0 || nextVec.y > 0) {
 | 
				
			||||||
 | 
					            ctx.fillRect(
 | 
				
			||||||
 | 
					                segment.x * tileSize + (tileSize - snakeSize) / 2,
 | 
				
			||||||
 | 
					                segment.y * tileSize + (tileSize - snakeSize) / 2,
 | 
				
			||||||
 | 
					                snakeSize + nextVec.x * (tileSize - snakeSize),
 | 
				
			||||||
 | 
					                snakeSize + nextVec.y * (tileSize - snakeSize)
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            ctx.fillRect(
 | 
				
			||||||
 | 
					                segment.x * tileSize + (tileSize - snakeSize) / 2 + nextVec.x * (tileSize - snakeSize),
 | 
				
			||||||
 | 
					                segment.y * tileSize + (tileSize - snakeSize) / 2 + nextVec.y * (tileSize - snakeSize),
 | 
				
			||||||
 | 
					                snakeSize + Math.abs(nextVec.x) * (tileSize - snakeSize),
 | 
				
			||||||
 | 
					                snakeSize + Math.abs(nextVec.y) * (tileSize - snakeSize)
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Draw food
 | 
				
			||||||
 | 
					    ctx.fillStyle = '#ff4d4d';
 | 
				
			||||||
 | 
					    ctx.fillRect(
 | 
				
			||||||
 | 
					        food.x * tileSize + (tileSize - foodSize) / 2,
 | 
				
			||||||
 | 
					        food.y * tileSize + (tileSize - foodSize) / 2,
 | 
				
			||||||
 | 
					        foodSize,
 | 
				
			||||||
 | 
					        foodSize
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function update() {
 | 
				
			||||||
 | 
					    if (gameOver) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Move snake
 | 
				
			||||||
 | 
					    const head = { x: snake[0].x + direction.x, y: snake[0].y + direction.y };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Add new head
 | 
				
			||||||
 | 
					    snake.unshift(head);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Check for food collision
 | 
				
			||||||
 | 
					    if (head.x === food.x && head.y === food.y) {
 | 
				
			||||||
 | 
					        score += 10; // Increase score
 | 
				
			||||||
 | 
					        placeFood();
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        snake.pop(); // Remove tail if no food eaten
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Check for wall collision
 | 
				
			||||||
 | 
					    if (head.x < 0 || head.x >= gridSize || head.y < 0 || head.y >= gridSize) {
 | 
				
			||||||
 | 
					        gameOver = true;
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Check for self collision
 | 
				
			||||||
 | 
					    for (let i = 1; i < snake.length; i++) {
 | 
				
			||||||
 | 
					        if (head.x === snake[i].x && head.y === snake[i].y) {
 | 
				
			||||||
 | 
					            gameOver = true;
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function placeFood() {
 | 
				
			||||||
 | 
					    do {
 | 
				
			||||||
 | 
					        food.x = Math.floor(Math.random() * gridSize);
 | 
				
			||||||
 | 
					        food.y = Math.floor(Math.random() * gridSize);
 | 
				
			||||||
 | 
					    } while (snake.some(segment => segment.x === food.x && segment.y === food.y));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function changeDirection(event) {
 | 
				
			||||||
 | 
					    switch (event.key) {
 | 
				
			||||||
 | 
					        case 'w':
 | 
				
			||||||
 | 
					            if (direction.y === 0) direction = { x: 0, y: -1 };
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case 's':
 | 
				
			||||||
 | 
					            if (direction.y === 0) direction = { x: 0, y: 1 };
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case 'a':
 | 
				
			||||||
 | 
					            if (direction.x === 0) direction = { x: -1, y: 0 };
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case 'd':
 | 
				
			||||||
 | 
					            if (direction.x === 0) direction = { x: 1, y: 0 };
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Menu to start the game
 | 
				
			||||||
 | 
					function menu() {
 | 
				
			||||||
 | 
					    ctx.clearRect(0, 0, canvas.width, canvas.height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ctx.fillStyle = getComputedStyle(document.documentElement).getPropertyValue('--background-color');
 | 
				
			||||||
 | 
					    ctx.fillRect(0, 0, canvas.width, canvas.height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ctx.textAlign = 'center';
 | 
				
			||||||
 | 
					    ctx.fillStyle = getComputedStyle(document.documentElement).getPropertyValue('--text-color');
 | 
				
			||||||
 | 
					    ctx.font = '200px Arial';
 | 
				
			||||||
 | 
					    ctx.fillText('Snake Game', canvas.width / 2, canvas.height / 2);
 | 
				
			||||||
 | 
					    ctx.font = '100px Arial';
 | 
				
			||||||
 | 
					    ctx.fillText('Press W/A/S/D to move', canvas.width / 2, canvas.height / 2 + 100);
 | 
				
			||||||
 | 
					    ctx.fillText('Click to start', canvas.width / 2, canvas.height / 2 + 200);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    canvas.addEventListener('click', startGame);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function gameLoop() {
 | 
				
			||||||
 | 
					    if (!gameOver) {
 | 
				
			||||||
 | 
					        update();
 | 
				
			||||||
 | 
					        draw();
 | 
				
			||||||
 | 
					        setTimeout(gameLoop, 100);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        document.removeEventListener('keydown', changeDirection);
 | 
				
			||||||
 | 
					        document.getElementById('snake-score').value = score;
 | 
				
			||||||
 | 
					        alert(`Game Over! Your score: ${score}`);
 | 
				
			||||||
 | 
					        menu();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function startGame() {
 | 
				
			||||||
 | 
					    snake = [{ x: 10, y: 10 }, { x: 10, y: 11 }, { x: 10, y: 12 }];
 | 
				
			||||||
 | 
					    direction = { x: 1, y: 0 };
 | 
				
			||||||
 | 
					    food = { x: Math.floor(Math.random() * gridSize), y: Math.floor(Math.random() * gridSize) };
 | 
				
			||||||
 | 
					    score = 0;
 | 
				
			||||||
 | 
					    gameOver = false;
 | 
				
			||||||
 | 
					    canvas.removeEventListener('click', startGame);
 | 
				
			||||||
 | 
					    document.addEventListener('keydown', changeDirection);
 | 
				
			||||||
 | 
					    gameLoop();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					menu();
 | 
				
			||||||
@@ -14,16 +14,17 @@
 | 
				
			|||||||
    <p>
 | 
					    <p>
 | 
				
			||||||
        It seems like the thing you are looking for is not here :[
 | 
					        It seems like the thing you are looking for is not here :[
 | 
				
			||||||
        <br><br>
 | 
					        <br><br>
 | 
				
			||||||
        while you're here, why not play some snake? (use wasd to move, not mobile compatable :<)
 | 
					        while you're here, why not play some snake?
 | 
				
			||||||
    </p>
 | 
					    </p>
 | 
				
			||||||
    <canvas id="snakeCanvas"></canvas>
 | 
					    <canvas id="snakeCanvas"></canvas>
 | 
				
			||||||
</section>
 | 
					</section>
 | 
				
			||||||
<section class="flex-row">
 | 
					<section class="flex-row">
 | 
				
			||||||
    <section class="min-width">
 | 
					    <section class="min-width">
 | 
				
			||||||
        <h2>Submit score</h2>
 | 
					        <h2>Submit score</h2>
 | 
				
			||||||
        <form id="snakeForm">
 | 
					        <form action="/404/submit" method="POST" id="snakeForm">
 | 
				
			||||||
            <input type="text" id="name" placeholder="Your name" required>
 | 
					            <input type="text" id="username" name="username" placeholder="Your name" required>
 | 
				
			||||||
            <cap-widget id="cap" data-cap-api-endpoint="https://cap.alfieking.dev/57d36430b9cb/api/"></cap-widget>
 | 
					            <cap-widget id="captcha" data-cap-api-endpoint="https://cap.alfieking.dev/57d36430b9cb/api/"></cap-widget>
 | 
				
			||||||
 | 
					            <input type="hidden" id="snake-score" name="snake-score" value="0">
 | 
				
			||||||
            <button type="submit" id="submit">Submit</button>
 | 
					            <button type="submit" id="submit">Submit</button>
 | 
				
			||||||
        </form>
 | 
					        </form>
 | 
				
			||||||
    </section>
 | 
					    </section>
 | 
				
			||||||
@@ -40,6 +41,19 @@
 | 
				
			|||||||
        </ul>
 | 
					        </ul>
 | 
				
			||||||
    </section>
 | 
					    </section>
 | 
				
			||||||
</section>
 | 
					</section>
 | 
				
			||||||
 | 
					{% if error %}
 | 
				
			||||||
 | 
					<dialog id="errorDialog">
 | 
				
			||||||
 | 
					    <h2>Error</h2>
 | 
				
			||||||
 | 
					    <p>{{ error }}</p>
 | 
				
			||||||
 | 
					    <button onclick="errorDialog.close()">Close</button>
 | 
				
			||||||
 | 
					</dialog>
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
					    const errorDialog = document.getElementById('errorDialog');
 | 
				
			||||||
 | 
					    if (errorDialog) {
 | 
				
			||||||
 | 
					        errorDialog.showModal();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					{% endif %}
 | 
				
			||||||
{% endblock %}
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% block scripts %}
 | 
					{% block scripts %}
 | 
				
			||||||
							
								
								
									
										17
									
								
								templates/500.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					{% extends "base.html" %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block title %}500 - Internal Server Error{% endblock %}
 | 
				
			||||||
 | 
					{% block description %}An unexpected error occurred on the server.{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block head %}
 | 
				
			||||||
 | 
					<link rel="stylesheet" href="/static/css/500.css">
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block content %}
 | 
				
			||||||
 | 
					<section class="bluescreen">
 | 
				
			||||||
 | 
					    <h1>:(</h1>
 | 
				
			||||||
 | 
					    <p>
 | 
				
			||||||
 | 
					        Oopsie Woopsie! Uwu We made a fucky wucky!! A wittle fucko boingo! The code monkeys at our headquarters are working VEWY HAWD to fix this!
 | 
				
			||||||
 | 
					    </p>
 | 
				
			||||||
 | 
					</section>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
@@ -5,7 +5,7 @@
 | 
				
			|||||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
					    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
				
			||||||
    <title>{% block title %}Alfie's basement{% endblock %}</title>
 | 
					    <title>{% block title %}Alfie's basement{% endblock %}</title>
 | 
				
			||||||
    <link rel="icon" href="/static/content/icon.webp">
 | 
					    <link rel="icon" href="/static/content/icon.webp">
 | 
				
			||||||
    <link rel="stylesheet" href="/static/css/index.css">
 | 
					    <link rel="stylesheet" href="/static/css/base.css">
 | 
				
			||||||
    <meta name="description" content="{% block description %}server backend survivor{% endblock %}">
 | 
					    <meta name="description" content="{% block description %}server backend survivor{% endblock %}">
 | 
				
			||||||
    <meta name="keywords" content="Alfie King, Alfie, King, Alfieking, Alfieking.dev, dev, server">
 | 
					    <meta name="keywords" content="Alfie King, Alfie, King, Alfieking, Alfieking.dev, dev, server">
 | 
				
			||||||
    <meta name="author" content="Alfie King">
 | 
					    <meta name="author" content="Alfie King">
 | 
				
			||||||
@@ -26,7 +26,7 @@
 | 
				
			|||||||
            <section>
 | 
					            <section>
 | 
				
			||||||
                <h1>Things to see :3</h1>
 | 
					                <h1>Things to see :3</h1>
 | 
				
			||||||
                <ul>
 | 
					                <ul>
 | 
				
			||||||
                    <li><a href="#Home" onclick="alert('ur already here :3')">Home</a></li>
 | 
					                    <li><a href="/">Home</a></li>
 | 
				
			||||||
                    <li><a href="https://git.alfieking.dev/acetheking987">Gitea</a></li>
 | 
					                    <li><a href="https://git.alfieking.dev/acetheking987">Gitea</a></li>
 | 
				
			||||||
                    <li><a href="https://www.last.fm/user/acetheking987">LastFm</a></li>
 | 
					                    <li><a href="https://www.last.fm/user/acetheking987">LastFm</a></li>
 | 
				
			||||||
                    <li><a href="https://prismic.alfieking.dev">Prismic</a></li>
 | 
					                    <li><a href="https://prismic.alfieking.dev">Prismic</a></li>
 | 
				
			||||||
@@ -35,6 +35,7 @@
 | 
				
			|||||||
                    <li><a href="https://www.youtube.com/@acetheking987">YouTube</a></li>
 | 
					                    <li><a href="https://www.youtube.com/@acetheking987">YouTube</a></li>
 | 
				
			||||||
                    <li><a href="https://acetheking987.tumblr.com/">Tumblr</a></li>
 | 
					                    <li><a href="https://acetheking987.tumblr.com/">Tumblr</a></li>
 | 
				
			||||||
                    <li><a href="https://www.reddit.com/user/acetheking987">Reddit</a></li>
 | 
					                    <li><a href="https://www.reddit.com/user/acetheking987">Reddit</a></li>
 | 
				
			||||||
 | 
					                    <li><a href="/404">404 >:3</a></li>
 | 
				
			||||||
                </ul>
 | 
					                </ul>
 | 
				
			||||||
            </section>
 | 
					            </section>
 | 
				
			||||||
        </nav>
 | 
					        </nav>
 | 
				
			||||||
@@ -60,7 +61,7 @@
 | 
				
			|||||||
        <section>
 | 
					        <section>
 | 
				
			||||||
            <pre class="vsmoltext">      |\      _,,,---,,_<br>ZZZzz /,`.-'`'    -.  ;-;;,_<br>     |,4-  ) )-,_. ,\ (  `'-'<br>    '---''(_/--'  `-'\_)</pre>
 | 
					            <pre class="vsmoltext">      |\      _,,,---,,_<br>ZZZzz /,`.-'`'    -.  ;-;;,_<br>     |,4-  ) )-,_. ,\ (  `'-'<br>    '---''(_/--'  `-'\_)</pre>
 | 
				
			||||||
        </section>
 | 
					        </section>
 | 
				
			||||||
        <img src="static/content/haj.gif" alt="haj" class="haj">
 | 
					        <img src="/static/content/haj.gif" alt="haj" class="haj">
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <main id="main">
 | 
					    <main id="main">
 | 
				
			||||||
        <header id="home">
 | 
					        <header id="home">
 | 
				
			||||||
@@ -92,6 +93,6 @@
 | 
				
			|||||||
        </section>
 | 
					        </section>
 | 
				
			||||||
    </main>
 | 
					    </main>
 | 
				
			||||||
    {% block scripts %}{% endblock %}
 | 
					    {% block scripts %}{% endblock %}
 | 
				
			||||||
    <script src="/static/js/index.js"></script>
 | 
					    <script src="/static/js/base.js"></script>
 | 
				
			||||||
</body>
 | 
					</body>
 | 
				
			||||||
</html>
 | 
					</html>
 | 
				
			||||||