feat: Move error responses to new function in tools.py
All checks were successful
Build Docker / BuildImage (push) Successful in 1m6s

This commit is contained in:
2025-10-11 17:39:46 +11:00
parent 00d035a0e8
commit 22cd49a012
6 changed files with 94 additions and 490 deletions

View File

@@ -4,6 +4,7 @@ import datetime
import requests import requests
from mail import sendEmail from mail import sendEmail
from sol import create_transaction from sol import create_transaction
from tools import getClientIP
api_bp = Blueprint('api', __name__) api_bp = Blueprint('api', __name__)
@@ -17,14 +18,6 @@ if 'time-zone' not in ncConfig:
ncConfig['time-zone'] = 10 ncConfig['time-zone'] = 10
def getClientIP(request):
x_forwarded_for = request.headers.get("X-Forwarded-For")
if x_forwarded_for:
ip = x_forwarded_for.split(",")[0]
else:
ip = request.remote_addr
return ip
def getGitCommit(): def getGitCommit():
# if .git exists, get the latest commit hash # if .git exists, get the latest commit hash
if os.path.isdir(".git"): if os.path.isdir(".git"):

View File

@@ -22,8 +22,8 @@ from PIL import Image
from blueprints.now import now_bp from blueprints.now import now_bp
from blueprints.blog import blog_bp from blueprints.blog import blog_bp
from blueprints.wellknown import wk_bp from blueprints.wellknown import wk_bp
from blueprints.api import api_bp, getGitCommit, getClientIP from blueprints.api import api_bp, getGitCommit
from tools import isCurl, isCrawler, getAddress, getFilePath from tools import isCurl, isCrawler, getAddress, getFilePath, error_response, getClientIP
app = Flask(__name__) app = Flask(__name__)
CORS(app) CORS(app)
@@ -115,13 +115,15 @@ def asset_get(path):
if os.path.isfile("templates/assets/img/favicon/" + filename): if os.path.isfile("templates/assets/img/favicon/" + filename):
return send_from_directory("templates/assets/img/favicon", filename) return send_from_directory("templates/assets/img/favicon", filename)
return render_template("404.html"), 404 return error_response(request)
@app.route("/sitemap") @app.route("/sitemap")
@app.route("/sitemap.xml") @app.route("/sitemap.xml")
def sitemap_get(): def sitemap_get():
# Remove all .html from sitemap # Remove all .html from sitemap
if not os.path.isfile("templates/sitemap.xml"):
return error_response(request)
with open("templates/sitemap.xml") as file: with open("templates/sitemap.xml") as file:
sitemap = file.read() sitemap = file.read()
@@ -132,7 +134,7 @@ def sitemap_get():
@app.route("/favicon.<ext>") @app.route("/favicon.<ext>")
def favicon_get(ext): def favicon_get(ext):
if ext not in ("png", "svg", "ico"): if ext not in ("png", "svg", "ico"):
return render_template("404.html"), 404 return error_response(request)
return send_from_directory("templates/assets/img/favicon", f"favicon.{ext}") return send_from_directory("templates/assets/img/favicon", f"favicon.{ext}")
@@ -140,7 +142,7 @@ def favicon_get(ext):
def javascript_get(name): def javascript_get(name):
# Check if file in js directory # Check if file in js directory
if not os.path.isfile("templates/assets/js/" + request.path.split("/")[-1]): if not os.path.isfile("templates/assets/js/" + request.path.split("/")[-1]):
return render_template("404.html"), 404 return error_response(request)
return send_from_directory("templates/assets/js", request.path.split("/")[-1]) return send_from_directory("templates/assets/js", request.path.split("/")[-1])
# endregion # endregion
@@ -580,12 +582,13 @@ def supersecretpath_get():
@app.route("/download/<path:path>") @app.route("/download/<path:path>")
def download_get(path): def download_get(path):
if path not in DOWNLOAD_ROUTES: if path not in DOWNLOAD_ROUTES:
return render_template("404.html"), 404 return error_response(request, message="Invalid download")
# Check if file exists # Check if file exists
path = DOWNLOAD_ROUTES[path] path = DOWNLOAD_ROUTES[path]
if os.path.isfile(path): if os.path.isfile(path):
return send_file(path) return send_file(path)
return render_template("404.html"), 404
return error_response(request, message="File not found")
@app.route("/hosting/send-enquiry", methods=["POST"]) @app.route("/hosting/send-enquiry", methods=["POST"])
@@ -704,7 +707,7 @@ def resume_pdf_get():
# Check if file exists # Check if file exists
if os.path.isfile("data/resume.pdf"): if os.path.isfile("data/resume.pdf"):
return send_file("data/resume.pdf") return send_file("data/resume.pdf")
return render_template("404.html"), 404 return error_response(request, message="Resume not found")
# endregion # endregion
@@ -814,7 +817,7 @@ def catch_all_get(path: str):
HANDSHAKE_SCRIPTS = "" HANDSHAKE_SCRIPTS = ""
if path.lower().replace(".html", "") in RESTRICTED_ROUTES: if path.lower().replace(".html", "") in RESTRICTED_ROUTES:
return render_template("404.html"), 404 return error_response(request, message="Restricted route", code=403)
if path in REDIRECT_ROUTES: if path in REDIRECT_ROUTES:
return redirect(REDIRECT_ROUTES[path], code=302) return redirect(REDIRECT_ROUTES[path], code=302)
@@ -841,34 +844,11 @@ def catch_all_get(path: str):
if filename: if filename:
return send_file(filename) return send_file(filename)
if isCurl(request): return error_response(request)
return jsonify(
{
"status": 404,
"message": "Page not found",
"ip": getClientIP(request),
}
), 404
return render_template("404.html"), 404
# 404 catch all
@app.errorhandler(404) @app.errorhandler(404)
def not_found(e): def not_found(e):
if isCurl(request): return error_response(request)
return jsonify(
{
"status": 404,
"message": "Page not found",
"ip": getClientIP(request),
}
), 404
return render_template("404.html"), 404
# endregion
if __name__ == "__main__": if __name__ == "__main__":
app.run(debug=True, port=5000, host="127.0.0.1") app.run(debug=True, port=5000, host="127.0.0.1")

