From 4a5fe2f73c195a603e7be9daa8b510da8a9f5d8b Mon Sep 17 00:00:00 2001 From: Taylor Helsper Date: Fri, 25 Feb 2022 12:54:42 -0600 Subject: [PATCH] Large python refactor to use 'pynode' and Premium+ basics --- .../etc/systemd/system/check_in.service | 1 + .../etc/systemd/system/lnd_backup.service | 5 +- rootfs/standard/usr/bin/clone_drive.py | 82 +-------- rootfs/standard/usr/bin/mynode_backup_scb.py | 143 ++++++++++++++++ rootfs/standard/usr/bin/mynode_check_in.py | 70 +------- .../usr/bin/mynode_lnd_channel_backup.sh | 1 + .../standard/usr/bin/mynode_post_upgrade.sh | 7 +- .../usr/bin/mynode_premium_plus_connect.py | 101 ++---------- rootfs/standard/usr/bin/mynode_startup.sh | 5 + rootfs/standard/usr/bin/mynode_usb_extras.py | 77 +-------- .../usr/share/mynode/application_info.json | 6 +- .../{www/mynode => pynode}/bitcoin_info.py | 0 .../var/{www/mynode => pynode}/config.py | 0 .../var/{www/mynode => pynode}/device_info.py | 105 +++++++++--- rootfs/standard/var/pynode/drive_info.py | 137 +++++++++++++++ .../{www/mynode => pynode}/electrum_info.py | 0 .../enable_disable_functions.py | 0 .../{www/mynode => pynode}/lightning_info.py | 3 - .../{www/mynode => pynode}/systemctl_info.py | 4 +- .../var/{www/mynode => pynode}/utilities.py | 29 +++- .../var/www/mynode/application_info.py | 4 +- rootfs/standard/var/www/mynode/bitcoin.py | 1 - rootfs/standard/var/www/mynode/mynode.py | 4 + .../standard/var/www/mynode/premium_plus.py | 60 +++++++ rootfs/standard/var/www/mynode/settings.py | 3 +- .../www/mynode/templates/premium_plus.html | 156 ++++++++++++++++++ 26 files changed, 654 insertions(+), 350 deletions(-) create mode 100755 rootfs/standard/usr/bin/mynode_backup_scb.py rename rootfs/standard/var/{www/mynode => pynode}/bitcoin_info.py (100%) rename rootfs/standard/var/{www/mynode => pynode}/config.py (100%) rename rootfs/standard/var/{www/mynode => pynode}/device_info.py (91%) create mode 100644 rootfs/standard/var/pynode/drive_info.py rename rootfs/standard/var/{www/mynode => pynode}/electrum_info.py (100%) rename rootfs/standard/var/{www/mynode => pynode}/enable_disable_functions.py (100%) rename rootfs/standard/var/{www/mynode => pynode}/lightning_info.py (99%) rename rootfs/standard/var/{www/mynode => pynode}/systemctl_info.py (92%) rename rootfs/standard/var/{www/mynode => pynode}/utilities.py (85%) create mode 100644 rootfs/standard/var/www/mynode/premium_plus.py create mode 100644 rootfs/standard/var/www/mynode/templates/premium_plus.html diff --git a/rootfs/standard/etc/systemd/system/check_in.service b/rootfs/standard/etc/systemd/system/check_in.service index a2b2b685..1e131965 100644 --- a/rootfs/standard/etc/systemd/system/check_in.service +++ b/rootfs/standard/etc/systemd/system/check_in.service @@ -8,6 +8,7 @@ After=network-online.target [Service] Type=simple +KillMode=control-group ExecStart=/usr/local/bin/python3 /usr/bin/mynode_check_in.py User=root Group=root diff --git a/rootfs/standard/etc/systemd/system/lnd_backup.service b/rootfs/standard/etc/systemd/system/lnd_backup.service index c335a00d..69df5d36 100644 --- a/rootfs/standard/etc/systemd/system/lnd_backup.service +++ b/rootfs/standard/etc/systemd/system/lnd_backup.service @@ -8,7 +8,10 @@ After=lnd.service [Service] Type=simple -ExecStart=/usr/bin/mynode_lnd_channel_backup.sh +ExecStartPre=/usr/bin/is_not_shutting_down.sh +ExecStartPre=/usr/bin/wait_on_bitcoin.sh +ExecStartPre=/usr/bin/wait_on_lnd.sh +ExecStart=/usr/local/bin/python3 /usr/bin/mynode_backup_scb.py Restart=always RestartSec=1 User=bitcoin diff --git a/rootfs/standard/usr/bin/clone_drive.py b/rootfs/standard/usr/bin/clone_drive.py index dbcca71f..a582fbb0 100755 --- a/rootfs/standard/usr/bin/clone_drive.py +++ b/rootfs/standard/usr/bin/clone_drive.py @@ -2,17 +2,15 @@ import time import os import subprocess -import signal import logging -from threading import Thread +from systemd import journal +from utilities import * +from drive_info import * log = logging.getLogger('mynode') +log.addHandler(journal.JournaldLogHandler()) log.setLevel(logging.INFO) - -def log_message(msg): - global log - print(msg) - log.info(msg) +set_logger(log) def set_clone_state(state): log_message("Clone State: {}".format(state)) @@ -49,74 +47,6 @@ def wait_on_clone_error_dismiss(): while os.path.isfile("/tmp/.clone_error"): time.sleep(1) -def get_drive_size(drive): - size = -1 - try: - lsblk_output = subprocess.check_output(f"lsblk -b /dev/{drive} | grep disk", shell=True).decode("utf-8") - parts = lsblk_output.split() - size = int(parts[3]) - except: - pass - log_message(f"Drive {drive} size: {size}") - return size - - -def check_partition_for_mynode(partition): - is_mynode = False - try: - subprocess.check_output(f"mount -o ro /dev/{partition} /mnt/hdd", shell=True) - if os.path.isfile("/mnt/hdd/.mynode"): - is_mynode = True - except Exception as e: - # Mount failed, could be target drive - pass - finally: - time.sleep(1) - os.system("umount /mnt/hdd") - - return is_mynode - -def find_partitions_for_drive(drive): - partitions = [] - try: - ls_output = subprocess.check_output(f"ls /sys/block/{drive}/ | grep {drive}", shell=True).decode("utf-8") - partitions = ls_output.split() - except: - pass - return partitions - -def is_drive_detected_by_fdisk(d): - detected = False - try: - # Command fails and throws exception if not mounted - ls_output = subprocess.check_output(f"fdisk -l /dev/{d}", shell=True).decode("utf-8") - detected = True - except: - pass - return detected - -def is_drive_mounted(d): - mounted = True - try: - # Command fails and throws exception if not mounted - ls_output = subprocess.check_output(f"grep -qs '/dev/{d}' /proc/mounts", shell=True).decode("utf-8") - except: - mounted = False - return mounted - -def find_drives(): - drives = [] - try: - ls_output = subprocess.check_output("ls /sys/block/ | egrep 'hd.*|vd.*|sd.*|nvme.*'", shell=True).decode("utf-8") - all_drives = ls_output.split() - - # Only return drives that are not mounted (VM may have /dev/sda as OS drive) - for d in all_drives: - if is_drive_detected_by_fdisk(d) and not is_drive_mounted(d): - drives.append(d) - except: - pass - return drives def main(): # Set initial state @@ -130,7 +60,7 @@ def main(): os.system("rm /tmp/.clone_target_drive_has_mynode") # Detect drives - drives = find_drives() + drives = find_unmounted_drives() log_message(f"Drives: {drives}") # Check exactly two drives found diff --git a/rootfs/standard/usr/bin/mynode_backup_scb.py b/rootfs/standard/usr/bin/mynode_backup_scb.py new file mode 100755 index 00000000..ceab1b32 --- /dev/null +++ b/rootfs/standard/usr/bin/mynode_backup_scb.py @@ -0,0 +1,143 @@ +#!/usr/local/bin/python3 +import os +import requests +import time +import subprocess +import logging +import shutil +from utilities import * +from drive_info import * +from device_info import * +from inotify_simple import INotify, flags +from systemd import journal +import random + +BACKUP_SCB_URL = "https://www.mynodebtc.com/device_api/backup_scb.php" + +LND_MAINNET_CHANNEL_FILE = "/mnt/hdd/mynode/lnd/data/chain/bitcoin/mainnet/channel.backup" +LND_MAINNET_CHANNEL_FILE_BACKUP = "/home/bitcoin/lnd_backup/channel.backup" +LND_TESTNET_CHANNEL_FILE = "/mnt/hdd/mynode/lnd/data/chain/bitcoin/testnet/channel.backup" +LND_TESTNET_CHANNEL_FILE_BACKUP = "/home/bitcoin/lnd_backup/channel_testnet.backup" + + +log = logging.getLogger('lndbackup') +log.addHandler(journal.JournaldLogHandler()) +log.setLevel(logging.INFO) +set_logger(log) + +# Helper functions + + +# Local Backup +def local_backup(original_scb, backup_scb): + if os.path.isfile(original_scb): + md5_1 = get_md5_file_hash(original_scb) + md5_2 = "REPLACE_FILE" + if os.path.isfile(backup_scb): + md5_2 = get_md5_file_hash(backup_scb) + + log_message(" Hash 1: {}".format(md5_1)) + log_message(" Hash 2: {}".format(md5_2)) + + # If file is missing or different, back it up! + if md5_1 != md5_2: + shutil.copyfile(original_scb, backup_scb) + log_message("Local Backup: Backup Updated!") + else: + log_message("Local Backup: Hashes Match. Skipping Backup.") + else: + log_message("Local Backup: Missing File") + +# Remote Backup +def remote_backup(original, backup): + + # Mainnet only + if is_testnet_enabled(): + log_message("Remote Backup: Skipping (testnet enabled") + return + + # Premium+ Feature + if not has_premium_plus_token() or get_premium_plus_token_status != "OK": + log_message("Remote Backup: Skipping (not Premium+)") + return + + # POST Data + data = { + "token": get_premium_plus_token(), + "product_key": get_product_key(), + "scb_file": "REPLACE ME WITH FILE CONTENTS" + } + + # Setup tor proxy + session = requests.session() + session.proxies = {} + session.proxies['http'] = 'socks5h://localhost:9050' + session.proxies['https'] = 'socks5h://localhost:9050' + + # Backup to server + fail_count = 0 + backup_success = False + while not backup_success: + try: + # Use tor for check in unless there have been tor 5 failures in a row + r = None + if (fail_count+1) % 5 == 0: + r = requests.post(BACKUP_SCB_URL, data=data, timeout=20) + else: + r = session.post(BACKUP_SCB_URL, data=data, timeout=20) + + if r.status_code == 200: + if r.text == "OK": + log_message("Remote Backup: Success ({})".format(r.text)) + else: + log_message("Remote Backup: Error: ({})".format(r.text)) + backup_success = True + else: + log_message("Remote Backup: Connect Failed. Retrying... Code {}".format(r.status_code)) + except Exception as e: + log_message("Remote Backup: Connect Failed. Retrying... Exception {}".format(e)) + + if not backup_success: + # Check in failed, try again in 1 minute + time.sleep(60) + fail_count = fail_count + 1 + + return True + +def backup(original_scb, backup_scb): + log_message("Backing up SCB file...") + local_backup(original_scb, backup_scb) + remote_backup(original_scb, backup_scb) + log_message("Backup Complete.") + +# Backup SCB file +if __name__ == "__main__": + one_hour_in_ms = 60 * 60 * 1000 + + while True: + try: + # Wait for drive to be mounted + while not is_mynode_drive_mounted(): + log_message("Checking if drive mounted...") + time.sleep(10) + log_message("Drive mounted!") + + # Determine backup file + original_scb = LND_MAINNET_CHANNEL_FILE + backup_scb = LND_MAINNET_CHANNEL_FILE_BACKUP + if is_testnet_enabled(): + original_scb = LND_TESTNET_CHANNEL_FILE + backup_scb = LND_TESTNET_CHANNEL_FILE_BACKUP + + # Perform backup + backup(original_scb, backup_scb) + + # Watch for updates + inotify = INotify() + watch_flags = flags.CREATE | flags.DELETE | flags.MODIFY | flags.DELETE_SELF + wd = inotify.add_watch(original_scb, watch_flags) + for event in inotify.read(timeout=one_hour_in_ms): + log_message("File changed: " + str(event)) + except Exception as e: + log_message("Error: {}".format(e)) + time.sleep(60) diff --git a/rootfs/standard/usr/bin/mynode_check_in.py b/rootfs/standard/usr/bin/mynode_check_in.py index eee0f685..be06f6d3 100755 --- a/rootfs/standard/usr/bin/mynode_check_in.py +++ b/rootfs/standard/usr/bin/mynode_check_in.py @@ -4,79 +4,17 @@ import requests import time import subprocess import random +from drive_info import * +from device_info import * CHECKIN_URL = "https://www.mynodebtc.com/device_api/check_in.php" latest_version_check_count = 0 # Helper functions -def unset_skipped_product_key(): - os.system("rm -rf /home/bitcoin/.mynode/.product_key_skipped") - os.system("rm -rf /mnt/hdd/mynode/settings/.product_key_skipped") -def delete_product_key_error(): - os.system("rm -rf /home/bitcoin/.mynode/.product_key_error") - os.system("rm -rf /mnt/hdd/mynode/settings/.product_key_error") -def has_product_key_error(): - if os.path.isfile("/home/bitcoin/.mynode/.product_key_error") or os.path.isfile("/mnt/hdd/mynode/settings/.product_key_error"): - return True - return False -def get_current_version(): - current_version = "0.0" - try: - with open("/usr/share/mynode/version", "r") as f: - current_version = f.read().strip() - except: - current_version = "error" - return current_version -def get_device_type(): - device = subprocess.check_output("mynode-get-device-type", shell=True).decode("utf-8").strip() - return device -def get_device_arch(): - arch = subprocess.check_output("uname -m", shell=True).decode("utf-8").strip() - return arch -def get_device_serial(): - serial = subprocess.check_output("mynode-get-device-serial", shell=True).decode("utf-8").strip() - return serial -def skipped_product_key(): - return os.path.isfile("/home/bitcoin/.mynode/.product_key_skipped") or \ - os.path.isfile("/mnt/hdd/mynode/settings/.product_key_skipped") -def has_product_key(): - return os.path.isfile("/home/bitcoin/.mynode/.product_key") -def get_product_key(): - product_key = "no_product_key" - if skipped_product_key(): - return "community_edition" - - if not has_product_key(): - return "product_key_missing" - - try: - with open("/home/bitcoin/.mynode/.product_key", "r") as f: - product_key = f.read().strip() - except: - product_key = "product_key_error" - return product_key -def is_drive_mounted(): - mounted = True - try: - # Command fails and throws exception if not mounted - output = subprocess.check_output(f"grep -qs '/mnt/hdd ext4' /proc/mounts", shell=True).decode("utf-8") - except: - mounted = False - return mounted -def get_drive_size(): - size = -1 - if not is_drive_mounted(): - return -3 - try: - size = subprocess.check_output("df /mnt/hdd | grep /dev | awk '{print $2}'", shell=True).strip() - size = int(size) / 1000 / 1000 - except Exception as e: - size = -2 - return size def get_quicksync_enabled(): enabled = 1 - if not is_drive_mounted(): + if not is_mynode_drive_mounted(): return -3 if os.path.isfile("/mnt/hdd/mynode/settings/quicksync_disabled"): enabled = 0 @@ -117,7 +55,7 @@ def check_in(): "device_arch": get_device_arch(), "version": get_current_version(), "product_key": product_key, - "drive_size": get_drive_size(), + "drive_size": get_mynode_drive_size(), "quicksync_enabled": get_quicksync_enabled(), } diff --git a/rootfs/standard/usr/bin/mynode_lnd_channel_backup.sh b/rootfs/standard/usr/bin/mynode_lnd_channel_backup.sh index 4dae58f3..f4fdeca3 100755 --- a/rootfs/standard/usr/bin/mynode_lnd_channel_backup.sh +++ b/rootfs/standard/usr/bin/mynode_lnd_channel_backup.sh @@ -1,4 +1,5 @@ #!/bin/bash +# NO LONGER USED - MIGRATED TO PYTHON source /usr/share/mynode/mynode_config.sh diff --git a/rootfs/standard/usr/bin/mynode_post_upgrade.sh b/rootfs/standard/usr/bin/mynode_post_upgrade.sh index e86c19ff..fee8f4e6 100755 --- a/rootfs/standard/usr/bin/mynode_post_upgrade.sh +++ b/rootfs/standard/usr/bin/mynode_post_upgrade.sh @@ -206,12 +206,15 @@ if ! skip_base_upgrades ; then [ -d /usr/local/lib/python3.8/site-packages ] && echo "/var/pynode" > /usr/local/lib/python3.8/site-packages/pynode.pth # Remove old python files so new copies are used (files migrated to pynode) + set +x PYNODE_FILES="/var/pynode/*.py" for pynode_file in $PYNODE_FILES - do + echo "Migrating pynode file $pynode_file..." pynode_file="$(basename -- $pynode_file)" - rm -f /var/www/mynode/$pynode_file* + rm -f /var/www/mynode/${pynode_file} # .py + rm -f /var/www/mynode/${pynode_file}c # .pyc done + set -x # Install any pip3 software diff --git a/rootfs/standard/usr/bin/mynode_premium_plus_connect.py b/rootfs/standard/usr/bin/mynode_premium_plus_connect.py index 86cae9d2..50f112ea 100755 --- a/rootfs/standard/usr/bin/mynode_premium_plus_connect.py +++ b/rootfs/standard/usr/bin/mynode_premium_plus_connect.py @@ -6,99 +6,25 @@ import subprocess import logging from inotify_simple import INotify, flags from systemd import journal -import random +from utilities import * +from device_info import * +from drive_info import * + PREMIUM_PLUS_CONNECT_URL = "https://www.mynodebtc.com/device_api/premium_plus_connect.php" log = logging.getLogger('premium_plus_connect') log.addHandler(journal.JournaldLogHandler()) log.setLevel(logging.INFO) +set_logger(log) # Helper functions -def log_message(msg): - global log - print(msg) - log.info(msg) -def set_premium_plus_token_error(msg): - os.system("echo '{}' > /home/bitcoin/.mynode/.premium_plus_token_error".format(msg)) -def delete_premium_plus_token_error(): - os.system("rm -rf /home/bitcoin/.mynode/.premium_plus_token_error") - os.system("rm -rf /mnt/hdd/mynode/settings/.premium_plus_token_error") -def has_premium_plus_token_error(): - if os.path.isfile("/home/bitcoin/.mynode/.premium_plus_token_error") or \ - os.path.isfile("/mnt/hdd/mynode/settings/.premium_plus_token_error"): - return True - return False - -def get_current_version(): - current_version = "0.0" - try: - with open("/usr/share/mynode/version", "r") as f: - current_version = f.read().strip() - except: - current_version = "error" - return current_version -def get_device_type(): - device = subprocess.check_output("mynode-get-device-type", shell=True).decode("utf-8").strip() - return device -def get_device_arch(): - arch = subprocess.check_output("uname -m", shell=True).decode("utf-8").strip() - return arch -def get_device_serial(): - serial = subprocess.check_output("mynode-get-device-serial", shell=True).decode("utf-8").strip() - return serial -def has_premium_plus_token(): - return os.path.isfile("/home/bitcoin/.mynode/.premium_plus_token") or \ - os.path.isfile("/mnt/hdd/mynode/settings/.premium_plus_token") -def get_premium_plus_token(): - token = "no_token" - if not has_premium_plus_token(): - return "no_token" - - try: - if os.path.isfile("/home/bitcoin/.mynode/.premium_plus_token"): - with open("/home/bitcoin/.mynode/.premium_plus_token", "r") as f: - token = f.read().strip() - elif os.path.isfile("/mnt/hdd/mynode/settings/.premium_plus_token"): - with open("/mnt/hdd/mynode/settings/.premium_plus_token", "r") as f: - token = f.read().strip() - except: - token = "token_error" - return token -def get_product_key(): - try: - with open("/home/bitcoin/.mynode/.product_key", "r") as f: - product_key = f.read().strip() - except: - product_key = "product_key_error" - return product_key -def is_drive_mounted(): - mounted = True - try: - # Command fails and throws exception if not mounted - output = subprocess.check_output(f"grep -qs '/mnt/hdd ext4' /proc/mounts", shell=True).decode("utf-8") - except: - mounted = False - return mounted -def get_drive_size(): - size = -1 - if not is_drive_mounted(): - return -3 - try: - size = subprocess.check_output("df /mnt/hdd | grep /dev | awk '{print $2}'", shell=True).strip() - size = int(size) / 1000 / 1000 - except Exception as e: - size = -2 - return size -def get_drive_usage(): - return "TODO" # Update hourly def premium_plus_connect(): # Check in - token = get_premium_plus_token() data = { "serial": get_device_serial(), "device_type": get_device_type(), @@ -106,8 +32,8 @@ def premium_plus_connect(): "version": get_current_version(), "token": get_premium_plus_token(), "product_key": get_product_key(), - "drive_size": get_drive_size(), - "drive_usage": get_drive_usage(), + "drive_size": get_mynode_drive_size(), + "drive_usage": get_mynode_drive_usage(), } # Setup tor proxy @@ -129,10 +55,10 @@ def premium_plus_connect(): r = session.post(PREMIUM_PLUS_CONNECT_URL, data=data, timeout=20) if r.status_code == 200: + set_premium_plus_token_status(r.text) if r.text == "OK": log_message("Premium+ Connect Success: {}".format(r.text)) else: - set_premium_plus_token_error(r.text) log_message("Check In Returned Error: {}".format(r.text)) os.system("rm -f /tmp/premium_plus_connect_error") @@ -142,9 +68,11 @@ def premium_plus_connect(): except Exception as e: log_message("Premium+ Connect Failed. Retrying... Exception {}".format(e)) + update_premium_plus_last_sync_time() + if not premium_plus_connect_success: # Check in failed, try again in 1 minute - os.system("touch /tmp/premium_plus_connect_error") + set_premium_plus_token_status("CONNECTION_ERROR") time.sleep(60) fail_count = fail_count + 1 @@ -157,26 +85,27 @@ if __name__ == "__main__": while True: try: # Wait for drive to be mounted - while not is_drive_mounted(): + while not is_mynode_drive_mounted(): log_message("Checking if drive mounted...") time.sleep(10) log_message("Drive mounted!") # Wait on token - log_message("Waiting on Premium+ Token") + log_message("Looking for Premium+ Token...") while not os.path.isfile("/home/bitcoin/.mynode/.premium_plus_token"): time.sleep(10) log_message("Token found!") premium_plus_connect() # Watch for updates + log_message("") + log_message("Watching for file changes or 1hr...") inotify = INotify() watch_flags = flags.CREATE | flags.DELETE | flags.MODIFY | flags.DELETE_SELF wd = inotify.add_watch('/home/bitcoin/.mynode/', watch_flags) for event in inotify.read(timeout=one_hour_in_ms): log_message("File changed: " + str(event)) log_message("Running connect again: " + str(event)) - premium_plus_connect() except Exception as e: log_message("Error: {}".format(e)) time.sleep(60) diff --git a/rootfs/standard/usr/bin/mynode_startup.sh b/rootfs/standard/usr/bin/mynode_startup.sh index 981239e1..5d69a850 100755 --- a/rootfs/standard/usr/bin/mynode_startup.sh +++ b/rootfs/standard/usr/bin/mynode_startup.sh @@ -78,6 +78,11 @@ rm -rf /etc/motd # Remove simple motd for update-motd.d mkdir -p /mnt/hdd mkdir -p /mnt/usb_extras +# Add to python path +[ -d /usr/local/lib/python2.7/dist-packages ] && echo "/var/pynode" > /usr/local/lib/python2.7/dist-packages/pynode.pth +[ -d /usr/local/lib/python3.7/site-packages ] && echo "/var/pynode" > /usr/local/lib/python3.7/site-packages/pynode.pth +[ -d /usr/local/lib/python3.8/site-packages ] && echo "/var/pynode" > /usr/local/lib/python3.8/site-packages/pynode.pth + # Customize logo for resellers if [ -f /opt/mynode/custom/logo_custom.png ]; then cp -f /opt/mynode/custom/logo_custom.png /var/www/mynode/static/images/logo_custom.png diff --git a/rootfs/standard/usr/bin/mynode_usb_extras.py b/rootfs/standard/usr/bin/mynode_usb_extras.py index 640bd9cb..1835955d 100755 --- a/rootfs/standard/usr/bin/mynode_usb_extras.py +++ b/rootfs/standard/usr/bin/mynode_usb_extras.py @@ -2,20 +2,20 @@ import time import os import subprocess -import signal import logging -import random -import string import json import atexit from http.server import HTTPServer, SimpleHTTPRequestHandler import pyudev from systemd import journal from threading import Thread +from utilities import * +from drive_info import * log = logging.getLogger('mynode') log.addHandler(journal.JournaldLogHandler()) log.setLevel(logging.INFO) +set_logger(log) ################################ ## USB Device Cache @@ -54,12 +54,6 @@ def write_usb_devices_json(): ################################ ## Utility Functions ################################ - -def log_message(msg): - global log - print(msg) - log.info(msg) - def set_usb_extras_state(state): log_message("USB Extras State: {}".format(state)) try: @@ -71,71 +65,6 @@ def set_usb_extras_state(state): return False return False -def get_drive_size(drive): - size = -1 - try: - lsblk_output = subprocess.check_output(f"lsblk -b /dev/{drive} | grep disk", shell=True).decode("utf-8") - parts = lsblk_output.split() - size = int(parts[3]) - except: - pass - log_message(f"Drive {drive} size: {size}") - return size - -def mount_partition(partition, folder_name, permissions="ro"): - try: - subprocess.check_output(f"mkdir -p /mnt/usb_extras/{folder_name}", shell=True) - subprocess.check_output(f"mount -o {permissions} /dev/{partition} /mnt/usb_extras/{folder_name}", shell=True) - return True - except Exception as e: - return False - -def unmount_partition(folder_name): - os.system(f"umount /mnt/usb_extras/{folder_name}") - os.system(f"rm -rf /mnt/usb_extras/{folder_name}") - time.sleep(1) - -def find_partitions_for_drive(drive): - partitions = [] - try: - ls_output = subprocess.check_output(f"ls /sys/block/{drive}/ | grep {drive}", shell=True).decode("utf-8") - partitions = ls_output.split() - except: - pass - return partitions - -def is_drive_detected_by_fdisk(d): - detected = False - try: - # Command fails and throws exception if not mounted - ls_output = subprocess.check_output(f"fdisk -l /dev/{d}", shell=True).decode("utf-8") - detected = True - except: - pass - return detected - -def is_drive_mounted(d): - mounted = True - try: - # Command fails and throws exception if not mounted - ls_output = subprocess.check_output(f"grep -qs '/dev/{d}' /proc/mounts", shell=True).decode("utf-8") - except: - mounted = False - return mounted - -def find_unmounted_drives(): - drives = [] - try: - ls_output = subprocess.check_output("ls /sys/block/ | egrep 'hd.*|vd.*|sd.*|nvme.*'", shell=True).decode("utf-8") - all_drives = ls_output.split() - - # Only return drives that are not mounted (VM may have /dev/sda as OS drive) - for d in all_drives: - if is_drive_detected_by_fdisk(d) and not is_drive_mounted(d): - drives.append(d) - except: - pass - return drives ################################ ## HTTP Server Functions diff --git a/rootfs/standard/usr/share/mynode/application_info.json b/rootfs/standard/usr/share/mynode/application_info.json index 35d98fc5..69e4d044 100644 --- a/rootfs/standard/usr/share/mynode/application_info.json +++ b/rootfs/standard/usr/share/mynode/application_info.json @@ -290,11 +290,11 @@ "short_name": "premium_plus", "hide_status_icon": true, "can_enable_disable": false, - "app_tile_button_text": "Open", + "app_tile_button_text": "Manage", "app_tile_default_status_text": "Access and Backup", - "app_tile_button_href": "/premium_plus", + "app_tile_button_href": "/premium-plus", "show_on_application_page": false, - "show_on_homepage": false, + "show_on_homepage": true, "can_uninstall": false, "can_reinstall": false, "journalctl_log_name": "premium_plus_connect", diff --git a/rootfs/standard/var/www/mynode/bitcoin_info.py b/rootfs/standard/var/pynode/bitcoin_info.py similarity index 100% rename from rootfs/standard/var/www/mynode/bitcoin_info.py rename to rootfs/standard/var/pynode/bitcoin_info.py diff --git a/rootfs/standard/var/www/mynode/config.py b/rootfs/standard/var/pynode/config.py similarity index 100% rename from rootfs/standard/var/www/mynode/config.py rename to rootfs/standard/var/pynode/config.py diff --git a/rootfs/standard/var/www/mynode/device_info.py b/rootfs/standard/var/pynode/device_info.py similarity index 91% rename from rootfs/standard/var/www/mynode/device_info.py rename to rootfs/standard/var/pynode/device_info.py index 251668d6..13c27116 100644 --- a/rootfs/standard/var/www/mynode/device_info.py +++ b/rootfs/standard/var/pynode/device_info.py @@ -81,7 +81,7 @@ def check_and_mark_reboot_action(tmp_marker): def reload_throttled_data(): global cached_data if os.path.isfile("/tmp/get_throttled_data"): - cached_data["get_throttled_data"] = get_file_contents("/tmp/get_throttled_data") + cached_data["get_throttled_data"] = to_string( get_file_contents("/tmp/get_throttled_data") ) def get_throttled_data(): global cached_data @@ -207,7 +207,7 @@ def cleanup_log(log): return log def get_recent_upgrade_log(): - log = get_file_contents("/home/admin/upgrade_logs/upgrade_log_latest.txt").decode("utf8") + log = to_string( get_file_contents("/home/admin/upgrade_logs/upgrade_log_latest.txt") ) return cleanup_log(log) def get_all_upgrade_logs(): @@ -237,7 +237,7 @@ def get_all_upgrade_logs(): log["name"] = f modTimeSeconds = os.path.getmtime(fullpath) log["date"] = time.strftime('%Y-%m-%d', time.localtime(modTimeSeconds)) - log["log"] = cleanup_log( get_file_contents(fullpath).decode("utf8") ) + log["log"] = cleanup_log( to_string( get_file_contents(fullpath) ) ) log_list.append( log ) log_id += 1 except Exception as e: @@ -362,7 +362,7 @@ def set_swap_size(size): return set_file_contents("/mnt/hdd/mynode/settings/swap_size", size) def get_swap_size(): - return get_file_contents("/mnt/hdd/mynode/settings/swap_size") + return to_string( get_file_contents("/mnt/hdd/mynode/settings/swap_size") ) #================================== # myNode Status @@ -423,19 +423,19 @@ CLONE_STATE_IN_PROGRESS = "in_progress" CLONE_STATE_COMPLETE = "complete" def get_clone_state(): - return get_file_contents("/tmp/.clone_state") + return to_string( get_file_contents("/tmp/.clone_state") ) def get_clone_error(): - return get_file_contents("/tmp/.clone_error") + return to_string( get_file_contents("/tmp/.clone_error") ) def get_clone_progress(): - return get_file_contents("/tmp/.clone_progress") + return to_string( get_file_contents("/tmp/.clone_progress") ) def get_clone_source_drive(): - return get_file_contents("/tmp/.clone_source") + return to_string( get_file_contents("/tmp/.clone_source") ) def get_clone_target_drive(): - return get_file_contents("/tmp/.clone_target") + return to_string( get_file_contents("/tmp/.clone_target") ) def get_clone_target_drive_has_mynode(): return os.path.isfile("/tmp/.clone_target_drive_has_mynode") @@ -563,7 +563,7 @@ def regen_https_cert(): def get_flask_secret_key(): if os.path.isfile("/home/bitcoin/.mynode/flask_secret_key"): - key = get_file_contents("/home/bitcoin/.mynode/flask_secret_key") + key = to_string( get_file_contents("/home/bitcoin/.mynode/flask_secret_key") ) else: letters = string.ascii_letters key = ''.join(random.choice(letters) for i in range(32)) @@ -573,7 +573,7 @@ def get_flask_secret_key(): def get_flask_session_timeout(): try: if os.path.isfile("/home/bitcoin/.mynode/flask_session_timeout"): - timeout = get_file_contents("/home/bitcoin/.mynode/flask_session_timeout") + timeout = to_string( get_file_contents("/home/bitcoin/.mynode/flask_session_timeout") ) parts = timeout.split(",") d = parts[0] h = parts[1] @@ -607,21 +607,6 @@ def set_www_python3(use_python3): delete_file("/home/bitcoin/.mynode/.www_use_python3") -#================================== -# Drive Functions -#================================== -def is_uas_usb_enabled(): - return os.path.isfile('/home/bitcoin/.mynode/.uas_usb_enabled') or \ - os.path.isfile('/mnt/hdd/mynode/settings/.uas_usb_enabled') - -def set_uas_usb_enabled(use_uas): - if use_uas: - touch("/home/bitcoin/.mynode/.uas_usb_enabled") - touch("/mnt/hdd/mynode/settings/.uas_usb_enabled") - else: - delete_file("/home/bitcoin/.mynode/.uas_usb_enabled") - delete_file("/mnt/hdd/mynode/settings/.uas_usb_enabled") - #================================== # Web Server Functions #================================== @@ -743,6 +728,74 @@ def recheck_product_key(): os.system("systemctl restart check_in") +#================================== +# Premium+ Token Functions +#================================== +def delete_premium_plus_token(): + delete_file("/home/bitcoin/.mynode/.premium_plus_token") + delete_file("/mnt/hdd/mynode/settings/.premium_plus_token") +def has_premium_plus_token(): + return os.path.isfile("/home/bitcoin/.mynode/.premium_plus_token") or \ + os.path.isfile("/mnt/hdd/mynode/settings/.premium_plus_token") +def get_premium_plus_token(): + token = "error_1" + if not has_premium_plus_token(): + return "" + + try: + if os.path.isfile("/home/bitcoin/.mynode/.premium_plus_token"): + with open("/home/bitcoin/.mynode/.premium_plus_token", "r") as f: + token = f.read().strip() + elif os.path.isfile("/mnt/hdd/mynode/settings/.premium_plus_token"): + with open("/mnt/hdd/mynode/settings/.premium_plus_token", "r") as f: + token = f.read().strip() + except: + token = "error_2" + return token +def reset_premium_plus_token_status(): + delete_file("/home/bitcoin/.mynode/.premium_plus_token_status") +def set_premium_plus_token_status(msg): + os.system("echo '{}' > /home/bitcoin/.mynode/.premium_plus_token_status".format(msg)) +def get_premium_plus_token_status(): + status = "UNKNOWN" + if not has_premium_plus_token(): + return "No Token Set" + if not os.path.isfile("/home/bitcoin/.mynode/.premium_plus_token_status"): + return "Updating..." + try: + with open("/home/bitcoin/.mynode/.premium_plus_token_status", "r") as f: + status = f.read().strip() + except: + status = "STATUS_ERROR_2" + return status +def get_premium_plus_is_connected(): + status = get_premium_plus_token_status() + if status == "OK": + return True + return False +def update_premium_plus_last_sync_time(): + t = int(round(time.time())) + os.system("echo '{}' > /home/bitcoin/.mynode/.premium_plus_last_sync".format(t)) +def get_premium_plus_last_sync(): + try: + now = int(round(time.time())) + last = int(get_file_contents("/home/bitcoin/.mynode/.premium_plus_last_sync")) + diff_min = int((now - last) / 60) + if diff_min == 0: + return "Now" + else: + return "{} minutes(s) ago".format(diff_min) + except Exception as e: + return "Unknown" +def save_premium_plus_token(token): + set_file_contents("/home/bitcoin/.mynode/.premium_plus_token", token) + set_file_contents("/mnt/hdd/mynode/settings/.premium_plus_token", token) + +def recheck_premium_plus_token(): + reset_premium_plus_token_status() + os.system("systemctl restart premium_plus_connect") + + #================================== # Drive Repair Functions #================================== diff --git a/rootfs/standard/var/pynode/drive_info.py b/rootfs/standard/var/pynode/drive_info.py new file mode 100644 index 00000000..b92adc37 --- /dev/null +++ b/rootfs/standard/var/pynode/drive_info.py @@ -0,0 +1,137 @@ +from config import * +from utilities import * +import time +import json +import os +import subprocess +import random +import string +import re + + +#================================== +# Drive Functions +#================================== +def is_mynode_drive_mounted(): + mounted = True + try: + # Command fails and throws exception if not mounted + output = to_string(subprocess.check_output("grep -qs '/mnt/hdd ext4' /proc/mounts", shell=True)) + except: + mounted = False + return mounted + +def is_device_mounted(d): + mounted = True + try: + # Command fails and throws exception if not mounted + ls_output = to_string(subprocess.check_output("grep -qs '/dev/{}' /proc/mounts".format(d), shell=True)) + except: + mounted = False + return mounted + +def get_drive_size(drive): + size = -1 + try: + lsblk_output = to_string(subprocess.check_output("lsblk -b /dev/{} | grep disk".format(drive), shell=True)) + parts = lsblk_output.split() + size = int(parts[3]) + except: + pass + #log_message(f"Drive {drive} size: {size}") + return size + +def get_mynode_drive_size(): + size = -1 + if not is_mynode_drive_mounted(): + return -3 + try: + size = to_string(subprocess.check_output("df /mnt/hdd | grep /dev | awk '{print $2}'", shell=True)).strip() + size = int(size) / 1000 / 1000 + except Exception as e: + size = -2 + return size + +def get_mynode_drive_usage(): + return "TODO" + +def check_partition_for_mynode(partition): + is_mynode = False + try: + subprocess.check_output("mount -o ro /dev/{} /mnt/hdd".format(partition), shell=True) + if os.path.isfile("/mnt/hdd/.mynode"): + is_mynode = True + except Exception as e: + # Mount failed, could be target drive + pass + finally: + time.sleep(1) + os.system("umount /mnt/hdd") + + return is_mynode + +def find_partitions_for_drive(drive): + partitions = [] + try: + ls_output = to_string(subprocess.check_output("ls /sys/block/{}/ | grep {}".format(drive, drive), shell=True)) + partitions = ls_output.split() + except: + pass + return partitions + +def is_device_detected_by_fdisk(d): + detected = False + try: + # Command fails and throws exception if not mounted + output = to_string(subprocess.check_output("fdisk -l /dev/{}".format(d), shell=True)) + detected = True + except: + pass + return detected + +def find_unmounted_drives(): + drives = [] + try: + ls_output = subprocess.check_output("ls /sys/block/ | egrep 'hd.*|vd.*|sd.*|nvme.*'", shell=True).decode("utf-8") + all_drives = ls_output.split() + + # Only return drives that are not mounted (VM may have /dev/sda as OS drive) + for d in all_drives: + if is_device_detected_by_fdisk(d) and not is_device_mounted(d): + drives.append(d) + except: + pass + return drives + + +#================================== +# Mount / Unmount Parition Functions +#================================== +def mount_partition(partition, folder_name, permissions="ro"): + try: + subprocess.check_output("mkdir -p /mnt/usb_extras/{}".format(folder_name), shell=True) + subprocess.check_output("mount -o {} /dev/{} /mnt/usb_extras/{}".format(permissions, partition, folder_name), shell=True) + return True + except Exception as e: + return False + +def unmount_partition(folder_name): + os.system("umount /mnt/usb_extras/{}".format(folder_name)) + os.system("rm -rf /mnt/usb_extras/{}".format(folder_name)) + time.sleep(1) + + +#================================== +# Drive Driver Functions +#================================== +def is_uas_usb_enabled(): + return os.path.isfile('/home/bitcoin/.mynode/.uas_usb_enabled') or \ + os.path.isfile('/mnt/hdd/mynode/settings/.uas_usb_enabled') + +def set_uas_usb_enabled(use_uas): + if use_uas: + touch("/home/bitcoin/.mynode/.uas_usb_enabled") + touch("/mnt/hdd/mynode/settings/.uas_usb_enabled") + else: + delete_file("/home/bitcoin/.mynode/.uas_usb_enabled") + delete_file("/mnt/hdd/mynode/settings/.uas_usb_enabled") diff --git a/rootfs/standard/var/www/mynode/electrum_info.py b/rootfs/standard/var/pynode/electrum_info.py similarity index 100% rename from rootfs/standard/var/www/mynode/electrum_info.py rename to rootfs/standard/var/pynode/electrum_info.py diff --git a/rootfs/standard/var/www/mynode/enable_disable_functions.py b/rootfs/standard/var/pynode/enable_disable_functions.py similarity index 100% rename from rootfs/standard/var/www/mynode/enable_disable_functions.py rename to rootfs/standard/var/pynode/enable_disable_functions.py diff --git a/rootfs/standard/var/www/mynode/lightning_info.py b/rootfs/standard/var/pynode/lightning_info.py similarity index 99% rename from rootfs/standard/var/www/mynode/lightning_info.py rename to rootfs/standard/var/pynode/lightning_info.py index 501acffc..e0a67f76 100644 --- a/rootfs/standard/var/www/mynode/lightning_info.py +++ b/rootfs/standard/var/pynode/lightning_info.py @@ -409,9 +409,6 @@ def restart_lnd(): time.sleep(1) -def is_testnet_enabled(): - return os.path.isfile("/mnt/hdd/mynode/settings/.testnet_enabled") - def get_lightning_wallet_file(): if is_testnet_enabled(): return "/mnt/hdd/mynode/lnd/data/chain/bitcoin/testnet/wallet.db" diff --git a/rootfs/standard/var/www/mynode/systemctl_info.py b/rootfs/standard/var/pynode/systemctl_info.py similarity index 92% rename from rootfs/standard/var/www/mynode/systemctl_info.py rename to rootfs/standard/var/pynode/systemctl_info.py index 43280b5c..3f086b6a 100644 --- a/rootfs/standard/var/www/mynode/systemctl_info.py +++ b/rootfs/standard/var/pynode/systemctl_info.py @@ -11,10 +11,10 @@ def clear_service_enabled_cache(): global service_enabled_cache service_enabled_cache = {} -def is_service_enabled(service_name): +def is_service_enabled(service_name, force_refresh=False): global service_enabled_cache - if service_name in service_enabled_cache: + if service_name in service_enabled_cache and force_refresh == False: return service_enabled_cache[service_name] code = os.system("systemctl is-enabled {} > /dev/null".format(service_name)) diff --git a/rootfs/standard/var/www/mynode/utilities.py b/rootfs/standard/var/pynode/utilities.py similarity index 85% rename from rootfs/standard/var/www/mynode/utilities.py rename to rootfs/standard/var/pynode/utilities.py index 38e34d15..7dcd2d1d 100644 --- a/rootfs/standard/var/www/mynode/utilities.py +++ b/rootfs/standard/var/pynode/utilities.py @@ -18,23 +18,25 @@ def isPython3(): def to_bytes(s): if type(s) is bytes: return s - elif type(s) is str or (sys.version_info[0] < 3 and type(s) is unicode): + elif type(s) is str or (not isPython3() and type(s) is unicode): return codecs.encode(s, 'utf-8', 'ignore') else: raise TypeError("to_bytes: Expected bytes or string, but got %s." % type(s)) def to_string(s): b = to_bytes(s) - return b.decode("utf-8") + r = b.decode("utf-8") + print("S TYPE: "+str(type(r))) + return r def quote_plus(s): - if (sys.version_info > (3, 0)): + if isPython3(): return urllib.parse.quote_plus(s) else: return urllib.quote_plus(s) def unquote_plus(s): - if (sys.version_info > (3, 0)): + if isPython3(): return urllib.parse.unquote_plus(s) else: return urllib.unquote_plus(s) @@ -66,7 +68,7 @@ def get_file_contents(filename): contents = "UNKNOWN" try: with open(filename, "r") as f: - contents = f.read().strip() + contents = to_string(f.read()).strip() except: contents = "ERROR" return to_bytes(contents) @@ -90,11 +92,12 @@ def log_message(msg): # Logs to www log global mynode_logger if mynode_logger != None: + print(msg) mynode_logger.info(msg) -def set_logger(l): +def set_logger(logger): global mynode_logger - mynode_logger = l + mynode_logger = logger def get_logger(): global mynode_logger @@ -153,3 +156,15 @@ def download_file(directory, filename, downloaded_file_name=None, as_attachment= return send_from_directory(directory=directory, path=filename, filename=None, as_attachment=as_attachment) else: return send_from_directory(directory=directory, filename=filename, as_attachment=as_attachment) + +#================================== +# Hashing Functions +#================================== +def get_md5_file_hash(path): + import hashlib + if not os.path.isfile(path): + return "MISSING_FILE" + try: + return hashlib.md5(open(path,'rb').read()).hexdigest() + except Exception as e: + return "ERROR ({})".format(e) \ No newline at end of file diff --git a/rootfs/standard/var/www/mynode/application_info.py b/rootfs/standard/var/www/mynode/application_info.py index 760eb781..8e09aa04 100644 --- a/rootfs/standard/var/www/mynode/application_info.py +++ b/rootfs/standard/var/www/mynode/application_info.py @@ -381,9 +381,9 @@ def get_app_version_data(): def get_custom_app_version_data(): if os.path.isfile("/usr/share/mynode/mynode_app_versions_custom.sh"): - return get_file_contents("/usr/share/mynode/mynode_app_versions_custom.sh") + return to_string( get_file_contents("/usr/share/mynode/mynode_app_versions_custom.sh") ) if os.path.isfile("/mnt/hdd/mynode/settings/mynode_app_versions_custom.sh"): - return get_file_contents("/mnt/hdd/mynode/settings/mynode_app_versions_custom.sh") + return to_string( get_file_contents("/mnt/hdd/mynode/settings/mynode_app_versions_custom.sh") ) return "" def save_custom_app_version_data(data): diff --git a/rootfs/standard/var/www/mynode/bitcoin.py b/rootfs/standard/var/www/mynode/bitcoin.py index b1404680..f821ee3a 100644 --- a/rootfs/standard/var/www/mynode/bitcoin.py +++ b/rootfs/standard/var/www/mynode/bitcoin.py @@ -8,7 +8,6 @@ from subprocess import check_output, check_call from electrum_info import * from user_management import check_logged_in import socket -import hashlib import json import time diff --git a/rootfs/standard/var/www/mynode/mynode.py b/rootfs/standard/var/www/mynode/mynode.py index f1ed779f..5d963dba 100644 --- a/rootfs/standard/var/www/mynode/mynode.py +++ b/rootfs/standard/var/www/mynode/mynode.py @@ -15,6 +15,7 @@ from wardenterminal import mynode_wardenterminal from lndmanage import mynode_lndmanage from manage_apps import mynode_manage_apps from usb_extras import mynode_usb_extras +from premium_plus import mynode_premium_plus from tor import mynode_tor from vpn import mynode_vpn from electrum_server import * @@ -97,6 +98,7 @@ app.register_blueprint(mynode_wardenterminal) app.register_blueprint(mynode_lndmanage) app.register_blueprint(mynode_manage_apps) app.register_blueprint(mynode_tor) +app.register_blueprint(mynode_premium_plus) app.register_blueprint(mynode_electrum_server) app.register_blueprint(mynode_vpn) app.register_blueprint(mynode_usb_extras) @@ -256,6 +258,8 @@ def index(): return render_template('state.html', **templateData) elif status == STATE_DRIVE_CLONE: clone_state = get_clone_state() + log_message("CLONE_STATE") + log_message(clone_state) if clone_state == CLONE_STATE_DETECTING: templateData = { "title": "myNode Clone Tool", diff --git a/rootfs/standard/var/www/mynode/premium_plus.py b/rootfs/standard/var/www/mynode/premium_plus.py new file mode 100644 index 00000000..6bec14bd --- /dev/null +++ b/rootfs/standard/var/www/mynode/premium_plus.py @@ -0,0 +1,60 @@ +from flask import Blueprint, render_template, session, abort, Markup, request, redirect, flash +from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException +from pprint import pprint, pformat +from device_info import * +from user_management import check_logged_in +from enable_disable_functions import restart_service +import json +import time + +mynode_premium_plus = Blueprint('mynode_premium_plus',__name__) + + +### Page functions +@mynode_premium_plus.route("/premium-plus") +def premium_plus_page(): + check_logged_in() + + + # Load page + templateData = { + "title": "myNode Premium+", + "has_access_token": has_premium_plus_token(), + "access_token": get_premium_plus_token(), + "status": get_premium_plus_token_status(), + "is_connected": get_premium_plus_is_connected(), + "last_sync": get_premium_plus_last_sync(), + "ui_settings": read_ui_settings() + } + return render_template('premium_plus.html', **templateData) + +@mynode_premium_plus.route("/premium-plus/sync") +def premium_plus_sync_page(): + check_logged_in() + restart_service("premium_plus_connect") + time.sleep(3) + flash("Syncing...", category="message") + return redirect("/premium-plus") + +@mynode_premium_plus.route("/premium-plus/clear-token") +def premium_plus_clear_token_page(): + check_logged_in() + delete_premium_plus_token() + reset_premium_plus_token_status() + restart_service("premium_plus_connect") + time.sleep(3) + flash("Token Cleared", category="message") + return redirect("/premium-plus") + +@mynode_premium_plus.route("/premium-plus/set-token", methods=["POST"]) +def premium_plus_set_token_page(): + check_logged_in() + token = request.form.get('token') + if token == None: + flash("Missing Token", category="error") + return redirect("/premium-plus") + save_premium_plus_token(token) + restart_service("premium_plus_connect") + time.sleep(3) + flash("Token Set", category="message") + return redirect("/premium-plus") \ No newline at end of file diff --git a/rootfs/standard/var/www/mynode/settings.py b/rootfs/standard/var/www/mynode/settings.py index e54f088d..f9830541 100644 --- a/rootfs/standard/var/www/mynode/settings.py +++ b/rootfs/standard/var/www/mynode/settings.py @@ -11,6 +11,7 @@ from user_management import check_logged_in from lightning_info import * from price_info import * from utilities import * +from drive_info import * from application_info import * import pam import time @@ -346,7 +347,7 @@ def upgrade_beta_page(): def get_upgrade_log_page(): check_logged_in() - log = get_file_contents("/home/admin/upgrade_logs/upgrade_log_latest.txt").decode("utf8") + log = to_string( get_file_contents("/home/admin/upgrade_logs/upgrade_log_latest.txt") ) if (log == "ERROR"): log = "No log file found" diff --git a/rootfs/standard/var/www/mynode/templates/premium_plus.html b/rootfs/standard/var/www/mynode/templates/premium_plus.html new file mode 100644 index 00000000..cd8b8251 --- /dev/null +++ b/rootfs/standard/var/www/mynode/templates/premium_plus.html @@ -0,0 +1,156 @@ + + + {{ title }} + {% include 'includes/head.html' %} + + + + + + + {% include 'includes/logo_header.html' %} +
+ home  +
+ + {% include 'includes/message_display.html' %} + +
Premium+
+
+ + +
+
+
Access Token and Status
+
+ + + + + + {% if has_access_token %} + + + + + + + + + + + + + {% endif %} +
Access Token + {% if has_access_token %} + {{access_token}} + + + {% else %} + + {% endif %} +
Status{{status}}
Last Sync{{last_sync}}
ActionsForce Sync
+
+
+
+
+ + +
+
+
Premium+ Services
+
+ + + + + + + + + + + + + + + + + + + + + +
Sync Device Status{{mempool_tx}}
Backup Lightning SCB File{{mempool_tx}}
myNode Watchtower{{mempool_size}}
Proxy + + show +
Peer Bloom Filters (BIP 37) + + +
+
+
+
+ + + + + {% include 'includes/footer.html' %} + + \ No newline at end of file