From d2e31bb684c530a845da3b8b79b9c29b49c1c71d Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Fri, 25 Aug 2023 15:51:47 +1000 Subject: [PATCH 01/23] main: Initial dashboard --- master/main.py | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/master/main.py b/master/main.py index 45ffc35..e6c0ae2 100644 --- a/master/main.py +++ b/master/main.py @@ -390,6 +390,61 @@ def workerIP(worker): return ip +# Home page +@app.route('/') +def home(): + # Show stats and info + + # Get worker info + workers = [] + try: + workers_file = open('/data/workers.txt', 'r') + workers = workers_file.readlines() + workers_file.close() + except FileNotFoundError: + pass + + # Get site info + sites = [] + try: + sites_file = open('/data/sites.txt', 'r') + sites = sites_file.readlines() + sites_file.close() + except FileNotFoundError: + pass + + # Get licence info + licences = [] + try: + licences_file = open('/data/licence_key.txt', 'r') + licences = licences_file.readlines() + licences_file.close() + except FileNotFoundError: + pass + + # Create html page + html = "

Stats


" + html += "

Workers

" + html += "

Number of workers: " + str(len(workers)) + "

" + html += "

Workers:

" + html += "" + html += "

Sites

" + html += "

Number of sites: " + str(len(sites)) + "

" + html += "

Sites:

" + html += "" + html += "

Licences

" + html += "

Number of licences: " + str(len(licences)) + "

" + + + + + # Start the server if __name__ == '__main__': app.run(debug=False, port=5000, host='0.0.0.0') \ No newline at end of file From 0c20572369d02a58b34c79461a8c88da19bb4104 Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Fri, 25 Aug 2023 15:53:55 +1000 Subject: [PATCH 02/23] actions: Attempt to fix --- .gitea/workflows/build.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index c09eabc..b108d97 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -2,8 +2,6 @@ name: Build Docker run-name: Build Docker Images on: push: - branches: - - '*' tags-ignore: - '*' From 0e2ad55eb7b91760ad02bac6f97cdf205dd80eb0 Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Fri, 25 Aug 2023 15:53:55 +1000 Subject: [PATCH 03/23] Revert "actions: Attempt to fix" This reverts commit 0c20572369d02a58b34c79461a8c88da19bb4104. --- .gitea/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index b108d97..c09eabc 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -2,6 +2,8 @@ name: Build Docker run-name: Build Docker Images on: push: + branches: + - '*' tags-ignore: - '*' From 4db44bb99ed31333e17635bdf77e782bc204ea2e Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Fri, 25 Aug 2023 16:03:45 +1000 Subject: [PATCH 04/23] actions: Try run on feature branch --- .gitea/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index c09eabc..a5f1c1e 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -4,6 +4,7 @@ on: push: branches: - '*' + - '*/*' tags-ignore: - '*' From 52fca38af98964770dbe9afb0f9044711697851c Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Fri, 25 Aug 2023 16:07:25 +1000 Subject: [PATCH 05/23] main: Added return to dash --- master/main.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/master/main.py b/master/main.py index e6c0ae2..db60ed4 100644 --- a/master/main.py +++ b/master/main.py @@ -440,6 +440,8 @@ def home(): html += "" html += "

Licences

" html += "

Number of licences: " + str(len(licences)) + "

" + + return html From 83bde4b2182273c5d1ed5c25aed0a96ba9e63134 Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Fri, 25 Aug 2023 16:29:25 +1000 Subject: [PATCH 06/23] main: Added admin page --- README.md | 2 +- master/main.py | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6957d02..def6909 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ General commands (as anyone) Docker is the easiest way to install the master server. ```sh -docker run -d -p 5000:5000 -e LICENCE-API=your-api-key -e WORKER_KEY=your-api-key --name hnshosting-master git.woodburn.au/nathanwoodburn/hnshosting-master:latest -v ./data:/data +docker run -d -p 5000:5000 -e LICENCE_KEY=your-api-key -e WORKER_KEY=your-api-key -e ADMIN_KEY=admin-key --name hnshosting-master git.woodburn.au/nathanwoodburn/hnshosting-master:latest -v ./data:/data ``` You can also mount a docker volume to /data to store the files instead of mounting a host directory. diff --git a/master/main.py b/master/main.py index db60ed4..4755bf0 100644 --- a/master/main.py +++ b/master/main.py @@ -1,4 +1,4 @@ -from flask import Flask, request, jsonify +from flask import Flask, make_response, redirect, request, jsonify import dotenv import os import requests @@ -10,12 +10,14 @@ dotenv.load_dotenv() app = Flask(__name__) +loggins = [] + # API add license key (requires API key in header) @app.route('/add-licence', methods=['POST']) def add_license(): # Get API header api_key = request.headers.get('key') - if api_key != os.getenv('LICENCE-API'): + if api_key != os.getenv('LICENCE_KEY'): return jsonify({'error': 'Invalid API key', 'success': 'false'}) # Generate licence key @@ -441,7 +443,35 @@ def home(): html += "

