improve ux

master
lza_menace 1 year ago
parent 04c74bb243
commit 65e7e5c4e3

@ -13,6 +13,10 @@ Will be adding client side application to tie the whole thing together.
* https://github.com/vtnerd/monero-lws/blob/feature/no_auth_admin/docs/administration.md
* https://github.com/monero-project/meta/blob/master/api/lightwallet_rest.md
* https://github.com/CryptoGrampy/monero-lws-admin
* https://www.npmjs.com/package/@mymonero/mymonero-wallet-manager/v/3.0.0
* https://github.com/mymonero/mymonero-utils/tree/master/packages/mymonero-lws-client
* https://github.com/mymonero/mymonero-utils/tree/master/packages/mymonero-monero-client
* https://github.com/mymonero/mymonero-utils/tree/master/packages/mymonero-wallet-manager
## Notes

@ -1,4 +1,4 @@
from quart import Quart, redirect
from quart import Quart, redirect, request
from quart_auth import (
AuthManager, Unauthorized
)
@ -33,7 +33,7 @@ def create_app():
@app.errorhandler(Unauthorized)
async def redirect_to_login(*_):
return redirect("/login")
return redirect(f"/login?next={request.path}")
return app
bcrypt = Bcrypt(create_app())

@ -81,6 +81,26 @@ class Wallet(Model):
print(f"Failed to add wallet {self.address}: {e}")
return False
def disable_wallet_lws(self):
endpoint = f"{config.LWS_ADMIN_URL}/modify_account_status"
data = {
"auth": self.user.view_key,
"params": {
"addresses": [self.address],
"status": "inactive"
}
}
try:
req = requests.post(endpoint, json=data, timeout=5)
req.raise_for_status()
if req.ok:
return True
return False
except Exception as e:
print(f"Failed to add wallet {self.address}: {e}")
return False
def get_wallet_info(self):
endpoint = f"{config.LWS_URL}/get_address_info"
data = {
@ -97,6 +117,22 @@ class Wallet(Model):
print(f"Failed to get wallet info {self.address}: {e}")
return False
def get_wallet_txes(self):
endpoint = f"{config.LWS_URL}/get_address_txs"
data = {
"address": self.address,
"view_key": self.view_key
}
try:
req = requests.post(endpoint, json=data, timeout=5)
req.raise_for_status()
if req.ok:
return req.json()
return {}
except Exception as e:
print(f"Failed to get wallet info {self.address}: {e}")
return False
def rescan(self):
endpoint = f"{config.LWS_ADMIN_URL}/rescan"
data = {

@ -33,6 +33,9 @@ async def login():
await flash("invalid password")
return redirect("/login")
login_user(AuthUser(user.id))
nxt = request.args.get("next")
if nxt:
return redirect(nxt)
return redirect("/")
return await render_template("login.html")

@ -2,7 +2,7 @@ import monero.address
from quart import Blueprint, render_template, request, flash, redirect
from quart_auth import login_required, current_user
from lws.models import Wallet
from lws.models import Wallet, User
bp = Blueprint('wallet', 'wallet')
@ -26,10 +26,10 @@ async def add():
restore_height = form.get("restore_height", 0)
valid_view_key = False
if not address:
await flash("must provide an LWS admin address")
await flash("must provide an address")
return redirect("/wallet/add")
if not view_key:
await flash("must provide an LWS admin view_key")
await flash("must provide a view_key")
return redirect("/wallet/add")
try:
_a = monero.address.Address(address)
@ -52,7 +52,7 @@ async def add():
wallet.name = f"wallet-{id}"
wallet.add_wallet_lws()
await flash("wallet added")
return redirect(f"/wallet/{wallet.id}")
return redirect(f"/wallet/{wallet.id}/rescan")
return await render_template("wallet/add.html")
@ -62,7 +62,7 @@ async def show(id):
wallet = Wallet.select().where(Wallet.id == id).first()
if not wallet:
await flash("wallet does not exist")
return redirect("/")
return redirect("/wallets")
return await render_template(
"wallet/show.html",
wallet=wallet
@ -74,21 +74,18 @@ async def rescan(id):
wallet = Wallet.select().where(Wallet.id == id).first()
if not wallet:
await flash("wallet does not exist")
return redirect("/")
return redirect("/wallets")
wallet.rescan()
return redirect(f"/wallet/{id}")
# / - redirect to /setup if user not setup, to /login if not authenticated
# /setup - first time setup user account, encrypted session
# /login - log into encrypted session
# /wallet/add - add a wallet to LWS
# /wallet/:id - show wallet details (balances, txes, etc)
# /wallet/:id/remove - remove a wallet from LWS
# /wallet/:id/resync - resync wallet
# get_address_info
# get_address_txs
# get_random_outs
# get_unspent_outs
# import_request
# submit_raw_tx
@bp.route("/wallet/<id>/disable")
@login_required
async def disable(id):
wallet = Wallet.select().where(Wallet.id == id).first()
if not wallet:
await flash("wallet does not exist")
return redirect("/wallets")
wallet.disable_wallet_lws()
return redirect(f"/wallet/{id}")

@ -2,4 +2,25 @@
color: #666;
font-size: .9em;
user-select: all;
}
.smol {
font-size: .8em;
color: #666;
}
.content {
padding-top: 2em;
}
.subtext {
font-size: 1em;
display: block;
color: #666;
}
input {
display: block;
padding-bottom: .5em;
margin-bottom: 1em;
}

@ -1,12 +1,11 @@
{% extends 'includes/base.html' %}
{% block content %}
<div id="dashboard">
<h1>LWS Web Admin</h1>
<p>LWS Admin: {{ config.LWS_ADMIN_URL }}</p>
<p>LWS RPC: {{ config.LWS_URL }}</p>
<p>{{ wallets.count() }} wallet{% if wallets.count() > 1 %}s{% endif %} being tracked</p>
<a href="{{ url_for('wallet.list') }}" class="button outline primary">Manage Wallets</a>
</div>
<h1>LWS Web Admin</h1>
<a href="{{ url_for('wallet.list') }}" class="button outline primary">Manage Wallets</a>
<hr>
<p>LWS Admin: {{ config.LWS_ADMIN_URL }}</p>
<p>LWS RPC: {{ config.LWS_URL }}</p>
<p>{{ wallets.count() }} wallet{% if wallets.count() > 1 %}s{% endif %} being scanned by LWS</p>
{% endblock %}

@ -1,21 +1,12 @@
{% extends 'includes/base.html' %}
{% block content %}
<div id="login">
<form method="post">
<label for="username">Username</label>
<input type="text" name="username" />
<label for="password">Password</label>
<input type="password" name="password" />
<button type="submit">Send</button>
</form>
</div>
<style>
input {
display: block;
padding-bottom: .5em;
margin-bottom: 1em;
}
</style>
<h1>Login</h1>
<form method="post">
<label for="username">Username</label>
<input type="text" name="username" />
<label for="password">Password</label>
<input type="password" name="password" />
<button type="submit">Send</button>
</form>
{% endblock %}

@ -1,32 +1,17 @@
{% extends 'includes/base.html' %}
{% block content %}
<div id="setup">
<h1>Setup your Monero light wallet</h1>
<p class="subtext">The LWS address and view key must be generated from <i>monero-lws</i> using the initialization scripts.</p>
<form method="post">
<label for="username">Username</label>
<input type="text" name="username" />
<label for="password">Password</label>
<input type="password" name="password" />
<label for="address">LWS Address</label>
<input type="text" name="address" />
<label for="view_key">LWS View Key</label>
<input type="text" name="view_key" />
<button type="submit">Submit</button>
</form>
</div>
<style>
input {
display: block;
padding-bottom: .5em;
margin-bottom: 1em;
}
.subtext {
font-size: 1em;
display: block;
color: #666;
}
</style>
<h1>Setup your Monero LWS</h1>
<p class="subtext">The LWS address and view key must be generated from <i>monero-lws</i> using the initialization scripts.</p>
<form method="post">
<label for="username">Username</label>
<input type="text" name="username" />
<label for="password">Password</label>
<input type="password" name="password" />
<label for="address">LWS Address</label>
<input type="text" name="address" />
<label for="view_key">LWS View Key</label>
<input type="text" name="view_key" />
<button type="submit">Submit</button>
</form>
{% endblock %}

@ -1,21 +1,13 @@
{% extends 'includes/base.html' %}
{% block content %}
<div>
<h1>Parse Mnemonic Seed</h1>
<form method="post">
<label for="seed">25 Word Seed</label>
<input type="password" name="seed" />
<button type="submit">Send</button>
</form>
</div>
<style>
input {
display: block;
padding-bottom: .5em;
margin-bottom: 1em;
}
</style>
<a href="/utils">Go Back</a>
<h1>Parse Mnemonic Seed</h1>
<form method="post">
<label for="seed">25 Word Seed</label>
<input type="password" name="seed" />
<button type="submit">Send</button>
</form>
{% if results %}
<h2>Results</h2>
<p>Public Address: <span class="key">{{ results.public_address() }}</span></p>

@ -1,28 +1,21 @@
{% extends 'includes/base.html' %}
{% block content %}
<div id="wallet_add">
<form method="post">
<label for="name">Name</label>
<input type="text" name="name" />
<label for="description">Description</label>
<input type="text" name="description" />
<label for="address">Address</label>
<input type="text" name="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>
</div>
<style>
input {
display: block;
padding-bottom: .5em;
margin-bottom: 1em;
}
</style>
<a href="/wallets">Go Back</a>
<h1>Add a Wallet</h1>
<form method="post">
<label for="name">Name</label>
<input type="text" name="name" />
<label for="description">Description</label>
<input type="text" name="description" />
<label for="address">Address</label>
<input type="text" name="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 %}

@ -3,14 +3,9 @@
{% block content %}
<h1>Manage Wallets</h1>
<a class="button outline primary" href="{{ url_for('wallet.add') }}">Add a Wallet</a>
<div id="wallets" class="content">
<div class="content">
{% for wallet in wallets %}
<p>{{ wallet.id }} - <a href="{{ url_for('wallet.show', id=wallet.id) }}">{{ wallet.name }}</a> - {{ wallet.description }} - {{ wallet.date }}</p>
{% endfor %}
</div>
<style>
.content {
padding-top: 2em;
}
</style>
{% endblock %}

@ -1,27 +1,21 @@
{% extends 'includes/base.html' %}
{% block content %}
<a href="/wallets">Go Back</a>
{% set info = wallet.get_wallet_info() %}
<h1>{{ wallet.name }}</h1>
<h5>{{ wallet.description }}</h5>
<h6>{{ wallet.date }}</h6>
<p>Restore Height: {{ wallet.restore_height }}</p>
<p>Address: {{ wallet.address }}</p>
<p>Locked: {{ info['locked_funds'] }}</p>
<p>Received: {{ info['total_received'] }}</p>
<p>Sent: {{ info['total_sent'] }}</p>
<p>Scanned Height: {{ info['scanned_height'] }} ({{ info['blockchain_height'] - info['scanned_height'] }} blocks away from top)</p>
<p>Address: <span class="key">{{ wallet.address }}</span></p>
<p>Scanned Height: {{ info['scanned_height'] }} <span class="smol">({{ info['blockchain_height'] - info['scanned_height'] }} blocks away from top)</span></p>
<p>Chain Height: {{ info['blockchain_height'] }}</p>
<p>Spent Outputs: {{ info['spent_outputs'] | length }}</p>
<!--
{
'amount': '123123123',
'key_image': 'asdasdasd',
'tx_pub_key': 'asdasdasd',
'out_index': 1,
'mixin': 15
}
-->
<a href="{{ url_for('wallet.rescan', id=wallet.id) }}"><button>rescan</button></a>
<a href="{{ url_for('wallet.rescan', id=wallet.id) }}" class="button dark outline">rescan</a>
{% if request.args.get('disable') %}
<a href="{{ url_for('wallet.disable', id=wallet.id) }}" class="button error">are you sure?</a>
{% else %}
<a href="?disable=true" class="button error">disable</a>
{% endif %}
{% endblock %}
Loading…
Cancel
Save