4 Commits

Author SHA1 Message Date
c4cd2bc443 fix: Check if blocksSinceExpired to get expired domains
All checks were successful
Build Docker / Build Images (map[dockerfile:Dockerfile.hsd tag_suffix:-hsd target:hsd]) (push) Successful in 2m56s
Build Docker / Build Images (map[dockerfile:Dockerfile tag_suffix: target:default]) (push) Successful in 3m4s
Tests and Linting / Tests-Linting (3.11) (push) Successful in 3m6s
Tests and Linting / Tests-Linting (3.10) (push) Successful in 3m24s
Tests and Linting / Tests-Linting (3.13) (push) Successful in 3m24s
2025-09-18 16:35:34 +10:00
608933c228 fix: Calculate balance with expired domains 2025-09-18 16:34:45 +10:00
d9e847a995 feat: Add support for verbose sync status and don't require auth for some api routes
All checks were successful
Tests and Linting / Tests-Linting (3.11) (push) Successful in 28s
Tests and Linting / Tests-Linting (3.10) (push) Successful in 30s
Tests and Linting / Tests-Linting (3.13) (push) Successful in 30s
Build Docker / Build Images (map[dockerfile:Dockerfile.hsd tag_suffix:-hsd target:hsd]) (push) Successful in 45s
Build Docker / Build Images (map[dockerfile:Dockerfile tag_suffix: target:default]) (push) Successful in 48s
2025-09-16 22:16:29 +10:00
15d919ca97 feat: Add option to use http basic auth for api routes
All checks were successful
Build Docker / Build Images (map[dockerfile:Dockerfile.hsd tag_suffix:-hsd target:hsd]) (push) Successful in 3m2s
Build Docker / Build Images (map[dockerfile:Dockerfile tag_suffix: target:default]) (push) Successful in 3m8s
Tests and Linting / Tests-Linting (3.11) (push) Successful in 3m12s
Tests and Linting / Tests-Linting (3.13) (push) Successful in 3m19s
Tests and Linting / Tests-Linting (3.10) (push) Successful in 3m24s
This shoudl make it easier to use curl to access info
2025-09-16 16:42:09 +10:00
2 changed files with 60 additions and 28 deletions

View File