Licences

" html += "

Number of licences: " + str(len(licences)) + "

" + html += "

API

" return html + +# Admin page +@app.route('/admin',) +def admin(): + # Check if logged in + loggin_key = request.cookies.get('login_key') + + if request.method == 'POST': + # Handle login + password = request.form['password'] + if os.getenv(ADMIN_KEY) == password: + # Generate login key + login_key = os.urandom(32).hex() + loggins.append(login_key) + # Set cookie + resp = make_response(redirect('/admin')) + resp.set_cookie('login_key', login_key) + return resp + + + if loggin_key == None: + return "

Admin


" + if loggin_key not in loggins: + return "

Admin


" + + return "

Admin


Logged in" + From 9e485265af0972e457d0b0bbad01958896b40406 Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Fri, 25 Aug 2023 16:32:50 +1000 Subject: [PATCH 07/23] main: Added the post method --- master/main.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/master/main.py b/master/main.py index 4755bf0..40697a1 100644 --- a/master/main.py +++ b/master/main.py @@ -447,10 +447,10 @@ def home(): return html # Admin page -@app.route('/admin',) +@app.route('/admin', methods=['GET', 'POST']) def admin(): # Check if logged in - loggin_key = request.cookies.get('login_key') + login_key = request.cookies.get('login_key') if request.method == 'POST': # Handle login @@ -465,9 +465,9 @@ def admin(): return resp - if loggin_key == None: + if login_key == None: return "

Admin


" - if loggin_key not in loggins: + if login_key not in loggins: return "

Admin


" return "

Admin


Logged in" From 8f962804a43c814de7b30722a7aa5fc7bd3b0461 Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Fri, 25 Aug 2023 16:36:53 +1000 Subject: [PATCH 08/23] main: Login fixes --- master/main.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/master/main.py b/master/main.py index 40697a1..733e91c 100644 --- a/master/main.py +++ b/master/main.py @@ -10,7 +10,7 @@ dotenv.load_dotenv() app = Flask(__name__) -loggins = [] +logins = [] # API add license key (requires API key in header) @app.route('/add-licence', methods=['POST']) @@ -447,33 +447,35 @@ def home(): return html # Admin page -@app.route('/admin', methods=['GET', 'POST']) +@app.route('/admin') def admin(): # Check if logged in login_key = request.cookies.get('login_key') + if login_key == None: + return "

Admin


" + if login_key not in logins: + return "

Admin


" + + return "

Admin


Logged in" + + +@app.route('/login', methods=['POST']) +def admin(): if request.method == 'POST': # Handle login + print('Login attempt', flush=True) password = request.form['password'] - if os.getenv(ADMIN_KEY) == password: + if os.getenv('ADMIN_KEY') == password: # Generate login key login_key = os.urandom(32).hex() - loggins.append(login_key) + logins.append(login_key) # Set cookie resp = make_response(redirect('/admin')) resp.set_cookie('login_key', login_key) return resp - if login_key == None: - return "

Admin


" - if login_key not in loggins: - return "

Admin


" - - return "

Admin


