feat: Add recent projects to index and updated projects page
All checks were successful
Build Docker / BuildImage (push) Successful in 37s

This commit is contained in:
Nathan Woodburn 2024-06-18 12:44:26 +10:00
parent 719221d74f
commit 18619efe39
Signed by: nathanwoodburn
GPG Key ID: 203B000478AD0EF1
5 changed files with 126 additions and 132 deletions

View File

@ -3,7 +3,8 @@
"url": "https://nathan3dprinting.au", "url": "https://nathan3dprinting.au",
"img": "/assets/img/external/nathan3dprinting.webp", "img": "/assets/img/external/nathan3dprinting.webp",
"name": "Nathan 3D Printing", "name": "Nathan 3D Printing",
"description": "Offering 3D Printing and CAD modelling services to the Canberra region" "description": "Offering 3D Printing and CAD modelling services to the Canberra region",
"enabled": false
}, },
{ {
"url": "https://domains.hns.au", "url": "https://domains.hns.au",
@ -46,5 +47,17 @@
"img": "/favicon.png", "img": "/favicon.png",
"name": "LINKR/", "name": "LINKR/",
"description": "A free link shortener with a Handshake TLD and using DNS for authentication" "description": "A free link shortener with a Handshake TLD and using DNS for authentication"
},
{
"url": "https://faucet.woodburn.au",
"img": "/favicon.png",
"name": "HNS Domain Faucet",
"description": "A service providing free Handshake TLDs to allow for quick testing for new users"
},
{
"url":"https://hnsdoh.com",
"img": "/assets/img/external/HNS.png",
"name": "HNS DoH",
"description": "A DNS over HTTPS resolver for Handshake domains"
} }
] ]

112
server.py
View File

