Compare commits

...

4 Commits

Author SHA1 Message Date
de2ffd022a
feat: Add HNS Login to HNS Links 2024-06-19 14:21:57 +10:00
e844936a00
fix: Add https.js 2024-03-27 20:41:24 +11:00
dcc28483e3
fix: Inject ssl based on host only 2024-03-27 20:37:02 +11:00
1b6536d8ae
feat: Add nostr 2024-03-27 16:59:22 +11:00
13 changed files with 384 additions and 8 deletions

179
main.py
View File

@ -8,6 +8,7 @@ import render
import secrets import secrets
import nginx import nginx
import threading import threading
import nostr as nostr_module
app = Flask(__name__) app = Flask(__name__)
dotenv.load_dotenv() dotenv.load_dotenv()
@ -50,6 +51,10 @@ if not os.path.isdir('certs'):
def send_report(path): def send_report(path):
return send_from_directory('templates/assets', path) return send_from_directory('templates/assets', path)
@app.route('/https.js')
def httpsJS():
return send_from_directory('templates', 'https.js')
@app.route('/favicon.png') @app.route('/favicon.png')
def faviconPNG(): def faviconPNG():
return send_from_directory('templates/assets/img', 'favicon.png') return send_from_directory('templates/assets/img', 'favicon.png')
@ -64,7 +69,7 @@ def index():
for i in cookies: for i in cookies:
if i['cookie'] == auth: if i['cookie'] == auth:
return render_template('index.html',varo="window.location = '/site';", year=datetime.datetime.now().year) 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) return render_template('index.html',varo=render.hnslogin(), year=datetime.datetime.now().year)
# Remove any ports # Remove any ports
host = request.host.split(':')[0] host = request.host.split(':')[0]
# Get content from site # Get content from site
@ -72,7 +77,7 @@ def index():
with open(f'sites/{host}.json') as file: with open(f'sites/{host}.json') as file:
data = json.load(file) data = json.load(file)
return render.site(data, True) return render.site(data, host)
return redirect(f'https://{DOMAINS[0]}') return redirect(f'https://{DOMAINS[0]}')
@app.route('/site') @app.route('/site')
@ -109,7 +114,8 @@ def site():
"btn_bg": "#2c54cf", "btn_bg": "#2c54cf",
"btn_fg": "#ffffff", "btn_fg": "#ffffff",
"socials": [], "socials": [],
"address": [] "address": [],
"nostrs": []
} }
@ -298,6 +304,104 @@ def publish():
response.set_cookie('auth', '', expires=0) response.set_cookie('auth', '', expires=0)
return response return response
@app.route('/nostr')
def nostr():
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)
nostr = []
if 'nostr' in data:
nostr = data['nostr']
return render_template('nostr.html',year=datetime.datetime.now().year, domain=i['name'],nostr=nostr)
response = make_response(redirect('/'))
response.set_cookie('auth', '', expires=0)
return response
@app.route('/nostr', methods=['POST'])
def nostr_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:
return redirect('/site')
nostr = []
if 'nostr' in data:
nostr = data['nostr']
# Check for new nostr links
if 'new-name' in request.form and 'new-pub' in request.form:
name = request.form['new-name']
pub = request.form['new-pub']
id = len(nostr)
for link in nostr:
if link['name'] == name:
link['pub'] = pub
data['nostr'] = nostr
with open(f'sites/{i["name"]}.json', 'w') as file:
json.dump(data, file)
return redirect('/nostr')
if link['id'] >= id:
id = link['id'] + 1
nostr.append({'name': name, 'pub': pub, 'id': id})
data['nostr'] = nostr
with open(f'sites/{i["name"]}.json', 'w') as file:
json.dump(data, file)
return redirect('/nostr')
response = make_response(redirect('/'))
response.set_cookie('auth', '', expires=0)
return response
@app.route('/nostr/delete/<int:id>')
def nostr_delete(id):
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:
return redirect('/site')
nostr = []
if 'nostr' in data:
nostr = data['nostr']
nostr = [i for i in nostr if i['id'] != id]
data['nostr'] = nostr
with open(f'sites/{i["name"]}.json', 'w') as file:
json.dump(data, file)
return redirect('/nostr')
response = make_response(redirect('/'))
response.set_cookie('auth', '', expires=0)
return response
@app.route('/.well-known/wallets/<path:path>') @app.route('/.well-known/wallets/<path:path>')
def wallets(path): def wallets(path):
# Check if host is in domains # Check if host is in domains
@ -333,6 +437,43 @@ def wallets(path):
return response return response
return render_template('404.html', year=datetime.datetime.now().year), 404 return render_template('404.html', year=datetime.datetime.now().year), 404
@app.route('/.well-known/nostr.json')
def nostr_account():
# 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)
if 'nostr' in data:
nostr = data['nostr']
# Return as plain text
response = make_response(nostr_module.json(nostr))
response.headers['Content-Type'] = 'text/plain'
response.headers.add('Access-Control-Allow-Origin', '*')
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)
if 'nostr' in data:
nostr = data['nostr']
# Return as plain text
response = make_response(nostr_module.json(nostr))
response.headers['Content-Type'] = 'text/plain'
response.headers.add('Access-Control-Allow-Origin', '*')
return response
return render_template('404.html', year=datetime.datetime.now().year), 404
@app.route('/publishing') @app.route('/publishing')
def publishing(): def publishing():
@ -356,6 +497,38 @@ def auth():
resp.set_cookie('auth', auth_cookie) resp.set_cookie('auth', auth_cookie)
return resp return resp
@app.route('/auth', methods=['GET'])
def auth_get():
global cookies
if 'username' not in request.args:
return redirect('/?error=Failed to login&reason=No username')
if 'token' not in request.args:
return redirect('/?error=Failed to login&reason=No token')
username = request.args['username']
token = request.args['token']
# Check if user is valid
r = requests.get(f'https://login.hns.au/auth/user?token={token}')
r = r.json()
if 'error' in r:
return redirect('/?error=Failed to login&reason=' + r['error'])
if r['username'] != username:
return redirect('/?error=Failed to login&reason=Username mismatch')
auth_cookie = secrets.token_hex(12 // 2)
cookies.append({'name': username, 'cookie': auth_cookie})
with open('cookies.json', 'w') as file:
json.dump(cookies, file)
resp = make_response(redirect('/site'))
resp.set_cookie('auth', auth_cookie)
return resp
@app.route('/logout') @app.route('/logout')
def logout(): def logout():
global cookies global cookies

29
nostr.py Normal file
View File

@ -0,0 +1,29 @@
from base64 import b32decode
from base64 import b16encode
from bech32 import bech32_decode
from bech32 import bech32_encode
B32 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
def add_padding(base32_len):
bits = base32_len * 5
padding_size = (8 - (bits % 8)) % 8
return padding_size
def npub_to_hex(npub):
hrd, data = bech32_decode(npub)
b32_data = [B32[index] for index in data]
data_str = "".join(b32_data)
data_length = len(data_str)
data_str += "=" * add_padding(data_length)
decoded_data = b32decode(data_str)
b16_encoded_data = b16encode(decoded_data)
hex_str = b16_encoded_data.decode("utf-8").lower()
return hex_str
def json(links):
names = {}
for link in links:
names[link['name']] = npub_to_hex(link['pub'])
return {'names':names}

View File

@ -11,6 +11,14 @@ def varo_login():
} }
});''' });'''
def hnslogin():
# Redirect to https://login.hns.au/auth?return={{scheme}}{{host}}/auth
return """
const { protocol, hostname, port } = window.location;
const rootUrl = `${protocol}//${hostname}${port ? `:${port}` : ''}/auth`;
window.location.href = `https://login.hns.au/auth?return=${encodeURIComponent(rootUrl)}`;
"""
def preview(data): def preview(data):
title = data['title'] title = data['title']
@ -63,7 +71,7 @@ def address_links(addresses,foreground):
html = '' html = ''
for address in addresses: for address in addresses:
token = address['token'].upper() token = address['token'].upper()
html += f'<a href=".well-known/wallets/{token}" target="_blank">{tokenImage(token,foreground)}</a>' html += f'<a style="margin:5px;" href=".well-known/wallets/{token}" target="_blank">{tokenImage(token,foreground)}</a>'
return html return html
def tokenImage(token,foreground): def tokenImage(token,foreground):
@ -91,7 +99,7 @@ def site(data, injectSSL = False):
ssl = '' ssl = ''
if injectSSL: if injectSSL:
ssl = '<script src="https://nathan.woodburn/https.js" async=""></script>' ssl = f'<script src="https://{injectSSL}/https.js" async=""></script>'
page = "page.html" page = "page.html"
if data['image'] == "": if data['image'] == "":
page = "page_no_image.html" page = "page_no_image.html"

