feat: Add initial logggin system
All checks were successful
Tests and Linting / Tests-Linting (3.10) (push) Successful in 37s
Tests and Linting / Tests-Linting (3.11) (push) Successful in 37s
Tests and Linting / Tests-Linting (3.13) (push) Successful in 37s
Build Docker / Build Images (map[dockerfile:Dockerfile tag_suffix: target:default]) (push) Successful in 48s
Build Docker / Build Images (map[dockerfile:Dockerfile.hsd tag_suffix:-hsd target:hsd]) (push) Successful in 51s
All checks were successful
Tests and Linting / Tests-Linting (3.10) (push) Successful in 37s
Tests and Linting / Tests-Linting (3.11) (push) Successful in 37s
Tests and Linting / Tests-Linting (3.13) (push) Successful in 37s
Build Docker / Build Images (map[dockerfile:Dockerfile tag_suffix: target:default]) (push) Successful in 48s
Build Docker / Build Images (map[dockerfile:Dockerfile.hsd tag_suffix:-hsd target:hsd]) (push) Successful in 51s
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -18,5 +18,6 @@ build/
|
|||||||
dist/
|
dist/
|
||||||
hsd/
|
hsd/
|
||||||
hsd_data/
|
hsd_data/
|
||||||
|
logs/
|
||||||
hsd.lock
|
hsd.lock
|
||||||
hsdconfig.json
|
hsdconfig.json
|
||||||
|
|||||||
BIN
FireWalletBrowser.bsdesign
LFS
BIN
FireWalletBrowser.bsdesign
LFS
Binary file not shown.
82
account.py
82
account.py
@@ -13,6 +13,8 @@ import signal
|
|||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger("firewallet")
|
||||||
|
|
||||||
|
|
||||||
dotenv.load_dotenv()
|
dotenv.load_dotenv()
|
||||||
@@ -270,7 +272,7 @@ def getCachedDomains():
|
|||||||
domain_cache[row['name']] = json.loads(row['info'])
|
domain_cache[row['name']] = json.loads(row['info'])
|
||||||
domain_cache[row['name']]['last_updated'] = row['last_updated']
|
domain_cache[row['name']]['last_updated'] = row['last_updated']
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
print(f"Error parsing cached data for domain {row['name']}")
|
logger.error(f"Error parsing cached data for domain {row['name']}")
|
||||||
|
|
||||||
conn.close()
|
conn.close()
|
||||||
return domain_cache
|
return domain_cache
|
||||||
@@ -310,7 +312,7 @@ def update_domain_cache(domain_names: list):
|
|||||||
domain_info = getDomain(domain_name)
|
domain_info = getDomain(domain_name)
|
||||||
|
|
||||||
if 'error' in domain_info or not domain_info.get('info'):
|
if 'error' in domain_info or not domain_info.get('info'):
|
||||||
print(f"Failed to get info for domain {domain_name}: {domain_info.get('error', 'Unknown error')}", flush=True)
|
logger.error(f"Failed to get info for domain {domain_name}: {domain_info.get('error', 'Unknown error')}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Update or insert into database
|
# Update or insert into database
|
||||||
@@ -322,9 +324,9 @@ def update_domain_cache(domain_names: list):
|
|||||||
(domain_name, serialized_info, now)
|
(domain_name, serialized_info, now)
|
||||||
)
|
)
|
||||||
|
|
||||||
print(f"Updated cache for domain {domain_name}")
|
logger.info(f"Updated cache for domain {domain_name}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error updating cache for domain {domain_name}: {str(e)}")
|
logger.error(f"Error updating cache for domain {domain_name}: {str(e)}", exc_info=True)
|
||||||
finally:
|
finally:
|
||||||
# Always remove from active set, even if there was an error
|
# Always remove from active set, even if there was an error
|
||||||
with DOMAIN_UPDATE_LOCK:
|
with DOMAIN_UPDATE_LOCK:
|
||||||
@@ -336,14 +338,14 @@ def update_domain_cache(domain_names: list):
|
|||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error updating domain cache: {str(e)}", flush=True)
|
logger.error(f"Error updating domain cache: {str(e)}", exc_info=True)
|
||||||
# Make sure to clean up the active set on any exception
|
# Make sure to clean up the active set on any exception
|
||||||
with DOMAIN_UPDATE_LOCK:
|
with DOMAIN_UPDATE_LOCK:
|
||||||
for domain in domains_to_update:
|
for domain in domains_to_update:
|
||||||
if domain in ACTIVE_DOMAIN_UPDATES:
|
if domain in ACTIVE_DOMAIN_UPDATES:
|
||||||
ACTIVE_DOMAIN_UPDATES.remove(domain)
|
ACTIVE_DOMAIN_UPDATES.remove(domain)
|
||||||
|
|
||||||
print("Updated cache for domains")
|
logger.info("Updated cache for domains")
|
||||||
|
|
||||||
|
|
||||||
def getBalance(account: str):
|
def getBalance(account: str):
|
||||||
@@ -594,14 +596,14 @@ def getTransactions(account, page=1, limit=100):
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
print(response.text)
|
logger.error(f"Error fetching transactions: {response.status_code} - {response.text}")
|
||||||
return []
|
return []
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
# Refresh the cache if the next page is different
|
# Refresh the cache if the next page is different
|
||||||
nextPage = getPageTXCache(account, page, limit)
|
nextPage = getPageTXCache(account, page, limit)
|
||||||
if nextPage is not None and nextPage != data[-1]['hash']:
|
if nextPage is not None and nextPage != data[-1]['hash']:
|
||||||
print(f'Refreshing page {page}')
|
logger.info(f'Refreshing tx page {page}')
|
||||||
pushPageTXCache(account, page, data[-1]['hash'], limit)
|
pushPageTXCache(account, page, data[-1]['hash'], limit)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@@ -788,11 +790,12 @@ def getAddressFromCoin(coinhash: str, coinindex = 0):
|
|||||||
# Get the address from the hash
|
# Get the address from the hash
|
||||||
response = requests.get(get_node_api_url(f"coin/{coinhash}/{coinindex}"))
|
response = requests.get(get_node_api_url(f"coin/{coinhash}/{coinindex}"))
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
print("Error getting address from coin")
|
logger.error("Error getting address from coin")
|
||||||
return "No Owner"
|
return "No Owner"
|
||||||
data = response.json()
|
data = response.json()
|
||||||
if 'address' not in data:
|
if 'address' not in data:
|
||||||
print(json.dumps(data, indent=4))
|
logger.error("Error getting address from coin")
|
||||||
|
logger.error(json.dumps(data, indent=4))
|
||||||
return "No Owner"
|
return "No Owner"
|
||||||
return data['address']
|
return data['address']
|
||||||
|
|
||||||
@@ -998,7 +1001,7 @@ def getPendingRedeems(account, password):
|
|||||||
else:
|
else:
|
||||||
pending.append(name['result'])
|
pending.append(name['result'])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Failed to parse redeems: {str(e)}")
|
logger.error(f"Failed to parse redeems: {str(e)}", exc_info=True)
|
||||||
|
|
||||||
return pending
|
return pending
|
||||||
|
|
||||||
@@ -1042,7 +1045,7 @@ def getPendingFinalizes(account, password):
|
|||||||
else:
|
else:
|
||||||
pending.append(name['result'])
|
pending.append(name['result'])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Failed to parse finalizes: {str(e)}")
|
logger.error(f"Failed to parse finalizes: {str(e)}", exc_info=True)
|
||||||
return pending
|
return pending
|
||||||
|
|
||||||
|
|
||||||
@@ -1052,9 +1055,8 @@ def getRevealTX(reveal):
|
|||||||
index = prevout['index']
|
index = prevout['index']
|
||||||
tx = hsd.getTxByHash(hash)
|
tx = hsd.getTxByHash(hash)
|
||||||
if 'inputs' not in tx:
|
if 'inputs' not in tx:
|
||||||
print(f'Something is up with this tx: {hash}')
|
logger.error(f'Something is up with this tx: {hash}')
|
||||||
print(tx)
|
logger.error(tx)
|
||||||
print('---')
|
|
||||||
# No idea what happened here
|
# No idea what happened here
|
||||||
# Check if registered?
|
# Check if registered?
|
||||||
return None
|
return None
|
||||||
@@ -1498,10 +1500,10 @@ def getMempoolBids():
|
|||||||
for txid in mempoolTxs:
|
for txid in mempoolTxs:
|
||||||
tx = hsd.getTxByHash(txid)
|
tx = hsd.getTxByHash(txid)
|
||||||
if 'error' in tx and tx['error'] is not None:
|
if 'error' in tx and tx['error'] is not None:
|
||||||
print(f"Error getting tx {txid}: {tx['error']}")
|
logger.error(f"Error getting tx {txid}: {tx['error']}")
|
||||||
continue
|
continue
|
||||||
if 'outputs' not in tx:
|
if 'outputs' not in tx:
|
||||||
print(f"Error getting outputs for tx {txid}")
|
logger.error(f"Error getting outputs for tx {txid}")
|
||||||
continue
|
continue
|
||||||
for output in tx['outputs']:
|
for output in tx['outputs']:
|
||||||
if output['covenant']['action'] not in ["BID", "REVEAL"]:
|
if output['covenant']['action'] not in ["BID", "REVEAL"]:
|
||||||
@@ -1679,7 +1681,7 @@ def verifyMessageWithName(domain, signature, message):
|
|||||||
return response['result']
|
return response['result']
|
||||||
return False
|
return False
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error verifying message with name: {str(e)}")
|
logger.error(f"Error verifying message with name: {str(e)}", exc_info=True)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
@@ -1690,7 +1692,7 @@ def verifyMessage(address, signature, message):
|
|||||||
return response['result']
|
return response['result']
|
||||||
return False
|
return False
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error verifying message: {str(e)}")
|
logger.error(f"Error verifying message: {str(e)}", exc_info=True)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
@@ -1835,7 +1837,7 @@ def hsdInit():
|
|||||||
prerequisites = checkPreRequisites()
|
prerequisites = checkPreRequisites()
|
||||||
|
|
||||||
minNodeVersion = HSD_CONFIG.get("minNodeVersion", 20)
|
minNodeVersion = HSD_CONFIG.get("minNodeVersion", 20)
|
||||||
minNPMVersion = HSD_CONFIG.get("minNPMVersion", 8)
|
minNPMVersion = HSD_CONFIG.get("minNpmVersion", 8)
|
||||||
PREREQ_MESSAGES = {
|
PREREQ_MESSAGES = {
|
||||||
"node": f"Install Node.js from https://nodejs.org/en/download (Version >= {minNodeVersion})",
|
"node": f"Install Node.js from https://nodejs.org/en/download (Version >= {minNodeVersion})",
|
||||||
"npm": f"Install npm (version >= {minNPMVersion}) - usually comes with Node.js",
|
"npm": f"Install npm (version >= {minNPMVersion}) - usually comes with Node.js",
|
||||||
@@ -1844,18 +1846,21 @@ def hsdInit():
|
|||||||
|
|
||||||
# Check if all prerequisites are met (except hsd)
|
# Check if all prerequisites are met (except hsd)
|
||||||
if not all(prerequisites[key] for key in prerequisites if key != "hsd"):
|
if not all(prerequisites[key] for key in prerequisites if key != "hsd"):
|
||||||
print("HSD Internal Node prerequisites not met:")
|
print("HSD Internal Node prerequisites not met:",flush=True)
|
||||||
|
logger.error("HSD Internal Node prerequisites not met:")
|
||||||
for key, value in prerequisites.items():
|
for key, value in prerequisites.items():
|
||||||
if not value:
|
if not value:
|
||||||
print(f" - {key} is missing or does not meet the version requirement.",flush=True)
|
print(f" - {key} is missing or does not meet the version requirement.",flush=True)
|
||||||
|
logger.error(f" - {key} is missing or does not meet the version requirement.")
|
||||||
if key in PREREQ_MESSAGES:
|
if key in PREREQ_MESSAGES:
|
||||||
print(PREREQ_MESSAGES[key],flush=True)
|
print(PREREQ_MESSAGES[key],flush=True)
|
||||||
|
logger.error(PREREQ_MESSAGES[key])
|
||||||
exit(1)
|
exit(1)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Check if hsd is installed
|
# Check if hsd is installed
|
||||||
if not prerequisites["hsd"]:
|
if not prerequisites["hsd"]:
|
||||||
print("HSD not found, installing...")
|
logger.info("HSD not found, installing...")
|
||||||
# If hsd folder exists, remove it
|
# If hsd folder exists, remove it
|
||||||
if os.path.exists("hsd"):
|
if os.path.exists("hsd"):
|
||||||
os.rmdir("hsd")
|
os.rmdir("hsd")
|
||||||
@@ -1863,19 +1868,22 @@ def hsdInit():
|
|||||||
# Clone hsd repo
|
# Clone hsd repo
|
||||||
gitClone = subprocess.run(["git", "clone", "--depth", "1", "--branch", HSD_CONFIG.get("version", "latest"), "https://github.com/handshake-org/hsd.git", "hsd"], capture_output=True, text=True)
|
gitClone = subprocess.run(["git", "clone", "--depth", "1", "--branch", HSD_CONFIG.get("version", "latest"), "https://github.com/handshake-org/hsd.git", "hsd"], capture_output=True, text=True)
|
||||||
if gitClone.returncode != 0:
|
if gitClone.returncode != 0:
|
||||||
print("Failed to clone hsd repository:")
|
print("Failed to clone hsd repository:",flush=True)
|
||||||
print(gitClone.stderr)
|
logger.error("Failed to clone hsd repository:")
|
||||||
|
print(gitClone.stderr,flush=True)
|
||||||
|
logger.error(gitClone.stderr)
|
||||||
exit(1)
|
exit(1)
|
||||||
print("Cloned hsd repository.")
|
logger.info("Cloned hsd repository.")
|
||||||
|
logger.info("Installing hsd dependencies...")
|
||||||
# Install hsd dependencies
|
# Install hsd dependencies
|
||||||
print("Installing hsd dependencies...")
|
|
||||||
npmInstall = subprocess.run(["npm", "install"], cwd="hsd", capture_output=True, text=True)
|
npmInstall = subprocess.run(["npm", "install"], cwd="hsd", capture_output=True, text=True)
|
||||||
if npmInstall.returncode != 0:
|
if npmInstall.returncode != 0:
|
||||||
print("Failed to install hsd dependencies:")
|
print("Failed to install hsd dependencies:",flush=True)
|
||||||
print(npmInstall.stderr)
|
logger.error("Failed to install hsd dependencies:")
|
||||||
|
print(npmInstall.stderr,flush=True)
|
||||||
|
logger.error(npmInstall.stderr)
|
||||||
exit(1)
|
exit(1)
|
||||||
print("Installed hsd dependencies.")
|
logger.info("Installed hsd dependencies.")
|
||||||
|
|
||||||
def hsdStart():
|
def hsdStart():
|
||||||
global HSD_PROCESS
|
global HSD_PROCESS
|
||||||
global SPV_MODE
|
global SPV_MODE
|
||||||
@@ -1886,12 +1894,12 @@ def hsdStart():
|
|||||||
if os.path.exists("hsd.lock"):
|
if os.path.exists("hsd.lock"):
|
||||||
lock_time = os.path.getmtime("hsd.lock")
|
lock_time = os.path.getmtime("hsd.lock")
|
||||||
if time.time() - lock_time < 30:
|
if time.time() - lock_time < 30:
|
||||||
print("HSD was started recently, skipping start.")
|
logger.info("HSD was started recently, skipping start.")
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
os.remove("hsd.lock")
|
os.remove("hsd.lock")
|
||||||
|
|
||||||
print("Starting HSD...")
|
logger.info("Starting HSD...")
|
||||||
# Create a lock file
|
# Create a lock file
|
||||||
with open("hsd.lock", "w") as f:
|
with open("hsd.lock", "w") as f:
|
||||||
f.write(str(time.time()))
|
f.write(str(time.time()))
|
||||||
@@ -1935,7 +1943,7 @@ def hsdStart():
|
|||||||
text=True
|
text=True
|
||||||
)
|
)
|
||||||
|
|
||||||
print(f"HSD started with PID {HSD_PROCESS.pid}")
|
logger.info(f"HSD started with PID {HSD_PROCESS.pid}")
|
||||||
|
|
||||||
atexit.register(hsdStop)
|
atexit.register(hsdStop)
|
||||||
|
|
||||||
@@ -1944,7 +1952,7 @@ def hsdStart():
|
|||||||
signal.signal(signal.SIGINT, lambda s, f: (hsdStop(), sys.exit(0)))
|
signal.signal(signal.SIGINT, lambda s, f: (hsdStop(), sys.exit(0)))
|
||||||
signal.signal(signal.SIGTERM, lambda s, f: (hsdStop(), sys.exit(0)))
|
signal.signal(signal.SIGTERM, lambda s, f: (hsdStop(), sys.exit(0)))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Failed to set signal handlers: {str(e)}")
|
logger.error(f"Failed to set signal handlers: {str(e)}", exc_info=True)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@@ -1954,16 +1962,16 @@ def hsdStop():
|
|||||||
if HSD_PROCESS is None:
|
if HSD_PROCESS is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
print("Stopping HSD...")
|
logger.info("Stopping HSD...")
|
||||||
|
|
||||||
# Send SIGINT (like Ctrl+C)
|
# Send SIGINT (like Ctrl+C)
|
||||||
HSD_PROCESS.send_signal(signal.SIGINT)
|
HSD_PROCESS.send_signal(signal.SIGINT)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
HSD_PROCESS.wait(timeout=10) # wait for graceful exit
|
HSD_PROCESS.wait(timeout=10) # wait for graceful exit
|
||||||
print("HSD shut down cleanly.")
|
logger.info("HSD shut down cleanly.")
|
||||||
except subprocess.TimeoutExpired:
|
except subprocess.TimeoutExpired:
|
||||||
print("HSD did not exit yet, is it alright???")
|
logger.warning("HSD did not exit yet, is it alright???")
|
||||||
|
|
||||||
# Clean up lock file
|
# Clean up lock file
|
||||||
if os.path.exists("hsd.lock"):
|
if os.path.exists("hsd.lock"):
|
||||||
|
|||||||
88
main.py
88
main.py
@@ -17,6 +17,8 @@ import plugin as plugins_module
|
|||||||
import gitinfo
|
import gitinfo
|
||||||
import datetime
|
import datetime
|
||||||
import time
|
import time
|
||||||
|
import logging
|
||||||
|
from logging.handlers import RotatingFileHandler
|
||||||
|
|
||||||
dotenv.load_dotenv()
|
dotenv.load_dotenv()
|
||||||
|
|
||||||
@@ -32,29 +34,16 @@ revokeCheck = random.randint(100000,999999)
|
|||||||
THEME = os.getenv("THEME", "black")
|
THEME = os.getenv("THEME", "black")
|
||||||
|
|
||||||
|
|
||||||
def blocks_to_time(blocks: int) -> str:
|
# Setup logging
|
||||||
"""
|
if not os.path.exists('logs'):
|
||||||
Convert blocks to time in a human-readable format.
|
os.makedirs('logs')
|
||||||
Blocks are mined approximately every 10 minutes.
|
log_file = 'logs/firewallet.log'
|
||||||
"""
|
handler = RotatingFileHandler(log_file, maxBytes=1024*1024, backupCount=3)
|
||||||
if blocks < 0:
|
formatter = logging.Formatter('[%(asctime)s] %(levelname)s in %(module)s: %(message)s')
|
||||||
return "Invalid time"
|
handler.setFormatter(formatter)
|
||||||
|
logger = logging.getLogger()
|
||||||
if blocks < 6:
|
logger.setLevel(logging.WARNING)
|
||||||
return f"{blocks * 10} mins"
|
logger.addHandler(handler)
|
||||||
elif blocks < 144:
|
|
||||||
hours = blocks // 6
|
|
||||||
minutes = (blocks % 6) * 10
|
|
||||||
if minutes == 0:
|
|
||||||
return f"{hours} hrs"
|
|
||||||
|
|
||||||
return f"{hours} hrs {minutes} mins"
|
|
||||||
else:
|
|
||||||
days = blocks // 144
|
|
||||||
hours = (blocks % 144) // 6
|
|
||||||
if hours == 0:
|
|
||||||
return f"{days} days"
|
|
||||||
return f"{days} days {hours} hrs"
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
def index():
|
def index():
|
||||||
@@ -892,7 +881,6 @@ def transferConfirm(domain):
|
|||||||
|
|
||||||
return redirect(f"/success?tx={response['hash']}")
|
return redirect(f"/success?tx={response['hash']}")
|
||||||
|
|
||||||
|
|
||||||
@app.route('/auction/<domain>')
|
@app.route('/auction/<domain>')
|
||||||
def auction(domain):
|
def auction(domain):
|
||||||
# Check if the user is logged in
|
# Check if the user is logged in
|
||||||
@@ -1249,7 +1237,27 @@ def settings_action(action):
|
|||||||
title="API Information",
|
title="API Information",
|
||||||
content=content)
|
content=content)
|
||||||
|
|
||||||
|
if action == "logs":
|
||||||
|
if not os.path.exists(log_file):
|
||||||
|
return jsonify({"error": "Log file not found"}), 404
|
||||||
|
try:
|
||||||
|
with open(log_file, 'rb') as f:
|
||||||
|
response = requests.put(f"https://upload.woodburn.au/{os.path.basename(log_file)}", data=f)
|
||||||
|
if response.status_code == 200 or response.status_code == 201:
|
||||||
|
url = response.text.strip().split('\n')[-1]
|
||||||
|
logger.info(f"Log upload successful: {url}")
|
||||||
|
return redirect(url)
|
||||||
|
else:
|
||||||
|
logger.error(f"Failed to upload log: {response.status_code} {response.text}")
|
||||||
|
return redirect(f"/settings?error=Failed to upload log: {response.status_code}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Exception during log upload: {e}", exc_info=True)
|
||||||
|
return redirect("/settings?error=An error occurred during log upload")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
logger.warning(f"Unknown settings action: {action}")
|
||||||
return redirect("/settings?error=Invalid action")
|
return redirect("/settings?error=Invalid action")
|
||||||
|
|
||||||
@app.route('/settings/upload', methods=['POST'])
|
@app.route('/settings/upload', methods=['POST'])
|
||||||
@@ -1865,6 +1873,29 @@ def api_status():
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Helper functions
|
#region Helper functions
|
||||||
|
def blocks_to_time(blocks: int) -> str:
|
||||||
|
"""
|
||||||
|
Convert blocks to time in a human-readable format.
|
||||||
|
Blocks are mined approximately every 10 minutes.
|
||||||
|
"""
|
||||||
|
if blocks < 0:
|
||||||
|
return "Invalid time"
|
||||||
|
|
||||||
|
if blocks < 6:
|
||||||
|
return f"{blocks * 10} mins"
|
||||||
|
elif blocks < 144:
|
||||||
|
hours = blocks // 6
|
||||||
|
minutes = (blocks % 6) * 10
|
||||||
|
if minutes == 0:
|
||||||
|
return f"{hours} hrs"
|
||||||
|
|
||||||
|
return f"{hours} hrs {minutes} mins"
|
||||||
|
else:
|
||||||
|
days = blocks // 144
|
||||||
|
hours = (blocks % 144) // 6
|
||||||
|
if hours == 0:
|
||||||
|
return f"{days} days"
|
||||||
|
return f"{days} days {hours} hrs"
|
||||||
|
|
||||||
def renderDomain(name: str) -> str:
|
def renderDomain(name: str) -> str:
|
||||||
"""
|
"""
|
||||||
@@ -2020,8 +2051,8 @@ def try_path(path):
|
|||||||
|
|
||||||
@app.errorhandler(404)
|
@app.errorhandler(404)
|
||||||
def page_not_found(e):
|
def page_not_found(e):
|
||||||
|
logger.warning(f"404 Not Found: {request.path}")
|
||||||
account = account_module.check_account(request.cookies.get("account"))
|
account = account_module.check_account(request.cookies.get("account"))
|
||||||
|
|
||||||
return render_template('404.html',account=account), 404
|
return render_template('404.html',account=account), 404
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -2042,8 +2073,13 @@ if __name__ == '__main__':
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Check to see if --debug is in the command line arguments
|
# Print logs to console if --debug is set
|
||||||
if "--debug" in sys.argv:
|
if "--debug" in sys.argv:
|
||||||
|
console_handler = logging.StreamHandler(sys.stdout)
|
||||||
|
# Use a simple format for console
|
||||||
|
console_formatter = logging.Formatter('%(message)s')
|
||||||
|
console_handler.setFormatter(console_formatter)
|
||||||
|
logger.addHandler(console_handler)
|
||||||
app.run(debug=True, host=host, port=port)
|
app.run(debug=True, host=host, port=port)
|
||||||
else:
|
else:
|
||||||
app.run(host=host, port=port)
|
app.run(host=host, port=port)
|
||||||
|
|||||||
@@ -133,7 +133,7 @@
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title">About</h4>
|
<h4 class="card-title">About</h4>
|
||||||
<h6 class="text-muted mb-2 card-subtitle">FireWallet is a UI to allow easy connection with HSD created by <a href="https://nathan.woodburn.au" target="_blank">Nathan.Woodburn/</a> and freely available. Please contact him <a href="https://l.woodburn.au/contact" target="_blank">here</a> if you would like to request any features or report any bugs.<br>FireWallet version: <code>{{version}}</code></h6>
|
<h6 class="text-muted mb-2 card-subtitle">FireWallet is a UI to allow easy connection with HSD created by <a href="https://nathan.woodburn.au" target="_blank">Nathan.Woodburn/</a> and freely available. Please contact him <a href="https://l.woodburn.au/contact" target="_blank">here</a> if you would like to request any features or report any bugs.<br>FireWallet version: <code>{{version}}</code></h6>
|
||||||
<div class="text-center"><a href="https://github.com/nathanwoodburn/firewalletbrowser" style="margin: 15px;color: var(--bs-emphasis-color);text-decoration:none;" target="_blank"><i class="icon ion-social-github" style="color: var(--bs-emphasis-color);"></i> Github</a><a href="https://firewallet.au" style="margin: 15px;color: var(--bs-emphasis-color);text-decoration:none;" target="_blank"><i class="icon ion-ios-information" style="color: var(--bs-emphasis-color);"></i> Website</a><a href="https://l.woodburn.au/donate" style="margin: 15px;color: var(--bs-emphasis-color);text-decoration:none;" target="_blank"><i class="icon ion-social-usd" style="color: var(--bs-emphasis-color);"></i> Donate to support development</a></div>
|
<div class="text-center"><a href="https://github.com/nathanwoodburn/firewalletbrowser" style="margin: 15px;color: var(--bs-emphasis-color);text-decoration:none;" target="_blank"><i class="icon ion-social-github" style="color: var(--bs-emphasis-color);"></i> Github</a><a href="https://firewallet.au" style="margin: 15px;color: var(--bs-emphasis-color);text-decoration:none;" target="_blank"><i class="icon ion-ios-information" style="color: var(--bs-emphasis-color);"></i> Website</a><a href="https://l.woodburn.au/donate" style="margin: 15px;color: var(--bs-emphasis-color);text-decoration:none;" target="_blank"><i class="icon ion-social-usd" style="color: var(--bs-emphasis-color);"></i> Donate to support development</a><a href="/settings/logs" style="margin: 15px;color: var(--bs-emphasis-color);text-decoration:none;" target="_blank"><i class="icon ion-help" style="color: var(--bs-emphasis-color);"></i> Upload logs for debugging</a></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user