@@ -117,6 +117,8 @@ def check_account(cookie: str | None):
return False return False
account = cookie.split(":")[0] account = cookie.split(":")[0]
if len(account) < 1:
return False
# Check if the account is valid # Check if the account is valid
info = hsw.getAccountInfo(account, 'default') info = hsw.getAccountInfo(account, 'default')
if 'error' in info: if 'error' in info:
@@ -484,9 +486,14 @@ def getDomains(account, own: bool = True, expired: bool = SHOW_EXPIRED):
# Remove any expired domains # Remove any expired domains
domains = [] domains = []
for domain in info: for domain in info:
if 'stats' in domain: if 'stats' in domain and domain['stats'] is not None:
if 'daysUntilExpire' in domain['stats']: if 'daysUntilExpire' in domain['stats']:
if domain['stats']['daysUntilExpire'] < 0: if domain['stats']['daysUntilExpire'] < 0:
logger.debug(f"Excluding expired domain: {domain['name']} due to daysUntilExpire")
continue
if 'blocksSinceExpired' in domain['stats']:
if domain['stats']['blocksSinceExpired'] > 0:
logger.debug(f"Excluding expired domain: {domain['name']} due to blocksSinceExpired")
continue continue
domains.append(domain) domains.append(domain)
@@ -925,11 +932,14 @@ def getNodeSync():
return sync return sync
def getWalletStatus(): def getWalletStatus(verbose: bool = False):
response = hsw.rpc_getWalletInfo() response = hsw.rpc_getWalletInfo()
if 'error' in response and response['error'] is not None: if 'error' in response and response['error'] is not None:
return "Error" return "Error"
if verbose:
return response.get('result', {})
# return response # return response
walletHeight = response['result']['height'] walletHeight = response['result']['height']
# Get the current block height # Get the current block height
@@ -1566,9 +1576,15 @@ def getMempoolBids():
# region settingsAPIs # region settingsAPIs
def rescan(): def rescan(height:int = 0):
try: try:
response = hsw.walletRescan(0) response = hsw.walletRescan(height)
if 'success' in response and response['success'] is False:
return {
"error": {
"message": "Rescan already in progress"
}
}
return response return response
except Exception as e: except Exception as e:
return { return {

62
main.py
View File

@@ -1607,14 +1607,7 @@ def plugin_function(ptype,plugin,function):
#region API Routes #region API Routes
@app.route('/api/v1/hsd/<function>', methods=["GET"]) @app.route('/api/v1/hsd/<function>', methods=["GET"])
def api_hsd(function): def api_hsd(function):
# Check if the user is logged in
if request.cookies.get("account") is None:
return jsonify({"error": "Not logged in"})
account = account_module.check_account(request.cookies.get("account"))
if not account:
return jsonify({"error": "Invalid account"})
if function == "sync": if function == "sync":
return jsonify({"result": account_module.getNodeSync()}) return jsonify({"result": account_module.getNodeSync()})
if function == "version": if function == "version":
@@ -1623,8 +1616,22 @@ def api_hsd(function):
return jsonify({"result": account_module.getBlockHeight()}) return jsonify({"result": account_module.getBlockHeight()})
if function == "mempool": if function == "mempool":
return jsonify({"result": account_module.getMempoolTxs()}) return jsonify({"result": account_module.getMempoolTxs()})
if function == "mempoolBids":
# Check if the user is logged in for all other functions
account = None
if request.cookies.get("account") is not None:
account = account_module.check_account(request.cookies.get("account"))
# Allow login using http basic auth
if account is None and request.authorization is not None:
account = account_module.check_account(f"{request.authorization.username}:{request.authorization.password}")
if not account:
return jsonify({"error": "Not logged in"})
if function == "mempoolBids": # This is a heavy function so only allow for logged in users
return jsonify({"result": account_module.getMempoolBids()}) return jsonify({"result": account_module.getMempoolBids()})
if function == "nextAuctionState": if function == "nextAuctionState":
# Get the domain from the query parameters # Get the domain from the query parameters
domain = request.args.get('domain') domain = request.args.get('domain')
@@ -1708,21 +1715,30 @@ def api_hsd_mobile(function):
@app.route('/api/v1/wallet/<function>', methods=["GET"]) @app.route('/api/v1/wallet/<function>', methods=["GET"])
def api_wallet(function): def api_wallet(function):
# Check if the user is logged in
if request.cookies.get("account") is None: if function == "sync":
# Check if arg verbose is set
verbose = request.args.get('verbose', 'false').lower() == 'true'
return jsonify({"result": account_module.getWalletStatus(verbose)})
# Check if the user is logged in for all other functions
account = None
password = None
if request.cookies.get("account") is not None:
account = account_module.check_account(request.cookies.get("account"))
password = request.cookies.get("account","").split(":")[1]
# Allow login using http basic auth
if account is None and request.authorization is not None:
account = account_module.check_account(f"{request.authorization.username}:{request.authorization.password}")
password = request.authorization.password
if not account:
return jsonify({"error": "Not logged in"}) return jsonify({"error": "Not logged in"})
account = account_module.check_account(request.cookies.get("account")) if function == "balance":
if not account: return jsonify({"result": account_module.getBalance(account)})
return jsonify({"error": "Invalid account"})
password = request.cookies.get("account","").split(":")[1]
if not account:
return jsonify({"error": "Invalid account"})
if function == "sync":
return jsonify({"result": account_module.getWalletStatus()})
if function == "available": if function == "available":
return jsonify({"result": account_module.getBalance(account)['available']}) return jsonify({"result": account_module.getBalance(account)['available']})
if function == "total": if function == "total":
@@ -2021,8 +2037,8 @@ def get_alerts(account:str) -> list:
wallet_status = account_module.getWalletStatus() wallet_status = account_module.getWalletStatus()
if wallet_status != "Ready": if wallet_status != "Ready":
alerts.append({ alerts.append({
"name": "Wallet", "name": "Wallet Not Synced",
"output": f"The wallet is not synced ({wallet_status}). Please wait for it to sync." "output": "Please wait for it to sync."
}) })
# Try to read from notifications sqlite database # Try to read from notifications sqlite database
if os.path.exists("user_data/notifications.db"): if os.path.exists("user_data/notifications.db"):