Compare commits
39 Commits
b24a3147dd
...
v1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
fb9cb50a90
|
|||
|
db5e672d7b
|
|||
|
8ec23e3a32
|
|||
|
f84359a74b
|
|||
|
92f05992ab
|
|||
|
2ae618c68a
|
|||
|
cfb814d006
|
|||
|
8c61a09e5b
|
|||
|
5c61bad9a2
|
|||
|
35d3ccd0c0
|
|||
|
2b895a524a
|
|||
|
f7968fc218
|
|||
|
d39f433738
|
|||
|
2b6447fd12
|
|||
|
4b7b9f991b
|
|||
|
f5fc0766a1
|
|||
|
209c3794fc
|
|||
|
693bf13450
|
|||
|
cff65b167a
|
|||
|
d2ad6eaa58
|
|||
|
8099320673
|
|||
|
6c91ec88c7
|
|||
|
aa92220756
|
|||
|
3290240a25
|
|||
|
40f520ba5e
|
|||
|
9f1f7fb18e
|
|||
|
3e78732333
|
|||
|
2595503dc0
|
|||
|
0be6b8b435
|
|||
|
00379aaa8f
|
|||
|
da9354cb23
|
|||
|
d516e91592
|
|||
|
f695d53f8d
|
|||
|
45fdaa2de4
|
|||
|
40b4c42492
|
|||
|
afd7ae2947
|
|||
|
171e891555
|
|||
|
5e21074b24
|
|||
|
703f1ed5c1
|
5
.gitignore
vendored
5
.gitignore
vendored
@@ -8,3 +8,8 @@ templates/assets/css/styles.min.css
|
|||||||
ignore/
|
ignore/
|
||||||
|
|
||||||
plugins/signatures.json
|
plugins/signatures.json
|
||||||
|
|
||||||
|
.venv/
|
||||||
|
|
||||||
|
user_data/
|
||||||
|
customPlugins/
|
||||||
@@ -10,6 +10,7 @@ COPY . /app
|
|||||||
|
|
||||||
# Add mount point for data volume
|
# Add mount point for data volume
|
||||||
# VOLUME /data
|
# VOLUME /data
|
||||||
|
RUN apk add git
|
||||||
|
|
||||||
ENTRYPOINT ["python3"]
|
ENTRYPOINT ["python3"]
|
||||||
CMD ["server.py"]
|
CMD ["server.py"]
|
||||||
|
|||||||
Binary file not shown.
@@ -34,7 +34,6 @@ Then access the wallet at http://localhost:5000
|
|||||||
|
|
||||||
|
|
||||||
Also available as a docker image:
|
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
|
||||||
@@ -47,6 +46,8 @@ If you have HSD running on a different IP/container
|
|||||||
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)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
- Basic wallet functionality
|
- Basic wallet functionality
|
||||||
- Create new wallet
|
- Create new wallet
|
||||||
|
|||||||
111
account.py
111
account.py
@@ -15,6 +15,9 @@ ip = os.getenv("hsd_ip")
|
|||||||
if ip is None:
|
if ip is None:
|
||||||
ip = "localhost"
|
ip = "localhost"
|
||||||
|
|
||||||
|
show_expired = os.getenv("show_expired")
|
||||||
|
if show_expired is None:
|
||||||
|
show_expired = False
|
||||||
|
|
||||||
hsd = api.hsd(APIKEY,ip)
|
hsd = api.hsd(APIKEY,ip)
|
||||||
hsw = api.hsw(APIKEY,ip)
|
hsw = api.hsw(APIKEY,ip)
|
||||||
@@ -138,6 +141,8 @@ def getBalance(account: str):
|
|||||||
if domain['state'] == "CLOSED":
|
if domain['state'] == "CLOSED":
|
||||||
domainValue += domain['value']
|
domainValue += domain['value']
|
||||||
total = total - (domainValue/1000000)
|
total = total - (domainValue/1000000)
|
||||||
|
locked = locked - (domainValue/1000000)
|
||||||
|
|
||||||
|
|
||||||
# Only keep 2 decimal places
|
# Only keep 2 decimal places
|
||||||
total = round(total, 2)
|
total = round(total, 2)
|
||||||
@@ -177,7 +182,21 @@ def getDomains(account,own=True):
|
|||||||
else:
|
else:
|
||||||
response = requests.get(f"http://x:{APIKEY}@{ip}:12039/wallet/{account}/name")
|
response = requests.get(f"http://x:{APIKEY}@{ip}:12039/wallet/{account}/name")
|
||||||
info = response.json()
|
info = response.json()
|
||||||
return info
|
|
||||||
|
if show_expired:
|
||||||
|
return info
|
||||||
|
|
||||||
|
# Remove any expired domains
|
||||||
|
domains = []
|
||||||
|
for domain in info:
|
||||||
|
if 'stats' in domain:
|
||||||
|
if 'daysUntilExpire' in domain['stats']:
|
||||||
|
if domain['stats']['daysUntilExpire'] < 0:
|
||||||
|
continue
|
||||||
|
domains.append(domain)
|
||||||
|
|
||||||
|
|
||||||
|
return domains
|
||||||
|
|
||||||
def getTransactions(account):
|
def getTransactions(account):
|
||||||
# Get the transactions
|
# Get the transactions
|
||||||
@@ -237,10 +256,6 @@ def check_hip2(domain: str):
|
|||||||
def send(account,address,amount):
|
def send(account,address,amount):
|
||||||
account_name = check_account(account)
|
account_name = check_account(account)
|
||||||
password = ":".join(account.split(":")[1:])
|
password = ":".join(account.split(":")[1:])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
response = hsw.rpc_selectWallet(account_name)
|
response = hsw.rpc_selectWallet(account_name)
|
||||||
if response['error'] is not None:
|
if response['error'] is not None:
|
||||||
return {
|
return {
|
||||||
@@ -303,6 +318,12 @@ def getDNS(domain: str):
|
|||||||
return {
|
return {
|
||||||
"error": response['error']['message']
|
"error": response['error']['message']
|
||||||
}
|
}
|
||||||
|
if 'result' not in response:
|
||||||
|
return {
|
||||||
|
"error": "No DNS records"
|
||||||
|
}
|
||||||
|
if 'records' not in response['result']:
|
||||||
|
return []
|
||||||
return response['result']['records']
|
return response['result']['records']
|
||||||
|
|
||||||
|
|
||||||
@@ -360,13 +381,37 @@ def getNodeSync():
|
|||||||
sync = round(sync, 2)
|
sync = round(sync, 2)
|
||||||
return sync
|
return sync
|
||||||
|
|
||||||
|
def getWalletStatus():
|
||||||
|
response = hsw.rpc_getWalletInfo()
|
||||||
|
if 'error' in response and response['error'] != None:
|
||||||
|
return "Error"
|
||||||
|
|
||||||
|
# return response
|
||||||
|
walletHeight = response['result']['height']
|
||||||
|
# Get the current block height
|
||||||
|
nodeHeight = getBlockHeight()
|
||||||
|
|
||||||
|
if walletHeight < nodeHeight:
|
||||||
|
return f"Scanning {walletHeight/nodeHeight*100:.2f}%"
|
||||||
|
elif walletHeight == nodeHeight:
|
||||||
|
return "Ready"
|
||||||
|
else:
|
||||||
|
return "Error wallet ahead of node"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def getBids(account, domain="NONE"):
|
def getBids(account, domain="NONE"):
|
||||||
if domain == "NONE":
|
if domain == "NONE":
|
||||||
return hsw.getWalletBids(account)
|
response = hsw.getWalletBids(account)
|
||||||
|
else:
|
||||||
|
response = hsw.getWalletBidsByName(domain,account)
|
||||||
response = hsw.getWalletBidsByName(domain,account)
|
# Add backup for bids with no value
|
||||||
return response
|
bids = []
|
||||||
|
for bid in response:
|
||||||
|
if 'value' not in bid:
|
||||||
|
bid['value'] = -1000000
|
||||||
|
bids.append(bid)
|
||||||
|
return bids
|
||||||
|
|
||||||
def getReveals(account,domain):
|
def getReveals(account,domain):
|
||||||
return hsw.getWalletRevealsByName(domain,account)
|
return hsw.getWalletRevealsByName(domain,account)
|
||||||
@@ -619,6 +664,52 @@ def revoke(account,domain):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def sendBatch(account, batch):
|
||||||
|
account_name = check_account(account)
|
||||||
|
password = ":".join(account.split(":")[1:])
|
||||||
|
|
||||||
|
if account_name == False:
|
||||||
|
return {
|
||||||
|
"error": {
|
||||||
|
"message": "Invalid account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = hsw.rpc_selectWallet(account_name)
|
||||||
|
if response['error'] is not None:
|
||||||
|
return {
|
||||||
|
"error": {
|
||||||
|
"message": response['error']['message']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
response = hsw.rpc_walletPassphrase(password,10)
|
||||||
|
if response['error'] is not None:
|
||||||
|
return {
|
||||||
|
"error": {
|
||||||
|
"message": response['error']['message']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
response = requests.post(f"http://x:{APIKEY}@{ip}:12039",json={
|
||||||
|
"method": "sendbatch",
|
||||||
|
"params": [batch]
|
||||||
|
}).json()
|
||||||
|
if response['error'] is not None:
|
||||||
|
return response
|
||||||
|
if 'result' not in response:
|
||||||
|
return {
|
||||||
|
"error": {
|
||||||
|
"message": "No result"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return response['result']
|
||||||
|
except Exception as e:
|
||||||
|
return {
|
||||||
|
"error": {
|
||||||
|
"message": str(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#region settingsAPIs
|
#region settingsAPIs
|
||||||
|
|||||||
@@ -168,4 +168,10 @@ def emoji_to_punycode(emoji):
|
|||||||
try:
|
try:
|
||||||
return emoji.encode("idna").decode("ascii")
|
return emoji.encode("idna").decode("ascii")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return ""
|
return emoji
|
||||||
|
|
||||||
|
def punycode_to_emoji(punycode):
|
||||||
|
try:
|
||||||
|
return punycode.encode("ascii").decode("idna")
|
||||||
|
except Exception as e:
|
||||||
|
return punycode
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
hsd_api=123480615465636893475aCwyaae6s45
|
hsd_api=123480615465636893475aCwyaae6s45
|
||||||
hsd_ip=localhost
|
hsd_ip=localhost
|
||||||
theme=black
|
theme=black
|
||||||
|
show_expired=false
|
||||||
96
main.py
96
main.py
@@ -101,13 +101,11 @@ def index():
|
|||||||
functionOutput = plugins_module.runPluginFunction(function["plugin"],function["function"],{},request.cookies.get("account"))
|
functionOutput = plugins_module.runPluginFunction(function["plugin"],function["function"],{},request.cookies.get("account"))
|
||||||
plugins += render.plugin_output_dash(functionOutput,plugins_module.getPluginFunctionReturns(function["plugin"],function["function"]))
|
plugins += render.plugin_output_dash(functionOutput,plugins_module.getPluginFunctionReturns(function["plugin"],function["function"]))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return render_template("index.html", account=account, available=available,
|
return render_template("index.html", account=account, available=available,
|
||||||
total=total, pending=pending, domains=domains,
|
total=total, pending=pending, domains=domains,
|
||||||
domainsMobile=domainsMobile, plugins=plugins,
|
domainsMobile=domainsMobile, plugins=plugins,
|
||||||
domain_count=domain_count, sync=account_module.getNodeSync(),
|
domain_count=domain_count, sync=account_module.getNodeSync(),
|
||||||
|
wallet_status=account_module.getWalletStatus(),
|
||||||
sort_price=sort_price,sort_expiry=sort_expiry,
|
sort_price=sort_price,sort_expiry=sort_expiry,
|
||||||
sort_domain=sort_domain,sort_price_next=sort_price_next,
|
sort_domain=sort_domain,sort_price_next=sort_price_next,
|
||||||
sort_expiry_next=sort_expiry_next,sort_domain_next=sort_domain_next)
|
sort_expiry_next=sort_expiry_next,sort_domain_next=sort_domain_next)
|
||||||
@@ -133,7 +131,7 @@ def transactions():
|
|||||||
transactions = render.transactions(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(),
|
||||||
tx=transactions)
|
wallet_status=account_module.getWalletStatus(),tx=transactions)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/send')
|
@app.route('/send')
|
||||||
@@ -161,6 +159,7 @@ def send_page():
|
|||||||
|
|
||||||
|
|
||||||
return render_template("send.html", account=account,sync=account_module.getNodeSync(),
|
return render_template("send.html", account=account,sync=account_module.getNodeSync(),
|
||||||
|
wallet_status=account_module.getWalletStatus(),
|
||||||
max=max,message=message,address=address,amount=amount)
|
max=max,message=message,address=address,amount=amount)
|
||||||
|
|
||||||
@app.route('/send', methods=["POST"])
|
@app.route('/send', methods=["POST"])
|
||||||
@@ -209,7 +208,8 @@ def send():
|
|||||||
|
|
||||||
|
|
||||||
return render_template("confirm.html", account=account_module.check_account(request.cookies.get("account")),
|
return render_template("confirm.html", account=account_module.check_account(request.cookies.get("account")),
|
||||||
sync=account_module.getNodeSync(),action=action,
|
sync=account_module.getNodeSync(),
|
||||||
|
wallet_status=account_module.getWalletStatus(),action=action,
|
||||||
content=content,cancel=cancel,confirm=confirm)
|
content=content,cancel=cancel,confirm=confirm)
|
||||||
|
|
||||||
|
|
||||||
@@ -239,6 +239,7 @@ def receive():
|
|||||||
address = account_module.getAddress(account)
|
address = account_module.getAddress(account)
|
||||||
|
|
||||||
return render_template("receive.html", account=account,sync=account_module.getNodeSync(),
|
return render_template("receive.html", account=account,sync=account_module.getNodeSync(),
|
||||||
|
wallet_status=account_module.getWalletStatus(),
|
||||||
address=address)
|
address=address)
|
||||||
|
|
||||||
@app.route('/success')
|
@app.route('/success')
|
||||||
@@ -253,7 +254,7 @@ def success():
|
|||||||
|
|
||||||
tx = request.args.get("tx")
|
tx = request.args.get("tx")
|
||||||
return render_template("success.html", account=account,sync=account_module.getNodeSync(),
|
return render_template("success.html", account=account,sync=account_module.getNodeSync(),
|
||||||
tx=tx)
|
wallet_status=account_module.getWalletStatus(),tx=tx)
|
||||||
|
|
||||||
@app.route('/checkaddress')
|
@app.route('/checkaddress')
|
||||||
def check_address():
|
def check_address():
|
||||||
@@ -277,6 +278,8 @@ def auctions():
|
|||||||
|
|
||||||
balance = account_module.getBalance(account)
|
balance = account_module.getBalance(account)
|
||||||
locked = balance['locked']
|
locked = balance['locked']
|
||||||
|
# Round to 2 decimals
|
||||||
|
locked = round(locked, 2)
|
||||||
|
|
||||||
# Add commas to the numbers
|
# Add commas to the numbers
|
||||||
locked = "{:,}".format(locked)
|
locked = "{:,}".format(locked)
|
||||||
@@ -288,7 +291,7 @@ def auctions():
|
|||||||
# Sort
|
# Sort
|
||||||
sort = request.args.get("sort")
|
sort = request.args.get("sort")
|
||||||
if sort == None:
|
if sort == None:
|
||||||
sort = "domain"
|
sort = "time"
|
||||||
sort = sort.lower()
|
sort = sort.lower()
|
||||||
sort_price = ""
|
sort_price = ""
|
||||||
sort_price_next = "⬇"
|
sort_price_next = "⬇"
|
||||||
@@ -296,11 +299,16 @@ def auctions():
|
|||||||
sort_state_next = "⬇"
|
sort_state_next = "⬇"
|
||||||
sort_domain = ""
|
sort_domain = ""
|
||||||
sort_domain_next = "⬇"
|
sort_domain_next = "⬇"
|
||||||
|
sort_time = ""
|
||||||
|
sort_time_next = "⬇"
|
||||||
reverse = False
|
reverse = False
|
||||||
|
|
||||||
direction = request.args.get("direction")
|
direction = request.args.get("direction")
|
||||||
if direction == None:
|
if direction == None:
|
||||||
direction = "⬇"
|
if sort == "time":
|
||||||
|
direction = "⬆"
|
||||||
|
else:
|
||||||
|
direction = "⬇"
|
||||||
|
|
||||||
if direction == "⬆":
|
if direction == "⬆":
|
||||||
reverse = True
|
reverse = True
|
||||||
@@ -314,6 +322,10 @@ def auctions():
|
|||||||
sort_state = direction
|
sort_state = direction
|
||||||
sort_state_next = reverseDirection(direction)
|
sort_state_next = reverseDirection(direction)
|
||||||
domains = sorted(domains, key=lambda k: k['state'],reverse=reverse)
|
domains = sorted(domains, key=lambda k: k['state'],reverse=reverse)
|
||||||
|
elif sort == "time":
|
||||||
|
sort_time = direction
|
||||||
|
sort_time_next = reverseDirection(direction)
|
||||||
|
bids = sorted(bids, key=lambda k: k['height'],reverse=reverse)
|
||||||
else:
|
else:
|
||||||
# Sort by domain
|
# Sort by domain
|
||||||
bids = sorted(bids, key=lambda k: k['name'],reverse=reverse)
|
bids = sorted(bids, key=lambda k: k['name'],reverse=reverse)
|
||||||
@@ -341,10 +353,6 @@ def auctions():
|
|||||||
pending_reveals += 1
|
pending_reveals += 1
|
||||||
|
|
||||||
plugins = ""
|
plugins = ""
|
||||||
# dashFunctions = plugins_module.getDashboardFunctions()
|
|
||||||
# for function in dashFunctions:
|
|
||||||
# functionOutput = plugins_module.runPluginFunction(function["plugin"],function["function"],{},request.cookies.get("account"))
|
|
||||||
# plugins += render.plugin_output_dash(functionOutput,plugins_module.getPluginFunctionReturns(function["plugin"],function["function"]))
|
|
||||||
|
|
||||||
message = ''
|
message = ''
|
||||||
if 'message' in request.args:
|
if 'message' in request.args:
|
||||||
@@ -352,10 +360,12 @@ def auctions():
|
|||||||
return render_template("auctions.html", account=account, locked=locked, domains=bidsHtml,
|
return render_template("auctions.html", account=account, locked=locked, domains=bidsHtml,
|
||||||
domainsMobile=bidsHtml, plugins=plugins,
|
domainsMobile=bidsHtml, plugins=plugins,
|
||||||
domain_count=bidsHtml, sync=account_module.getNodeSync(),
|
domain_count=bidsHtml, sync=account_module.getNodeSync(),
|
||||||
|
wallet_status=account_module.getWalletStatus(),
|
||||||
sort_price=sort_price,sort_state=sort_state,
|
sort_price=sort_price,sort_state=sort_state,
|
||||||
sort_domain=sort_domain,sort_price_next=sort_price_next,
|
sort_domain=sort_domain,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),reveal=pending_reveals,message=message,
|
||||||
|
sort_time=sort_time,sort_time_next=sort_time_next)
|
||||||
|
|
||||||
@app.route('/reveal')
|
@app.route('/reveal')
|
||||||
def revealAllBids():
|
def revealAllBids():
|
||||||
@@ -375,7 +385,7 @@ def revealAllBids():
|
|||||||
return redirect("/auctions?message=No reveals pending")
|
return redirect("/auctions?message=No reveals pending")
|
||||||
return redirect("/auctions?message=" + response['error']['message'])
|
return redirect("/auctions?message=" + response['error']['message'])
|
||||||
|
|
||||||
return redirect("/success?tx=" + response['hash'])
|
return redirect("/success?tx=" + response['result']['hash'])
|
||||||
|
|
||||||
|
|
||||||
@app.route('/search')
|
@app.route('/search')
|
||||||
@@ -412,10 +422,12 @@ def search():
|
|||||||
|
|
||||||
if 'error' in domain:
|
if 'error' in domain:
|
||||||
return render_template("search.html", account=account,sync=account_module.getNodeSync(),
|
return render_template("search.html", account=account,sync=account_module.getNodeSync(),
|
||||||
|
wallet_status=account_module.getWalletStatus(),
|
||||||
search_term=search_term, domain=domain['error'],plugins=plugins)
|
search_term=search_term, domain=domain['error'],plugins=plugins)
|
||||||
|
|
||||||
if domain['info'] is None:
|
if domain['info'] is None:
|
||||||
return render_template("search.html", account=account, sync=account_module.getNodeSync(),
|
return render_template("search.html", account=account, sync=account_module.getNodeSync(),
|
||||||
|
wallet_status=account_module.getWalletStatus(),
|
||||||
search_term=search_term,domain=search_term,
|
search_term=search_term,domain=search_term,
|
||||||
state="AVAILABLE", next="Available Now",plugins=plugins)
|
state="AVAILABLE", next="Available Now",plugins=plugins)
|
||||||
|
|
||||||
@@ -459,6 +471,7 @@ def search():
|
|||||||
txs = render.txs(txs)
|
txs = render.txs(txs)
|
||||||
|
|
||||||
return render_template("search.html", account=account, sync=account_module.getNodeSync(),
|
return render_template("search.html", account=account, sync=account_module.getNodeSync(),
|
||||||
|
wallet_status=account_module.getWalletStatus(),
|
||||||
search_term=search_term,domain=domain['info']['name'],
|
search_term=search_term,domain=domain['info']['name'],
|
||||||
raw=domain,state=state, next=next, owner=owner,
|
raw=domain,state=state, next=next, owner=owner,
|
||||||
dns=dns, txs=txs,plugins=plugins)
|
dns=dns, txs=txs,plugins=plugins)
|
||||||
@@ -484,6 +497,7 @@ def manage(domain: str):
|
|||||||
domain_info = account_module.getDomain(domain)
|
domain_info = account_module.getDomain(domain)
|
||||||
if 'error' in domain_info:
|
if 'error' in domain_info:
|
||||||
return render_template("manage.html", account=account, sync=account_module.getNodeSync(),
|
return render_template("manage.html", account=account, sync=account_module.getNodeSync(),
|
||||||
|
wallet_status=account_module.getWalletStatus(),
|
||||||
domain=domain, error=domain_info['error'])
|
domain=domain, error=domain_info['error'])
|
||||||
|
|
||||||
expiry = domain_info['info']['stats']['daysUntilExpire']
|
expiry = domain_info['info']['stats']['daysUntilExpire']
|
||||||
@@ -520,6 +534,7 @@ def manage(domain: str):
|
|||||||
|
|
||||||
|
|
||||||
return render_template("manage.html", account=account, sync=account_module.getNodeSync(),
|
return render_template("manage.html", account=account, sync=account_module.getNodeSync(),
|
||||||
|
wallet_status=account_module.getWalletStatus(),
|
||||||
error=errorMessage, address=address,
|
error=errorMessage, address=address,
|
||||||
domain=domain,expiry=expiry, dns=dns,
|
domain=domain,expiry=expiry, dns=dns,
|
||||||
raw_dns=urllib.parse.quote(raw_dns),
|
raw_dns=urllib.parse.quote(raw_dns),
|
||||||
@@ -588,7 +603,8 @@ def revokeInit(domain: str):
|
|||||||
|
|
||||||
|
|
||||||
return render_template("confirm-password.html", account=account_module.check_account(request.cookies.get("account")),
|
return render_template("confirm-password.html", account=account_module.check_account(request.cookies.get("account")),
|
||||||
sync=account_module.getNodeSync(),action=action,
|
sync=account_module.getNodeSync(),
|
||||||
|
wallet_status=account_module.getWalletStatus(),action=action,
|
||||||
content=content,cancel=cancel,confirm=confirm,check=revokeCheck)
|
content=content,cancel=cancel,confirm=confirm,check=revokeCheck)
|
||||||
|
|
||||||
@app.route('/manage/<domain>/revoke/confirm', methods=["POST"])
|
@app.route('/manage/<domain>/revoke/confirm', methods=["POST"])
|
||||||
@@ -695,6 +711,7 @@ def editPage(domain: str):
|
|||||||
|
|
||||||
|
|
||||||
return render_template("edit.html", account=account, sync=account_module.getNodeSync(),
|
return render_template("edit.html", account=account, sync=account_module.getNodeSync(),
|
||||||
|
wallet_status=account_module.getWalletStatus(),
|
||||||
domain=domain, error=errorMessage,
|
domain=domain, error=errorMessage,
|
||||||
dns=dns,raw_dns=urllib.parse.quote(raw_dns))
|
dns=dns,raw_dns=urllib.parse.quote(raw_dns))
|
||||||
|
|
||||||
@@ -753,7 +770,8 @@ def transfer(domain):
|
|||||||
|
|
||||||
|
|
||||||
return render_template("confirm.html", account=account_module.check_account(request.cookies.get("account")),
|
return render_template("confirm.html", account=account_module.check_account(request.cookies.get("account")),
|
||||||
sync=account_module.getNodeSync(),action=action,
|
sync=account_module.getNodeSync(),
|
||||||
|
wallet_status=account_module.getWalletStatus(),action=action,
|
||||||
content=content,cancel=cancel,confirm=confirm)
|
content=content,cancel=cancel,confirm=confirm)
|
||||||
|
|
||||||
@app.route('/manage/<domain>/sign')
|
@app.route('/manage/<domain>/sign')
|
||||||
@@ -794,6 +812,7 @@ def signMessage(domain):
|
|||||||
|
|
||||||
|
|
||||||
return render_template("message.html", account=account,sync=account_module.getNodeSync(),
|
return render_template("message.html", account=account,sync=account_module.getNodeSync(),
|
||||||
|
wallet_status=account_module.getWalletStatus(),
|
||||||
title="Sign Message",content=content)
|
title="Sign Message",content=content)
|
||||||
|
|
||||||
|
|
||||||
@@ -835,11 +854,13 @@ def auction(domain):
|
|||||||
|
|
||||||
if 'error' in domainInfo:
|
if 'error' in domainInfo:
|
||||||
return render_template("auction.html", account=account,sync=account_module.getNodeSync(),
|
return render_template("auction.html", account=account,sync=account_module.getNodeSync(),
|
||||||
|
wallet_status=account_module.getWalletStatus(),
|
||||||
search_term=search_term, domain=domainInfo['error'])
|
search_term=search_term, domain=domainInfo['error'])
|
||||||
|
|
||||||
if domainInfo['info'] is None:
|
if domainInfo['info'] is None:
|
||||||
next_action = f'<a href="/auction/{domain}/open">Open Auction</a>'
|
next_action = f'<a href="/auction/{domain}/open">Open Auction</a>'
|
||||||
return render_template("auction.html", account=account, sync=account_module.getNodeSync(),
|
return render_template("auction.html", account=account, sync=account_module.getNodeSync(),
|
||||||
|
wallet_status=account_module.getWalletStatus(),
|
||||||
search_term=search_term,domain=search_term,next_action=next_action,
|
search_term=search_term,domain=search_term,next_action=next_action,
|
||||||
state="AVAILABLE", next="Open Auction")
|
state="AVAILABLE", next="Open Auction")
|
||||||
|
|
||||||
@@ -892,6 +913,7 @@ def auction(domain):
|
|||||||
|
|
||||||
|
|
||||||
return render_template("auction.html", account=account, sync=account_module.getNodeSync(),
|
return render_template("auction.html", account=account, sync=account_module.getNodeSync(),
|
||||||
|
wallet_status=account_module.getWalletStatus(),
|
||||||
search_term=search_term,domain=domainInfo['info']['name'],
|
search_term=search_term,domain=domainInfo['info']['name'],
|
||||||
raw=domainInfo,state=state, next=next,
|
raw=domainInfo,state=state, next=next,
|
||||||
next_action=next_action, bids=bids,error=message)
|
next_action=next_action, bids=bids,error=message)
|
||||||
@@ -954,7 +976,8 @@ def bid(domain):
|
|||||||
|
|
||||||
|
|
||||||
return render_template("confirm.html", account=account_module.check_account(request.cookies.get("account")),
|
return render_template("confirm.html", account=account_module.check_account(request.cookies.get("account")),
|
||||||
sync=account_module.getNodeSync(),action=action,
|
sync=account_module.getNodeSync(),wallet_status=account_module.getWalletStatus(),
|
||||||
|
action=action,
|
||||||
domain=domain,content=content,cancel=cancel,confirm=confirm)
|
domain=domain,content=content,cancel=cancel,confirm=confirm)
|
||||||
|
|
||||||
@app.route('/auction/<domain>/bid/confirm')
|
@app.route('/auction/<domain>/bid/confirm')
|
||||||
@@ -1043,16 +1066,23 @@ def settings():
|
|||||||
if success == None:
|
if success == None:
|
||||||
success = ""
|
success = ""
|
||||||
|
|
||||||
|
if not os.path.exists(".git"):
|
||||||
|
return render_template("settings.html", account=account,sync=account_module.getNodeSync(),
|
||||||
|
wallet_status=account_module.getWalletStatus(),
|
||||||
|
error=error,success=success,version="Error")
|
||||||
info = gitinfo.get_git_info()
|
info = gitinfo.get_git_info()
|
||||||
branch = info['refs']
|
branch = info['refs']
|
||||||
if branch != "main":
|
if branch != "main":
|
||||||
branch = f"({branch})"
|
branch = f"({branch})"
|
||||||
|
else:
|
||||||
|
branch = ""
|
||||||
last_commit = info['author_date']
|
last_commit = info['author_date']
|
||||||
# import to time from format "2024-02-13 11:24:03"
|
# import to time from format "2024-02-13 11:24:03"
|
||||||
last_commit = datetime.datetime.strptime(last_commit, "%Y-%m-%d %H:%M:%S")
|
last_commit = datetime.datetime.strptime(last_commit, "%Y-%m-%d %H:%M:%S")
|
||||||
version = f'{last_commit.strftime("%y-%m-%d")} {branch}'
|
version = f'{last_commit.strftime("%y-%m-%d")} {branch}'
|
||||||
|
|
||||||
return render_template("settings.html", account=account,sync=account_module.getNodeSync(),
|
return render_template("settings.html", account=account,sync=account_module.getNodeSync(),
|
||||||
|
wallet_status=account_module.getWalletStatus(),
|
||||||
error=error,success=success,version=version)
|
error=error,success=success,version=version)
|
||||||
|
|
||||||
@app.route('/settings/<action>')
|
@app.route('/settings/<action>')
|
||||||
@@ -1069,7 +1099,7 @@ def settings_action(action):
|
|||||||
resp = account_module.rescan()
|
resp = account_module.rescan()
|
||||||
if 'error' in resp:
|
if 'error' in resp:
|
||||||
return redirect("/settings?error=" + str(resp['error']))
|
return redirect("/settings?error=" + str(resp['error']))
|
||||||
return redirect("/settings?success=Resent transactions")
|
return redirect("/settings?success=Rescan started")
|
||||||
elif action == "resend":
|
elif action == "resend":
|
||||||
resp = account_module.resendTXs()
|
resp = account_module.resendTXs()
|
||||||
if 'error' in resp:
|
if 'error' in resp:
|
||||||
@@ -1090,6 +1120,7 @@ def settings_action(action):
|
|||||||
content += "<button id='copyButton' onclick='copyToClipboard()' class='btn btn-secondary'>Copy to clipboard</button>"
|
content += "<button id='copyButton' onclick='copyToClipboard()' class='btn btn-secondary'>Copy to clipboard</button>"
|
||||||
|
|
||||||
return render_template("message.html", account=account,sync=account_module.getNodeSync(),
|
return render_template("message.html", account=account,sync=account_module.getNodeSync(),
|
||||||
|
wallet_status=account_module.getWalletStatus(),
|
||||||
title="xPub Key",
|
title="xPub Key",
|
||||||
content="<code>"+xpub+"</code>" + content)
|
content="<code>"+xpub+"</code>" + content)
|
||||||
|
|
||||||
@@ -1108,9 +1139,11 @@ def login():
|
|||||||
|
|
||||||
if 'message' in request.args:
|
if 'message' in request.args:
|
||||||
return render_template("login.html", sync=account_module.getNodeSync(),
|
return render_template("login.html", sync=account_module.getNodeSync(),
|
||||||
|
wallet_status=account_module.getWalletStatus(),
|
||||||
error=request.args.get("message"),wallets=wallets)
|
error=request.args.get("message"),wallets=wallets)
|
||||||
|
|
||||||
return render_template("login.html", sync=account_module.getNodeSync(),
|
return render_template("login.html", sync=account_module.getNodeSync(),
|
||||||
|
wallet_status=account_module.getWalletStatus(),
|
||||||
wallets=wallets)
|
wallets=wallets)
|
||||||
|
|
||||||
@app.route('/login', methods=["POST"])
|
@app.route('/login', methods=["POST"])
|
||||||
@@ -1122,6 +1155,7 @@ def login_post():
|
|||||||
# Check if the account is valid
|
# Check if the account is valid
|
||||||
if account.count(":") > 0:
|
if account.count(":") > 0:
|
||||||
return render_template("login.html", sync=account_module.getNodeSync(),
|
return render_template("login.html", sync=account_module.getNodeSync(),
|
||||||
|
wallet_status=account_module.getWalletStatus(),
|
||||||
error="Invalid account")
|
error="Invalid account")
|
||||||
|
|
||||||
account = account + ":" + password
|
account = account + ":" + password
|
||||||
@@ -1180,6 +1214,7 @@ def register():
|
|||||||
|
|
||||||
# Set the cookie
|
# Set the cookie
|
||||||
response = make_response(render_template("message.html", sync=account_module.getNodeSync(),
|
response = make_response(render_template("message.html", sync=account_module.getNodeSync(),
|
||||||
|
wallet_status=account_module.getWalletStatus(),
|
||||||
title="Account Created",
|
title="Account Created",
|
||||||
content="Your account has been created. Here is your seed phrase. Please write it down and keep it safe as it will not be shown again<br><br>" + response['seed']))
|
content="Your account has been created. Here is your seed phrase. Please write it down and keep it safe as it will not be shown again<br><br>" + response['seed']))
|
||||||
response.set_cookie("account", account+":"+password)
|
response.set_cookie("account", account+":"+password)
|
||||||
@@ -1262,10 +1297,11 @@ def plugins_index():
|
|||||||
plugins = render.plugins(plugins_module.listPlugins())
|
plugins = render.plugins(plugins_module.listPlugins())
|
||||||
|
|
||||||
return render_template("plugins.html", account=account, sync=account_module.getNodeSync(),
|
return render_template("plugins.html", account=account, sync=account_module.getNodeSync(),
|
||||||
|
wallet_status=account_module.getWalletStatus(),
|
||||||
plugins=plugins)
|
plugins=plugins)
|
||||||
|
|
||||||
@app.route('/plugin/<plugin>')
|
@app.route('/plugin/<ptype>/<path:plugin>')
|
||||||
def plugin(plugin):
|
def plugin(ptype,plugin):
|
||||||
# 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:
|
||||||
return redirect("/login")
|
return redirect("/login")
|
||||||
@@ -1274,7 +1310,10 @@ def plugin(plugin):
|
|||||||
if not account:
|
if not account:
|
||||||
return redirect("/logout")
|
return redirect("/logout")
|
||||||
|
|
||||||
|
plugin = f"{ptype}/{plugin}"
|
||||||
|
|
||||||
if not plugins_module.pluginExists(plugin):
|
if not plugins_module.pluginExists(plugin):
|
||||||
|
print(f"Plugin {plugin} not found")
|
||||||
return redirect("/plugins")
|
return redirect("/plugins")
|
||||||
|
|
||||||
data = plugins_module.getPluginData(plugin)
|
data = plugins_module.getPluginData(plugin)
|
||||||
@@ -1291,12 +1330,13 @@ def plugin(plugin):
|
|||||||
error = ""
|
error = ""
|
||||||
|
|
||||||
return render_template("plugin.html", account=account, sync=account_module.getNodeSync(),
|
return render_template("plugin.html", account=account, sync=account_module.getNodeSync(),
|
||||||
|
wallet_status=account_module.getWalletStatus(),
|
||||||
name=data['name'],description=data['description'],
|
name=data['name'],description=data['description'],
|
||||||
author=data['author'],version=data['version'],
|
author=data['author'],version=data['version'],
|
||||||
functions=functions,error=error)
|
source=data['source'],functions=functions,error=error)
|
||||||
|
|
||||||
@app.route('/plugin/<plugin>/verify')
|
@app.route('/plugin/<ptype>/<path:plugin>/verify')
|
||||||
def plugin_verify(plugin):
|
def plugin_verify(ptype,plugin):
|
||||||
# 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:
|
||||||
return redirect("/login")
|
return redirect("/login")
|
||||||
@@ -1304,6 +1344,8 @@ def plugin_verify(plugin):
|
|||||||
account = account_module.check_account(request.cookies.get("account"))
|
account = account_module.check_account(request.cookies.get("account"))
|
||||||
if not account:
|
if not account:
|
||||||
return redirect("/logout")
|
return redirect("/logout")
|
||||||
|
|
||||||
|
plugin = f"{ptype}/{plugin}"
|
||||||
|
|
||||||
if not plugins_module.pluginExists(plugin):
|
if not plugins_module.pluginExists(plugin):
|
||||||
return redirect("/plugins")
|
return redirect("/plugins")
|
||||||
@@ -1315,8 +1357,8 @@ def plugin_verify(plugin):
|
|||||||
|
|
||||||
return redirect("/plugin/" + plugin)
|
return redirect("/plugin/" + plugin)
|
||||||
|
|
||||||
@app.route('/plugin/<plugin>/<function>', methods=["POST"])
|
@app.route('/plugin/<ptype>/<path:plugin>/<function>', methods=["POST"])
|
||||||
def plugin_function(plugin,function):
|
def plugin_function(ptype,plugin,function):
|
||||||
# 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:
|
||||||
return redirect("/login")
|
return redirect("/login")
|
||||||
@@ -1325,6 +1367,8 @@ def plugin_function(plugin,function):
|
|||||||
if not account:
|
if not account:
|
||||||
return redirect("/logout")
|
return redirect("/logout")
|
||||||
|
|
||||||
|
plugin = f"{ptype}/{plugin}"
|
||||||
|
|
||||||
if not plugins_module.pluginExists(plugin):
|
if not plugins_module.pluginExists(plugin):
|
||||||
return redirect("/plugins")
|
return redirect("/plugins")
|
||||||
|
|
||||||
@@ -1357,8 +1401,8 @@ def plugin_function(plugin,function):
|
|||||||
return redirect("/plugin/" + plugin + "?error=" + response['error'])
|
return redirect("/plugin/" + plugin + "?error=" + response['error'])
|
||||||
|
|
||||||
response = render.plugin_output(response,plugins_module.getPluginFunctionReturns(plugin,function))
|
response = render.plugin_output(response,plugins_module.getPluginFunctionReturns(plugin,function))
|
||||||
|
|
||||||
return render_template("plugin-output.html", account=account, sync=account_module.getNodeSync(),
|
return render_template("plugin-output.html", account=account, sync=account_module.getNodeSync(),
|
||||||
|
wallet_status=account_module.getWalletStatus(),
|
||||||
name=data['name'],description=data['description'],output=response)
|
name=data['name'],description=data['description'],output=response)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
110
plugin.py
110
plugin.py
@@ -3,11 +3,12 @@ import json
|
|||||||
import importlib
|
import importlib
|
||||||
import sys
|
import sys
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
def listPlugins():
|
def listPlugins():
|
||||||
plugins = []
|
plugins = []
|
||||||
|
customPlugins = []
|
||||||
for file in os.listdir("plugins"):
|
for file in os.listdir("plugins"):
|
||||||
if file.endswith(".py"):
|
if file.endswith(".py"):
|
||||||
if file != "main.py":
|
if file != "main.py":
|
||||||
@@ -15,19 +16,52 @@ def listPlugins():
|
|||||||
if "info" not in dir(plugin):
|
if "info" not in dir(plugin):
|
||||||
continue
|
continue
|
||||||
details = plugin.info
|
details = plugin.info
|
||||||
details["link"] = file[:-3]
|
details["source"] = "built-in"
|
||||||
|
details["link"] = f"plugins/{file[:-3]}"
|
||||||
plugins.append(details)
|
plugins.append(details)
|
||||||
|
|
||||||
|
# Check for imported plugins
|
||||||
|
if not os.path.exists("user_data/plugins.json"):
|
||||||
|
with open("user_data/plugins.json", "w") as f:
|
||||||
|
json.dump([], f)
|
||||||
|
|
||||||
|
with open("user_data/plugins.json", "r") as f:
|
||||||
|
importurls = json.load(f)
|
||||||
|
|
||||||
|
for importurl in importurls:
|
||||||
|
# Get only repo name
|
||||||
|
importPath = importurl.split("/")[-1].removesuffix(".git")
|
||||||
|
|
||||||
|
# Git clone into customPlugins/<importPath>
|
||||||
|
if not os.path.exists(f"customPlugins/{importPath}"):
|
||||||
|
if os.system(f"git clone {importurl} customPlugins/{importPath}") != 0:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
if os.system(f"cd customPlugins/{importPath} && git pull") != 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Import plugins from customPlugins/<importPath>
|
||||||
|
for file in os.listdir(f"customPlugins/{importPath}"):
|
||||||
|
if file.endswith(".py"):
|
||||||
|
if file != "main.py":
|
||||||
|
plugin = importlib.import_module(f"customPlugins.{importPath}."+file[:-3])
|
||||||
|
if "info" not in dir(plugin):
|
||||||
|
continue
|
||||||
|
details = plugin.info
|
||||||
|
details["source"] = importPath
|
||||||
|
details["link"] = f"customPlugins/{importPath}/{file[:-3]}"
|
||||||
|
plugins.append(details)
|
||||||
|
|
||||||
# Verify plugin signature
|
# Verify plugin signature
|
||||||
signatures = []
|
signatures = []
|
||||||
try:
|
try:
|
||||||
with open("plugins/signatures.json", "r") as f:
|
with open("user_data/plugin_signatures.json", "r") as f:
|
||||||
signatures = json.load(f)
|
signatures = json.load(f)
|
||||||
except:
|
except:
|
||||||
# Write a new signatures file
|
# Write a new signatures file
|
||||||
with open("plugins/signatures.json", "w") as f:
|
with open("user_data/plugin_signatures.json", "w") as f:
|
||||||
json.dump(signatures, f)
|
json.dump(signatures, f)
|
||||||
|
|
||||||
for plugin in plugins:
|
for plugin in plugins:
|
||||||
# Hash the plugin file
|
# Hash the plugin file
|
||||||
pluginHash = hashPlugin(plugin["link"])
|
pluginHash = hashPlugin(plugin["link"])
|
||||||
@@ -40,79 +74,85 @@ def listPlugins():
|
|||||||
|
|
||||||
|
|
||||||
def pluginExists(plugin: str):
|
def pluginExists(plugin: str):
|
||||||
for file in os.listdir("plugins"):
|
return os.path.exists(plugin+".py")
|
||||||
if file == plugin+".py":
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def verifyPlugin(plugin: str):
|
def verifyPlugin(plugin: str):
|
||||||
signatures = []
|
signatures = []
|
||||||
try:
|
try:
|
||||||
with open("plugins/signatures.json", "r") as f:
|
with open("user_data/plugin_signatures.json", "r") as f:
|
||||||
signatures = json.load(f)
|
signatures = json.load(f)
|
||||||
except:
|
except:
|
||||||
# Write a new signatures file
|
# Write a new signatures file
|
||||||
with open("plugins/signatures.json", "w") as f:
|
with open("user_data/plugin_signatures.json", "w") as f:
|
||||||
json.dump(signatures, f)
|
json.dump(signatures, f)
|
||||||
|
|
||||||
# Hash the plugin file
|
# Hash the plugin file
|
||||||
pluginHash = hashPlugin(plugin)
|
pluginHash = hashPlugin(plugin)
|
||||||
if pluginHash not in signatures:
|
if pluginHash not in signatures:
|
||||||
signatures.append(pluginHash)
|
signatures.append(pluginHash)
|
||||||
with open("plugins/signatures.json", "w") as f:
|
with open("user_data/plugin_signatures.json", "w") as f:
|
||||||
json.dump(signatures, f)
|
json.dump(signatures, f)
|
||||||
|
|
||||||
|
|
||||||
def hashPlugin(plugin: str):
|
def hashPlugin(plugin: str):
|
||||||
BUF_SIZE = 65536
|
BUF_SIZE = 65536
|
||||||
sha256 = hashlib.sha256()
|
sha256 = hashlib.sha256()
|
||||||
with open("plugins/"+plugin+".py", 'rb') as f:
|
with open(plugin+".py", 'rb') as f:
|
||||||
while True:
|
while True:
|
||||||
data = f.read(BUF_SIZE)
|
data = f.read(BUF_SIZE)
|
||||||
if not data:
|
if not data:
|
||||||
break
|
break
|
||||||
sha256.update(data)
|
sha256.update(data)
|
||||||
return sha256.hexdigest()
|
return sha256.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def getPluginData(pluginStr: str):
|
def getPluginData(pluginStr: str):
|
||||||
plugin = importlib.import_module("plugins."+pluginStr)
|
plugin = importlib.import_module(pluginStr.replace("/","."))
|
||||||
|
|
||||||
# Check if the plugin is verified
|
# Check if the plugin is verified
|
||||||
signatures = []
|
signatures = []
|
||||||
try:
|
try:
|
||||||
with open("plugins/signatures.json", "r") as f:
|
with open("user_data/plugin_signatures.json", "r") as f:
|
||||||
signatures = json.load(f)
|
signatures = json.load(f)
|
||||||
except:
|
except:
|
||||||
# Write a new signatures file
|
# Write a new signatures file
|
||||||
with open("plugins/signatures.json", "w") as f:
|
with open("user_data/plugin_signatures.json", "w") as f:
|
||||||
json.dump(signatures, f)
|
json.dump(signatures, f)
|
||||||
|
|
||||||
info = plugin.info
|
info = plugin.info
|
||||||
|
info["source"] = "built-in"
|
||||||
|
|
||||||
|
# Check if the plugin is in customPlugins
|
||||||
|
if pluginStr.startswith("customPlugins"):
|
||||||
|
# Get git url for dir
|
||||||
|
print(f"cd customPlugins/{pluginStr.split('/')[-2]} && git remote get-url origin")
|
||||||
|
url = subprocess.check_output(f"cd customPlugins/{pluginStr.split('/')[-2]} && git remote get-url origin", shell=True).decode("utf-8").strip()
|
||||||
|
info["source"] = url
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Hash the plugin file
|
# Hash the plugin file
|
||||||
pluginHash = hashPlugin(pluginStr)
|
pluginHash = hashPlugin(pluginStr)
|
||||||
if pluginHash not in signatures:
|
if pluginHash not in signatures:
|
||||||
info["verified"] = False
|
info["verified"] = False
|
||||||
else:
|
else:
|
||||||
info["verified"] = True
|
info["verified"] = True
|
||||||
|
|
||||||
return info
|
return info
|
||||||
|
|
||||||
|
|
||||||
def getPluginFunctions(plugin: str):
|
def getPluginFunctions(plugin: str):
|
||||||
plugin = importlib.import_module("plugins."+plugin)
|
plugin = importlib.import_module(plugin.replace("/","."))
|
||||||
return plugin.functions
|
return plugin.functions
|
||||||
|
|
||||||
|
|
||||||
def runPluginFunction(plugin: str, function: str, params: dict, authentication: str):
|
def runPluginFunction(plugin: str, function: str, params: dict, authentication: str):
|
||||||
plugin_module = importlib.import_module("plugins."+plugin)
|
plugin_module = importlib.import_module(plugin.replace("/","."))
|
||||||
if function not in plugin_module.functions:
|
if function not in plugin_module.functions:
|
||||||
return {"error": "Function not found"}
|
return {"error": "Function not found"}
|
||||||
|
|
||||||
if not hasattr(plugin_module, function):
|
if not hasattr(plugin_module, function):
|
||||||
return {"error": "Function not found"}
|
return {"error": "Function not found"}
|
||||||
|
|
||||||
@@ -122,11 +162,11 @@ def runPluginFunction(plugin: str, function: str, params: dict, authentication:
|
|||||||
# Check if the function is in the signature list
|
# Check if the function is in the signature list
|
||||||
signatures = []
|
signatures = []
|
||||||
try:
|
try:
|
||||||
with open("plugins/signatures.json", "r") as f:
|
with open("user_data/plugin_signatures.json", "r") as f:
|
||||||
signatures = json.load(f)
|
signatures = json.load(f)
|
||||||
except:
|
except:
|
||||||
# Write a new signatures file
|
# Write a new signatures file
|
||||||
with open("plugins/signatures.json", "w") as f:
|
with open("user_data/plugin_signatures.json", "w") as f:
|
||||||
json.dump(signatures, f)
|
json.dump(signatures, f)
|
||||||
|
|
||||||
# Hash the plugin file
|
# Hash the plugin file
|
||||||
@@ -134,7 +174,6 @@ def runPluginFunction(plugin: str, function: str, params: dict, authentication:
|
|||||||
if pluginHash not in signatures:
|
if pluginHash not in signatures:
|
||||||
return {"error": "Plugin not verified"}
|
return {"error": "Plugin not verified"}
|
||||||
|
|
||||||
|
|
||||||
# Call the function with provided parameters
|
# Call the function with provided parameters
|
||||||
try:
|
try:
|
||||||
result = plugin_function(params, authentication)
|
result = plugin_function(params, authentication)
|
||||||
@@ -144,14 +183,17 @@ def runPluginFunction(plugin: str, function: str, params: dict, authentication:
|
|||||||
return {"error": str(e)}
|
return {"error": str(e)}
|
||||||
# return plugin.runFunction(function, params, authentication)
|
# return plugin.runFunction(function, params, authentication)
|
||||||
|
|
||||||
|
|
||||||
def getPluginFunctionInputs(plugin: str, function: str):
|
def getPluginFunctionInputs(plugin: str, function: str):
|
||||||
plugin = importlib.import_module("plugins."+plugin)
|
plugin = importlib.import_module(plugin.replace("/","."))
|
||||||
return plugin.functions[function]["params"]
|
return plugin.functions[function]["params"]
|
||||||
|
|
||||||
|
|
||||||
def getPluginFunctionReturns(plugin: str, function: str):
|
def getPluginFunctionReturns(plugin: str, function: str):
|
||||||
plugin = importlib.import_module("plugins."+plugin)
|
plugin = importlib.import_module(plugin.replace("/","."))
|
||||||
return plugin.functions[function]["returns"]
|
return plugin.functions[function]["returns"]
|
||||||
|
|
||||||
|
|
||||||
def getDomainFunctions():
|
def getDomainFunctions():
|
||||||
plugins = listPlugins()
|
plugins = listPlugins()
|
||||||
domainFunctions = []
|
domainFunctions = []
|
||||||
@@ -166,6 +208,7 @@ def getDomainFunctions():
|
|||||||
})
|
})
|
||||||
return domainFunctions
|
return domainFunctions
|
||||||
|
|
||||||
|
|
||||||
def getSearchFunctions():
|
def getSearchFunctions():
|
||||||
plugins = listPlugins()
|
plugins = listPlugins()
|
||||||
searchFunctions = []
|
searchFunctions = []
|
||||||
@@ -180,6 +223,7 @@ def getSearchFunctions():
|
|||||||
})
|
})
|
||||||
return searchFunctions
|
return searchFunctions
|
||||||
|
|
||||||
|
|
||||||
def getDashboardFunctions():
|
def getDashboardFunctions():
|
||||||
plugins = listPlugins()
|
plugins = listPlugins()
|
||||||
dashboardFunctions = []
|
dashboardFunctions = []
|
||||||
@@ -192,4 +236,4 @@ def getDashboardFunctions():
|
|||||||
"function": function,
|
"function": function,
|
||||||
"description": functions[function]["description"]
|
"description": functions[function]["description"]
|
||||||
})
|
})
|
||||||
return dashboardFunctions
|
return dashboardFunctions
|
||||||
|
|||||||
@@ -3,13 +3,15 @@ import account
|
|||||||
import requests
|
import requests
|
||||||
import threading
|
import threading
|
||||||
import os
|
import os
|
||||||
import datetime
|
import time
|
||||||
|
|
||||||
APIKEY = os.environ.get("hsd_api")
|
APIKEY = os.environ.get("hsd_api")
|
||||||
ip = os.getenv("hsd_ip")
|
ip = os.getenv("hsd_ip")
|
||||||
if ip is None:
|
if ip is None:
|
||||||
ip = "localhost"
|
ip = "localhost"
|
||||||
|
|
||||||
|
if not os.path.exists("user_data"):
|
||||||
|
os.mkdir("user_data")
|
||||||
|
|
||||||
# Plugin Data
|
# Plugin Data
|
||||||
info = {
|
info = {
|
||||||
@@ -34,48 +36,123 @@ functions = {
|
|||||||
"type": "text"
|
"type": "text"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"disable":{
|
||||||
|
"name": "Disable Automations",
|
||||||
|
"type": "default",
|
||||||
|
"description": "Disable Automations for this wallet",
|
||||||
|
"params": {},
|
||||||
|
"returns": {
|
||||||
|
"Status":
|
||||||
|
{
|
||||||
|
"name": "Status",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"enable":{
|
||||||
|
"name": "Enable Automations",
|
||||||
|
"type": "default",
|
||||||
|
"description": "Enable Automations for this wallet",
|
||||||
|
"params": {},
|
||||||
|
"returns": {
|
||||||
|
"Status":
|
||||||
|
{
|
||||||
|
"name": "Status",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"list":{
|
||||||
|
"name": "List Disabled Wallets",
|
||||||
|
"type": "default",
|
||||||
|
"description": "List wallets with automations disabled",
|
||||||
|
"params": {},
|
||||||
|
"returns": {
|
||||||
|
"wallets":
|
||||||
|
{
|
||||||
|
"name": "List of wallets",
|
||||||
|
"type": "list"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
started = 0
|
started = False
|
||||||
|
|
||||||
# Main entry point only lets the main function run every 5 mins
|
# Main entry point only lets the main function run every 5 mins
|
||||||
def automation(params, authentication):
|
def automation(params, authentication):
|
||||||
global started
|
global started
|
||||||
now = datetime.datetime.now().timestamp()
|
|
||||||
# Add 5 mins
|
wallet = authentication.split(":")[0]
|
||||||
now = now - 300
|
if os.path.exists(f"user_data/{wallet}.autoRenew"):
|
||||||
if now < started:
|
return {"Status": "Automations disabled"}
|
||||||
return {"Status": "Waiting before checking for new actions"}
|
|
||||||
started = datetime.datetime.now().timestamp()
|
if started:
|
||||||
|
return {"Status": "Automations running"}
|
||||||
|
started = True
|
||||||
|
|
||||||
threading.Thread(target=automations_background, args=(authentication,)).start()
|
threading.Thread(target=automations_background, args=(authentication,)).start()
|
||||||
return {"Status": "Checking for actions"}
|
return {"Status": "Starting Automations..."}
|
||||||
|
|
||||||
|
def disable(params, authentication):
|
||||||
|
# Create walletname file in user_data
|
||||||
|
wallet = authentication.split(":")[0]
|
||||||
|
if not os.path.exists("user_data"):
|
||||||
|
os.mkdir("user_data")
|
||||||
|
with open(f"user_data/{wallet}.autoRenew", "w") as f:
|
||||||
|
f.write(f"This file is used to disable automations for '{wallet}' wallet.\nDelete this file to enable automations.")
|
||||||
|
return {"Status": "Disabled Automations"}
|
||||||
|
|
||||||
|
def enable(params, authentication):
|
||||||
|
# Delete walletname file in user_data
|
||||||
|
wallet = authentication.split(":")[0]
|
||||||
|
if os.path.exists(f"user_data/{wallet}.autoRenew"):
|
||||||
|
os.remove(f"user_data/{wallet}.autoRenew")
|
||||||
|
|
||||||
|
return {"Status": "Enabled Automations"}
|
||||||
|
|
||||||
|
def list(params, authentication):
|
||||||
|
wallets = []
|
||||||
|
for file in os.listdir("user_data"):
|
||||||
|
if file.endswith(".autoRenew"):
|
||||||
|
wallets.append(file[:-10])
|
||||||
|
return {"wallets": wallets}
|
||||||
|
|
||||||
# Background function to run the automations
|
# Background function to run the automations
|
||||||
def automations_background(authentication):
|
def automations_background(authentication):
|
||||||
print("Running automations")
|
|
||||||
# Get account details
|
|
||||||
account_name = account.check_account(authentication)
|
|
||||||
password = ":".join(authentication.split(":")[1:])
|
|
||||||
|
|
||||||
if account_name == False:
|
while True:
|
||||||
return {
|
# Get account details
|
||||||
"error": {
|
account_name = account.check_account(authentication)
|
||||||
"message": "Invalid account"
|
password = ":".join(authentication.split(":")[1:])
|
||||||
|
|
||||||
|
if account_name == False:
|
||||||
|
return {
|
||||||
|
"error": {
|
||||||
|
"message": "Invalid account"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if os.path.exists(f"user_data/{account_name}.autoRenew"):
|
||||||
|
print("Skipping Automations")
|
||||||
|
time.sleep(300)
|
||||||
|
continue
|
||||||
|
print("Running automations")
|
||||||
|
try:
|
||||||
|
# Try to select and login to the wallet
|
||||||
|
response = account.hsw.rpc_selectWallet(account_name)
|
||||||
|
if response['error'] is not None:
|
||||||
|
return
|
||||||
|
response = account.hsw.rpc_walletPassphrase(password,30)
|
||||||
|
if response['error'] is not None:
|
||||||
|
return
|
||||||
|
# Try to send the batch of all renew, reveal and redeem actions
|
||||||
|
requests.post(f"http://x:{APIKEY}@{ip}:12039",json={"method": "sendbatch","params": [[["RENEW"]]]})
|
||||||
|
requests.post(f"http://x:{APIKEY}@{ip}:12039",json={"method": "sendbatch","params": [[["REVEAL"]]]})
|
||||||
|
requests.post(f"http://x:{APIKEY}@{ip}:12039",json={"method": "sendbatch","params": [[["REDEEM"]]]})
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
try:
|
# Sleep for 5 mins before running again
|
||||||
# Try to select and login to the wallet
|
time.sleep(300)
|
||||||
response = account.hsw.rpc_selectWallet(account_name)
|
|
||||||
if response['error'] is not None:
|
|
||||||
return
|
|
||||||
response = account.hsw.rpc_walletPassphrase(password,10)
|
|
||||||
if response['error'] is not None:
|
|
||||||
return
|
|
||||||
# Try to send the batch of all renew, reveal and redeem actions
|
|
||||||
requests.post(f"http://x:{APIKEY}@{ip}:12039",json={"method": "sendbatch","params": [[["RENEW"]]]})
|
|
||||||
requests.post(f"http://x:{APIKEY}@{ip}:12039",json={"method": "sendbatch","params": [[["REVEAL"]]]})
|
|
||||||
requests.post(f"http://x:{APIKEY}@{ip}:12039",json={"method": "sendbatch","params": [[["REDEEM"]]]})
|
|
||||||
except Exception as e:
|
|
||||||
print(e)
|
|
||||||
518
plugins/batching.py
Normal file
518
plugins/batching.py
Normal file
@@ -0,0 +1,518 @@
|
|||||||
|
import json
|
||||||
|
import account
|
||||||
|
import requests
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Plugin Data
|
||||||
|
info = {
|
||||||
|
"name": "Batching Functions",
|
||||||
|
"description": "This is a plugin that provides multiple functions to batch transactions",
|
||||||
|
"version": "1.0",
|
||||||
|
"author": "Nathan.Woodburn/"
|
||||||
|
}
|
||||||
|
# https://hsd-dev.org/api-docs/?shell--cli#sendbatch
|
||||||
|
|
||||||
|
|
||||||
|
# Functions
|
||||||
|
functions = {
|
||||||
|
"transfer":{
|
||||||
|
"name": "Batch transfer",
|
||||||
|
"type": "default",
|
||||||
|
"description": "Transfer a ton of domains",
|
||||||
|
"params": {
|
||||||
|
"domains": {
|
||||||
|
"name":"List of domains to transfer (one per line)",
|
||||||
|
"type":"longText"
|
||||||
|
},
|
||||||
|
"address": {
|
||||||
|
"name":"Address to transfer to",
|
||||||
|
"type":"address"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"returns": {
|
||||||
|
"status":
|
||||||
|
{
|
||||||
|
"name": "Status",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"transaction":
|
||||||
|
{
|
||||||
|
"name": "Hash of the transaction",
|
||||||
|
"type": "tx"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"finalize":{
|
||||||
|
"name": "Batch finalize a transfer",
|
||||||
|
"type": "default",
|
||||||
|
"description": "Finalize transferring a ton of domains",
|
||||||
|
"params": {
|
||||||
|
"domains": {
|
||||||
|
"name":"List of domains to finalize (one per line)",
|
||||||
|
"type":"longText"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"returns": {
|
||||||
|
"status":
|
||||||
|
{
|
||||||
|
"name": "Status",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"transaction":
|
||||||
|
{
|
||||||
|
"name": "Hash of the transaction",
|
||||||
|
"type": "tx"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cancel":{
|
||||||
|
"name": "Batch cancel a transfer",
|
||||||
|
"type": "default",
|
||||||
|
"description": "Cancel transferring a ton of domains",
|
||||||
|
"params": {
|
||||||
|
"domains": {
|
||||||
|
"name":"List of domains to cancel (one per line)",
|
||||||
|
"type":"longText"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"returns": {
|
||||||
|
"status":
|
||||||
|
{
|
||||||
|
"name": "Status",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"transaction":
|
||||||
|
{
|
||||||
|
"name": "Hash of the transaction",
|
||||||
|
"type": "tx"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"open":{
|
||||||
|
"name": "Batch open auctions",
|
||||||
|
"type": "default",
|
||||||
|
"description": "Open auctions for a ton of domains",
|
||||||
|
"params": {
|
||||||
|
"domains": {
|
||||||
|
"name":"List of domains to open (one per line)",
|
||||||
|
"type":"longText"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"returns": {
|
||||||
|
"status":
|
||||||
|
{
|
||||||
|
"name": "Status",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"transaction":
|
||||||
|
{
|
||||||
|
"name": "Hash of the transaction",
|
||||||
|
"type": "tx"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bid":{
|
||||||
|
"name": "Batch bid on auctions",
|
||||||
|
"type": "default",
|
||||||
|
"description": "Bid on auctions for a ton of domains",
|
||||||
|
"params": {
|
||||||
|
"domains": {
|
||||||
|
"name":"List of domains to bid on (one per line)",
|
||||||
|
"type":"longText"
|
||||||
|
},
|
||||||
|
"bid": {
|
||||||
|
"name":"Bid amount",
|
||||||
|
"type":"text"
|
||||||
|
},
|
||||||
|
"blind": {
|
||||||
|
"name":"Blind amount",
|
||||||
|
"type":"text"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"returns": {
|
||||||
|
"status":
|
||||||
|
{
|
||||||
|
"name": "Status",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"transaction":
|
||||||
|
{
|
||||||
|
"name": "Hash of the transaction",
|
||||||
|
"type": "tx"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"reveal":{
|
||||||
|
"name": "Batch reveal bids",
|
||||||
|
"type": "default",
|
||||||
|
"description": "Reveal bids for tons of auctions",
|
||||||
|
"params": {
|
||||||
|
"domains": {
|
||||||
|
"name":"List of domains to reveal (one per line)",
|
||||||
|
"type":"longText"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"returns": {
|
||||||
|
"status":
|
||||||
|
{
|
||||||
|
"name": "Status",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"transaction":
|
||||||
|
{
|
||||||
|
"name": "Hash of the transaction",
|
||||||
|
"type": "tx"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"redeem":{
|
||||||
|
"name": "Batch redeem bids",
|
||||||
|
"type": "default",
|
||||||
|
"description": "Redeem lost bids to get funds back",
|
||||||
|
"params": {
|
||||||
|
"domains": {
|
||||||
|
"name":"List of domains to redeem (one per line)",
|
||||||
|
"type":"longText"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"returns": {
|
||||||
|
"status":
|
||||||
|
{
|
||||||
|
"name": "Status",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"transaction":
|
||||||
|
{
|
||||||
|
"name": "Hash of the transaction",
|
||||||
|
"type": "tx"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"register":{
|
||||||
|
"name": "Batch register domains",
|
||||||
|
"type": "default",
|
||||||
|
"description": "Register domains won in auction",
|
||||||
|
"params": {
|
||||||
|
"domains": {
|
||||||
|
"name":"List of domains to redeem (one per line)",
|
||||||
|
"type":"longText"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"returns": {
|
||||||
|
"status":
|
||||||
|
{
|
||||||
|
"name": "Status",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"transaction":
|
||||||
|
{
|
||||||
|
"name": "Hash of the transaction",
|
||||||
|
"type": "tx"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"renew":{
|
||||||
|
"name": "Batch renew domains",
|
||||||
|
"type": "default",
|
||||||
|
"description": "Renew a ton of domain",
|
||||||
|
"params": {
|
||||||
|
"domains": {
|
||||||
|
"name": "Domains to renew (one per line)",
|
||||||
|
"type": "longText"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"returns": {
|
||||||
|
"status": {
|
||||||
|
"name": "Status",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"transaction":
|
||||||
|
{
|
||||||
|
"name": "Hash of the transaction",
|
||||||
|
"type": "tx"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"advancedBid":{
|
||||||
|
"name": "Bid on domains with csv",
|
||||||
|
"type": "default",
|
||||||
|
"description": "Bid on domains using a csv format",
|
||||||
|
"params": {
|
||||||
|
"bids": {
|
||||||
|
"name":"List of bids in format `domain,bid,blind` (one per line)",
|
||||||
|
"type":"longText"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"returns": {
|
||||||
|
"status":
|
||||||
|
{
|
||||||
|
"name": "Status",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"transaction":
|
||||||
|
{
|
||||||
|
"name": "Hash of the transaction",
|
||||||
|
"type": "tx"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"advancedBatch":{
|
||||||
|
"name": "Batch transactions with csv",
|
||||||
|
"type": "default",
|
||||||
|
"description": "Batch transactions using a csv format",
|
||||||
|
"params": {
|
||||||
|
"transactions": {
|
||||||
|
"name":"List of transactions in format `type,domain,param1,param2` (one per line) Eg.<br>TRANSFER,woodburn1,hs1q4rkfe5df7ss6wzhnw388hv27we0hp7ha2np0hk<br>OPEN,woodburn2",
|
||||||
|
"type":"longText"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"returns": {
|
||||||
|
"status":
|
||||||
|
{
|
||||||
|
"name": "Status",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"transaction":
|
||||||
|
{
|
||||||
|
"name": "Hash of the transaction",
|
||||||
|
"type": "tx"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"advancedChangeLookahead":{
|
||||||
|
"name": "Change wallet lookahead",
|
||||||
|
"type": "default",
|
||||||
|
"description": "Change the lookahead of the wallet",
|
||||||
|
"params": {
|
||||||
|
"lookahead": {
|
||||||
|
"name":"Lookahead (default 200)",
|
||||||
|
"type":"number"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"returns": {
|
||||||
|
"status":
|
||||||
|
{
|
||||||
|
"name": "Status",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def sendBatch(batch, authentication):
|
||||||
|
response = account.sendBatch(authentication, batch)
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
def transfer(params, authentication):
|
||||||
|
domains = params["domains"]
|
||||||
|
address = params["address"]
|
||||||
|
domains = domains.splitlines()
|
||||||
|
domains = [x.strip() for x in domains]
|
||||||
|
domains = [x for x in domains if x != ""]
|
||||||
|
|
||||||
|
wallet = authentication.split(":")[0]
|
||||||
|
owned = account.getDomains(wallet)
|
||||||
|
# Only keep owned domains ["name"]
|
||||||
|
ownedNames = [domain["name"] for domain in owned]
|
||||||
|
|
||||||
|
for domain in domains:
|
||||||
|
if domain not in ownedNames:
|
||||||
|
return {
|
||||||
|
"status":f"Domain {domain} not owned",
|
||||||
|
"transaction":None
|
||||||
|
}
|
||||||
|
|
||||||
|
batch = []
|
||||||
|
for domain in domains:
|
||||||
|
batch.append(['TRANSFER', domain, address])
|
||||||
|
|
||||||
|
response = sendBatch(batch, authentication)
|
||||||
|
if 'error' in response:
|
||||||
|
return {
|
||||||
|
"status":response['error']['message'],
|
||||||
|
"transaction":None
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status":"Sent batch successfully",
|
||||||
|
"transaction":response['hash']
|
||||||
|
}
|
||||||
|
|
||||||
|
def simple(batchType,params, authentication):
|
||||||
|
domains = params["domains"]
|
||||||
|
domains = domains.splitlines()
|
||||||
|
domains = [x.strip() for x in domains]
|
||||||
|
domains = [x for x in domains if x != ""]
|
||||||
|
|
||||||
|
batch = []
|
||||||
|
for domain in domains:
|
||||||
|
batch.append([batchType, domain])
|
||||||
|
|
||||||
|
print(batch)
|
||||||
|
response = sendBatch(batch, authentication)
|
||||||
|
if 'error' in response:
|
||||||
|
print(response)
|
||||||
|
return {
|
||||||
|
"status":response['error']['message'],
|
||||||
|
"transaction":None
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status":"Sent batch successfully",
|
||||||
|
"transaction":response['hash']
|
||||||
|
}
|
||||||
|
|
||||||
|
def finalize(params, authentication):
|
||||||
|
return simple("FINALIZE",params,authentication)
|
||||||
|
|
||||||
|
def cancel(params, authentication):
|
||||||
|
return simple("CANCEL",params,authentication)
|
||||||
|
|
||||||
|
def open(params, authentication):
|
||||||
|
return simple("OPEN",params,authentication)
|
||||||
|
|
||||||
|
def bid(params, authentication):
|
||||||
|
domains = params["domains"]
|
||||||
|
domains = domains.splitlines()
|
||||||
|
domains = [x.strip() for x in domains]
|
||||||
|
domains = [x for x in domains if x != ""]
|
||||||
|
|
||||||
|
try:
|
||||||
|
bid = float(params["bid"])
|
||||||
|
blind = float(params["blind"])
|
||||||
|
blind+=bid
|
||||||
|
except:
|
||||||
|
return {
|
||||||
|
"status":"Invalid bid amount",
|
||||||
|
"transaction":None
|
||||||
|
}
|
||||||
|
|
||||||
|
batch = []
|
||||||
|
for domain in domains:
|
||||||
|
batch.append(['BID', domain, bid, blind])
|
||||||
|
|
||||||
|
print(batch)
|
||||||
|
response = sendBatch(batch, authentication)
|
||||||
|
if 'error' in response:
|
||||||
|
return {
|
||||||
|
"status":response['error']['message'],
|
||||||
|
"transaction":None
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status":"Sent batch successfully",
|
||||||
|
"transaction":response['hash']
|
||||||
|
}
|
||||||
|
|
||||||
|
def reveal(params, authentication):
|
||||||
|
return simple("REVEAL",params,authentication)
|
||||||
|
|
||||||
|
def redeem(params, authentication):
|
||||||
|
return simple("REDEEM",params,authentication)
|
||||||
|
|
||||||
|
def register(params, authentication):
|
||||||
|
domains = params["domains"]
|
||||||
|
domains = domains.splitlines()
|
||||||
|
domains = [x.strip() for x in domains]
|
||||||
|
domains = [x for x in domains if x != ""]
|
||||||
|
|
||||||
|
batch = []
|
||||||
|
for domain in domains:
|
||||||
|
batch.append(['UPDATE', domain,{"records": []}])
|
||||||
|
|
||||||
|
print(batch)
|
||||||
|
response = sendBatch(batch, authentication)
|
||||||
|
if 'error' in response:
|
||||||
|
return {
|
||||||
|
"status":response['error']['message'],
|
||||||
|
"transaction":None
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status":"Sent batch successfully",
|
||||||
|
"transaction":response['hash']
|
||||||
|
}
|
||||||
|
|
||||||
|
def renew(params, authentication):
|
||||||
|
return simple("RENEW", params, authentication)
|
||||||
|
|
||||||
|
def advancedBid(params, authentication):
|
||||||
|
bids = params["bids"]
|
||||||
|
bids = bids.splitlines()
|
||||||
|
bids = [x.strip() for x in bids]
|
||||||
|
bids = [x for x in bids if x != ""]
|
||||||
|
|
||||||
|
batch = []
|
||||||
|
for bid in bids:
|
||||||
|
# Split the bid
|
||||||
|
line = bid.split(",")
|
||||||
|
domain = line[0]
|
||||||
|
bid = float(line[1])
|
||||||
|
blind = float(line[2])
|
||||||
|
blind+=bid
|
||||||
|
batch.append(['BID', domain, bid, blind])
|
||||||
|
|
||||||
|
print(batch)
|
||||||
|
response = sendBatch(batch, authentication)
|
||||||
|
if 'error' in response:
|
||||||
|
return {
|
||||||
|
"status":response['error']['message'],
|
||||||
|
"transaction":None
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status":"Sent batch successfully",
|
||||||
|
"transaction":response['hash']
|
||||||
|
}
|
||||||
|
|
||||||
|
def advancedBatch(params, authentication):
|
||||||
|
transactions = params["transactions"]
|
||||||
|
transactions = transactions.splitlines()
|
||||||
|
transactions = [x.strip() for x in transactions]
|
||||||
|
transactions = [x for x in transactions if x != ""]
|
||||||
|
|
||||||
|
batch = []
|
||||||
|
for transaction in transactions:
|
||||||
|
# Split the bid
|
||||||
|
line = transaction.split(",")
|
||||||
|
line[0] = line[0].upper()
|
||||||
|
batch.append(line)
|
||||||
|
|
||||||
|
print(batch)
|
||||||
|
response = sendBatch(batch, authentication)
|
||||||
|
if 'error' in response:
|
||||||
|
return {
|
||||||
|
"status":response['error']['message'],
|
||||||
|
"transaction":None
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status":"Sent batch successfully",
|
||||||
|
"transaction":response['hash']
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def advancedChangeLookahead(params, authentication):
|
||||||
|
lookahead = params["lookahead"]
|
||||||
|
lookahead = int(lookahead)
|
||||||
|
wallet = authentication.split(":")[0]
|
||||||
|
password = ":".join(authentication.split(":")[1:])
|
||||||
|
APIKEY = os.getenv("hsd_api")
|
||||||
|
ip = os.getenv("hsd_ip")
|
||||||
|
if ip is None:
|
||||||
|
ip = "localhost"
|
||||||
|
|
||||||
|
# Unlock wallet
|
||||||
|
response = requests.post(f"http://x:{APIKEY}@{ip}:12039/wallet/{wallet}/unlock",
|
||||||
|
json={"passphrase": password, "timeout": 10})
|
||||||
|
|
||||||
|
response = requests.patch(f"http://x:{APIKEY}@{ip}:12039/wallet/{wallet}/account/default",
|
||||||
|
json={"lookahead": lookahead})
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status":f"Status: {'Success' if response.status_code == 200 else 'Error'}"
|
||||||
|
}
|
||||||
114
plugins/customPlugins.py
Normal file
114
plugins/customPlugins.py
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import json
|
||||||
|
import account
|
||||||
|
import requests
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Plugin Data
|
||||||
|
info = {
|
||||||
|
"name": "Custom Plugin Manager",
|
||||||
|
"description": "Import custom plugins from git repositories",
|
||||||
|
"version": "1.0",
|
||||||
|
"author": "Nathan.Woodburn/"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Functions
|
||||||
|
functions = {
|
||||||
|
"add":{
|
||||||
|
"name": "Add Plugin repo",
|
||||||
|
"type": "default",
|
||||||
|
"description": "Add a plugin repo",
|
||||||
|
"params": {
|
||||||
|
"url": {
|
||||||
|
"name":"URL",
|
||||||
|
"type":"text"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"returns": {
|
||||||
|
"status":
|
||||||
|
{
|
||||||
|
"name": "Status of the function",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"remove":{
|
||||||
|
"name": "Remove Plugins",
|
||||||
|
"type": "default",
|
||||||
|
"description": "Remove a plugin repo from the list",
|
||||||
|
"params": {
|
||||||
|
"url": {
|
||||||
|
"name":"URL",
|
||||||
|
"type":"text"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"returns": {
|
||||||
|
"status":
|
||||||
|
{
|
||||||
|
"name": "Status of the function",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"list":{
|
||||||
|
"name": "List Plugins",
|
||||||
|
"type": "default",
|
||||||
|
"description": "List all imported plugins",
|
||||||
|
"params": {},
|
||||||
|
"returns": {
|
||||||
|
"plugins":
|
||||||
|
{
|
||||||
|
"name": "List of plugins",
|
||||||
|
"type": "list"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def add(params, authentication):
|
||||||
|
url = params["url"]
|
||||||
|
if not os.path.exists("user_data/plugins.json"):
|
||||||
|
with open("user_data/plugins.json", "w") as f:
|
||||||
|
json.dump([], f)
|
||||||
|
|
||||||
|
with open("user_data/plugins.json", "r") as f:
|
||||||
|
importurls = json.load(f)
|
||||||
|
|
||||||
|
# Check if the plugin is already imported
|
||||||
|
if url in importurls:
|
||||||
|
return {"status": "Plugin already imported"}
|
||||||
|
|
||||||
|
importurls.append(url)
|
||||||
|
with open("user_data/plugins.json", "w") as f:
|
||||||
|
json.dump(importurls, f)
|
||||||
|
|
||||||
|
return {"status": "Imported"}
|
||||||
|
|
||||||
|
|
||||||
|
def remove(params, authentication):
|
||||||
|
url = params["url"]
|
||||||
|
if not os.path.exists("user_data/plugins.json"):
|
||||||
|
with open("user_data/plugins.json", "w") as f:
|
||||||
|
json.dump([], f)
|
||||||
|
|
||||||
|
with open("user_data/plugins.json", "r") as f:
|
||||||
|
importurls = json.load(f)
|
||||||
|
|
||||||
|
# Check if the plugin is already imported
|
||||||
|
if url not in importurls:
|
||||||
|
return {"status": "Plugin not imported"}
|
||||||
|
|
||||||
|
importurls.remove(url)
|
||||||
|
with open("user_data/plugins.json", "w") as f:
|
||||||
|
json.dump(importurls, f)
|
||||||
|
|
||||||
|
return {"status": "Removed"}
|
||||||
|
|
||||||
|
def list(params, authentication):
|
||||||
|
if not os.path.exists("user_data/plugins.json"):
|
||||||
|
with open("user_data/plugins.json", "w") as f:
|
||||||
|
json.dump([], f)
|
||||||
|
|
||||||
|
with open("user_data/plugins.json", "r") as f:
|
||||||
|
importurls = json.load(f)
|
||||||
|
|
||||||
|
return {"plugins": importurls}
|
||||||
98
plugins/renewal.py
Normal file
98
plugins/renewal.py
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
import json
|
||||||
|
import account
|
||||||
|
import requests
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Plugin Data
|
||||||
|
info = {
|
||||||
|
"name": "Batch Renew Domains",
|
||||||
|
"description": "Renew the next 100 domains",
|
||||||
|
"version": "1.0",
|
||||||
|
"author": "Nathan.Woodburn/"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Functions
|
||||||
|
functions = {
|
||||||
|
"main":{
|
||||||
|
"name": "Renew",
|
||||||
|
"type": "default",
|
||||||
|
"description": "Renew the next 100 domains in one transaction. Please wait for at least 1 block confirmation before renewing the next 100 domains",
|
||||||
|
"params": {},
|
||||||
|
"returns": {
|
||||||
|
"status":
|
||||||
|
{
|
||||||
|
"name": "Status of the function",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"transaction":
|
||||||
|
{
|
||||||
|
"name": "Transaction ID",
|
||||||
|
"type": "tx"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def main(params, authentication):
|
||||||
|
password = authentication.split(":")[1]
|
||||||
|
wallet = authentication.split(":")[0]
|
||||||
|
domains = account.getDomains(wallet)
|
||||||
|
domains = sorted(domains, key=lambda k: k['renewal'])
|
||||||
|
|
||||||
|
names = []
|
||||||
|
for domain in domains:
|
||||||
|
name = domain["name"]
|
||||||
|
names.append(name)
|
||||||
|
|
||||||
|
# Split names into batches of 100
|
||||||
|
batches = []
|
||||||
|
for i in range(0, len(names), 100):
|
||||||
|
batches.append(names[i:i+100])
|
||||||
|
|
||||||
|
# Unlock wallet
|
||||||
|
api_key = os.getenv("hsd_api")
|
||||||
|
ip = os.getenv("hsd_ip")
|
||||||
|
if api_key is None:
|
||||||
|
print("API key not set")
|
||||||
|
return {"status": "API key not set", "transaction": "None"}
|
||||||
|
response = requests.post(f'http://x:{api_key}@{ip}:12039/wallet/{wallet}/unlock',
|
||||||
|
json={'passphrase': password, 'timeout': 600})
|
||||||
|
if response.status_code != 200:
|
||||||
|
print("Failed to unlock wallet")
|
||||||
|
print(f'Status code: {response.status_code}')
|
||||||
|
print(f'Response: {response.text}')
|
||||||
|
return {"status": "Failed unlocking wallet", "transaction": "None"}
|
||||||
|
|
||||||
|
|
||||||
|
tx = "None"
|
||||||
|
for batch in batches:
|
||||||
|
batch = []
|
||||||
|
|
||||||
|
for domain in names:
|
||||||
|
batch.append(f'["RENEW", "{domain}"]')
|
||||||
|
|
||||||
|
|
||||||
|
batchTX = "[" + ", ".join(batch) + "]"
|
||||||
|
responseContent = f'{{"method": "sendbatch","params":[ {batchTX} ]}}'
|
||||||
|
response = requests.post(f'http://x:{api_key}@{ip}:12039', data=responseContent)
|
||||||
|
if response.status_code != 200:
|
||||||
|
print("Failed to create batch",flush=True)
|
||||||
|
print(f'Status code: {response.status_code}',flush=True)
|
||||||
|
print(f'Response: {response.text}',flush=True)
|
||||||
|
return {"status": "Failed", "transaction": "None"}
|
||||||
|
|
||||||
|
batch = response.json()
|
||||||
|
# Verify the batch
|
||||||
|
print("Verifying tx...")
|
||||||
|
if batch["error"]:
|
||||||
|
if batch["error"] != "":
|
||||||
|
print("Failed to verify batch",flush=True)
|
||||||
|
print(batch["error"]["message"],flush=True)
|
||||||
|
return {"status": f"Failed: {batch['error']['message']}", "transaction": "None"}
|
||||||
|
|
||||||
|
if 'result' in batch:
|
||||||
|
if batch['result'] != None:
|
||||||
|
tx = batch['result']['hash']
|
||||||
|
return {"status": "Success", "transaction": tx}
|
||||||
|
# Note only one batch can be sent at a time
|
||||||
|
|
||||||
35
plugins/txcount.py
Normal file
35
plugins/txcount.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import json
|
||||||
|
import account
|
||||||
|
import requests
|
||||||
|
|
||||||
|
# Plugin Data
|
||||||
|
info = {
|
||||||
|
"name": "TX Count",
|
||||||
|
"description": "Plugin for checking how many txs are in a wallet",
|
||||||
|
"version": "1.0",
|
||||||
|
"author": "Nathan.Woodburn/"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Functions
|
||||||
|
functions = {
|
||||||
|
"main":{
|
||||||
|
"name": "List TXs",
|
||||||
|
"type": "default",
|
||||||
|
"description": "Get TXs",
|
||||||
|
"params": {},
|
||||||
|
"returns": {
|
||||||
|
"txs":
|
||||||
|
{
|
||||||
|
"name": "Transactions",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def main(params, authentication):
|
||||||
|
wallet = authentication.split(":")[0]
|
||||||
|
txs = account.getTransactions(wallet)
|
||||||
|
|
||||||
|
return {"txs": f'Total TXs: {len(txs)}'}
|
||||||
|
|
||||||
@@ -2,7 +2,9 @@ import json
|
|||||||
import account
|
import account
|
||||||
import requests
|
import requests
|
||||||
import os
|
import os
|
||||||
import dotenv
|
|
||||||
|
if not os.path.exists("user_data"):
|
||||||
|
os.mkdir("user_data")
|
||||||
|
|
||||||
# Plugin Data
|
# Plugin Data
|
||||||
info = {
|
info = {
|
||||||
@@ -17,7 +19,7 @@ functions = {
|
|||||||
"status":{
|
"status":{
|
||||||
"name": "Check connection",
|
"name": "Check connection",
|
||||||
"type": "dashboard",
|
"type": "dashboard",
|
||||||
"description": "You need tp set varo_instance to the ICANN domain of the chosen Varo instance and varo_api to your varo API key before you can connect",
|
"description": "You need to login to the varo instance before you can use this function.",
|
||||||
"params": {},
|
"params": {},
|
||||||
"returns": {
|
"returns": {
|
||||||
"status":
|
"status":
|
||||||
@@ -27,6 +29,28 @@ functions = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"login":{
|
||||||
|
"name": "Login to Varo",
|
||||||
|
"type": "default",
|
||||||
|
"description": "Login to Varo<br>Use the domain of the varo instance (eg. <a target='_blank' href='https://domains.hns.au'>domains.hns.au</a>) and API key from the dashboard.",
|
||||||
|
"params": {
|
||||||
|
"instance": {
|
||||||
|
"name":"Varo instance",
|
||||||
|
"type":"text"
|
||||||
|
},
|
||||||
|
"api": {
|
||||||
|
"name":"API key",
|
||||||
|
"type":"text"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"returns": {
|
||||||
|
"status":
|
||||||
|
{
|
||||||
|
"name": "Status of the function",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"addDomain":{
|
"addDomain":{
|
||||||
"name": "Add domain",
|
"name": "Add domain",
|
||||||
"type": "default",
|
"type": "default",
|
||||||
@@ -50,13 +74,18 @@ functions = {
|
|||||||
|
|
||||||
def status(params, authentication):
|
def status(params, authentication):
|
||||||
# Try to connect to Varo
|
# Try to connect to Varo
|
||||||
dotenv.load_dotenv()
|
if not os.path.exists("user_data/varo.json"):
|
||||||
api = os.getenv("varo_api")
|
|
||||||
instance = os.getenv("varo_instance")
|
|
||||||
|
|
||||||
if not api or not instance:
|
|
||||||
return {"status": "Missing Varo API or instance"}
|
return {"status": "Missing Varo API or instance"}
|
||||||
|
|
||||||
|
with open("user_data/varo.json", "r") as f:
|
||||||
|
auth = json.load(f)
|
||||||
|
if not auth:
|
||||||
|
return {"status": "Missing Varo API or instance"}
|
||||||
|
if 'api' not in auth or 'instance' not in auth:
|
||||||
|
return {"status": "Missing Varo API or instance"}
|
||||||
|
api = auth["api"]
|
||||||
|
instance = auth["instance"]
|
||||||
|
|
||||||
headers = {"Authorization": f"Bearer {api}"}
|
headers = {"Authorization": f"Bearer {api}"}
|
||||||
data = {
|
data = {
|
||||||
"action": "getInfo"
|
"action": "getInfo"
|
||||||
@@ -66,18 +95,49 @@ def status(params, authentication):
|
|||||||
return {"status": "Error connecting to Varo"}
|
return {"status": "Error connecting to Varo"}
|
||||||
if response.json()["success"] != True:
|
if response.json()["success"] != True:
|
||||||
return {"status": "Error connecting to Varo"}
|
return {"status": "Error connecting to Varo"}
|
||||||
|
return {"status": f"Connected to {instance}"}
|
||||||
|
|
||||||
|
def login(params, authentication):
|
||||||
|
# Verify the user has entered the correct details
|
||||||
|
instance = params["instance"]
|
||||||
|
api = params["api"]
|
||||||
|
|
||||||
|
# Strip the https:// from the instance
|
||||||
|
instance = instance.replace("https://", "")
|
||||||
|
instance = instance.replace("http://", "")
|
||||||
|
|
||||||
|
response = requests.post(f"https://{instance}/api", json={"action": "getInfo"}, headers={"Authorization": f"Bearer {api}"})
|
||||||
|
if response.status_code != 200:
|
||||||
|
return {"status": "Error connecting to Varo"}
|
||||||
|
|
||||||
|
if response.json()["success"] != True:
|
||||||
|
return {"status": "Error connecting to Varo"}
|
||||||
|
|
||||||
|
auth = {
|
||||||
|
"instance": instance,
|
||||||
|
"api": api
|
||||||
|
}
|
||||||
|
# Save the API key to the varo.json file
|
||||||
|
with open("user_data/varo.json", "w") as f:
|
||||||
|
json.dump(auth, f)
|
||||||
|
|
||||||
return {"status": "Success"}
|
return {"status": "Success"}
|
||||||
|
|
||||||
def addDomain(params, authentication):
|
def addDomain(params, authentication):
|
||||||
# Add a domain to Varo
|
# Add a domain to Varo
|
||||||
domain = params["domain"]
|
domain = params["domain"]
|
||||||
|
|
||||||
dotenv.load_dotenv()
|
if not os.path.exists("user_data/varo.json"):
|
||||||
api = os.getenv("varo_api")
|
|
||||||
instance = os.getenv("varo_instance")
|
|
||||||
|
|
||||||
if not api or not instance:
|
|
||||||
return {"status": "Missing Varo API or instance"}
|
return {"status": "Missing Varo API or instance"}
|
||||||
|
|
||||||
|
with open("user_data/varo.json", "r") as f:
|
||||||
|
auth = json.load(f)
|
||||||
|
if not auth:
|
||||||
|
return {"status": "Missing Varo API or instance"}
|
||||||
|
if 'api' not in auth or 'instance' not in auth:
|
||||||
|
return {"status": "Missing Varo API or instance"}
|
||||||
|
api = auth["api"]
|
||||||
|
instance = auth["instance"]
|
||||||
|
|
||||||
headers = {"Authorization": f"Bearer {api}"}
|
headers = {"Authorization": f"Bearer {api}"}
|
||||||
data = {
|
data = {
|
||||||
|
|||||||
18
render.py
18
render.py
@@ -2,14 +2,11 @@ import datetime
|
|||||||
import json
|
import json
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
from flask import render_template
|
from flask import render_template
|
||||||
|
from domainLookup import punycode_to_emoji
|
||||||
|
|
||||||
def domains(domains, mobile=False):
|
def domains(domains, mobile=False):
|
||||||
html = ''
|
html = ''
|
||||||
for domain in domains:
|
for domain in domains:
|
||||||
owner = domain['owner']
|
|
||||||
if 'index' in owner:
|
|
||||||
if owner['index'] == 0:
|
|
||||||
continue
|
|
||||||
expires = domain['stats']
|
expires = domain['stats']
|
||||||
if 'daysUntilExpire' in expires:
|
if 'daysUntilExpire' in expires:
|
||||||
expires = expires['daysUntilExpire']
|
expires = expires['daysUntilExpire']
|
||||||
@@ -17,12 +14,17 @@ def domains(domains, mobile=False):
|
|||||||
expires = "No expiration date"
|
expires = "No expiration date"
|
||||||
paid = domain['value']
|
paid = domain['value']
|
||||||
paid = paid / 1000000
|
paid = paid / 1000000
|
||||||
|
|
||||||
|
# Handle punycodes
|
||||||
|
name = domain['name']
|
||||||
|
emoji = punycode_to_emoji(name)
|
||||||
|
if emoji != name:
|
||||||
|
name = f'{emoji} ({name})'
|
||||||
|
|
||||||
if not mobile:
|
if not mobile:
|
||||||
html += f'<tr><td>{domain["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} HNS</td><td><a href="/manage/{domain["name"]}">Manage</a></td></tr>'
|
||||||
else:
|
else:
|
||||||
html += f'<tr><td><a href="/manage/{domain["name"]}">{domain["name"]}</a></td><td>{expires} days</td></tr>'
|
html += f'<tr><td><a href="/manage/{domain["name"]}">{name}</a></td><td>{expires} days</td></tr>'
|
||||||
|
|
||||||
return html
|
return html
|
||||||
|
|
||||||
@@ -187,6 +189,7 @@ def bidDomains(bids,domains, sortState=False):
|
|||||||
bidValue = round(bidValue, 2)
|
bidValue = round(bidValue, 2)
|
||||||
blind = lockup - bidValue
|
blind = lockup - bidValue
|
||||||
bidValue = "{:,}".format(bidValue)
|
bidValue = "{:,}".format(bidValue)
|
||||||
|
blind = round(blind, 2)
|
||||||
blind = "{:,}".format(blind)
|
blind = "{:,}".format(blind)
|
||||||
|
|
||||||
bidDisplay = f'<b>{bidValue} HNS</b> + {blind} HNS blind'
|
bidDisplay = f'<b>{bidValue} HNS</b> + {blind} HNS blind'
|
||||||
@@ -196,6 +199,7 @@ def bidDomains(bids,domains, sortState=False):
|
|||||||
html += f"<td>{domain['name']}</td>"
|
html += f"<td>{domain['name']}</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 += "</tr>"
|
html += "</tr>"
|
||||||
else:
|
else:
|
||||||
for domain in domains:
|
for domain in domains:
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
||||||
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="text-center d-none d-md-inline"><button class="btn rounded-circle border-0" id="sidebarToggle" type="button"></button></div>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
||||||
@@ -44,7 +43,7 @@
|
|||||||
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
||||||
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
||||||
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
||||||
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span>
|
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span><span style="color: var(--bs-dark);margin-left: 10px;">Wallet: {{wallet_status}}</span>
|
||||||
<ul class="navbar-nav flex-nowrap ms-auto">
|
<ul class="navbar-nav flex-nowrap ms-auto">
|
||||||
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
||||||
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -35,7 +35,6 @@
|
|||||||
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
||||||
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="text-center d-none d-md-inline"><button class="btn rounded-circle border-0" id="sidebarToggle" type="button"></button></div>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
||||||
@@ -44,7 +43,7 @@
|
|||||||
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
||||||
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
||||||
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
||||||
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span>
|
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span><span style="color: var(--bs-dark);margin-left: 10px;">Wallet: {{wallet_status}}</span>
|
||||||
<ul class="navbar-nav flex-nowrap ms-auto">
|
<ul class="navbar-nav flex-nowrap ms-auto">
|
||||||
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
||||||
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||||
<title>Dashboard - FireWallet</title>
|
<title>Auctions - FireWallet</title>
|
||||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||||
@@ -35,7 +35,6 @@
|
|||||||
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
||||||
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="text-center d-none d-md-inline"><button class="btn rounded-circle border-0" id="sidebarToggle" type="button"></button></div>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
||||||
@@ -44,7 +43,7 @@
|
|||||||
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
||||||
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
||||||
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
||||||
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span>
|
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span><span style="color: var(--bs-dark);margin-left: 10px;">Wallet: {{wallet_status}}</span>
|
||||||
<ul class="navbar-nav flex-nowrap ms-auto">
|
<ul class="navbar-nav flex-nowrap ms-auto">
|
||||||
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
||||||
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
||||||
@@ -125,6 +124,7 @@
|
|||||||
<th><a href="/auctions?direction={{sort_domain_next}}">Domain{{sort_domain}}</a></th>
|
<th><a href="/auctions?direction={{sort_domain_next}}">Domain{{sort_domain}}</a></th>
|
||||||
<th><a href="/auctions?sort=state&direction={{sort_state_next}}">State{{sort_state}}</a></th>
|
<th><a href="/auctions?sort=state&direction={{sort_state_next}}">State{{sort_state}}</a></th>
|
||||||
<th><a href="/auctions?sort=price&direction={{sort_price_next}}">Bid{{sort_price}}</a></th>
|
<th><a href="/auctions?sort=price&direction={{sort_price_next}}">Bid{{sort_price}}</a></th>
|
||||||
|
<th><a href="/auctions?sort=time&direction={{sort_time_next}}">Block{{sort_time}}</a></th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="card shadow border-start-warning py-2">
|
<div class="card shadow border-start-warning py-2">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="text-uppercase fw-bold text-xs mb-1"><span style="color: var(--bs-dark);">{{name}}</span></div>
|
<div class="text-uppercase fw-bold text-xs mb-1"><span style="color: var(--bs-dark);">{{name}}</span></div>
|
||||||
<div class="text-dark fw-bold h5 mb-0"><span>{{output}}</span></div>
|
<div class="text-dark fw-bold h5 mb-0"><span>{{output | safe}}</span></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
<span style="display: block;font-size: 12px;">TX: {{tx}}</span>
|
<span style="display: block;font-size: 12px;">TX: {{tx}}</span>
|
||||||
<span style="display: block;">Check your transaction on a block explorer</span>
|
<span style="display: block;">Check your transaction on a block explorer</span>
|
||||||
<a class="card-link" href="https://niami.io/tx/{{tx}}" target="_blank">Niami</a>
|
<a class="card-link" href="https://niami.io/tx/{{tx}}" target="_blank">Niami</a>
|
||||||
<a class="card-link" href="https://3xpl.com/handshake/transaction/{{tx}}" target="_blank">3xpl</a>
|
<a class="card-link" href="https://3xpl.com/handshake/transaction/{{tx}}" target="_blank">3xpl</a>
|
||||||
|
<a class="card-link" href="https://hns.cymon.de/tx/{{tx}}" target="_blank">Cymon.de</a>
|
||||||
@@ -35,7 +35,6 @@
|
|||||||
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
||||||
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="text-center d-none d-md-inline"><button class="btn rounded-circle border-0" id="sidebarToggle" type="button"></button></div>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
||||||
@@ -44,7 +43,7 @@
|
|||||||
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
||||||
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
||||||
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
||||||
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span>
|
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span><span style="color: var(--bs-dark);margin-left: 10px;">Wallet: {{wallet_status}}</span>
|
||||||
<ul class="navbar-nav flex-nowrap ms-auto">
|
<ul class="navbar-nav flex-nowrap ms-auto">
|
||||||
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
||||||
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
||||||
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="text-center d-none d-md-inline"><button class="btn rounded-circle border-0" id="sidebarToggle" type="button"></button></div>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
||||||
@@ -44,7 +43,7 @@
|
|||||||
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
||||||
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
||||||
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
||||||
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span>
|
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span><span style="color: var(--bs-dark);margin-left: 10px;">Wallet: {{wallet_status}}</span>
|
||||||
<ul class="navbar-nav flex-nowrap ms-auto">
|
<ul class="navbar-nav flex-nowrap ms-auto">
|
||||||
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
||||||
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
||||||
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="text-center d-none d-md-inline"><button class="btn rounded-circle border-0" id="sidebarToggle" type="button"></button></div>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
||||||
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="text-center d-none d-md-inline"><button class="btn rounded-circle border-0" id="sidebarToggle" type="button"></button></div>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
||||||
@@ -44,7 +43,7 @@
|
|||||||
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
||||||
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
||||||
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
||||||
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span>
|
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span><span style="color: var(--bs-dark);margin-left: 10px;">Wallet: {{wallet_status}}</span>
|
||||||
<ul class="navbar-nav flex-nowrap ms-auto">
|
<ul class="navbar-nav flex-nowrap ms-auto">
|
||||||
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
||||||
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
||||||
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="text-center d-none d-md-inline"><button class="btn rounded-circle border-0" id="sidebarToggle" type="button"></button></div>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
||||||
@@ -44,7 +43,7 @@
|
|||||||
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
||||||
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
||||||
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
||||||
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span>
|
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span><span style="color: var(--bs-dark);margin-left: 10px;">Wallet: {{wallet_status}}</span>
|
||||||
<ul class="navbar-nav flex-nowrap ms-auto">
|
<ul class="navbar-nav flex-nowrap ms-auto">
|
||||||
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
||||||
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
||||||
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="text-center d-none d-md-inline"><button class="btn rounded-circle border-0" id="sidebarToggle" type="button"></button></div>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
||||||
@@ -44,7 +43,7 @@
|
|||||||
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
||||||
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
||||||
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
||||||
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span>
|
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span><span style="color: var(--bs-dark);margin-left: 10px;">Wallet: {{wallet_status}}</span>
|
||||||
<ul class="navbar-nav flex-nowrap ms-auto">
|
<ul class="navbar-nav flex-nowrap ms-auto">
|
||||||
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
||||||
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
||||||
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="text-center d-none d-md-inline"><button class="btn rounded-circle border-0" id="sidebarToggle" type="button"></button></div>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
||||||
@@ -44,7 +43,7 @@
|
|||||||
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
||||||
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
||||||
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
||||||
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span>
|
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span><span style="color: var(--bs-dark);margin-left: 10px;">Wallet: {{wallet_status}}</span>
|
||||||
<ul class="navbar-nav flex-nowrap ms-auto">
|
<ul class="navbar-nav flex-nowrap ms-auto">
|
||||||
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
||||||
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
||||||
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="text-center d-none d-md-inline"><button class="btn rounded-circle border-0" id="sidebarToggle" type="button"></button></div>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
||||||
@@ -44,7 +43,7 @@
|
|||||||
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
||||||
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
||||||
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
||||||
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span>
|
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span><span style="color: var(--bs-dark);margin-left: 10px;">Wallet: {{wallet_status}}</span>
|
||||||
<ul class="navbar-nav flex-nowrap ms-auto">
|
<ul class="navbar-nav flex-nowrap ms-auto">
|
||||||
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
||||||
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
||||||
@@ -67,7 +66,7 @@
|
|||||||
<div class="container-fluid" style="margin-bottom: 20px;">
|
<div class="container-fluid" style="margin-bottom: 20px;">
|
||||||
<h3 class="text-dark mb-1">{{name}}</h3>
|
<h3 class="text-dark mb-1">{{name}}</h3>
|
||||||
<h4 class="text-dark mb-1">{{description}}</h4>
|
<h4 class="text-dark mb-1">{{description}}</h4>
|
||||||
<h6 class="text-dark mb-1">Author: {{author}}<br>Version: {{version}}</h6>{{functions|safe}}
|
<h6 class="text-dark mb-1">Author: {{author}}<br>Version: {{version}}<br>Source: {{source}}</h6>{{functions|safe}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<footer class="sticky-footer" style="background: var(--bs-primary-text-emphasis);">
|
<footer class="sticky-footer" style="background: var(--bs-primary-text-emphasis);">
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
||||||
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="text-center d-none d-md-inline"><button class="btn rounded-circle border-0" id="sidebarToggle" type="button"></button></div>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
||||||
@@ -44,7 +43,7 @@
|
|||||||
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
||||||
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
||||||
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
||||||
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span>
|
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span><span style="color: var(--bs-dark);margin-left: 10px;">Wallet: {{wallet_status}}</span>
|
||||||
<ul class="navbar-nav flex-nowrap ms-auto">
|
<ul class="navbar-nav flex-nowrap ms-auto">
|
||||||
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
||||||
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
||||||
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="text-center d-none d-md-inline"><button class="btn rounded-circle border-0" id="sidebarToggle" type="button"></button></div>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
||||||
@@ -44,7 +43,7 @@
|
|||||||
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
||||||
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
||||||
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
||||||
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span>
|
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span><span style="color: var(--bs-dark);margin-left: 10px;">Wallet: {{wallet_status}}</span>
|
||||||
<ul class="navbar-nav flex-nowrap ms-auto">
|
<ul class="navbar-nav flex-nowrap ms-auto">
|
||||||
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
||||||
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
||||||
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="text-center d-none d-md-inline"><button class="btn rounded-circle border-0" id="sidebarToggle" type="button"></button></div>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
||||||
@@ -44,7 +43,7 @@
|
|||||||
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
||||||
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
||||||
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
||||||
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span>
|
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span><span style="color: var(--bs-dark);margin-left: 10px;">Wallet: {{wallet_status}}</span>
|
||||||
<ul class="navbar-nav flex-nowrap ms-auto">
|
<ul class="navbar-nav flex-nowrap ms-auto">
|
||||||
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
||||||
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
||||||
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="text-center d-none d-md-inline"><button class="btn rounded-circle border-0" id="sidebarToggle" type="button"></button></div>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
||||||
@@ -44,7 +43,7 @@
|
|||||||
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
||||||
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
||||||
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
||||||
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span>
|
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span><span style="color: var(--bs-dark);margin-left: 10px;">Wallet: {{wallet_status}}</span>
|
||||||
<ul class="navbar-nav flex-nowrap ms-auto">
|
<ul class="navbar-nav flex-nowrap ms-auto">
|
||||||
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
||||||
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
||||||
|
|||||||
@@ -36,7 +36,6 @@
|
|||||||
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
||||||
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="text-center d-none d-md-inline"><button class="btn rounded-circle border-0" id="sidebarToggle" type="button"></button></div>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
||||||
@@ -45,7 +44,7 @@
|
|||||||
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
||||||
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
||||||
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
||||||
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span>
|
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span><span style="color: var(--bs-dark);margin-left: 10px;">Wallet: {{wallet_status}}</span>
|
||||||
<ul class="navbar-nav flex-nowrap ms-auto">
|
<ul class="navbar-nav flex-nowrap ms-auto">
|
||||||
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
||||||
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
||||||
@@ -110,8 +109,8 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title">About</h4>
|
<h4 class="card-title">About</h4>
|
||||||
<h6 class="text-muted card-subtitle mb-2">FireWallet version: <code>{{version}}</code></h6>
|
<h6 class="text-muted card-subtitle mb-2">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</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></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
||||||
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="text-center d-none d-md-inline"><button class="btn rounded-circle border-0" id="sidebarToggle" type="button"></button></div>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
||||||
@@ -44,7 +43,7 @@
|
|||||||
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
||||||
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
||||||
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
||||||
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span>
|
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span><span style="color: var(--bs-dark);margin-left: 10px;">Wallet: {{wallet_status}}</span>
|
||||||
<ul class="navbar-nav flex-nowrap ms-auto">
|
<ul class="navbar-nav flex-nowrap ms-auto">
|
||||||
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
||||||
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/receive"><i class="material-icons">call_received</i><span>Receive</span></a></li>
|
||||||
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
<li class="nav-item"><a class="nav-link" href="/plugins"><i class="material-icons">code</i><span>Plugins</span></a><a class="nav-link" href="/settings"><i class="material-icons">settings</i><span>Settings</span></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="text-center d-none d-md-inline"><button class="btn rounded-circle border-0" id="sidebarToggle" type="button"></button></div>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
<div class="d-flex flex-column" id="content-wrapper" style="background: var(--bs-primary);">
|
||||||
@@ -44,7 +43,7 @@
|
|||||||
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
||||||
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search" action="/search" method="get">
|
||||||
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}" style="color: var(--bs-dark-text-emphasis);background: var(--bs-light);"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
||||||
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span>
|
</form><span style="color: var(--bs-dark);">Sync: {{sync}}%</span><span style="color: var(--bs-dark);margin-left: 10px;">Wallet: {{wallet_status}}</span>
|
||||||
<ul class="navbar-nav flex-nowrap ms-auto">
|
<ul class="navbar-nav flex-nowrap ms-auto">
|
||||||
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
||||||
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
||||||
|
|||||||
Reference in New Issue
Block a user