37 Commits

Author SHA1 Message Date
67ed81fd2e main: Added info link to domains in admin page
All checks were successful
Build Docker / Build Bot (push) Successful in 21s
Build Docker / Build Master (push) Successful in 27s
2023-08-25 18:22:20 +10:00
2ef8e2c38d main: Fixed reused function name
All checks were successful
Build Docker / Build Bot (push) Successful in 24s
Build Docker / Build Master (push) Successful in 26s
2023-08-25 18:17:12 +10:00
c8eafb6406 main: Updated domain info page
All checks were successful
Build Docker / Build Bot (push) Successful in 27s
Build Docker / Build Master (push) Successful in 28s
2023-08-25 18:13:36 +10:00
73d11a0f76 main: Fixed customer side
All checks were successful
Build Docker / Build Bot (push) Successful in 26s
Build Docker / Build Master (push) Successful in 28s
2023-08-25 18:01:49 +10:00
3628e0a4ac main: Added customer self service
All checks were successful
Build Docker / Build Bot (push) Successful in 22s
Build Docker / Build Master (push) Successful in 28s
2023-08-25 17:57:51 +10:00
20dbf6c0df main: Fixed a few bits of the admin panel
All checks were successful
Build Docker / Build Bot (push) Successful in 21s
Build Docker / Build Master (push) Successful in 27s
2023-08-25 17:52:17 +10:00
8a2cad16ec main: Fixed typo
All checks were successful
Build Docker / Build Master (push) Successful in 27s
Build Docker / Build Bot (push) Successful in 26s
2023-08-25 17:48:42 +10:00
48330f7ef9 main: Added licence and site creation for admin
All checks were successful
Build Docker / Build Bot (push) Successful in 24s
Build Docker / Build Master (push) Successful in 27s
2023-08-25 17:45:46 +10:00
7703bfa4d5 main: Catch for offline worker
All checks were successful
Build Docker / Build Bot (push) Successful in 22s
Build Docker / Build Master (push) Successful in 28s
2023-08-25 17:31:32 +10:00
ba96f9b84b main: Added new worker admin
All checks were successful
Build Docker / Build Master (push) Successful in 28s
Build Docker / Build Bot (push) Successful in 27s
2023-08-25 17:18:37 +10:00
0a33a6150d main: Fixed worker info
All checks were successful
Build Docker / Build Bot (push) Successful in 26s
Build Docker / Build Master (push) Successful in 27s
2023-08-25 17:13:34 +10:00
c9be5cedcb main: Initial admin page
All checks were successful
Build Docker / Build Bot (push) Successful in 27s
Build Docker / Build Master (push) Successful in 29s
2023-08-25 17:07:44 +10:00
1fb2493848 main: Updated form
All checks were successful
Build Docker / Build Bot (push) Successful in 20s
Build Docker / Build Master (push) Successful in 26s
2023-08-25 16:51:19 +10:00
9853214d83 main: Add failed login page
All checks were successful
Build Docker / Build Bot (push) Successful in 22s
Build Docker / Build Master (push) Successful in 28s
2023-08-25 16:47:22 +10:00
843d2d12a0 main: Fixed login typo
All checks were successful
Build Docker / Build Bot (push) Successful in 25s
Build Docker / Build Master (push) Successful in 26s
2023-08-25 16:38:34 +10:00
8f962804a4 main: Login fixes
All checks were successful
Build Docker / Build Bot (push) Successful in 25s
Build Docker / Build Master (push) Successful in 27s
2023-08-25 16:36:53 +10:00
9e485265af main: Added the post method
All checks were successful
Build Docker / Build Bot (push) Successful in 24s
Build Docker / Build Master (push) Successful in 26s
2023-08-25 16:32:50 +10:00
83bde4b218 main: Added admin page
All checks were successful
Build Docker / Build Bot (push) Successful in 21s
Build Docker / Build Master (push) Successful in 27s
2023-08-25 16:29:25 +10:00
52fca38af9 main: Added return to dash
All checks were successful
Build Docker / Build Bot (push) Successful in 20s
Build Docker / Build Master (push) Successful in 26s
2023-08-25 16:07:25 +10:00
4db44bb99e actions: Try run on feature branch
All checks were successful
Build Docker / Build Bot (push) Successful in 26s
Build Docker / Build Master (push) Successful in 27s
2023-08-25 16:03:45 +10:00
0e2ad55eb7 Revert "actions: Attempt to fix"
This reverts commit 0c20572369.
2023-08-25 16:01:30 +10:00
0c20572369 actions: Attempt to fix 2023-08-25 15:53:55 +10:00
d2e31bb684 main: Initial dashboard 2023-08-25 15:51:57 +10:00
bbe70647d7 docs: Added info on free mode
All checks were successful
Build Docker / Build Master (push) Successful in 23s
Build Docker / Build Bot (push) Successful in 27s
2023-08-25 14:26:59 +10:00
d6dffc0464 main: Small fixed for licences
All checks were successful
Build Docker / Build Master (push) Successful in 23s
Build Docker / Build Bot (push) Successful in 25s
2023-08-25 14:20:58 +10:00
fb9295c260 bot: Made licence an optional arg
All checks were successful
Build Docker / Build Master (push) Successful in 20s
Build Docker / Build Bot (push) Successful in 24s
2023-08-25 14:17:12 +10:00
c4ae4561e3 main: Cleared up invalid licence response
All checks were successful
Build Docker / Build Bot (push) Successful in 25s
Build Docker / Build Master (push) Successful in 26s
2023-08-25 14:13:29 +10:00
2902624637 bot: Fix to enable free licences of FREE MODE
All checks were successful
Build Docker / Build Master (push) Successful in 21s
Build Docker / Build Bot (push) Successful in 25s
2023-08-25 14:09:26 +10:00
5049796d07 bot: Added free mode
All checks were successful
Build Docker / Build Master (push) Successful in 21s
Build Docker / Build Bot (push) Successful in 24s
2023-08-25 14:01:36 +10:00
5efe4860fc actions: Fix syntax
All checks were successful
Build Docker for Release / Build Master (push) Successful in 24s
Build Docker for Release / Build Bot (push) Successful in 24s
Build Docker / Build Master (push) Successful in 29s
Build Docker / Build Bot (push) Successful in 27s
2023-08-25 13:55:31 +10:00
499a7e348b Merge branch 'develop' into main
All checks were successful
Build Docker / Build Master (push) Successful in 23s
Build Docker / Build Bot (push) Successful in 21s
2023-08-25 13:53:58 +10:00
ed94263050 actions: Don't run dev action when tag is pushed
All checks were successful
Build Docker / Build Master (push) Successful in 25s
Build Docker / Build Bot (push) Successful in 23s
2023-08-25 13:53:28 +10:00
4841344d63 actions: Finished adding release
All checks were successful
Build Docker / Build Master (push) Successful in 26s
Build Docker / Build Bot (push) Successful in 26s
2023-08-25 13:48:39 +10:00
3266dbafa9 Merge branch 'develop' into main
All checks were successful
Build Docker / Build Master (push) Successful in 26s
Build Docker / Build Bot (push) Successful in 25s
2023-08-25 12:36:40 +10:00
b56ece2216 Merge branch 'develop' into main
All checks were successful
Build Docker / Build Bot (push) Successful in 25s
Build Docker / Build Master (push) Successful in 25s
2023-08-25 12:14:06 +10:00
0383b47b8e Merge branch 'develop' into main
All checks were successful
Build Docker / Build Bot (push) Successful in 21s
Build Docker / Build Master (push) Successful in 23s
2023-08-24 18:36:46 +10:00
afd5286ef5 Merge branch 'develop' into main
All checks were successful
Build Docker / Build Master (push) Successful in 22s
Build Docker / Build Bot (push) Successful in 20s
2023-08-24 18:15:16 +10:00
5 changed files with 502 additions and 26 deletions

