implementing htmx

htmx
lza_menace 1 year ago
parent 6f6b971b9d
commit 1622c26d75

@ -7,8 +7,6 @@ x-log-config: &log-config
max-file: "5"
volumes:
lws:
lwsadmin:
mymonero:
services:
monero-lws:
container_name: monero-lws

@ -18,4 +18,5 @@ QUART_AUTH_DURATION = int(env.get('QUART_AUTH_DURATION', 60 * 60)) # 1 hour
LWS_URL = env.get("LWS_URL", "http://127.0.0.1:8080")
LWS_ADMIN_URL = env.get("LWS_ADMIN_URL", "http://127.0.0.1:8081")
#
# Monerod
MONEROD_URL = env.get("MONEROD_URL", "http://singapore.node.xmr.pm:18089")

@ -20,12 +20,13 @@ def create_app():
@app.before_serving
async def startup():
from lws.routes import auth, wallet, meta
from lws.routes import auth, wallet, meta, htmx
from lws import filters
app.register_blueprint(filters.bp)
app.register_blueprint(auth.bp)
app.register_blueprint(meta.bp)
app.register_blueprint(wallet.bp)
app.register_blueprint(htmx.bp)
@app.errorhandler(Unauthorized)
async def redirect_to_login(*_):

@ -18,6 +18,14 @@ from lws import config
# webhook_delete_uuid: {"event_ids": [...]}
# webhook_list: {}
def get_height() -> int:
try:
r = requests.get(f"{config.MONEROD_URL}/get_info")
print(r.content)
return int(r.json()["height"])
except Exception as e:
print(e)
return 0
class LWS:
def __init__(self):
@ -26,6 +34,16 @@ class LWS:
def init(self, admin_key):
self.admin_key = admin_key
def get_address_info(self, address, view_key):
endpoint = f"{config.LWS_URL}/get_address_info"
data = {
"address": address,
"view_key": view_key
}
r = requests.post(endpoint, json=data, timeout=5)
r.raise_for_status()
return r.json()
def get_wallet(self, address: str) -> dict:
try:
res = self.list_accounts()

@ -0,0 +1,44 @@
from quart import Blueprint, render_template
from monero.seed import Seed
from quart_auth import login_required
from lws.models import User
from lws.helpers import lws
from lws import config
bp = Blueprint('htmx', 'htmx', url_prefix="/htmx")
@bp.route("/create_wallet")
@login_required
async def create_wallet():
seed = Seed()
return await render_template(
"htmx/create_wallet.html",
seed=seed.phrase,
address=seed.public_address(),
psk=seed.public_spend_key(),
pvk=seed.public_view_key(),
ssk=seed.secret_spend_key(),
svk=seed.secret_view_key()
)
@bp.route("/import_wallet")
@login_required
async def import_wallet():
return await render_template("htmx/import_wallet.html")
@bp.route("/show_wallets")
@login_required
async def show_wallets():
admin = User.select().first()
lws.init(admin.view_key)
accounts = lws.list_accounts()
if 'hidden' in accounts:
del accounts["hidden"]
requests = lws.list_requests()
return await render_template(
"htmx/show_wallets.html",
accounts=accounts,
requests=requests
)

@ -15,7 +15,7 @@ async def add():
if form:
address = form.get("address", "")
view_key = form.get("view_key", "")
restore_height = form.get("restore_height", 0)
restore_height = form.get("restore_height", None)
valid_view_key = False
if not address:
await flash("must provide an address")
@ -33,7 +33,8 @@ async def add():
await flash("Invalid view key provided for address")
return redirect("/wallet/add")
lws.add_wallet(address, view_key)
lws.rescan(address, int(restore_height))
if restore_height != "-1":
lws.rescan(address, int(restore_height))
await flash("wallet added")
return redirect(f"/")
return await render_template("wallet/add.html")

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -24,3 +24,7 @@ input {
padding-bottom: .5em;
margin-bottom: 1em;
}
.hidden {
display: none !important;
}

@ -0,0 +1,15 @@
<div id="create_wallet">
<p>Seed: <span class="key">{{ seed }}</span></p>
<p>Public Address: <span class="key">{{ address }}</span></p>
<p>Public Spend Key: <span class="key">{{ psk }}</span></p>
<p>Public View Key: <span class="key">{{ pvk }}</span></p>
<p>Secret Spend Key: <span class="key">{{ ssk }}</span></p>
<p>Secret View Key: <span class="key">{{ svk }}</span></p>
<form method="post" action="{{ url_for('wallet.add') }}" hx-confirm="Please confirm">
<input type="text" name="address" value="{{ address }}" class="hidden" />
<input type="text" name="view_key" value="{{ svk }}" class="hidden" />
<input type="number" name="restore_height" value="-1" class="hidden" />
<button onclick="document.getElementById('create_wallet').innerHTML = ''">Cancel</button>
<button type="submit" >Create</button>
</form>
</div>

@ -0,0 +1,10 @@
<form hx-post="{{ url_for('wallet.add') }}" id="import_wallet">
<label for="address">Address</label>
<input type="text" name="address" value="{{ request.args.get('address', '') }}" />
<label for="view_key">Secret View Key</label>
<input type="text" name="view_key" />
<label for="restore_height">Restore Height</label>
<input type="number" name="restore_height" />
<button onclick="document.getElementById('import_wallet').innerHTML = ''">Cancel</button>
<button type="submit">Import</button>
</form>