View File

@ -3,3 +3,4 @@ python-dotenv
gunicorn gunicorn
requests requests
apscheduler apscheduler
bech32

View File

@ -5,7 +5,15 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>HNS Links</title> <title>HNS Links</title>
<meta name="twitter:title" content="HNS Links">
<meta name="twitter:image" content="https://links.hns.au/assets/img/dashboard.png">
<meta name="twitter:card" content="summary">
<meta property="og:type" content="website">
<meta property="og:title" content="HNS Links">
<meta name="description" content="Create a links page for your domain"> <meta name="description" content="Create a links page for your domain">
<meta property="og:description" content="Create a links page for your domain">
<meta property="og:image" content="https://links.hns.au/assets/img/dashboard.png">
<meta name="twitter: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="icon" type="image/png" sizes="600x627" href="/assets/img/favicon.png">

View File

@ -1 +1 @@
.video-container{position:relative;padding-bottom:56.25%;padding-top:0;height:0;overflow:hidden}.video-container embed,.video-container iframe,.video-container object{position:absolute;top:0;left:0;width:100%;height:100%}.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)} .video-container{position:relative;padding-bottom:56.25%;padding-top:0;height:0;overflow:hidden}.video-container embed,.video-container iframe,.video-container object{position:absolute;top:0;left:0;width:100%;height:100%}.nostr{margin-top:1em}.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)}