@ -24,6 +24,12 @@ sites = []
if os.path.isfile('data/sites.json'): if os.path.isfile('data/sites.json'):
with open('data/sites.json') as file: with open('data/sites.json') as file:
sites = json.load(file) sites = json.load(file)
# Remove any sites that are not enabled
sites = [site for site in sites if 'enabled' not in site or site['enabled'] == True]
projects = []
projectsUpdated = 0
def getAddress(): def getAddress():
global address global address
@ -37,7 +43,19 @@ def send_report(path):
if path.endswith('.json'): if path.endswith('.json'):
return send_from_directory('templates/assets', path, mimetype='application/json') return send_from_directory('templates/assets', path, mimetype='application/json')
return send_from_directory('templates/assets', path) if os.path.isfile('templates/assets/' + path):
return send_from_directory('templates/assets', path)
# Try looking in one of the directories
filename:str = path.split('/')[-1]
if filename.endswith('.png') or filename.endswith('.jpg') \
or filename.endswith('.jpeg') or filename.endswith('.svg'):
if os.path.isfile('templates/assets/img/' + filename):
return send_from_directory('templates/assets/img', filename)
if os.path.isfile('templates/assets/img/favicon/' + filename):
return send_from_directory('templates/assets/img/favicon', filename)
return render_template('404.html'), 404
# Special routes # Special routes
@ -117,7 +135,21 @@ def nostr():
}) })
# Main routes @app.route('/manifest.json')
def manifest():
host = request.host
if host == 'nathan.woodburn.au':
return send_from_directory('templates', 'manifest.json')
# Read as json
with open('templates/manifest.json') as file:
manifest = json.load(file)
scheme = request.scheme
manifest['start_url'] = f'{scheme}://{host}/'
return jsonify(manifest)
# region Main routes
@app.route('/') @app.route('/')
def index(): def index():
# Check if host if podcast.woodburn.au # Check if host if podcast.woodburn.au
@ -141,8 +173,11 @@ def index():
global address global address
global handshake_scripts global handshake_scripts
global projects
try: try:
git=requests.get('https://git.woodburn.au/api/v1/users/nathanwoodburn/activities/feeds?only-performed-by=true&limit=1&token=' + os.getenv('git_token')) git=requests.get('https://git.woodburn.au/api/v1/users/nathanwoodburn/activities/feeds?only-performed-by=true&limit=1',
headers={'Authorization': os.getenv('git_token')})
git = git.json() git = git.json()
git = git[0] git = git[0]
repo_name=git['repo']['name'] repo_name=git['repo']['name']
@ -152,8 +187,39 @@ def index():
repo_name = "nathanwoodburn.github.io" repo_name = "nathanwoodburn.github.io"
repo_description = "Personal website" repo_description = "Personal website"
git = {'repo': {'html_url': 'https://nathan.woodburn.au', 'name': 'nathanwoodburn.github.io', 'description': 'Personal website'}} git = {'repo': {'html_url': 'https://nathan.woodburn.au', 'name': 'nathanwoodburn.github.io', 'description': 'Personal website'}}
print("Error getting git data")
custom = "" custom = ""
# Get only repo names for the newest updates
if projects == [] or projectsUpdated < datetime.datetime.now() - datetime.timedelta(hours=2):
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
for project in projects:
if project['avatar_url'] == 'https://git.woodburn.au/':
project['avatar_url'] = '/favicon.png'
project['name'] = project['name'].replace('_', ' ').replace('-', ' ')
# Sort by last updated
projectsList = sorted(projects, key=lambda x: x['updated_at'], reverse=True)
projects = []
projectNames = []
projectNum = 0
while len(projects) < 3:
if projectsList[projectNum]['name'] not in projectNames:
projects.append(projectsList[projectNum])
projectNames.append(projectsList[projectNum]['name'])
projectNum += 1
projectsUpdated = datetime.datetime.now()
# Check for downtime # Check for downtime
uptime = requests.get('https://uptime.woodburn.au/api/status-page/main/badge') uptime = requests.get('https://uptime.woodburn.au/api/status-page/main/badge')
uptime = uptime.content.count(b'Up') > 1 uptime = uptime.content.count(b'Up') > 1
@ -173,14 +239,20 @@ def index():
handshake_scripts = "" handshake_scripts = ""
if request.cookies.get('HNS'): if request.cookies.get('HNS'):
resp = make_response(render_template('index.html', handshake_scripts=handshake_scripts, HNS=request.cookies.get('HNS'), repo=repo, repo_description=repo_description, custom=custom,sites=sites), 200, {'Content-Type': 'text/html'}) resp = make_response(render_template('index.html', handshake_scripts=handshake_scripts,
HNS=request.cookies.get('HNS'), repo=repo,
repo_description=repo_description,
custom=custom,sites=sites, projects=projects), 200, {'Content-Type': 'text/html'})
resp.set_cookie('loaded', 'true', max_age=604800) resp.set_cookie('loaded', 'true', max_age=604800)
return resp return resp
if address == '': if address == '':
address = getAddress() address = getAddress()
# Set cookie # Set cookie
resp = make_response(render_template('index.html', handshake_scripts=handshake_scripts, HNS=address, repo=repo, repo_description=repo_description, custom=custom,sites=sites), 200, {'Content-Type': 'text/html'}) resp = make_response(render_template('index.html', handshake_scripts=handshake_scripts,
HNS=address, repo=repo,
repo_description=repo_description,
custom=custom,sites=sites,projects=projects), 200, {'Content-Type': 'text/html'})
# Cookie should last 1 week # Cookie should last 1 week
resp.set_cookie('HNS', address, max_age=604800) resp.set_cookie('HNS', address, max_age=604800)
@ -189,6 +261,8 @@ def index():
return resp return resp
# region Now Pages
@app.route('/now') @app.route('/now')
@app.route('/now/') @app.route('/now/')
def now(): def now():
@ -261,6 +335,7 @@ def now_old():
html += '</ul>' html += '</ul>'
return render_template('now/old.html', handshake_scripts=handshake_scripts,now_pages=html) return render_template('now/old.html', handshake_scripts=handshake_scripts,now_pages=html)
# endregion
@app.route('/donate') @app.route('/donate')
@ -405,30 +480,19 @@ def catch_all(path):
return render_template('404.html'), 404 return render_template('404.html'), 404
# If file exists, load it # If file exists, load it
if os.path.isfile('templates/' + path): if os.path.isfile('templates/' + path):
return render_template(path, handshake_scripts=handshake_scripts) return render_template(path, handshake_scripts=handshake_scripts,sites=sites)
# Try with .html # Try with .html
if os.path.isfile('templates/' + path + '.html'): if os.path.isfile('templates/' + path + '.html'):
return render_template(path + '.html', handshake_scripts=handshake_scripts) return render_template(path + '.html', handshake_scripts=handshake_scripts,sites=sites)
if os.path.isfile('templates/' + path.strip('/') + '.html'): if os.path.isfile('templates/' + path.strip('/') + '.html'):
return render_template(path.strip('/') + '.html', handshake_scripts=handshake_scripts) return render_template(path.strip('/') + '.html', handshake_scripts=handshake_scripts,sites=sites)
return render_template('404.html'), 404 return render_template('404.html'), 404
# endregion
@app.route('/manifest.json') #region ACME
def manifest():
host = request.host
if host == 'nathan.woodburn.au':
return send_from_directory('templates', 'manifest.json')
# Read as json
with open('templates/manifest.json') as file:
manifest = json.load(file)
scheme = request.scheme
manifest['start_url'] = f'{scheme}://{host}/'
return jsonify(manifest)
@app.route('/hnsdoh-acme', methods=['POST']) @app.route('/hnsdoh-acme', methods=['POST'])
def hnsdoh_acme(): def hnsdoh_acme():
# Get the TXT record from the request # Get the TXT record from the request
@ -459,7 +523,9 @@ def hnsdoh_acme():
record = cf.zones.dns_records.post(zone_id, data={'type': 'TXT', 'name': '_acme-challenge', 'content': txt}) record = cf.zones.dns_records.post(zone_id, data={'type': 'TXT', 'name': '_acme-challenge', 'content': txt})
print(record) print(record)
return jsonify({'status': 'success'}) return jsonify({'status': 'success'})
#endregion
#region Podcast
@app.route('/ID1') @app.route('/ID1')
def ID1(): def ID1():
# Proxy to ID1 url # Proxy to ID1 url
@ -488,13 +554,15 @@ def ID1_xml():
def podsync(): def podsync():
req = requests.get('https://id1.woodburn.au/podsync.opml') req = requests.get('https://id1.woodburn.au/podsync.opml')
return make_response(req.content, 200, {'Content-Type': req.headers['Content-Type']}) return make_response(req.content, 200, {'Content-Type': req.headers['Content-Type']})
#endregion
#region Error Catching
# 404 catch all # 404 catch all
@app.errorhandler(404) @app.errorhandler(404)
def not_found(e): def not_found(e):
return render_template('404.html'), 404 return render_template('404.html'), 404
#endregion
if __name__ == '__main__': if __name__ == '__main__':
app.run(debug=True, port=5000, host='0.0.0.0') app.run(debug=True, port=5000, host='0.0.0.0')