53
templates/403.html Normal file
View File

@@ -0,0 +1,53 @@
<!DOCTYPE html>
<html data-bs-theme="light" lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>Nathan.Woodburn/</title>
<meta name="theme-color" content="#000000">
<link rel="canonical" href="https://nathan.woodburn.au/403">
<meta property="og:url" content="https://nathan.woodburn.au/403">
<meta name="fediverse:creator" content="@nathanwoodburn@mastodon.woodburn.au">
<meta name="twitter:description" content="G'day, this is my personal website. You can find out about me or check out some of my projects.">
<meta property="og:title" content="Nathan.Woodburn/">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://nathan.woodburn.au/assets/img/profile.jpg">
<meta property="og:type" content="website">
<meta name="twitter:title" content="Nathan.Woodburn/">
<meta property="og:description" content="G'day, this is my personal website. You can find out about me or check out some of my projects.">
<meta name="description" content="G'day, this is my personal website. You can find out about me or check out some of my projects.">
<meta property="og:image" content="https://nathan.woodburn.au/assets/img/profile.jpg">
<link rel="apple-touch-icon" type="image/png" sizes="180x180" href="/assets/img/favicon/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="16x16" href="/assets/img/favicon/favicon-16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="/assets/img/favicon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="180x180" href="/assets/img/favicon/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="192x192" href="/assets/img/favicon/android-chrome-192x192.png">
<link rel="icon" type="image/png" sizes="512x512" href="/assets/img/favicon/android-chrome-512x512.png">
<link rel="stylesheet" href="/assets/bootstrap/css/bootstrap.min.css">
<link rel="manifest" href="/manifest.json" crossorigin="use-credentials">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic&amp;display=swap">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Cabin:700&amp;display=swap">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Anonymous+Pro&amp;display=swap">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&amp;display=swap">
<link rel="stylesheet" href="/assets/css/styles.min.css">
<link rel="stylesheet" href="/assets/css/404.min.css">
<link rel="stylesheet" href="/assets/css/brand-reveal.min.css">
<link rel="stylesheet" href="/assets/css/profile.min.css">
<link rel="stylesheet" href="/assets/css/Social-Icons.min.css">
<link rel="me" href="https://mastodon.woodburn.au/@nathanwoodburn" />
<script async src="https://umami.woodburn.au/script.js" data-website-id="6a55028e-aad3-481c-9a37-3e096ff75589"></script>
</head>
<body>
<p>HTTP:&nbsp;<span>403</span></p>
<div class="text-center">
<div class="text-start" style="display: inline-block;"><code><em>this_page</em>.<em>found</em>&nbsp;= true;</code><code><span>if</span>&nbsp;(<em>this_page</em>.<em>readable</em>) {<br><span class="tab-space"></span><b>return</b> <em>this_page</em>;<br>}&nbsp;<span>else</span> {<br><span class="tab-space"></span><b>alert</b>('<i>This page is not readable!</i>');<br>}</code></div>
</div>
<script src="/assets/bootstrap/js/bootstrap.min.js"></script>
<script src="/assets/js/script.min.js"></script>
<script src="/assets/js/grayscale.min.js"></script>
<script src="/assets/js/403.min.js"></script>
</body>
</html>

