Merge pull request #1361 from frennkie/ip2tor-blitz-error

ip2tor: add blitz error + refactor
This commit is contained in:
Christian Rotzoll 2020-07-19 16:50:52 +02:00 committed by GitHub
commit fd4647a605
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 474 additions and 414 deletions

View file

@ -5,6 +5,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
## [0.3.0] - 2020-07-19
### Added
- add BlitzError Class
## [0.2.0] - 2020-05-23 ## [0.2.0] - 2020-05-23
### Added ### Added
- add write() to BlitzPy config Classes - add write() to BlitzPy config Classes

View file

@ -1,8 +1,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from .config import RaspiBlitzConfig, RaspiBlitzInfo from .config import RaspiBlitzConfig, RaspiBlitzInfo
from .exceptions import BlitzError
__all__ = [
'RaspiBlitzConfig', __all__ = [
'RaspiBlitzInfo', 'RaspiBlitzConfig',
] 'RaspiBlitzInfo',
'BlitzError'
]

View file

@ -0,0 +1,16 @@
from datetime import datetime
TS_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
class BlitzError(Exception):
def __init__(self, short: str, details: dict = None, org: Exception = None):
self.short: str = str(short)
if details:
self.details: dict = details
self.details.update({'timestamp': datetime.utcnow().strftime(TS_FORMAT)})
else:
self.details = dict()
self.details['timestamp'] = datetime.utcnow().strftime(TS_FORMAT)
self.org: Exception = org

View file

@ -4,5 +4,5 @@
# 3) we can import it into your module module # 3) we can import it into your module module
""" """
__version_info__ = ('0', '2', '0') __version_info__ = ('0', '3', '0')
__version__ = '.'.join(__version_info__) __version__ = '.'.join(__version_info__)

Binary file not shown.

Binary file not shown.

View file

