feat: Add initial domain management
All checks were successful
Build Docker / Build Docker (push) Successful in 25s
All checks were successful
Build Docker / Build Docker (push) Successful in 25s
This commit is contained in:
parent
8a9f35e044
commit
64547ef6d4
34
account.py
34
account.py
@ -196,6 +196,40 @@ def updateNotifications(token, notifications):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def updateDomainNotifications(token, domain, notifications):
|
||||||
|
# Get userID from userToken
|
||||||
|
if (token.count('$') != 1):
|
||||||
|
return False
|
||||||
|
token = token.split('$')
|
||||||
|
userID = token[0]
|
||||||
|
token = token[1]
|
||||||
|
|
||||||
|
conn = mysql.connector.connect(**db_config)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("SELECT domains FROM users WHERE id = %s", (userID,))
|
||||||
|
user = cursor.fetchall()
|
||||||
|
cursor.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
print(domain)
|
||||||
|
print(notifications)
|
||||||
|
|
||||||
|
# Read json token from first user
|
||||||
|
user = user[0][0]
|
||||||
|
user = json.loads(user)
|
||||||
|
for userDomain in user:
|
||||||
|
if (userDomain['name'] == domain):
|
||||||
|
userDomain['notifications'] = notifications
|
||||||
|
|
||||||
|
conn = mysql.connector.connect(**db_config)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("UPDATE users SET domains = %s WHERE id = %s", (json.dumps(user), userID,))
|
||||||
|
conn.commit()
|
||||||
|
cursor.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def updateNotificationProvider(token, provider,account):
|
def updateNotificationProvider(token, provider,account):
|
||||||
# Get userID from userToken
|
# Get userID from userToken
|
||||||
if (token.count('$') != 1):
|
if (token.count('$') != 1):
|
||||||
|
253
domains.py
Normal file
253
domains.py
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
import mysql.connector
|
||||||
|
from mysql.connector import Error
|
||||||
|
import dotenv
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
import smtplib
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
from email.mime.multipart import MIMEMultipart
|
||||||
|
import requests
|
||||||
|
import re
|
||||||
|
|
||||||
|
dotenv.load_dotenv()
|
||||||
|
|
||||||
|
db_config = {
|
||||||
|
'host': os.getenv('DB_HOST'),
|
||||||
|
'database': os.getenv('DB_NAME'),
|
||||||
|
'user': os.getenv('DB_USER'),
|
||||||
|
'password': os.getenv('DB_PASSWORD')
|
||||||
|
}
|
||||||
|
|
||||||
|
def addDomain(userID:int, domain:str):
|
||||||
|
# Get user domains
|
||||||
|
conn = mysql.connector.connect(**db_config)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("SELECT domains FROM users WHERE id = %s", (userID,))
|
||||||
|
user = cursor.fetchall()
|
||||||
|
cursor.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
# Read json domains from first user
|
||||||
|
user = user[0][0]
|
||||||
|
userDomains = json.loads(user)
|
||||||
|
|
||||||
|
# Check if domain is already in use
|
||||||
|
for userDomain in userDomains:
|
||||||
|
if (userDomain['name'] == domain):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Add domain to user
|
||||||
|
userDomains.append({
|
||||||
|
"name": domain,
|
||||||
|
"status": "pending"
|
||||||
|
})
|
||||||
|
|
||||||
|
# Update user domains
|
||||||
|
conn = mysql.connector.connect(**db_config)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("UPDATE users SET domains = %s WHERE id = %s", (json.dumps(userDomains), userID))
|
||||||
|
conn.commit()
|
||||||
|
cursor.close()
|
||||||
|
conn.close()
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def verifyDomain(domain:str):
|
||||||
|
# Remove any trailing slashes
|
||||||
|
domain = domain.rstrip('/')
|
||||||
|
# Remove any protocol
|
||||||
|
domain = domain.replace('https://', '')
|
||||||
|
domain = domain.replace('http://', '')
|
||||||
|
domain = domain.lower()
|
||||||
|
# Verify domain contains only valid characters
|
||||||
|
if (re.match("^[a-z0-9.-]*$", domain) == None):
|
||||||
|
return False
|
||||||
|
return domain
|
||||||
|
|
||||||
|
def deleteDomain(userID:int, domain:str):
|
||||||
|
# Get user domains
|
||||||
|
conn = mysql.connector.connect(**db_config)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("SELECT domains FROM users WHERE id = %s", (userID,))
|
||||||
|
user = cursor.fetchall()
|
||||||
|
cursor.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
# Read json domains from first user
|
||||||
|
user = user[0][0]
|
||||||
|
userDomains = json.loads(user)
|
||||||
|
|
||||||
|
# Check if domain is already in use
|
||||||
|
for userDomain in userDomains:
|
||||||
|
if (userDomain['name'] == domain):
|
||||||
|
userDomains.remove(userDomain)
|
||||||
|
break
|
||||||
|
|
||||||
|
# Update user domains
|
||||||
|
conn = mysql.connector.connect(**db_config)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("UPDATE users SET domains = %s WHERE id = %s", (json.dumps(userDomains), userID))
|
||||||
|
conn.commit()
|
||||||
|
cursor.close()
|
||||||
|
conn.close()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def syncDomains():
|
||||||
|
# Verify connection to HSD node
|
||||||
|
try:
|
||||||
|
r = requests.get('http://x:' + os.getenv('HSD_API_KEY') + '@' + os.getenv('HSD_IP')
|
||||||
|
+ ':' + os.getenv('HSD_PORT'))
|
||||||
|
if (r.status_code != 200):
|
||||||
|
return "HSD node is not responding"
|
||||||
|
# Check to make sure the node is synced
|
||||||
|
data = r.json()
|
||||||
|
if (data['chain']['progress'] < 0.999):
|
||||||
|
return "HSD node is not synced"
|
||||||
|
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Get all users
|
||||||
|
conn = mysql.connector.connect(**db_config)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("SELECT id, domains FROM users")
|
||||||
|
users = cursor.fetchall()
|
||||||
|
cursor.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
# Loop through users
|
||||||
|
for user in users:
|
||||||
|
userID = user[0]
|
||||||
|
userDomains = json.loads(user[1])
|
||||||
|
# Loop through user domains
|
||||||
|
for userDomain in userDomains:
|
||||||
|
# Check if domain is pending
|
||||||
|
if (userDomain['status'] == 'pending'):
|
||||||
|
try:
|
||||||
|
print('Importing domain: ' + userDomain['name'])
|
||||||
|
r = requests.post('http://x:' + os.getenv('HSD_API_KEY') + '@' + os.getenv('HSD_IP')
|
||||||
|
+ ':' + os.getenv('HSD_WALLET_PORT'), json={'method': 'importname', 'params': [userDomain['name']]})
|
||||||
|
|
||||||
|
if (r.status_code == 200):
|
||||||
|
# Update domain status
|
||||||
|
userDomain['status'] = 'added'
|
||||||
|
|
||||||
|
# Set some defaults
|
||||||
|
userDomain['transfering'] = 0
|
||||||
|
userDomain['next'] = 'none'
|
||||||
|
userDomain['when'] = 0
|
||||||
|
|
||||||
|
# Update user domains
|
||||||
|
conn = mysql.connector.connect(**db_config)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("UPDATE users SET domains = %s WHERE id = %s", (json.dumps(userDomains), userID))
|
||||||
|
conn.commit()
|
||||||
|
cursor.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Get domain info
|
||||||
|
r = requests.post('http://x:' + os.getenv('HSD_API_KEY') + '@' + os.getenv('HSD_IP')
|
||||||
|
+ ':' + os.getenv('HSD_PORT'), json={'method': 'getnameinfo', 'params': [userDomain['name']]})
|
||||||
|
print(json.dumps(r.json(), indent=4))
|
||||||
|
if (r.status_code == 200):
|
||||||
|
data = r.json()
|
||||||
|
# Check if domain is registered
|
||||||
|
info = data['result']['info']
|
||||||
|
if (userDomain['transfering'] != info['transfer']):
|
||||||
|
# Update domain status
|
||||||
|
alert('transfer', userDomain['name'], userID)
|
||||||
|
userDomain['transfering'] = info['transfer']
|
||||||
|
|
||||||
|
if 'stats' in info:
|
||||||
|
if 'blocksUntilExpire' in info['stats']:
|
||||||
|
# Update domain status
|
||||||
|
userDomain['next'] = 'expires'
|
||||||
|
|
||||||
|
previous = userDomain['when']
|
||||||
|
userDomain['when'] = info['stats']['blocksUntilExpire']
|
||||||
|
|
||||||
|
if (crossTimeAlert(userDomain['next'], previous, userDomain['when'])):
|
||||||
|
alert('expire', userDomain['name'], userID)
|
||||||
|
|
||||||
|
elif 'blocksUntilBidding' in info['stats']:
|
||||||
|
# Update domain status
|
||||||
|
userDomain['next'] = 'opens for bidding'
|
||||||
|
userDomain['when'] = info['stats']['blocksUntilBidding']
|
||||||
|
|
||||||
|
# Update user domains
|
||||||
|
conn = mysql.connector.connect(**db_config)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("UPDATE users SET domains = %s WHERE id = %s", (json.dumps(userDomains), userID))
|
||||||
|
conn.commit()
|
||||||
|
cursor.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
else:
|
||||||
|
return "Failed to get info about domain: " + userDomain['name'] + "<br>" + str(r.text)
|
||||||
|
return "Finished syncing domains"
|
||||||
|
|
||||||
|
def alert(event,domain,user):
|
||||||
|
# TODO this later
|
||||||
|
pass
|
||||||
|
|
||||||
|
def crossTimeAlert(event,was, now):
|
||||||
|
# Check for each of these times to see if we should alert
|
||||||
|
month = 4320
|
||||||
|
week = 1008
|
||||||
|
|
||||||
|
# If the time crossed the month mark
|
||||||
|
if (was > month and now <= month):
|
||||||
|
return True
|
||||||
|
# If the time crossed the week mark
|
||||||
|
if (was > week and now <= week):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def getCachedDomainInfo(domain):
|
||||||
|
# Get domain info from user domains
|
||||||
|
conn = mysql.connector.connect(**db_config)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("SELECT domains FROM users")
|
||||||
|
users = cursor.fetchall()
|
||||||
|
cursor.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
# Loop through users
|
||||||
|
for user in users:
|
||||||
|
userDomains = json.loads(user[0])
|
||||||
|
# Loop through user domains
|
||||||
|
for userDomain in userDomains:
|
||||||
|
if (userDomain['name'] == domain):
|
||||||
|
return userDomain
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def blocksToTime(blocks):
|
||||||
|
# Get minutes
|
||||||
|
minutes = blocks * 10
|
||||||
|
years, minutes = divmod(minutes, 525600)
|
||||||
|
days, minutes = divmod(minutes, 1440)
|
||||||
|
hours, minutes = divmod(minutes, 60)
|
||||||
|
|
||||||
|
# Build the string
|
||||||
|
time_string = ""
|
||||||
|
if years:
|
||||||
|
time_string += f"{years} {'year' if years == 1 else 'years'}, "
|
||||||
|
if days:
|
||||||
|
time_string += f"{days} {'day' if days == 1 else 'days'}, "
|
||||||
|
if hours and not years:
|
||||||
|
time_string += f"{hours} {'hour' if hours == 1 else 'hours'}, "
|
||||||
|
if minutes and not years and not days:
|
||||||
|
time_string += f"{minutes} {'min' if minutes == 1 else 'mins'}"
|
||||||
|
|
||||||
|
return time_string.rstrip(', ')
|
180
main.py
180
main.py
@ -9,6 +9,7 @@ import time
|
|||||||
import json
|
import json
|
||||||
import account
|
import account
|
||||||
import threading
|
import threading
|
||||||
|
import domains
|
||||||
|
|
||||||
|
|
||||||
dotenv.load_dotenv()
|
dotenv.load_dotenv()
|
||||||
@ -107,7 +108,20 @@ def dashboard():
|
|||||||
if (account.verifyUser(request.cookies.get('user_token'))):
|
if (account.verifyUser(request.cookies.get('user_token'))):
|
||||||
# Get user data
|
# Get user data
|
||||||
user = account.getUser(request.cookies.get('user_token'))
|
user = account.getUser(request.cookies.get('user_token'))
|
||||||
domains = user['domains']
|
error = ""
|
||||||
|
if (request.args.get('error')):
|
||||||
|
error = request.args.get('error')
|
||||||
|
success = ""
|
||||||
|
if (request.args.get('success')):
|
||||||
|
success = request.args.get('success')
|
||||||
|
|
||||||
|
|
||||||
|
# For each domain in domains get only the name
|
||||||
|
domains = []
|
||||||
|
for domain in user['domains']:
|
||||||
|
domains.append(domain['name'])
|
||||||
|
|
||||||
|
|
||||||
notifications = user['notifications']
|
notifications = user['notifications']
|
||||||
email = user['email'] + ' <a href="/test?service=email">Test</a>'
|
email = user['email'] + ' <a href="/test?service=email">Test</a>'
|
||||||
|
|
||||||
@ -140,7 +154,8 @@ def dashboard():
|
|||||||
|
|
||||||
return render_template('dashboard.html', domains=domains, notifications=notifications,
|
return render_template('dashboard.html', domains=domains, notifications=notifications,
|
||||||
email=email, discord=discord, telegram=telegram,
|
email=email, discord=discord, telegram=telegram,
|
||||||
expiry_week=expiry_week,expiry_month=expiry_month)
|
expiry_week=expiry_week,expiry_month=expiry_month,
|
||||||
|
error=error,success=success,admin=user['admin'])
|
||||||
|
|
||||||
return redirect('/login')
|
return redirect('/login')
|
||||||
|
|
||||||
@ -200,30 +215,161 @@ def notification_options():
|
|||||||
if (account.verifyUser(request.cookies.get('user_token'))):
|
if (account.verifyUser(request.cookies.get('user_token'))):
|
||||||
# Get user data
|
# Get user data
|
||||||
user = account.getUser(request.cookies.get('user_token'))
|
user = account.getUser(request.cookies.get('user_token'))
|
||||||
notifications = user['notifications']
|
|
||||||
expiry_week = {
|
expiry_week = {
|
||||||
"email": False,
|
"email": False,
|
||||||
"discord": False,
|
"discord": False,
|
||||||
"telegram": False
|
"telegram": False
|
||||||
}
|
}
|
||||||
expiry_month = {
|
expiry_month = {
|
||||||
"email": False,
|
"email": False,
|
||||||
"discord": False,
|
"discord": False,
|
||||||
"telegram": False
|
"telegram": False
|
||||||
}
|
}
|
||||||
for key in request.form:
|
|
||||||
if (key.endswith('_week')):
|
|
||||||
key = key[:-5]
|
|
||||||
expiry_week[key] = True
|
|
||||||
elif (key.endswith('_month')):
|
|
||||||
key = key[:-6]
|
|
||||||
expiry_month[key] = True
|
|
||||||
|
|
||||||
notifications['expiry_week'] = expiry_week
|
if not request.form.get('domain'):
|
||||||
notifications['expiry_month'] = expiry_month
|
notifications = user['notifications']
|
||||||
account.updateNotifications(request.cookies.get('user_token'), notifications)
|
for key in request.form:
|
||||||
|
if (key.endswith('_week')):
|
||||||
|
key = key[:-5]
|
||||||
|
expiry_week[key] = True
|
||||||
|
elif (key.endswith('_month')):
|
||||||
|
key = key[:-6]
|
||||||
|
expiry_month[key] = True
|
||||||
|
|
||||||
return redirect('/dashboard')
|
notifications['expiry_week'] = expiry_week
|
||||||
|
notifications['expiry_month'] = expiry_month
|
||||||
|
account.updateNotifications(request.cookies.get('user_token'), notifications)
|
||||||
|
return redirect('/dashboard')
|
||||||
|
else:
|
||||||
|
notifications = {
|
||||||
|
"expiry_week": {
|
||||||
|
"email": False,
|
||||||
|
"discord": False,
|
||||||
|
"telegram": False
|
||||||
|
},
|
||||||
|
"expiry_month": {
|
||||||
|
"email": False,
|
||||||
|
"discord": False,
|
||||||
|
"telegram": False
|
||||||
|
}
|
||||||
|
}
|
||||||
|
domainList = user['domains']
|
||||||
|
domain = request.form.get('domain').lower()
|
||||||
|
|
||||||
|
|
||||||
|
for key in request.form:
|
||||||
|
if (key.endswith('_week')):
|
||||||
|
key = key[:-5]
|
||||||
|
notifications['expiry_week'][key] = True
|
||||||
|
elif (key.endswith('_month')):
|
||||||
|
key = key[:-6]
|
||||||
|
notifications['expiry_month'][key] = True
|
||||||
|
|
||||||
|
account.updateDomainNotifications(request.cookies.get('user_token'),
|
||||||
|
domain,notifications)
|
||||||
|
return redirect('/' + domain + '/info')
|
||||||
|
|
||||||
|
|
||||||
|
#region domains
|
||||||
|
@app.route('/new-domain', methods=['POST'])
|
||||||
|
def new_domain():
|
||||||
|
# Check if user is logged in
|
||||||
|
if 'user_token' in request.cookies:
|
||||||
|
if (account.verifyUser(request.cookies.get('user_token'))):
|
||||||
|
# Get user data
|
||||||
|
user = account.getUser(request.cookies.get('user_token'))
|
||||||
|
domain = request.form.get('domain')
|
||||||
|
# Verify domain
|
||||||
|
domain = domains.verifyDomain(domain)
|
||||||
|
if (domain):
|
||||||
|
# Add domain to user
|
||||||
|
if (domains.addDomain(user['id'], domain)):
|
||||||
|
return redirect('/dashboard?success=Domain added')
|
||||||
|
else:
|
||||||
|
return redirect('/dashboard?error=Unable to add domain')
|
||||||
|
else:
|
||||||
|
return redirect('/dashboard?error=Invalid domain')
|
||||||
|
|
||||||
|
return redirect('/login')
|
||||||
|
|
||||||
|
@app.route('/<domain>/delete')
|
||||||
|
def delete_domain(domain):
|
||||||
|
# Check if user is logged in
|
||||||
|
if 'user_token' in request.cookies:
|
||||||
|
if (account.verifyUser(request.cookies.get('user_token'))):
|
||||||
|
# Get user data
|
||||||
|
user = account.getUser(request.cookies.get('user_token'))
|
||||||
|
# Delete domain from user
|
||||||
|
if (domains.deleteDomain(user['id'], domain)):
|
||||||
|
return redirect('/dashboard?success=Domain deleted')
|
||||||
|
else:
|
||||||
|
return redirect('/dashboard?error=Unable to delete domain')
|
||||||
|
|
||||||
|
return redirect('/login')
|
||||||
|
|
||||||
|
@app.route('/sync')
|
||||||
|
def sync_domains():
|
||||||
|
# Check if user is logged in
|
||||||
|
if 'user_token' in request.cookies:
|
||||||
|
if (account.verifyUser(request.cookies.get('user_token'))):
|
||||||
|
# Get user data
|
||||||
|
user = account.getUser(request.cookies.get('user_token'))
|
||||||
|
if (user['admin'] == False):
|
||||||
|
return redirect('/dashboard?error=You are not an admin')
|
||||||
|
|
||||||
|
# Sync domains
|
||||||
|
result =domains.syncDomains()
|
||||||
|
return redirect('/dashboard?error=' + result)
|
||||||
|
|
||||||
|
return redirect('/login')
|
||||||
|
|
||||||
|
@app.route('/<domain>/info')
|
||||||
|
def domain(domain):
|
||||||
|
# Check if user is logged in
|
||||||
|
if 'user_token' in request.cookies:
|
||||||
|
if (account.verifyUser(request.cookies.get('user_token'))):
|
||||||
|
# Get domain info
|
||||||
|
domainInfo = domains.getCachedDomainInfo(domain)
|
||||||
|
print(domainInfo)
|
||||||
|
if (domainInfo):
|
||||||
|
if (domainInfo['status'] == 'pending'):
|
||||||
|
return redirect('/dashboard?error=Domain is pending<br>Please wait a few minutes')
|
||||||
|
|
||||||
|
|
||||||
|
next = domainInfo['next']
|
||||||
|
when_blocks = domainInfo['when']
|
||||||
|
when_time = domains.blocksToTime(when_blocks)
|
||||||
|
transfering = domainInfo['transfering'] == 1
|
||||||
|
|
||||||
|
expiry_week = {
|
||||||
|
"email": False,
|
||||||
|
"discord": False,
|
||||||
|
"telegram": False
|
||||||
|
}
|
||||||
|
expiry_month = {
|
||||||
|
"email": False,
|
||||||
|
"discord": False,
|
||||||
|
"telegram": False
|
||||||
|
}
|
||||||
|
if ('notifications' in domainInfo):
|
||||||
|
if ('expiry_week' in domainInfo['notifications']):
|
||||||
|
expiry_week = domainInfo['notifications']['expiry_week']
|
||||||
|
|
||||||
|
if ('expiry_month' in domainInfo['notifications']):
|
||||||
|
expiry_month = domainInfo['notifications']['expiry_month']
|
||||||
|
|
||||||
|
return render_template('info.html', domain=str(domain).capitalize(),
|
||||||
|
next=next,when_blocks=when_blocks,when_time=when_time,
|
||||||
|
transfering=transfering,expiry_week=expiry_week,
|
||||||
|
expiry_month=expiry_month)
|
||||||
|
else:
|
||||||
|
return render_template('info.html', domain=str(domain).capitalize())
|
||||||
|
|
||||||
|
return redirect('/login')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
@app.route('/assets/<path:path>')
|
@app.route('/assets/<path:path>')
|
||||||
def send_assets(path):
|
def send_assets(path):
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<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>Products - HNS Alert</title>
|
<title>Dashboard - HNS Alert</title>
|
||||||
<meta name="twitter:image" content="https://alert.woodburn.au/assets/img/favicon.png">
|
<meta name="twitter:image" content="https://alert.woodburn.au/assets/img/favicon.png">
|
||||||
<meta property="og:description" content="Alert system for Handshake domains">
|
<meta property="og:description" content="Alert system for Handshake domains">
|
||||||
<meta property="og:type" content="website">
|
<meta property="og:type" content="website">
|
||||||
@ -12,25 +12,30 @@
|
|||||||
<meta name="description" content="Alert system for domains on the Handshake chain">
|
<meta name="description" content="Alert system for domains on the Handshake chain">
|
||||||
<meta property="og:title" content="HNSAlert">
|
<meta property="og:title" content="HNSAlert">
|
||||||
<meta name="twitter:card" content="summary">
|
<meta name="twitter:card" content="summary">
|
||||||
<link rel="apple-touch-icon" type="image/png" sizes="180x180" href="assets/img/apple-touch-icon.png">
|
<link rel="apple-touch-icon" type="image/png" sizes="180x180" href="/assets/img/apple-touch-icon.png">
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="assets/img/favicon-16x16.png">
|
<link rel="icon" type="image/png" sizes="16x16" href="/assets/img/favicon-16x16.png">
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="assets/img/favicon-16x16.png" media="(prefers-color-scheme: dark)">
|
<link rel="icon" type="image/png" sizes="16x16" href="/assets/img/favicon-16x16.png" media="(prefers-color-scheme: dark)">
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="assets/img/favicon-32x32.png">
|
<link rel="icon" type="image/png" sizes="32x32" href="/assets/img/favicon-32x32.png">
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="assets/img/favicon-32x32.png" media="(prefers-color-scheme: dark)">
|
<link rel="icon" type="image/png" sizes="32x32" href="/assets/img/favicon-32x32.png" media="(prefers-color-scheme: dark)">
|
||||||
<link rel="icon" type="image/png" sizes="180x180" href="assets/img/apple-touch-icon.png">
|
<link rel="icon" type="image/png" sizes="180x180" href="/assets/img/apple-touch-icon.png">
|
||||||
<link rel="icon" type="image/png" sizes="192x192" href="assets/img/android-chrome-192x192.png">
|
<link rel="icon" type="image/png" sizes="192x192" href="/assets/img/android-chrome-192x192.png">
|
||||||
<link rel="icon" type="image/png" sizes="512x512" href="assets/img/android-chrome-512x512.png">
|
<link rel="icon" type="image/png" sizes="512x512" href="/assets/img/android-chrome-512x512.png">
|
||||||
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
<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&display=swap">
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800&display=swap">
|
||||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
<link rel="stylesheet" href="/assets/css/Login-Form-Basic-icons.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<nav class="navbar navbar-expand-md sticky-top py-3 navbar-dark" id="mainNav">
|
<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/server-icon.png" style="margin-right: 20px;"><span>HNS Alert</span></a><a class="btn btn-primary shadow" role="button" href="logout">Logout</a></div>
|
<div class="container"><a class="navbar-brand d-flex align-items-center" href="/"><img src="/assets/img/server-icon.png" style="margin-right: 20px;"><span>HNS Alert</span></a>{%if admin%}
|
||||||
|
<a href="/sync">Sync</a>
|
||||||
|
|
||||||
|
{%endif%}<a class="btn btn-primary shadow" role="button" href="logout">Logout</a></div>
|
||||||
</nav>
|
</nav>
|
||||||
<section class="py-5">
|
<section class="py-5">
|
||||||
<div class="container py-5">
|
<div class="container py-5">
|
||||||
|
<h1 class="text-center" style="color: rgb(255,0,0);">{{error | safe}}</h1>
|
||||||
|
<h1 class="text-center" style="color: rgb(0,255,0);">{{success | safe}}</h1>
|
||||||
<div class="row mb-4 mb-lg-5">
|
<div class="row mb-4 mb-lg-5">
|
||||||
<div class="col-md-8 col-xl-6 text-center mx-auto">
|
<div class="col-md-8 col-xl-6 text-center mx-auto">
|
||||||
<h2 class="fw-bold">Dashboard</h2>
|
<h2 class="fw-bold">Dashboard</h2>
|
||||||
@ -77,6 +82,29 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mx-auto">
|
||||||
|
<div class="col">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h4 class="card-title">Domains</h4><ul class="list-group">
|
||||||
|
<li class="list-group-item">
|
||||||
|
<form action="/new-domain" method="post"><input class="form-control" type="text" name="domain" placeholder="newdomain/" />
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
{% for domain in domains %}
|
||||||
|
<li class="list-group-item">
|
||||||
|
<span style="margin-right: 20px;">{{domain}}/</span> <div class="btn-group" role="group" style="position: absolute;height: 75%;right: 10px;top: 0.3em;">
|
||||||
|
<a class="btn btn-primary" role="button" style="padding: 0px;padding-left: 36px;padding-right: 36px;" href="/{{domain}}/info">Info</a>
|
||||||
|
<a class="btn btn-primary" role="button" style="padding: 0px;padding-right: 32px;padding-left: 32px;" href="/{{domain}}/delete">Delete</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<footer class="bg-dark">
|
<footer class="bg-dark">
|
||||||
@ -99,7 +127,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</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="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"><img src="assets/img/favicon-32x32.png" style="margin-right: 10px;"><span>HNS Alert</span></div>
|
<div class="fw-bold d-flex align-items-center mb-2"><img src="/assets/img/favicon-32x32.png" style="margin-right: 10px;"><span>HNS Alert</span></div>
|
||||||
<p class="text-muted">A Woodburn/ project</p>
|
<p class="text-muted">A Woodburn/ project</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -109,8 +137,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
<script src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||||
<script src="assets/js/bold-and-dark.js"></script>
|
<script src="/assets/js/bold-and-dark.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
@ -20,22 +20,22 @@
|
|||||||
"url": "https://alert.woodburn.au"
|
"url": "https://alert.woodburn.au"
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<link rel="apple-touch-icon" type="image/png" sizes="180x180" href="assets/img/apple-touch-icon.png">
|
<link rel="apple-touch-icon" type="image/png" sizes="180x180" href="/assets/img/apple-touch-icon.png">
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="assets/img/favicon-16x16.png">
|
<link rel="icon" type="image/png" sizes="16x16" href="/assets/img/favicon-16x16.png">
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="assets/img/favicon-16x16.png" media="(prefers-color-scheme: dark)">
|
<link rel="icon" type="image/png" sizes="16x16" href="/assets/img/favicon-16x16.png" media="(prefers-color-scheme: dark)">
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="assets/img/favicon-32x32.png">
|
<link rel="icon" type="image/png" sizes="32x32" href="/assets/img/favicon-32x32.png">
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="assets/img/favicon-32x32.png" media="(prefers-color-scheme: dark)">
|
<link rel="icon" type="image/png" sizes="32x32" href="/assets/img/favicon-32x32.png" media="(prefers-color-scheme: dark)">
|
||||||
<link rel="icon" type="image/png" sizes="180x180" href="assets/img/apple-touch-icon.png">
|
<link rel="icon" type="image/png" sizes="180x180" href="/assets/img/apple-touch-icon.png">
|
||||||
<link rel="icon" type="image/png" sizes="192x192" href="assets/img/android-chrome-192x192.png">
|
<link rel="icon" type="image/png" sizes="192x192" href="/assets/img/android-chrome-192x192.png">
|
||||||
<link rel="icon" type="image/png" sizes="512x512" href="assets/img/android-chrome-512x512.png">
|
<link rel="icon" type="image/png" sizes="512x512" href="/assets/img/android-chrome-512x512.png">
|
||||||
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
<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&display=swap">
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800&display=swap">
|
||||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
<link rel="stylesheet" href="/assets/css/Login-Form-Basic-icons.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<nav class="navbar navbar-expand-md sticky-top py-3 navbar-dark" id="mainNav">
|
<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/server-icon.png" style="margin-right: 20px;"><span>HNS Alert</span></a><a class="btn btn-primary shadow" role="button" href="login">Account</a></div>
|
<div class="container"><a class="navbar-brand d-flex align-items-center" href="/"><img src="/assets/img/server-icon.png" style="margin-right: 20px;"><span>HNS Alert</span></a><a class="btn btn-primary shadow" role="button" href="login">Account</a></div>
|
||||||
</nav>
|
</nav>
|
||||||
<header class="bg-dark">
|
<header class="bg-dark">
|
||||||
<div class="container pt-4 pt-xl-5">
|
<div class="container pt-4 pt-xl-5">
|
||||||
@ -48,9 +48,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-lg-10 mx-auto">
|
<div class="col-12 col-lg-10 mx-auto">
|
||||||
<div class="position-relative" style="display: flex;flex-wrap: wrap;justify-content: flex-end;">
|
<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(-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 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 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>
|
||||||
</div>
|
</div>
|
||||||
@ -164,7 +164,7 @@
|
|||||||
<div class="col mb-4">
|
<div class="col mb-4">
|
||||||
<div class="d-flex flex-column align-items-center align-items-sm-start">
|
<div class="d-flex flex-column align-items-center align-items-sm-start">
|
||||||
<p class="bg-dark border rounded border-dark p-4">Nisi sit justo faucibus nec ornare amet, tortor torquent. Blandit class dapibus, aliquet morbi.</p>
|
<p class="bg-dark border rounded border-dark p-4">Nisi sit justo faucibus nec ornare amet, tortor torquent. Blandit class dapibus, aliquet morbi.</p>
|
||||||
<div class="d-flex"><img class="rounded-circle flex-shrink-0 me-3 fit-cover" width="50" height="50" src="assets/img/team/avatar2.jpg">
|
<div class="d-flex"><img class="rounded-circle flex-shrink-0 me-3 fit-cover" width="50" height="50" src="/assets/img/team/avatar2.jpg">
|
||||||
<div>
|
<div>
|
||||||
<p class="fw-bold text-primary mb-0">John Smith</p>
|
<p class="fw-bold text-primary mb-0">John Smith</p>
|
||||||
<p class="text-muted mb-0">Erat netus</p>
|
<p class="text-muted mb-0">Erat netus</p>
|
||||||
@ -175,7 +175,7 @@
|
|||||||
<div class="col mb-4">
|
<div class="col mb-4">
|
||||||
<div class="d-flex flex-column align-items-center align-items-sm-start">
|
<div class="d-flex flex-column align-items-center align-items-sm-start">
|
||||||
<p class="bg-dark border rounded border-dark p-4">Nisi sit justo faucibus nec ornare amet, tortor torquent. Blandit class dapibus, aliquet morbi.</p>
|
<p class="bg-dark border rounded border-dark p-4">Nisi sit justo faucibus nec ornare amet, tortor torquent. Blandit class dapibus, aliquet morbi.</p>
|
||||||
<div class="d-flex"><img class="rounded-circle flex-shrink-0 me-3 fit-cover" width="50" height="50" src="assets/img/team/avatar4.jpg">
|
<div class="d-flex"><img class="rounded-circle flex-shrink-0 me-3 fit-cover" width="50" height="50" src="/assets/img/team/avatar4.jpg">
|
||||||
<div>
|
<div>
|
||||||
<p class="fw-bold text-primary mb-0">John Smith</p>
|
<p class="fw-bold text-primary mb-0">John Smith</p>
|
||||||
<p class="text-muted mb-0">Erat netus</p>
|
<p class="text-muted mb-0">Erat netus</p>
|
||||||
@ -186,7 +186,7 @@
|
|||||||
<div class="col mb-4">
|
<div class="col mb-4">
|
||||||
<div class="d-flex flex-column align-items-center align-items-sm-start">
|
<div class="d-flex flex-column align-items-center align-items-sm-start">
|
||||||
<p class="bg-dark border rounded border-dark p-4">Nisi sit justo faucibus nec ornare amet, tortor torquent. Blandit class dapibus, aliquet morbi.</p>
|
<p class="bg-dark border rounded border-dark p-4">Nisi sit justo faucibus nec ornare amet, tortor torquent. Blandit class dapibus, aliquet morbi.</p>
|
||||||
<div class="d-flex"><img class="rounded-circle flex-shrink-0 me-3 fit-cover" width="50" height="50" src="assets/img/team/avatar5.jpg">
|
<div class="d-flex"><img class="rounded-circle flex-shrink-0 me-3 fit-cover" width="50" height="50" src="/assets/img/team/avatar5.jpg">
|
||||||
<div>
|
<div>
|
||||||
<p class="fw-bold text-primary mb-0">John Smith</p>
|
<p class="fw-bold text-primary mb-0">John Smith</p>
|
||||||
<p class="text-muted mb-0">Erat netus</p>
|
<p class="text-muted mb-0">Erat netus</p>
|
||||||
@ -251,7 +251,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</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="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"><img src="assets/img/favicon-32x32.png" style="margin-right: 10px;"><span>HNS Alert</span></div>
|
<div class="fw-bold d-flex align-items-center mb-2"><img src="/assets/img/favicon-32x32.png" style="margin-right: 10px;"><span>HNS Alert</span></div>
|
||||||
<p class="text-muted">A Woodburn/ project</p>
|
<p class="text-muted">A Woodburn/ project</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -261,8 +261,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
<script src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||||
<script src="assets/js/bold-and-dark.js"></script>
|
<script src="/assets/js/bold-and-dark.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
112
templates/info.html
Normal file
112
templates/info.html
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
<!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>{{domain}}/ - HNS Alert</title>
|
||||||
|
<meta name="twitter:image" content="https://alert.woodburn.au/assets/img/favicon.png">
|
||||||
|
<meta property="og:description" content="Alert system for Handshake domains">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<meta property="og:image" content="https://alert.woodburn.au/assets/img/favicon.png">
|
||||||
|
<meta name="description" content="Alert system for domains on the Handshake chain">
|
||||||
|
<meta property="og:title" content="HNSAlert">
|
||||||
|
<meta name="twitter:card" content="summary">
|
||||||
|
<link rel="apple-touch-icon" type="image/png" sizes="180x180" href="/assets/img/apple-touch-icon.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="/assets/img/favicon-16x16.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="/assets/img/favicon-16x16.png" media="(prefers-color-scheme: dark)">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="/assets/img/favicon-32x32.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="/assets/img/favicon-32x32.png" media="(prefers-color-scheme: dark)">
|
||||||
|
<link rel="icon" type="image/png" sizes="180x180" href="/assets/img/apple-touch-icon.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="192x192" href="/assets/img/android-chrome-192x192.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="512x512" href="/assets/img/android-chrome-512x512.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&display=swap">
|
||||||
|
<link rel="stylesheet" href="/assets/css/Login-Form-Basic-icons.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/server-icon.png" style="margin-right: 20px;"><span>HNS Alert</span></a><a class="btn btn-primary shadow" role="button" href="/login">Account</a></div>
|
||||||
|
</nav>
|
||||||
|
<section class="py-5">
|
||||||
|
<h1 class="text-center">{{domain}}/ Info</h1>
|
||||||
|
<div class="container py-5">
|
||||||
|
<div class="row mx-auto">
|
||||||
|
<div class="col">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<section class="position-relative py-4 py-xl-5">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row mb-5">
|
||||||
|
<div class="col-md-8 col-xl-6 text-center mx-auto">
|
||||||
|
<h2>{{domain}}/ Status</h2>
|
||||||
|
<p class="w-lg-50">{{domain}} {{next}} in {{when_blocks}} blocks (~{{when_time}})<br>{%if transfering %}<br>{{domain}} is currently transferring<br>{%endif%}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h4 class="card-title">Notification options</h4>
|
||||||
|
<h6 class="text-muted card-subtitle mb-2">These are per domain options. These options will override global settings once you save them</h6>
|
||||||
|
<form method="post" action="/notification-options"><input class="form-control" type="hidden" name="domain" value="{{domain}}">
|
||||||
|
<ul class="list-group">
|
||||||
|
<li class="list-group-item"><span style="display: block;">Domain expiry 1 week notice</span>{% for platform, enabled in expiry_week.items() %}
|
||||||
|
<div class="form-check form-switch form-check-inline">
|
||||||
|
<input id="formCheck-{{ loop.index }}" class="form-check-input" type="checkbox" name="{{ platform }}_week" {% if enabled %}checked{% endif %}/>
|
||||||
|
<label class="form-check-label" for="formCheck-{{ loop.index }}">{{ platform|capitalize }}</label>
|
||||||
|
</div>
|
||||||
|
{% endfor %}</li>
|
||||||
|
<li class="list-group-item"><span style="display: block;">Domain expiry 1 month notice</span>{% for platform, enabled in expiry_month.items() %}
|
||||||
|
<div class="form-check form-switch form-check-inline">
|
||||||
|
<input id="formCheck-{{ loop.index }}" class="form-check-input" type="checkbox" name="{{ platform }}_month" {% if enabled %}checked{% endif %}/>
|
||||||
|
<label class="form-check-label" for="formCheck-{{ loop.index }}">{{ platform|capitalize }}</label>
|
||||||
|
</div>
|
||||||
|
{% endfor %}</li>
|
||||||
|
</ul>
|
||||||
|
<div style="text-align: right;"><input class="btn btn-primary" type="submit" value="Save" style="margin-top: 20px;"></div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</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://faucet.woodburn.au" target="_blank">Domain Faucet</a></li>
|
||||||
|
<li><a href="https://github.com/nathanwoodburn" style="margin-top: 0px;" target="_blank">Other Projects</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://github.com/nathanwoodburn" target="_blank">Github</a></li>
|
||||||
|
<li><a href="https://nathan.woodburn.au" target="_blank">Nathan.Woodburn/</a></li>
|
||||||
|
<li><a href="https://l.woodburn.au/donate" target="_blank">Donate</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"><img src="/assets/img/favicon-32x32.png" style="margin-right: 10px;"><span>HNS Alert</span></div>
|
||||||
|
<p class="text-muted">A Woodburn/ project</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="text-muted d-flex justify-content-between align-items-center pt-3">
|
||||||
|
<p class="mb-0">Copyright © 2023 HNS Alert</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
<script src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||||
|
<script src="/assets/js/bold-and-dark.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -4,7 +4,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<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>Products - HNS Alert</title>
|
<title>Link Service - HNS Alert</title>
|
||||||
<meta name="twitter:image" content="https://alert.woodburn.au/assets/img/favicon.png">
|
<meta name="twitter:image" content="https://alert.woodburn.au/assets/img/favicon.png">
|
||||||
<meta property="og:description" content="Alert system for Handshake domains">
|
<meta property="og:description" content="Alert system for Handshake domains">
|
||||||
<meta property="og:type" content="website">
|
<meta property="og:type" content="website">
|
||||||
@ -12,22 +12,22 @@
|
|||||||
<meta name="description" content="Alert system for domains on the Handshake chain">
|
<meta name="description" content="Alert system for domains on the Handshake chain">
|
||||||
<meta property="og:title" content="HNSAlert">
|
<meta property="og:title" content="HNSAlert">
|
||||||
<meta name="twitter:card" content="summary">
|
<meta name="twitter:card" content="summary">
|
||||||
<link rel="apple-touch-icon" type="image/png" sizes="180x180" href="assets/img/apple-touch-icon.png">
|
<link rel="apple-touch-icon" type="image/png" sizes="180x180" href="/assets/img/apple-touch-icon.png">
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="assets/img/favicon-16x16.png">
|
<link rel="icon" type="image/png" sizes="16x16" href="/assets/img/favicon-16x16.png">
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="assets/img/favicon-16x16.png" media="(prefers-color-scheme: dark)">
|
<link rel="icon" type="image/png" sizes="16x16" href="/assets/img/favicon-16x16.png" media="(prefers-color-scheme: dark)">
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="assets/img/favicon-32x32.png">
|
<link rel="icon" type="image/png" sizes="32x32" href="/assets/img/favicon-32x32.png">
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="assets/img/favicon-32x32.png" media="(prefers-color-scheme: dark)">
|
<link rel="icon" type="image/png" sizes="32x32" href="/assets/img/favicon-32x32.png" media="(prefers-color-scheme: dark)">
|
||||||
<link rel="icon" type="image/png" sizes="180x180" href="assets/img/apple-touch-icon.png">
|
<link rel="icon" type="image/png" sizes="180x180" href="/assets/img/apple-touch-icon.png">
|
||||||
<link rel="icon" type="image/png" sizes="192x192" href="assets/img/android-chrome-192x192.png">
|
<link rel="icon" type="image/png" sizes="192x192" href="/assets/img/android-chrome-192x192.png">
|
||||||
<link rel="icon" type="image/png" sizes="512x512" href="assets/img/android-chrome-512x512.png">
|
<link rel="icon" type="image/png" sizes="512x512" href="/assets/img/android-chrome-512x512.png">
|
||||||
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
<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&display=swap">
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800&display=swap">
|
||||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
<link rel="stylesheet" href="/assets/css/Login-Form-Basic-icons.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<nav class="navbar navbar-expand-md sticky-top py-3 navbar-dark" id="mainNav">
|
<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/server-icon.png" style="margin-right: 20px;"><span>HNS Alert</span></a><a class="btn btn-primary shadow" role="button" href="logout">Logout</a></div>
|
<div class="container"><a class="navbar-brand d-flex align-items-center" href="/"><img src="/assets/img/server-icon.png" style="margin-right: 20px;"><span>HNS Alert</span></a><a class="btn btn-primary shadow" role="button" href="logout">Logout</a></div>
|
||||||
</nav>
|
</nav>
|
||||||
<section class="py-5">
|
<section class="py-5">
|
||||||
<div class="container py-5">
|
<div class="container py-5">
|
||||||
@ -83,7 +83,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</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="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"><img src="assets/img/favicon-32x32.png" style="margin-right: 10px;"><span>HNS Alert</span></div>
|
<div class="fw-bold d-flex align-items-center mb-2"><img src="/assets/img/favicon-32x32.png" style="margin-right: 10px;"><span>HNS Alert</span></div>
|
||||||
<p class="text-muted">A Woodburn/ project</p>
|
<p class="text-muted">A Woodburn/ project</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -93,8 +93,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
<script src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||||
<script src="assets/js/bold-and-dark.js"></script>
|
<script src="/assets/js/bold-and-dark.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
@ -12,22 +12,22 @@
|
|||||||
<meta name="description" content="Alert system for domains on the Handshake chain">
|
<meta name="description" content="Alert system for domains on the Handshake chain">
|
||||||
<meta property="og:title" content="HNSAlert">
|
<meta property="og:title" content="HNSAlert">
|
||||||
<meta name="twitter:card" content="summary">
|
<meta name="twitter:card" content="summary">
|
||||||
<link rel="apple-touch-icon" type="image/png" sizes="180x180" href="assets/img/apple-touch-icon.png">
|
<link rel="apple-touch-icon" type="image/png" sizes="180x180" href="/assets/img/apple-touch-icon.png">
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="assets/img/favicon-16x16.png">
|
<link rel="icon" type="image/png" sizes="16x16" href="/assets/img/favicon-16x16.png">
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="assets/img/favicon-16x16.png" media="(prefers-color-scheme: dark)">
|
<link rel="icon" type="image/png" sizes="16x16" href="/assets/img/favicon-16x16.png" media="(prefers-color-scheme: dark)">
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="assets/img/favicon-32x32.png">
|
<link rel="icon" type="image/png" sizes="32x32" href="/assets/img/favicon-32x32.png">
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="assets/img/favicon-32x32.png" media="(prefers-color-scheme: dark)">
|
<link rel="icon" type="image/png" sizes="32x32" href="/assets/img/favicon-32x32.png" media="(prefers-color-scheme: dark)">
|
||||||
<link rel="icon" type="image/png" sizes="180x180" href="assets/img/apple-touch-icon.png">
|
<link rel="icon" type="image/png" sizes="180x180" href="/assets/img/apple-touch-icon.png">
|
||||||
<link rel="icon" type="image/png" sizes="192x192" href="assets/img/android-chrome-192x192.png">
|
<link rel="icon" type="image/png" sizes="192x192" href="/assets/img/android-chrome-192x192.png">
|
||||||
<link rel="icon" type="image/png" sizes="512x512" href="assets/img/android-chrome-512x512.png">
|
<link rel="icon" type="image/png" sizes="512x512" href="/assets/img/android-chrome-512x512.png">
|
||||||
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
<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&display=swap">
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800&display=swap">
|
||||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
<link rel="stylesheet" href="/assets/css/Login-Form-Basic-icons.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<nav class="navbar navbar-expand-md sticky-top py-3 navbar-dark" id="mainNav">
|
<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/server-icon.png" style="margin-right: 20px;"><span>HNS Alert</span></a><a class="btn btn-primary shadow" role="button" href="login">Account</a></div>
|
<div class="container"><a class="navbar-brand d-flex align-items-center" href="/"><img src="/assets/img/server-icon.png" style="margin-right: 20px;"><span>HNS Alert</span></a><a class="btn btn-primary shadow" role="button" href="login">Account</a></div>
|
||||||
</nav>
|
</nav>
|
||||||
<section class="py-5">
|
<section class="py-5">
|
||||||
<div class="container py-5">
|
<div class="container py-5">
|
||||||
@ -77,7 +77,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</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="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"><img src="assets/img/favicon-32x32.png" style="margin-right: 10px;"><span>HNS Alert</span></div>
|
<div class="fw-bold d-flex align-items-center mb-2"><img src="/assets/img/favicon-32x32.png" style="margin-right: 10px;"><span>HNS Alert</span></div>
|
||||||
<p class="text-muted">A Woodburn/ project</p>
|
<p class="text-muted">A Woodburn/ project</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -87,8 +87,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
<script src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||||
<script src="assets/js/bold-and-dark.js"></script>
|
<script src="/assets/js/bold-and-dark.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
@ -12,22 +12,22 @@
|
|||||||
<meta name="description" content="Alert system for domains on the Handshake chain">
|
<meta name="description" content="Alert system for domains on the Handshake chain">
|
||||||
<meta property="og:title" content="HNSAlert">
|
<meta property="og:title" content="HNSAlert">
|
||||||
<meta name="twitter:card" content="summary">
|
<meta name="twitter:card" content="summary">
|
||||||
<link rel="apple-touch-icon" type="image/png" sizes="180x180" href="assets/img/apple-touch-icon.png">
|
<link rel="apple-touch-icon" type="image/png" sizes="180x180" href="/assets/img/apple-touch-icon.png">
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="assets/img/favicon-16x16.png">
|
<link rel="icon" type="image/png" sizes="16x16" href="/assets/img/favicon-16x16.png">
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="assets/img/favicon-16x16.png" media="(prefers-color-scheme: dark)">
|
<link rel="icon" type="image/png" sizes="16x16" href="/assets/img/favicon-16x16.png" media="(prefers-color-scheme: dark)">
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="assets/img/favicon-32x32.png">
|
<link rel="icon" type="image/png" sizes="32x32" href="/assets/img/favicon-32x32.png">
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="assets/img/favicon-32x32.png" media="(prefers-color-scheme: dark)">
|
<link rel="icon" type="image/png" sizes="32x32" href="/assets/img/favicon-32x32.png" media="(prefers-color-scheme: dark)">
|
||||||
<link rel="icon" type="image/png" sizes="180x180" href="assets/img/apple-touch-icon.png">
|
<link rel="icon" type="image/png" sizes="180x180" href="/assets/img/apple-touch-icon.png">
|
||||||
<link rel="icon" type="image/png" sizes="192x192" href="assets/img/android-chrome-192x192.png">
|
<link rel="icon" type="image/png" sizes="192x192" href="/assets/img/android-chrome-192x192.png">
|
||||||
<link rel="icon" type="image/png" sizes="512x512" href="assets/img/android-chrome-512x512.png">
|
<link rel="icon" type="image/png" sizes="512x512" href="/assets/img/android-chrome-512x512.png">
|
||||||
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
<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&display=swap">
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800&display=swap">
|
||||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
<link rel="stylesheet" href="/assets/css/Login-Form-Basic-icons.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<nav class="navbar navbar-expand-md sticky-top py-3 navbar-dark" id="mainNav">
|
<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/server-icon.png" style="margin-right: 20px;"><span>HNS Alert</span></a><a class="btn btn-primary shadow" role="button" href="login">Account</a></div>
|
<div class="container"><a class="navbar-brand d-flex align-items-center" href="/"><img src="/assets/img/server-icon.png" style="margin-right: 20px;"><span>HNS Alert</span></a><a class="btn btn-primary shadow" role="button" href="login">Account</a></div>
|
||||||
</nav>
|
</nav>
|
||||||
<section class="py-5">
|
<section class="py-5">
|
||||||
<div class="container py-5">
|
<div class="container py-5">
|
||||||
@ -49,7 +49,7 @@
|
|||||||
<div class="mb-3"><input class="form-control" type="email" name="email" placeholder="Email"></div>
|
<div class="mb-3"><input class="form-control" type="email" name="email" placeholder="Email"></div>
|
||||||
<div class="mb-3"><input class="form-control" type="password" name="password" placeholder="Password"></div>
|
<div class="mb-3"><input class="form-control" type="password" name="password" placeholder="Password"></div>
|
||||||
<div class="mb-3"><button class="btn btn-primary shadow d-block w-100" type="submit">Sign up</button></div>
|
<div class="mb-3"><button class="btn btn-primary shadow d-block w-100" type="submit">Sign up</button></div>
|
||||||
<p class="text-muted">Already have an account? <a href="login.html">Log in</a></p>
|
<p class="text-muted">Already have an account? <a href="/login.html">Log in</a></p>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -77,7 +77,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</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="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"><img src="assets/img/favicon-32x32.png" style="margin-right: 10px;"><span>HNS Alert</span></div>
|
<div class="fw-bold d-flex align-items-center mb-2"><img src="/assets/img/favicon-32x32.png" style="margin-right: 10px;"><span>HNS Alert</span></div>
|
||||||
<p class="text-muted">A Woodburn/ project</p>
|
<p class="text-muted">A Woodburn/ project</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -87,8 +87,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
<script src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||||
<script src="assets/js/bold-and-dark.js"></script>
|
<script src="/assets/js/bold-and-dark.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
Loading…
Reference in New Issue
Block a user