From e175f68d254f71ca60d8d90067412fa99ebca1a0 Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Sun, 26 Oct 2025 15:13:36 +1100 Subject: [PATCH 1/4] feat: Add initial ascii art for curl connections --- curl.py | 121 ++++++++++++++++++++++++++++++++++++ server.py | 19 +++--- templates/contact.ascii | 14 +++++ templates/donate.ascii | 25 ++++++++ templates/donate_coin.ascii | 10 +++ templates/donate_more.ascii | 13 ++++ templates/favicon.ascii | 25 ++++++++ templates/header.ascii | 11 ++++ templates/index.ascii | 28 +++++++++ templates/projects.ascii | 7 +++ 10 files changed, 264 insertions(+), 9 deletions(-) create mode 100644 curl.py create mode 100644 templates/contact.ascii create mode 100644 templates/donate.ascii create mode 100644 templates/donate_coin.ascii create mode 100644 templates/donate_more.ascii create mode 100644 templates/favicon.ascii create mode 100644 templates/header.ascii create mode 100644 templates/index.ascii create mode 100644 templates/projects.ascii diff --git a/curl.py b/curl.py new file mode 100644 index 0000000..81dd50d --- /dev/null +++ b/curl.py @@ -0,0 +1,121 @@ +from flask import render_template +from tools import error_response, getAddress +import os +from functools import lru_cache +import requests + + +def clean_path(path:str): + path = path.strip("/ ").lower() + # Strip any .html extension + if path.endswith(".html"): + path = path[:-5] + + # If the path is empty, set it to "index" + if path == "": + path = "index" + return path + +@lru_cache(maxsize=1) +def get_header(): + with open("templates/header.ascii", "r") as f: + return f.read() + +@lru_cache(maxsize=1) +def get_current_project(): + git = requests.get( + "https://git.woodburn.au/api/v1/users/nathanwoodburn/activities/feeds?only-performed-by=true&limit=1", + headers={"Authorization": os.getenv("GIT_AUTH") if os.getenv("GIT_AUTH") else os.getenv("git_token")}, + ) + git = git.json() + git = git[0] + repo_name = git["repo"]["name"] + repo_name = repo_name.lower() + repo_description = git["repo"]["description"] + return f"{repo_name} - {repo_description}" + + +@lru_cache(maxsize=1) +def get_projects(): + projectsreq = requests.get( + "https://git.woodburn.au/api/v1/users/nathanwoodburn/repos" + ) + + projects = projectsreq.json() + + # Check for next page + pageNum = 1 + while 'rel="next"' in projectsreq.headers["link"]: + projectsreq = requests.get( + "https://git.woodburn.au/api/v1/users/nathanwoodburn/repos?page=" + + str(pageNum) + ) + projects += projectsreq.json() + pageNum += 1 + + # Sort by last updated + projectsList = sorted( + projects, key=lambda x: x["updated_at"], reverse=True) + projects = "" + projectNum = 0 + includedNames = [] + while len(includedNames) < 5 and projectNum < len(projectsList): + # Avoid duplicates + if projectsList[projectNum]["name"] in includedNames: + projectNum += 1 + continue + includedNames.append(projectsList[projectNum]["name"]) + project = projectsList[projectNum] + projects += f"""{project['name']} - {project['description'] if project['description'] else 'No description'} +{project['html_url']} + +""" + projectNum += 1 + + return projects + +def curl_response(request): + # Check if .ascii exists + path = clean_path(request.path) + + # Handle special cases + if path == "index": + # Get current project + return render_template("index.ascii",header=get_header(),repo=get_current_project()), 200, {'Content-Type': 'text/plain; charset=utf-8'} + if path == "projects": + # Get projects + return render_template("projects.ascii",header=get_header(),projects=get_projects()), 200, {'Content-Type': 'text/plain; charset=utf-8'} + + if path == "donate": + # Get donation info + return render_template("donate.ascii",header=get_header(), + HNS=getAddress("HNS"), BTC=getAddress("BTC"), + SOL=getAddress("SOL"), ETH=getAddress("ETH") + ), 200, {'Content-Type': 'text/plain; charset=utf-8'} + + if path == "donate/more": + coinList = os.listdir(".well-known/wallets") + coinList = [file for file in coinList if file[0] != "."] + coinList.sort() + return render_template("donate_more.ascii",header=get_header(), + coins=coinList + ), 200, {'Content-Type': 'text/plain; charset=utf-8'} + + + + # For other donation pages, fall back to ascii if it exists + if path.startswith("donate/"): + coin = path.split("/")[1] + address = getAddress(coin) + if address != "": + return render_template("donate_coin.ascii",header=get_header(),coin=coin.upper(),address=address), 200, {'Content-Type': 'text/plain; charset=utf-8'} + + + if os.path.exists(f"templates/{path}.ascii"): + return render_template(f"{path}.ascii",header=get_header()), 200, {'Content-Type': 'text/plain; charset=utf-8'} + + # Fallback to html if it exists + if os.path.exists(f"templates/{path}.html"): + return render_template(f"{path}.html") + + return error_response(request) \ No newline at end of file diff --git a/server.py b/server.py index 37ced1c..87c568a 100644 --- a/server.py +++ b/server.py @@ -25,7 +25,8 @@ from blueprints.wellknown import wk_bp from blueprints.api import api_bp from blueprints.podcast import podcast_bp from blueprints.acme import acme_bp -from tools import isCurl, isCrawler, getAddress, getFilePath, error_response, getClientIP, json_response, getGitCommit, isDev, getHandshakeScript, get_tools_data +from tools import isCurl, isCrawler, getAddress, getFilePath, error_response, getClientIP, json_response, getHandshakeScript, get_tools_data +from curl import curl_response app = Flask(__name__) CORS(app) @@ -242,14 +243,7 @@ def index(): if request.args.get("load"): loaded = False if isCurl(request): - return jsonify( - { - "message": "Welcome to Nathan.Woodburn/! This is a personal website. For more information, visit https://nathan.woodburn.au", - "ip": getClientIP(request), - "dev": isDev(request.host), - "version": getGitCommit() - } - ) + return curl_response(request) if not loaded and not isCrawler(request): # Set cookie @@ -401,6 +395,9 @@ def index(): # region Donate @app.route("/donate") def donate(): + if isCurl(request): + return curl_response(request) + coinList = os.listdir(".well-known/wallets") coinList = [file for file in coinList if file[0] != "."] coinList.sort() @@ -702,6 +699,10 @@ def catch_all(path: str): if path.lower().replace(".html", "") in RESTRICTED_ROUTES: return error_response(request, message="Restricted route", code=403) + # If curl request, return curl response + if isCurl(request): + return curl_response(request) + if path in REDIRECT_ROUTES: return redirect(REDIRECT_ROUTES[path], code=302) diff --git a/templates/contact.ascii b/templates/contact.ascii new file mode 100644 index 0000000..9264a45 --- /dev/null +++ b/templates/contact.ascii @@ -0,0 +1,14 @@ +{{header}} +─────────────────────────────────────────────── + CONTACT ME  +──────────── + +Here are my socials — I’m most active on Discord 💬 + +- Twitter: https://twitter.com/woodburn_nathan +- GitHub: https://github.com/Nathanwoodburn +- Email: mailto:about@nathan.woodburn.au +- Discord: https://l.woodburn.au/discord +- Mastodon: https://mastodon.woodburn.au/@nathanwoodburn +- YouTube: https://www.youtube.com/@nathanjwoodburn + diff --git a/templates/donate.ascii b/templates/donate.ascii new file mode 100644 index 0000000..cf8b6a3 --- /dev/null +++ b/templates/donate.ascii @@ -0,0 +1,25 @@ +{{header}} +─────────────────────────────────────────────── + DONATE  +──────── + +If you’d like to support my work 💙 + +- PayPal: [https://paypal.me/nathanwoodburn] +- GitHub: [https://github.com/sponsors/Nathanwoodburn] +- Stripe: [https://donate.stripe.com/8wM6pv0VD08Xe408ww] + +HNS: nathan.woodburn +{{ HNS }} + +BTC: thinbadger6@primal.net +{{ BTC }} + +SOL: woodburn.sol +{{ SOL }} + +ETH: woodburn.au +{{ ETH }} + +More donation options → [/donate/more] + diff --git a/templates/donate_coin.ascii b/templates/donate_coin.ascii new file mode 100644 index 0000000..05c846c --- /dev/null +++ b/templates/donate_coin.ascii @@ -0,0 +1,10 @@ +{{header}} +─────────────────────────────────────────────── + DONATE  +──────── + +Here is my {{ coin }} address if you'd like to send a donation 💙 +{{ address }} + +Thank you for your support! 🙏 + diff --git a/templates/donate_more.ascii b/templates/donate_more.ascii new file mode 100644 index 0000000..d030734 --- /dev/null +++ b/templates/donate_more.ascii @@ -0,0 +1,13 @@ +{{header}} +─────────────────────────────────────────────── + DONATE  +──────── + +Here is a list of additional cryptocurrencies and donation methods 💙 +For each coin below, you can get the address from /donate/ + +{% for coin in coins %}{% if loop.index0 % 4 == 0 and loop.index0 != 0 %} +{% endif %}{{ coin }}{% if not loop.last %}, {% endif %}{% endfor %} + +Thank you for your support! 🙏 + diff --git a/templates/favicon.ascii b/templates/favicon.ascii new file mode 100644 index 0000000..f09df9a --- /dev/null +++ b/templates/favicon.ascii @@ -0,0 +1,25 @@ +▒▒▒ ▓▓▓ +▒░░░░▒▓ ▓▓▓▓▓▓▓ +▒░░░░░░▒▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓ +▒░░░░░▒▒▒▒▒▒▒ ▓▓▒▓▓▓▓▓▓▓▓▓▓ +▒░░░▒▒▒▒▒▒▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓ +▒░░▒▒▒▒▒▒▒▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓ +▒▒▒▒▒▒▒▒▒▒▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓ +▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒▒ ▒▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓ +▒▒▒▒▒▒▒▒▒▒▒▒▒ ▓▒▒▒▒ ▒▒▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓ +▒▒▒▒▒▒▒▒▒▒▒▒▒ ▓▒▒▒▒▒▒ ▒▒▒▒▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓ +▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒ ▒▒▒▒▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓ +▒▒▒▒▒▒▒▒▒▒▒▒▒ ▓▒▒▒▒▒▒▒▒▒ ▒▒▒▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓ +▒▒▒▒▒▒▒▒▒▒▒▒▒ ▓▒▒▒▒▒▒▒▒▒▒▒ ▒▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓ +▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█ + ▓▒▒▒▒▒▒▒▒▒▒▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + ▓▓▒▒▒▒▓▓▓ ▓▓▓▓▓▓▓▓█ + ▓▓▓▓ ▓▓▓█ + diff --git a/templates/header.ascii b/templates/header.ascii new file mode 100644 index 0000000..f50ed64 --- /dev/null +++ b/templates/header.ascii @@ -0,0 +1,11 @@ +───────────────────────────────────────────────────── + . . , . . . .. /  + |\ | _.-+-|_ _.._ | | _ _ _||_ . .._.._ /  + | \|(_] | [ )(_][ ) * |/\|(_)(_)(_][_)(_|[ [ )/  +───────────────────────────────────────────────────── + +Home [/] +Contact [/contact] +Projects [/projects] +Donate [/donate] + diff --git a/templates/index.ascii b/templates/index.ascii new file mode 100644 index 0000000..00c193b --- /dev/null +++ b/templates/index.ascii @@ -0,0 +1,28 @@ +{{header}} +─────────────────────────────────────────────── + ABOUT ME  +────────── + +Hi, I'm Nathan Woodburn from Canberra, Australia. +I've been homeschooled through Year 12 and am now studying a +Bachelor of Computer Science. + +I love building random projects, so this site is always evolving. +I'm also one of the founders of Handshake AU [https://hns.au], +working to grow Handshake adoption across Australia. + +I'm currently working on: {{ repo | safe }} + +─────────────────────────────────────────────── + SKILLS  +──────── + +- Linux servers & CLI +- DNS, DNSSEC, and Trustless SSL +- NGINX web servers +- Programming: + - Python 3 + - C# + - Java + - Bash + diff --git a/templates/projects.ascii b/templates/projects.ascii new file mode 100644 index 0000000..61f4067 --- /dev/null +++ b/templates/projects.ascii @@ -0,0 +1,7 @@ +{{header}} +─────────────────────────────────────────────── + RECENT PROJECTS  +───────────────── + +{{projects}} + -- 2.49.1 From abcaa9283dc820982183e2fc0f3e1930f0158d25 Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Sun, 26 Oct 2025 18:19:49 +1100 Subject: [PATCH 2/4] feat: Add tools curl page --- curl.py | 10 ++++++---- server.py | 2 ++ templates/tools.ascii | 13 +++++++++++++ 3 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 templates/tools.ascii diff --git a/curl.py b/curl.py index 81dd50d..b1ca116 100644 --- a/curl.py +++ b/curl.py @@ -1,5 +1,5 @@ from flask import render_template -from tools import error_response, getAddress +from tools import error_response, getAddress, get_tools_data import os from functools import lru_cache import requests @@ -99,10 +99,8 @@ def curl_response(request): coinList.sort() return render_template("donate_more.ascii",header=get_header(), coins=coinList - ), 200, {'Content-Type': 'text/plain; charset=utf-8'} + ), 200, {'Content-Type': 'text/plain; charset=utf-8'} - - # For other donation pages, fall back to ascii if it exists if path.startswith("donate/"): coin = path.split("/")[1] @@ -110,6 +108,10 @@ def curl_response(request): if address != "": return render_template("donate_coin.ascii",header=get_header(),coin=coin.upper(),address=address), 200, {'Content-Type': 'text/plain; charset=utf-8'} + if path == "tools": + tools = get_tools_data() + return render_template("tools.ascii",header=get_header(),tools=tools), 200, {'Content-Type': 'text/plain; charset=utf-8'} + if os.path.exists(f"templates/{path}.ascii"): return render_template(f"{path}.ascii",header=get_header()), 200, {'Content-Type': 'text/plain; charset=utf-8'} diff --git a/server.py b/server.py index 87c568a..b4ce245 100644 --- a/server.py +++ b/server.py @@ -685,6 +685,8 @@ def resume_pdf(): @app.route("/tools") def tools(): + if isCurl(request): + return curl_response(request) return render_template("tools.html", tools=get_tools_data()) # endregion diff --git a/templates/tools.ascii b/templates/tools.ascii new file mode 100644 index 0000000..cadaa2c --- /dev/null +++ b/templates/tools.ascii @@ -0,0 +1,13 @@ +{{header}} +─────────────────────────────────────────────── + Tools  +──────────── + +Here are some of the tools I use regularly — most of them are open source! 🛠️ + +{% for tool in tools %} +{{tool.name}} +{{tool.description}} +Website: {{tool.url}} +{% if tool.demo_url %}Demo: {{tool.demo_url}}{% endif %} +{% endfor %} -- 2.49.1 From 724e80020146e4a7bfc3aaa6587b2b204935954b Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Sun, 26 Oct 2025 18:40:22 +1100 Subject: [PATCH 3/4] feat: Update curl template for index --- curl.py | 4 ++-- data/tools.json | 16 ++++++++-------- templates/index.ascii | 19 +++++++++++++++++-- templates/tools.ascii | 9 ++++++++- tests/api.hurl | 5 ++++- 5 files changed, 39 insertions(+), 14 deletions(-) diff --git a/curl.py b/curl.py index b1ca116..e7c4f37 100644 --- a/curl.py +++ b/curl.py @@ -1,5 +1,5 @@ from flask import render_template -from tools import error_response, getAddress, get_tools_data +from tools import error_response, getAddress, get_tools_data, getClientIP import os from functools import lru_cache import requests @@ -81,7 +81,7 @@ def curl_response(request): # Handle special cases if path == "index": # Get current project - return render_template("index.ascii",header=get_header(),repo=get_current_project()), 200, {'Content-Type': 'text/plain; charset=utf-8'} + return render_template("index.ascii",repo=get_current_project(), ip=getClientIP(request)), 200, {'Content-Type': 'text/plain; charset=utf-8'} if path == "projects": # Get projects return render_template("projects.ascii",header=get_header(),projects=get_projects()), 200, {'Content-Type': 'text/plain; charset=utf-8'} diff --git a/data/tools.json b/data/tools.json index 1d51384..4f33210 100644 --- a/data/tools.json +++ b/data/tools.json @@ -25,13 +25,13 @@ }, { "name": "Zellij", - "type": "terminal", + "type": "Terminal Tools", "url": "https://zellij.dev/", "description": "A terminal workspace and multiplexer" }, { "name": "Fx", - "type": "terminal", + "type": "Terminal Tools", "url": "https://fx.wtf/", "description": "A command-line JSON viewer and processor", "demo": "", @@ -39,7 +39,7 @@ }, { "name": "Zoxide", - "type": "terminal", + "type": "Terminal Tools", "url": "https://github.com/ajeetdsouza/zoxide", "description": "cd but with fuzzy matching and other cool features", "demo": "", @@ -47,7 +47,7 @@ }, { "name": "Atuin", - "type": "terminal", + "type": "Terminal Tools", "url": "https://atuin.sh/", "description": "A next-generation shell history manager", "demo": "", @@ -55,7 +55,7 @@ }, { "name": "Tmate", - "type": "terminal", + "type": "Terminal Tools", "url": "https://tmate.io/", "description": "Instant terminal sharing", "demo": "", @@ -63,7 +63,7 @@ }, { "name": "Eza", - "type": "terminal", + "type": "Terminal Tools", "url": "https://eza.rocks/", "description": "A modern replacement for 'ls'", "demo": "", @@ -71,7 +71,7 @@ }, { "name": "Bat", - "type": "terminal", + "type": "Terminal Tools", "url": "https://github.com/sharkdp/bat", "description": "A cat clone with syntax highlighting and Git integration", "demo": "", @@ -79,7 +79,7 @@ }, { "name": "Oh My Zsh", - "type": "terminal", + "type": "Terminal Tools", "url": "https://ohmyz.sh/", "description": "A delightful community-driven framework for managing your Zsh configuration" }, diff --git a/templates/index.ascii b/templates/index.ascii index 00c193b..4843b28 100644 --- a/templates/index.ascii +++ b/templates/index.ascii @@ -1,4 +1,15 @@ -{{header}} +───────────────────────────────────────────────────── + . . , . . . .. /  + |\ | _.-+-|_ _.._ | | _ _ _||_ . .._.._ /  + | \|(_] | [ )(_][ ) * |/\|(_)(_)(_][_)(_|[ [ )/  +───────────────────────────────────────────────────── + +Home [/] +Contact [/contact] +Projects [/projects] +Donate [/donate] +API [/api/v1/] + ───────────────────────────────────────────────  ABOUT ME  ────────── @@ -18,7 +29,7 @@ I'm currently working on: {{ repo | safe }} ──────── - Linux servers & CLI -- DNS, DNSSEC, and Trustless SSL +- DNS & DNSSEC - NGINX web servers - Programming: - Python 3 @@ -26,3 +37,7 @@ I'm currently working on: {{ repo | safe }} - Java - Bash + +Served to: {{ ip }} +─────────────────────────────────────────────── + diff --git a/templates/tools.ascii b/templates/tools.ascii index cadaa2c..5840c19 100644 --- a/templates/tools.ascii +++ b/templates/tools.ascii @@ -5,9 +5,16 @@ Here are some of the tools I use regularly — most of them are open source! 🛠️ -{% for tool in tools %} +{% for type, tools_in_type in tools | groupby('type') %} +{{type}} +{% for tool in tools_in_type %} {{tool.name}} {{tool.description}} Website: {{tool.url}} {% if tool.demo_url %}Demo: {{tool.demo_url}}{% endif %} {% endfor %} + +─────────────────────────────────────────────── +{% endfor %} + + diff --git a/tests/api.hurl b/tests/api.hurl index 899619e..0d16c63 100644 --- a/tests/api.hurl +++ b/tests/api.hurl @@ -14,4 +14,7 @@ GET http://127.0.0.1:5000/api/v1/message HTTP 200 GET http://127.0.0.1:5000/api/v1/project HTTP 200 - +GET http://127.0.0.1:5000/api/v1/tools +HTTP 200 +[Asserts] +jsonpath "$.tools" count > 5 \ No newline at end of file -- 2.49.1 From a71c5b6663caeca303027d126c37d6b53caf2627 Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Sun, 26 Oct 2025 18:47:25 +1100 Subject: [PATCH 4/4] feat: Update ascii templates to be nicer --- templates/donate.ascii | 6 +++--- templates/header.ascii | 1 + templates/index.ascii | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/templates/donate.ascii b/templates/donate.ascii index cf8b6a3..bbc5a2c 100644 --- a/templates/donate.ascii +++ b/templates/donate.ascii @@ -5,9 +5,9 @@ If you’d like to support my work 💙 -- PayPal: [https://paypal.me/nathanwoodburn] -- GitHub: [https://github.com/sponsors/Nathanwoodburn] -- Stripe: [https://donate.stripe.com/8wM6pv0VD08Xe408ww] +- PayPal: https://paypal.me/nathanwoodburn +- GitHub: https://github.com/sponsors/Nathanwoodburn +- Stripe: https://donate.stripe.com/8wM6pv0VD08Xe408ww HNS: nathan.woodburn {{ HNS }} diff --git a/templates/header.ascii b/templates/header.ascii index f50ed64..e349bed 100644 --- a/templates/header.ascii +++ b/templates/header.ascii @@ -7,5 +7,6 @@ Home [/] Contact [/contact] Projects [/projects] +Tools [/tools] Donate [/donate] diff --git a/templates/index.ascii b/templates/index.ascii index 4843b28..0ea8493 100644 --- a/templates/index.ascii +++ b/templates/index.ascii @@ -7,6 +7,7 @@ Home [/] Contact [/contact] Projects [/projects] +Tools [/tools] Donate [/donate] API [/api/v1/] -- 2.49.1