feat: Initial code drop

This commit is contained in:
Nathan Woodburn 2024-02-24 18:47:23 +11:00
parent 76ad2ffa99
commit 4b21186b8f
Signed by: nathanwoodburn
GPG Key ID: 203B000478AD0EF1
53 changed files with 3273 additions and 0 deletions

12
.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
.env
__pycache__/
avatars/
certs/
cookies.json
sites/

369
main.py Normal file
View File

@ -0,0 +1,369 @@
from flask import Flask, make_response, redirect, request, jsonify, render_template, send_from_directory, render_template_string
import os
import dotenv
import requests
import datetime
import json
import render
import secrets
import nginx
app = Flask(__name__)
dotenv.load_dotenv()
# Get site domains
DOMAINS = os.getenv('DOMAINS')
DOMAINS = json.loads(DOMAINS)
# Add local domains
# DOMAINS.append('localhost:5000')
DOMAINS.append('127.0.0.1:5000')
IP = "0.0.0.0"
try:
IP = requests.get('https://ipinfo.io/ip').text.strip()
except:
IP = "Error"
# Load cookies
cookies = []
if os.path.isfile('cookies.json'):
with open('cookies.json') as file:
cookies = json.load(file)
else:
with open('cookies.json', 'w') as file:
json.dump(cookies, file)
if not os.path.isdir('avatars'):
os.mkdir('avatars')
if not os.path.isdir('sites'):
os.mkdir('sites')
if not os.path.isdir('certs'):
os.mkdir('certs')
#Assets routes
@app.route('/assets/<path:path>')
def send_report(path):
return send_from_directory('templates/assets', path)
@app.route('/favicon.png')
def faviconPNG():
return send_from_directory('templates/assets/img', 'favicon.png')
# Main routes
@app.route('/')
def index():
if request.host in DOMAINS:
if 'auth' in request.cookies:
auth = request.cookies['auth']
for i in cookies:
if i['cookie'] == auth:
return render_template('index.html',varo="window.location = '/site';", year=datetime.datetime.now().year)
return render_template('index.html',varo=render.varo_login(), year=datetime.datetime.now().year)
# Remove any ports
host = request.host.split(':')[0]
# Get content from site
if os.path.isfile(f'sites/{host}.json'):
with open(f'sites/{host}.json') as file:
data = json.load(file)
return render.site(data)
return redirect(f'https://{DOMAINS[0]}')
@app.route('/site')
def site():
# Get auth domain
if 'auth' not in request.cookies:
return redirect('/')
auth = request.cookies['auth']
for i in cookies:
if i['cookie'] == auth:
# Load site content
if os.path.isfile(f'sites/{i["name"]}.json'):
with open(f'sites/{i["name"]}.json') as file:
data = json.load(file)
preview = render.preview(data)
else:
with open(f'sites/example.json') as file:
data = json.load(file)
preview = render.preview(data)
data = {
'title': '',
'link_0': '',
'link_1': '',
'link_2': '',
'link_3': '',
'link_0_url': '',
'link_1_url': '',
'link_2_url': '',
'link_3_url': '',
'image': '',
"bg_0": "#001665",
"bg_1": "#000000",
"fg_0": "#ffffff",
"btn_bg": "#2c54cf",
"btn_fg": "#ffffff",
"socials": []
}
title = data['title']
link_0 = data['link_0']
link_1 = data['link_1']
link_2 = data['link_2']
link_3 = data['link_3']
link_0_url = data['link_0_url']
link_1_url = data['link_1_url']
link_2_url = data['link_2_url']
link_3_url = data['link_3_url']
fg_0 = data['fg_0']
bg_0 = data['bg_0']
bg_1 = data['bg_1']
btn_bg = data['btn_bg']
btn_fg = data['btn_fg']
socials = data['socials']
address = data['address']
# Convert socials to dict
socials = {i['name']: i['url'] for i in socials}
address = {i['token']: i['address'] for i in address}
tlsa = data['tlsa'] if 'tlsa' in data else ''
ip = IP
return render_template('site.html', year=datetime.datetime.now().year, domain=i['name'],
title=title, link_0=link_0, link_1=link_1, link_2=link_2, link_3=link_3,
link_0_url=link_0_url, link_1_url=link_1_url, link_2_url=link_2_url,
link_3_url=link_3_url, fg_0=fg_0, bg_0=bg_0, bg_1=bg_1, btn_bg=btn_bg, btn_fg=btn_fg,
socials=socials,address=address,preview=preview,tlsa=tlsa,ip=ip)
response = make_response(redirect('/'))
response.set_cookie('auth', '', expires=0)
return response
@app.route('/site', methods=['POST'])
def site_post():
if 'auth' not in request.cookies:
return redirect('/')
auth = request.cookies['auth']
for i in cookies:
if i['cookie'] == auth:
# Get site content
if os.path.isfile(f'sites/{i["name"]}.json'):
with open(f'sites/{i["name"]}.json') as file:
data = json.load(file)
else:
with open(f'sites/example.json') as file:
data = {}
# Save site content
data['title'] = request.form['title']
data['link_0'] = request.form['link_0']
data['link_1'] = request.form['link_1']
data['link_2'] = request.form['link_2']
data['link_3'] = request.form['link_3']
data['link_0_url'] = request.form['link_0_url']
data['link_1_url'] = request.form['link_1_url']
data['link_2_url'] = request.form['link_2_url']
data['link_3_url'] = request.form['link_3_url']
data['bg_0'] = request.form['bg_0']
data['bg_1'] = request.form['bg_1']
data['fg_0'] = request.form['fg_0']
data['btn_bg'] = request.form['btn_bg']
data['btn_fg'] = request.form['btn_fg']
socials = []
socials.append({'name': 'email', 'url': request.form['email']})
socials.append({'name': 'twitter', 'url': request.form['twitter']})
socials.append({'name': 'github', 'url': request.form['github']})
socials.append({'name': 'youtube', 'url': request.form['youtube']})
address = []
address.append({'token': 'hns', 'address': request.form['hns']})
address.append({'token': 'eth', 'address': request.form['eth']})
address.append({'token': 'btc', 'address': request.form['btc']})
address.append({'token': 'sol', 'address': request.form['sol']})
# Remove empty socials and addresses
socials = [i for i in socials if i['url'] != '']
data['socials'] = socials
address = [i for i in address if i['address'] != '']
data['address'] = address
if 'image' in request.files:
if request.files['image'].filename != '' and request.files['image'].filename != None:
# Make sure the file is an image
file = request.files['image']
extension = file.filename.split('.')[-1]
file.save(f'avatars/{i["name"]}.' + extension)
data['image'] = f'{i["name"]}.' + extension
with open(f'sites/{i["name"]}.json', 'w') as file:
json.dump(data, file)
return redirect('/site')
response = make_response(redirect('/'))
response.set_cookie('auth', '', expires=0)
return response
@app.route('/preview')
def site_preview():
if 'auth' not in request.cookies:
return redirect('/')
auth = request.cookies['auth']
for i in cookies:
if i['cookie'] == auth:
# Load site content
if os.path.isfile(f'sites/{i["name"]}.json'):
with open(f'sites/{i["name"]}.json') as file:
data = json.load(file)
else:
with open(f'sites/example.json') as file:
data = json.load(file)
return render.site(data)
response = make_response(redirect('/'))
response.set_cookie('auth', '', expires=0)
return response
@app.route('/publish')
def publish():
if 'auth' not in request.cookies:
return redirect('/')
auth = request.cookies['auth']
for i in cookies:
if i['cookie'] == auth:
# Load site content
if os.path.isfile(f'sites/{i["name"]}.json'):
with open(f'sites/{i["name"]}.json') as file:
data = json.load(file)
if 'tlsa' in data:
# Site is already published
return redirect('/site')
tlsa = nginx.generate_ssl(i['name'])
data['tlsa'] = tlsa
with open(f'sites/{i["name"]}.json', 'w') as file:
json.dump(data, file)
nginx.write_nginx_conf(i['name'])
return redirect('/site')
response = make_response(redirect('/'))
response.set_cookie('auth', '', expires=0)
return response
@app.route('/.well-known/wallets/<path:path>')
def wallets(path):
# Check if host is in domains
if request.host in DOMAINS:
# Check if user is logged in
if 'auth' not in request.cookies:
return redirect(f'https://{DOMAINS[0]}')
auth = request.cookies['auth']
for i in cookies:
if i['cookie'] == auth:
# Load site content
if os.path.isfile(f'sites/{i["name"]}.json'):
with open(f'sites/{i["name"]}.json') as file:
data = json.load(file)
for i in data['address']:
if i['token'].upper() == path:
# Return as plain text
response = make_response(i['address'])
response.headers['Content-Type'] = 'text/plain'
return response
# Get wallet from domain
host = request.host.split(':')[0]
if os.path.isfile(f'sites/{host}.json'):
with open(f'sites/{host}.json') as file:
data = json.load(file)
for i in data['address']:
if i['token'].upper() == path:
# Return as plain text
response = make_response(i['address'])
response.headers['Content-Type'] = 'text/plain'
return response
return render_template('404.html', year=datetime.datetime.now().year), 404
# region Auth
@app.route('/auth', methods=['POST'])
def auth():
global cookies
auth = login(request)
if auth == False:
return render_template('index.html',varo=render.varo_login(), year=datetime.datetime.now().year, error="Failed to login")
resp = make_response(render_template_string("Success"))
# Gen cookie
auth_cookie = secrets.token_hex(12 // 2)
cookies.append({'name': auth, 'cookie': auth_cookie})
with open('cookies.json', 'w') as file:
json.dump(cookies, file)
resp.set_cookie('auth', auth_cookie)
return resp
@app.route('/logout')
def logout():
global cookies
resp = make_response(redirect('/'))
resp.set_cookie('auth', '', expires=0)
cookies = [i for i in cookies if i['cookie'] != request.cookies['auth']]
with open('cookies.json', 'w') as file:
json.dump(cookies, file)
return resp
def login(request):
dict = request.form.to_dict()
keys = dict.keys()
keys = list(keys)[0]
keys = json.loads(keys)
auth_request = keys['request']
# return login(auth_request)
r = requests.get(f'https://auth.varo.domains/verify/{auth_request}')
r = r.json()
if r['success'] == False:
return False
if 'data' in r:
data = r['data']
if 'name' in data:
return data['name']
return False
# endregion
@app.route('/avatar/<path:path>')
def avatar(path):
return send_from_directory('avatars', path)
@app.route('/token/<path:path>')
def tokens(path):
# Colour is last char
colour = path[-1]
token = path[:-1]
if colour.lower() == 'w':
return send_from_directory('templates/assets/img/tokens', f'{token}W.png')
return send_from_directory('templates/assets/img/tokens', f'{token}.png')
# 404 catch all
@app.errorhandler(404)
def not_found(e):
return render_template('404.html', year=datetime.datetime.now().year), 404
if __name__ == '__main__':
app.run(debug=True, port=5000, host='0.0.0.0')

41
nginx.py Normal file
View File

@ -0,0 +1,41 @@
import os
def write_nginx_conf(domain):
ssl = f'''
listen 443 ssl;
ssl_certificate /root/hns-links/certs/{domain}/cert.crt;
ssl_certificate_key /root/hns-links/certs/{domain}/cert.key;
'''
conf = f'''
server {{
listen 80;
listen [::]:80;
server_name {domain} *.{domain};
location / {{
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}}
{ssl}
}}
'''
with open(f'/etc/nginx/sites-enabled/{domain}.conf', 'w') as file:
file.write(conf)
# Restart nginx
os.system('systemctl restart nginx')
return True
def generate_ssl(domain):
tlsa = os.popen(f'./tlsa.sh {domain}').read().strip()
return tlsa

115
render.py Normal file
View File

@ -0,0 +1,115 @@
from flask import Flask, render_template, request, jsonify
def varo_login():
return '''
varo.auth().then(auth => {
if (auth.success) {
// handle success by calling your api to update the users session
$.post("/auth", JSON.stringify(auth.data), (response) => {
window.location = '/site';
});
}
});'''
def preview(data):
title = data['title']
link_0 = data['link_0']
link_1 = data['link_1']
link_2 = data['link_2']
link_3 = data['link_3']
link_0_url = data['link_0_url']
link_1_url = data['link_1_url']
link_2_url = data['link_2_url']
link_3_url = data['link_3_url']
fg_0 = data['fg_0']
bg_0 = data['bg_0']
bg_1 = data['bg_1']
btn_bg = data['btn_bg']
btn_fg = data['btn_fg']
socials = data['socials']
image = data['image']
if image != "":
image = f'<img class="rounded" src="/avatar/{image}" width="128px" height="128px" style="margin-top: 25px;" />'
links = ""
links += link(link_0,link_0_url,btn_bg,btn_fg)
links += link(link_1,link_1_url,btn_bg,btn_fg)
links += link(link_2,link_2_url,btn_bg,btn_fg)
links += link(link_3,link_3_url,btn_bg,btn_fg)
socials = social_links(socials)
addresss = address_links(data['address'],fg_0)
return render_template('preview_card.html', title=title, links=links, image=image, bg_0=bg_0, bg_1=bg_1, fg_0=fg_0, btn_bg=btn_bg, btn_fg=btn_fg, socials=socials, addresses=addresss)
def link(name,url,btn_bg,btn_fg):
if url == "" or name == "":
return ""
colour = f'background-color: {btn_bg}; color: {btn_fg};'
return f'<a class="btn btn-primary custom-button" role="button" style="display: block;margin: auto;margin-bottom: 25px;width: 95%; {colour}" target="_blank" href="{url}">{name}</a>'
def social_links(socials):
html = ''
for social in socials:
html += render_template(social['name'] + '.html', url=social['url'])
return html
def address_links(addresses,foreground):
html = ''
for address in addresses:
token = address['token'].upper()
html += f'<a href=".well-known/wallets/{token}" target="_blank">{tokenImage(token,foreground)}</a>'
return html
def tokenImage(token,foreground):
if is_closer_to_black(foreground):
return f'<img src="/token/{token}B" width="32px" height="32px" style="margin-top: 25px;" />'
return f'<img src="/token/{token}W" width="32px" height="32px" style="margin-top: 25px;" />'
def site(data):
title = data['title']
link_0 = data['link_0']
link_1 = data['link_1']
link_2 = data['link_2']
link_3 = data['link_3']
link_0_url = data['link_0_url']
link_1_url = data['link_1_url']
link_2_url = data['link_2_url']
link_3_url = data['link_3_url']
image = f'/avatar/{data["image"]}'
links = link(link_0,link_0_url,data['btn_bg'],data['btn_fg'])
links += link(link_1,link_1_url,data['btn_bg'],data['btn_fg'])
links += link(link_2,link_2_url,data['btn_bg'],data['btn_fg'])
links += link(link_3,link_3_url,data['btn_bg'],data['btn_fg'])
socials = social_links(data['socials'])
addresses = address_links(data['address'],data['fg_0'])
html = render_template('page.html', title=title, links=links, image=image,
bg_0=data['bg_0'], bg_1=data['bg_1'], fg_0=data['fg_0'],
btn_bg=data['btn_bg'], btn_fg=data['btn_fg'],
socials=socials, addresses=addresses)
html = html.replace('/assets/img/favicon.png',f'/avatar/{data["image"]}')
return html
def hex_to_rgb(hex_color):
hex_color = hex_color.lstrip('#')
return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
def color_distance(color1, color2):
return sum((c1 - c2) ** 2 for c1, c2 in zip(color1, color2)) ** 0.5
def is_closer_to_black(hex_color):
white = (255, 255, 255)
black = (0, 0, 0)
color = hex_to_rgb(hex_color)
distance_to_white = color_distance(color, white)
distance_to_black = color_distance(color, black)
return distance_to_black < distance_to_white

5
requirements.txt Normal file
View File

@ -0,0 +1,5 @@
flask
python-dotenv
gunicorn
requests
apscheduler

52
templates/404.html Normal file
View File

@ -0,0 +1,52 @@
<!DOCTYPE html>
<html data-bs-theme="light" lang="en-au">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>HNS Links</title>
<meta name="description" content="Create a links page for your domain">
<link rel="icon" type="image/png" sizes="600x627" href="/assets/img/favicon.png">
<link rel="icon" type="image/png" sizes="600x627" href="/assets/img/favicon.png">
<link rel="icon" type="image/png" sizes="600x627" href="/assets/img/favicon.png">
<link rel="icon" type="image/png" sizes="600x627" href="/assets/img/favicon.png">
<link rel="icon" type="image/png" sizes="600x627" href="/assets/img/favicon.png">
<link rel="stylesheet" href="/assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800&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="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
</head>
<body><!-- partial:index.partial.html -->
<p>HTTP: <span>404</span></p>
<div style="text-align: center;">
<div style="display: inline-block; text-align: left;">
<code><span>this_page</span>.<em>not_found</em> = true;</code>
<code><span>if</span> (<b>you_spelt_it_wrong</b>){<br><span>autocorrect</span>.<em>enable</em>();<br>}</code>
<code><span>else if</span> (<b>we_screwed_up</b>){<br><em>alert</em>(<i>"Strewth mate, you're lost! Fair dinkum, why are you here?"</i>);<br><span>window</span>.<em>location</em> = home;<br>}</code>
</div>
</div>
<!-- partial -->
<script type="text/javascript">
function callback(){
return function(){
alert("Strewth mate, you're lost! Fair dinkum, why are you here?");
window.location = '/';
}
}
var a = "world";
setTimeout(callback(), 5000);
</script>
<script src="/assets/js/jquery.min.js"></script>
<script src="/assets/bootstrap/js/bootstrap.min.js"></script>
<script src="https://auth.varo.domains/v1"></script>
<script src="/assets/js/script.min.js"></script>
<script src="/assets/js/404.min.js"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
templates/assets/css/404.min.css vendored Normal file
View File

@ -0,0 +1 @@
*{padding:0;margin:0;box-sizing:border-box}body{background:#282828;overflow-y:auto}p{font-family:Bevan,cursive;font-size:130px;margin:10vh 0 0;text-align:center;overflow-y:auto;letter-spacing:5px;color:#000;text-shadow:2px 2px 3px rgba(255,255,255,.1);-webkit-background-clip:text;-moz-background-clip:text;background-clip:text}p span{font-size:1.2em}code{color:#bdbdbd;text-align:left;display:block;overflow-y:auto;font-size:16px;margin:0 30px 25px}code span{color:#f0c674}code i{color:#b5bd68}code em{color:#b294bb;font-style:unset}code b{color:#81a2be;font-weight:500;overflow-y:auto}a{color:#8abeb7;font-family:monospace;font-size:20px;text-decoration:underline;overflow-y:auto;margin-top:10px;display:inline-block}@media screen and (max-width:880px){p{font-size:14vw;overflow-y:auto}}

1
templates/assets/css/styles.min.css vendored Normal file
View File

@ -0,0 +1 @@
.social-icons{color:#313437;background-color:#fff;padding:70px 0}@media (max-width:767px){.social-icons{padding:50px 0}}.social-div{display:flex;justify-content:center;align-items:center}.social-list{max-width:100%;display:flex;list-style:none;gap:2.5rem}@media (max-width:500px){.social-list{gap:1.5rem}}@media (max-width:420px){.social-list{gap:0}}.custom-button{max-width:500px}.addresses{margin-bottom:20px;display:flex;justify-content:center;align-items:center}.social-icons i{color:#757980;margin:0 10px;width:60px;height:60px;border:1px solid #c8ced7;text-align:center;border-radius:50%;line-height:60px;display:inline-block}.social-link a{text-decoration:none;width:4.8rem;height:4.8rem;background-color:#f0f9fe;border-radius:50%;display:flex;justify-content:center;align-items:center;position:relative;z-index:1;border:3px solid #f0f9fe;overflow:hidden}.social-link a::before{content:"";position:absolute;width:100%;height:100%;background:#000;z-index:0;scale:1 0;transform-origin:bottom;transition:scale .5s}.social-link:hover a::before{scale:1 1}.icon{font-size:2rem;color:#011827;transition:.5s;z-index:2}.social-link a:hover .icon{color:#fff;transform:rotateY(360deg)}

Binary file not shown.

11
templates/assets/fonts/ionicons.min.css vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 326 KiB

Binary file not shown.

Binary file not shown.

BIN
templates/assets/img/16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
templates/assets/img/32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

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

@ -0,0 +1 @@
function type(e,t){var n=document.getElementsByTagName("code")[e].innerHTML.toString(),a=0;document.getElementsByTagName("code")[e].innerHTML="",setTimeout((function(){var t=setInterval((function(){a++,document.getElementsByTagName("code")[e].innerHTML=n.slice(0,a)+"|",a==n.length&&(clearInterval(t),document.getElementsByTagName("code")[e].innerHTML=n)}),10)}),t)}type(0,0),type(1,600),type(2,1300);

2
templates/assets/js/jquery.min.js vendored Normal file

File diff suppressed because one or more lines are too long

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

@ -0,0 +1 @@
!function(){"use strict";!function(){if("requestAnimationFrame"in window&&!/Mobile|Android/.test(navigator.userAgent)){var e=document.querySelectorAll("[data-bss-parallax]");if(e.length){var n,t=[];window.addEventListener("scroll",a),window.addEventListener("resize",a),a()}}function a(){t.length=0;for(var a=0;a<e.length;a++){var o=e[a].getBoundingClientRect(),i=parseFloat(e[a].getAttribute("data-bss-parallax-speed"),10)||.5;o.bottom>0&&o.top<window.innerHeight&&t.push({speed:i,node:e[a]})}cancelAnimationFrame(n),t.length&&(n=requestAnimationFrame(r))}function r(){for(var e=0;e<t.length;e++){var n=t[e].node,a=t[e].speed;n.style.transform="translate3d(0, "+-window.scrollY*a+"px, 0)"}}}()}();var varo=new Varo;

1
templates/email.html Normal file
View File

@ -0,0 +1 @@
<li class="social-link"><a href="{{url}}" target="_blank"><i class="fas fa-envelope icon"></i></a></li>

3
templates/github.html Normal file
View File

@ -0,0 +1,3 @@
<li class="social-link"><a href="{{url}}" target="_blank"><svg class="bi bi-github icon" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8"></path>
</svg></a></li>

147
templates/index.html Normal file
View File

@ -0,0 +1,147 @@
<!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>HNS Links</title>
<meta name="description" content="Create a links page for your domain">
<link rel="icon" type="image/png" sizes="600x627" href="/assets/img/favicon.png">
<link rel="icon" type="image/png" sizes="600x627" href="/assets/img/favicon.png">
<link rel="icon" type="image/png" sizes="600x627" href="/assets/img/favicon.png">
<link rel="icon" type="image/png" sizes="600x627" href="/assets/img/favicon.png">
<link rel="icon" type="image/png" sizes="600x627" href="/assets/img/favicon.png">
<link rel="stylesheet" href="/assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800&amp;display=swap">
<link rel="stylesheet" href="/assets/css/styles.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
</head>
<body>
<nav class="navbar navbar-expand-md sticky-top py-3 navbar-dark" id="mainNav">
<div class="container"><a class="navbar-brand d-flex align-items-center" href="/"><img src="/assets/img/favicon.png" width="45px"><span>&nbsp;HNS Links</span></a><button data-bs-toggle="collapse" class="navbar-toggler" data-bs-target="#navcol-1"><span class="visually-hidden">Toggle navigation</span><span class="navbar-toggler-icon"></span></button>
<div class="collapse navbar-collapse" id="navcol-1">
<ul class="navbar-nav mx-auto">
<li class="nav-item"><a class="nav-link active" href="/index.html">Home</a></li>
<li class="nav-item"><a class="nav-link active" href="/#contact">Contact</a></li>
</ul><button class="btn btn-primary shadow" type="button" onclick="{{varo}}">Create/edit site</button>
</div>
</div>
</nav>
<header class="bg-dark">
<div class="container pt-4 pt-xl-5">
<h1 class="text-center" style="color: rgb(255,0,0);">{{error}}</h1>
<div class="row pt-5">
<div class="col-md-8 col-xl-6 text-center text-md-start mx-auto">
<div class="text-center">
<h1 class="fw-bold">The best solution for your HNS Domain</h1>
</div>
</div>
<div class="col-12 col-lg-10 mx-auto">
<div class="position-relative" style="display: flex;flex-wrap: wrap;justify-content: flex-end;">
<div style="position: relative;flex: 0 0 45%;transform: translate3d(-15%, 35%, 0);"><img class="img-fluid" data-bss-parallax="" data-bss-parallax-speed="0.8" src="/assets/img/products/3.jpg"></div>
<div style="position: relative;flex: 0 0 45%;transform: translate3d(-5%, 20%, 0);"><img class="img-fluid" data-bss-parallax="" data-bss-parallax-speed="0.4" src="/assets/img/products/2.jpg"></div>
<div style="position: relative;flex: 0 0 60%;transform: translate3d(0, 0%, 0);"><img class="img-fluid" data-bss-parallax="" data-bss-parallax-speed="0.25" src="/assets/img/products/1.jpg"></div>
</div>
</div>
</div>
</div>
</header>
<section>
<div class="container bg-dark py-5">
<div class="row">
<div class="col-md-8 col-xl-6 text-center mx-auto">
<p class="fw-bold text-success mb-2">Features</p>
<h3 class="fw-bold">What we can do for you</h3>
</div>
</div>
<div class="py-5 p-lg-5">
<div class="row row-cols-1 row-cols-md-2 mx-auto" style="max-width: 900px;">
<div class="col mb-5">
<div class="card shadow-sm">
<div class="card-body px-4 py-5 px-md-5">
<h5 class="fw-bold card-title">Easy to create site</h5>
<p class="text-muted card-text mb-4">We use varo auth to allow you to login with any Handshake domain to allow building your page.<br>Once you login just fill in some info and publish your site in minutes.</p>
</div>
</div>
</div>
<div class="col mb-5">
<div class="card shadow-sm">
<div class="card-body px-4 py-5 px-md-5">
<h5 class="fw-bold card-title">Open Source</h5>
<p class="text-muted card-text mb-4">Want full control over your website? Deploy this entire system on your server for free</p><a class="btn btn-primary shadow" role="button" target="_blank" href="https://git.woodburn.au/nathanwoodburn/hns-links">Learn more</a>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<section id="contact" class="py-5">
<div class="container">
<div class="row mb-5">
<div class="col-md-8 col-xl-6 text-center mx-auto">
<p class="fw-bold text-success mb-2">Contact</p>
<h2 class="fw-bold">How you can reach us</h2>
</div>
</div>
<div class="row d-flex justify-content-center">
<div class="col-md-4 col-xl-4 d-flex justify-content-center justify-content-xl-start">
<div class="d-flex flex-wrap flex-md-column justify-content-md-start align-items-md-start h-100">
<div class="d-flex align-items-center p-3">
<div class="bs-icon-md bs-icon-circle bs-icon-primary shadow d-flex flex-shrink-0 justify-content-center align-items-center d-inline-block bs-icon bs-icon-md"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" class="bi bi-discord">
<path d="M13.545 2.907a13.227 13.227 0 0 0-3.257-1.011.05.05 0 0 0-.052.025c-.141.25-.297.577-.406.833a12.19 12.19 0 0 0-3.658 0 8.258 8.258 0 0 0-.412-.833.051.051 0 0 0-.052-.025c-1.125.194-2.22.534-3.257 1.011a.041.041 0 0 0-.021.018C.356 6.024-.213 9.047.066 12.032c.001.014.01.028.021.037a13.276 13.276 0 0 0 3.995 2.02.05.05 0 0 0 .056-.019c.308-.42.582-.863.818-1.329a.05.05 0 0 0-.01-.059.051.051 0 0 0-.018-.011 8.875 8.875 0 0 1-1.248-.595.05.05 0 0 1-.02-.066.051.051 0 0 1 .015-.019c.084-.063.168-.129.248-.195a.05.05 0 0 1 .051-.007c2.619 1.196 5.454 1.196 8.041 0a.052.052 0 0 1 .053.007c.08.066.164.132.248.195a.051.051 0 0 1-.004.085 8.254 8.254 0 0 1-1.249.594.05.05 0 0 0-.03.03.052.052 0 0 0 .003.041c.24.465.515.909.817 1.329a.05.05 0 0 0 .056.019 13.235 13.235 0 0 0 4.001-2.02.049.049 0 0 0 .021-.037c.334-3.451-.559-6.449-2.366-9.106a.034.034 0 0 0-.02-.019Zm-8.198 7.307c-.789 0-1.438-.724-1.438-1.612 0-.889.637-1.613 1.438-1.613.807 0 1.45.73 1.438 1.613 0 .888-.637 1.612-1.438 1.612m5.316 0c-.788 0-1.438-.724-1.438-1.612 0-.889.637-1.613 1.438-1.613.807 0 1.451.73 1.438 1.613 0 .888-.631 1.612-1.438 1.612"></path>
</svg></div>
<div class="px-2">
<h6 class="fw-bold mb-0">Discord</h6><a href="https://discord.hns.au" target="_blank">Join our server</a>
</div>
</div>
<div class="d-flex align-items-center p-3">
<div class="bs-icon-md bs-icon-circle bs-icon-primary shadow d-flex flex-shrink-0 justify-content-center align-items-center d-inline-block bs-icon bs-icon-md"><svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 24 24" width="1em" fill="currentColor" style="color: #ffffff;">
<path d="M0 0h24v24H0z" fill="none"></path>
<path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"></path>
</svg></div>
<div class="px-2">
<h6 class="fw-bold mb-0">Email</h6><a href="mailto:links@hns.au">links@hns.au</a>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<footer class="bg-dark">
<div class="container py-4 py-lg-5">
<div class="row justify-content-center">
<div class="col-sm-4 col-md-3 text-center text-lg-start d-flex flex-column">
<h3 class="fs-6 fw-bold">Services</h3>
<ul class="list-unstyled">
<li><a href="https://hnshosting.au" target="_blank">Wordpress Hosting</a></li>
<li><a href="https://firewallet.au" target="_blank">FireWallet</a></li>
</ul>
</div>
<div class="col-sm-4 col-md-3 text-center text-lg-start d-flex flex-column">
<h3 class="fs-6 fw-bold">About</h3>
<ul class="list-unstyled">
<li><a href="https://hns.au" target="_blank">HNSAU</a></li>
<li><a href="https://nathan.woodburn.au" target="_blank">Nathan.Woodburn/</a></li>
</ul>
</div>
<div class="col-lg-3 text-center text-lg-start d-flex flex-column align-items-center order-first align-items-lg-start order-lg-last">
<div class="fw-bold d-flex align-items-center mb-2"><span>HNS Links</span></div>
<p class="text-muted">Easy to manage links page on Handshake domains</p>
</div>
</div>
<hr>
<div class="text-muted d-flex justify-content-between align-items-center pt-3">
<p class="mb-0">Copyright © {{year}} Nathan.Woodburn/</p>
</div>
</div>
</footer>
<script src="/assets/js/jquery.min.js"></script>
<script src="/assets/bootstrap/js/bootstrap.min.js"></script>
<script src="https://auth.varo.domains/v1"></script>
<script src="/assets/js/script.min.js"></script>
</body>
</html>

57
templates/page.html Normal file
View File

@ -0,0 +1,57 @@
<!DOCTYPE html>
<html data-bs-theme="light" lang="en" style="text-align: center;">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>{{title}}</title>
<meta name="twitter:title" content="{{title}}">
<meta property="og:title" content="{{title}}">
<meta name="description" content="{{title}}">
<meta property="og:description" content="{{title}}">
<meta name="twitter:description" content="{{title}}">
<link rel="icon" type="image/png" sizes="600x627" href="/assets/img/favicon.png">
<link rel="icon" type="image/png" sizes="600x627" href="/assets/img/favicon.png">
<link rel="icon" type="image/png" sizes="600x627" href="/assets/img/favicon.png">
<link rel="icon" type="image/png" sizes="600x627" href="/assets/img/favicon.png">
<link rel="icon" type="image/png" sizes="600x627" href="/assets/img/favicon.png">
<link rel="stylesheet" href="/assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800&amp;display=swap">
<link rel="stylesheet" href="/assets/css/styles.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
</head>
<body style="background: transparent;">
<div style="border-radius: 20px;"><img class="rounded" src="{{image}}" width="128px" height="128px" style="margin-top: 25px;">
<h1 style="margin-top: 10px;">{{title}}</h1> <div class="addresses">
{{addresses|safe}}
</div>
<!-- <div style="height:25px;"></div> --> <div class="social-div">
<ul class="list-unstyled social-list">
{{socials|safe}}
</ul>
</div>
<div style="height:25px;"></div><style>
.custom-button:hover {
background-color: {{btn_fg}} !important;
color: {{btn_bg}} !important;
}
html {
background: linear-gradient({{bg_0}} 0%, {{bg_1}} 100%); color:{{fg_0}};"
}
body {
min-height: 100vh;
}
</style>
<div>
{{links|safe}}
</div>
</div>
<script src="/assets/js/jquery.min.js"></script>
<script src="/assets/bootstrap/js/bootstrap.min.js"></script>
<script src="https://auth.varo.domains/v1"></script>
<script src="/assets/js/script.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,24 @@
<div
style="border-radius: 20px;border-width: 2px;border-style: solid;background: linear-gradient({{bg_0}} 0%, {{bg_1}} 100%); color:{{fg_0}};">
{{image|safe}}
<h1>{{title}}</h1>
<style>
.custom-button:hover {
background-color: {{btn_fg}} !important;
color: {{btn_bg}} !important;
}
</style>
<div class="addresses">
{{addresses|safe}}
</div>
<div class="social-div">
<ul class="list-unstyled social-list">
{{socials|safe}}
</ul>
</div>
<div>
{{links|safe}}
</div>
</div>

157
templates/site.html Normal file
View File

@ -0,0 +1,157 @@
<!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>Home - HNS Links</title>
<meta name="description" content="Create a links page for your domain">
<link rel="icon" type="image/png" sizes="600x627" href="/assets/img/favicon.png">
<link rel="icon" type="image/png" sizes="600x627" href="/assets/img/favicon.png">
<link rel="icon" type="image/png" sizes="600x627" href="/assets/img/favicon.png">
<link rel="icon" type="image/png" sizes="600x627" href="/assets/img/favicon.png">
<link rel="icon" type="image/png" sizes="600x627" href="/assets/img/favicon.png">
<link rel="stylesheet" href="/assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800&amp;display=swap">
<link rel="stylesheet" href="/assets/css/styles.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
</head>
<body>
<nav class="navbar navbar-expand-md sticky-top py-3 navbar-dark" id="mainNav">
<div class="container"><a class="navbar-brand d-flex align-items-center" href="/"><img src="/assets/img/favicon.png" width="45px"><span>&nbsp;HNS Links</span></a><button data-bs-toggle="collapse" class="navbar-toggler" data-bs-target="#navcol-1"><span class="visually-hidden">Toggle navigation</span><span class="navbar-toggler-icon"></span></button>
<div class="collapse navbar-collapse" id="navcol-1">
<ul class="navbar-nav mx-auto"></ul><a class="btn btn-primary shadow" role="button" href="/preview" style="margin-right: 10px;">Preview</a><a class="btn btn-primary shadow" role="button" href="/logout">Logout</a>
</div>
</div>
</nav>
<header class="bg-dark">
<div class="container pt-4 pt-xl-5">
<div class="row pt-5">
<div class="col-md-8 col-xl-6 text-center text-md-start mx-auto">
<div class="text-center">
<p class="fw-bold text-success mb-2">{{domain}}/</p>
<h1 class="fw-bold">Manage your site</h1>
</div>
</div>
</div>
</div>
</header>
<section>
<div style="padding-top: 25px;padding-bottom: 25px;">
<div class="container">
<div class="row">
<div class="col-md-6">
<form method="post" enctype="multipart/form-data">
<h1>Content</h1><label class="form-label" style="font-size: 24px;margin-right: 10px;">Image</label><input class="form-control" type="file" style="margin-bottom: 25px;width: auto;display: inline-block;" name="image"><button class="btn btn-primary" type="button" style="display: inline;padding-top: 8px;padding-right: 12px;padding-bottom: 8px;padding-left: 12px;margin-left: 5px;"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" class="bi bi-trash3-fill">
<path d="M11 1.5v1h3.5a.5.5 0 0 1 0 1h-.538l-.853 10.66A2 2 0 0 1 11.115 16h-6.23a2 2 0 0 1-1.994-1.84L2.038 3.5H1.5a.5.5 0 0 1 0-1H5v-1A1.5 1.5 0 0 1 6.5 0h3A1.5 1.5 0 0 1 11 1.5m-5 0v1h4v-1a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5M4.5 5.029l.5 8.5a.5.5 0 1 0 .998-.06l-.5-8.5a.5.5 0 1 0-.998.06Zm6.53-.528a.5.5 0 0 0-.528.47l-.5 8.5a.5.5 0 0 0 .998.058l.5-8.5a.5.5 0 0 0-.47-.528ZM8 4.5a.5.5 0 0 0-.5.5v8.5a.5.5 0 0 0 1 0V5a.5.5 0 0 0-.5-.5"></path>
</svg></button><input class="form-control" type="text" style="margin-bottom: 25px;" name="title" value="{{title}}" placeholder="Title">
<div style="margin-bottom: 25px;">
<h4>Socials</h4><input class="form-control" type="text" style="display: inline-block;width: auto;" name="email" value="{{socials['email']}}" placeholder="Email"><input class="form-control" type="text" style="display: inline-block;width: auto;" name="twitter" value="{{socials['twitter']}}" placeholder="Twitter"><input class="form-control" type="text" style="display: inline-block;width: auto;" name="github" value="{{socials['github']}}" placeholder="Github"><input class="form-control" type="text" style="display: inline-block;width: auto;" name="youtube" value="{{socials['youtube']}}" placeholder="YouTube">
</div>
<div style="margin-bottom: 25px;">
<h4>Addresses</h4><input class="form-control" type="text" style="display: inline-block;width: auto;" name="hns" value="{{address['hns']}}" placeholder="HNS Address"><input class="form-control" type="text" style="display: inline-block;width: auto;" name="eth" value="{{address['eth']}}" placeholder="ETH Address"><input class="form-control" type="text" style="display: inline-block;width: auto;" name="btc" value="{{address['btc']}}" placeholder="BTC Address"><input class="form-control" type="text" style="display: inline-block;width: auto;" name="sol" value="{{address['sol']}}" placeholder="SOL Address">
</div>
<div style="margin-bottom: 25px;">
<h4>Link</h4><input class="form-control" type="text" style="display: inline-block;width: 45%;" name="link_0" value="{{link_0}}" placeholder="Link Name"><input class="form-control" type="text" style="display: inline-block;width: auto;" name="link_0_url" value="{{link_0_url}}" placeholder="Url">
</div>
<div style="margin-bottom: 25px;">
<h4>Link</h4><input class="form-control" type="text" style="display: inline-block;width: 45%;" name="link_1" value="{{link_1}}" placeholder="Link Name"><input class="form-control" type="text" style="display: inline-block;width: auto;" name="link_1_url" value="{{link_1_url}}" placeholder="Url">
</div>
<div style="margin-bottom: 25px;">
<h4>Link</h4><input class="form-control" type="text" style="display: inline-block;width: 45%;" name="link_2" value="{{link_2}}" placeholder="Link Name"><input class="form-control" type="text" style="display: inline-block;width: auto;" name="link_2_url" value="{{link_2_url}}" placeholder="Url">
</div>
<div style="margin-bottom: 25px;">
<h4>Link</h4><input class="form-control" type="text" style="display: inline-block;width: 45%;" name="link_3" value="{{link_3}}" placeholder="Link Name"><input class="form-control" type="text" style="display: inline-block;width: auto;" name="link_3_url" value="{{link_3_url}}" placeholder="Url">
</div>
<div style="margin-bottom: 25px;">
<p>Main Colours</p>
<div>
<p style="display: inline-block;">Background Top</p><input class="form-control form-control-color" type="color" style="display: inline-block;margin-left: 25px;height: 2em;" name="bg_0" value="{{bg_0}}">
</div>
<div>
<p style="display: inline-block;">Background Bottom</p><input class="form-control form-control-color" type="color" style="display: inline-block;margin-left: 25px;height: 2em;" value="{{bg_1}}" name="bg_1">
</div>
<div>
<p style="display: inline-block;">Text</p><input class="form-control form-control-color" type="color" style="display: inline-block;margin-left: 25px;height: 2em;" name="fg_0" value="{{fg_0}}">
</div>
</div>
<div style="margin-bottom: 25px;">
<p>Button Colours</p>
<div>
<p style="display: inline-block;">Background</p><input class="form-control form-control-color" type="color" style="display: inline-block;margin-left: 25px;height: 2em;" name="btn_bg" value="{{btn_bg}}">
</div>
<div>
<p style="display: inline-block;">Text</p><input class="form-control form-control-color" type="color" style="display: inline-block;margin-left: 25px;height: 2em;" name="btn_fg" value="{{btn_fg}}">
</div>
</div><input class="btn btn-primary" type="submit" value="Save" style="margin-right: 20px;"><a class="btn btn-primary" role="button" href="/publish">Publish</a>
</form>
</div>
<div class="col-md-6" style="text-align: center;">{{preview|safe}}</div>
</div>
</div>
</div>
</section>
<section>
<div style="max-width: 1000px;margin: auto;">
<h2 class="text-center">DNS Records</h2>
<div class="table-responsive">
<table class="table table-dark">
<thead>
<tr>
<th>Type</th>
<th>Name</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>A</td>
<td>@</td>
<td>{{ip}}</td>
</tr>
<tr>
<td>TLSA</td>
<td>_443._tcp</td>
<td>{{tlsa}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<footer class="bg-dark">
<div class="container py-4 py-lg-5">
<div class="row justify-content-center">
<div class="col-sm-4 col-md-3 text-center text-lg-start d-flex flex-column">
<h3 class="fs-6 fw-bold">Services</h3>
<ul class="list-unstyled">
<li><a href="https://hnshosting.au" target="_blank">Wordpress Hosting</a></li>
<li><a href="https://firewallet.au" target="_blank">FireWallet</a></li>
</ul>
</div>
<div class="col-sm-4 col-md-3 text-center text-lg-start d-flex flex-column">
<h3 class="fs-6 fw-bold">About</h3>
<ul class="list-unstyled">
<li><a href="https://hns.au" target="_blank">HNSAU</a></li>
<li><a href="https://nathan.woodburn.au" target="_blank">Nathan.Woodburn/</a></li>
</ul>
</div>
<div class="col-lg-3 text-center text-lg-start d-flex flex-column align-items-center order-first align-items-lg-start order-lg-last">
<div class="fw-bold d-flex align-items-center mb-2"><span>HNS Links</span></div>
<p class="text-muted">Easy to manage links page on Handshake domains</p>
</div>
</div>
<hr>
<div class="text-muted d-flex justify-content-between align-items-center pt-3">
<p class="mb-0">Copyright © {{year}} Nathan.Woodburn/</p>
</div>
</div>
</footer>
<script src="/assets/js/jquery.min.js"></script>
<script src="/assets/bootstrap/js/bootstrap.min.js"></script>
<script src="https://auth.varo.domains/v1"></script>
<script src="/assets/js/script.min.js"></script>
</body>
</html>

8
templates/twitter.html Normal file
View File

@ -0,0 +1,8 @@
<li class="social-link"><a href="{{url}}" target="_blank"><svg class="bi bi-twitter-x icon"
xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">
<path
d="M12.6.75h2.454l-5.36 6.142L16 15.25h-4.937l-3.867-5.07-4.425 5.07H.316l5.733-6.57L0 .75h5.063l3.495 4.633L12.601.75Zm-.86 13.028h1.36L4.323 2.145H2.865l8.875 11.633Z">
</path>
</svg>
</a>
</li>

3
templates/youtube.html Normal file
View File

@ -0,0 +1,3 @@
<li class="social-link"><a href="{{url}}" target="_blank"><svg class="bi bi-youtube icon" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">
<path d="M8.051 1.999h.089c.822.003 4.987.033 6.11.335a2.01 2.01 0 0 1 1.415 1.42c.101.38.172.883.22 1.402l.01.104.022.26.008.104c.065.914.073 1.77.074 1.957v.075c-.001.194-.01 1.108-.082 2.06l-.008.105-.009.104c-.05.572-.124 1.14-.235 1.558a2.007 2.007 0 0 1-1.415 1.42c-1.16.312-5.569.334-6.18.335h-.142c-.309 0-1.587-.006-2.927-.052l-.17-.006-.087-.004-.171-.007-.171-.007c-1.11-.049-2.167-.128-2.654-.26a2.007 2.007 0 0 1-1.415-1.419c-.111-.417-.185-.986-.235-1.558L.09 9.82l-.008-.104A31.4 31.4 0 0 1 0 7.68v-.123c.002-.215.01-.958.064-1.778l.007-.103.003-.052.008-.104.022-.26.01-.104c.048-.519.119-1.023.22-1.402a2.007 2.007 0 0 1 1.415-1.42c.487-.13 1.544-.21 2.654-.26l.17-.007.172-.006.086-.003.171-.007A99.788 99.788 0 0 1 7.858 2h.193zM6.4 5.209v4.818l4.157-2.408z"></path>
</svg></a></li>

20
tlsa.sh Executable file
View File

@ -0,0 +1,20 @@
#!/bin/bash
# Get domain name from arguments
domain=$1
mkdir certs/$domain
openssl req -x509 -newkey rsa:4096 -sha256 -days 365 -nodes \
-keyout certs/$domain/cert.key -out certs/$domain/cert.crt \
-extensions ext -config \
<(echo "[req]";
echo distinguished_name=req;
echo "[ext]";
echo "keyUsage=critical,digitalSignature,keyEncipherment";
echo "extendedKeyUsage=serverAuth";
echo "basicConstraints=critical,CA:FALSE";
echo "subjectAltName=DNS:$domain,DNS:*.$domain";
) -subj "/CN=*.$domain"
echo -n "3 1 1 " && openssl x509 -in certs/$domain/cert.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | xxd -p -u -c 32