View File

@ -98,24 +98,18 @@ Check them out here!</blockquote><img class="img-fluid" src="/assets/img/pfront.
</section><img class="no-drag" src="/assets/img/tilt.svg" style="background: #363636;width: 100%;" alt=""> </section><img class="no-drag" src="/assets/img/tilt.svg" style="background: #363636;width: 100%;" alt="">
<section class="text-center content-section sites" id="sites" style="background: #363636;padding-top: 0px;padding-bottom: 0px;"> <section class="text-center content-section sites" id="sites" style="background: #363636;padding-top: 0px;padding-bottom: 0px;">
<div class="site-container"> <div class="site-container">
<h1>My Websites</h1> <h1>Some recent projects</h1>
<div class="swiper"> <div class="swiper">
<div class="swiper-wrapper"><!-- <div class="swiper-slide site" data-url="https://nathan3dprinting.au"><img class="site-img" src="/assets/img/external/nathan3dprinting.webp" /> <div class="swiper-wrapper">{% for project in projects %}
<div class="site-body"> <div class="swiper-slide site" data-url="{{ project_url }}">
<div class="site-detail" style="width: 100%;"> <img class="site-img" src="{{ project.avatar_url }}" />
<h2 class="site-name" style="text-align: left;">Nathan 3D Printing</h2>
<p class="text-start site-author">Offering 3D Printing and CAD modelling services to the Canberra region</p>
</div>
</div>
</div> -->
{% for site in sites %}
<div class="swiper-slide site" data-url="{{ site.url }}">
<img class="site-img" src="{{ site.img }}" />
<div class="site-body"> <div class="site-body">
<div class="site-detail" style="width: 100%;"> <div class="site-detail" style="width: 100%;">
<h2 class="site-name" style="text-align: left;">{{ site.name }}</h2> <h2 class="site-name" style="text-align: left;">{{ project.name }}</h2>
<p class="text-start site-author">{{ site.description }}</p> <p class="text-start site-author">{{ project.description }}</p>
{% if project.actions %}
<img src="{{project.actions}}" alt="Build Status">
{% endif %}
</div> </div>
</div> </div>
</div> </div>

