feat: Add pagination for HSD v7 and return errors when node not connected
All checks were successful
Build Docker / Build Image (push) Successful in 52s
All checks were successful
Build Docker / Build Image (push) Successful in 52s
This commit is contained in:
parent
1962c9345e
commit
42e8f642b4
1
.gitignore
vendored
1
.gitignore
vendored
@ -13,3 +13,4 @@ plugins/signatures.json
|
|||||||
|
|
||||||
user_data/
|
user_data/
|
||||||
customPlugins/
|
customPlugins/
|
||||||
|
cache/
|
Binary file not shown.
140
account.py
140
account.py
@ -6,7 +6,7 @@ import requests
|
|||||||
import re
|
import re
|
||||||
import domainLookup
|
import domainLookup
|
||||||
import json
|
import json
|
||||||
|
import time
|
||||||
|
|
||||||
dotenv.load_dotenv()
|
dotenv.load_dotenv()
|
||||||
|
|
||||||
@ -22,6 +22,7 @@ if show_expired is None:
|
|||||||
hsd = api.hsd(APIKEY,ip)
|
hsd = api.hsd(APIKEY,ip)
|
||||||
hsw = api.hsw(APIKEY,ip)
|
hsw = api.hsw(APIKEY,ip)
|
||||||
|
|
||||||
|
cacheTime = 3600
|
||||||
|
|
||||||
# Verify the connection
|
# Verify the connection
|
||||||
response = hsd.getInfo()
|
response = hsd.getInfo()
|
||||||
@ -30,6 +31,17 @@ exclude = ["primary"]
|
|||||||
if os.getenv("exclude") is not None:
|
if os.getenv("exclude") is not None:
|
||||||
exclude = os.getenv("exclude").split(",")
|
exclude = os.getenv("exclude").split(",")
|
||||||
|
|
||||||
|
def hsdConnected():
|
||||||
|
if hsdVersion() == -1:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def hsdVersion():
|
||||||
|
info = hsd.getInfo()
|
||||||
|
if 'error' in info:
|
||||||
|
return -1
|
||||||
|
return float('.'.join(info['version'].split(".")[:2]))
|
||||||
|
|
||||||
def check_account(cookie: str):
|
def check_account(cookie: str):
|
||||||
if cookie is None:
|
if cookie is None:
|
||||||
return False
|
return False
|
||||||
@ -61,12 +73,15 @@ def check_password(cookie: str, password: str):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def createWallet(account: str, password: str):
|
def createWallet(account: str, password: str):
|
||||||
|
if not hsdConnected():
|
||||||
|
return {
|
||||||
|
"error": {
|
||||||
|
"message": "Node not connected"
|
||||||
|
}
|
||||||
|
}
|
||||||
# Create the account
|
# Create the account
|
||||||
# Python wrapper doesn't support this yet
|
# Python wrapper doesn't support this yet
|
||||||
response = requests.put(f"http://x:{APIKEY}@{ip}:12039/wallet/{account}")
|
response = requests.put(f"http://x:{APIKEY}@{ip}:12039/wallet/{account}")
|
||||||
print(response)
|
|
||||||
print(response.json())
|
|
||||||
|
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
return {
|
return {
|
||||||
"error": {
|
"error": {
|
||||||
@ -82,7 +97,6 @@ def createWallet(account: str, password: str):
|
|||||||
# Encrypt the wallet (python wrapper doesn't support this yet)
|
# Encrypt the wallet (python wrapper doesn't support this yet)
|
||||||
response = requests.post(f"http://x:{APIKEY}@{ip}:12039/wallet/{account}/passphrase",
|
response = requests.post(f"http://x:{APIKEY}@{ip}:12039/wallet/{account}/passphrase",
|
||||||
json={"passphrase": password})
|
json={"passphrase": password})
|
||||||
print(response)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"seed": seed,
|
"seed": seed,
|
||||||
@ -91,6 +105,13 @@ def createWallet(account: str, password: str):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def importWallet(account: str, password: str,seed: str):
|
def importWallet(account: str, password: str,seed: str):
|
||||||
|
if not hsdConnected():
|
||||||
|
return {
|
||||||
|
"error": {
|
||||||
|
"message": "Node not connected"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# Import the wallet
|
# Import the wallet
|
||||||
data = {
|
data = {
|
||||||
"passphrase": password,
|
"passphrase": password,
|
||||||
@ -98,9 +119,6 @@ def importWallet(account: str, password: str,seed: str):
|
|||||||
}
|
}
|
||||||
|
|
||||||
response = requests.put(f"http://x:{APIKEY}@{ip}:12039/wallet/{account}",json=data)
|
response = requests.put(f"http://x:{APIKEY}@{ip}:12039/wallet/{account}",json=data)
|
||||||
print(response)
|
|
||||||
print(response.json())
|
|
||||||
|
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
return {
|
return {
|
||||||
"error": {
|
"error": {
|
||||||
@ -127,6 +145,16 @@ def listWallets():
|
|||||||
return response
|
return response
|
||||||
return ['Wallet not connected']
|
return ['Wallet not connected']
|
||||||
|
|
||||||
|
def selectWallet(account: str):
|
||||||
|
# Select wallet
|
||||||
|
response = hsw.rpc_selectWallet(account)
|
||||||
|
if response['error'] is not None:
|
||||||
|
return {
|
||||||
|
"error": {
|
||||||
|
"message": response['error']['message']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def getBalance(account: str):
|
def getBalance(account: str):
|
||||||
# Get the total balance
|
# Get the total balance
|
||||||
info = hsw.getBalance('default',account)
|
info = hsw.getBalance('default',account)
|
||||||
@ -204,13 +232,97 @@ def getDomains(account,own=True):
|
|||||||
|
|
||||||
return domains
|
return domains
|
||||||
|
|
||||||
def getTransactions(account):
|
def getPageTXCache(account,page):
|
||||||
# Get the transactions
|
page = str(page)
|
||||||
info = hsw.getWalletTxHistory(account)
|
if not os.path.exists(f'cache'):
|
||||||
if 'error' in info:
|
os.mkdir(f'cache')
|
||||||
return []
|
|
||||||
return info
|
|
||||||
|
|
||||||
|
if not os.path.exists(f'cache/{account}_page.json'):
|
||||||
|
with open(f'cache/{account}_page.json', 'w') as f:
|
||||||
|
f.write('{}')
|
||||||
|
with open(f'cache/{account}_page.json') as f:
|
||||||
|
pageCache = json.load(f)
|
||||||
|
|
||||||
|
if page in pageCache and pageCache[page]['time'] > int(time.time()) - cacheTime:
|
||||||
|
return pageCache[page]['txid']
|
||||||
|
return None
|
||||||
|
|
||||||
|
def pushPageTXCache(account,page,txid):
|
||||||
|
page = str(page)
|
||||||
|
if not os.path.exists(f'cache/{account}_page.json'):
|
||||||
|
with open(f'cache/{account}_page.json', 'w') as f:
|
||||||
|
f.write('{}')
|
||||||
|
with open(f'cache/{account}_page.json') as f:
|
||||||
|
pageCache = json.load(f)
|
||||||
|
|
||||||
|
pageCache[page] = {
|
||||||
|
'time': int(time.time()),
|
||||||
|
'txid': txid
|
||||||
|
}
|
||||||
|
with open(f'cache/{account}_page.json', 'w') as f:
|
||||||
|
json.dump(pageCache, f,indent=4)
|
||||||
|
|
||||||
|
return pageCache[page]['txid']
|
||||||
|
|
||||||
|
def getTXFromPage(account,page):
|
||||||
|
if page == 1:
|
||||||
|
return getTransactions(account)[-1]['hash']
|
||||||
|
|
||||||
|
cached = getPageTXCache(account,page)
|
||||||
|
if cached:
|
||||||
|
return getPageTXCache(account,page)
|
||||||
|
previous = getTransactions(account,page)
|
||||||
|
if len(previous) == 0:
|
||||||
|
return None
|
||||||
|
hash = previous[-1]['hash']
|
||||||
|
pushPageTXCache(account,page,hash)
|
||||||
|
return hash
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def getTransactions(account,page=1,limit=100):
|
||||||
|
# Get the transactions
|
||||||
|
if hsdVersion() < 7:
|
||||||
|
info = hsw.getWalletTxHistory(account)
|
||||||
|
if 'error' in info:
|
||||||
|
return []
|
||||||
|
return info[::-1]
|
||||||
|
|
||||||
|
lastTX = None
|
||||||
|
if page < 1:
|
||||||
|
return []
|
||||||
|
if page > 1:
|
||||||
|
lastTX = getTXFromPage(account,page-1)
|
||||||
|
|
||||||
|
if lastTX:
|
||||||
|
response = requests.get(f'http://x:{APIKEY}@{ip}:12039/wallet/{account}/tx/history?reverse=true&limit={limit}&after={lastTX}')
|
||||||
|
elif page == 1:
|
||||||
|
response = requests.get(f'http://x:{APIKEY}@{ip}:12039/wallet/{account}/tx/history?reverse=true&limit={limit}')
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
print(response.text)
|
||||||
|
return []
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
# Refresh the cache if the next page is different
|
||||||
|
nextPage = getPageTXCache(account,page)
|
||||||
|
if nextPage is not None and nextPage != data[-1]['hash']:
|
||||||
|
print(f'Refreshing page {page}')
|
||||||
|
pushPageTXCache(account,page,data[-1]['hash'])
|
||||||
|
return data
|
||||||
|
|
||||||
|
def getAllTransactions(account):
|
||||||
|
# Get the transactions
|
||||||
|
page = 0
|
||||||
|
txs = []
|
||||||
|
while True:
|
||||||
|
txs += getTransactions(account,page)
|
||||||
|
if len(txs) == 0:
|
||||||
|
break
|
||||||
|
page += 1
|
||||||
|
return txs
|
||||||
|
|
||||||
def check_address(address: str, allow_name: bool = True, return_address: bool = False):
|
def check_address(address: str, allow_name: bool = True, return_address: bool = False):
|
||||||
# Check if the address is valid
|
# Check if the address is valid
|
||||||
|
23
main.py
23
main.py
@ -125,13 +125,22 @@ def transactions():
|
|||||||
return redirect("/login")
|
return redirect("/login")
|
||||||
|
|
||||||
account = account_module.check_account(request.cookies.get("account"))
|
account = account_module.check_account(request.cookies.get("account"))
|
||||||
|
|
||||||
# Get the transactions
|
# Get the transactions
|
||||||
transactions = account_module.getTransactions(account)
|
page = request.args.get('page')
|
||||||
transactions = render.transactions(transactions)
|
try:
|
||||||
|
page = int(page)
|
||||||
|
except:
|
||||||
|
page = 1
|
||||||
|
|
||||||
|
if page < 1:
|
||||||
|
page = 1
|
||||||
|
|
||||||
|
transactions = account_module.getTransactions(account,page)
|
||||||
|
txCount = len(transactions)
|
||||||
|
transactions = render.transactions(transactions)
|
||||||
return render_template("tx.html", account=account, sync=account_module.getNodeSync(),
|
return render_template("tx.html", account=account, sync=account_module.getNodeSync(),
|
||||||
wallet_status=account_module.getWalletStatus(),tx=transactions)
|
wallet_status=account_module.getWalletStatus(),tx=transactions,
|
||||||
|
page=page,txCount=txCount)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/send')
|
@app.route('/send')
|
||||||
@ -1435,6 +1444,12 @@ def send_assets(path):
|
|||||||
# Try path
|
# Try path
|
||||||
@app.route('/<path:path>')
|
@app.route('/<path:path>')
|
||||||
def try_path(path):
|
def try_path(path):
|
||||||
|
# Check if node connected
|
||||||
|
if not account_module.hsdConnected():
|
||||||
|
return redirect("/login?message=Node not connected")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if os.path.isfile("templates/" + path + ".html"):
|
if os.path.isfile("templates/" + path + ".html"):
|
||||||
return render_template(path + ".html")
|
return render_template(path + ".html")
|
||||||
else:
|
else:
|
||||||
|
@ -29,7 +29,14 @@ functions = {
|
|||||||
|
|
||||||
def main(params, authentication):
|
def main(params, authentication):
|
||||||
wallet = authentication.split(":")[0]
|
wallet = authentication.split(":")[0]
|
||||||
txs = account.getTransactions(wallet)
|
txCount = 0
|
||||||
|
page = 1
|
||||||
|
while True:
|
||||||
|
txs = account.getTransactions(wallet,page)
|
||||||
|
if len(txs) == 0:
|
||||||
|
break
|
||||||
|
txCount += len(txs)
|
||||||
|
page += 1
|
||||||
|
|
||||||
return {"txs": f'Total TXs: {len(txs)}'}
|
return {"txs": f'Total TXs: {txCount}'}
|
||||||
|
|
@ -29,11 +29,10 @@ def domains(domains, mobile=False):
|
|||||||
return html
|
return html
|
||||||
|
|
||||||
def transactions(txs):
|
def transactions(txs):
|
||||||
|
|
||||||
|
if len(txs) == 0:
|
||||||
|
return '<tr><td colspan="5">No transactions found</td></tr>'
|
||||||
html = ''
|
html = ''
|
||||||
|
|
||||||
# Reverse the list
|
|
||||||
txs = txs[::-1]
|
|
||||||
|
|
||||||
for tx in txs:
|
for tx in txs:
|
||||||
action = "HNS Transfer"
|
action = "HNS Transfer"
|
||||||
address = tx["outputs"][0]["address"]
|
address = tx["outputs"][0]["address"]
|
||||||
|
@ -65,8 +65,17 @@
|
|||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<h3 class="text-dark mb-4">Transactions</h3>
|
<h3 class="text-dark mb-4">Transactions</h3>
|
||||||
<div class="card shadow">
|
<div class="card shadow">
|
||||||
<div class="card-header py-3">
|
<div class="card-header py-3" style="padding: 16px;">
|
||||||
<p class="text-primary m-0 fw-bold">Transactions</p>
|
<div style="height: 38px;">
|
||||||
|
<p class="text-primary m-0 fw-bold" style="display: inline-block;">Transactions</p><div class="btn-group" role="group" style="right: 16px;top: 16px;position: absolute;height: 38px;">
|
||||||
|
{% if page != 1 %}
|
||||||
|
<a class="btn btn-primary" role="button" href="/tx?page={{page-1}}">Prev</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if txCount == 100 %}
|
||||||
|
<a class="btn btn-primary" role="button" href="/tx?page={{page+1}}">Next</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body"><div id="dataTable" class="table-responsive table mt-2" role="grid" aria-describedby="dataTable_info">
|
<div class="card-body"><div id="dataTable" class="table-responsive table mt-2" role="grid" aria-describedby="dataTable_info">
|
||||||
<table id="dataTable" class="table my-0">
|
<table id="dataTable" class="table my-0">
|
||||||
|
Loading…
Reference in New Issue
Block a user