Compare commits
6 Commits
50164e9d5d
...
a877b5bf9e
| Author | SHA1 | Date | |
|---|---|---|---|
|
a877b5bf9e
|
|||
|
22d301581b
|
|||
|
a888a3bd55
|
|||
|
c247aef2d5
|
|||
|
0118200098
|
|||
|
7e861534a6
|
Binary file not shown.
14
README.md
14
README.md
@@ -35,13 +35,13 @@ Also available as a docker image:
|
|||||||
To run using a HSD running directly on the host:
|
To run using a HSD running directly on the host:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo docker run --network=host -e hsd_api=yourapikeyhere git.woodburn.au/nathanwoodburn/firewallet:latest
|
sudo docker run --network=host -e HSD_API=yourapikeyhere git.woodburn.au/nathanwoodburn/firewallet:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
If you have HSD running on a different IP/container
|
If you have HSD running on a different IP/container
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo docker run -p 5000:5000 -e hsd_api=yourapikeyhere -e hsd_ip=hsdcontainer git.woodburn.au/nathanwoodburn/firewallet:latest
|
sudo docker run -p 5000:5000 -e HSD_API=yourapikeyhere -e HSD_IP=hsdcontainer git.woodburn.au/nathanwoodburn/firewallet:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
For Docker you can mount a volume to persist the user data (/app/user_data)
|
For Docker you can mount a volume to persist the user data (/app/user_data)
|
||||||
@@ -115,11 +115,11 @@ Auction page
|
|||||||
## Environment variables
|
## Environment variables
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
hsd_api: HSD API key
|
HSD_API: HSD API key
|
||||||
hsd_ip: HSD IP address
|
HSD_IP: HSD IP address
|
||||||
theme: Theme to use (dark-purple, black)
|
THEME: Theme to use (dark-purple, black)
|
||||||
show_expired: Show expired domains (true/false)
|
SHOW_EXPIRED: Show expired domains (true/false)
|
||||||
exclude: Comma separated list of wallets to exclude from the wallet list
|
EXCLUDE: Comma separated list of wallets to exclude from the wallet list
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
131
account.py
131
account.py
@@ -10,12 +10,12 @@ import time
|
|||||||
|
|
||||||
dotenv.load_dotenv()
|
dotenv.load_dotenv()
|
||||||
|
|
||||||
HSD_API = os.getenv("hsd_api")
|
HSD_API = os.getenv("HSD_API")
|
||||||
HSD_IP = os.getenv("hsd_ip")
|
HSD_IP = os.getenv("HSD_IP")
|
||||||
if HSD_IP is None:
|
if HSD_IP is None:
|
||||||
HSD_IP = "localhost"
|
HSD_IP = "localhost"
|
||||||
|
|
||||||
HSD_NETWORK = os.getenv("hsd_network")
|
HSD_NETWORK = os.getenv("HSD_NETWORK")
|
||||||
HSD_WALLET_PORT = 12039
|
HSD_WALLET_PORT = 12039
|
||||||
HSD_NODE_PORT = 12037
|
HSD_NODE_PORT = 12037
|
||||||
|
|
||||||
@@ -35,9 +35,9 @@ elif HSD_NETWORK == "regtest":
|
|||||||
HSD_NODE_PORT = 14037
|
HSD_NODE_PORT = 14037
|
||||||
|
|
||||||
|
|
||||||
show_expired = os.getenv("show_expired")
|
SHOW_EXPIRED = os.getenv("SHOW_EXPIRED")
|
||||||
if show_expired is None:
|
if SHOW_EXPIRED is None:
|
||||||
show_expired = False
|
SHOW_EXPIRED = False
|
||||||
|
|
||||||
hsd = api.hsd(HSD_API,HSD_IP,HSD_NODE_PORT)
|
hsd = api.hsd(HSD_API,HSD_IP,HSD_NODE_PORT)
|
||||||
hsw = api.hsw(HSD_API,HSD_IP,HSD_WALLET_PORT)
|
hsw = api.hsw(HSD_API,HSD_IP,HSD_WALLET_PORT)
|
||||||
@@ -47,9 +47,9 @@ cacheTime = 3600
|
|||||||
# Verify the connection
|
# Verify the connection
|
||||||
response = hsd.getInfo()
|
response = hsd.getInfo()
|
||||||
|
|
||||||
exclude = ["primary"]
|
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():
|
def hsdConnected():
|
||||||
if hsdVersion() == -1:
|
if hsdVersion() == -1:
|
||||||
@@ -163,7 +163,7 @@ def listWallets():
|
|||||||
# Check if response is json or an array
|
# Check if response is json or an array
|
||||||
if isinstance(response, list):
|
if isinstance(response, list):
|
||||||
# Remove excluded wallets
|
# Remove excluded wallets
|
||||||
response = [wallet for wallet in response if wallet not in exclude]
|
response = [wallet for wallet in response if wallet not in EXCLUDE]
|
||||||
|
|
||||||
return response
|
return response
|
||||||
return ['Wallet not connected']
|
return ['Wallet not connected']
|
||||||
@@ -244,7 +244,7 @@ def getDomains(account,own=True):
|
|||||||
response = requests.get(f"http://x:{HSD_API}@{HSD_IP}:{HSD_WALLET_PORT}/wallet/{account}/name")
|
response = requests.get(f"http://x:{HSD_API}@{HSD_IP}:{HSD_WALLET_PORT}/wallet/{account}/name")
|
||||||
info = response.json()
|
info = response.json()
|
||||||
|
|
||||||
if show_expired:
|
if SHOW_EXPIRED:
|
||||||
return info
|
return info
|
||||||
|
|
||||||
# Remove any expired domains
|
# Remove any expired domains
|
||||||
@@ -433,6 +433,15 @@ def send(account,address,amount):
|
|||||||
"tx": response['result']
|
"tx": response['result']
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def isOwnDomain(account,name: str):
|
||||||
|
domains = getDomains(account)
|
||||||
|
for domain in domains:
|
||||||
|
print(domain)
|
||||||
|
if domain['name'] == name:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def getDomain(domain: str):
|
def getDomain(domain: str):
|
||||||
# Get the domain
|
# Get the domain
|
||||||
response = hsd.rpc_getNameInfo(domain)
|
response = hsd.rpc_getNameInfo(domain)
|
||||||
@@ -476,7 +485,6 @@ def getDNS(domain: str):
|
|||||||
return []
|
return []
|
||||||
return response['result']['records']
|
return response['result']['records']
|
||||||
|
|
||||||
|
|
||||||
def setDNS(account,domain,records):
|
def setDNS(account,domain,records):
|
||||||
account_name = check_account(account)
|
account_name = check_account(account)
|
||||||
password = ":".join(account.split(":")[1:])
|
password = ":".join(account.split(":")[1:])
|
||||||
@@ -521,6 +529,9 @@ def setDNS(account,domain,records):
|
|||||||
response = hsw.sendUPDATE(account_name,password,domain,data)
|
response = hsw.sendUPDATE(account_name,password,domain,data)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
def register(account,domain):
|
||||||
|
# Maybe add default dns records?
|
||||||
|
return setDNS(account,domain,'[]')
|
||||||
|
|
||||||
def getNodeSync():
|
def getNodeSync():
|
||||||
response = hsd.getInfo()
|
response = hsd.getInfo()
|
||||||
@@ -566,6 +577,43 @@ def getBids(account, domain="NONE"):
|
|||||||
def getReveals(account,domain):
|
def getReveals(account,domain):
|
||||||
return hsw.getWalletRevealsByName(domain,account)
|
return hsw.getWalletRevealsByName(domain,account)
|
||||||
|
|
||||||
|
def getPendingReveals(account):
|
||||||
|
bids = getBids(account)
|
||||||
|
domains = getDomains(account,False)
|
||||||
|
pending = []
|
||||||
|
for domain in domains:
|
||||||
|
if domain['state'] == "REVEAL":
|
||||||
|
reveals = getReveals(account,domain['name'])
|
||||||
|
for bid in bids:
|
||||||
|
if bid['name'] == domain['name']:
|
||||||
|
state_found = False
|
||||||
|
for reveal in reveals:
|
||||||
|
if reveal['own'] == True:
|
||||||
|
if bid['value'] == reveal['value']:
|
||||||
|
state_found = True
|
||||||
|
|
||||||
|
if not state_found:
|
||||||
|
pending.append(bid)
|
||||||
|
return pending
|
||||||
|
|
||||||
|
#! TODO
|
||||||
|
def getPendingRedeems(account):
|
||||||
|
bids = getBids(account)
|
||||||
|
domains = getDomains(account,False)
|
||||||
|
pending = []
|
||||||
|
return pending
|
||||||
|
|
||||||
|
def getPendingRegisters(account):
|
||||||
|
bids = getBids(account)
|
||||||
|
domains = getDomains(account,False)
|
||||||
|
pending = []
|
||||||
|
for domain in domains:
|
||||||
|
if domain['state'] == "CLOSED" and domain['registered'] == False:
|
||||||
|
for bid in bids:
|
||||||
|
if bid['name'] == domain['name']:
|
||||||
|
if bid['value'] == domain['highest']:
|
||||||
|
pending.append(bid)
|
||||||
|
return pending
|
||||||
|
|
||||||
def getRevealTX(reveal):
|
def getRevealTX(reveal):
|
||||||
prevout = reveal['prevout']
|
prevout = reveal['prevout']
|
||||||
@@ -573,7 +621,11 @@ 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:
|
||||||
# Check if registered
|
print(f'Something is up with this tx: {hash}')
|
||||||
|
print(tx)
|
||||||
|
print('---')
|
||||||
|
# No idea what happened here
|
||||||
|
# Check if registered?
|
||||||
return None
|
return None
|
||||||
return tx['inputs'][index]['prevout']['hash']
|
return tx['inputs'][index]['prevout']['hash']
|
||||||
|
|
||||||
@@ -616,7 +668,6 @@ def revealAll(account):
|
|||||||
response = hsw.rpc_walletPassphrase(password,10)
|
response = hsw.rpc_walletPassphrase(password,10)
|
||||||
if response['error'] is not None:
|
if response['error'] is not None:
|
||||||
return
|
return
|
||||||
# Try to send the batch of all renew, reveal and redeem actions
|
|
||||||
|
|
||||||
return requests.post(f"http://x:{HSD_API}@{HSD_IP}:{HSD_WALLET_PORT}",json={"method": "sendbatch","params": [[["REVEAL"]]]}).json()
|
return requests.post(f"http://x:{HSD_API}@{HSD_IP}:{HSD_WALLET_PORT}",json={"method": "sendbatch","params": [[["REVEAL"]]]}).json()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -626,6 +677,58 @@ def revealAll(account):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def redeemAll(account):
|
||||||
|
account_name = check_account(account)
|
||||||
|
password = ":".join(account.split(":")[1:])
|
||||||
|
|
||||||
|
if account_name == False:
|
||||||
|
return {
|
||||||
|
"error": {
|
||||||
|
"message": "Invalid account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Try to select and login to the wallet
|
||||||
|
response = hsw.rpc_selectWallet(account_name)
|
||||||
|
if response['error'] is not None:
|
||||||
|
return
|
||||||
|
response = hsw.rpc_walletPassphrase(password,10)
|
||||||
|
if response['error'] is not None:
|
||||||
|
return
|
||||||
|
|
||||||
|
return requests.post(f"http://x:{HSD_API}@{HSD_IP}:{HSD_WALLET_PORT}",json={"method": "sendbatch","params": [[["REDEEM"]]]}).json()
|
||||||
|
except Exception as e:
|
||||||
|
return {
|
||||||
|
"error": {
|
||||||
|
"message": str(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def registerAll(account):
|
||||||
|
account_name = check_account(account)
|
||||||
|
password = ":".join(account.split(":")[1:])
|
||||||
|
|
||||||
|
if account_name == False:
|
||||||
|
return {
|
||||||
|
"error": {
|
||||||
|
"message": "Invalid account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# try:
|
||||||
|
domains = getPendingRegisters(account_name)
|
||||||
|
if len(domains) == 0:
|
||||||
|
return {
|
||||||
|
"error": {
|
||||||
|
"message": "Nothing to do."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
batch = []
|
||||||
|
for domain in domains:
|
||||||
|
batch.append(["UPDATE",domain['name'],{"records":[]}])
|
||||||
|
return sendBatch(account,batch)
|
||||||
|
|
||||||
def rescan_auction(account,domain):
|
def rescan_auction(account,domain):
|
||||||
# Get height of the start of the auction
|
# Get height of the start of the auction
|
||||||
response = hsw.rpc_selectWallet(account)
|
response = hsw.rpc_selectWallet(account)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
hsd_api=123480615465636893475aCwyaae6s45
|
HSD_API=123480615465636893475aCwyaae6s45
|
||||||
hsd_ip=localhost
|
HSD_IP=localhost
|
||||||
theme=black
|
THEME=black
|
||||||
show_expired=false
|
SHOW_EXPIRED=false
|
||||||
|
EXPLORER_TX=https://niami.io/tx/
|
||||||
110
main.py
110
main.py
@@ -26,7 +26,7 @@ fees = 0.02
|
|||||||
revokeCheck = random.randint(100000,999999)
|
revokeCheck = random.randint(100000,999999)
|
||||||
|
|
||||||
|
|
||||||
theme = os.getenv("theme")
|
THEME = os.getenv("THEME")
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
def index():
|
def index():
|
||||||
@@ -322,19 +322,7 @@ def auctions():
|
|||||||
bidsHtml = render.bidDomains(bids,domains)
|
bidsHtml = render.bidDomains(bids,domains)
|
||||||
|
|
||||||
|
|
||||||
pending_reveals = 0
|
|
||||||
for domain in domains:
|
|
||||||
if domain['state'] == "REVEAL":
|
|
||||||
for bid in bids:
|
|
||||||
if bid['name'] == domain['name']:
|
|
||||||
bid_found = False
|
|
||||||
reveals = account_module.getReveals(account,domain['name'])
|
|
||||||
for reveal in reveals:
|
|
||||||
if reveal['own'] == True:
|
|
||||||
if bid['value'] == reveal['value']:
|
|
||||||
bid_found = True
|
|
||||||
if not bid_found:
|
|
||||||
pending_reveals += 1
|
|
||||||
|
|
||||||
plugins = ""
|
plugins = ""
|
||||||
|
|
||||||
@@ -347,10 +335,11 @@ def auctions():
|
|||||||
sort_state=sort_state,sort_domain=sort_domain,
|
sort_state=sort_state,sort_domain=sort_domain,
|
||||||
sort_price_next=sort_price_next,
|
sort_price_next=sort_price_next,
|
||||||
sort_state_next=sort_state_next,sort_domain_next=sort_domain_next,
|
sort_state_next=sort_state_next,sort_domain_next=sort_domain_next,
|
||||||
bids=len(bids),reveal=pending_reveals,message=message,
|
bids=len(bids),message=message,
|
||||||
sort_time=sort_time,sort_time_next=sort_time_next)
|
sort_time=sort_time,sort_time_next=sort_time_next)
|
||||||
|
|
||||||
@app.route('/reveal')
|
@app.route('/reveal')
|
||||||
|
@app.route('/all/reveal')
|
||||||
def revealAllBids():
|
def revealAllBids():
|
||||||
# Check if the user is logged in
|
# Check if the user is logged in
|
||||||
if request.cookies.get("account") is None:
|
if request.cookies.get("account") is None:
|
||||||
@@ -371,6 +360,46 @@ def revealAllBids():
|
|||||||
return redirect("/success?tx=" + response['result']['hash'])
|
return redirect("/success?tx=" + response['result']['hash'])
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/all/redeem')
|
||||||
|
def redeemAllBids():
|
||||||
|
# Check if the user is logged in
|
||||||
|
if request.cookies.get("account") is None:
|
||||||
|
return redirect("/login")
|
||||||
|
|
||||||
|
account = account_module.check_account(request.cookies.get("account"))
|
||||||
|
if not account:
|
||||||
|
return redirect("/logout")
|
||||||
|
|
||||||
|
response = account_module.redeemAll(request.cookies.get("account"))
|
||||||
|
if 'error' in response:
|
||||||
|
print(response)
|
||||||
|
if response['error'] != None:
|
||||||
|
if response['error']['message'] == "Nothing to do.":
|
||||||
|
return redirect("/auctions?message=No redeems pending")
|
||||||
|
return redirect("/auctions?message=" + response['error']['message'])
|
||||||
|
|
||||||
|
return redirect("/success?tx=" + response['result']['hash'])
|
||||||
|
|
||||||
|
@app.route('/all/register')
|
||||||
|
def registerAllDomains():
|
||||||
|
# Check if the user is logged in
|
||||||
|
if request.cookies.get("account") is None:
|
||||||
|
return redirect("/login")
|
||||||
|
|
||||||
|
account = account_module.check_account(request.cookies.get("account"))
|
||||||
|
if not account:
|
||||||
|
return redirect("/logout")
|
||||||
|
|
||||||
|
response = account_module.registerAll(request.cookies.get("account"))
|
||||||
|
if 'error' in response:
|
||||||
|
print(response)
|
||||||
|
if response['error'] != None:
|
||||||
|
if response['error']['message'] == "Nothing to do.":
|
||||||
|
return redirect("/auctions?message=No domains to register")
|
||||||
|
return redirect("/auctions?message=" + response['error']['message'])
|
||||||
|
|
||||||
|
return redirect("/success?tx=" + response['hash'])
|
||||||
|
|
||||||
@app.route('/search')
|
@app.route('/search')
|
||||||
def search():
|
def search():
|
||||||
# Check if the user is logged in
|
# Check if the user is logged in
|
||||||
@@ -845,7 +874,11 @@ def auction(domain):
|
|||||||
error=error)
|
error=error)
|
||||||
|
|
||||||
if domainInfo['info'] is None:
|
if domainInfo['info'] is None:
|
||||||
next_action = f'<a href="/auction/{domain}/open">Open Auction</a>'
|
if domainInfo['registered'] == False and domainInfo['expired'] == False:
|
||||||
|
# Needs to be registered
|
||||||
|
next_action = f'ERROR GETTING NEXT STATE'
|
||||||
|
else:
|
||||||
|
next_action = f'<a href="/auction/{domain}/open">Open Auction</a>'
|
||||||
return render_template("auction.html", account=account,
|
return render_template("auction.html", account=account,
|
||||||
|
|
||||||
search_term=search_term,domain=search_term,next_action=next_action,
|
search_term=search_term,domain=search_term,next_action=next_action,
|
||||||
@@ -871,9 +904,17 @@ def auction(domain):
|
|||||||
|
|
||||||
if state == 'CLOSED':
|
if state == 'CLOSED':
|
||||||
if not domainInfo['info']['registered']:
|
if not domainInfo['info']['registered']:
|
||||||
state = 'AVAILABLE'
|
if account_module.isOwnDomain(account,domain):
|
||||||
next = "Available Now"
|
print("Waiting to be registered")
|
||||||
next_action = f'<a href="/auction/{domain}/open">Open Auction</a>'
|
state = 'PENDING REGISTER'
|
||||||
|
next = "Pending Register"
|
||||||
|
next_action = f'<a href="/auction/{domain}/register">Register Domain</a>'
|
||||||
|
|
||||||
|
else:
|
||||||
|
print("Not registered")
|
||||||
|
state = 'AVAILABLE'
|
||||||
|
next = "Available Now"
|
||||||
|
next_action = f'<a href="/auction/{domain}/open">Open Auction</a>'
|
||||||
else:
|
else:
|
||||||
state = 'REGISTERED'
|
state = 'REGISTERED'
|
||||||
expires = domainInfo['info']['stats']['daysUntilExpire']
|
expires = domainInfo['info']['stats']['daysUntilExpire']
|
||||||
@@ -1030,7 +1071,22 @@ def reveal_auction(domain):
|
|||||||
return redirect("/logout")
|
return redirect("/logout")
|
||||||
|
|
||||||
domain = domain.lower()
|
domain = domain.lower()
|
||||||
response = account_module.revealAuction(request.cookies.get("account"),domain)
|
response = account_module(request.cookies.get("account"),domain)
|
||||||
|
if 'error' in response:
|
||||||
|
return redirect("/auction/" + domain + "?message=" + response['error']['message'])
|
||||||
|
return redirect("/success?tx=" + response['hash'])
|
||||||
|
|
||||||
|
@app.route('/auction/<domain>/register')
|
||||||
|
def registerdomain(domain):
|
||||||
|
# Check if the user is logged in
|
||||||
|
if request.cookies.get("account") is None:
|
||||||
|
return redirect("/login")
|
||||||
|
|
||||||
|
if not account_module.check_account(request.cookies.get("account")):
|
||||||
|
return redirect("/logout")
|
||||||
|
|
||||||
|
domain = domain.lower()
|
||||||
|
response = account_module.register(request.cookies.get("account"),domain)
|
||||||
if 'error' in response:
|
if 'error' in response:
|
||||||
return redirect("/auction/" + domain + "?message=" + response['error']['message'])
|
return redirect("/auction/" + domain + "?message=" + response['error']['message'])
|
||||||
return redirect("/success?tx=" + response['hash'])
|
return redirect("/success?tx=" + response['hash'])
|
||||||
@@ -1449,6 +1505,16 @@ def api_wallet(function):
|
|||||||
|
|
||||||
if function == "domainCount":
|
if function == "domainCount":
|
||||||
return jsonify({"result": len(account_module.getDomains(account))})
|
return jsonify({"result": len(account_module.getDomains(account))})
|
||||||
|
|
||||||
|
if function == "bidCount":
|
||||||
|
return jsonify({"result": len(account_module.getBids(account))})
|
||||||
|
|
||||||
|
if function == "pendingReveal":
|
||||||
|
return jsonify({"result": len(account_module.getPendingReveals(account))})
|
||||||
|
if function == "pendingRegister":
|
||||||
|
return jsonify({"result": len(account_module.getPendingRegisters(account))})
|
||||||
|
if function == "pendingRedeem":
|
||||||
|
return jsonify({"result": len(account_module.getPendingRedeems(account))})
|
||||||
|
|
||||||
|
|
||||||
return jsonify({"error": "Invalid function", "result": "Invalid function"}), 400
|
return jsonify({"error": "Invalid function", "result": "Invalid function"}), 400
|
||||||
@@ -1466,9 +1532,9 @@ def qr(data):
|
|||||||
# Theme
|
# Theme
|
||||||
@app.route('/assets/css/styles.min.css')
|
@app.route('/assets/css/styles.min.css')
|
||||||
def send_css():
|
def send_css():
|
||||||
if theme == "live":
|
if THEME == "live":
|
||||||
return send_from_directory('templates/assets/css', 'styles.min.css')
|
return send_from_directory('templates/assets/css', 'styles.min.css')
|
||||||
return send_from_directory('themes', f'{theme}.css')
|
return send_from_directory('themes', f'{THEME}.css')
|
||||||
|
|
||||||
@app.route('/assets/<path:path>')
|
@app.route('/assets/<path:path>')
|
||||||
def send_assets(path):
|
def send_assets(path):
|
||||||
|
|||||||
67
render.py
67
render.py
@@ -3,6 +3,14 @@ import json
|
|||||||
import urllib.parse
|
import urllib.parse
|
||||||
from flask import render_template
|
from flask import render_template
|
||||||
from domainLookup import punycode_to_emoji
|
from domainLookup import punycode_to_emoji
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Get Explorer URL
|
||||||
|
TX_EXPLORER_URL = os.getenv("EXPLORER_TX")
|
||||||
|
if TX_EXPLORER_URL is None:
|
||||||
|
TX_EXPLORER_URL = "https://niami.io/tx/"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def domains(domains, mobile=False):
|
def domains(domains, mobile=False):
|
||||||
html = ''
|
html = ''
|
||||||
@@ -21,10 +29,17 @@ def domains(domains, mobile=False):
|
|||||||
if emoji != name:
|
if emoji != name:
|
||||||
name = f'{emoji} ({name})'
|
name = f'{emoji} ({name})'
|
||||||
|
|
||||||
|
|
||||||
|
link = f'/manage/{domain["name"]}'
|
||||||
|
link_action = "Manage"
|
||||||
|
if domain['registered'] == False:
|
||||||
|
link_action = "Register"
|
||||||
|
link = f'/auction/{domain["name"]}/register'
|
||||||
|
|
||||||
if not mobile:
|
if not mobile:
|
||||||
html += f'<tr><td>{name}</td><td>{expires} days</td><td>{paid} HNS</td><td><a href="/manage/{domain["name"]}">Manage</a></td></tr>'
|
html += f'<tr><td>{name}</td><td>{expires} days</td><td>{paid:,.2f} HNS</td><td><a href="{link}">{link_action}</a></td></tr>'
|
||||||
else:
|
else:
|
||||||
html += f'<tr><td><a href="/manage/{domain["name"]}">{name}</a></td><td>{expires} days</td></tr>'
|
html += f'<tr><td><a href="{link}">{name}</a></td><td>{expires} days</td></tr>'
|
||||||
|
|
||||||
return html
|
return html
|
||||||
|
|
||||||
@@ -58,17 +73,15 @@ def transactions(txs):
|
|||||||
amount += output["value"]
|
amount += output["value"]
|
||||||
|
|
||||||
amount = amount / 1000000
|
amount = amount / 1000000
|
||||||
amount = round(amount, 2)
|
|
||||||
amount = "{:,}".format(amount)
|
|
||||||
|
|
||||||
hash = "<a target='_blank' href='https://niami.io/tx/" + hash + "'>" + hash[:8] + "...</a>"
|
hash = f"<a target='_blank' href='{TX_EXPLORER_URL}{hash}'>{hash[:8]}...</a>"
|
||||||
if confirmations < 5:
|
if confirmations < 5:
|
||||||
confirmations = "<td style='background-color: red;'>" + str(confirmations) + "</td>"
|
confirmations = f"<td style='background-color: red;'>{confirmations}</td>"
|
||||||
else:
|
else:
|
||||||
confirmations = "<td>" + str(confirmations) + "</td>"
|
confirmations = f"<td>{confirmations:,}</td>"
|
||||||
|
|
||||||
|
|
||||||
html += f'<tr><td>{action}</td><td>{address}</td><td>{hash}</td>{confirmations}<td>{amount} HNS</td></tr>'
|
html += f'<tr><td>{action}</td><td>{address}</td><td>{hash}</td>{confirmations}<td>{amount:,.2f} HNS</td></tr>'
|
||||||
return html
|
return html
|
||||||
|
|
||||||
|
|
||||||
@@ -92,14 +105,14 @@ def dns(data, edit=False):
|
|||||||
|
|
||||||
|
|
||||||
elif entry['type'] == 'DS':
|
elif entry['type'] == 'DS':
|
||||||
ds = str(entry['keyTag']) + " " + str(entry['algorithm']) + " " + str(entry['digestType']) + " " + entry['digest']
|
ds = f'{entry['keyTag']} {entry['algorithm']} {entry['digestType']} {entry['digest']}'
|
||||||
html_output += f"<td>{ds}</td>\n"
|
html_output += f"<td>{ds}</td>\n"
|
||||||
|
|
||||||
else:
|
else:
|
||||||
value = ""
|
value = ""
|
||||||
for key, val in entry.items():
|
for key, val in entry.items():
|
||||||
if key != 'type':
|
if key != 'type':
|
||||||
value += str(val) + " "
|
value += f'{val} '
|
||||||
html_output += f"<td>{value}</td>\n"
|
html_output += f"<td>{value}</td>\n"
|
||||||
|
|
||||||
if edit:
|
if edit:
|
||||||
@@ -119,18 +132,16 @@ def txs(data):
|
|||||||
|
|
||||||
for entry in data:
|
for entry in data:
|
||||||
html_output += f"<tr><td>{entry['action']}</td>\n"
|
html_output += f"<tr><td>{entry['action']}</td>\n"
|
||||||
html_output += f"<td><a target='_blank' href='https://niami.io/tx/{entry['txid']}'>{entry['txid'][:8]}...</a></td>\n"
|
html_output += f"<td><a target='_blank' href='{TX_EXPLORER_URL}{entry['txid']}'>{entry['txid'][:8]}...</a></td>\n"
|
||||||
amount = entry['amount']
|
amount = entry['amount']
|
||||||
amount = amount / 1000000
|
amount = amount / 1000000
|
||||||
amount = round(amount, 2)
|
|
||||||
|
|
||||||
if entry['blind'] == None:
|
if entry['blind'] == None:
|
||||||
html_output += f"<td>{amount} HNS</td>\n"
|
html_output += f"<td>{amount:,.2f} HNS</td>\n"
|
||||||
else:
|
else:
|
||||||
blind = entry['blind']
|
blind = entry['blind']
|
||||||
blind = blind / 1000000
|
blind = blind / 1000000
|
||||||
blind = round(blind, 2)
|
html_output += f"<td>{amount:,.2f} + {blind:,.2f} HNS</td>\n"
|
||||||
html_output += f"<td>{amount} + {blind} HNS</td>\n"
|
|
||||||
|
|
||||||
html_output += f"<td>{timestamp_to_readable_time(entry['time'])}</td>\n"
|
html_output += f"<td>{timestamp_to_readable_time(entry['time'])}</td>\n"
|
||||||
html_output += f"</tr>\n"
|
html_output += f"</tr>\n"
|
||||||
@@ -149,20 +160,17 @@ def bids(bids,reveals):
|
|||||||
for bid in bids:
|
for bid in bids:
|
||||||
lockup = bid['lockup']
|
lockup = bid['lockup']
|
||||||
lockup = lockup / 1000000
|
lockup = lockup / 1000000
|
||||||
lockup = round(lockup, 2)
|
|
||||||
html += "<tr>"
|
html += "<tr>"
|
||||||
html += f"<td>{lockup} HNS</td>"
|
html += f"<td>{lockup:,.2f} HNS</td>"
|
||||||
revealed = False
|
revealed = False
|
||||||
for reveal in reveals:
|
for reveal in reveals:
|
||||||
if reveal['bid'] == bid['prevout']['hash']:
|
if reveal['bid'] == bid['prevout']['hash']:
|
||||||
revealed = True
|
revealed = True
|
||||||
value = reveal['value']
|
value = reveal['value']
|
||||||
value = value / 1000000
|
value = value / 1000000
|
||||||
value = round(value, 2)
|
html += f"<td>{value:,.2f} HNS</td>"
|
||||||
html += f"<td>{value} HNS</td>"
|
|
||||||
bidValue = lockup - value
|
bidValue = lockup - value
|
||||||
bidValue = round(bidValue, 2)
|
html += f"<td>{bidValue:,.2f} HNS</td>"
|
||||||
html += f"<td>{bidValue} HNS</td>"
|
|
||||||
break
|
break
|
||||||
if not revealed:
|
if not revealed:
|
||||||
html += f"<td>Hidden until reveal</td>"
|
html += f"<td>Hidden until reveal</td>"
|
||||||
@@ -183,22 +191,17 @@ def bidDomains(bids,domains, sortState=False):
|
|||||||
if bid['name'] == domain['name']:
|
if bid['name'] == domain['name']:
|
||||||
lockup = bid['lockup']
|
lockup = bid['lockup']
|
||||||
lockup = lockup / 1000000
|
lockup = lockup / 1000000
|
||||||
lockup = round(lockup, 2)
|
|
||||||
bidValue = bid['value'] / 1000000
|
bidValue = bid['value'] / 1000000
|
||||||
bidValue = round(bidValue, 2)
|
|
||||||
blind = lockup - bidValue
|
blind = lockup - bidValue
|
||||||
bidValue = "{:,}".format(bidValue)
|
|
||||||
blind = round(blind, 2)
|
|
||||||
blind = "{:,}".format(blind)
|
|
||||||
|
|
||||||
bidDisplay = f'<b>{bidValue} HNS</b> + {blind} HNS blind'
|
bidDisplay = f'<b>{bidValue:,.2f} HNS</b> + {blind:,.2f} HNS blind'
|
||||||
|
|
||||||
|
|
||||||
html += "<tr>"
|
html += "<tr>"
|
||||||
html += f"<td><a class='text-decoration-none' style='color: var(--bs-table-color-state, var(--bs-table-color-type, var(--bs-table-color)));' href='/auction/{domain['name']}'>{domain['name']}</a></td>"
|
html += f"<td><a class='text-decoration-none' style='color: var(--bs-table-color-state, var(--bs-table-color-type, var(--bs-table-color)));' href='/auction/{domain['name']}'>{domain['name']}</a></td>"
|
||||||
html += f"<td>{domain['state']}</td>"
|
html += f"<td>{domain['state']}</td>"
|
||||||
html += f"<td>{bidDisplay}</td>"
|
html += f"<td>{bidDisplay}</td>"
|
||||||
html += f"<td>{bid['height']}</td>"
|
html += f"<td>{bid['height']:,}</td>"
|
||||||
html += "</tr>"
|
html += "</tr>"
|
||||||
else:
|
else:
|
||||||
for domain in domains:
|
for domain in domains:
|
||||||
@@ -206,19 +209,15 @@ def bidDomains(bids,domains, sortState=False):
|
|||||||
if bid['name'] == domain['name']:
|
if bid['name'] == domain['name']:
|
||||||
lockup = bid['lockup']
|
lockup = bid['lockup']
|
||||||
lockup = lockup / 1000000
|
lockup = lockup / 1000000
|
||||||
lockup = round(lockup, 2)
|
|
||||||
bidValue = bid['value'] / 1000000
|
bidValue = bid['value'] / 1000000
|
||||||
bidValue = round(bidValue, 2)
|
|
||||||
blind = lockup - bidValue
|
blind = lockup - bidValue
|
||||||
bidValue = "{:,}".format(bidValue)
|
|
||||||
blind = "{:,}".format(blind)
|
|
||||||
|
|
||||||
bidDisplay = f'<b>{bidValue} HNS</b> + {blind} HNS blind'
|
bidDisplay = f'<b>{bidValue:,.2f} HNS</b> + {blind:,.2f} HNS blind'
|
||||||
html += "<tr>"
|
html += "<tr>"
|
||||||
html += f"<td><a class='text-decoration-none' style='color: var(--bs-table-color-state, var(--bs-table-color-type, var(--bs-table-color)));' href='/auction/{domain['name']}'>{domain['name']}</a></td>"
|
html += f"<td><a class='text-decoration-none' style='color: var(--bs-table-color-state, var(--bs-table-color-type, var(--bs-table-color)));' href='/auction/{domain['name']}'>{domain['name']}</a></td>"
|
||||||
html += f"<td>{domain['state']}</td>"
|
html += f"<td>{domain['state']}</td>"
|
||||||
html += f"<td>{bidDisplay}</td>"
|
html += f"<td>{bidDisplay}</td>"
|
||||||
html += f"<td>{domain['height']}</td>"
|
html += f"<td>{domain['height']:,}</td>"
|
||||||
html += "</tr>"
|
html += "</tr>"
|
||||||
return html
|
return html
|
||||||
|
|
||||||
|
|||||||
2
templates/assets/js/script.min.js
vendored
2
templates/assets/js/script.min.js
vendored
@@ -1 +1 @@
|
|||||||
async function request(e){try{const t=await fetch(`/api/v1/${e}`);if(!t.ok)throw new Error(`HTTP error! Status: ${t.status}`);const o=await t.json();return void 0!==o.error?`Error: ${o.error}`:o.result}catch(e){return console.error("Request failed:",e),"Error"}}window.addEventListener("load",(async()=>{const e=["hsd-sync","hsd-version","hsd-height","wallet-sync","wallet-available","wallet-total","wallet-locked","wallet-pending","wallet-domainCount"],t=["wallet-available","wallet-total","wallet-locked","wallet-pending"];for(const o of e){const e=document.getElementById(o);if(e){const l=o.replace(/-/g,"/");let n=await request(l);t.includes(o)&&(n=Number(n).toFixed(2),n=n.toString().replace(/\B(?=(\d{3})+(?!\d))/g,",")),e.innerHTML=n}}})),function(){"use strict";var e=document.querySelector(".sidebar"),t=document.querySelectorAll("#sidebarToggle, #sidebarToggleTop");if(e){e.querySelector(".collapse");var o=[].slice.call(document.querySelectorAll(".sidebar .collapse")).map((function(e){return new bootstrap.Collapse(e,{toggle:!1})}));for(var l of t)l.addEventListener("click",(function(t){if(document.body.classList.toggle("sidebar-toggled"),e.classList.toggle("toggled"),e.classList.contains("toggled"))for(var l of o)l.hide()}));window.addEventListener("resize",(function(){if(Math.max(document.documentElement.clientWidth||0,window.innerWidth||0)<768)for(var e of o)e.hide()}))}var n=document.querySelector("body.fixed-nav .sidebar");n&&n.on("mousewheel DOMMouseScroll wheel",(function(e){if(Math.max(document.documentElement.clientWidth||0,window.innerWidth||0)>768){var t=e.originalEvent,o=t.wheelDelta||-t.detail;this.scrollTop+=30*(o<0?1:-1),e.preventDefault()}}));var r=document.querySelector(".scroll-to-top");r&&window.addEventListener("scroll",(function(){var e=window.pageYOffset;r.style.display=e>100?"block":"none"}))}();
|
async function request(e){try{const t=await fetch(`/api/v1/${e}`);if(!t.ok)throw new Error(`HTTP error! Status: ${t.status}`);const l=await t.json();return void 0!==l.error?`Error: ${l.error}`:l.result}catch(e){return console.error("Request failed:",e),"Error"}}window.addEventListener("load",(async()=>{const e=["hsd-sync","hsd-version","hsd-height","wallet-sync","wallet-available","wallet-total","wallet-locked","wallet-pending","wallet-domainCount","wallet-bidCount","wallet-pendingReveal","wallet-pendingRegister","wallet-pendingRedeem"],t=["wallet-available","wallet-total","wallet-locked","wallet-pending"];for(const l of e){const e=document.getElementById(l);if(e){const o=l.replace(/-/g,"/");let n=await request(o);t.includes(l)&&(n=Number(n).toFixed(2),n=n.toString().replace(/\B(?=(\d{3})+(?!\d))/g,",")),e.innerHTML=n}}})),function(){"use strict";var e=document.querySelector(".sidebar"),t=document.querySelectorAll("#sidebarToggle, #sidebarToggleTop");if(e){e.querySelector(".collapse");var l=[].slice.call(document.querySelectorAll(".sidebar .collapse")).map((function(e){return new bootstrap.Collapse(e,{toggle:!1})}));for(var o of t)o.addEventListener("click",(function(t){if(document.body.classList.toggle("sidebar-toggled"),e.classList.toggle("toggled"),e.classList.contains("toggled"))for(var o of l)o.hide()}));window.addEventListener("resize",(function(){if(Math.max(document.documentElement.clientWidth||0,window.innerWidth||0)<768)for(var e of l)e.hide()}))}var n=document.querySelector("body.fixed-nav .sidebar");n&&n.on("mousewheel DOMMouseScroll wheel",(function(e){if(Math.max(document.documentElement.clientWidth||0,window.innerWidth||0)>768){var t=e.originalEvent,l=t.wheelDelta||-t.detail;this.scrollTop+=30*(l<0?1:-1),e.preventDefault()}}));var r=document.querySelector(".scroll-to-top");r&&window.addEventListener("scroll",(function(){var e=window.pageYOffset;r.style.display=e>100?"block":"none"}))}();
|
||||||
@@ -87,7 +87,7 @@
|
|||||||
<div class="row align-items-center no-gutters">
|
<div class="row align-items-center no-gutters">
|
||||||
<div class="col me-2">
|
<div class="col me-2">
|
||||||
<div class="text-uppercase text-success fw-bold text-xs mb-1"><span>Total Bids</span></div>
|
<div class="text-uppercase text-success fw-bold text-xs mb-1"><span>Total Bids</span></div>
|
||||||
<div class="text-dark fw-bold h5 mb-0"><span>{{bids}}</span></div>
|
<div class="text-dark fw-bold h5 mb-0"><span id="wallet-bidCount">0</span></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -101,9 +101,43 @@
|
|||||||
<div class="text-uppercase text-info fw-bold text-xs mb-1"><span>Pending Reveal</span></div>
|
<div class="text-uppercase text-info fw-bold text-xs mb-1"><span>Pending Reveal</span></div>
|
||||||
<div class="row g-0 align-items-center">
|
<div class="row g-0 align-items-center">
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<div class="text-dark fw-bold h5 mb-0 me-3"><span>{{reveal}}</span></div>
|
<div class="text-dark fw-bold h5 mb-0 me-3"><span id="wallet-pendingReveal">0</span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col"><a class="btn btn-primary" role="button" href="/reveal">Reveal All</a></div>
|
<div class="col"><a class="btn btn-primary" role="button" href="/all/reveal">Reveal All</a></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-xl-3 mb-4">
|
||||||
|
<div class="card shadow border-start-info py-2">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row align-items-center no-gutters">
|
||||||
|
<div class="col me-2">
|
||||||
|
<div class="text-uppercase text-info fw-bold text-xs mb-1"><span>Pending Redeem</span></div>
|
||||||
|
<div class="row g-0 align-items-center">
|
||||||
|
<div class="col-auto">
|
||||||
|
<div class="text-dark fw-bold h5 mb-0 me-3"><span id="wallet-pendingRedeem">0</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="col"><a class="btn btn-primary" role="button" href="/all/redeem">Redeem All</a></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-xl-3 mb-4">
|
||||||
|
<div class="card shadow border-start-info py-2">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row align-items-center no-gutters">
|
||||||
|
<div class="col me-2">
|
||||||
|
<div class="text-uppercase text-info fw-bold text-xs mb-1"><span>Pending Register</span></div>
|
||||||
|
<div class="row g-0 align-items-center">
|
||||||
|
<div class="col-auto">
|
||||||
|
<div class="text-dark fw-bold h5 mb-0 me-3"><span id="wallet-pendingRegister">0</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="col"><a class="btn btn-primary" role="button" href="/all/register">Register All</a></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user