6
templates/https.js Normal file
View File

@ -0,0 +1,6 @@
if (location.protocol !== 'https:') {
location.replace(`https:${location.href.substring(location.protocol.length)}`);
}
else{
console.log("Already Https");
}

View File

@ -5,7 +5,23 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>HNS Links</title> <title>HNS Links</title>
<meta name="twitter:title" content="HNS Links">
<meta name="twitter:image" content="https://links.hns.au/assets/img/dashboard.png">
<meta name="twitter:card" content="summary">
<meta property="og:type" content="website">
<meta property="og:title" content="HNS Links">
<meta name="description" content="Create a links page for your domain"> <meta name="description" content="Create a links page for your domain">
<meta property="og:description" content="Create a links page for your domain">
<meta property="og:image" content="https://links.hns.au/assets/img/dashboard.png">
<meta name="twitter:description" content="Create a links page for your domain">
<script type="application/ld+json">
{
"@context": "http://schema.org",
"@type": "WebSite",
"name": "HNS Links",
"url": "https://links.hns.au"
}
</script>
<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="icon" type="image/png" sizes="600x627" href="/assets/img/favicon.png">

111
templates/nostr.html Normal file
View File

@ -0,0 +1,111 @@
<!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>Nostr - HNS Links</title>
<meta name="twitter:title" content="HNS Links">
<meta name="twitter:image" content="https://links.hns.au/assets/img/dashboard.png">
<meta name="twitter:card" content="summary">
<meta property="og:type" content="website">
<meta property="og:title" content="HNS Links">
<meta name="description" content="Create a links page for your domain">
<meta property="og:description" content="Create a links page for your domain">
<meta property="og:image" content="https://links.hns.au/assets/img/dashboard.png">
<meta name="twitter: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">
<script async src="https://umami.woodburn.au/script.js" data-website-id="a165bb1c-eef8-4737-8066-fcd8b4a380bd"></script>
</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: 1em;padding-bottom: 25px;max-width: 800px;margin: auto;">
<form method="post" enctype="multipart/form-data">
<h1>Nostr</h1>
<div style="margin-bottom: 25px;">
<h4>Nostr Links</h4>{% for link in nostr %}
<div class='nostr'>
<input class="form-control" type="text" style="display: inline-block;width: auto;" value="{{link.name}}" readonly/>
<input class="form-control" type="text" style="display: inline-block;width: auto;" value="{{link.pub}}" readonly/>
<a class="btn btn-primary" role="button" href="/nostr/delete/{{link.id}}"><svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 24 24" width="1em" fill="currentColor">
<path d="M0 0h24v24H0z" fill="none"></path>
<path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"></path>
</svg></a>
</div>
{% endfor %}
<div class='nostr'>
<span>Link a new nostr profile</span><br>
<input class="form-control" type="text" style="display: inline-block;width: auto;" name="new-name" placeholder="User Name" />
<input class="form-control" type="text" style="display: inline-block;width: auto;" name="new-pub" placeholder="npub..." />
<br><br>
<input class="btn btn-primary" type="submit" value="Link" style="margin-right: 20px;" />
</div>
</div>
</form>
</div>
</section>
<section></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>

