mirror of
https://github.com/rootzoll/raspiblitz.git
synced 2025-02-24 22:58:43 +01:00
Merge pull request #1361 from frennkie/ip2tor-blitz-error
ip2tor: add blitz error + refactor
This commit is contained in:
commit
fd4647a605
9 changed files with 474 additions and 414 deletions
|
@ -5,6 +5,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.3.0] - 2020-07-19
|
||||
### Added
|
||||
- add BlitzError Class
|
||||
|
||||
## [0.2.0] - 2020-05-23
|
||||
### Added
|
||||
- add write() to BlitzPy config Classes
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from .config import RaspiBlitzConfig, RaspiBlitzInfo
|
||||
|
||||
__all__ = [
|
||||
'RaspiBlitzConfig',
|
||||
'RaspiBlitzInfo',
|
||||
]
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from .config import RaspiBlitzConfig, RaspiBlitzInfo
|
||||
from .exceptions import BlitzError
|
||||
|
||||
__all__ = [
|
||||
'RaspiBlitzConfig',
|
||||
'RaspiBlitzInfo',
|
||||
'BlitzError'
|
||||
]
|
||||
|
|
16
home.admin/BlitzPy/blitzpy/exceptions.py
Normal file
16
home.admin/BlitzPy/blitzpy/exceptions.py
Normal 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
|
|
@ -4,5 +4,5 @@
|
|||
# 3) we can import it into your module module
|
||||
"""
|
||||
|
||||
__version_info__ = ('0', '2', '0')
|
||||
__version_info__ = ('0', '3', '0')
|
||||
__version__ = '.'.join(__version_info__)
|
||||
|
|
BIN
home.admin/BlitzPy/dist/BlitzPy-0.3.0-py2.py3-none-any.whl
vendored
Normal file
BIN
home.admin/BlitzPy/dist/BlitzPy-0.3.0-py2.py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
home.admin/BlitzPy/dist/BlitzPy-0.3.0.tar.gz
vendored
Normal file
BIN
home.admin/BlitzPy/dist/BlitzPy-0.3.0.tar.gz
vendored
Normal file
Binary file not shown.
|
@ -1,6 +1,5 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
import ast
|
||||
import codecs
|
||||
import json
|
||||
import math
|
||||
|
@ -13,7 +12,7 @@ from pathlib import Path
|
|||
import grpc
|
||||
import requests
|
||||
import toml
|
||||
from blitzpy import RaspiBlitzConfig
|
||||
from blitzpy import RaspiBlitzConfig, BlitzError
|
||||
from lndlibs import rpc_pb2 as lnrpc
|
||||
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")
|
||||
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
|
||||
#####################
|
||||
|
@ -70,17 +75,6 @@ else:
|
|||
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
|
||||
#####################
|
||||
|
@ -91,9 +85,9 @@ def eprint(*args, **kwargs):
|
|||
|
||||
def handleException(e):
|
||||
if isinstance(e, BlitzError):
|
||||
eprint(e.errorLong)
|
||||
eprint(e.errorException)
|
||||
print("error='{0}'".format(e.errorShort))
|
||||
eprint(e.details)
|
||||
eprint(e.org)
|
||||
print("error='{0}'".format(e.short))
|
||||
else:
|
||||
eprint(e)
|
||||
print("error='{0}'".format(str(e)))
|
||||
|
@ -150,17 +144,17 @@ def apiGetHosts(session, shopurl):
|
|||
try:
|
||||
response = session.get(url)
|
||||
except Exception as e:
|
||||
raise BlitzError("failed HTTP request", url, e)
|
||||
raise BlitzError("failed HTTP request", {'url': url}, e)
|
||||
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
|
||||
try:
|
||||
jData = json.loads(response.content)
|
||||
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):
|
||||
raise BlitzError("hosts not list", response.content)
|
||||
raise BlitzError("hosts not list", {'content': response.content})
|
||||
for idx, hostEntry in enumerate(jData):
|
||||
try:
|
||||
# ignore if not offering tor bridge
|
||||
|
@ -188,7 +182,7 @@ def apiGetHosts(session, shopurl):
|
|||
# shorten names to 20 chars max
|
||||
hostEntry['name'] = hostEntry['name'][:20]
|
||||
except Exception as e:
|
||||
raise BlitzError("failed host entry pasring", str(hostEntry), e)
|
||||
raise BlitzError("failed host entry pasring", hostEntry, e)
|
||||
|
||||
hosts.append(hostEntry)
|
||||
|
||||
|
@ -211,11 +205,11 @@ def apiPlaceOrderNew(session, shopurl, hostid, toraddressWithPort):
|
|||
try:
|
||||
response = session.post(url, data=postData)
|
||||
except Exception as e:
|
||||
raise BlitzError("failed HTTP request", url, e)
|
||||
raise BlitzError("failed HTTP request", {'url': url}, e)
|
||||
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:
|
||||
raise BlitzError("failed HTTP code", response.status_code)
|
||||
raise BlitzError("failed HTTP code", {'status_code': response.status_code})
|
||||
|
||||
# parse & validate data
|
||||
try:
|
||||
|
@ -224,7 +218,7 @@ def apiPlaceOrderNew(session, shopurl, hostid, toraddressWithPort):
|
|||
print("error='MISSING ID'")
|
||||
return
|
||||
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']
|
||||
|
||||
|
@ -236,11 +230,11 @@ def apiPlaceOrderExtension(session, shopurl, bridgeid):
|
|||
try:
|
||||
response = session.post(url)
|
||||
except Exception as e:
|
||||
raise BlitzError("failed HTTP request", url, e)
|
||||
raise BlitzError("failed HTTP request", {'url': url}, e)
|
||||
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:
|
||||
raise BlitzError("failed HTTP code", response.status_code)
|
||||
raise BlitzError("failed HTTP code", {'status_code': response.status_code})
|
||||
|
||||
# parse & validate data
|
||||
print("# parse")
|
||||
|
@ -250,12 +244,12 @@ def apiPlaceOrderExtension(session, shopurl, bridgeid):
|
|||
print("error='MISSING ID'")
|
||||
return
|
||||
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']
|
||||
|
||||
|
||||
def apiGetOrder(session, shopurl, orderid):
|
||||
def apiGetOrder(session, shopurl, orderid) -> dict:
|
||||
print("# apiGetOrder")
|
||||
|
||||
# make HTTP request
|
||||
|
@ -263,19 +257,19 @@ def apiGetOrder(session, shopurl, orderid):
|
|||
try:
|
||||
response = session.get(url)
|
||||
except Exception as e:
|
||||
raise BlitzError("failed HTTP request", url, e)
|
||||
raise BlitzError("failed HTTP request", {'url': url}, e)
|
||||
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
|
||||
try:
|
||||
jData = json.loads(response.content)
|
||||
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:
|
||||
raise BlitzError("more than one invoice", response.content)
|
||||
raise BlitzError("more than one invoice", {'content': response.content})
|
||||
except Exception as e:
|
||||
raise BlitzError("failed JSON parsing", response.content, e)
|
||||
raise BlitzError("failed JSON parsing", {'content': response.content}, e)
|
||||
|
||||
return jData
|
||||
|
||||
|
@ -288,16 +282,16 @@ def apiGetBridgeStatus(session, shopurl, bridgeid):
|
|||
try:
|
||||
response = session.get(url)
|
||||
except Exception as e:
|
||||
raise BlitzError("failed HTTP request", url, e)
|
||||
raise BlitzError("failed HTTP request", {'url': url}, e)
|
||||
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
|
||||
try:
|
||||
jData = json.loads(response.content)
|
||||
if len(jData['id']) == 0:
|
||||
raise BlitzError("missing id", response.content)
|
||||
raise BlitzError("missing id", {'content': response.content})
|
||||
except Exception as e:
|
||||
raise BlitzError("failed JSON parsing", response.content, e)
|
||||
raise BlitzError("failed JSON parsing", {'content': response.content}, e)
|
||||
|
||||
return jData
|
||||
|
||||
|
@ -322,10 +316,10 @@ def lndDecodeInvoice(lnInvoiceString):
|
|||
|
||||
# validate results
|
||||
if response.num_msat <= 0:
|
||||
raise BlitzError("zero invoice not allowed", lnInvoiceString)
|
||||
raise BlitzError("zero invoice not allowed", {'invoice': lnInvoiceString})
|
||||
|
||||
except Exception as e:
|
||||
raise BlitzError("failed LND invoice decoding", lnInvoiceString, e)
|
||||
raise BlitzError("failed LND invoice decoding", {'invoice': lnInvoiceString}, e)
|
||||
|
||||
return response
|
||||
|
||||
|
@ -346,10 +340,10 @@ def lndPayInvoice(lnInvoiceString):
|
|||
|
||||
# validate results
|
||||
if len(response.payment_error) > 0:
|
||||
raise BlitzError(response.payment_error, lnInvoiceString)
|
||||
raise BlitzError(response.payment_error, {'invoice': lnInvoiceString})
|
||||
|
||||
except Exception as e:
|
||||
raise BlitzError("payment failed", lnInvoiceString, e)
|
||||
raise BlitzError("payment failed", {'invoice': lnInvoiceString}, e)
|
||||
|
||||
return response
|
||||
|
||||
|
@ -480,7 +474,7 @@ def shopOrder(shopUrl, hostid, servicename, torTarget, duration, msatsFirst, msa
|
|||
|
||||
except Exception as 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))
|
||||
return subscription
|
||||
|
@ -578,7 +572,7 @@ def subscriptionExtend(shopUrl, bridgeid, durationAdvertised, msatsNext, bridge_
|
|||
|
||||
except Exception as 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']))
|
||||
|
||||
|
@ -664,7 +658,8 @@ Try again later, enter another address or cancel.
|
|||
choices=choices, title="Available Subscriptions")
|
||||
|
||||
# if user cancels
|
||||
if code != d.OK: sys.exit(0)
|
||||
if code != d.OK:
|
||||
sys.exit(0)
|
||||
|
||||
# get data of selected
|
||||
seletedIndex = int(tag)
|
||||
|
@ -712,7 +707,8 @@ More information on the service you can find under:
|
|||
height=30)
|
||||
|
||||
# if user AGREED break loop and continue with selected host
|
||||
if code == "extra": break
|
||||
if code == "extra":
|
||||
break
|
||||
|
||||
############################
|
||||
# PHASE 3: Make Subscription
|
||||
|
@ -729,16 +725,15 @@ More information on the service you can find under:
|
|||
|
||||
exitcode = 0
|
||||
|
||||
order = ast.literal_eval(be.errorLong)
|
||||
try :
|
||||
message = order['message']
|
||||
except Exception as e:
|
||||
message = "n/a"
|
||||
try:
|
||||
message = be.details['message']
|
||||
except KeyError:
|
||||
message = ""
|
||||
|
||||
if (be.errorShort == "timeout on waiting for extending bridge" or
|
||||
be.errorShort == "fail on subscription storage" or
|
||||
be.errorShort == "invalid port" or
|
||||
be.errorShort == "timeout bridge not getting ready"):
|
||||
if (be.short == "timeout on waiting for extending bridge" or
|
||||
be.short == "fail on subscription storage" or
|
||||
be.short == "invalid port" or
|
||||
be.short == "timeout bridge not getting ready"):
|
||||
|
||||
# error happened after payment
|
||||
exitcode = Dialog(dialog="dialog", autowidgetsize=True).msgbox('''
|
||||
|
@ -748,7 +743,7 @@ Subscription will be ignored.
|
|||
|
||||
Error: {0}
|
||||
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:
|
||||
|
||||
# error happened before payment
|
||||
|
@ -759,7 +754,7 @@ Subscription will be ignored.
|
|||
|
||||
Error: {0}
|
||||
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)
|
||||
if exitcode == Dialog.EXTRA:
|
||||
|
@ -767,13 +762,13 @@ Message: {1}
|
|||
print('###### ERROR DETAIL FOR DEBUG #######')
|
||||
print("")
|
||||
print("Error Short:")
|
||||
print(be.errorShort)
|
||||
print(be.short)
|
||||
print('Shop:')
|
||||
print(shopurl)
|
||||
print('Bridge:')
|
||||
print(str(host))
|
||||
print("Error Detail:")
|
||||
print(be.errorLong)
|
||||
print(be.details)
|
||||
print("")
|
||||
input("Press Enter to continue ...")
|
||||
|
||||
|
@ -796,7 +791,7 @@ Message: {1}
|
|||
sys.exit(1)
|
||||
|
||||
# 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.credentials.sh reset tls")
|
||||
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
|
||||
protocol = ""
|
||||
if blitzServiceName == "LNBITS":
|
||||
if blitzServiceName == SERVICE_LNBITS:
|
||||
protocol = "https://"
|
||||
|
||||
# Give final result feedback to user
|
||||
|
@ -847,13 +842,11 @@ MAIN MENU > Manage Subscriptions > My Subscriptions
|
|||
# CREATE SSH DIALOG
|
||||
# use for ssh shell menu
|
||||
###############
|
||||
|
||||
if sys.argv[1] == "create-ssh-dialog":
|
||||
|
||||
def create_ssh_dialog():
|
||||
# check parameters
|
||||
try:
|
||||
if len(sys.argv) <= 4:
|
||||
raise BlitzError("incorrect parameters", "")
|
||||
raise BlitzError("incorrect parameters")
|
||||
except Exception as e:
|
||||
handleException(e)
|
||||
|
||||
|
@ -865,17 +858,16 @@ if sys.argv[1] == "create-ssh-dialog":
|
|||
|
||||
sys.exit()
|
||||
|
||||
|
||||
###############
|
||||
# SHOP LIST
|
||||
# call from web interface
|
||||
###############
|
||||
|
||||
if sys.argv[1] == "shop-list":
|
||||
|
||||
def shop_list():
|
||||
# check parameters
|
||||
try:
|
||||
if len(sys.argv) <= 2:
|
||||
raise BlitzError("incorrect parameters", "")
|
||||
raise BlitzError("incorrect parameters")
|
||||
except Exception as e:
|
||||
handleException(e)
|
||||
|
||||
|
@ -891,17 +883,16 @@ if sys.argv[1] == "shop-list":
|
|||
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
##########################
|
||||
# SHOP ORDER
|
||||
# call from web interface
|
||||
##########################
|
||||
|
||||
if sys.argv[1] == "shop-order":
|
||||
|
||||
def shop_order():
|
||||
# check parameters
|
||||
try:
|
||||
if len(sys.argv) <= 8:
|
||||
raise BlitzError("incorrect parameters", "")
|
||||
raise BlitzError("incorrect parameters")
|
||||
except Exception as e:
|
||||
handleException(e)
|
||||
|
||||
|
@ -926,13 +917,12 @@ if sys.argv[1] == "shop-order":
|
|||
except Exception as e:
|
||||
handleException(e)
|
||||
|
||||
|
||||
#######################
|
||||
# SUBSCRIPTIONS LIST
|
||||
# call in intervals from background process
|
||||
#######################
|
||||
|
||||
if sys.argv[1] == "subscriptions-list":
|
||||
|
||||
def subscriptions_list():
|
||||
try:
|
||||
|
||||
if Path(SUBSCRIPTIONS_FILE).is_file():
|
||||
|
@ -947,15 +937,12 @@ if sys.argv[1] == "subscriptions-list":
|
|||
except Exception as e:
|
||||
handleException(e)
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
#######################
|
||||
# SUBSCRIPTIONS RENEW
|
||||
# call in intervals from background process
|
||||
#######################
|
||||
|
||||
if sys.argv[1] == "subscriptions-renew":
|
||||
|
||||
def subscriptions_renew():
|
||||
print("# RUNNING subscriptions-renew")
|
||||
|
||||
# check parameters
|
||||
|
@ -1002,16 +989,16 @@ if sys.argv[1] == "subscriptions-renew":
|
|||
subs = toml.load(SUBSCRIPTIONS_FILE)
|
||||
for sub in subs['subscriptions_ip2tor']:
|
||||
if sub['id'] == subscription['id']:
|
||||
sub['warning'] = "Exception on Renew: {0}".format(be.errorShort)
|
||||
if be.errorShort == "invoice bigger amount than advertised":
|
||||
sub['warning'] = "Exception on Renew: {0}".format(be.short)
|
||||
if be.short == "invoice bigger amount than advertised":
|
||||
sub['contract_breached'] = True
|
||||
sub['active'] = False
|
||||
with open(SUBSCRIPTIONS_FILE, 'w') as writer:
|
||||
writer.write(toml.dumps(subs))
|
||||
writer.close()
|
||||
break
|
||||
print("# BLITZERROR on subscriptions-renew of subscription index {0}: {1}".format(idx, be.errorShort))
|
||||
print("# {0}".format(be.errorShort))
|
||||
print("# BLITZERROR on subscriptions-renew of subscription index {0}: {1}".format(idx, be.short))
|
||||
print("# {0}".format(be.short))
|
||||
|
||||
except Exception as e:
|
||||
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
|
||||
print("# DONE subscriptions-renew")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
#######################
|
||||
# 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
|
||||
try:
|
||||
if len(sys.argv) <= 2:
|
||||
raise BlitzError("incorrect parameters", "")
|
||||
raise BlitzError("incorrect parameters")
|
||||
except Exception as e:
|
||||
handleException(e)
|
||||
|
||||
|
@ -1058,30 +1044,28 @@ if sys.argv[1] == "subscription-cancel":
|
|||
except Exception as 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
|
||||
# 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
|
||||
try:
|
||||
if len(sys.argv) <= 2:
|
||||
raise BlitzError("incorrect parameters", "")
|
||||
raise BlitzError("incorrect parameters")
|
||||
except Exception as e:
|
||||
handleException(e)
|
||||
|
||||
servicename = sys.argv[2]
|
||||
service_name = sys.argv[2]
|
||||
|
||||
try:
|
||||
if os.path.isfile(SUBSCRIPTIONS_FILE):
|
||||
os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE))
|
||||
subs = toml.load(SUBSCRIPTIONS_FILE)
|
||||
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("ip='{0}'".format(sub['ip']))
|
||||
print("port='{0}'".format(sub['port']))
|
||||
|
@ -1089,24 +1073,22 @@ if sys.argv[1] == "subscription-by-service":
|
|||
sys.exit(0)
|
||||
|
||||
print("error='not found'")
|
||||
sys.exit(0)
|
||||
|
||||
except Exception as 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
|
||||
# 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
|
||||
try:
|
||||
if len(sys.argv) <= 2:
|
||||
raise BlitzError("incorrect parameters", "")
|
||||
raise BlitzError("incorrect parameters")
|
||||
except Exception as e:
|
||||
handleException(e)
|
||||
|
||||
|
@ -1126,12 +1108,41 @@ if sys.argv[1] == "ip-by-tor":
|
|||
sys.exit(0)
|
||||
|
||||
print("error='not found'")
|
||||
sys.exit(0)
|
||||
|
||||
except Exception as e:
|
||||
handleException(e)
|
||||
sys.exit(1)
|
||||
|
||||
sys.exit(1)
|
||||
|
||||
# unknown command
|
||||
print("# unknown command")
|
||||
def main():
|
||||
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()
|
||||
|
|
|
@ -10,7 +10,7 @@ from pathlib import Path
|
|||
|
||||
import requests
|
||||
import toml
|
||||
from blitzpy import RaspiBlitzConfig
|
||||
from blitzpy import RaspiBlitzConfig,BlitzError
|
||||
|
||||
#####################
|
||||
# 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>")
|
||||
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
|
||||
#####################
|
||||
|
@ -49,6 +55,7 @@ if cfg.run_behind_tor:
|
|||
# HELPER CLASSES
|
||||
#####################
|
||||
|
||||
# ToDo(frennkie) replace this with updated BlitzError from blitzpy
|
||||
class BlitzError(Exception):
|
||||
def __init__(self, errorShort, errorLong="", errorException=None):
|
||||
self.errorShort = str(errorShort)
|
||||
|
@ -75,19 +82,19 @@ def handleException(e):
|
|||
sys.exit(1)
|
||||
|
||||
|
||||
def getsubdomain(fulldomainstring):
|
||||
return fulldomainstring.split('.')[0]
|
||||
def get_subdomain(fulldomain_str):
|
||||
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))
|
||||
|
||||
# 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:
|
||||
response = session.get(url)
|
||||
if response.status_code != 200:
|
||||
|
@ -102,31 +109,33 @@ def duckDNSupdate(domain, token, ip):
|
|||
# PROCESS FUNCTIONS
|
||||
#####################
|
||||
|
||||
def subscriptionsNew(ip, dnsservice, id, token, target):
|
||||
# id needs to the full domain name
|
||||
def subscriptions_new(ip, dnsservice, id, token, target):
|
||||
# id needs to be the full domain name
|
||||
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
|
||||
if len(getSubscription(id)) > 0:
|
||||
if len(get_subscription(id)) > 0:
|
||||
raise BlitzError("id already exists", id)
|
||||
|
||||
# make sure lets encrypt client is installed
|
||||
os.system("/home/admin/config.scripts/bonus.letsencrypt.sh on")
|
||||
|
||||
# dyndns
|
||||
realip = ip
|
||||
real_ip = ip
|
||||
if ip == "dyndns":
|
||||
updateURL = ""
|
||||
update_url = ""
|
||||
if dnsservice == "duckdns":
|
||||
updateURL = "https://www.duckdns.org/update?domains={0}&token={1}".format(getsubdomain(domain), token, ip)
|
||||
subprocess.run(['/home/admin/config.scriprs/internet.dyndomain.sh', 'on', id, updateURL],
|
||||
# ToDo(frennkie) domain doesn't exit
|
||||
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()
|
||||
realip = cfg.public_ip
|
||||
real_ip = cfg.public_ip
|
||||
|
||||
# update DNS with actual IP
|
||||
if dnsservice == "duckdns":
|
||||
duckDNSupdate(getsubdomain(id), token, realip)
|
||||
duckdns_update(get_subdomain(id), token, real_ip)
|
||||
|
||||
# create subscription data for storage
|
||||
subscription = dict()
|
||||
|
@ -164,10 +173,10 @@ def subscriptionsNew(ip, dnsservice, id, token, target):
|
|||
|
||||
# run the 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],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf8')
|
||||
out, err = acmeResult.communicate()
|
||||
out, err = acme_result.communicate()
|
||||
eprint(str(out))
|
||||
eprint(str(err))
|
||||
if out.find("error=") > -1:
|
||||
|
@ -178,26 +187,24 @@ def subscriptionsNew(ip, dnsservice, id, token, target):
|
|||
return subscription
|
||||
|
||||
|
||||
def subscriptionsCancel(id):
|
||||
# ToDo(frennkie) id is not used..
|
||||
|
||||
def subscriptions_cancel(s_id):
|
||||
os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE))
|
||||
subs = toml.load(SUBSCRIPTIONS_FILE)
|
||||
newList = []
|
||||
removedCert = None
|
||||
new_list = []
|
||||
removed_cert = None
|
||||
for idx, sub in enumerate(subs['subscriptions_letsencrypt']):
|
||||
if sub['id'] != subscriptionID:
|
||||
newList.append(sub)
|
||||
if sub['id'] != s_id:
|
||||
new_list.append(sub)
|
||||
else:
|
||||
removedCert = sub
|
||||
subs['subscriptions_letsencrypt'] = newList
|
||||
removed_cert = sub
|
||||
subs['subscriptions_letsencrypt'] = new_list
|
||||
|
||||
# run the ACME script to remove cert
|
||||
if removedCert:
|
||||
acmeResult = subprocess.Popen(
|
||||
["/home/admin/config.scripts/bonus.letsencrypt.sh", "remove-cert", removedCert['id'],
|
||||
removedCert['target']], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf8')
|
||||
out, err = acmeResult.communicate()
|
||||
if removed_cert:
|
||||
acme_result = subprocess.Popen(
|
||||
["/home/admin/config.scripts/bonus.letsencrypt.sh", "remove-cert", removed_cert['id'],
|
||||
removed_cert['target']], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf8')
|
||||
out, err = acme_result.communicate()
|
||||
if out.find("error=") > -1:
|
||||
time.sleep(6)
|
||||
raise BlitzError("letsencrypt acme failed", out)
|
||||
|
@ -212,7 +219,7 @@ def subscriptionsCancel(id):
|
|||
# todo: deinstall letsencrypt if this was last subscription
|
||||
|
||||
|
||||
def getSubscription(subscriptionID):
|
||||
def get_subscription(subscription_id):
|
||||
try:
|
||||
|
||||
if Path(SUBSCRIPTIONS_FILE).is_file():
|
||||
|
@ -223,7 +230,7 @@ def getSubscription(subscriptionID):
|
|||
if "subscriptions_letsencrypt" not in subs:
|
||||
return []
|
||||
for idx, sub in enumerate(subs['subscriptions_letsencrypt']):
|
||||
if sub['id'] == subscriptionID:
|
||||
if sub['id'] == subscription_id:
|
||||
return sub
|
||||
return []
|
||||
|
||||
|
@ -231,7 +238,7 @@ def getSubscription(subscriptionID):
|
|||
return []
|
||||
|
||||
|
||||
def getDomainByIP(ip):
|
||||
def get_domain_by_ip(ip):
|
||||
# does subscriptin file exists
|
||||
if Path(SUBSCRIPTIONS_FILE).is_file():
|
||||
os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE))
|
||||
|
@ -253,7 +260,7 @@ def getDomainByIP(ip):
|
|||
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
|
||||
from dialog import Dialog
|
||||
|
||||
|
@ -299,7 +306,7 @@ If you havent already go to https://duckdns.org
|
|||
title="DuckDNS Domain")
|
||||
subdomain = text.strip()
|
||||
subdomain = subdomain.split(' ')[0]
|
||||
subdomain = getsubdomain(subdomain)
|
||||
subdomain = get_subdomain(subdomain)
|
||||
domain = "{0}.duckdns.org".format(subdomain)
|
||||
os.system("clear")
|
||||
|
||||
|
@ -326,7 +333,7 @@ This looks not like a valid subdomain.
|
|||
if len(token) < 20:
|
||||
Dialog(dialog="dialog", autowidgetsize=True).msgbox('''
|
||||
This looks not like a valid token.
|
||||
''', title="Unvalid Input")
|
||||
''', title="Invalid Input")
|
||||
sys.exit(0)
|
||||
|
||||
else:
|
||||
|
@ -350,7 +357,7 @@ This looks not like a valid token.
|
|||
"\nChoose the kind of IP you want to use:",
|
||||
choices=choices, width=60, height=10, title="Select Service")
|
||||
|
||||
# if user chosses CANCEL
|
||||
# if user chooses CANCEL
|
||||
os.system("clear")
|
||||
if code != d.OK:
|
||||
sys.exit(0)
|
||||
|
@ -362,16 +369,16 @@ This looks not like a valid token.
|
|||
if tag == "IP2TOR":
|
||||
|
||||
# get all active IP2TOR subscriptions (just in case)
|
||||
ip2torSubs = []
|
||||
ip2tor_subs = []
|
||||
if Path(SUBSCRIPTIONS_FILE).is_file():
|
||||
os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE))
|
||||
subs = toml.load(SUBSCRIPTIONS_FILE)
|
||||
for idx, sub in enumerate(subs['subscriptions_ip2tor']):
|
||||
if sub['active']:
|
||||
ip2torSubs.append(sub)
|
||||
ip2tor_subs.append(sub)
|
||||
|
||||
# when user has no IP2TOR subs yet
|
||||
if len(ip2torSubs) == 0:
|
||||
if len(ip2tor_subs) == 0:
|
||||
Dialog(dialog="dialog", autowidgetsize=True).msgbox('''
|
||||
You have no active IP2TOR subscriptions.
|
||||
Create one first and try again.
|
||||
|
@ -380,7 +387,7 @@ Create one first and try again.
|
|||
|
||||
# let user select a IP2TOR subscription
|
||||
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'])))
|
||||
|
||||
d = Dialog(dialog="dialog", autowidgetsize=True)
|
||||
|
@ -394,8 +401,8 @@ Create one first and try again.
|
|||
sys.exit(0)
|
||||
|
||||
# get the slected IP2TOR bridge
|
||||
ip2torSelect = ip2torSubs[int(tag)]
|
||||
ip = ip2torSelect["ip"]
|
||||
ip2tor_select = ip2tor_subs[int(tag)]
|
||||
ip = ip2tor_select["ip"]
|
||||
target = "tor"
|
||||
|
||||
elif tag == "DYNDNS":
|
||||
|
@ -421,13 +428,13 @@ Create one first and try again.
|
|||
if len(ip) == 0:
|
||||
Dialog(dialog="dialog", autowidgetsize=True).msgbox('''
|
||||
This looks not like a valid IP.
|
||||
''', title="Unvalid Input")
|
||||
''', title="Invalid Input")
|
||||
sys.exit(0)
|
||||
|
||||
# create the letsencrypt subscription
|
||||
try:
|
||||
os.system("clear")
|
||||
subscription = subscriptionsNew(ip, dnsservice, domain, token, target)
|
||||
subscription = subscriptions_new(ip, dnsservice, domain, token, target)
|
||||
|
||||
# success dialog
|
||||
Dialog(dialog="dialog", autowidgetsize=True).msgbox('''
|
||||
|
@ -455,19 +462,15 @@ Unknown Error happened - please report to developers:
|
|||
# CREATE SSH DIALOG
|
||||
# use for ssh shell menu
|
||||
###############
|
||||
def create_ssh_dialog():
|
||||
menu_make_subscription()
|
||||
|
||||
if sys.argv[1] == "create-ssh-dialog":
|
||||
menuMakeSubscription()
|
||||
|
||||
sys.exit()
|
||||
|
||||
##########################
|
||||
# SUBSCRIPTIONS NEW
|
||||
# call from web interface
|
||||
##########################
|
||||
|
||||
if sys.argv[1] == "subscription-new":
|
||||
|
||||
def subscription_new():
|
||||
# check parameters
|
||||
try:
|
||||
if len(sys.argv) <= 5:
|
||||
|
@ -486,7 +489,7 @@ if sys.argv[1] == "subscription-new":
|
|||
|
||||
# create the subscription
|
||||
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
|
||||
print(json.dumps(subscription, indent=2))
|
||||
|
@ -495,12 +498,11 @@ if sys.argv[1] == "subscription-new":
|
|||
except Exception as e:
|
||||
handleException(e)
|
||||
|
||||
|
||||
#######################
|
||||
# SUBSCRIPTIONS LIST
|
||||
#######################
|
||||
|
||||
if sys.argv[1] == "subscriptions-list":
|
||||
|
||||
def subscriptions_list():
|
||||
try:
|
||||
|
||||
if Path(SUBSCRIPTIONS_FILE).is_file():
|
||||
|
@ -515,13 +517,11 @@ if sys.argv[1] == "subscriptions-list":
|
|||
except Exception as e:
|
||||
handleException(e)
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
#######################
|
||||
# SUBSCRIPTION DETAIL
|
||||
#######################
|
||||
if sys.argv[1] == "subscription-detail":
|
||||
|
||||
def subscription_detail():
|
||||
# check parameters
|
||||
try:
|
||||
if len(sys.argv) <= 2:
|
||||
|
@ -529,22 +529,20 @@ if sys.argv[1] == "subscription-detail":
|
|||
except Exception as e:
|
||||
handleException(e)
|
||||
|
||||
subscriptionID = sys.argv[2]
|
||||
subscription_id = sys.argv[2]
|
||||
try:
|
||||
sub = getSubscription(subscriptionID)
|
||||
sub = get_subscription(subscription_id)
|
||||
print(json.dumps(sub, indent=2))
|
||||
|
||||
except Exception as e:
|
||||
handleException(e)
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
#######################
|
||||
# DOMAIN BY IP
|
||||
# to check if an ip has a domain mapping
|
||||
#######################
|
||||
if sys.argv[1] == "domain-by-ip":
|
||||
|
||||
def domain_by_ip():
|
||||
# check parameters
|
||||
try:
|
||||
if len(sys.argv) <= 2:
|
||||
|
@ -556,19 +554,17 @@ if sys.argv[1] == "domain-by-ip":
|
|||
ip = sys.argv[2]
|
||||
try:
|
||||
|
||||
domain = getDomainByIP(ip)
|
||||
domain = get_domain_by_ip(ip)
|
||||
print("domain='{0}'".format(domain))
|
||||
|
||||
except Exception as e:
|
||||
handleException(e)
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
#######################
|
||||
# SUBSCRIPTION CANCEL
|
||||
#######################
|
||||
if sys.argv[1] == "subscription-cancel":
|
||||
|
||||
def subscription_cancel():
|
||||
# check parameters
|
||||
try:
|
||||
if len(sys.argv) <= 2:
|
||||
|
@ -576,13 +572,36 @@ if sys.argv[1] == "subscription-cancel":
|
|||
except Exception as e:
|
||||
handleException(e)
|
||||
|
||||
subscriptionID = sys.argv[2]
|
||||
subscription_id = sys.argv[2]
|
||||
try:
|
||||
subscriptionsCancel(subscriptionID)
|
||||
subscriptions_cancel(subscription_id)
|
||||
except Exception as e:
|
||||
handleException(e)
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# unknown command
|
||||
print("# unknown command")
|
||||
def main():
|
||||
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()
|
||||
|
|
|
@ -15,10 +15,10 @@ from blitzpy import RaspiBlitzConfig
|
|||
from dialog import Dialog
|
||||
|
||||
# constants for standard services
|
||||
LND_REST_API = "LND-REST-API"
|
||||
LND_GRPC_API = "LND-GRPC-API"
|
||||
LNBITS = "LNBITS"
|
||||
BTCPAY = "BTCPAY"
|
||||
SERVICE_LND_REST_API = "LND-REST-API"
|
||||
SERVICE_LND_GRPC_API = "LND-GRPC-API"
|
||||
SERVICE_LNBITS = "LNBITS"
|
||||
SERVICE_BTCPAY = "BTCPAY"
|
||||
|
||||
# load config
|
||||
cfg = RaspiBlitzConfig()
|
||||
|
@ -32,35 +32,38 @@ SUBSCRIPTIONS_FILE = "/mnt/hdd/app-data/subscriptions/subscriptions.toml"
|
|||
# HELPER FUNCTIONS
|
||||
#######################
|
||||
|
||||
# ToDo(frennkie) these are not being used!
|
||||
|
||||
def eprint(*args, **kwargs):
|
||||
print(*args, file=sys.stderr, **kwargs)
|
||||
|
||||
|
||||
def parseDateIP2TORSERVER(datestr):
|
||||
return datetime.strptime(datestr, "%Y-%m-%dT%H:%M:%S.%fZ")
|
||||
def parse_date_ip2tor(date_str):
|
||||
return datetime.strptime(date_str, "%Y-%m-%dT%H:%M:%S.%fZ")
|
||||
|
||||
|
||||
def secondsLeft(dateObj):
|
||||
return round((dateObj - datetime.utcnow()).total_seconds())
|
||||
def seconds_left(date_obj):
|
||||
return round((date_obj - datetime.utcnow()).total_seconds())
|
||||
|
||||
|
||||
#######################
|
||||
# SSH MENU FUNCTIONS
|
||||
#######################
|
||||
|
||||
def mySubscriptions():
|
||||
def my_subscriptions():
|
||||
# check if any subscriptions are available
|
||||
countSubscriptions = 0
|
||||
count_subscriptions = 0
|
||||
try:
|
||||
os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE))
|
||||
subs = toml.load(SUBSCRIPTIONS_FILE)
|
||||
if 'subscriptions_ip2tor' in subs:
|
||||
countSubscriptions += len(subs['subscriptions_ip2tor'])
|
||||
count_subscriptions += len(subs['subscriptions_ip2tor'])
|
||||
if 'subscriptions_letsencrypt' in subs:
|
||||
countSubscriptions += len(subs['subscriptions_letsencrypt'])
|
||||
count_subscriptions += len(subs['subscriptions_letsencrypt'])
|
||||
except Exception as e:
|
||||
pass
|
||||
if countSubscriptions == 0:
|
||||
print(f"warning: {e}")
|
||||
|
||||
if count_subscriptions == 0:
|
||||
Dialog(dialog="dialog", autowidgetsize=True).msgbox('''
|
||||
You have no active or inactive subscriptions.
|
||||
''', title="Info")
|
||||
|
@ -69,36 +72,36 @@ You have no active or inactive subscriptions.
|
|||
# load subscriptions and make dialog choices out of it
|
||||
choices = []
|
||||
lookup = {}
|
||||
lookupIndex = 0
|
||||
lookup_index = 0
|
||||
subs = toml.load(SUBSCRIPTIONS_FILE)
|
||||
|
||||
# list ip2tor subscriptions
|
||||
if 'subscriptions_ip2tor' in subs:
|
||||
for sub in subs['subscriptions_ip2tor']:
|
||||
# remember subscription under lookupindex
|
||||
lookupIndex += 1
|
||||
lookup[str(lookupIndex)] = sub
|
||||
lookup_index += 1
|
||||
lookup[str(lookup_index)] = sub
|
||||
# add to dialog choices
|
||||
if sub['active']:
|
||||
activeState = "active"
|
||||
active_state = "active"
|
||||
else:
|
||||
activeState = "in-active"
|
||||
active_state = "in-active"
|
||||
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
|
||||
if 'subscriptions_letsencrypt' in subs:
|
||||
for sub in subs['subscriptions_letsencrypt']:
|
||||
# remember subscription under lookupindex
|
||||
lookupIndex += 1
|
||||
lookup[str(lookupIndex)] = sub
|
||||
lookup_index += 1
|
||||
lookup[str(lookup_index)] = sub
|
||||
# add to dialog choices
|
||||
if sub['active']:
|
||||
activeState = "active"
|
||||
active_state = "active"
|
||||
else:
|
||||
activeState = "in-active"
|
||||
active_state = "in-active"
|
||||
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
|
||||
d = Dialog(dialog="dialog", autowidgetsize=True)
|
||||
|
@ -111,15 +114,15 @@ You have no active or inactive subscriptions.
|
|||
if code != d.OK:
|
||||
return
|
||||
|
||||
# get data of selected subscrption
|
||||
selectedSub = lookup[str(tag)]
|
||||
# get data of selected subscription
|
||||
selected_sub = lookup[str(tag)]
|
||||
|
||||
# show details of selected
|
||||
d = Dialog(dialog="dialog", autowidgetsize=True)
|
||||
d.set_background_title("My Subscriptions")
|
||||
if selectedSub['type'] == "letsencrypt-v1":
|
||||
if len(selectedSub['warning']) > 0:
|
||||
selectedSub['warning'] = "\n{0}".format(selectedSub['warning'])
|
||||
if selected_sub['type'] == "letsencrypt-v1":
|
||||
if len(selected_sub['warning']) > 0:
|
||||
selected_sub['warning'] = "\n{0}".format(selected_sub['warning'])
|
||||
text = '''
|
||||
This is a LetsEncrypt subscription using the free DNS service
|
||||
{dnsservice}
|
||||
|
@ -135,17 +138,17 @@ The state of the subscription is: {active} {warning}
|
|||
The following additional information is available:
|
||||
{description}
|
||||
|
||||
'''.format(dnsservice=selectedSub['dnsservice_type'],
|
||||
domain=selectedSub['id'],
|
||||
ip=selectedSub['ip'],
|
||||
active="ACTIVE" if selectedSub['active'] else "NOT ACTIVE",
|
||||
warning=selectedSub['warning'],
|
||||
description=selectedSub['description']
|
||||
'''.format(dnsservice=selected_sub['dnsservice_type'],
|
||||
domain=selected_sub['id'],
|
||||
ip=selected_sub['ip'],
|
||||
active="ACTIVE" if selected_sub['active'] else "NOT ACTIVE",
|
||||
warning=selected_sub['warning'],
|
||||
description=selected_sub['description']
|
||||
)
|
||||
|
||||
elif selectedSub['type'] == "ip2tor-v1":
|
||||
if len(selectedSub['warning']) > 0:
|
||||
selectedSub['warning'] = "\n{0}".format(selectedSub['warning'])
|
||||
elif selected_sub['type'] == "ip2tor-v1":
|
||||
if len(selected_sub['warning']) > 0:
|
||||
selected_sub['warning'] = "\n{0}".format(selected_sub['warning'])
|
||||
text = '''
|
||||
This is a IP2TOR subscription bought on {initdate} at
|
||||
{shop}
|
||||
|
@ -161,26 +164,26 @@ The state of the subscription is: {active} {warning}
|
|||
|
||||
The following additional information is available:
|
||||
{description}
|
||||
'''.format(initdate=selectedSub['time_created'],
|
||||
shop=selectedSub['shop'],
|
||||
publicaddress="{0}:{1}".format(selectedSub['ip'], selectedSub['port']),
|
||||
toraddress=selectedSub['tor'],
|
||||
renewhours=(round(int(selectedSub['duration']) / 3600)),
|
||||
renewsats=(round(int(selectedSub['price_extension']) / 1000)),
|
||||
totalsats=(round(int(selectedSub['price_total']) / 1000)),
|
||||
active="ACTIVE" if selectedSub['active'] else "NOT ACTIVE",
|
||||
warning=selectedSub['warning'],
|
||||
description=selectedSub['description'],
|
||||
service=selectedSub['name']
|
||||
'''.format(initdate=selected_sub['time_created'],
|
||||
shop=selected_sub['shop'],
|
||||
publicaddress="{0}:{1}".format(selected_sub['ip'], selected_sub['port']),
|
||||
toraddress=selected_sub['tor'],
|
||||
renewhours=(round(int(selected_sub['duration']) / 3600)),
|
||||
renewsats=(round(int(selected_sub['price_extension']) / 1000)),
|
||||
totalsats=(round(int(selected_sub['price_total']) / 1000)),
|
||||
active="ACTIVE" if selected_sub['active'] else "NOT ACTIVE",
|
||||
warning=selected_sub['warning'],
|
||||
description=selected_sub['description'],
|
||||
service=selected_sub['name']
|
||||
)
|
||||
else:
|
||||
text = "no text?! FIXME"
|
||||
|
||||
if selectedSub['active']:
|
||||
extraLable = "CANCEL SUBSCRIPTION"
|
||||
if selected_sub['active']:
|
||||
extra_label = "CANCEL SUBSCRIPTION"
|
||||
else:
|
||||
extraLable = "DELETE SUBSCRIPTION"
|
||||
code = d.msgbox(text, title="Subscription Detail", ok_label="Back", extra_button=True, extra_label=extraLable,
|
||||
extra_label = "DELETE SUBSCRIPTION"
|
||||
code = d.msgbox(text, title="Subscription Detail", ok_label="Back", extra_button=True, extra_label=extra_label,
|
||||
width=75, height=30)
|
||||
|
||||
# user wants to delete this subscription
|
||||
|
@ -188,15 +191,15 @@ The following additional information is available:
|
|||
# api calls when canceling
|
||||
if code == "extra":
|
||||
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(
|
||||
selectedSub['id'])
|
||||
selected_sub['id'])
|
||||
print("# running: {0}".format(cmd))
|
||||
os.system(cmd)
|
||||
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(
|
||||
selectedSub['id'])
|
||||
selected_sub['id'])
|
||||
print("# running: {0}".format(cmd))
|
||||
os.system(cmd)
|
||||
time.sleep(2)
|
||||
|
@ -205,183 +208,188 @@ The following additional information is available:
|
|||
time.sleep(3)
|
||||
|
||||
# loop until no more subscriptions or user chooses CANCEL on subscription list
|
||||
mySubscriptions()
|
||||
my_subscriptions()
|
||||
|
||||
|
||||
#######################
|
||||
# SSH MENU
|
||||
#######################
|
||||
def main():
|
||||
#######################
|
||||
# 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.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 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"))
|
||||
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(
|
||||
"\nChoose RaspiBlitz Service to create Bridge for:",
|
||||
choices=choices, width=60, height=10, title="Select Service")
|
||||
"\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)
|
||||
|
||||
servicename = None
|
||||
torAddress = None
|
||||
torPort = None
|
||||
if tag == "REST":
|
||||
# get TOR address for REST
|
||||
servicename = LND_REST_API
|
||||
torAddress = subprocess.run(['sudo', 'cat', '/mnt/hdd/tor/lndrest8080/hostname'],
|
||||
stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
|
||||
torPort = 8080
|
||||
if tag == "GRPC":
|
||||
# get TOR address for GRPC
|
||||
servicename = LND_GRPC_API
|
||||
torAddress = subprocess.run(['sudo', 'cat', '/mnt/hdd/tor/lndrpc10009/hostname'],
|
||||
stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
|
||||
torPort = 10009
|
||||
if tag == "LNBITS":
|
||||
# get TOR address for LNBits
|
||||
servicename = LNBITS
|
||||
torAddress = subprocess.run(['sudo', 'cat', '/mnt/hdd/tor/lnbits/hostname'],
|
||||
stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
|
||||
torPort = 443
|
||||
if tag == "BTCPAY":
|
||||
# get TOR address for BTCPAY
|
||||
servicename = BTCPAY
|
||||
torAddress = subprocess.run(['sudo', 'cat', '/mnt/hdd/tor/btcpay/hostname'],
|
||||
stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
|
||||
torPort = 443
|
||||
if tag == "SELF":
|
||||
servicename = "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)
|
||||
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)
|
||||
#######################
|
||||
# MANAGE SUBSCRIPTIONS
|
||||
#######################
|
||||
|
||||
if tag == "LIST":
|
||||
my_subscriptions()
|
||||
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)
|
||||
|
||||
# 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(
|
||||
servicename, torAddress, torPort)
|
||||
print("# running: {0}".format(cmd))
|
||||
os.system(cmd)
|
||||
sys.exit(0)
|
||||
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'] == 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()
|
||||
|
|
Loading…
Add table
Reference in a new issue