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

master
lza_menace 2 years ago
parent fe06f6f74f
commit 9e8f3733d3

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

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

@ -1,13 +1,53 @@
import requests import requests
from lws.models import User
from lws import config 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: class LWS:
def __init__(self, admin_key): def __init__(self):
pass
def init(self, admin_key):
self.admin_key = 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" endpoint = f"{config.LWS_ADMIN_URL}/list_accounts"
data = {"auth": self.admin_key} data = {"auth": self.admin_key}
try: try:
@ -20,7 +60,20 @@ class LWS:
print(f"Failed to list accounts: {e}") print(f"Failed to list accounts: {e}")
return {} 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" endpoint = f"{config.LWS_URL}/get_address_txs"
data = { data = {
"address": address, "address": address,
@ -34,4 +87,103 @@ class LWS:
return {} return {}
except Exception as e: except Exception as e:
print(f"Failed to get wallet info {address}: {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 from datetime import datetime
import requests
from peewee import * from peewee import *
from lws import config
db = SqliteDatabase('lws.db') db = SqliteDatabase('lws.db')
@ -20,186 +17,4 @@ class User(Model):
database = db database = db
class Wallet(Model): db.create_tables([User])
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])

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

@ -2,34 +2,28 @@ import monero.seed
from quart import Blueprint, redirect, request, flash, render_template from quart import Blueprint, redirect, request, flash, render_template
from quart_auth import login_required from quart_auth import login_required
from lws.models import Wallet, User from lws.models import User
from lws.helpers import LWS from lws.helpers import lws
from lws import config from lws import config
bp = Blueprint("meta", "meta") bp = Blueprint("meta", "meta")
@bp.route("/") @bp.route("/")
@login_required
async def index(): async def index():
admin = User.select().first() admin = User.select().first()
if not admin: if not admin:
await flash("must setup admin first") await flash("must setup admin first")
return redirect("/setup") return redirect("/setup")
lws = LWS(admin.view_key) lws.init(admin.view_key)
accounts = lws.list_accounts() accounts = lws.list_accounts()
data = {} del accounts["hidden"]
for status in accounts: requests = lws.list_requests()
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
return await render_template( return await render_template(
"index.html", "index.html",
config=config, 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 import Blueprint, render_template, request, flash, redirect
from quart_auth import login_required, current_user from quart_auth import login_required, current_user
from lws.models import Wallet, User from lws.helpers import lws
bp = Blueprint('wallet', 'wallet') 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"]) @bp.route("/wallet/add", methods=["GET", "POST"])
@login_required @login_required
async def add(): async def add():
form = await request.form form = await request.form
if form: if form:
name = form.get("name", "")
description = form.get("description", "")
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", 0) restore_height = form.get("restore_height", 0)
@ -44,63 +32,63 @@ async def add():
if not valid_view_key: if not valid_view_key:
await flash("Invalid view key provided for address") await flash("Invalid view key provided for address")
return redirect("/wallet/add") return redirect("/wallet/add")
wallet = Wallet.create( lws.add_wallet(address, view_key)
name=name, lws.rescan(address, int(restore_height))
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()
await flash("wallet added") await flash("wallet added")
return redirect(f"/wallet/{wallet.id}/rescan") return redirect(f"/")
return await render_template("wallet/add.html") return await render_template("wallet/add.html")
@bp.route("/wallet/<id>") # @bp.route("/wallet/<id>")
@login_required # @login_required
async def show(id): # async def show(id):
wallet = Wallet.select().where(Wallet.id == id).first() # wallet = Wallet.select().where(Wallet.id == id).first()
if not wallet: # if not wallet:
await flash("wallet does not exist") # await flash("wallet does not exist")
return redirect("/") # return redirect("/")
return await render_template( # return await render_template(
"wallet/show.html", # "wallet/show.html",
wallet=wallet # wallet=wallet
) # )
@bp.route("/wallet/<id>/rescan") # @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 @login_required
async def rescan(id): async def disable(address):
wallet = Wallet.select().where(Wallet.id == id).first() lws.modify_wallet(address, False)
if not wallet: await flash(f"{address} disabled in LWS")
await flash("wallet does not exist") return redirect(f"/")
return redirect("/")
wallet.rescan()
return redirect(f"/wallet/{id}")
@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 @login_required
async def disable(id): async def accept(address):
wallet = Wallet.select().where(Wallet.id == id).first() lws.accept_request(address)
if not wallet: await flash(f"{address} accepted")
await flash("wallet does not exist") return redirect(f"/")
return redirect("/")
wallet.set_active(False)
return redirect(f"/wallet/{id}")
@bp.route("/wallet/<id>/enable") @bp.route("/wallet/<address>/reject")
@login_required @login_required
async def enable(id): async def reject(address):
wallet = Wallet.select().where(Wallet.id == id).first() lws.reject_request(address)
if not wallet: await flash(f"{address} rejected")
await flash("wallet does not exist") return redirect(f"/")
return redirect("/")
wallet.set_active(True)
return redirect(f"/wallet/{id}")

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

@ -4,10 +4,6 @@
<a href="/">Go Back</a> <a href="/">Go Back</a>
<h1>Add a Wallet</h1> <h1>Add a Wallet</h1>
<form method="post"> <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> <label for="address">Address</label>
<input type="text" name="address" value="{{ request.args.get('address', '') }}" /> <input type="text" name="address" value="{{ request.args.get('address', '') }}" />
<label for="view_key">View Key</label> <label for="view_key">View Key</label>

Loading…
Cancel
Save