Logged in" - - - From 843d2d12a0d017d78437b4d052560031768ae2e5 Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Fri, 25 Aug 2023 16:38:34 +1000 Subject: [PATCH 09/23] main: Fixed login typo --- master/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/master/main.py b/master/main.py index 733e91c..ad3a760 100644 --- a/master/main.py +++ b/master/main.py @@ -461,7 +461,7 @@ def admin(): @app.route('/login', methods=['POST']) -def admin(): +def login(): if request.method == 'POST': # Handle login print('Login attempt', flush=True) From 9853214d8309ad16df830e2fb3bdfecbfce5653f Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Fri, 25 Aug 2023 16:47:22 +1000 Subject: [PATCH 10/23] main: Add failed login page --- master/main.py | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/master/main.py b/master/main.py index ad3a760..e3b4243 100644 --- a/master/main.py +++ b/master/main.py @@ -462,18 +462,25 @@ def admin(): @app.route('/login', methods=['POST']) def login(): - if request.method == 'POST': - # Handle login - print('Login attempt', flush=True) - password = request.form['password'] - if os.getenv('ADMIN_KEY') == password: - # Generate login key - login_key = os.urandom(32).hex() - logins.append(login_key) - # Set cookie - resp = make_response(redirect('/admin')) - resp.set_cookie('login_key', login_key) - return resp + # Handle login + print('Login attempt', flush=True) + password = request.form['password'] + print('Password: ' + password, flush=True) + if os.getenv('ADMIN_KEY') == password: + print('Login success', flush=True) + # Generate login key + login_key = os.urandom(32).hex() + logins.append(login_key) + # Set cookie + resp = make_response(redirect('/admin')) + resp.set_cookie('login_key', login_key) + return resp + print('Login failed', flush=True) + return redirect('/failed-login') + +@app.route('/failed-login') +def failed_login(): + return "

Failed login


" From 1fb2493848a665bd6f8c8c16339f9a11d9872f68 Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Fri, 25 Aug 2023 16:51:19 +1000 Subject: [PATCH 11/23] main: Updated form --- master/main.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/master/main.py b/master/main.py index e3b4243..9b38814 100644 --- a/master/main.py +++ b/master/main.py @@ -453,9 +453,9 @@ def admin(): login_key = request.cookies.get('login_key') if login_key == None: - return "

Admin


" + return "

Admin


" if login_key not in logins: - return "

Admin


" + return "

Admin


" return "

Admin


Logged in" @@ -480,7 +480,7 @@ def login(): @app.route('/failed-login') def failed_login(): - return "

Failed login


" + return "

Failed login


" From c9be5cedcb884a86cd7357b1ba2e9d877e8aac02 Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Fri, 25 Aug 2023 17:07:41 +1000 Subject: [PATCH 12/23] main: Initial admin page --- master/main.py | 60 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/master/main.py b/master/main.py index 9b38814..b075002 100644 --- a/master/main.py +++ b/master/main.py @@ -443,7 +443,7 @@ def home(): html += "

Licences

" html += "

Number of licences: " + str(len(licences)) + "

" - html += "

API

" + html += "

Admin

" return html # Admin page @@ -457,15 +457,69 @@ def admin(): if login_key not in logins: return "

Admin


" - return "

Admin


Logged in" + # Show some admin stuff + licences = [] + try: + licences_file = open('/data/licence_key.txt', 'r') + licences = licences_file.readlines() + licences_file.close() + except FileNotFoundError: + pass + + # Create html page + html = "

Admin


" + html += "

Licences

" + html += "

Number of licences: " + str(len(licences)) + "

" + html += "

Licences:

" + html += "
    " + for licence in licences: + html += "
  • " + licence.strip('\n') + "
  • " + html += "
" + html += "

API

" + html += "

API key: " + os.getenv('LICENCE_KEY') + "

" + html += "

Worker key: " + os.getenv('WORKER_KEY') + "

" + html += "

Stripe

" + # Check if stripe is enabled + if os.getenv('STRIPE_SECRET') == None: + html += "

Stripe is not enabled

" + else: + html += "

Stripe is enabled

" + + html += "

Workers

" + for worker in list_workers().json()['workers']: + html += "

Worker: " + worker['worker'] + " | IP: " + worker['ip'] + " | Status: " + worker['status'] + " | Sites: " + str(worker['sites']) + "

" + + html += "

Sites

" + sites = [] + try: + sites_file = open('/data/sites.txt', 'r') + sites = sites_file.readlines() + sites_file.close() + except FileNotFoundError: + pass + + for site in sites: + html += "

