remove persistent wallet keys in db....only realtime api

master
lza_menace 12 months ago
parent fe06f6f74f
commit 9e8f3733d3

@ -7,13 +7,13 @@ x-log-config: &log-config
max-file: "5"
volumes:
lws:
lws-web:
lwsadmin:
mymonero:
services:
# lws-web:
# container_name: lws-web
# lwsadmin:
# container_name: lwsadmin
# build:
# context: lws-web
# context: lwsadmin
# dockerfile: Dockerfile
# restart: unless-stopped
# depends_on:
@ -27,30 +27,30 @@ services:
# ports:
# - 127.0.0.1:5000:5000
# volumes:
# - lws-web:/srv/lws-web
# - lwsadmin:/srv/lwsadmin
# user: "1000:1000"
# command:
# ./.venv/bin/poetry run start
# <<: *log-config
mymonero-web:
container_name: mymonero-web
build:
context: mymonero-web-js
dockerfile: Dockerfile
restart: unless-stopped
environment:
MYMONERO_WEB_NETTYPE: 0
MYMONERO_WEB_SERVER_URL: localhost:8080
MYMONERO_WEB_APP_NAME: MyMonero-SelfHosted
depends_on:
- monero-lws
expose:
- 80/tcp
ports:
- 127.0.0.1:8000:80
volumes:
- mymonero:/app
<<: *log-config
# mymonero-web:
# container_name: mymonero-web
# build:
# context: mymonero-web-js
# dockerfile: Dockerfile
# restart: unless-stopped
# environment:
# MYMONERO_WEB_NETTYPE: 0
# MYMONERO_WEB_SERVER_URL: localhost:8080
# MYMONERO_WEB_APP_NAME: MyMonero-SelfHosted
# depends_on:
# - monero-lws
# expose:
# - 80/tcp
# ports:
# - 127.0.0.1:8000:80
# volumes:
# - mymonero:/app
# <<: *log-config
monero-lws:
container_name: monero-lws
build:

@ -2,19 +2,19 @@ FROM ubuntu:22.04
RUN apt-get update
RUN apt-get install -y python3 python3-venv
WORKDIR /srv/lws-web
WORKDIR /srv/lwsadmin
RUN adduser \
--system \
--shell /bin/bash \
--gecos 'lws-web' \
--gecos 'lwsadmin' \
--group \
--disabled-password \
--home /srv/lws-web \
--home /srv/lwsadmin \
--uid 1000 \
lws-web
lwsadmin
COPY . .
RUN chown -R lws-web:lws-web .
USER lws-web
RUN chown -R lwsadmin:lwsadmin .
USER lwsadmin
RUN python3 -m venv .venv
RUN .venv/bin/pip install poetry
RUN .venv/bin/poetry install

