Merge branch 'develop' into main
All checks were successful
Build Docker / Build Bot (push) Successful in 25s
Build Docker / Build Master (push) Successful in 26s

This commit is contained in:
Nathan Woodburn 2023-09-18 14:56:15 +10:00
commit 94ebacd84d
Signed by: nathanwoodburn
GPG Key ID: 203B000478AD0EF1
7 changed files with 217 additions and 14 deletions

View File

@ -10,7 +10,7 @@ on:
jobs: jobs:
Build Master: Build Master:
runs-on: ubuntu-latest runs-on: [ubuntu-latest, arm]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
@ -51,7 +51,7 @@ jobs:
Build Bot: Build Bot:
runs-on: ubuntu-latest runs-on: [ubuntu-latest, arm]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2

View File

@ -8,7 +8,7 @@ on:
jobs: jobs:
Build Master: Build Master:
runs-on: ubuntu-latest runs-on: [ubuntu-latest,arm]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
@ -34,7 +34,7 @@ jobs:
docker push git.woodburn.au/nathanwoodburn/hnshosting-master:release docker push git.woodburn.au/nathanwoodburn/hnshosting-master:release
Build Bot: Build Bot:
runs-on: ubuntu-latest runs-on: [ubuntu-latest,arm]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2

View File

@ -51,6 +51,7 @@ async def listworkers(ctx):
await ctx.response.send_message(f"Error listing workers\n" + r.text,ephemeral=True) await ctx.response.send_message(f"Error listing workers\n" + r.text,ephemeral=True)
else: else:
await ctx.response.send_message("You do not have permission to use this command",ephemeral=True) await ctx.response.send_message("You do not have permission to use this command",ephemeral=True)
update_bot_status()
@tree.command(name="licence", description="Gets a licence key") @tree.command(name="licence", description="Gets a licence key")
async def license(ctx): async def license(ctx):
@ -104,6 +105,7 @@ async def createsite(ctx, domain: str, licence: str = None):
await ctx.response.send_message(f"Error creating site\n" + json['error']) await ctx.response.send_message(f"Error creating site\n" + json['error'])
else: else:
await ctx.response.send_message(f"Error creating site\n" + r.text) await ctx.response.send_message(f"Error creating site\n" + r.text)
update_bot_status()
@tree.command(name="siteinfo", description="Get info about a WordPress site") @tree.command(name="siteinfo", description="Get info about a WordPress site")
@ -128,6 +130,17 @@ async def check_site_ready(domain):
return False return False
else: else:
return False return False
def get_site_count():
r = requests.get(f"http://{Master_IP}:{Master_Port}/site-count")
if r.status_code == 200:
return r.text
else:
return "Error getting site count\n" + r.text
def update_bot_status():
site_count = get_site_count()
client.loop.create_task(client.change_presence(activity=discord.Activity(type=discord.ActivityType.watching, name="over " + site_count + " wordpress sites")))
# When the bot is ready # When the bot is ready
@client.event @client.event
@ -135,6 +148,9 @@ async def on_ready():
global ADMINID global ADMINID
ADMINID = client.application.owner.id ADMINID = client.application.owner.id
await tree.sync() await tree.sync()
await client.loop.create_task(client.change_presence(activity=discord.Activity(type=discord.ActivityType.watching, name="over HNSHosting wordpress")))
# Get the number of sites
site_count = get_site_count()
await client.loop.create_task(client.change_presence(activity=discord.Activity(type=discord.ActivityType.watching, name="over " + site_count + " wordpress sites")))
client.run(TOKEN) client.run(TOKEN)

View File