@ -1,6 +1,5 @@
#!/usr/bin/python3 #!/usr/bin/python3
import ast
import codecs import codecs
import json import json
import math import math
@ -13,7 +12,7 @@ from pathlib import Path
import grpc import grpc
import requests import requests
import toml import toml
from blitzpy import RaspiBlitzConfig from blitzpy import RaspiBlitzConfig, BlitzError
from lndlibs import rpc_pb2 as lnrpc from lndlibs import rpc_pb2 as lnrpc
from lndlibs import rpc_pb2_grpc as rpcstub from lndlibs import rpc_pb2_grpc as rpcstub
@ -35,6 +34,12 @@ if len(sys.argv) <= 1 or sys.argv[1] == "-h" or sys.argv[1] == "help":
print("# blitz.subscriptions.ip2tor.py ip-by-tor onionaddress") print("# blitz.subscriptions.ip2tor.py ip-by-tor onionaddress")
sys.exit(1) sys.exit(1)
# constants for standard services
SERVICE_LND_REST_API = "LND-REST-API"
SERVICE_LND_GRPC_API = "LND-GRPC-API"
SERVICE_LNBITS = "LNBITS"
SERVICE_BTCPAY = "BTCPAY"
##################### #####################
# BASIC SETTINGS # BASIC SETTINGS
##################### #####################
@ -70,17 +75,6 @@ else:
is_testnet = False is_testnet = False
#####################
# HELPER CLASSES
#####################
class BlitzError(Exception):
def __init__(self, errorShort, errorLong="", errorException=None):
self.errorShort = str(errorShort)
self.errorLong = str(errorLong)
self.errorException = errorException
##################### #####################
# HELPER FUNCTIONS # HELPER FUNCTIONS
##################### #####################
@ -91,9 +85,9 @@ def eprint(*args, **kwargs):
def handleException(e): def handleException(e):
if isinstance(e, BlitzError): if isinstance(e, BlitzError):
eprint(e.errorLong) eprint(e.details)
eprint(e.errorException) eprint(e.org)
print("error='{0}'".format(e.errorShort)) print("error='{0}'".format(e.short))
else: else:
eprint(e) eprint(e)
print("error='{0}'".format(str(e))) print("error='{0}'".format(str(e)))
@ -150,17 +144,17 @@ def apiGetHosts(session, shopurl):
try: try:
response = session.get(url) response = session.get(url)
except Exception as e: except Exception as e:
raise BlitzError("failed HTTP request", url, e) raise BlitzError("failed HTTP request", {'url': url}, e)
if response.status_code != 200: if response.status_code != 200:
raise BlitzError("failed HTTP code", response.status_code, ) raise BlitzError("failed HTTP code", {'status_code': response.status_code})
# parse & validate data # parse & validate data
try: try:
jData = json.loads(response.content) jData = json.loads(response.content)
except Exception as e: except Exception as e:
raise BlitzError("failed JSON parsing", response.content, e) raise BlitzError("failed JSON parsing", {'content': response.content}, e)
if not isinstance(jData, list): if not isinstance(jData, list):
raise BlitzError("hosts not list", response.content) raise BlitzError("hosts not list", {'content': response.content})
for idx, hostEntry in enumerate(jData): for idx, hostEntry in enumerate(jData):
try: try:
# ignore if not offering tor bridge # ignore if not offering tor bridge
@ -188,7 +182,7 @@ def apiGetHosts(session, shopurl):
# shorten names to 20 chars max # shorten names to 20 chars max
hostEntry['name'] = hostEntry['name'][:20] hostEntry['name'] = hostEntry['name'][:20]
except Exception as e: except Exception as e:
raise BlitzError("failed host entry pasring", str(hostEntry), e) raise BlitzError("failed host entry pasring", hostEntry, e)
hosts.append(hostEntry) hosts.append(hostEntry)
@ -211,11 +205,11 @@ def apiPlaceOrderNew(session, shopurl, hostid, toraddressWithPort):
try: try:
response = session.post(url, data=postData) response = session.post(url, data=postData)
except Exception as e: except Exception as e:
raise BlitzError("failed HTTP request", url, e) raise BlitzError("failed HTTP request", {'url': url}, e)
if response.status_code == 420: if response.status_code == 420:
raise BlitzError("forwarding this address was rejected", response.status_code) raise BlitzError("forwarding this address was rejected", {'status_code': response.status_code})
if response.status_code != 201: if response.status_code != 201:
raise BlitzError("failed HTTP code", response.status_code) raise BlitzError("failed HTTP code", {'status_code': response.status_code})
# parse & validate data # parse & validate data
try: try:
@ -224,7 +218,7 @@ def apiPlaceOrderNew(session, shopurl, hostid, toraddressWithPort):
print("error='MISSING ID'") print("error='MISSING ID'")
return return
except Exception as e: except Exception as e:
raise BlitzError("failed JSON parsing", response.status_code, e) raise BlitzError("failed JSON parsing", {'status_code': response.status_code}, e)
return jData['id'] return jData['id']
@ -236,11 +230,11 @@ def apiPlaceOrderExtension(session, shopurl, bridgeid):
try: try:
response = session.post(url) response = session.post(url)
except Exception as e: except Exception as e:
raise BlitzError("failed HTTP request", url, e) raise BlitzError("failed HTTP request", {'url': url}, e)
if response.status_code == 420: if response.status_code == 420:
raise BlitzError("forwarding this address was rejected", response.status_code) raise BlitzError("forwarding this address was rejected", {'status_code': response.status_code})
if response.status_code != 200 and response.status_code != 201: if response.status_code != 200 and response.status_code != 201:
raise BlitzError("failed HTTP code", response.status_code) raise BlitzError("failed HTTP code", {'status_code': response.status_code})
# parse & validate data # parse & validate data
print("# parse") print("# parse")
@ -250,12 +244,12 @@ def apiPlaceOrderExtension(session, shopurl, bridgeid):
print("error='MISSING ID'") print("error='MISSING ID'")
return return
except Exception as e: except Exception as e:
raise BlitzError("failed JSON parsing", response.content, e) raise BlitzError("failed JSON parsing", {'content': response.content}, e)
return jData['po_id'] return jData['po_id']
def apiGetOrder(session, shopurl, orderid): def apiGetOrder(session, shopurl, orderid) -> dict:
print("# apiGetOrder") print("# apiGetOrder")
# make HTTP request # make HTTP request
@ -263,19 +257,19 @@ def apiGetOrder(session, shopurl, orderid):
try: try:
response = session.get(url) response = session.get(url)
except Exception as e: except Exception as e:
raise BlitzError("failed HTTP request", url, e) raise BlitzError("failed HTTP request", {'url': url}, e)
if response.status_code != 200: if response.status_code != 200:
raise BlitzError("failed HTTP code", response.status_code) raise BlitzError("failed HTTP code", {'status_code': response.status_code})
# parse & validate data # parse & validate data
try: try:
jData = json.loads(response.content) jData = json.loads(response.content)
if len(jData['item_details']) == 0: if len(jData['item_details']) == 0:
raise BlitzError("missing item", response.content) raise BlitzError("missing item", {'content': response.content})
if len(jData['ln_invoices']) > 1: if len(jData['ln_invoices']) > 1:
raise BlitzError("more than one invoice", response.content) raise BlitzError("more than one invoice", {'content': response.content})
except Exception as e: except Exception as e:
raise BlitzError("failed JSON parsing", response.content, e) raise BlitzError("failed JSON parsing", {'content': response.content}, e)
return jData return jData
@ -288,16 +282,16 @@ def apiGetBridgeStatus(session, shopurl, bridgeid):
try: try:
response = session.get(url) response = session.get(url)
except Exception as e: except Exception as e:
raise BlitzError("failed HTTP request", url, e) raise BlitzError("failed HTTP request", {'url': url}, e)
if response.status_code != 200: if response.status_code != 200:
raise BlitzError("failed HTTP code", response.status_code) raise BlitzError("failed HTTP code", {'status_code': response.status_code})
# parse & validate data # parse & validate data
try: try:
jData = json.loads(response.content) jData = json.loads(response.content)
if len(jData['id']) == 0: if len(jData['id']) == 0:
raise BlitzError("missing id", response.content) raise BlitzError("missing id", {'content': response.content})
except Exception as e: except Exception as e:
raise BlitzError("failed JSON parsing", response.content, e) raise BlitzError("failed JSON parsing", {'content': response.content}, e)
return jData return jData
@ -322,10 +316,10 @@ def lndDecodeInvoice(lnInvoiceString):
# validate results # validate results
if response.num_msat <= 0: if response.num_msat <= 0:
raise BlitzError("zero invoice not allowed", lnInvoiceString) raise BlitzError("zero invoice not allowed", {'invoice': lnInvoiceString})
except Exception as e: except Exception as e:
raise BlitzError("failed LND invoice decoding", lnInvoiceString, e) raise BlitzError("failed LND invoice decoding", {'invoice': lnInvoiceString}, e)
return response return response
@ -346,10 +340,10 @@ def lndPayInvoice(lnInvoiceString):
# validate results # validate results
if len(response.payment_error) > 0: if len(response.payment_error) > 0:
raise BlitzError(response.payment_error, lnInvoiceString) raise BlitzError(response.payment_error, {'invoice': lnInvoiceString})
except Exception as e: except Exception as e:
raise BlitzError("payment failed", lnInvoiceString, e) raise BlitzError("payment failed", {'invoice': lnInvoiceString}, e)
return response return response
@ -480,7 +474,7 @@ def shopOrder(shopUrl, hostid, servicename, torTarget, duration, msatsFirst, msa
except Exception as e: except Exception as e:
eprint(e) eprint(e)
raise BlitzError("fail on subscription storage", str(subscription), e) raise BlitzError("fail on subscription storage", subscription, e)
print("# OK - BRIDGE READY: {0}:{1} -> {2}".format(bridge_ip, bridge_port, torTarget)) print("# OK - BRIDGE READY: {0}:{1} -> {2}".format(bridge_ip, bridge_port, torTarget))
return subscription return subscription
@ -578,7 +572,7 @@ def subscriptionExtend(shopUrl, bridgeid, durationAdvertised, msatsNext, bridge_
except Exception as e: except Exception as e:
eprint(e) eprint(e)
raise BlitzError("fail on subscription storage", "", e) raise BlitzError("fail on subscription storage", org=e)
print("# BRIDGE GOT EXTENDED: {0} -> {1}".format(bridge_suspendafter, bridge['suspend_after'])) print("# BRIDGE GOT EXTENDED: {0} -> {1}".format(bridge_suspendafter, bridge['suspend_after']))
@ -664,7 +658,8 @@ Try again later, enter another address or cancel.
choices=choices, title="Available Subscriptions") choices=choices, title="Available Subscriptions")
# if user cancels # if user cancels
if code != d.OK: sys.exit(0) if code != d.OK:
sys.exit(0)
# get data of selected # get data of selected
seletedIndex = int(tag) seletedIndex = int(tag)
@ -712,7 +707,8 @@ More information on the service you can find under:
height=30) height=30)
# if user AGREED break loop and continue with selected host # if user AGREED break loop and continue with selected host
if code == "extra": break if code == "extra":
break
############################ ############################
# PHASE 3: Make Subscription # PHASE 3: Make Subscription
@ -729,16 +725,15 @@ More information on the service you can find under:
exitcode = 0 exitcode = 0
order = ast.literal_eval(be.errorLong) try:
try : message = be.details['message']
message = order['message'] except KeyError:
except Exception as e: message = ""
message = "n/a"
if (be.errorShort == "timeout on waiting for extending bridge" or if (be.short == "timeout on waiting for extending bridge" or
be.errorShort == "fail on subscription storage" or be.short == "fail on subscription storage" or
be.errorShort == "invalid port" or be.short == "invalid port" or
be.errorShort == "timeout bridge not getting ready"): be.short == "timeout bridge not getting ready"):
# error happened after payment # error happened after payment
exitcode = Dialog(dialog="dialog", autowidgetsize=True).msgbox(''' exitcode = Dialog(dialog="dialog", autowidgetsize=True).msgbox('''
@ -748,7 +743,7 @@ Subscription will be ignored.
Error: {0} Error: {0}
Message: {1} Message: {1}
'''.format(be.errorShort, message), title="Error on Subscription", extra_button=True, extra_label="Details") '''.format(be.short, message), title="Error on Subscription", extra_button=True, extra_label="Details")
else: else:
# error happened before payment # error happened before payment
@ -759,7 +754,7 @@ Subscription will be ignored.
Error: {0} Error: {0}
Message: {1} Message: {1}
'''.format(be.errorShort, message), title="Error on Subscription", extra_button=True, extra_label="Details") '''.format(be.short, message), title="Error on Subscription", extra_button=True, extra_label="Details")
# show more details (when user used extra button) # show more details (when user used extra button)
if exitcode == Dialog.EXTRA: if exitcode == Dialog.EXTRA:
@ -767,13 +762,13 @@ Message: {1}
print('###### ERROR DETAIL FOR DEBUG #######') print('###### ERROR DETAIL FOR DEBUG #######')
print("") print("")
print("Error Short:") print("Error Short:")
print(be.errorShort) print(be.short)
print('Shop:') print('Shop:')
print(shopurl) print(shopurl)
print('Bridge:') print('Bridge:')
print(str(host)) print(str(host))
print("Error Detail:") print("Error Detail:")
print(be.errorLong) print(be.details)
print("") print("")
input("Press Enter to continue ...") input("Press Enter to continue ...")
@ -796,7 +791,7 @@ Message: {1}
sys.exit(1) sys.exit(1)
# if LND REST or LND GRPC service ... add bridge IP to TLS # if LND REST or LND GRPC service ... add bridge IP to TLS
if servicename == "LND-REST-API" or servicename == "LND-GRPC-API": if blitzServiceName == SERVICE_LND_REST_API or blitzServiceName == SERVICE_LND_GRPC_API:
os.system("sudo /home/admin/config.scripts/lnd.tlscert.sh ip-add {0}".format(subscription['ip'])) os.system("sudo /home/admin/config.scripts/lnd.tlscert.sh ip-add {0}".format(subscription['ip']))
os.system("sudo /home/admin/config.scripts/lnd.credentials.sh reset tls") os.system("sudo /home/admin/config.scripts/lnd.credentials.sh reset tls")
os.system("sudo /home/admin/config.scripts/lnd.credentials.sh sync") os.system("sudo /home/admin/config.scripts/lnd.credentials.sh sync")
@ -811,7 +806,7 @@ You may want to consider to cancel the subscription later.
# decide if https:// address # decide if https:// address
protocol = "" protocol = ""
if blitzServiceName == "LNBITS": if blitzServiceName == SERVICE_LNBITS:
protocol = "https://" protocol = "https://"
# Give final result feedback to user # Give final result feedback to user
@ -847,13 +842,11 @@ MAIN MENU > Manage Subscriptions > My Subscriptions
# CREATE SSH DIALOG # CREATE SSH DIALOG
# use for ssh shell menu # use for ssh shell menu
############### ###############
def create_ssh_dialog():
if sys.argv[1] == "create-ssh-dialog":
# check parameters # check parameters
try: try:
if len(sys.argv) <= 4: if len(sys.argv) <= 4:
raise BlitzError("incorrect parameters", "") raise BlitzError("incorrect parameters")
except Exception as e: except Exception as e:
handleException(e) handleException(e)
@ -865,17 +858,16 @@ if sys.argv[1] == "create-ssh-dialog":
sys.exit() sys.exit()
############### ###############
# SHOP LIST # SHOP LIST
# call from web interface # call from web interface
############### ###############
def shop_list():
if sys.argv[1] == "shop-list":
# check parameters # check parameters
try: try:
if len(sys.argv) <= 2: if len(sys.argv) <= 2:
raise BlitzError("incorrect parameters", "") raise BlitzError("incorrect parameters")
except Exception as e: except Exception as e:
handleException(e) handleException(e)
@ -891,17 +883,16 @@ if sys.argv[1] == "shop-list":
sys.exit(0) sys.exit(0)
########################## ##########################
# SHOP ORDER # SHOP ORDER
# call from web interface # call from web interface
########################## ##########################
def shop_order():
if sys.argv[1] == "shop-order":
# check parameters # check parameters
try: try:
if len(sys.argv) <= 8: if len(sys.argv) <= 8:
raise BlitzError("incorrect parameters", "") raise BlitzError("incorrect parameters")
except Exception as e: except Exception as e:
handleException(e) handleException(e)
@ -926,13 +917,12 @@ if sys.argv[1] == "shop-order":
except Exception as e: except Exception as e:
handleException(e) handleException(e)
####################### #######################
# SUBSCRIPTIONS LIST # SUBSCRIPTIONS LIST
# call in intervals from background process # call in intervals from background process
####################### #######################
def subscriptions_list():
if sys.argv[1] == "subscriptions-list":
try: try:
if Path(SUBSCRIPTIONS_FILE).is_file(): if Path(SUBSCRIPTIONS_FILE).is_file():
@ -947,15 +937,12 @@ if sys.argv[1] == "subscriptions-list":
except Exception as e: except Exception as e:
handleException(e) handleException(e)
sys.exit(0)
####################### #######################
# SUBSCRIPTIONS RENEW # SUBSCRIPTIONS RENEW
# call in intervals from background process # call in intervals from background process
####################### #######################
def subscriptions_renew():
if sys.argv[1] == "subscriptions-renew":
print("# RUNNING subscriptions-renew") print("# RUNNING subscriptions-renew")
# check parameters # check parameters
@ -1002,16 +989,16 @@ if sys.argv[1] == "subscriptions-renew":
subs = toml.load(SUBSCRIPTIONS_FILE) subs = toml.load(SUBSCRIPTIONS_FILE)
for sub in subs['subscriptions_ip2tor']: for sub in subs['subscriptions_ip2tor']:
if sub['id'] == subscription['id']: if sub['id'] == subscription['id']:
sub['warning'] = "Exception on Renew: {0}".format(be.errorShort) sub['warning'] = "Exception on Renew: {0}".format(be.short)
if be.errorShort == "invoice bigger amount than advertised": if be.short == "invoice bigger amount than advertised":
sub['contract_breached'] = True sub['contract_breached'] = True
sub['active'] = False sub['active'] = False
with open(SUBSCRIPTIONS_FILE, 'w') as writer: with open(SUBSCRIPTIONS_FILE, 'w') as writer:
writer.write(toml.dumps(subs)) writer.write(toml.dumps(subs))
writer.close() writer.close()
break break
print("# BLITZERROR on subscriptions-renew of subscription index {0}: {1}".format(idx, be.errorShort)) print("# BLITZERROR on subscriptions-renew of subscription index {0}: {1}".format(idx, be.short))
print("# {0}".format(be.errorShort)) print("# {0}".format(be.short))
except Exception as e: except Exception as e:
print("# EXCEPTION on subscriptions-renew of subscription index {0}".format(idx)) print("# EXCEPTION on subscriptions-renew of subscription index {0}".format(idx))
@ -1022,18 +1009,17 @@ if sys.argv[1] == "subscriptions-renew":
# output - not needed only for debug logs # output - not needed only for debug logs
print("# DONE subscriptions-renew") print("# DONE subscriptions-renew")
sys.exit(1)
####################### #######################
# SUBSCRIPTION CANCEL # SUBSCRIPTION CANCEL
# call in intervalls from background process # call in intervals from background process
####################### #######################
if sys.argv[1] == "subscription-cancel": def subscription_cancel():
# check parameters # check parameters
try: try:
if len(sys.argv) <= 2: if len(sys.argv) <= 2:
raise BlitzError("incorrect parameters", "") raise BlitzError("incorrect parameters")
except Exception as e: except Exception as e:
handleException(e) handleException(e)
@ -1058,30 +1044,28 @@ if sys.argv[1] == "subscription-cancel":
except Exception as e: except Exception as e:
handleException(e) handleException(e)
sys.exit(0)
####################### #######################
# GET ADDRESS BY SERVICENAME # GET ADDRESS BY SERVICE NAME
# gets called by other scripts to check if service has a ip2tor bridge address # gets called by other scripts to check if service has a ip2tor bridge address
# output is bash key/value style so that it can be imported with source # output is bash key/value style so that it can be imported with source
####################### #######################
if sys.argv[1] == "subscription-by-service": def subscription_by_service():
# check parameters # check parameters
try: try:
if len(sys.argv) <= 2: if len(sys.argv) <= 2:
raise BlitzError("incorrect parameters", "") raise BlitzError("incorrect parameters")
except Exception as e: except Exception as e:
handleException(e) handleException(e)
servicename = sys.argv[2] service_name = sys.argv[2]
try: try:
if os.path.isfile(SUBSCRIPTIONS_FILE): if os.path.isfile(SUBSCRIPTIONS_FILE):
os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE)) os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE))
subs = toml.load(SUBSCRIPTIONS_FILE) subs = toml.load(SUBSCRIPTIONS_FILE)
for idx, sub in enumerate(subs['subscriptions_ip2tor']): for idx, sub in enumerate(subs['subscriptions_ip2tor']):
if sub['active'] and sub['name'] == servicename: if sub['active'] and sub['name'] == service_name:
print("type='{0}'".format(sub['type'])) print("type='{0}'".format(sub['type']))
print("ip='{0}'".format(sub['ip'])) print("ip='{0}'".format(sub['ip']))
print("port='{0}'".format(sub['port'])) print("port='{0}'".format(sub['port']))
@ -1089,24 +1073,22 @@ if sys.argv[1] == "subscription-by-service":
sys.exit(0) sys.exit(0)
print("error='not found'") print("error='not found'")
sys.exit(0)
except Exception as e: except Exception as e:
handleException(e) handleException(e)
sys.exit(1)
sys.exit(1)
####################### #######################
# GET IP BY ONIONADDRESS # GET IP BY ONION ADDRESS
# gets called by other scripts to check if a onion address as a IP2TOR bridge # gets called by other scripts to check if a onion address as a IP2TOR bridge
# output is bash key/value style so that it can be imported with source # output is bash key/value style so that it can be imported with source
####################### #######################
if sys.argv[1] == "ip-by-tor": def ip_by_tor():
# check parameters # check parameters
try: try:
if len(sys.argv) <= 2: if len(sys.argv) <= 2:
raise BlitzError("incorrect parameters", "") raise BlitzError("incorrect parameters")
except Exception as e: except Exception as e:
handleException(e) handleException(e)
@ -1126,12 +1108,41 @@ if sys.argv[1] == "ip-by-tor":
sys.exit(0) sys.exit(0)
print("error='not found'") print("error='not found'")
sys.exit(0)
except Exception as e: except Exception as e:
handleException(e) handleException(e)
sys.exit(1)
sys.exit(1)
# unknown command def main():
print("# unknown command") if sys.argv[1] == "create-ssh-dialog":
create_ssh_dialog()
elif sys.argv[1] == "shop-list":
shop_list()
elif sys.argv[1] == "shop-order":
shop_order()
elif sys.argv[1] == "subscriptions-list":
subscriptions_list()
elif sys.argv[1] == "subscriptions-renew":
subscriptions_renew()
elif sys.argv[1] == "subscription-cancel":
subscription_cancel()
elif sys.argv[1] == "subscription-by-service":
subscription_by_service()
elif sys.argv[1] == "ip-by-tor":
ip_by_tor()
else:
# unknown command
print("# unknown command")
if __name__ == '__main__':
main()