View File

@ -40,17 +40,18 @@
<noscript><iframe src="https://www.googletagmanager.com/ns?id=GTM-NNXTCKW" <noscript><iframe src="https://www.googletagmanager.com/ns?id=GTM-NNXTCKW"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript> height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) --> <!-- End Google Tag Manager (noscript) -->
<nav class="navbar navbar-expand-md fixed-top navbar-light" id="mainNav"> <nav class="navbar navbar-expand-md fixed-top navbar-light" id="mainNav" style="background: var(--bs-navbar-hover-color);">
<div class="container"><a class="navbar-brand nathanwoodburn" href="/#">Nathan.Woodburn/</a><button data-bs-toggle="collapse" class="navbar-toggler navbar-toggler-right" data-bs-target="#navbarResponsive" type="button" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation" value="Menu"><i class="fa fa-bars"></i></button> <div class="container-fluid"><a class="navbar-brand nathanwoodburn" href="/#">Nathan.Woodburn/</a><button data-bs-toggle="collapse" class="navbar-toggler navbar-toggler-right" data-bs-target="#navbarResponsive" type="button" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation" value="Menu"><i class="fa fa-bars"></i></button>
<div class="collapse navbar-collapse" id="navbarResponsive"> <div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ms-auto"> <ul class="navbar-nav ms-auto">
<li class="nav-item nav-link"><a class="nav-link" href="/">Home</a></li> <li class="nav-item nav-link"><a class="nav-link" href="/">Home</a></li>
<li class="nav-item nav-link"><a class="nav-link" href="projects">Projects</a></li> <li class="nav-item nav-link"><a class="nav-link" href="/projects">Projects</a></li>
<li class="nav-item nav-link"><a class="nav-link" href="/now">Now</a></li>
</ul> </ul>
</div> </div>
</div> </div>
</nav> </nav>
<header class="masthead" style="height: auto;background: url(&quot;/assets/img/bg/projects.webp&quot;) bottom / cover no-repeat;"> <header class="masthead" style="background: url(&quot;/assets/img/bg/projects.webp&quot;) bottom / cover no-repeat;height: auto;padding-top: 20px;">
<div style="margin-top: 150px;margin-bottom: 100px;"> <div style="margin-top: 150px;margin-bottom: 100px;">
<div class="container"> <div class="container">
<div class="row"> <div class="row">
@ -61,61 +62,17 @@ height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
</div> </div>
</div> </div>
</header> </header>
<section class="text-center content-section" id="projects" style="padding-bottom: 100px;"> <section class="text-center content-section" id="sites" style="padding-bottom: 100px;">
<p style="font-size: 28px;">Check out my <a href="https://git.woodburn.au/nathanwoodburn" target="_blank">Git</a>&nbsp;for all my projects</p>
<div class="container"> <div class="container">
<div class="row"> <div class="row gx-5 row-cols-1 row-cols-sm-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-3 row-cols-xxl-4">{% for site in sites %}
<div class="col" style="padding-top: 20px;"> <div class="col" style="padding-top: 20px;">
<h2>ShakeCities</h2> <h2>{{site.name}}</h2>
<p>A single page website creator where each user's page on their free HNS domain</p><a class="btn btn-primary" role="button" href="https://shakecities.com" target="_blank">More Info</a> <p>{{site.description}}</p><a class="btn btn-primary" role="button" href="{{site.url}}" target="_blank">More Info</a>
</div> </div>
<div class="col" style="padding-top: 20px;"> {% endfor %}</div>
<h2>Woodburn Faucet</h2>
<p>A service providing free Handshake TLDs to allow for quick testing for new users</p><a class="btn btn-primary" role="button" href="https://faucet.woodburn.au" target="_blank">More Info</a>
</div>
</div>
<div class="row">
<div class="col" style="padding-top: 20px;">
<h2>Linkr</h2>
<p>A shortlinks service on the Handshake domain Linkr/</p><a class="btn btn-primary" role="button" href="https://linkr" target="_blank">More Info</a>
</div>
<div class="col" style="padding-top: 20px;">
<h2>HNSDoh</h2>
<p>A DNS server with support for Handshake domains using multiple upstream providers to provide as little downtime as possible.</p><a class="btn btn-primary" role="button" href="https://hnsdoh.com" target="_blank">More Info</a>
</div>
</div>
<div class="row">
<div class="col" style="padding-top: 20px;">
<h2>FireWallet</h2>
<p><span style="background-color: initial;">The Handshake wallet that is Fire.</span><br><span style="background-color: initial;">A new frontend to HSD or Bob Wallet</span></p><a class="btn btn-primary" role="button" target="_blank" href="https://firewallet.au">More Info</a>
</div>
<div class="col" style="padding-top: 20px;">
<h2>HNS HOSting</h2>
<p>A hosting service for Handshake domains<br><br></p><a class="btn btn-primary" role="button" href="https://hnshosting.au" target="_blank">More Info</a>
</div>
</div>
<div class="row">
<div class="col" style="padding-top: 20px;">
<h2>HNS AU</h2>
<p><span style="background-color: initial;">The home of Australians using the decentralized web, built on Handshake.</span><br></p><a class="btn btn-primary" role="button" href="https://hns.au" target="_blank">More Info</a>
</div>
<div class="col" style="padding-top: 20px;">
<h2>HNS Proxy</h2>
<p><span style="background-color: initial;">A Proxy to access Handshake domains over SSL.</span><br></p><a class="btn btn-primary" role="button" href="https://hnsproxy.au" target="_blank">More Info</a>
</div>
</div>
<div class="row">
<div class="col" style="padding-top: 20px;">
<h2>Woodburn Registry</h2>
<p>Selling SLDs off domains secured on the Handshake blockchain.</p><a class="btn btn-primary" role="button" href="https://reg.woodburn.au" target="_blank">More Info</a>
</div>
<div class="col" style="padding-top: 20px;">
<h2>HSD Batcher</h2>
<p><span style="background-color: initial;">A GUI to send transaction batches to the Handshake chain.</span></p><a class="btn btn-primary" role="button" target="_blank" href="https://github.com/Nathanwoodburn/HSDBatcherGUI">More Info</a>
</div>
</div>
</div> </div>
</section> </section>
<p class="text-center" style="font-size: 28px;">Check out my <a href="https://git.woodburn.au/nathanwoodburn" target="_blank">Git</a>&nbsp;for all my projects</p>
<footer> <footer>
<div class="container text-center"> <div class="container text-center">
<p class="copyright">Copyright ©&nbsp;Nathan.Woodburn/ 2024</p> <p class="copyright">Copyright ©&nbsp;Nathan.Woodburn/ 2024</p>