Domain: " + site.split(':')[0] + " | Worker: " + site.split(':')[1].strip('\n') + "

" + + html += "


" + + html += "

Logout

" + html += "

Logout

" + + + return html @app.route('/login', methods=['POST']) def login(): # Handle login print('Login attempt', flush=True) + # Check if form contains password + if 'password' not in request.form: + print('Login failed', flush=True) + return redirect('/failed-login') + password = request.form['password'] - print('Password: ' + password, flush=True) if os.getenv('ADMIN_KEY') == password: print('Login success', flush=True) # Generate login key From 0a33a6150d003304f07072e3ab817338030409b5 Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Fri, 25 Aug 2023 17:13:34 +1000 Subject: [PATCH 13/23] main: Fixed worker info --- master/main.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/master/main.py b/master/main.py index b075002..eae92e6 100644 --- a/master/main.py +++ b/master/main.py @@ -486,8 +486,27 @@ def admin(): html += "

Stripe is enabled

" html += "

Workers

" - for worker in list_workers().json()['workers']: - html += "

Worker: " + worker['worker'] + " | IP: " + worker['ip'] + " | Status: " + worker['status'] + " | Sites: " + str(worker['sites']) + "

" + workers = [] + try: + workers_file = open('/data/workers.txt', 'r') + workers = workers_file.readlines() + workers_file.close() + except FileNotFoundError: + pass + + for worker in workers: + html += "

Name: " + worker.split(':')[0] + " | Public IP " + worker.split(':')[2].strip('\n') + " | Private IP " + worker.split(':')[1] + # Check worker status + online=True + resp=requests.get("http://"+worker.split(':')[1].strip('\n') + ":5000/status",timeout=2) + if (resp.status_code != 200): + online=False + if online: + html += " | Status: Online | Sites: " + str(resp.json()['num_sites']) + " | Availability: " + str(resp.json()['availability']) + else: + html += " | Status: Offline" + html += "

" + html += "

Sites

" sites = [] From ba96f9b84b9d11f14b4286aa41513f21501c1cdb Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Fri, 25 Aug 2023 17:18:37 +1000 Subject: [PATCH 14/23] main: Added new worker admin --- master/main.py | 67 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/master/main.py b/master/main.py index eae92e6..3b8bfe7 100644 --- a/master/main.py +++ b/master/main.py @@ -495,6 +495,9 @@ def admin(): pass for worker in workers: + if not worker.__contains__(':'): + continue + html += "

Name: " + worker.split(':')[0] + " | Public IP " + worker.split(':')[2].strip('\n') + " | Private IP " + worker.split(':')[1] # Check worker status online=True @@ -518,17 +521,79 @@ def admin(): pass for site in sites: + if not site.__contains__(':'): + continue + html += "

Domain: " + site.split(':')[0] + " | Worker: " + site.split(':')[1].strip('\n') + "

" html += "


" + # Form to add worker + html += "

Add worker

" + html += "
" + html += "

Name:

" + html += "

Public IP:

" + html += "

Private IP:

" + html += "" + html += "
" + # Form to add licence + #! TODO - html += "

Logout

" + + html += "

Logout

" html += "

Logout

