trying out htmx for more pieces

htmx
lza_menace 1 year ago
parent 1622c26d75
commit 62b593b6b2

@ -0,0 +1 @@
from lws.factory import create_app

@ -30,7 +30,11 @@ def create_app():
@app.errorhandler(Unauthorized) @app.errorhandler(Unauthorized)
async def redirect_to_login(*_): async def redirect_to_login(*_):
if request.path == "/":
return redirect(f"/login?next={request.path}") return redirect(f"/login?next={request.path}")
else:
return f"<p>you need to authenticate first</p><a href=\"/login\">login</a>"
return app return app
bcrypt = Bcrypt(create_app()) bcrypt = Bcrypt(create_app())

@ -1,6 +1,8 @@
from monero.numbers import from_atomic from monero.numbers import from_atomic
from quart import Blueprint from quart import Blueprint
from lws.models import Wallet, get_random_words
bp = Blueprint('filters', 'filters') bp = Blueprint('filters', 'filters')
@ -13,3 +15,11 @@ def atomic(amt):
@bp.app_template_filter('shorten') @bp.app_template_filter('shorten')
def shorten(s): def shorten(s):
return f"{s[:6]}...{s[-6:]}" return f"{s[:6]}...{s[-6:]}"
@bp.app_template_filter('find_label')
def find_label(s):
w = Wallet.select().where(Wallet.address == s).first()
if w:
return w.label
else:
return get_random_words()

