2023-08-25 16:29:25 +10:00
from flask import Flask , make_response , redirect , request , jsonify
2023-08-16 13:47:49 +10:00
import dotenv
import os
2023-08-16 16:59:57 +10:00
import requests
2023-08-24 16:53:39 +10:00
import stripe # For stripe payments
import smtplib , ssl # For sending emails
2023-08-16 13:47:49 +10:00
dotenv . load_dotenv ( )
app = Flask ( __name__ )
2023-08-25 16:36:53 +10:00
logins = [ ]
2023-08-25 16:29:25 +10:00
2023-08-16 13:47:49 +10:00
# 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 ' )
2023-08-25 16:29:25 +10:00
if api_key != os . getenv ( ' LICENCE_KEY ' ) :
2023-08-16 13:47:49 +10:00
return jsonify ( { ' error ' : ' Invalid API key ' , ' success ' : ' false ' } )
# Generate licence key
licence_key = os . urandom ( 16 ) . hex ( )
# Add license key to file
2023-08-16 17:22:54 +10:00
key_file = open ( ' /data/licence_key.txt ' , ' a ' )
2023-08-16 13:47:49 +10:00
key_file . write ( licence_key + ' \n ' )
key_file . close ( )
return jsonify ( { ' success ' : " true " , ' licence_key ' : licence_key } )
@app.route ( ' /new-site ' , methods = [ ' POST ' ] )
def new_site ( ) :
domain = request . args . get ( ' domain ' )
# Get API header
api_key = request . headers . get ( ' key ' )
# Verify both API key and domain exist
2023-08-24 14:03:29 +10:00
if api_key == None :
2023-08-25 14:20:58 +10:00
return jsonify ( { ' error ' : ' No licence provided ' , ' success ' : ' false ' } )
2023-08-24 14:03:29 +10:00
if domain == None :
return jsonify ( { ' error ' : ' Missing domain ' , ' success ' : ' false ' } )
2023-08-16 13:47:49 +10:00
# Check if API key is a valid site key
2023-08-25 14:13:22 +10:00
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 ' } )
2023-08-16 13:47:49 +10:00
# Check if domain already exists
if site_exists ( domain ) :
return jsonify ( { ' error ' : ' Domain already exists ' , ' success ' : ' false ' } )
2023-08-16 16:59:57 +10:00
# 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 :
2023-08-16 17:22:54 +10:00
worker_file = open ( ' /data/workers.txt ' , ' r ' )
2023-08-16 16:59:57 +10:00
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 :
2023-08-24 14:09:25 +10:00
if not line . __contains__ ( ' : ' ) :
continue
2023-08-16 16:59:57 +10:00
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 ' } )
2023-08-16 13:47:49 +10:00
# Add domain to file
2023-08-16 17:22:54 +10:00
sites_file = open ( ' /data/sites.txt ' , ' a ' )
2023-08-16 16:59:57 +10:00
sites_file . write ( domain + ' : ' + worker . split ( ' : ' ) [ 0 ] + ' \n ' )
2023-08-16 13:47:49 +10:00
sites_file . close ( )
# Use key
2023-08-16 17:22:54 +10:00
key_file = open ( ' /data/licence_key.txt ' , ' r ' )
2023-08-16 13:47:49 +10:00
lines = key_file . readlines ( )
key_file . close ( )
2023-08-16 17:22:54 +10:00
key_file = open ( ' /data/licence_key.txt ' , ' w ' )
2023-08-16 13:47:49 +10:00
for line in lines :
if line . strip ( " \n " ) != api_key :
key_file . write ( line )
key_file . close ( )
2023-08-16 16:59:57 +10:00
# Send worker request
requests . post ( " http:// " + worker . split ( ' : ' ) [ 1 ] . strip ( ' \n ' ) + " :5000/new-site?domain= " + domain )
2023-08-16 13:47:49 +10:00
return jsonify ( { ' success ' : ' true ' , ' domain ' : domain , ' status ' : " creating " } )
# Add worker
@app.route ( ' /add-worker ' , methods = [ ' POST ' ] )
def add_worker ( ) :
worker = request . args . get ( ' worker ' )
2023-08-16 16:59:57 +10:00
worker_IP = request . args . get ( ' ip ' )
2023-08-25 12:25:23 +10:00
worker_PRIV = request . args . get ( ' priv ' )
2023-08-16 13:47:49 +10:00
# Get API header
api_key = request . headers . get ( ' key ' )
2023-08-25 12:25:23 +10:00
if api_key == None or worker == None or worker_IP == None or worker_PRIV == None :
2023-08-16 16:59:57 +10:00
return jsonify ( { ' error ' : ' Invalid API key or worker info ' , ' success ' : ' false ' } )
2023-08-16 13:47:49 +10:00
if api_key != os . getenv ( ' WORKER_KEY ' ) :
return jsonify ( { ' error ' : ' Invalid API key ' , ' success ' : ' false ' } )
# Check worker file
try :
2023-08-16 17:22:54 +10:00
workers_file = open ( ' /data/workers.txt ' , ' r ' )
2023-08-16 13:47:49 +10:00
except FileNotFoundError :
2023-08-16 17:22:54 +10:00
workers_file = open ( ' /data/workers.txt ' , ' w ' )
2023-08-16 13:47:49 +10:00
workers_file . close ( )
2023-08-16 17:22:54 +10:00
workers_file = open ( ' /data/workers.txt ' , ' r ' )
2023-08-16 13:47:49 +10:00
# 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
2023-08-16 17:22:54 +10:00
workers_file = open ( ' /data/workers.txt ' , ' a ' )
2023-08-25 12:25:23 +10:00
workers_file . write ( worker + " : " + worker_PRIV + " : " + worker_IP + ' \n ' )
2023-08-16 13:47:49 +10:00
workers_file . close ( )
2023-08-16 16:59:57 +10:00
online = True
2023-08-25 12:25:23 +10:00
resp = requests . get ( " http:// " + worker_PRIV + " :5000/ping " , timeout = 2 )
2023-08-16 16:59:57 +10:00
if ( resp . status_code != 200 ) :
online = False
return jsonify ( { ' success ' : ' true ' , ' worker ' : worker , ' online ' : online } )
2023-08-16 13:47:49 +10:00
2023-08-17 12:45:36 +10:00
@app.route ( ' /list-workers ' , methods = [ ' GET ' ] )
def list_workers ( ) :
# Get API header
api_key = request . headers . get ( ' key ' )
if api_key == None :
return jsonify ( { ' error ' : ' Invalid API key ' , ' success ' : ' false ' } )
if api_key != os . getenv ( ' WORKER_KEY ' ) :
return jsonify ( { ' error ' : ' Invalid API key ' , ' success ' : ' false ' } )
# 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 ' )
workers = workers_file . readlines ( )
workers_file . close ( )
2023-08-24 11:55:46 +10:00
# Check if there are any workers (by seeing if there are any :)
2023-08-24 11:52:13 +10:00
if len ( workers ) == 0 :
return jsonify ( { ' error ' : ' No workers available ' , ' success ' : ' false ' } )
2023-08-17 12:45:36 +10:00
worker_list = [ ]
for worker in workers :
# Check worker status
2023-08-24 12:03:20 +10:00
if not worker . __contains__ ( ' : ' ) :
2023-08-25 11:42:31 +10:00
continue
2023-08-24 12:03:20 +10:00
2023-08-17 12:45:36 +10:00
online = True
2023-08-25 17:31:32 +10:00
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 ' } )
2023-08-17 12:45:36 +10:00
continue
2023-08-25 17:31:32 +10:00
2023-08-25 11:42:31 +10:00
if len ( worker_list ) == 0 :
return jsonify ( { ' error ' : ' No workers available ' , ' success ' : ' false ' } )
2023-08-17 12:45:36 +10:00
return jsonify ( { ' success ' : ' true ' , ' workers ' : worker_list } )
2023-08-24 14:19:03 +10:00
@app.route ( ' /site-info ' , methods = [ ' GET ' ] )
def site_status ( ) :
domain = request . args . get ( ' domain ' )
if domain == None :
return jsonify ( { ' error ' : ' Invalid domain ' , ' success ' : ' false ' } )
# Check if domain exists
if not site_exists ( domain ) :
return jsonify ( { ' error ' : ' Domain does not exist ' , ' success ' : ' false ' } )
# Get worker
worker = site_worker ( domain )
if worker == None :
return jsonify ( { ' error ' : ' Domain does not exist ' , ' success ' : ' false ' } )
# Get worker ip
2023-08-25 12:25:23 +10:00
ip = workerIP_PRIV ( worker )
2023-08-24 14:19:03 +10:00
# Get TLSA record
resp = requests . get ( " http:// " + ip + " :5000/tlsa?domain= " + domain , timeout = 2 )
json = resp . json ( )
2023-08-25 12:25:23 +10:00
publicIP = workerIP ( worker )
2023-08-24 14:19:03 +10:00
if " tlsa " in json :
tlsa = json [ ' tlsa ' ]
2023-08-25 12:25:23 +10:00
return jsonify ( { ' success ' : ' true ' , ' domain ' : domain , ' ip ' : publicIP , ' tlsa ' : tlsa } )
2023-08-24 14:19:03 +10:00
else :
2023-08-25 12:25:23 +10:00
return jsonify ( { ' success ' : ' false ' , ' domain ' : domain , ' ip ' : publicIP , ' tlsa ' : ' none ' , ' error ' : ' No TLSA record found ' } )
2023-08-24 14:19:03 +10:00
2023-08-25 18:13:36 +10:00
@app.route ( ' /info ' )
2023-08-25 18:17:12 +10:00
def site_status_human ( ) :
2023-08-25 18:13:36 +10:00
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> "
2023-08-17 13:27:57 +10:00
@app.route ( ' /tlsa ' , methods = [ ' GET ' ] )
def tlsa ( ) :
domain = request . args . get ( ' domain ' )
if domain == None :
return jsonify ( { ' error ' : ' Invalid domain ' , ' success ' : ' false ' } )
# Check if domain exists
if not site_exists ( domain ) :
return jsonify ( { ' error ' : ' Domain does not exist ' , ' success ' : ' false ' } )
# Get worker
worker = site_worker ( domain )
if worker == None :
return jsonify ( { ' error ' : ' Domain does not exist ' , ' success ' : ' false ' } )
2023-08-17 13:43:15 +10:00
# Get worker ip
2023-08-25 12:25:23 +10:00
ip = workerIP_PRIV ( worker )
2023-08-17 13:43:15 +10:00
2023-08-17 13:27:57 +10:00
# Get TLSA record
2023-08-17 13:43:15 +10:00
resp = requests . get ( " http:// " + ip + " :5000/tlsa?domain= " + domain , timeout = 2 )
2023-08-17 13:27:57 +10:00
return resp . json ( )
2023-08-16 13:47:49 +10:00
2023-08-24 16:13:24 +10:00
2023-08-24 16:17:27 +10:00
@app.route ( ' /stripe ' , methods = [ ' POST ' ] )
2023-08-24 16:41:38 +10:00
def stripeapi ( ) :
payload = request . data
stripe . api_key = os . getenv ( ' STRIPE_SECRET ' )
endpoint_secret = os . getenv ( ' STRIPE_ENDPOINT_SECRET ' )
2023-08-24 16:44:17 +10:00
sig_header = request . headers . get ( ' Stripe-Signature ' )
2023-08-24 16:41:38 +10:00
events = None
try :
event = stripe . Webhook . construct_event (
payload , sig_header , endpoint_secret
)
except ValueError as e :
# Invalid payload
return jsonify ( { ' success ' : ' false ' } )
except stripe . error . SignatureVerificationError as e :
return jsonify ( { ' success ' : ' false ' } )
2023-08-24 17:18:24 +10:00
2023-08-24 16:41:38 +10:00
if event . type == ' payment_intent.succeeded ' :
2023-08-24 16:53:39 +10:00
payment_intent = event . data . object
# Get email
email = payment_intent [ ' receipt_email ' ]
# Create licence key
licence_key = os . urandom ( 16 ) . hex ( )
# Add licence key to file
key_file = open ( ' /data/licence_key.txt ' , ' a ' )
key_file . write ( licence_key + ' \n ' )
key_file . close ( )
# Send email
host = os . getenv ( ' SMTP_HOST ' )
port = os . getenv ( ' SMTP_PORT ' )
user = os . getenv ( ' SMTP_USER ' )
password = os . getenv ( ' SMTP_PASS ' )
from_email = os . getenv ( ' SMTP_FROM ' )
if from_email == None :
2023-08-24 17:18:24 +10:00
from_email = " Hosting < " + user + " > "
2023-08-24 16:53:39 +10:00
context = ssl . create_default_context ( )
with smtplib . SMTP_SSL ( host , port , context = context ) as server :
server . login ( user , password )
2023-08-24 17:18:24 +10:00
message = " From: " + from_email + " \n To: " + email + \
2023-08-24 17:11:42 +10:00
" \n Subject: Your Licence key \n \n Hello, \n \n " \
+ " This email contains your licence key for your new wordpress site. \n " \
+ " You can redeem this key via the discord bot or api. \n \n " \
+ " Your licence key is: " + licence_key + " \n Thanks, \n HNSHosting "
2023-08-24 16:57:05 +10:00
server . sendmail ( from_email , email , message )
2023-08-24 16:53:39 +10:00
2023-08-24 17:18:24 +10:00
print ( ' Licence sent via email for stripe payment ' , flush = True )
2023-08-24 16:41:38 +10:00
else :
print ( ' Unhandled event type {} ' . format ( event . type ) )
2023-08-24 16:13:24 +10:00
return jsonify ( { ' success ' : ' true ' } )
2023-08-16 13:47:49 +10:00
def get_sites_count ( ) :
# If file doesn't exist, create it
try :
2023-08-16 17:22:54 +10:00
sites_file = open ( ' /data/sites.txt ' , ' r ' )
2023-08-16 13:47:49 +10:00
except FileNotFoundError :
2023-08-16 17:22:54 +10:00
sites_file = open ( ' /data/sites.txt ' , ' w ' )
2023-08-16 13:47:49 +10:00
sites_file . close ( )
2023-08-16 17:22:54 +10:00
sites_file = open ( ' /data/sites.txt ' , ' r ' )
2023-08-16 13:47:49 +10:00
num = len ( sites_file . readlines ( ) )
sites_file . close ( )
# Return number of lines in file
return num
def site_exists ( domain ) :
# If file doesn't exist, create it
try :
2023-08-16 17:22:54 +10:00
sites_file = open ( ' /data/sites.txt ' , ' r ' )
2023-08-16 13:47:49 +10:00
except FileNotFoundError :
2023-08-16 17:22:54 +10:00
sites_file = open ( ' /data/sites.txt ' , ' w ' )
2023-08-16 13:47:49 +10:00
sites_file . close ( )
2023-08-16 17:22:54 +10:00
sites_file = open ( ' /data/sites.txt ' , ' r ' )
2023-08-16 13:47:49 +10:00
contains_site = False
2023-08-16 16:59:57 +10:00
for line in sites_file . readlines ( ) :
if domain == line . split ( ' : ' ) [ 0 ] :
contains_site = True
break
2023-08-16 13:47:49 +10:00
sites_file . close ( )
return contains_site
2023-08-16 16:59:57 +10:00
def site_worker ( domain ) :
# If file doesn't exist, create it
try :
2023-08-16 17:22:54 +10:00
sites_file = open ( ' /data/sites.txt ' , ' r ' )
2023-08-16 16:59:57 +10:00
except FileNotFoundError :
2023-08-16 17:22:54 +10:00
sites_file = open ( ' /data/sites.txt ' , ' w ' )
2023-08-16 16:59:57 +10:00
sites_file . close ( )
2023-08-16 17:22:54 +10:00
sites_file = open ( ' /data/sites.txt ' , ' r ' )
2023-08-16 16:59:57 +10:00
worker = None
for line in sites_file . readlines ( ) :
if domain == line . split ( ' : ' ) [ 0 ] :
worker = line . split ( ' : ' ) [ 1 ] . strip ( ' \n ' )
break
sites_file . close ( )
return worker
2023-08-17 13:43:15 +10:00
2023-08-25 12:25:23 +10:00
def workerIP_PRIV ( worker ) :
# If file doesn't exist, create it
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 ' )
ip = None
for line in workers_file . readlines ( ) :
if worker == line . split ( ' : ' ) [ 0 ] :
ip = line . split ( ' : ' ) [ 2 ] . strip ( ' \n ' )
break
workers_file . close ( )
return ip
2023-08-17 13:43:15 +10:00
def workerIP ( worker ) :
# If file doesn't exist, create it
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 ' )
ip = None
for line in workers_file . readlines ( ) :
if worker == line . split ( ' : ' ) [ 0 ] :
ip = line . split ( ' : ' ) [ 1 ] . strip ( ' \n ' )
break
workers_file . close ( )
return ip
2023-08-16 13:47:49 +10:00
2023-08-25 15:51:47 +10:00
# 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
2023-08-25 17:57:51 +10:00
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> "
2023-08-25 22:57:11 +10:00
html + = " <h3>Workers</h3> "
2023-08-25 15:51:47 +10:00
html + = " <ul> "
for worker in workers :
2023-08-25 22:47:19 +10:00
html + = " <li>Name: " + worker . split ( ' : ' ) [ 0 ] + " | IP: " + worker . split ( ' : ' ) [ 2 ] . strip ( ' \n ' ) + " </li> "
2023-08-25 15:51:47 +10:00
html + = " </ul> "
2023-08-25 22:57:11 +10:00
html + = " <h3>Sites</h3> "
html + = " <p>Total sites: " + str ( len ( sites ) ) + " </p> "
2023-08-25 15:51:47 +10:00
html + = " <ul> "
for site in sites :
2023-08-25 22:53:35 +10:00
html + = " <li>Domain: <a href= \" https:// " + site . split ( ' : ' ) [ 0 ] + " \" target= \" _blank \" > " + site . split ( ' : ' ) [ 0 ] + " </a> | Worker: " + site . split ( ' : ' ) [ 1 ] . strip ( ' \n ' ) + " </li> "
2023-08-25 15:51:47 +10:00
html + = " </ul> "
2023-08-25 22:57:11 +10:00
html + = " <h3>Number of unclaimed licences: " + str ( len ( licences ) ) + " </h3> "
2023-08-25 16:07:25 +10:00
2023-08-25 17:07:41 +10:00
html + = " <h2><a href= ' /admin ' >Admin</a></h2> "
2023-08-25 16:07:25 +10:00
return html
2023-08-25 16:29:25 +10:00
# Admin page
2023-08-25 16:36:53 +10:00
@app.route ( ' /admin ' )
2023-08-25 16:29:25 +10:00
def admin ( ) :
# Check if logged in
2023-08-25 16:32:50 +10:00
login_key = request . cookies . get ( ' login_key ' )
2023-08-25 16:29:25 +10:00
2023-08-25 16:36:53 +10:00
if login_key == None :
2023-08-25 16:51:19 +10:00
return " <h1>Admin</h1><br><form action= ' /login ' method= ' POST ' ><input type= ' password ' name= ' password ' ><input type= ' submit ' value= ' Login ' ></form> "
2023-08-25 16:36:53 +10:00
if login_key not in logins :
2023-08-25 16:51:19 +10:00
return " <h1>Admin</h1><br><form action= ' /login ' method= ' POST ' ><input type= ' password ' name= ' password ' ><input type= ' submit ' value= ' Login ' ></form> "
2023-08-25 16:36:53 +10:00
2023-08-25 17:07:41 +10:00
# 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> "
2023-08-25 17:13:34 +10:00
workers = [ ]
try :
workers_file = open ( ' /data/workers.txt ' , ' r ' )
workers = workers_file . readlines ( )
workers_file . close ( )
except FileNotFoundError :
pass
for worker in workers :
2023-08-25 17:18:37 +10:00
if not worker . __contains__ ( ' : ' ) :
continue
2023-08-25 17:13:34 +10:00
html + = " <p>Name: " + worker . split ( ' : ' ) [ 0 ] + " | Public IP " + worker . split ( ' : ' ) [ 2 ] . strip ( ' \n ' ) + " | Private IP " + worker . split ( ' : ' ) [ 1 ]
# Check worker status
online = True
2023-08-25 17:31:32 +10:00
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 :
2023-08-25 17:13:34 +10:00
html + = " | Status: Offline "
2023-08-25 17:31:32 +10:00
2023-08-25 17:13:34 +10:00
html + = " </p> "
2023-08-25 17:07:41 +10:00
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 :
2023-08-25 17:18:37 +10:00
if not site . __contains__ ( ' : ' ) :
continue
2023-08-25 18:22:20 +10:00
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> "
2023-08-25 17:07:41 +10:00
2023-08-25 17:52:17 +10:00
html + = " <br><br> "
2023-08-25 17:18:37 +10:00
# 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> "
2023-08-25 17:45:46 +10:00
2023-08-25 17:52:17 +10:00
html + = " <br><h2><a href= ' /licence ' >Add Licence</a></h2><br> "
2023-08-25 17:45:46 +10:00
# 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> "
2023-08-25 17:18:37 +10:00
2023-08-25 17:45:46 +10:00
html + = " <br><a href= ' /logout ' >Logout</a></h2> "
2023-08-25 17:07:41 +10:00
return html
2023-08-25 16:36:53 +10:00
2023-08-25 17:45:46 +10:00
2023-08-25 17:52:17 +10:00
@app.route ( ' /add-site ' , methods = [ ' POST ' ] )
2023-08-25 17:48:42 +10:00
def addsite ( ) :
2023-08-25 17:57:51 +10:00
# 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 ( )
2023-08-25 17:45:46 +10:00
# Get domain
2023-08-25 18:01:49 +10:00
domain = request . form [ ' domain ' ]
2023-08-25 17:45:46 +10:00
if domain == None :
2023-08-25 18:01:49 +10:00
return jsonify ( { ' error ' : ' No domain sent ' , ' success ' : ' false ' } )
2023-08-25 17:45:46 +10:00
# 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 )
2023-08-25 17:57:51 +10:00
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> "
2023-08-25 18:13:36 +10:00
html + = " <p><a href= ' /info?domain= " + domain + " ' >Check status</a></p> "
2023-08-25 17:57:51 +10:00
return html
2023-08-25 17:45:46 +10:00
@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 ' )
2023-08-25 17:48:42 +10:00
2023-08-25 17:45:46 +10:00
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> "
2023-08-25 17:18:37 +10:00
@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 ' )
2023-08-25 16:36:53 +10:00
2023-08-25 17:18:37 +10:00
# 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 ' )
2023-08-25 16:36:53 +10:00
@app.route ( ' /login ' , methods = [ ' POST ' ] )
2023-08-25 16:38:34 +10:00
def login ( ) :
2023-08-25 16:47:22 +10:00
# Handle login
print ( ' Login attempt ' , flush = True )
2023-08-25 17:07:41 +10:00
# Check if form contains password
if ' password ' not in request . form :
print ( ' Login failed ' , flush = True )
return redirect ( ' /failed-login ' )
2023-08-25 16:47:22 +10:00
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 ( ) :
2023-08-25 16:51:19 +10:00
return " <h1>Failed login</h1><br><form action= ' /login ' method= ' POST ' ><input type= ' password ' name= ' password ' ><input type= ' submit ' value= ' Login ' ></form> "
2023-08-25 16:29:25 +10:00
2023-08-25 15:51:47 +10:00
2023-08-16 13:47:49 +10:00
# Start the server
if __name__ == ' __main__ ' :
2023-08-16 17:05:15 +10:00
app . run ( debug = False , port = 5000 , host = ' 0.0.0.0 ' )