" return html +@app.route('/new-worker', methods=['POST']) +def new_worker(): + # Check cookie + login_key = request.cookies.get('login_key') + + if login_key == None: + return redirect('/admin') + if login_key not in logins: + return redirect('/admin') + worker = request.form['name'] + worker_IP = request.form['ip'] + worker_PRIV = request.form['priv'] + + + # Check worker file + try: + workers_file = open('/data/workers.txt', 'r') + except FileNotFoundError: + workers_file = open('/data/workers.txt', 'w') + workers_file.close() + workers_file = open('/data/workers.txt', 'r') + + # Check if worker already exists + if worker in workers_file.read(): + return jsonify({'error': 'Worker already exists', 'success': 'false'}) + + workers_file.close() + + # Add worker to file + workers_file = open('/data/workers.txt', 'a') + workers_file.write(worker + ":" + worker_PRIV + ":"+ worker_IP + '\n') + workers_file.close() + + return redirect('/admin') + + +@app.route('/logout') +def logout(): + login_key = request.cookies.get('login_key') + if login_key == None: + return redirect('/admin') + if login_key not in logins: + return redirect('/admin') + + logins.remove(login_key) + return redirect('/admin') + + @app.route('/login', methods=['POST']) def login(): # Handle login From 7703bfa4d5f239f0d9efb7d0d1b86d67579f2a9f Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Fri, 25 Aug 2023 17:31:32 +1000 Subject: [PATCH 15/23] main: Catch for offline worker --- master/main.py | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/master/main.py b/master/main.py index 3b8bfe7..bdbf9c7 100644 --- a/master/main.py +++ b/master/main.py @@ -182,18 +182,23 @@ def list_workers(): continue online=True - resp=requests.get("http://"+worker.split(':')[1].strip('\n') + ":5000/status",timeout=2) - if (resp.status_code != 200): - online=False - worker_list.append({'worker': worker.split(':')[0],'ip': worker.split(':')[2].strip('\n'), 'online': online, 'sites': 0, 'status': 'offline'}) + try: + resp=requests.get("http://"+worker.split(':')[1].strip('\n') + ":5000/status",timeout=2) + + if (resp.status_code != 200): + online=False + worker_list.append({'worker': worker.split(':')[0],'ip': worker.split(':')[2].strip('\n'), 'online': online, 'sites': 0, 'status': 'offline'}) + continue + sites = resp.json()['num_sites'] + availability = resp.json()['availability'] + if availability == True: + worker_list.append({'worker': worker.split(':')[0],'ip': worker.split(':')[2].strip('\n'), 'online': online, 'sites': sites, 'status': 'ready'}) + else: + worker_list.append({'worker': worker.split(':')[0],'ip': worker.split(':')[2].strip('\n'), 'online': online, 'sites': sites, 'status': 'full'}) + except: + worker_list.append({'worker': worker.split(':')[0],'ip': worker.split(':')[2].strip('\n'), 'online': 'False', 'sites': 0, 'status': 'offline'}) continue - sites = resp.json()['num_sites'] - availability = resp.json()['availability'] - if availability == True: - worker_list.append({'worker': worker.split(':')[0],'ip': worker.split(':')[2].strip('\n'), 'online': online, 'sites': sites, 'status': 'ready'}) - else: - worker_list.append({'worker': worker.split(':')[0],'ip': worker.split(':')[2].strip('\n'), 'online': online, 'sites': sites, 'status': 'full'}) - + if len(worker_list) == 0: return jsonify({'error': 'No workers available', 'success': 'false'}) return jsonify({'success': 'true', 'workers': worker_list}) @@ -501,13 +506,15 @@ def admin(): html += "

Name: " + worker.split(':')[0] + " | Public IP " + worker.split(':')[2].strip('\n') + " | Private IP " + worker.split(':')[1] # Check worker status online=True - resp=requests.get("http://"+worker.split(':')[1].strip('\n') + ":5000/status",timeout=2) - if (resp.status_code != 200): - online=False - if online: - html += " | Status: Online | Sites: " + str(resp.json()['num_sites']) + " | Availability: " + str(resp.json()['availability']) - else: + try: + resp=requests.get("http://"+worker.split(':')[1].strip('\n') + ":5000/status",timeout=2) + if (resp.status_code != 200): + html += " | Status: Offline" + else: + html += " | Status: Online | Sites: " + str(resp.json()['num_sites']) + " | Availability: " + str(resp.json()['availability']) + except: html += " | Status: Offline" + html += "

" From 48330f7ef9fe7de148cf5d5c3d65891b275fd0d3 Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Fri, 25 Aug 2023 17:45:46 +1000 Subject: [PATCH 16/23] main: Added licence and site creation for admin --- master/main.py | 94 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 89 insertions(+), 5 deletions(-) diff --git a/master/main.py b/master/main.py index bdbf9c7..1f3d600 100644 --- a/master/main.py +++ b/master/main.py @@ -542,16 +542,100 @@ def admin(): html += "

Private IP:

" html += "" html += "" - # Form to add licence - #! TODO + + html += "

New Licence

" + # Form to add site + html += "

Add site

" + html += "
" + html += "

Domain:

" + html += "" + html += "
" + - - html += "

Logout

" - html += "

Logout