@ -206,6 +206,7 @@ def list_workers():
@app.route('/site-info', methods=['GET']) @app.route('/site-info', methods=['GET'])
def site_status(): def site_status():
domain = request.args.get('domain') domain = request.args.get('domain')
domain = domain.lower()
if domain == None: if domain == None:
return jsonify({'error': 'Invalid domain', 'success': 'false'}) return jsonify({'error': 'Invalid domain', 'success': 'false'})
@ -236,6 +237,7 @@ def site_status():
@app.route('/info') @app.route('/info')
def site_status_human(): def site_status_human():
domain = request.args.get('domain') domain = request.args.get('domain')
domain = domain.lower()
if domain == None: if domain == None:
return "<h1>Invalid domain</h1>" return "<h1>Invalid domain</h1>"
@ -251,7 +253,7 @@ def site_status_human():
# Get worker ip # Get worker ip
ip = workerIP_PRIV(worker) ip = workerIP_PRIV(worker)
# Get TLSA record # Get TLSA record
resp=requests.get("http://"+ip + ":5000/tlsa?domain=" + domain,timeout=2) resp=requests.get("http://"+ip + ":5000/tlsa?domain=" + domain,timeout=2)
json = resp.json() json = resp.json()
publicIP = workerIP(worker) publicIP = workerIP(worker)
@ -265,6 +267,7 @@ def site_status_human():
@app.route('/tlsa', methods=['GET']) @app.route('/tlsa', methods=['GET'])
def tlsa(): def tlsa():
domain = request.args.get('domain') domain = request.args.get('domain')
domain = domain.lower()
if domain == None: if domain == None:
return jsonify({'error': 'Invalid domain', 'success': 'false'}) return jsonify({'error': 'Invalid domain', 'success': 'false'})
@ -401,7 +404,7 @@ def workerIP_PRIV(worker):
ip = None ip = None
for line in workers_file.readlines(): for line in workers_file.readlines():
if worker == line.split(':')[0]: if worker == line.split(':')[0]:
ip = line.split(':')[2].strip('\n') ip = line.split(':')[1].strip('\n')
break break
workers_file.close() workers_file.close()
@ -419,7 +422,7 @@ def workerIP(worker):
ip = None ip = None
for line in workers_file.readlines(): for line in workers_file.readlines():
if worker == line.split(':')[0]: if worker == line.split(':')[0]:
ip = line.split(':')[1].strip('\n') ip = line.split(':')[2].strip('\n')
break break
workers_file.close() workers_file.close()
@ -443,13 +446,127 @@ def home():
return render_template('index.html', site_count = str(len(sites))) return render_template('index.html', site_count = str(len(sites)))
# Register page # Register page
@app.route('/register') @app.route('/register', methods=['GET'])
def register(): def register():
buy_licence_link = os.getenv('BUY_LICENCE_LINK') buy_licence_link = os.getenv('BUY_LICENCE_LINK')
# Show register template # Show register template
return render_template('register.html', buy_licence_link=buy_licence_link) return render_template('register.html', buy_licence_link=buy_licence_link, ERROR_MESSAGE="")
@app.route('/register', methods=['POST'])
def register_post():
buy_licence_link = os.getenv('BUY_LICENCE_LINK')
if 'licence' not in request.form:
return render_template('register.html', buy_licence_link=buy_licence_link, ERROR_MESSAGE="No licence key provided")
licence_key = request.form['licence']
# Check if licence key is valid
key_file = open('/data/licence_key.txt', 'r')
valid_key = False
for line in key_file.readlines():
if licence_key == line.strip('\n'):
valid_key = True
break
key_file.close()
if not valid_key:
return render_template('register.html', buy_licence_link=buy_licence_link, ERROR_MESSAGE="Invalid licence key")
# Get domain
domain = request.form['domain']
if domain == None:
return render_template('register.html', buy_licence_link=buy_licence_link, ERROR_MESSAGE="No domain provided")
# Check if domain already exists
if site_exists(domain):
return render_template('register.html', buy_licence_link=buy_licence_link, ERROR_MESSAGE="Domain already exists")
# Check if domain contains http:// or https://
if domain.startswith("http://") or domain.startswith("https://"):
return render_template('register.html', buy_licence_link=buy_licence_link, ERROR_MESSAGE="Domain should not contain http:// or https://")
# Set domain to lowercase
domain = domain.lower()
# Check if worker file exists
workers = None
try:
worker_file = open('/data/workers.txt', 'r')
workers = worker_file.readlines()
worker_file.close()
except FileNotFoundError:
return render_template('register.html', buy_licence_link=buy_licence_link, ERROR_MESSAGE="No workers available\nPlease contact support")
# Get a worker that has available slots
worker = None
for line in workers:
if not line.__contains__(':'):
continue
ip = line.split(':')[1].strip('\n')
resp=requests.get("http://"+ip + ":5000/status",timeout=2)
if (resp.status_code == 200):
if resp.json()['availability'] == True:
worker = line
break
if worker == None:
return render_template('register.html', buy_licence_link=buy_licence_link, ERROR_MESSAGE="No workers available\nPlease contact support")
# Delete licence key
key_file = open('/data/licence_key.txt', 'r')
lines = key_file.readlines()
key_file.close()
key_file = open('/data/licence_key.txt', 'w')
for line in lines:
if line.strip("\n") != licence_key:
key_file.write(line)
key_file.close()
# Add domain to file
sites_file = open('/data/sites.txt', 'a')
sites_file.write(domain + ':' + worker.split(':')[0] + '\n')
sites_file.close()
# Send worker request
requests.post("http://"+ worker.split(':')[1].strip('\n') + ":5000/new-site?domain=" + domain)
return redirect('/success?domain=' + domain + '&status=creating')
@app.route('/success')
def success():
if 'domain' not in request.args:
return redirect('/')
domain = request.args.get('domain')
domain = domain.lower()
if not site_exists(domain):
return render_template('success.html', message="Error: Domain does not exist\nPlease contact support")
if 'status' not in request.args:
# Get worker
worker = site_worker(domain)
if worker == None:
return render_template('success.html', message="Error: Domain does not exist\nPlease contact support")
# Get worker ip
ip = workerIP_PRIV(worker)
# Get TLSA record
resp=requests.get("http://"+ip + ":5000/tlsa?domain=" + domain,timeout=2)
json = resp.json()
publicIP = workerIP(worker)
if "tlsa" in json:
tlsa = json['tlsa']
return render_template('success.html', message="Success\nDomain: " + domain + "\nIP: " + publicIP + "\nTLSA: " + tlsa + "\nMake sure to add the TLSA record to `_443._tcp." + domain + "` or `*." + domain + "`")
else:
return render_template('success.html', message="Success\nDomain: " + domain + "\nIP: " + publicIP + "\nTLSA: Pending\nNo TLSA record found")
elif request.args.get('status') == 'creating':
return render_template('success.html')
@app.route('/site-count')
def site_count_route():
return str(get_sites_count())
# Admin page # Admin page
@app.route('/admin') @app.route('/admin')
@ -604,7 +721,7 @@ def addsite():
if domain.startswith("http://") or domain.startswith("https://"): if domain.startswith("http://") or domain.startswith("https://"):
return jsonify({'error': 'Domain should not contain http:// or https://', 'success': 'false'}) return jsonify({'error': 'Domain should not contain http:// or https://', 'success': 'false'})
domain = domain.lower()
# Check if worker file exists # Check if worker file exists
workers = None workers = None
try: try:

View File

@ -0,0 +1,10 @@
// Refresh page without status arg
// Wait 10 seconds
setTimeout(function() {
// Refresh page
// Get domain from param
var domain = urlParams.get('domain');
window.location = "https://hnshosting.au/success?domain=" + domain;
}, 10000);

View File

@ -41,10 +41,12 @@
<div class="col-md-6 col-xl-4"> <div class="col-md-6 col-xl-4">
<div class="card"> <div class="card">
<div class="card-body text-center d-flex flex-column align-items-center"> <div class="card-body text-center d-flex flex-column align-items-center">
<form method="post" action="/add-site"> <form method="post" action="/register">
<div class="mb-3"><input type="text" name="domain" placeholder="Domain"></div> <div class="mb-3"><input type="text" name="domain" placeholder="Domain"></div>
<div class="mb-3"><input type="text" placeholder="Licence Key" name="licence"></div> <div class="mb-3"><input type="text" placeholder="Licence Key" name="licence"></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">
<p style="color: rgb(255,0,0);">{{ERROR_MESSAGE}}</p><button class="btn btn-primary shadow d-block w-100" type="submit">Create site</button>
</div>
<p class="text-muted">Don't have a licence?<br><a href="{{buy_licence_link}}" target="_blank">Click here to buy one</a></p> <p class="text-muted">Don't have a licence?<br><a href="{{buy_licence_link}}" target="_blank">Click here to buy one</a></p>
</form> </form>
</div> </div>

View File

@ -0,0 +1,58 @@
<!DOCTYPE html>
<html data-bs-theme="dark" lang="en" style="height: 100%;">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>Home - HNSHosting</title>
<link rel="canonical" href="https://wp.hnshosting.au/success.html">
<meta property="og:url" content="https://wp.hnshosting.au/success.html">
<meta name="description" content="HNS Hosting Wordpress">
<link rel="icon" type="image/png" sizes="508x430" href="assets/img/favicon.png">
<link rel="icon" type="image/png" sizes="508x430" href="assets/img/favicon.png">
<link rel="icon" type="image/png" sizes="508x430" href="assets/img/favicon.png">
<link rel="icon" type="image/png" sizes="508x430" href="assets/img/favicon.png">
<link rel="icon" type="image/png" sizes="508x430" 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">
</head>
<body style="height: 100%;">
<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="/"><span class="bs-icon-sm bs-icon-circle bs-icon-primary shadow d-flex justify-content-center align-items-center me-2 bs-icon" style="background: transparent;"><img src="assets/img/favicon.png" style="width: 100%;"></span><span>HNSHosting</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" href="/">Home</a></li>
<li class="nav-item"><a class="nav-link" href="/#contact">Contact</a></li>
<li class="nav-item"><a class="nav-link" href="register">Register Site</a></li>
</ul>
</div>
</div>
</nav>
<header class="bg-dark" style="height: 75%;">
<div class="container pt-4 pt-xl-5">
<div class="row pt-5" style="height: 100%;">
<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">Success</p>
<h1 class="fw-bold">Your site is installing.<br>Please wait...</h1>
</div>
<p class="text-center">{{message}}</p>
</div>
</div>
</div>
</header>
<footer class="bg-dark">
<div class="container py-4 py-lg-5">
<hr>
<div class="text-muted d-flex justify-content-between align-items-center pt-3">
<p class="mb-0">Copyright © 2023 HNSHosting</p>
</div>
</div>
</footer>
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/bold-and-dark.js"></script>
<script src="assets/js/status_update.js"></script>
</body>
</html>