View File

@ -5,6 +5,10 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>{{title}}</title> <title>{{title}}</title>
<meta name="twitter:image" content="https://links.hns.au/assets/img/dashboard.png">
<meta name="twitter:card" content="summary">
<meta property="og:type" content="website">
<meta property="og:image" content="https://links.hns.au/assets/img/dashboard.png">
<meta name="twitter:title" content="{{title}}"> <meta name="twitter:title" content="{{title}}">
<meta property="og:title" content="{{title}}"> <meta property="og:title" content="{{title}}">
<meta name="description" content="{{title}}"> <meta name="description" content="{{title}}">

View File

@ -5,6 +5,10 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>{{title}}</title> <title>{{title}}</title>
<meta name="twitter:image" content="https://links.hns.au/assets/img/dashboard.png">
<meta name="twitter:card" content="summary">
<meta property="og:type" content="website">
<meta property="og:image" content="https://links.hns.au/assets/img/dashboard.png">
<meta name="twitter:title" content="{{title}}"> <meta name="twitter:title" content="{{title}}">
<meta property="og:title" content="{{title}}"> <meta property="og:title" content="{{title}}">
<meta name="description" content="{{title}}"> <meta name="description" content="{{title}}">

View File

@ -5,7 +5,15 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>HNS Links</title> <title>HNS Links</title>
<meta name="twitter:title" content="HNS Links">
<meta name="twitter:image" content="https://links.hns.au/assets/img/dashboard.png">
<meta name="twitter:card" content="summary">
<meta property="og:type" content="website">
<meta property="og:title" content="HNS Links">
<meta name="description" content="Create a links page for your domain"> <meta name="description" content="Create a links page for your domain">
<meta property="og:description" content="Create a links page for your domain">
<meta property="og:image" content="https://links.hns.au/assets/img/dashboard.png">
<meta name="twitter: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="icon" type="image/png" sizes="600x627" href="/assets/img/favicon.png">

View File

@ -5,7 +5,15 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>Home - HNS Links</title> <title>Home - HNS Links</title>
<meta name="twitter:title" content="HNS Links">
<meta name="twitter:image" content="https://links.hns.au/assets/img/dashboard.png">
<meta name="twitter:card" content="summary">
<meta property="og:type" content="website">
<meta property="og:title" content="HNS Links">
<meta name="description" content="Create a links page for your domain"> <meta name="description" content="Create a links page for your domain">
<meta property="og:description" content="Create a links page for your domain">
<meta property="og:image" content="https://links.hns.au/assets/img/dashboard.png">
<meta name="twitter: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="icon" type="image/png" sizes="600x627" href="/assets/img/favicon.png">
@ -88,7 +96,7 @@
</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> </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> </form>
</div> </div>
<div class="col-md-6" style="text-align: center;">{{preview|safe}}</div> <div class="col-md-6" style="text-align: center;">{{preview|safe}}<a class="btn btn-primary" role="button" href="/nostr" style="margin-top: 1em;">Link Nostr</a></div>
</div> </div>
</div> </div>
</div> </div>