1
templates/assets/js/403.min.js vendored Normal file
View File

@@ -0,0 +1 @@
const trigger="s",secret="/supersecretpath",home="/";var isSecret=!1;function error_check(){return function(){isSecret?(alert("You found the secret path"),window.location=secret):(alert("This page is not readable!"))}}function type(e,t){var n=document.getElementsByTagName("code")[e].innerHTML.toString(),o=0;document.getElementsByTagName("code")[e].innerHTML="",setTimeout((function(){var t=setInterval((function(){o++,document.getElementsByTagName("code")[e].innerHTML=n.slice(0,o)+"|",o==n.length&&(clearInterval(t),document.getElementsByTagName("code")[e].innerHTML=n)}),10)}),t)}setTimeout(error_check(),5e3),document.addEventListener("keydown",(function(e){"s"==e.key&&(isSecret=!0)})),type(0,0),type(1,600),type(2,1300);

View File

@@ -1,446 +0,0 @@
;(function() {
"use strict";
// General
var canvas,
screen,
gameSize,
game;
// Assets
var invaderCanvas,
invaderMultiplier,
invaderSize = 20,
initialOffsetInvader,
invaderAttackRate,
invaderSpeed,
invaderSpawnDelay = 250;
// Counter
var i = 0,
kills = 0,
spawnDelayCounter = invaderSpawnDelay;
var invaderDownTimer;
// Text
var blocks = [
[3, 4, 8, 9, 10, 15, 16],
[2, 4, 7, 11, 14, 16],
[1, 4, 7, 11, 13, 16],
[1, 2, 3, 4, 5, 7, 11, 13, 14, 15, 16, 17],
[4, 7, 11, 16],
[4, 8, 9, 10, 16]
];
// Game Controller
// ---------------
var Game = function() {
this.level = -1;
this.lost = false;
this.player = new Player();
this.invaders = [];
this.invaderShots = [];
if (invaderDownTimer === undefined) {
invaderDownTimer = setInterval(function() {
for (i = 0; i < game.invaders.length; i++) game.invaders[i].move();
}, 1000 - (this.level * 1.8));
};
}
Game.prototype = {
update: function() {
// Next level
if (game.invaders.length === 0) {
spawnDelayCounter += 1;
if (spawnDelayCounter < invaderSpawnDelay) return;
this.level += 1;
invaderAttackRate -= 0.002;
invaderSpeed += 10;
game.invaders = createInvaders();
spawnDelayCounter = 0;
}
if (!this.lost) {
// Collision
game.player.projectile.forEach(function(projectile) {
game.invaders.forEach(function(invader) {
if (collides(projectile, invader)) {
invader.destroy();
projectile.active = false;
}
});
});
this.invaderShots.forEach(function(invaderShots) {
if (collides(invaderShots, game.player)) {
game.player.destroy();
}
});
for (i = 0; i < game.invaders.length; i++) game.invaders[i].update();
}
// Don't stop player & projectiles.. they look nice
game.player.update();
for (i = 0; i < game.invaderShots.length; i++) game.invaderShots[i].update();
this.invaders = game.invaders.filter(function(invader) {
return invader.active;
});
},
draw: function() {
if (this.lost) {
screen.fillStyle = "rgba(0, 0, 0, 0.03)";
screen.fillRect(0, 0, gameSize.width, gameSize.height);
screen.font = "55px Lucida Console";
screen.textAlign = "center";
screen.fillStyle = "white";
screen.fillText("You lost", gameSize.width / 2, gameSize.height / 2);
screen.font = "20px Lucida Console";
screen.fillText("Points: " + kills, gameSize.width / 2, gameSize.height / 2 + 30);
} else {
screen.clearRect(0, 0, gameSize.width, gameSize.height);
screen.font = "10px Lucida Console";
screen.textAlign = "right";
screen.fillText("Points: " + kills, gameSize.width, gameSize.height - 12);
}
screen.beginPath();
var i;
this.player.draw();
if (!this.lost)
for (i = 0; i < this.invaders.length; i++) this.invaders[i].draw();
for (i = 0; i < this.invaderShots.length; i++) this.invaderShots[i].draw();
screen.fill();
},
invadersBelow: function(invader) {
return this.invaders.filter(function(b) {
return Math.abs(invader.coordinates.x - b.coordinates.x) === 0 &&
b.coordinates.y > invader.coordinates.y;
}).length > 0;
}
};
// Invaders
// --------
var Invader = function(coordinates) {
this.active = true;
this.coordinates = coordinates;
this.size = {
width: invaderSize,
height: invaderSize
};
this.patrolX = 0;
this.speedX = invaderSpeed;
};
Invader.prototype = {
update: function() {
if (Math.random() > invaderAttackRate && !game.invadersBelow(this)) {
var projectile = new Projectile({
x: this.coordinates.x + this.size.width / 2,
y: this.coordinates.y + this.size.height - 5
}, {
x: 0,
y: 2
});
game.invaderShots.push(projectile);
}
},
draw: function() {
if (this.active) screen.drawImage(invaderCanvas, this.coordinates.x, this.coordinates.y);
},
move: function() {
if (this.patrolX < 0 || this.patrolX > 100) {
this.speedX = -this.speedX;
this.patrolX += this.speedX;
this.coordinates.y += this.size.height;
if (this.coordinates.y + this.size.height * 2 > gameSize.height) game.lost = true;
} else {
this.coordinates.x += this.speedX;
this.patrolX += this.speedX;
}
},
destroy: function() {
this.active = false;
kills += 1;
}
};
// Player
// ------
var Player = function() {
this.active = true;
this.size = {
width: 16,
height: 8
};
this.shooterHeat = -3;
this.coordinates = {
x: gameSize.width / 2 - (this.size.width / 2) | 0,
y: gameSize.height - this.size.height * 2
};
this.projectile = [];
this.keyboarder = new KeyController();
};
Player.prototype = {
update: function() {
for (var i = 0; i < this.projectile.length; i++) this.projectile[i].update();
this.projectile = this.projectile.filter(function(projectile) {
return projectile.active;
});
if (!this.active) return;
if (this.keyboarder.isDown(this.keyboarder.KEYS.LEFT) && this.coordinates.x > 0) this.coordinates.x -= 2;
else if (this.keyboarder.isDown(this.keyboarder.KEYS.RIGHT) && this.coordinates.x < gameSize.width - this.size.width) this.coordinates.x += 2;
if (this.keyboarder.isDown(this.keyboarder.KEYS.Space)) {
this.shooterHeat += 1;
if (this.shooterHeat < 0) {
var projectile = new Projectile({
x: this.coordinates.x + this.size.width / 2 - 1,
y: this.coordinates.y - 1
}, {
x: 0,
y: -7
});
this.projectile.push(projectile);
} else if (this.shooterHeat > 12) this.shooterHeat = -3;
} else {
this.shooterHeat = -3;
}
},
draw: function() {
if (this.active) {
screen.rect(this.coordinates.x, this.coordinates.y, this.size.width, this.size.height);
screen.rect(this.coordinates.x - 2, this.coordinates.y + 2, 20, 6);
screen.rect(this.coordinates.x + 6, this.coordinates.y - 4, 4, 4);
}
for (var i = 0; i < this.projectile.length; i++) this.projectile[i].draw();
},
destroy: function() {
this.active = false;
game.lost = true;
}
};
// Projectile
// ------
var Projectile = function(coordinates, velocity) {
this.active = true;
this.coordinates = coordinates;
this.size = {
width: 3,
height: 3
};
this.velocity = velocity;
};
Projectile.prototype = {
update: function() {
this.coordinates.x += this.velocity.x;
this.coordinates.y += this.velocity.y;
if (this.coordinates.y > gameSize.height || this.coordinates.y < 0) this.active = false;
},
draw: function() {
if (this.active) screen.rect(this.coordinates.x, this.coordinates.y, this.size.width, this.size.height);
}
};
// Keyboard input tracking
// -----------------------
var KeyController = function() {
this.KEYS = {
LEFT: 37,
RIGHT: 39,
Space: 32
};
var keyCode = [37, 39, 32];
var keyState = {};
var counter;
window.addEventListener('keydown', function(e) {
for (counter = 0; counter < keyCode.length; counter++)
if (keyCode[counter] == e.keyCode) {
keyState[e.keyCode] = true;
e.preventDefault();
}
});
window.addEventListener('keyup', function(e) {
for (counter = 0; counter < keyCode.length; counter++)
if (keyCode[counter] == e.keyCode) {
keyState[e.keyCode] = false;
e.preventDefault();
}
});
this.isDown = function(keyCode) {
return keyState[keyCode] === true;
};
};
// Other functions
// ---------------
function collides(a, b) {
return a.coordinates.x < b.coordinates.x + b.size.width &&
a.coordinates.x + a.size.width > b.coordinates.x &&
a.coordinates.y < b.coordinates.y + b.size.height &&
a.coordinates.y + a.size.height > b.coordinates.y;
}
function getPixelRow(rowRaw) {
var textRow = [],
placer = 0,
row = Math.floor(rowRaw / invaderMultiplier);
if (row >= blocks.length) return [];
for (var i = 0; i < blocks[row].length; i++) {
var tmpContent = blocks[row][i] * invaderMultiplier;
for (var j = 0; j < invaderMultiplier; j++) textRow[placer + j] = tmpContent + j;
placer += invaderMultiplier;
}
return textRow;
}
// Write Text
// -----------
function createInvaders() {
var invaders = [];
var i = blocks.length * invaderMultiplier;
while (i--) {
var j = getPixelRow(i);
for (var k = 0; k < j.length; k++) {
invaders.push(new Invader({
x: j[k] * invaderSize,
y: i * invaderSize
}));
}
}
return invaders;
}
// Start game
// ----------
window.addEventListener('load', function() {
var invaderAsset = new Image;
invaderAsset.onload = function() {
invaderCanvas = document.createElement('canvas');
invaderCanvas.width = invaderSize;
invaderCanvas.height = invaderSize;
invaderCanvas.getContext("2d").drawImage(invaderAsset, 0, 0);
// Game Creation
canvas = document.getElementById("space-invaders");
screen = canvas.getContext('2d');
initGameStart();
loop();
};
invaderAsset.src = "/assets/img/invader.gif";
});
window.addEventListener('resize', function() {
initGameStart();
});
document.getElementById('restart').addEventListener('click', function() {
initGameStart();
});
function initGameStart() {
if (window.innerWidth > 1200) {
screen.canvas.width = 1200;
screen.canvas.height = 500;
gameSize = {
width: 1200,
height: 500
};
invaderMultiplier = 3;
initialOffsetInvader = 420;
} else if (window.innerWidth > 800) {
screen.canvas.width = 900;
screen.canvas.height = 600;
gameSize = {
width: 900,
height: 600
};
invaderMultiplier = 2;
initialOffsetInvader = 280;
} else {
screen.canvas.width = 600;
screen.canvas.height = 300;
gameSize = {
width: 600,
height: 300
};
invaderMultiplier = 1;
initialOffsetInvader = 140;
}
kills = 0;
invaderAttackRate = 0.999;
invaderSpeed = 20;
spawnDelayCounter = invaderSpawnDelay;
game = new Game();
}
function loop() {
game.update();
game.draw();
requestAnimationFrame(loop);
}
})();

View File

@@ -1,7 +1,15 @@
from flask import Request from flask import Request, render_template, jsonify
import os import os
from functools import cache from functools import cache
def getClientIP(request):
x_forwarded_for = request.headers.get("X-Forwarded-For")
if x_forwarded_for:
ip = x_forwarded_for.split(",")[0]
else:
ip = request.remote_addr
return ip
def isCurl(request: Request) -> bool: def isCurl(request: Request) -> bool:
""" """
Check if the request is from curl Check if the request is from curl
@@ -48,4 +56,19 @@ def getAddress(coin: str) -> str:
def getFilePath(name, path): def getFilePath(name, path):
for root, dirs, files in os.walk(path): for root, dirs, files in os.walk(path):
if name in files: if name in files:
return os.path.join(root, name) return os.path.join(root, name)
def error_response(request: Request, message: str = "404 Not Found", code: int = 404):
if isCurl(request):
return jsonify(
{
"status": code,
"message": message,
"ip": getClientIP(request),
}
), code
# Check if <error code>.html exists in templates
if os.path.isfile(f"templates/{code}.html"):
return render_template(f"{code}.html"), code
return render_template("404.html"), code