" + html += "
Logout" return html + +@app.route('/add-site') +def licence(): + # Check cookie + login_key = request.cookies.get('login_key') + if login_key == None: + return redirect('/admin') + if login_key not in logins: + return redirect('/admin') + + # Get domain + domain = request.args.get('domain') + if domain == None: + return redirect('/admin') + # Check if domain already exists + if site_exists(domain): + return jsonify({'error': 'Domain already exists', 'success': 'false'}) + + # Check if domain contains http:// or https:// + if domain.startswith("http://") or domain.startswith("https://"): + return jsonify({'error': 'Domain should not contain http:// or https://', 'success': 'false'}) + + + # 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 jsonify({'error': 'No workers available', 'success': 'false'}) + + # 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 jsonify({'error': 'No workers available', 'success': 'false'}) + + + # 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('/admin') + + +@app.route('/licence') +def licence(): + # Check cookie + login_key = request.cookies.get('login_key') + if login_key == None: + return redirect('/admin') + if login_key not in logins: + return redirect('/admin') + licence_key = os.urandom(16).hex() + + # Add license key to file + key_file = open('/data/licence_key.txt', 'a') + key_file.write(licence_key + '\n') + key_file.close() + + return "

Licence key


" + licence_key + "


Back" + + + @app.route('/new-worker', methods=['POST']) def new_worker(): # Check cookie From 8a2cad16ec03fc21e088d90194e886934da971b0 Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Fri, 25 Aug 2023 17:48:42 +1000 Subject: [PATCH 17/23] main: Fixed typo --- master/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/master/main.py b/master/main.py index 1f3d600..057c1d8 100644 --- a/master/main.py +++ b/master/main.py @@ -559,7 +559,7 @@ def admin(): @app.route('/add-site') -def licence(): +def addsite(): # Check cookie login_key = request.cookies.get('login_key') if login_key == None: @@ -625,6 +625,7 @@ def licence(): return redirect('/admin') if login_key not in logins: return redirect('/admin') + licence_key = os.urandom(16).hex() # Add license key to file From 20dbf6c0df577582fb42d3c67c802a8337e9d60d Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Fri, 25 Aug 2023 17:52:17 +1000 Subject: [PATCH 18/23] main: Fixed a few bits of the admin panel --- master/main.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/master/main.py b/master/main.py index 057c1d8..4b5d6dd 100644 --- a/master/main.py +++ b/master/main.py @@ -533,7 +533,7 @@ def admin(): html += "

Domain: " + site.split(':')[0] + " | Worker: " + site.split(':')[1].strip('\n') + "

" - html += "


" + html += "

" # Form to add worker html += "

Add worker

" html += "
" @@ -543,7 +543,7 @@ def admin(): html += "" html += "
" - html += "

New Licence

" + html += "

Add Licence


" # Form to add site html += "

Add site

" html += "
" @@ -558,7 +558,7 @@ def admin(): return html -@app.route('/add-site') +@app.route('/add-site', methods=['POST']) def addsite(): # Check cookie login_key = request.cookies.get('login_key') From 3628e0a4ac6572c4d1062f9b5c375547060e20bd Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Fri, 25 Aug 2023 17:57:51 +1000 Subject: [PATCH 19/23] main: Added customer self service --- master/main.py | 57 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/master/main.py b/master/main.py index 4b5d6dd..e1cbc4e 100644 --- a/master/main.py +++ b/master/main.py @@ -430,7 +430,15 @@ def home(): pass # Create html page - html = "

Stats


" + html = "

Welcome


" + html += "

Create a site

" + html += "" + html += "

Domain:

" + html += "

Licence key:

" + html += "" + html += "" + + html += "

Stats


" html += "

Workers

" html += "

Number of workers: " + str(len(workers)) + "

" html += "

Workers:

" @@ -560,13 +568,38 @@ def admin(): @app.route('/add-site', methods=['POST']) def addsite(): - # Check cookie - login_key = request.cookies.get('login_key') - if login_key == None: - return redirect('/admin') - if login_key not in logins: - return redirect('/admin') - + # Check for licence key + if 'licence' not in request.form: + # Check cookie + login_key = request.cookies.get('login_key') + if login_key == None: + return redirect('/admin') + if login_key not in logins: + return redirect('/admin') + else: + # Use licence key + 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 jsonify({'error': 'Invalid licence', 'success': 'false'}) + + # 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() + # Get domain domain = request.args.get('domain') if domain == None: @@ -614,7 +647,13 @@ def addsite(): # Send worker request requests.post("http://"+ worker.split(':')[1].strip('\n') + ":5000/new-site?domain=" + domain) - return redirect('/admin') + html = "