View File

@@ -1,6 +1,12 @@
name: Build Docker
run-name: Build Docker Images
on: [push]
on:
push:
branches:
- '*'
- '*/*'
tags-ignore:
- '*'
jobs:
Build Master:

View File

@@ -7,10 +7,54 @@ on:
- '*'
jobs:
TEST:
Build Master:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Print tag
run: echo "tag=${GITHUB_REF#refs/tags/}"
- name: Install Docker
run : |
apt-get install ca-certificates curl gnupg
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update
apt-get install docker-ce-cli -y
- name: Build Docker image
run : |
cd master
echo "${{ secrets.DOCKERGIT_TOKEN }}" | docker login git.woodburn.au -u nathanwoodburn --password-stdin
tag=${GITHUB_REF#refs/tags/}
docker build -t hnshosting-master:release-$tag .
docker tag hnshosting-master:release-$tag git.woodburn.au/nathanwoodburn/hnshosting-master:release-$tag
docker push git.woodburn.au/nathanwoodburn/hnshosting-master:release-$tag
docker tag hnshosting-master:release-$tag git.woodburn.au/nathanwoodburn/hnshosting-master:release
docker push git.woodburn.au/nathanwoodburn/hnshosting-master:release
Build Bot:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install Docker
run : |
apt-get install ca-certificates curl gnupg
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update
apt-get install docker-ce-cli -y
- name: Build Docker image
run : |
cd discord-bot
echo "${{ secrets.DOCKERGIT_TOKEN }}" | docker login git.woodburn.au -u nathanwoodburn --password-stdin
tag=${GITHUB_REF#refs/tags/}
docker build -t hnshosting-bot:release-$tag .
docker tag hnshosting-bot:release-$tag git.woodburn.au/nathanwoodburn/hnshosting-bot:release-$tag
docker push git.woodburn.au/nathanwoodburn/hnshosting-bot:release-$tag
docker tag hnshosting-bot:release-$tag git.woodburn.au/nathanwoodburn/hnshosting-bot:release
docker push git.woodburn.au/nathanwoodburn/hnshosting-bot:release

View File

@@ -18,6 +18,10 @@ The bot will be used to provide an easier way to manage the master server.
| Red Connections | Secured by VPN or over LAN ONLY. (NOT API SECURED) |
| Yellow Connections | HTTP/HTTPS public traffic |
## Features
- [x] Add new worker server to master server pool
- [x] Create wordpress site on random worker server
- [x] Optional Free mode (see Bot section)
## Usage
@@ -43,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.
@@ -106,3 +110,9 @@ Docker install
```sh
docker run -d -e MASTER_IP=<MASTER SERVER IP> -e DISCORD_TOKEN=<YOUR-BOT-TOKEN> -e LICENCE_KEY=your-api-key -e WORKER_KEY=your-api-key --name hnshosting-bot git.woodburn.au/nathanwoodburn/hnshosting-bot:latest
```
Enable the free mode by setting the following environment variable.
This will allow you to create a wordpress site without using a licence key using the /createsite command.
```
FREE_MODE: true
```

View File

@@ -13,9 +13,14 @@ Master_Port = os.getenv('MASTER_PORT')
if Master_Port == None:
Master_Port = "5000"
FREE_LICENCE = os.getenv('FREE_LICENCE')
FREE_LICENCE = os.getenv('FREE_MODE')
if FREE_LICENCE == None:
FREE_LICENCE = False
else:
if FREE_LICENCE.lower() == "true":
FREE_LICENCE = True
else:
FREE_LICENCE = False
intents = discord.Intents.default()
client = discord.Client(intents=intents)
@@ -50,7 +55,6 @@ async def listworkers(ctx):
@tree.command(name="licence", description="Gets a licence key")
async def license(ctx):
if ctx.user.id != ADMINID:
if FREE_LICENCE == False: # If free licences are enabled then anyone can get a licence
await ctx.response.send_message("You do not have permission to use this command",ephemeral=True)
return
@@ -65,7 +69,17 @@ async def license(ctx):
await ctx.response.send_message(f"Error getting license\n" + r.text,ephemeral=True)
@tree.command(name="createsite", description="Create a new WordPress site")
async def createsite(ctx, domain: str, licence: str):
async def createsite(ctx, domain: str, licence: str = None):
if FREE_LICENCE == True: # If free licences are enabled then auto generate a licence
r = requests.post(f"http://{Master_IP}:{Master_Port}/add-licence",headers={"key":os.getenv('LICENCE_KEY')})
if r.status_code == 200:
json = r.json()
if json['success'] == "true":
licence = json['licence_key']
else:
await ctx.response.send_message(f"Error getting license\n" + json['error'])
return
r = requests.post(f"http://{Master_IP}:{Master_Port}/new-site?domain={domain}",headers={"key":licence})
if r.status_code == 200:
json = r.json()

View File

@@ -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__)
logins = []
# 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
@@ -37,14 +39,22 @@ def new_site():
# Verify both API key and domain exist
if api_key == None:
return jsonify({'error': 'Missing API key', 'success': 'false'})
return jsonify({'error': 'No licence provided', 'success': 'false'})
if domain == None:
return jsonify({'error': 'Missing domain', 'success': 'false'})
# Check if API key is a valid site key
if api_key not in open('/data/licence_key.txt', 'r').read():
return jsonify({'error': 'Invalid API key', 'success': 'false'})
key_file = open('/data/licence_key.txt', 'r')
valid_key = False
for line in key_file.readlines():
if api_key == line.strip('\n'):
valid_key = True
break
key_file.close()
if not valid_key:
return jsonify({'error': 'Invalid licence', 'success': 'false'})
# Check if domain already exists
if site_exists(domain):
@@ -172,7 +182,9 @@ def list_workers():
continue
online=True
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'})
@@ -183,6 +195,9 @@ def list_workers():
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
if len(worker_list) == 0:
return jsonify({'error': 'No workers available', 'success': 'false'})
@@ -218,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_human():
domain = request.args.get('domain')
if domain == None:
return "<h1>Invalid domain</h1>"
# Check if domain exists
if not site_exists(domain):
return "<h1>Domain does not exist</h1>"
# Get worker
worker = site_worker(domain)
if worker == None:
return "<h1>Domain does not exist</h1>"
# 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 "<h1>Domain: " + domain + "</h1><br><p>IP: " + publicIP + "</p><br><p>TLSA: " + tlsa + "</p><br><p>Make sure to add the TLSA record to `_443._tcp." + domain + "` or `*." + domain + "`</p>"
else:
return "<h1>Domain: " + domain + "</h1><br><p>IP: " + publicIP + "</p><br><p>TLSA: none</p><br><p>No TLSA record found</p>"
@app.route('/tlsa', methods=['GET'])
def tlsa():
domain = request.args.get('domain')
@@ -382,6 +426,364 @@ 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 = "<h1>Welcome</h1><br>"
html += "<h2>Create a site</h2>"
html += "<form action='/add-site' method='POST'>"
html += "<p>Domain: <input type='text' name='domain'></p>"
html += "<p>Licence key: <input type='text' name='licence'></p>"
html += "<input type='submit' value='Create site'>"
html += "</form>"
html += "<br><h2>Stats</h2><br>"
html += "<h2>Workers</h2>"
html += "<p>Number of workers: " + str(len(workers)) + "</p>"
html += "<p>Workers:</p>"
html += "<ul>"
for worker in workers:
html += "<li>Name: " + worker.split(':')[0] + " | IP " + worker.split(':')[2].strip('\n') + "</li>"
html += "</ul>"
html += "<h2>Sites</h2>"
html += "<p>Number of sites: " + str(len(sites)) + "</p>"
html += "<p>Sites:</p>"
html += "<ul>"
for site in sites:
html += "<li>Domain: " + site.split(':')[0] + " | Worker: " + site.split(':')[1].strip('\n') + "</li>"
html += "</ul>"
html += "<h2>Licences</h2>"
html += "<p>Number of licences: " + str(len(licences)) + "</p>"
html += "<h2><a href='/admin'>Admin</a></h2>"
return html
# Admin page
@app.route('/admin')
def admin():
# Check if logged in
login_key = request.cookies.get('login_key')
if login_key == None:
return "<h1>Admin</h1><br><form action='/login' method='POST'><input type='password' name='password'><input type='submit' value='Login'></form>"
if login_key not in logins:
return "<h1>Admin</h1><br><form action='/login' method='POST'><input type='password' name='password'><input type='submit' value='Login'></form>"
# 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 = "<h1>Admin</h1><br>"
html += "<h2>Licences</h2>"
html += "<p>Number of licences: " + str(len(licences)) + "</p>"
html += "<p>Licences:</p>"
html += "<ul>"
for licence in licences:
html += "<li>" + licence.strip('\n') + "</li>"
html += "</ul>"
html += "<h2>API</h2>"
html += "<p>API key: " + os.getenv('LICENCE_KEY') + "</p>"
html += "<p>Worker key: " + os.getenv('WORKER_KEY') + "</p>"
html += "<h2>Stripe</h2>"
# Check if stripe is enabled
if os.getenv('STRIPE_SECRET') == None:
html += "<p>Stripe is not enabled</p>"
else:
html += "<p>Stripe is enabled</p>"
html += "<br><br><h2>Workers</h2>"
workers = []
try:
workers_file = open('/data/workers.txt', 'r')
workers = workers_file.readlines()
workers_file.close()
except FileNotFoundError:
pass
for worker in workers:
if not worker.__contains__(':'):
continue
html += "<p>Name: " + worker.split(':')[0] + " | Public IP " + worker.split(':')[2].strip('\n') + " | Private IP " + worker.split(':')[1]
# Check worker status
online=True
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 += "</p>"
html += "<h2>Sites</h2>"
sites = []
try:
sites_file = open('/data/sites.txt', 'r')
sites = sites_file.readlines()
sites_file.close()
except FileNotFoundError:
pass
for site in sites:
if not site.__contains__(':'):
continue
domain = site.split(':')[0]
html += "<p>Domain: <a href='https://"+ domain + "'>" + domain + "</a> | Worker: " + site.split(':')[1].strip('\n') + " | <a href='/info?domain=" + domain + "'>Info</a></p>"
html += "<br><br>"
# Form to add worker
html += "<h2>Add worker</h2>"
html += "<form action='/new-worker' method='POST'>"
html += "<p>Name: <input type='text' name='name'></p>"
html += "<p>Public IP: <input type='text' name='ip'></p>"
html += "<p>Private IP: <input type='text' name='priv'></p>"
html += "<input type='submit' value='Add worker'>"
html += "</form>"
html += "<br><h2><a href='/licence'>Add Licence</a></h2><br>"
# Form to add site
html += "<h2>Add site</h2>"
html += "<form action='/add-site' method='POST'>"
html += "<p>Domain: <input type='text' name='domain'></p>"
html += "<input type='submit' value='Add site'>"
html += "</form>"
html += "<br><a href='/logout'>Logout</a></h2>"
return html
@app.route('/add-site', methods=['POST'])
def addsite():
# 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.form['domain']
if domain == None:
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'})
# 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)
html = "<h1>Site creating...</h1><br>"
html += "<p>Domain: " + domain + "</p>"
html += "<p>Worker: " + worker.split(':')[0] + "</p>"
html += "<p>Worker IP: " + worker.split(':')[1].strip('\n') + "</p>"
html += "<p><a href='/info?domain=" + domain + "'>Check status</a></p>"
return html
@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 "<h1>Licence key</h1><br><p>" + licence_key + "</p><br><a href='/admin'>Back</a>"
@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
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']
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 "<h1>Failed login</h1><br><form action='/login' method='POST'><input type='password' name='password'><input type='submit' value='Login'></form>"
# Start the server
if __name__ == '__main__':
app.run(debug=False, port=5000, host='0.0.0.0')