@ -1,13 +1,53 @@
import requests
from lws.models import User
from lws import config
# accept_requests: {"type": "import"|"create", "addresses":[...]}
# add_account: {"address": ..., "key": ...}
# list_accounts: {}
# list_requests: {}
# modify_account_status: {"status": "active"|"hidden"|"inactive", "addresses":[...]}
# reject_requests: {"type": "import"|"create", "addresses":[...]}
# rescan: {"height":..., "addresses":[...]}
# webhook_add: {"type":"tx-confirmation", "address":"...", "url":"...", ...} with optional fields:
# token: A string to be returned when the webhook is triggered
# payment_id: 16 hex characters representing a unique identifier for a transaction
# webhook_delete: {"addresses":[...]}
# webhook_delete_uuid: {"event_ids": [...]}
# webhook_list: {}
class LWS:
def __init__(self, admin_key):
def __init__(self):
pass
def init(self, admin_key):
self.admin_key = admin_key
def list_accounts(self):
def get_wallet(self, address: str) -> dict:
try:
res = self.list_accounts()
for _status in res:
for _wallet in res[_status]:
if _wallet["address"] == address:
_wallet["status"] = _status
return _wallet
return {}
except Exception as e:
print(f"Failed to check wallet active: {e}")
return {}
def exists(self, address: str) -> bool:
try:
res = self.get_wallet(address)
return False if res == {} else True
except Exception as e:
print(f"Failed to check wallet active: {e}")
return False
def list_accounts(self) -> dict:
endpoint = f"{config.LWS_ADMIN_URL}/list_accounts"
data = {"auth": self.admin_key}
try:
@ -20,7 +60,20 @@ class LWS:
print(f"Failed to list accounts: {e}")
return {}
def get_address_txs(self, address, view_key):
def list_requests(self) -> dict:
endpoint = f"{config.LWS_ADMIN_URL}/list_requests"
data = {"auth": self.admin_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 list accounts: {e}")
return {}
def get_address_txs(self, address: str, view_key: str) -> dict:
endpoint = f"{config.LWS_URL}/get_address_txs"
data = {
"address": address,
@ -34,4 +87,103 @@ class LWS:
return {}
except Exception as e:
print(f"Failed to get wallet info {address}: {e}")
return False
return {}
def add_wallet(self, address: str, view_key: str) -> dict:
endpoint = f"{config.LWS_ADMIN_URL}/add_account"
data = {
"auth": self.admin_key,
"params": {
"address": address,
"key": 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 add wallet {address}: {e}")
return {}
def modify_wallet(self, address: str, active: bool) -> dict:
endpoint = f"{config.LWS_ADMIN_URL}/modify_account_status"
status = "active" if active else "inactive"
data = {
"auth": self.admin_key,
"params": {
"addresses": [address],
"status": status
}
}
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 modify wallet {address}: {e}")
return {}
def accept_request(self, address: str, req_type: str="create") -> dict:
endpoint = f"{config.LWS_ADMIN_URL}/accept_requests"
data = {
"auth": self.admin_key,
"params": {
"addresses": [address],
"type": req_type
}
}
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 accept request wallet {address}: {e}")
return {}
def reject_request(self, address: str, req_type: str="create") -> dict:
endpoint = f"{config.LWS_ADMIN_URL}/reject_requests"
data = {
"auth": self.admin_key,
"params": {
"addresses": [address],
"type": req_type
}
}
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 reject request wallet {address}: {e}")
return {}
def rescan(self, address: str, height: int) -> dict:
endpoint = f"{config.LWS_ADMIN_URL}/rescan"
data = {
"auth": self.admin_key,
"params": {
"addresses": [address],
"height": height
}
}
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 rescan wallet {address}: {e}")
return {}
lws = LWS()

@ -1,10 +1,7 @@
from datetime import datetime
import requests
from peewee import *
from lws import config
db = SqliteDatabase('lws.db')
@ -20,186 +17,4 @@ class User(Model):
database = db
class Wallet(Model):
name = CharField(unique=True)
description = TextField(default="")
address = CharField(unique=True)
view_key = CharField(unique=True)
restore_height = IntegerField()
added = BooleanField(default=False)
date = DateTimeField(default=datetime.utcnow)
date_added = DateTimeField(null=True)
user = ForeignKeyField(User, backref="wallets")
def is_active(self):
endpoint = f"{config.LWS_ADMIN_URL}/list_accounts"
data = {
"auth": self.user.view_key,
"params": {}
}
try:
req = requests.post(endpoint, json=data, timeout=5)
req.raise_for_status()
if req.ok:
res = req.json()
for _status in res:
for _wallet in res[_status]:
if _wallet["address"] == self.address:
if _status == "active":
return True
return False
return False
except Exception as e:
print(f"Failed to list wallets: {e}")
return False
def check_wallet_lws(self):
endpoint = f"{config.LWS_ADMIN_URL}/list_accounts"
data = {
"auth": self.user.view_key,
"params": {}
}
try:
req = requests.post(endpoint, json=data, timeout=5)
req.raise_for_status()
if req.ok:
res = req.json()
for _status in res:
for _wallet in res[_status]:
if _wallet["address"] == self.address:
self.added = True
self.save()
return True
return False
return False
except Exception as e:
print(f"Failed to list wallets: {e}")
return False
def add_wallet_lws(self):
if self.check_wallet_lws() and self.added is False:
self.added = True
self.date_added = datetime.utcnow()
self.save()
return True
endpoint = f"{config.LWS_ADMIN_URL}/add_account"
data = {
"auth": self.user.view_key,
"params": {
"address": self.address,
"key": self.view_key
}
}
try:
req = requests.post(endpoint, json=data, timeout=5)
req.raise_for_status()
if req.ok:
self.added = True
self.date_added = datetime.utcnow()
self.save()
return True
return False
except Exception as e:
print(f"Failed to add wallet {self.address}: {e}")
return False
def set_active(self, status):
endpoint = f"{config.LWS_ADMIN_URL}/modify_account_status"
_status = ""
if status:
_status = "active"
else:
_status = "inactive"
data = {
"auth": self.user.view_key,
"params": {
"addresses": [self.address],
"status": _status
}
}
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 enable_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 = {
"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 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 = {
"auth": self.user.view_key,
"params": {
"height": self.restore_height,
"addresses": [self.address]
}
}
try:
req = requests.post(endpoint, json=data, timeout=5)
req.raise_for_status()
if req.ok:
print(req.content)
return True
return False
except Exception as e:
print(f"Failed to add wallet {self.address}: {e}")
return False
class Meta:
database = db
db.create_tables([User, Wallet])
db.create_tables([User])

@ -87,6 +87,7 @@ async def setup():
address=address,
view_key=view_key
)
admin.save()
login_user(AuthUser(admin.id))
return redirect("/")
return await render_template("setup.html")

@ -2,34 +2,28 @@ import monero.seed
from quart import Blueprint, redirect, request, flash, render_template
from quart_auth import login_required
from lws.models import Wallet, User
from lws.helpers import LWS
from lws.models import User
from lws.helpers import lws
from lws import config
bp = Blueprint("meta", "meta")
@bp.route("/")
@login_required
async def index():
admin = User.select().first()
if not admin:
await flash("must setup admin first")
return redirect("/setup")
lws = LWS(admin.view_key)
lws.init(admin.view_key)
accounts = lws.list_accounts()
data = {}
for status in accounts:
if status == "hidden":
continue
for account in accounts[status]:
account["wallet"] = Wallet.select().where(Wallet.address ** account["address"]).order_by(Wallet.date.asc()).first()
account["status"] = status
data[account["address"]] = account
del accounts["hidden"]
requests = lws.list_requests()
return await render_template(
"index.html",
config=config,
data=data
accounts=accounts,
requests=requests
)

@ -2,29 +2,17 @@ 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, User
from lws.helpers import lws
bp = Blueprint('wallet', 'wallet')
# @bp.route("/")
# accept_requests: {"type": "import"|"create", "addresses":[...]}
# add_account: {"address": ..., "key": ...}
# list_accounts: {}
# list_requests: {}
# modify_account_status: {"status": "active"|"hidden"|"inactive", "addresses":[...]}
# reject_requests: {"type": "import"|"create", "addresses":[...]}
# rescan: {"height":..., "addresses":[...]}
@bp.route("/wallet/add", methods=["GET", "POST"])
@login_required
async def add():
form = await request.form
if form:
name = form.get("name", "")
description = form.get("description", "")
address = form.get("address", "")
view_key = form.get("view_key", "")
restore_height = form.get("restore_height", 0)
@ -44,63 +32,63 @@ async def add():
if not valid_view_key:
await flash("Invalid view key provided for address")
return redirect("/wallet/add")
wallet = Wallet.create(
name=name,
description=description,
address=address,
view_key=view_key,
restore_height=restore_height,
user=User.get(current_user.auth_id)
)
if not name:
wallet.name = f"wallet-{id}"
wallet.add_wallet_lws()
lws.add_wallet(address, view_key)
lws.rescan(address, int(restore_height))
await flash("wallet added")
return redirect(f"/wallet/{wallet.id}/rescan")
return redirect(f"/")
return await render_template("wallet/add.html")
@bp.route("/wallet/<id>")
@login_required
async def show(id):
wallet = Wallet.select().where(Wallet.id == id).first()
if not wallet:
await flash("wallet does not exist")
return redirect("/")
return await render_template(
"wallet/show.html",
wallet=wallet
)
@bp.route("/wallet/<id>/rescan")
# @bp.route("/wallet/<id>")
# @login_required
# async def show(id):
# wallet = Wallet.select().where(Wallet.id == id).first()
# if not wallet:
# await flash("wallet does not exist")
# return redirect("/")
# return await render_template(
# "wallet/show.html",
# wallet=wallet
# )
# @bp.route("/wallet/<id>/rescan")
# @login_required
# async def rescan(id):
# wallet = Wallet.select().where(Wallet.id == id).first()
# if not wallet:
# await flash("wallet does not exist")
# return redirect("/")
# wallet.rescan()
# return redirect(f"/wallet/{id}")
@bp.route("/wallet/<address>/disable")
@login_required
async def rescan(id):
wallet = Wallet.select().where(Wallet.id == id).first()
if not wallet:
await flash("wallet does not exist")
return redirect("/")
wallet.rescan()
return redirect(f"/wallet/{id}")
async def disable(address):
lws.modify_wallet(address, False)
await flash(f"{address} disabled in LWS")
return redirect(f"/")
@bp.route("/wallet/<id>/disable")
@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")
@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("/")
wallet.set_active(False)
return redirect(f"/wallet/{id}")
async def accept(address):
lws.accept_request(address)
await flash(f"{address} accepted")
return redirect(f"/")
@bp.route("/wallet/<id>/enable")
@bp.route("/wallet/<address>/reject")
@login_required
async def enable(id):
wallet = Wallet.select().where(Wallet.id == id).first()
if not wallet:
await flash("wallet does not exist")
return redirect("/")
wallet.set_active(True)
return redirect(f"/wallet/{id}")
async def reject(address):
lws.reject_request(address)
await flash(f"{address} rejected")
return redirect(f"/")

@ -10,38 +10,50 @@
<thead>
<tr>
<th>Status</th>
<th>Wallet</th>
<th>Action</th>
<th>Address</th>
<th>Description</th>
<th>Height</th>
</tr>
</thead>
<tbody>
{% for address in data %}
{% set _data = data[address] %}
{% for status in accounts %}
{% for account in accounts[status] %}
<tr>
<td>
<span class="tag text-white {% if _data['status'] == 'active' %}bg-success{% else %}bg-error{% endif %}">
{{ _data['status'] | upper }}
<span class="tag text-white {% if status == 'active' %}bg-success{% else %}bg-error{% endif %}">
{{ status | upper }}
</span>
</td>
<td>
{% if _data['wallet'] %}
<a href="/wallet/{{ data[address]['wallet'].id }}">{{ data[address]['wallet'].name }}</a>
{% if status == 'active' %}
<a href="/wallet/{{ account['address'] }}/disable" class="">disable</a>
{% else %}
<a href="/wallet/add?address={{ address }}" class="button">ADD</a>
{% endif %}
<a href="/wallet/{{ account['address'] }}/enable" class="">enable</a>
{% endif %}
</td>
<td>{{ address | shorten }}</td>
<td>{{ account['address'] | shorten }}</td>
<td>{{ account['scan_height'] }}</td>
</tr>
{% endfor %}
{% endfor %}
{% for status in requests %}
{% for request in requests[status] %}
<tr>
<td>
{% if _data['wallet'] %}
{{ _data['wallet'].description or "-" }}
{% else %}
-
{% endif %}
<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>{{ data[address]['scan_height'] }}</td>
<td>{{ request['address'] | shorten }}</td>
<td>{{ request['start_height'] }}</td>
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>

@ -4,10 +4,6 @@
<a href="/">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" value="{{ request.args.get('address', '') }}" />
<label for="view_key">View Key</label>

Loading…
Cancel
Save