@ -0,0 +1,58 @@
<table class="striped">
<thead>
<tr>
<th>Status</th>
<th>Action</th>
<th>Address</th>
<th>Height</th>
</tr>
</thead>
<tbody>
{% for status in accounts %}
{% for account in accounts[status] %}
<tr>
<td>
<span class="tag text-white {% if status == 'active' %}bg-success{% else %}bg-error{% endif %}">
{{ status | upper }}
</span>
</td>
<td>
{% if status == 'active' %}
<a href="/wallet/{{ account['address'] }}/disable" class="">disable</a>
{% else %}
<a href="/wallet/{{ account['address'] }}/enable" class="">enable</a>
{% endif %}
</td>
<td>{{ account['address'] | shorten }}</td>
<td>
<form method="get" action="{{ url_for('wallet.rescan') }}">
{{ account['scan_height'] }}
<input type="integer" name="height" style="width: 5em; display: inline;" />
<input type="hidden" name="address" value="{{ account['address'] }}" />
<button type="submit">Rescan</button>
</form>
</td>
</tr>
{% endfor %}
{% endfor %}
{% for status in requests %}
{% for request in requests[status] %}
<tr>
<td>
<span class="tag text-white bg-dark">
PENDING
</span>
</td>
<td>
<a href="/wallet/{{ request['address'] }}/accept" class="">approve</a> |
<a href="/wallet/{{ request['address'] }}/reject" class="">reject</a>
</td>
<td>{{ request['address'] | shorten }}</td>
<td>{{ request['start_height'] }}</td>
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>

@ -18,12 +18,9 @@
<meta name="twitter:description" content="Monero LWS web app">
<meta name="twitter:image" content="">
<meta name="keywords" content="Wownero, Monero, crypto, wallet, explorer">
<link rel="stylesheet" href="https://unpkg.com/chota@latest">
<link rel="stylesheet" href="/static/chota.css">
<link rel="stylesheet" href="/static/main.css">
<!--
<link rel="stylesheet" href="/static/css/skeleton.css">
<link rel="stylesheet" href="/static/css/main.css">
-->
<script src="/static/htmx.js"></script>
</head>
<body>

@ -1,68 +1,25 @@
{% extends 'includes/base.html' %}
{% block content %}
<h1>LWS Web Admin</h1>
<h1>Monero Lightwallet Server</h1>
<p>LWS Admin: {{ config.LWS_ADMIN_URL }}</p>
<p>LWS RPC: {{ config.LWS_URL }}</p>
<h3>Accounts</h3>
<a href="/wallet/add" class="button outline primary">Add Wallet</a>
<table class="striped">
<thead>
<tr>
<th>Status</th>
<th>Action</th>
<th>Address</th>
<th>Height</th>
</tr>
</thead>
<tbody>
{% for status in accounts %}
{% for account in accounts[status] %}
<tr>
<td>
<span class="tag text-white {% if status == 'active' %}bg-success{% else %}bg-error{% endif %}">
{{ status | upper }}
</span>
</td>
<td>
{% if status == 'active' %}
<a href="/wallet/{{ account['address'] }}/disable" class="">disable</a>
{% else %}
<a href="/wallet/{{ account['address'] }}/enable" class="">enable</a>
{% endif %}
</td>
<td>{{ account['address'] | shorten }}</td>
<td>
<div>
<a hx-get="/htmx/import_wallet" hx-target="#walletEvent" class="button primary outline">
Import Wallet
</a>
<a hx-get="/htmx/create_wallet" hx-target="#walletEvent" class="button primary">
Create Wallet
</a>
</div>
<div class="" style="margin-top: 2em">
<p id="walletEvent"></p>
</div>
<form method="get" action="{{ url_for('wallet.rescan') }}">
{{ account['scan_height'] }}
<input type="integer" name="height" style="width: 5em; display: inline;" />
<input type="hidden" name="address" value="{{ account['address'] }}" />
<button type="submit">Rescan</button>
</form>
</td>
</tr>
{% endfor %}
{% endfor %}
<div hx-trigger="every 15s" hx-get="/htmx/show_wallets" hx-target="#show_wallets"></div>
<div hx-trigger="load" hx-get="/htmx/show_wallets" id="show_wallets"></div>
{% for status in requests %}
{% for request in requests[status] %}
<tr>
<td>
<span class="tag text-white bg-dark">
PENDING
</span>
</td>
<td>
<a href="/wallet/{{ request['address'] }}/accept" class="">approve</a> |
<a href="/wallet/{{ request['address'] }}/reject" class="">reject</a>
</td>
<td>{{ request['address'] | shorten }}</td>
<td>{{ request['start_height'] }}</td>
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>
{% endblock %}

@ -0,0 +1,15 @@
{% extends 'includes/base.html' %}
{% block content %}
<a href="/">Go Back</a>
<h1>Add a Wallet</h1>
<form method="post">
<label for="address">Address</label>
<input type="text" name="address" value="{{ request.args.get('address', '') }}" />
<label for="view_key">View Key</label>
<input type="text" name="view_key" />
<label for="restore_height">Restore Height</label>
<input type="number" name="restore_height" />
<button type="submit">Send</button>
</form>
{% endblock %}
Loading…
Cancel
Save