@ -126,9 +126,8 @@ class LWS:
print(f"Failed to add wallet {address}: {e}") print(f"Failed to add wallet {address}: {e}")
return {} return {}
def modify_wallet(self, address: str, active: bool) -> dict: def modify_wallet(self, address: str, status: str) -> dict:
endpoint = f"{config.LWS_ADMIN_URL}/modify_account_status" endpoint = f"{config.LWS_ADMIN_URL}/modify_account_status"
status = "active" if active else "inactive"
data = { data = {
"auth": self.admin_key, "auth": self.admin_key,
"params": { "params": {

@ -1,9 +1,16 @@
from random import choice
from datetime import datetime from datetime import datetime
from peewee import * from peewee import *
from monero.wordlists import English
db = SqliteDatabase('data/lws.db') db = SqliteDatabase("data/lws.db")
def get_random_words():
e = English().word_list
return f"{choice(e)}-{choice(e)}-{choice(e)}"
class User(Model): class User(Model):
@ -17,4 +24,14 @@ class User(Model):
database = db database = db
db.create_tables([User]) class Wallet(Model):
date = DateTimeField(default=datetime.utcnow)
address = CharField()
view_key = CharField()
label = CharField(default=get_random_words, null=False)
class Meta:
database = db
db.create_tables([User, Wallet])

@ -1,4 +1,4 @@
from quart import Blueprint, render_template from quart import Blueprint, render_template, request
from monero.seed import Seed from monero.seed import Seed
from quart_auth import login_required from quart_auth import login_required
@ -10,7 +10,6 @@ bp = Blueprint('htmx', 'htmx', url_prefix="/htmx")
@bp.route("/create_wallet") @bp.route("/create_wallet")
@login_required
async def create_wallet(): async def create_wallet():
seed = Seed() seed = Seed()
return await render_template( return await render_template(
@ -24,10 +23,19 @@ async def create_wallet():
) )
@bp.route("/import_wallet") @bp.route("/import_wallet")
@login_required
async def import_wallet(): async def import_wallet():
return await render_template("htmx/import_wallet.html") return await render_template("htmx/import_wallet.html")
@bp.route("/label_wallet")
async def label_wallet():
address = request.args.get("address")
label = request.args.get("label")
return await render_template(
"htmx/label_wallet.html",
address=address,
label=label
)
@bp.route("/show_wallets") @bp.route("/show_wallets")
@login_required @login_required
async def show_wallets(): async def show_wallets():

@ -14,15 +14,9 @@ bp = Blueprint("meta", "meta")
async def index(): async def index():
admin = User.select().first() admin = User.select().first()
lws.init(admin.view_key) lws.init(admin.view_key)
accounts = lws.list_accounts()
if 'hidden' in accounts:
del accounts["hidden"]
requests = lws.list_requests()
return await render_template( return await render_template(
"index.html", "index.html",
config=config, config=config
accounts=accounts,
requests=requests
) )

@ -1,11 +1,12 @@
import monero.address import monero.address
from quart import Blueprint, render_template, request, flash, redirect from quart import Blueprint, render_template, request, flash, redirect, url_for
from quart_auth import login_required, current_user from quart_auth import login_required, current_user
from lws.helpers import lws from lws.helpers import lws
from lws.models import Wallet, get_random_words
bp = Blueprint('wallet', 'wallet') bp = Blueprint("wallet", "wallet")
@bp.route("/wallet/add", methods=["GET", "POST"]) @bp.route("/wallet/add", methods=["GET", "POST"])
@ -13,6 +14,7 @@ bp = Blueprint('wallet', 'wallet')
async def add(): async def add():
form = await request.form form = await request.form
if form: if form:
label = form.get("label")
address = form.get("address", "") address = form.get("address", "")
view_key = form.get("view_key", "") view_key = form.get("view_key", "")
restore_height = form.get("restore_height", None) restore_height = form.get("restore_height", None)
@ -35,16 +37,23 @@ async def add():
lws.add_wallet(address, view_key) lws.add_wallet(address, view_key)
if restore_height != "-1": if restore_height != "-1":
lws.rescan(address, int(restore_height)) lws.rescan(address, int(restore_height))
w = Wallet(
address=address,
view_key=view_key,
label=label if label else get_random_words()
)
w.save()
await flash("wallet added") await flash("wallet added")
return redirect(f"/") return redirect(f"/")
return await render_template("wallet/add.html") else:
return ""
@bp.route("/wallet/rescan") @bp.route("/wallet/rescan")
@login_required @login_required
async def rescan(): async def rescan():
address = request.args.get('address') address = request.args.get("address")
height = request.args.get('height') height = request.args.get("height")
if not address or not height: if not address or not height:
await flash("you need to provide both address and height") await flash("you need to provide both address and height")
return redirect("/") return redirect("/")
@ -55,33 +64,35 @@ async def rescan():
return redirect(f"/") return redirect(f"/")
@bp.route("/wallet/<address>/disable") @bp.route("/wallet/<address>/<status>")
@login_required @login_required
async def disable(address): async def modify(address, status):
lws.modify_wallet(address, False) lws.modify_wallet(address, status)
await flash(f"{address} disabled in LWS") await flash(f"{address} {status} in LWS")
return redirect(f"/") return redirect(url_for("htmx.show_wallets"))
@bp.route("/wallet/<address>/enable")
@login_required
async def enable(address):
lws.modify_wallet(address, True)
await flash(f"{address} enabled in LWS")
return redirect(f"/")
@bp.route("/wallet/<address>/accept") @bp.route("/wallet/<address>/approve")
@login_required @login_required
async def accept(address): async def accept(address):
lws.accept_request(address) lws.accept_request(address)
await flash(f"{address} accepted") await flash(f"{address} accepted")
return redirect(f"/") return redirect(url_for("htmx.show_wallets"))
@bp.route("/wallet/<address>/reject") @bp.route("/wallet/<address>/deny")
@login_required @login_required
async def reject(address): async def reject(address):
lws.reject_request(address) lws.reject_request(address)
await flash(f"{address} rejected") await flash(f"{address} denied")
return redirect(f"/") return redirect(url_for("htmx.show_wallets"))
@bp.route("/wallet/<address>/label/<label>")
@login_required
async def label(address, label):
w = Wallet.select().where(Wallet.address == address).first()
if w and label:
w.label = label
w.save()
return redirect(url_for("htmx.show_wallets"))

@ -0,0 +1,13 @@
<form action="#" onsubmit="updateLabel(event)">
<input type="text" name="label" onkeyup="updateLabel(event)" value="{{ label }}" id="newLabel">
</form>
<script>
function updateLabel(e){
e.preventDefault();
if (e.keyCode === 13) {
htmx.ajax('GET', `/wallet/{{ address }}/label/${e.target.value}`, '#show_wallets');
} else if (e.keyCode === 27) {
htmx.ajax('GET', '{{ url_for("htmx.show_wallets") }}', '#show_wallets');
}
}
</script>

@ -1,6 +1,7 @@
<table class="striped"> <table class="striped">
<thead> <thead>
<tr> <tr>
<th>Label</th>
<th>Status</th> <th>Status</th>
<th>Action</th> <th>Action</th>
<th>Address</th> <th>Address</th>
@ -8,51 +9,57 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for status in requests %}
{% for request in requests[status] %}
<tr>
<td>?</td>
<td>
<span class="tag text-grey">
PENDING
</span>
</td>
<td>
<!-- <a hx-get="/htmx/import_wallet" hx-target="#walletForm" class="button primary outline"></a> -->
<a href="/wallet/{{ request['address'] }}/approve" class="button primary outline">Approve</a>
<a href="/wallet/{{ request['address'] }}/deny" class="button secondary outline">Deny</a>
</td>
<td>{{ request['address'] | shorten }}</td>
<td>{{ request['start_height'] }}</td>
</tr>
{% endfor %}
{% endfor %}
{% for status in accounts %} {% for status in accounts %}
{% for account in accounts[status] %} {% for account in accounts[status] %}
<tr> <tr>
<td> <td>
<span class="tag text-white {% if status == 'active' %}bg-success{% else %}bg-error{% endif %}"> <div hx-get="/htmx/label_wallet" hx-target="this" hx-swap="outerHTML" hx-vals='{"address": "{{ account['address'] }}", "label": "{{ account['address'] | find_label }}"}'>{{ account['address'] | find_label }}</div>
</td>
<td>
<span class="tag {% if status == 'active' %}text-primary{% else %}text-error{% endif %}">
{{ status | upper }} {{ status | upper }}
</span> </span>
</td> </td>
<td> <td>
{% if status == 'active' %} {% if status == 'active' %}
<a href="/wallet/{{ account['address'] }}/disable" class="">disable</a> <!-- <a href="/wallet/{{ account['address'] }}/inactive" class="button secondary outline">Disable</a> -->
<button class="button primary outline" tx-target="#show_wallets" hx-get="/wallet/{{ account['address'] }}/inactive">Disable</button>
{% else %} {% else %}
<a href="/wallet/{{ account['address'] }}/enable" class="">enable</a> <a href="/wallet/{{ account['address'] }}/active" class="button primary outline">Enable</a>
{% endif %} {% endif %}
</td> </td>
<td>{{ account['address'] | shorten }}</td> <td>{{ account['address'] | shorten }}</td>
<td> <td>
<form method="get" action="{{ url_for('wallet.rescan') }}">
{{ account['scan_height'] }} {{ account['scan_height'] }}
<!--
<form method="get" action="{{ url_for('wallet.rescan') }}">
<input type="integer" name="height" style="width: 5em; display: inline;" /> <input type="integer" name="height" style="width: 5em; display: inline;" />
<input type="hidden" name="address" value="{{ account['address'] }}" /> <input type="hidden" name="address" value="{{ account['address'] }}" />
<button type="submit">Rescan</button> <button type="submit">Rescan</button>
</form> </form>
-->
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
{% 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> </tbody>
</table> </table>

@ -5,21 +5,18 @@
<h1>Monero Lightwallet Server</h1> <h1>Monero Lightwallet Server</h1>
<p>LWS Admin: {{ config.LWS_ADMIN_URL }}</p> <p>LWS Admin: {{ config.LWS_ADMIN_URL }}</p>
<p>LWS RPC: {{ config.LWS_URL }}</p> <p>LWS RPC: {{ config.LWS_URL }}</p>
<h3>Accounts</h3>
<div> <div>
<a hx-get="/htmx/import_wallet" hx-target="#walletEvent" class="button primary outline"> <a hx-get="/htmx/import_wallet" hx-target="#walletForm" class="button primary outline">Import Wallet</a>
Import Wallet <a hx-get="/htmx/create_wallet" hx-target="#walletForm" class="button primary">Create Wallet</a>
</a> <p id="walletForm" style="margin: 2em 0 2em 0"></p>
<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> </div>
<div hx-trigger="every 15s" hx-get="/htmx/show_wallets" hx-target="#show_wallets"></div> <div>
<h3>Accounts</h3>
<a hx-get="/htmx/show_wallets" hx-target="#show_wallets" class="button outline" hx-indicator="#refreshLoader">Refresh</a>
<p class="indicator" id="refreshLoader">...</p>
<div hx-trigger="load" hx-get="/htmx/show_wallets" id="show_wallets"></div> <div hx-trigger="load" hx-get="/htmx/show_wallets" id="show_wallets"></div>
</div>
{% endblock %} {% endblock %}

@ -21,9 +21,9 @@
<a href="{{ url_for('wallet.rescan', id=wallet.id) }}" class="button dark outline">rescan</a> <a href="{{ url_for('wallet.rescan', id=wallet.id) }}" class="button dark outline">rescan</a>
{% if wallet.is_active() %} {% if wallet.is_active() %}
<a href="{{ url_for('wallet.disable', id=wallet.id) }}" class="button error">disable</a> <a href="{{ url_for('wallet.modify', id=wallet.id, status) }}" class="button error">disable</a>
{% else %} {% else %}
<a href="{{ url_for('wallet.enable', id=wallet.id) }}" class="button primary">enable</a> <a href="{{ url_for('wallet.modify', id=wallet.id, status) }}" class="button primary">enable</a>
{% endif %} {% endif %}
{% else %} {% else %}
<p>not connected to lws</p> <p>not connected to lws</p>

@ -22,4 +22,4 @@ requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api" build-backend = "poetry.core.masonry.api"
[tool.poetry.scripts] [tool.poetry.scripts]
start = "lws.app:app.run" start = "lws:create_app().run()"

Loading…
Cancel
Save