View File

@ -29,7 +29,6 @@
<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=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=Anonymous+Pro&amp;display=swap">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700"> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700">
<link rel="stylesheet" href="/assets/fonts/font-awesome.min.css">
<link rel="stylesheet" href="/assets/css/styles.min.css"> <link rel="stylesheet" href="/assets/css/styles.min.css">
<link rel="stylesheet" href="/assets/css/profile.min.css"> <link rel="stylesheet" href="/assets/css/profile.min.css">
<link rel="me" href="https://mastodon.woodburn.au/@nathanwoodburn" /> <link rel="me" href="https://mastodon.woodburn.au/@nathanwoodburn" />
@ -40,16 +39,6 @@
<noscript><iframe src="https://www.googletagmanager.com/ns?id=GTM-NNXTCKW" <noscript><iframe src="https://www.googletagmanager.com/ns?id=GTM-NNXTCKW"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript> height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) --> <!-- End Google Tag Manager (noscript) -->
<nav class="navbar navbar-expand-md fixed-top navbar-light" id="mainNav">
<div class="container"><a class="navbar-brand nathanwoodburn" href="/#">Nathan.Woodburn/</a><button data-bs-toggle="collapse" class="navbar-toggler navbar-toggler-right" data-bs-target="#navbarResponsive" type="button" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation" value="Menu"><i class="fa fa-bars"></i></button>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ms-auto">
<li class="nav-item nav-link"><a class="nav-link" href="/">Home</a></li>
<li class="nav-item nav-link"><a class="nav-link" href="projects">Projects</a></li>
</ul>
</div>
</div>
</nav>
<header class="masthead" style="background-image:url('/assets/img/bg/projects.webp');"> <header class="masthead" style="background-image:url('/assets/img/bg/projects.webp');">
<div class="intro-body"> <div class="intro-body">
<div class="container"> <div class="container">
@ -225,33 +214,6 @@ height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
</div> </div>
</div> </div>
<div class="row row-cols-1 row-cols-sm-1 row-cols-md-1 row-cols-lg-1 row-cols-xl-3 row-cols-xxl-3"> <div class="row row-cols-1 row-cols-sm-1 row-cols-md-1 row-cols-lg-1 row-cols-xl-3 row-cols-xxl-3">
<div class="col">
<div class="card" style="margin-top: 10px;margin-bottom: 10px;">
<div class="card-body text-primary text-bg-dark">
<h4 class="card-title">Potato2 (3D Printer)</h4>CPU<br>
<iframe
src="https://potato2dash.woodburn.au/?graph=cpu&showPercentage=true&surface=212529"
style="border-radius: 20px"
allowtransparency="true"
frameborder="0"
></iframe>
<br>MEM<br>
<iframe
src="https://potato2dash.woodburn.au/?graph=ram&showPercentage=true&surface=212529"
style="border-radius: 20px"
allowtransparency="true"
frameborder="0"
></iframe>
<br>Storage<br>
<iframe
src="https://potato2dash.woodburn.au/?graph=storage&showPercentage=true&surface=212529"
style="border-radius: 20px"
allowtransparency="true"
frameborder="0"
></iframe>
</div>
</div>
</div>
<div class="col"> <div class="col">
<div class="card" style="margin-top: 10px;margin-bottom: 10px;"> <div class="card" style="margin-top: 10px;margin-bottom: 10px;">
<div class="card-body text-primary text-bg-dark"> <div class="card-body text-primary text-bg-dark">