feat: Add DNS management
This commit is contained in:
parent
123e4eea98
commit
fd5d1cae2c
40
account.py
40
account.py
@ -201,6 +201,46 @@ def getDNS(domain: str):
|
|||||||
return response['result']['records']
|
return response['result']['records']
|
||||||
|
|
||||||
|
|
||||||
|
def setDNS(account,domain,records):
|
||||||
|
account_name = check_account(account)
|
||||||
|
password = ":".join(account.split(":")[1:])
|
||||||
|
|
||||||
|
if account_name == False:
|
||||||
|
return {
|
||||||
|
"error": "Invalid account"
|
||||||
|
}
|
||||||
|
|
||||||
|
records = json.loads(records)
|
||||||
|
newRecords = []
|
||||||
|
TXTRecords = []
|
||||||
|
for record in records:
|
||||||
|
if record['type'] == 'TXT':
|
||||||
|
TXTRecords.append(record['value'])
|
||||||
|
elif record['type'] == 'NS':
|
||||||
|
newRecords.append({
|
||||||
|
'type': 'NS',
|
||||||
|
'ns': record['value']
|
||||||
|
})
|
||||||
|
elif record['type'] in ['GLUE4','GLUE6',"SYNTH4","SYNTH6"]:
|
||||||
|
newRecords.append({
|
||||||
|
'type': record['type'],
|
||||||
|
'ns': str(record['value']).split(' ')[0],
|
||||||
|
'address': str(record['value']).split(' ')[1]
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
newRecords.append(record)
|
||||||
|
|
||||||
|
if len(TXTRecords) > 0:
|
||||||
|
newRecords.append({
|
||||||
|
'type': 'TXT',
|
||||||
|
'txt': TXTRecords
|
||||||
|
})
|
||||||
|
|
||||||
|
data = '{"records":'+str(newRecords).replace("'","\"")+'}'
|
||||||
|
response = hsw.sendUPDATE(account_name,password,domain,data)
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
def getNodeSync():
|
def getNodeSync():
|
||||||
response = hsd.getInfo()
|
response = hsd.getInfo()
|
||||||
sync = response['chain']['progress']*100
|
sync = response['chain']['progress']*100
|
||||||
|
93
main.py
93
main.py
@ -1,3 +1,4 @@
|
|||||||
|
import json
|
||||||
from flask import Flask, make_response, redirect, request, jsonify, render_template, send_from_directory,send_file
|
from flask import Flask, make_response, redirect, request, jsonify, render_template, send_from_directory,send_file
|
||||||
import os
|
import os
|
||||||
import dotenv
|
import dotenv
|
||||||
@ -7,6 +8,7 @@ import render
|
|||||||
import re
|
import re
|
||||||
from flask_qrcode import QRcode
|
from flask_qrcode import QRcode
|
||||||
import domainLookup
|
import domainLookup
|
||||||
|
import urllib.parse
|
||||||
|
|
||||||
dotenv.load_dotenv()
|
dotenv.load_dotenv()
|
||||||
|
|
||||||
@ -317,7 +319,7 @@ def search():
|
|||||||
dns=dns, txs=txs)
|
dns=dns, txs=txs)
|
||||||
|
|
||||||
@app.route('/manage/<domain>')
|
@app.route('/manage/<domain>')
|
||||||
def manage(domain):
|
def manage(domain: str):
|
||||||
# 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")
|
||||||
@ -341,15 +343,16 @@ def manage(domain):
|
|||||||
|
|
||||||
expiry = domain_info['info']['stats']['daysUntilExpire']
|
expiry = domain_info['info']['stats']['daysUntilExpire']
|
||||||
dns = account_module.getDNS(domain)
|
dns = account_module.getDNS(domain)
|
||||||
|
raw_dns = str(dns).replace("'",'"')
|
||||||
dns = render.dns(dns)
|
dns = render.dns(dns)
|
||||||
|
|
||||||
|
|
||||||
return render_template("manage.html", account=account, sync=account_module.getNodeSync(),
|
return render_template("manage.html", account=account, sync=account_module.getNodeSync(),
|
||||||
domain=domain,expiry=expiry, dns=dns)
|
domain=domain,expiry=expiry, dns=dns,raw_dns=urllib.parse.quote(raw_dns))
|
||||||
|
|
||||||
|
|
||||||
@app.route('/manage/<domain>/renew')
|
@app.route('/manage/<domain>/renew')
|
||||||
def renew(domain):
|
def renew(domain: str):
|
||||||
# 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")
|
||||||
@ -363,6 +366,90 @@ def renew(domain):
|
|||||||
return redirect("/success?tx=" + response['hash'])
|
return redirect("/success?tx=" + response['hash'])
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/manage/<domain>/edit')
|
||||||
|
def editPage(domain: str):
|
||||||
|
# Check if the user is logged in
|
||||||
|
if request.cookies.get("account") is None:
|
||||||
|
return redirect("/login")
|
||||||
|
|
||||||
|
account = account_module.check_account(request.cookies.get("account"))
|
||||||
|
if not account:
|
||||||
|
return redirect("/logout")
|
||||||
|
|
||||||
|
domain = domain.lower()
|
||||||
|
|
||||||
|
own_domains = account_module.getDomains(account)
|
||||||
|
own_domains = [x['name'] for x in own_domains]
|
||||||
|
own_domains = [x.lower() for x in own_domains]
|
||||||
|
if domain not in own_domains:
|
||||||
|
return redirect("/search?q=" + domain)
|
||||||
|
|
||||||
|
|
||||||
|
user_edits = request.args.get("dns")
|
||||||
|
if user_edits != None:
|
||||||
|
dns = urllib.parse.unquote(user_edits)
|
||||||
|
else:
|
||||||
|
dns = account_module.getDNS(domain)
|
||||||
|
|
||||||
|
dns = json.loads(dns)
|
||||||
|
|
||||||
|
# Check if new records have been added
|
||||||
|
dnsType = request.args.get("type")
|
||||||
|
dnsValue = request.args.get("value")
|
||||||
|
if dnsType != None and dnsValue != None:
|
||||||
|
if dnsType != "DS":
|
||||||
|
dns.append({"type": dnsType, "value": dnsValue})
|
||||||
|
else:
|
||||||
|
# Verify the DS record
|
||||||
|
ds = dnsValue.split(" ")
|
||||||
|
if len(ds) != 4:
|
||||||
|
raw_dns = str(dns).replace("'",'"')
|
||||||
|
return redirect("/manage/" + domain + "/edit?dns=" + urllib.parse.quote(str(raw_dns)) + "&error=Invalid DS record")
|
||||||
|
|
||||||
|
try:
|
||||||
|
ds[0] = int(ds[0])
|
||||||
|
ds[1] = int(ds[1])
|
||||||
|
ds[2] = int(ds[2])
|
||||||
|
except:
|
||||||
|
raw_dns = str(dns).replace("'",'"')
|
||||||
|
return redirect("/manage/" + domain + "/edit?dns=" + urllib.parse.quote(str(raw_dns)) + "&error=Invalid DS record")
|
||||||
|
finally:
|
||||||
|
dns.append({"type": dnsType, "keyTag": ds[0], "algorithm": ds[1], "digestType": ds[2], "digest": ds[3]})
|
||||||
|
|
||||||
|
dns = json.dumps(dns).replace("'",'"')
|
||||||
|
return redirect("/manage/" + domain + "/edit?dns=" + urllib.parse.quote(dns))
|
||||||
|
|
||||||
|
raw_dns = str(dns).replace("'",'"')
|
||||||
|
dns = render.dns(dns,True)
|
||||||
|
errorMessage = request.args.get("error")
|
||||||
|
if errorMessage == None:
|
||||||
|
errorMessage = ""
|
||||||
|
|
||||||
|
|
||||||
|
return render_template("edit.html", account=account, sync=account_module.getNodeSync(),
|
||||||
|
domain=domain, error=errorMessage,
|
||||||
|
dns=dns,raw_dns=urllib.parse.quote(raw_dns))
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/manage/<domain>/edit/save')
|
||||||
|
def editSave(domain: str):
|
||||||
|
# Check if the user is logged in
|
||||||
|
if request.cookies.get("account") is None:
|
||||||
|
return redirect("/login")
|
||||||
|
|
||||||
|
|
||||||
|
if not account_module.check_account(request.cookies.get("account")):
|
||||||
|
return redirect("/logout")
|
||||||
|
|
||||||
|
domain = domain.lower()
|
||||||
|
dns = request.args.get("dns")
|
||||||
|
raw_dns = dns
|
||||||
|
dns = urllib.parse.unquote(dns)
|
||||||
|
response = account_module.setDNS(request.cookies.get("account"),domain,dns)
|
||||||
|
if 'error' in response:
|
||||||
|
print(response)
|
||||||
|
return redirect("/manage/" + domain + "/edit?dns="+raw_dns+"&error=" + str(response['error']))
|
||||||
|
return redirect("/success?tx=" + response['hash'])
|
||||||
|
|
||||||
@app.route('/auction/<domain>')
|
@app.route('/auction/<domain>')
|
||||||
def auction(domain):
|
def auction(domain):
|
||||||
|
13
render.py
13
render.py
@ -1,6 +1,6 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
|
import urllib.parse
|
||||||
|
|
||||||
def domains(domains):
|
def domains(domains):
|
||||||
html = ''
|
html = ''
|
||||||
@ -71,9 +71,9 @@ def transactions(txs):
|
|||||||
return html
|
return html
|
||||||
|
|
||||||
|
|
||||||
def dns(data):
|
def dns(data, edit=False):
|
||||||
html_output = ""
|
html_output = ""
|
||||||
|
index = 0
|
||||||
for entry in data:
|
for entry in data:
|
||||||
html_output += f"<tr><td>{entry['type']}</td>\n"
|
html_output += f"<tr><td>{entry['type']}</td>\n"
|
||||||
|
|
||||||
@ -101,7 +101,14 @@ def dns(data):
|
|||||||
value += str(val) + " "
|
value += str(val) + " "
|
||||||
html_output += f"<td>{value}</td>\n"
|
html_output += f"<td>{value}</td>\n"
|
||||||
|
|
||||||
|
if edit:
|
||||||
|
# Remove the current entry from the list
|
||||||
|
keptRecords = str(data[:index] + data[index+1:]).replace("'", '"')
|
||||||
|
keptRecords = urllib.parse.quote(keptRecords)
|
||||||
|
html_output += f"<td><a href='edit?dns={keptRecords}'>Remove</a></td>\n"
|
||||||
|
|
||||||
html_output += " </tr>\n"
|
html_output += " </tr>\n"
|
||||||
|
index += 1
|
||||||
return html_output
|
return html_output
|
||||||
|
|
||||||
def txs(data):
|
def txs(data):
|
||||||
|
132
templates/edit.html
Normal file
132
templates/edit.html
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html data-bs-theme="light" lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||||
|
<title>Manage - 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="stylesheet" href="/assets/bootstrap/css/bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i&display=swap">
|
||||||
|
<link rel="stylesheet" href="/assets/fonts/fontawesome-all.min.css">
|
||||||
|
<link rel="stylesheet" href="/assets/fonts/material-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="/assets/css/styles.min.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body id="page-top">
|
||||||
|
<div id="wrapper">
|
||||||
|
<nav class="navbar align-items-start sidebar sidebar-dark accordion bg-gradient-primary p-0 navbar-dark">
|
||||||
|
<div class="container-fluid d-flex flex-column p-0"><a class="navbar-brand d-flex justify-content-center align-items-center sidebar-brand m-0" href="/">
|
||||||
|
<div class="sidebar-brand-icon"><img src="/assets/img/favicon.png" width="44"></div>
|
||||||
|
<div class="sidebar-brand-text mx-3"><span>FireWallet</span></div>
|
||||||
|
</a>
|
||||||
|
<hr class="sidebar-divider my-0">
|
||||||
|
<ul class="navbar-nav text-light" id="accordionSidebar">
|
||||||
|
<li class="nav-item"><a class="nav-link" href="/"><i class="fas fa-tachometer-alt"></i><span>Dashboard</span></a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="/tx"><i class="fas fa-table"></i><span>Transactions</span></a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="/send"><i class="material-icons">send</i><span>Send HNS</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>
|
||||||
|
</ul>
|
||||||
|
<div class="text-center d-none d-md-inline"><button class="btn rounded-circle border-0" id="sidebarToggle" type="button"></button></div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="d-flex flex-column" id="content-wrapper">
|
||||||
|
<div id="content">
|
||||||
|
<nav class="navbar navbar-expand bg-white shadow mb-4 topbar static-top navbar-light">
|
||||||
|
<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">
|
||||||
|
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain" name="q" value="{{search_term}}"><button class="btn btn-primary py-0" type="submit"><i class="fas fa-search"></i></button></div>
|
||||||
|
</form><span>Sync: {{sync}}%</span>
|
||||||
|
<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>
|
||||||
|
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
||||||
|
<form class="me-auto navbar-search w-100">
|
||||||
|
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for ...">
|
||||||
|
<div class="input-group-append"><button class="btn btn-primary py-0" type="button"><i class="fas fa-search"></i></button></div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item dropdown no-arrow">
|
||||||
|
<div class="nav-item dropdown no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><span class="d-none d-lg-inline me-2 text-gray-600 small">{{account}}</span><img class="border rounded-circle img-profile" src="/assets/img/HNS.png"></a>
|
||||||
|
<div class="dropdown-menu shadow dropdown-menu-end animated--grow-in"><a class="dropdown-item" href="/logout"><i class="fas fa-sign-out-alt fa-sm fa-fw me-2 text-gray-400"></i> Logout</a></div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<h4 class="text-center" style="color: rgb(255,0,0);">{{error}}</h4>
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h4 class="card-title">{{domain}}/</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="container-fluid" style="margin-top: 50px;">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h4 class="card-title" style="display: inline-block;">DNS</h4><div class="table-responsive">
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<th></th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{dns | safe}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<form style="width: 100%;">
|
||||||
|
<div>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Value</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><select class="form-select" name="type">
|
||||||
|
<option value="TXT" selected="">TXT</option>
|
||||||
|
<option value="NS">NS</option>
|
||||||
|
<option value="DS">DS</option>
|
||||||
|
<option value="GLUE4">GLUE4</option>
|
||||||
|
<option value="GLUE6">GLUE6</option>
|
||||||
|
<option value="SYNTH4">SYNTH4</option>
|
||||||
|
<option value="SYNTH6">SYNTH6</option>
|
||||||
|
</select></td>
|
||||||
|
<td><input class="form-control" type="text" name="value"></td>
|
||||||
|
<td><input class="btn btn-primary" type="submit" value="Add"></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div><input class="form-control" type="hidden" name="dns" value="{{raw_dns}}">
|
||||||
|
</form><a class="btn btn-primary" role="button" href="edit/save?dns={{raw_dns}}">Save DNS</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<footer class="bg-white sticky-footer">
|
||||||
|
<div class="container my-auto">
|
||||||
|
<div class="text-center my-auto copyright"><span>Copyright © FireWallet 2023</span></div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div><a class="border rounded d-inline scroll-to-top" href="#page-top"><i class="fas fa-angle-up"></i></a>
|
||||||
|
</div>
|
||||||
|
<script src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||||
|
<script src="/assets/js/script.min.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -71,7 +71,7 @@
|
|||||||
<div class="container-fluid" style="margin-top: 50px;">
|
<div class="container-fluid" style="margin-top: 50px;">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title" style="display: inline-block;">DNS</h4><a class="btn btn-primary" role="button" style="position: absolute; right:16px;" href="/manage/{{domain}}/edit">Edit</a><div class="table-responsive">
|
<h4 class="card-title" style="display: inline-block;">DNS</h4><a class="btn btn-primary" role="button" style="position: absolute; right:16px;" href="/manage/{{domain}}/edit?dns={{raw_dns}}">Edit</a><div class="table-responsive">
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
Loading…
Reference in New Issue
Block a user