Site creating...


" + html += "

Domain: " + domain + "

" + html += "

Worker: " + worker.split(':')[0] + "

" + html += "

Worker IP: " + worker.split(':')[1].strip('\n') + "

" + html += "

Check status here

" + + return html @app.route('/licence') From 73d11a0f765cf9f939e7c797f1cb6a38812e6c54 Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Fri, 25 Aug 2023 18:01:49 +1000 Subject: [PATCH 20/23] main: Fixed customer side --- master/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/master/main.py b/master/main.py index e1cbc4e..0dbc366 100644 --- a/master/main.py +++ b/master/main.py @@ -601,9 +601,9 @@ def addsite(): key_file.close() # Get domain - domain = request.args.get('domain') + domain = request.form['domain'] if domain == None: - return redirect('/admin') + return jsonify({'error': 'No domain sent', 'success': 'false'}) # Check if domain already exists if site_exists(domain): return jsonify({'error': 'Domain already exists', 'success': 'false'}) From c8eafb6406bcc5b5c77560506b0b6f1ac43d9c47 Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Fri, 25 Aug 2023 18:13:36 +1000 Subject: [PATCH 21/23] main: Updated domain info page --- master/main.py | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/master/main.py b/master/main.py index 0dbc366..aacbe9d 100644 --- a/master/main.py +++ b/master/main.py @@ -233,6 +233,35 @@ def site_status(): return jsonify({'success': 'false', 'domain': domain, 'ip': publicIP, 'tlsa': 'none','error': 'No TLSA record found'}) +@app.route('/info') +def site_status(): + domain = request.args.get('domain') + if domain == None: + return "

Invalid domain

" + + # Check if domain exists + if not site_exists(domain): + return "

Domain does not exist

" + + # Get worker + worker = site_worker(domain) + if worker == None: + return "

Domain does not exist

" + + # 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 "

Domain: " + domain + "


IP: " + publicIP + "


TLSA: " + tlsa + "


Make sure to add the TLSA record to `_443._tcp." + domain + "` or `*." + domain + "`

" + else: + return "

Domain: " + domain + "


IP: " + publicIP + "


TLSA: none


No TLSA record found

" + @app.route('/tlsa', methods=['GET']) def tlsa(): domain = request.args.get('domain') @@ -651,7 +680,7 @@ def addsite(): html += "

Domain: " + domain + "

" html += "

Worker: " + worker.split(':')[0] + "

" html += "

Worker IP: " + worker.split(':')[1].strip('\n') + "

" - html += "

Check status here

" + html += "

Check status

" return html From 2ef8e2c38d199c3efbe141cdaf5e520257b28c38 Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Fri, 25 Aug 2023 18:17:12 +1000 Subject: [PATCH 22/23] main: Fixed reused function name --- master/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/master/main.py b/master/main.py index aacbe9d..e816343 100644 --- a/master/main.py +++ b/master/main.py @@ -234,7 +234,7 @@ def site_status(): @app.route('/info') -def site_status(): +def site_status_human(): domain = request.args.get('domain') if domain == None: return "

Invalid domain

" From 67ed81fd2eb65290fa1e38aaeac2c0c7fc58c6d7 Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Fri, 25 Aug 2023 18:22:20 +1000 Subject: [PATCH 23/23] main: Added info link to domains in admin page --- master/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/master/main.py b/master/main.py index e816343..426ea7e 100644 --- a/master/main.py +++ b/master/main.py @@ -567,8 +567,8 @@ def admin(): for site in sites: if not site.__contains__(':'): continue - - html += "

Domain: " + site.split(':')[0] + " | Worker: " + site.split(':')[1].strip('\n') + "

" + domain = site.split(':')[0] + html += "

Domain: " + domain + " | Worker: " + site.split(':')[1].strip('\n') + " | Info

" html += "

" # Form to add worker