View file

@ -10,7 +10,7 @@ from pathlib import Path
import requests import requests
import toml import toml
from blitzpy import RaspiBlitzConfig from blitzpy import RaspiBlitzConfig,BlitzError
##################### #####################
# SCRIPT INFO # SCRIPT INFO
@ -30,6 +30,12 @@ if len(sys.argv) <= 1 or sys.argv[1] == "-h" or sys.argv[1] == "help":
print("# blitz.subscriptions.ip2tor.py subscription-cancel <id>") print("# blitz.subscriptions.ip2tor.py subscription-cancel <id>")
sys.exit(1) sys.exit(1)
# constants for standard services
SERVICE_LND_REST_API = "LND-REST-API"
SERVICE_LND_GRPC_API = "LND-GRPC-API"
SERVICE_LNBITS = "LNBITS"
SERVICE_BTCPAY = "BTCPAY"
##################### #####################
# BASIC SETTINGS # BASIC SETTINGS
##################### #####################
@ -49,6 +55,7 @@ if cfg.run_behind_tor:
# HELPER CLASSES # HELPER CLASSES
##################### #####################
# ToDo(frennkie) replace this with updated BlitzError from blitzpy
class BlitzError(Exception): class BlitzError(Exception):
def __init__(self, errorShort, errorLong="", errorException=None): def __init__(self, errorShort, errorLong="", errorException=None):
self.errorShort = str(errorShort) self.errorShort = str(errorShort)
@ -75,19 +82,19 @@ def handleException(e):
sys.exit(1) sys.exit(1)
def getsubdomain(fulldomainstring): def get_subdomain(fulldomain_str):
return fulldomainstring.split('.')[0] return fulldomain_str.split('.')[0]
############################ ############################
# API Calls to DNS Servcies # API Calls to DNS Services
############################ ############################
def duckDNSupdate(domain, token, ip): def duckdns_update(domain, token, ip):
print("# duckDNS update IP API call for {0}".format(domain)) print("# duckDNS update IP API call for {0}".format(domain))
# make HTTP request # make HTTP request
url = "https://www.duckdns.org/update?domains={0}&token={1}&ip={2}".format(getsubdomain(domain), token, ip) url = "https://www.duckdns.org/update?domains={0}&token={1}&ip={2}".format(get_subdomain(domain), token, ip)
try: try:
response = session.get(url) response = session.get(url)
if response.status_code != 200: if response.status_code != 200:
@ -102,31 +109,33 @@ def duckDNSupdate(domain, token, ip):
# PROCESS FUNCTIONS # PROCESS FUNCTIONS
##################### #####################
def subscriptionsNew(ip, dnsservice, id, token, target): def subscriptions_new(ip, dnsservice, id, token, target):
# id needs to the full domain name # id needs to be the full domain name
if id.find(".") == -1: if id.find(".") == -1:
raise BlitzError("not a fully qualified domainname", dnsservice_id) # ToDo(frennkie) dnsservice_id doesn't exit
raise BlitzError("not a fully qualified domain name", dnsservice_id)
# check if id already exists # check if id already exists
if len(getSubscription(id)) > 0: if len(get_subscription(id)) > 0:
raise BlitzError("id already exists", id) raise BlitzError("id already exists", id)
# make sure lets encrypt client is installed # make sure lets encrypt client is installed
os.system("/home/admin/config.scripts/bonus.letsencrypt.sh on") os.system("/home/admin/config.scripts/bonus.letsencrypt.sh on")
# dyndns # dyndns
realip = ip real_ip = ip
if ip == "dyndns": if ip == "dyndns":
updateURL = "" update_url = ""
if dnsservice == "duckdns": if dnsservice == "duckdns":
updateURL = "https://www.duckdns.org/update?domains={0}&token={1}".format(getsubdomain(domain), token, ip) # ToDo(frennkie) domain doesn't exit
subprocess.run(['/home/admin/config.scriprs/internet.dyndomain.sh', 'on', id, updateURL], update_url = "https://www.duckdns.org/update?domains={0}&token={1}".format(get_subdomain(domain), token, ip)
subprocess.run(['/home/admin/config.scriprs/internet.dyndomain.sh', 'on', id, update_url],
stdout=subprocess.PIPE).stdout.decode('utf-8').strip() stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
realip = cfg.public_ip real_ip = cfg.public_ip
# update DNS with actual IP # update DNS with actual IP
if dnsservice == "duckdns": if dnsservice == "duckdns":
duckDNSupdate(getsubdomain(id), token, realip) duckdns_update(get_subdomain(id), token, real_ip)
# create subscription data for storage # create subscription data for storage
subscription = dict() subscription = dict()
@ -164,10 +173,10 @@ def subscriptionsNew(ip, dnsservice, id, token, target):
# run the ACME script # run the ACME script
print("# Running letsencrypt ACME script ...") print("# Running letsencrypt ACME script ...")
acmeResult = subprocess.Popen( acme_result = subprocess.Popen(
["/home/admin/config.scripts/bonus.letsencrypt.sh", "issue-cert", dnsservice, id, token, target], ["/home/admin/config.scripts/bonus.letsencrypt.sh", "issue-cert", dnsservice, id, token, target],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf8') stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf8')
out, err = acmeResult.communicate() out, err = acme_result.communicate()
eprint(str(out)) eprint(str(out))
eprint(str(err)) eprint(str(err))
if out.find("error=") > -1: if out.find("error=") > -1:
@ -178,26 +187,24 @@ def subscriptionsNew(ip, dnsservice, id, token, target):
return subscription return subscription
def subscriptionsCancel(id): def subscriptions_cancel(s_id):
# ToDo(frennkie) id is not used..
os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE)) os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE))
subs = toml.load(SUBSCRIPTIONS_FILE) subs = toml.load(SUBSCRIPTIONS_FILE)
newList = [] new_list = []
removedCert = None removed_cert = None
for idx, sub in enumerate(subs['subscriptions_letsencrypt']): for idx, sub in enumerate(subs['subscriptions_letsencrypt']):
if sub['id'] != subscriptionID: if sub['id'] != s_id:
newList.append(sub) new_list.append(sub)
else: else:
removedCert = sub removed_cert = sub
subs['subscriptions_letsencrypt'] = newList subs['subscriptions_letsencrypt'] = new_list
# run the ACME script to remove cert # run the ACME script to remove cert
if removedCert: if removed_cert:
acmeResult = subprocess.Popen( acme_result = subprocess.Popen(
["/home/admin/config.scripts/bonus.letsencrypt.sh", "remove-cert", removedCert['id'], ["/home/admin/config.scripts/bonus.letsencrypt.sh", "remove-cert", removed_cert['id'],
removedCert['target']], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf8') removed_cert['target']], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf8')
out, err = acmeResult.communicate() out, err = acme_result.communicate()
if out.find("error=") > -1: if out.find("error=") > -1:
time.sleep(6) time.sleep(6)
raise BlitzError("letsencrypt acme failed", out) raise BlitzError("letsencrypt acme failed", out)
@ -212,7 +219,7 @@ def subscriptionsCancel(id):
# todo: deinstall letsencrypt if this was last subscription # todo: deinstall letsencrypt if this was last subscription
def getSubscription(subscriptionID): def get_subscription(subscription_id):
try: try:
if Path(SUBSCRIPTIONS_FILE).is_file(): if Path(SUBSCRIPTIONS_FILE).is_file():
@ -223,7 +230,7 @@ def getSubscription(subscriptionID):
if "subscriptions_letsencrypt" not in subs: if "subscriptions_letsencrypt" not in subs:
return [] return []
for idx, sub in enumerate(subs['subscriptions_letsencrypt']): for idx, sub in enumerate(subs['subscriptions_letsencrypt']):
if sub['id'] == subscriptionID: if sub['id'] == subscription_id:
return sub return sub
return [] return []
@ -231,7 +238,7 @@ def getSubscription(subscriptionID):
return [] return []
def getDomainByIP(ip): def get_domain_by_ip(ip):
# does subscriptin file exists # does subscriptin file exists
if Path(SUBSCRIPTIONS_FILE).is_file(): if Path(SUBSCRIPTIONS_FILE).is_file():
os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE)) os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE))
@ -253,7 +260,7 @@ def getDomainByIP(ip):
raise BlitzError("no match") raise BlitzError("no match")
def menuMakeSubscription(): def menu_make_subscription():
# late imports - so that rest of script can run also if dependency is not available # late imports - so that rest of script can run also if dependency is not available
from dialog import Dialog from dialog import Dialog
@ -299,7 +306,7 @@ If you havent already go to https://duckdns.org
title="DuckDNS Domain") title="DuckDNS Domain")
subdomain = text.strip() subdomain = text.strip()
subdomain = subdomain.split(' ')[0] subdomain = subdomain.split(' ')[0]
subdomain = getsubdomain(subdomain) subdomain = get_subdomain(subdomain)
domain = "{0}.duckdns.org".format(subdomain) domain = "{0}.duckdns.org".format(subdomain)
os.system("clear") os.system("clear")
@ -326,7 +333,7 @@ This looks not like a valid subdomain.
if len(token) < 20: if len(token) < 20:
Dialog(dialog="dialog", autowidgetsize=True).msgbox(''' Dialog(dialog="dialog", autowidgetsize=True).msgbox('''
This looks not like a valid token. This looks not like a valid token.
''', title="Unvalid Input") ''', title="Invalid Input")
sys.exit(0) sys.exit(0)
else: else:
@ -350,7 +357,7 @@ This looks not like a valid token.
"\nChoose the kind of IP you want to use:", "\nChoose the kind of IP you want to use:",
choices=choices, width=60, height=10, title="Select Service") choices=choices, width=60, height=10, title="Select Service")
# if user chosses CANCEL # if user chooses CANCEL
os.system("clear") os.system("clear")
if code != d.OK: if code != d.OK:
sys.exit(0) sys.exit(0)
@ -362,16 +369,16 @@ This looks not like a valid token.
if tag == "IP2TOR": if tag == "IP2TOR":
# get all active IP2TOR subscriptions (just in case) # get all active IP2TOR subscriptions (just in case)
ip2torSubs = [] ip2tor_subs = []
if Path(SUBSCRIPTIONS_FILE).is_file(): if Path(SUBSCRIPTIONS_FILE).is_file():
os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE)) os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE))
subs = toml.load(SUBSCRIPTIONS_FILE) subs = toml.load(SUBSCRIPTIONS_FILE)
for idx, sub in enumerate(subs['subscriptions_ip2tor']): for idx, sub in enumerate(subs['subscriptions_ip2tor']):
if sub['active']: if sub['active']:
ip2torSubs.append(sub) ip2tor_subs.append(sub)
# when user has no IP2TOR subs yet # when user has no IP2TOR subs yet
if len(ip2torSubs) == 0: if len(ip2tor_subs) == 0:
Dialog(dialog="dialog", autowidgetsize=True).msgbox(''' Dialog(dialog="dialog", autowidgetsize=True).msgbox('''
You have no active IP2TOR subscriptions. You have no active IP2TOR subscriptions.
Create one first and try again. Create one first and try again.
@ -380,7 +387,7 @@ Create one first and try again.
# let user select a IP2TOR subscription # let user select a IP2TOR subscription
choices = [] choices = []
for idx, sub in enumerate(ip2torSubs): for idx, sub in enumerate(ip2tor_subs):
choices.append(("{0}".format(idx), "IP2TOR {0} {1}:{2}".format(sub['name'], sub['ip'], sub['port']))) choices.append(("{0}".format(idx), "IP2TOR {0} {1}:{2}".format(sub['name'], sub['ip'], sub['port'])))
d = Dialog(dialog="dialog", autowidgetsize=True) d = Dialog(dialog="dialog", autowidgetsize=True)
@ -394,8 +401,8 @@ Create one first and try again.
sys.exit(0) sys.exit(0)
# get the slected IP2TOR bridge # get the slected IP2TOR bridge
ip2torSelect = ip2torSubs[int(tag)] ip2tor_select = ip2tor_subs[int(tag)]
ip = ip2torSelect["ip"] ip = ip2tor_select["ip"]
target = "tor" target = "tor"
elif tag == "DYNDNS": elif tag == "DYNDNS":
@ -421,13 +428,13 @@ Create one first and try again.
if len(ip) == 0: if len(ip) == 0:
Dialog(dialog="dialog", autowidgetsize=True).msgbox(''' Dialog(dialog="dialog", autowidgetsize=True).msgbox('''
This looks not like a valid IP. This looks not like a valid IP.
''', title="Unvalid Input") ''', title="Invalid Input")
sys.exit(0) sys.exit(0)
# create the letsencrypt subscription # create the letsencrypt subscription
try: try:
os.system("clear") os.system("clear")
subscription = subscriptionsNew(ip, dnsservice, domain, token, target) subscription = subscriptions_new(ip, dnsservice, domain, token, target)
# success dialog # success dialog
Dialog(dialog="dialog", autowidgetsize=True).msgbox(''' Dialog(dialog="dialog", autowidgetsize=True).msgbox('''
@ -455,19 +462,15 @@ Unknown Error happened - please report to developers:
# CREATE SSH DIALOG # CREATE SSH DIALOG
# use for ssh shell menu # use for ssh shell menu
############### ###############
def create_ssh_dialog():
menu_make_subscription()
if sys.argv[1] == "create-ssh-dialog":
menuMakeSubscription()
sys.exit()
########################## ##########################
# SUBSCRIPTIONS NEW # SUBSCRIPTIONS NEW
# call from web interface # call from web interface
########################## ##########################
def subscription_new():
if sys.argv[1] == "subscription-new":
# check parameters # check parameters
try: try:
if len(sys.argv) <= 5: if len(sys.argv) <= 5:
@ -486,7 +489,7 @@ if sys.argv[1] == "subscription-new":
# create the subscription # create the subscription
try: try:
subscription = subscriptionsNew(ip, dnsservice_type, dnsservice_id, dnsservice_token, target) subscription = subscriptions_new(ip, dnsservice_type, dnsservice_id, dnsservice_token, target)
# output json ordered bridge # output json ordered bridge
print(json.dumps(subscription, indent=2)) print(json.dumps(subscription, indent=2))
@ -495,12 +498,11 @@ if sys.argv[1] == "subscription-new":
except Exception as e: except Exception as e:
handleException(e) handleException(e)
####################### #######################
# SUBSCRIPTIONS LIST # SUBSCRIPTIONS LIST
####################### #######################
def subscriptions_list():
if sys.argv[1] == "subscriptions-list":
try: try:
if Path(SUBSCRIPTIONS_FILE).is_file(): if Path(SUBSCRIPTIONS_FILE).is_file():
@ -515,13 +517,11 @@ if sys.argv[1] == "subscriptions-list":
except Exception as e: except Exception as e:
handleException(e) handleException(e)
sys.exit(0)
####################### #######################
# SUBSCRIPTION DETAIL # SUBSCRIPTION DETAIL
####################### #######################
if sys.argv[1] == "subscription-detail": def subscription_detail():
# check parameters # check parameters
try: try:
if len(sys.argv) <= 2: if len(sys.argv) <= 2:
@ -529,22 +529,20 @@ if sys.argv[1] == "subscription-detail":
except Exception as e: except Exception as e:
handleException(e) handleException(e)
subscriptionID = sys.argv[2] subscription_id = sys.argv[2]
try: try:
sub = getSubscription(subscriptionID) sub = get_subscription(subscription_id)
print(json.dumps(sub, indent=2)) print(json.dumps(sub, indent=2))
except Exception as e: except Exception as e:
handleException(e) handleException(e)
sys.exit(0)
####################### #######################
# DOMAIN BY IP # DOMAIN BY IP
# to check if an ip has a domain mapping # to check if an ip has a domain mapping
####################### #######################
if sys.argv[1] == "domain-by-ip": def domain_by_ip():
# check parameters # check parameters
try: try:
if len(sys.argv) <= 2: if len(sys.argv) <= 2:
@ -556,19 +554,17 @@ if sys.argv[1] == "domain-by-ip":
ip = sys.argv[2] ip = sys.argv[2]
try: try:
domain = getDomainByIP(ip) domain = get_domain_by_ip(ip)
print("domain='{0}'".format(domain)) print("domain='{0}'".format(domain))
except Exception as e: except Exception as e:
handleException(e) handleException(e)
sys.exit(0)
####################### #######################
# SUBSCRIPTION CANCEL # SUBSCRIPTION CANCEL
####################### #######################
if sys.argv[1] == "subscription-cancel": def subscription_cancel():
# check parameters # check parameters
try: try:
if len(sys.argv) <= 2: if len(sys.argv) <= 2:
@ -576,13 +572,36 @@ if sys.argv[1] == "subscription-cancel":
except Exception as e: except Exception as e:
handleException(e) handleException(e)
subscriptionID = sys.argv[2] subscription_id = sys.argv[2]
try: try:
subscriptionsCancel(subscriptionID) subscriptions_cancel(subscription_id)
except Exception as e: except Exception as e:
handleException(e) handleException(e)
sys.exit(0)
# unknown command def main():
print("# unknown command") if sys.argv[1] == "create-ssh-dialog":
create_ssh_dialog()
elif sys.argv[1] == "domain-by-ip":
domain_by_ip()
elif sys.argv[1] == "subscriptions-list":
subscriptions_list()
elif sys.argv[1] == "subscription-cancel":
subscription_cancel()
elif sys.argv[1] == "subscription-detail":
subscription_detail()
elif sys.argv[1] == "subscription-new":
subscription_new()
else:
# unknown command
print("# unknown command")
if __name__ == '__main__':
main()

View file

@ -15,10 +15,10 @@ from blitzpy import RaspiBlitzConfig
from dialog import Dialog from dialog import Dialog
# constants for standard services # constants for standard services
LND_REST_API = "LND-REST-API" SERVICE_LND_REST_API = "LND-REST-API"
LND_GRPC_API = "LND-GRPC-API" SERVICE_LND_GRPC_API = "LND-GRPC-API"
LNBITS = "LNBITS" SERVICE_LNBITS = "LNBITS"
BTCPAY = "BTCPAY" SERVICE_BTCPAY = "BTCPAY"
# load config # load config
cfg = RaspiBlitzConfig() cfg = RaspiBlitzConfig()
@ -32,35 +32,38 @@ SUBSCRIPTIONS_FILE = "/mnt/hdd/app-data/subscriptions/subscriptions.toml"
# HELPER FUNCTIONS # HELPER FUNCTIONS
####################### #######################
# ToDo(frennkie) these are not being used!
def eprint(*args, **kwargs): def eprint(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs) print(*args, file=sys.stderr, **kwargs)
def parseDateIP2TORSERVER(datestr): def parse_date_ip2tor(date_str):
return datetime.strptime(datestr, "%Y-%m-%dT%H:%M:%S.%fZ") return datetime.strptime(date_str, "%Y-%m-%dT%H:%M:%S.%fZ")
def secondsLeft(dateObj): def seconds_left(date_obj):
return round((dateObj - datetime.utcnow()).total_seconds()) return round((date_obj - datetime.utcnow()).total_seconds())
####################### #######################
# SSH MENU FUNCTIONS # SSH MENU FUNCTIONS
####################### #######################
def mySubscriptions(): def my_subscriptions():
# check if any subscriptions are available # check if any subscriptions are available
countSubscriptions = 0 count_subscriptions = 0
try: try:
os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE)) os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE))
subs = toml.load(SUBSCRIPTIONS_FILE) subs = toml.load(SUBSCRIPTIONS_FILE)
if 'subscriptions_ip2tor' in subs: if 'subscriptions_ip2tor' in subs:
countSubscriptions += len(subs['subscriptions_ip2tor']) count_subscriptions += len(subs['subscriptions_ip2tor'])
if 'subscriptions_letsencrypt' in subs: if 'subscriptions_letsencrypt' in subs:
countSubscriptions += len(subs['subscriptions_letsencrypt']) count_subscriptions += len(subs['subscriptions_letsencrypt'])
except Exception as e: except Exception as e:
pass print(f"warning: {e}")
if countSubscriptions == 0:
if count_subscriptions == 0:
Dialog(dialog="dialog", autowidgetsize=True).msgbox(''' Dialog(dialog="dialog", autowidgetsize=True).msgbox('''
You have no active or inactive subscriptions. You have no active or inactive subscriptions.
''', title="Info") ''', title="Info")
@ -69,36 +72,36 @@ You have no active or inactive subscriptions.
# load subscriptions and make dialog choices out of it # load subscriptions and make dialog choices out of it
choices = [] choices = []
lookup = {} lookup = {}
lookupIndex = 0 lookup_index = 0
subs = toml.load(SUBSCRIPTIONS_FILE) subs = toml.load(SUBSCRIPTIONS_FILE)
# list ip2tor subscriptions # list ip2tor subscriptions
if 'subscriptions_ip2tor' in subs: if 'subscriptions_ip2tor' in subs:
for sub in subs['subscriptions_ip2tor']: for sub in subs['subscriptions_ip2tor']:
# remember subscription under lookupindex # remember subscription under lookupindex
lookupIndex += 1 lookup_index += 1
lookup[str(lookupIndex)] = sub lookup[str(lookup_index)] = sub
# add to dialog choices # add to dialog choices
if sub['active']: if sub['active']:
activeState = "active" active_state = "active"
else: else:
activeState = "in-active" active_state = "in-active"
name = "IP2TOR Bridge (P:{1}) for {0}".format(sub['name'], sub['port']) name = "IP2TOR Bridge (P:{1}) for {0}".format(sub['name'], sub['port'])
choices.append(("{0}".format(lookupIndex), "{0} ({1})".format(name.ljust(30), activeState))) choices.append(("{0}".format(lookup_index), "{0} ({1})".format(name.ljust(30), active_state)))
# list letsencrypt subscriptions # list letsencrypt subscriptions
if 'subscriptions_letsencrypt' in subs: if 'subscriptions_letsencrypt' in subs:
for sub in subs['subscriptions_letsencrypt']: for sub in subs['subscriptions_letsencrypt']:
# remember subscription under lookupindex # remember subscription under lookupindex
lookupIndex += 1 lookup_index += 1
lookup[str(lookupIndex)] = sub lookup[str(lookup_index)] = sub
# add to dialog choices # add to dialog choices
if sub['active']: if sub['active']:
activeState = "active" active_state = "active"
else: else:
activeState = "in-active" active_state = "in-active"
name = "LETSENCRYPT {0}".format(sub['id']) name = "LETSENCRYPT {0}".format(sub['id'])
choices.append(("{0}".format(lookupIndex), "{0} ({1})".format(name.ljust(30), activeState))) choices.append(("{0}".format(lookup_index), "{0} ({1})".format(name.ljust(30), active_state)))
# show menu with options # show menu with options
d = Dialog(dialog="dialog", autowidgetsize=True) d = Dialog(dialog="dialog", autowidgetsize=True)
@ -111,15 +114,15 @@ You have no active or inactive subscriptions.
if code != d.OK: if code != d.OK:
return return
# get data of selected subscrption # get data of selected subscription
selectedSub = lookup[str(tag)] selected_sub = lookup[str(tag)]
# show details of selected # show details of selected
d = Dialog(dialog="dialog", autowidgetsize=True) d = Dialog(dialog="dialog", autowidgetsize=True)
d.set_background_title("My Subscriptions") d.set_background_title("My Subscriptions")
if selectedSub['type'] == "letsencrypt-v1": if selected_sub['type'] == "letsencrypt-v1":
if len(selectedSub['warning']) > 0: if len(selected_sub['warning']) > 0:
selectedSub['warning'] = "\n{0}".format(selectedSub['warning']) selected_sub['warning'] = "\n{0}".format(selected_sub['warning'])
text = ''' text = '''
This is a LetsEncrypt subscription using the free DNS service This is a LetsEncrypt subscription using the free DNS service
{dnsservice} {dnsservice}
@ -135,17 +138,17 @@ The state of the subscription is: {active} {warning}
The following additional information is available: The following additional information is available:
{description} {description}
'''.format(dnsservice=selectedSub['dnsservice_type'], '''.format(dnsservice=selected_sub['dnsservice_type'],
domain=selectedSub['id'], domain=selected_sub['id'],
ip=selectedSub['ip'], ip=selected_sub['ip'],
active="ACTIVE" if selectedSub['active'] else "NOT ACTIVE", active="ACTIVE" if selected_sub['active'] else "NOT ACTIVE",
warning=selectedSub['warning'], warning=selected_sub['warning'],
description=selectedSub['description'] description=selected_sub['description']
) )
elif selectedSub['type'] == "ip2tor-v1": elif selected_sub['type'] == "ip2tor-v1":
if len(selectedSub['warning']) > 0: if len(selected_sub['warning']) > 0:
selectedSub['warning'] = "\n{0}".format(selectedSub['warning']) selected_sub['warning'] = "\n{0}".format(selected_sub['warning'])
text = ''' text = '''
This is a IP2TOR subscription bought on {initdate} at This is a IP2TOR subscription bought on {initdate} at
{shop} {shop}
@ -161,26 +164,26 @@ The state of the subscription is: {active} {warning}
The following additional information is available: The following additional information is available:
{description} {description}
'''.format(initdate=selectedSub['time_created'], '''.format(initdate=selected_sub['time_created'],
shop=selectedSub['shop'], shop=selected_sub['shop'],
publicaddress="{0}:{1}".format(selectedSub['ip'], selectedSub['port']), publicaddress="{0}:{1}".format(selected_sub['ip'], selected_sub['port']),
toraddress=selectedSub['tor'], toraddress=selected_sub['tor'],
renewhours=(round(int(selectedSub['duration']) / 3600)), renewhours=(round(int(selected_sub['duration']) / 3600)),
renewsats=(round(int(selectedSub['price_extension']) / 1000)), renewsats=(round(int(selected_sub['price_extension']) / 1000)),
totalsats=(round(int(selectedSub['price_total']) / 1000)), totalsats=(round(int(selected_sub['price_total']) / 1000)),
active="ACTIVE" if selectedSub['active'] else "NOT ACTIVE", active="ACTIVE" if selected_sub['active'] else "NOT ACTIVE",
warning=selectedSub['warning'], warning=selected_sub['warning'],
description=selectedSub['description'], description=selected_sub['description'],
service=selectedSub['name'] service=selected_sub['name']
) )
else: else:
text = "no text?! FIXME" text = "no text?! FIXME"
if selectedSub['active']: if selected_sub['active']:
extraLable = "CANCEL SUBSCRIPTION" extra_label = "CANCEL SUBSCRIPTION"
else: else:
extraLable = "DELETE SUBSCRIPTION" extra_label = "DELETE SUBSCRIPTION"
code = d.msgbox(text, title="Subscription Detail", ok_label="Back", extra_button=True, extra_label=extraLable, code = d.msgbox(text, title="Subscription Detail", ok_label="Back", extra_button=True, extra_label=extra_label,
width=75, height=30) width=75, height=30)
# user wants to delete this subscription # user wants to delete this subscription
@ -188,15 +191,15 @@ The following additional information is available:
# api calls when canceling # api calls when canceling
if code == "extra": if code == "extra":
os.system("clear") os.system("clear")
if selectedSub['type'] == "letsencrypt-v1": if selected_sub['type'] == "letsencrypt-v1":
cmd = "python /home/admin/config.scripts/blitz.subscriptions.letsencrypt.py subscription-cancel {0}".format( cmd = "python /home/admin/config.scripts/blitz.subscriptions.letsencrypt.py subscription-cancel {0}".format(
selectedSub['id']) selected_sub['id'])
print("# running: {0}".format(cmd)) print("# running: {0}".format(cmd))
os.system(cmd) os.system(cmd)
time.sleep(2) time.sleep(2)
elif selectedSub['type'] == "ip2tor-v1": elif selected_sub['type'] == "ip2tor-v1":
cmd = "python /home/admin/config.scripts/blitz.subscriptions.ip2tor.py subscription-cancel {0}".format( cmd = "python /home/admin/config.scripts/blitz.subscriptions.ip2tor.py subscription-cancel {0}".format(
selectedSub['id']) selected_sub['id'])
print("# running: {0}".format(cmd)) print("# running: {0}".format(cmd))
os.system(cmd) os.system(cmd)
time.sleep(2) time.sleep(2)
@ -205,183 +208,188 @@ The following additional information is available:
time.sleep(3) time.sleep(3)
# loop until no more subscriptions or user chooses CANCEL on subscription list # loop until no more subscriptions or user chooses CANCEL on subscription list
mySubscriptions() my_subscriptions()
####################### def main():
# SSH MENU #######################
####################### # SSH MENU
#######################
choices = list()
choices.append(("LIST", "My Subscriptions"))
choices.append(("NEW1", "+ IP2TOR Bridge (paid)"))
choices.append(("NEW2", "+ LetsEncrypt HTTPS Domain (free)"))
d = Dialog(dialog="dialog", autowidgetsize=True)
d.set_background_title("RaspiBlitz Subscriptions")
code, tag = d.menu(
"\nCheck existing subscriptions or create new:",
choices=choices, width=50, height=10, title="Subscription Management")
# if user chosses CANCEL
if code != d.OK:
sys.exit(0)
#######################
# MANAGE SUBSCRIPTIONS
#######################
if tag == "LIST":
mySubscriptions()
sys.exit(0)
###############################
# NEW LETSENCRYPT HTTPS DOMAIN
###############################
if tag == "NEW2":
# run creating a new IP2TOR subscription
os.system("clear")
cmd = "python /home/admin/config.scripts/blitz.subscriptions.letsencrypt.py create-ssh-dialog"
print("# running: {0}".format(cmd))
os.system(cmd)
sys.exit(0)
###############################
# NEW IP2TOR BRIDGE
###############################
if tag == "NEW1":
# check if Blitz is running behind TOR
cfg.reload()
if not cfg.run_behind_tor.value:
Dialog(dialog="dialog", autowidgetsize=True).msgbox('''
The IP2TOR service just makes sense if you run
your RaspiBlitz behind TOR.
''', title="Info")
sys.exit(1)
os.system("clear")
print("please wait ..")
# check for which standard services already a active bridge exists
lnd_rest_api = False
lnd_grpc_api = False
lnbits = False
btcpay = False
try:
if os.path.isfile(SUBSCRIPTIONS_FILE):
os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE))
subs = toml.load(SUBSCRIPTIONS_FILE)
for sub in subs['subscriptions_ip2tor']:
if not sub['active']:
continue
if sub['active'] and sub['name'] == LND_REST_API:
lnd_rest_api = True
if sub['active'] and sub['name'] == LND_GRPC_API:
lnd_grpc_api = True
if sub['active'] and sub['name'] == LNBITS:
lnbits = True
if sub['active'] and sub['name'] == BTCPAY:
btcpay = True
except Exception as e:
print(e)
# check if BTCPayserver is installed
btcPayServer = False
statusData = subprocess.run(['/home/admin/config.scripts/bonus.btcpayserver.sh', 'status'],
stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
if statusData.find("installed=1") > -1:
btcPayServer = True
# ask user for which RaspiBlitz service the bridge should be used
choices = list() choices = list()
choices.append(("REST", "LND REST API {0}".format("--> ALREADY BRIDGED" if lnd_rest_api else ""))) choices.append(("LIST", "My Subscriptions"))
choices.append(("GRPC", "LND gRPC API {0}".format("--> ALREADY BRIDGED" if lnd_grpc_api else ""))) choices.append(("NEW1", "+ IP2TOR Bridge (paid)"))
if cfg.lnbits: choices.append(("NEW2", "+ LetsEncrypt HTTPS Domain (free)"))
choices.append(("LNBITS", "LNbits Webinterface {0}".format("--> ALREADY BRIDGED" if lnd_grpc_api else "")))
if btcPayServer:
choices.append(("BTCPAY", "BTCPay Server Webinterface {0}".format("--> ALREADY BRIDGED" if btcpay else "")))
choices.append(("SELF", "Create a custom IP2TOR Bridge"))
d = Dialog(dialog="dialog", autowidgetsize=True) d = Dialog(dialog="dialog", autowidgetsize=True)
d.set_background_title("RaspiBlitz Subscriptions") d.set_background_title("RaspiBlitz Subscriptions")
code, tag = d.menu( code, tag = d.menu(
"\nChoose RaspiBlitz Service to create Bridge for:", "\nCheck existing subscriptions or create new:",
choices=choices, width=60, height=10, title="Select Service") choices=choices, width=50, height=10, title="Subscription Management")
# if user chosses CANCEL # if user chosses CANCEL
if code != d.OK: if code != d.OK:
sys.exit(0) sys.exit(0)
servicename = None #######################
torAddress = None # MANAGE SUBSCRIPTIONS
torPort = None #######################
if tag == "REST":
# get TOR address for REST if tag == "LIST":
servicename = LND_REST_API my_subscriptions()
torAddress = subprocess.run(['sudo', 'cat', '/mnt/hdd/tor/lndrest8080/hostname'], sys.exit(0)
stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
torPort = 8080 ###############################
if tag == "GRPC": # NEW LETSENCRYPT HTTPS DOMAIN
# get TOR address for GRPC ###############################
servicename = LND_GRPC_API
torAddress = subprocess.run(['sudo', 'cat', '/mnt/hdd/tor/lndrpc10009/hostname'], if tag == "NEW2":
stdout=subprocess.PIPE).stdout.decode('utf-8').strip() # run creating a new IP2TOR subscription
torPort = 10009 os.system("clear")
if tag == "LNBITS": cmd = "python /home/admin/config.scripts/blitz.subscriptions.letsencrypt.py create-ssh-dialog"
# get TOR address for LNBits print("# running: {0}".format(cmd))
servicename = LNBITS os.system(cmd)
torAddress = subprocess.run(['sudo', 'cat', '/mnt/hdd/tor/lnbits/hostname'], sys.exit(0)
stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
torPort = 443 ###############################
if tag == "BTCPAY": # NEW IP2TOR BRIDGE
# get TOR address for BTCPAY ###############################
servicename = BTCPAY
torAddress = subprocess.run(['sudo', 'cat', '/mnt/hdd/tor/btcpay/hostname'], if tag == "NEW1":
stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
torPort = 443 # check if Blitz is running behind TOR
if tag == "SELF": cfg.reload()
servicename = "CUSTOM" if not cfg.run_behind_tor.value:
try: Dialog(dialog="dialog", autowidgetsize=True).msgbox('''
# get custom TOR address The IP2TOR service just makes sense if you run
code, text = d.inputbox( your RaspiBlitz behind TOR.
"Enter TOR Onion-Address:", ''', title="Info")
height=10, width=60, init="",
title="IP2TOR Bridge Target")
text = text.strip()
os.system("clear")
if code != d.OK:
sys.exit(0)
if len(text) == 0:
sys.exit(0)
if text.find('.onion') < 0 or text.find(' ') > 0:
print("Not a TOR Onion Address")
time.sleep(3)
sys.exit(0)
torAddress = text
# get custom TOR port
code, text = d.inputbox(
"Enter TOR Port Number:",
height=10, width=40, init="80",
title="IP2TOR Bridge Target")
text = text.strip()
os.system("clear")
if code != d.OK:
sys.exit(0)
if len(text) == 0:
sys.exit(0)
torPort = int(text)
except Exception as e:
print(e)
time.sleep(3)
sys.exit(1) sys.exit(1)
# run creating a new IP2TOR subscription os.system("clear")
os.system("clear") print("please wait ..")
cmd = "python /home/admin/config.scripts/blitz.subscriptions.ip2tor.py create-ssh-dialog {0} {1} {2}".format(
servicename, torAddress, torPort) # check for which standard services already a active bridge exists
print("# running: {0}".format(cmd)) lnd_rest_api = False
os.system(cmd) lnd_grpc_api = False
sys.exit(0) lnbits = False
btcpay = False
try:
if os.path.isfile(SUBSCRIPTIONS_FILE):
os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE))
subs = toml.load(SUBSCRIPTIONS_FILE)
for sub in subs['subscriptions_ip2tor']:
if not sub['active']:
continue
if sub['active'] and sub['name'] == SERVICE_LND_REST_API:
lnd_rest_api = True
if sub['active'] and sub['name'] == SERVICE_LND_GRPC_API:
lnd_grpc_api = True
if sub['active'] and sub['name'] == SERVICE_LNBITS:
lnbits = True
if sub['active'] and sub['name'] == SERVICE_BTCPAY:
btcpay = True
except Exception as e:
print(e)
# check if BTCPayServer is installed
btc_pay_server = False
status_data = subprocess.run(['/home/admin/config.scripts/bonus.btcpayserver.sh', 'status'],
stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
if status_data.find("installed=1") > -1:
btc_pay_server = True
# ask user for which RaspiBlitz service the bridge should be used
choices = list()
choices.append(("REST", "LND REST API {0}".format("--> ALREADY BRIDGED" if lnd_rest_api else "")))
choices.append(("GRPC", "LND gRPC API {0}".format("--> ALREADY BRIDGED" if lnd_grpc_api else "")))
if cfg.lnbits:
choices.append(("LNBITS", "LNbits Webinterface {0}".format("--> ALREADY BRIDGED" if lnbits else "")))
if btc_pay_server:
choices.append(("BTCPAY", "BTCPay Server Webinterface {0}".format("--> ALREADY BRIDGED" if btcpay else "")))
choices.append(("SELF", "Create a custom IP2TOR Bridge"))
d = Dialog(dialog="dialog", autowidgetsize=True)
d.set_background_title("RaspiBlitz Subscriptions")
code, tag = d.menu(
"\nChoose RaspiBlitz Service to create Bridge for:",
choices=choices, width=60, height=10, title="Select Service")
# if user chosses CANCEL
if code != d.OK:
sys.exit(0)
service_name = None
tor_address = None
tor_port = None
if tag == "REST":
# get TOR address for REST
service_name = SERVICE_LND_REST_API
tor_address = subprocess.run(['sudo', 'cat', '/mnt/hdd/tor/lndrest8080/hostname'],
stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
tor_port = 8080
if tag == "GRPC":
# get TOR address for GRPC
service_name = SERVICE_LND_GRPC_API
tor_address = subprocess.run(['sudo', 'cat', '/mnt/hdd/tor/lndrpc10009/hostname'],
stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
tor_port = 10009
if tag == "LNBITS":
# get TOR address for LNBits
service_name = SERVICE_LNBITS
tor_address = subprocess.run(['sudo', 'cat', '/mnt/hdd/tor/lnbits/hostname'],
stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
tor_port = 443
if tag == "BTCPAY":
# get TOR address for BTCPAY
service_name = SERVICE_BTCPAY
tor_address = subprocess.run(['sudo', 'cat', '/mnt/hdd/tor/btcpay/hostname'],
stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
tor_port = 443
if tag == "SELF":
service_name = "CUSTOM"
try:
# get custom TOR address
code, text = d.inputbox(
"Enter TOR Onion-Address:",
height=10, width=60, init="",
title="IP2TOR Bridge Target")
text = text.strip()
os.system("clear")
if code != d.OK:
sys.exit(0)
if len(text) == 0:
sys.exit(0)
if text.find('.onion') < 0 or text.find(' ') > 0:
print("Not a TOR Onion Address")
time.sleep(3)
sys.exit(0)
tor_address = text
# get custom TOR port
code, text = d.inputbox(
"Enter TOR Port Number:",
height=10, width=40, init="80",
title="IP2TOR Bridge Target")
text = text.strip()
os.system("clear")
if code != d.OK:
sys.exit(0)
if len(text) == 0:
sys.exit(0)
tor_port = int(text)
except Exception as e:
print(e)
time.sleep(3)
sys.exit(1)
# run creating a new IP2TOR subscription
os.system("clear")
cmd = "python /home/admin/config.scripts/blitz.subscriptions.ip2tor.py create-ssh-dialog {0} {1} {2}".format(
service_name, tor_address, tor_port)
print("# running: {0}".format(cmd))
os.system(cmd)
sys.exit(0)
if __name__ == '__main__':
main()