diff --git a/wowstash/blueprints/auth/routes.py b/wowstash/blueprints/auth/routes.py index 47ce054..1ec6d42 100644 --- a/wowstash/blueprints/auth/routes.py +++ b/wowstash/blueprints/auth/routes.py @@ -6,6 +6,7 @@ from wowstash.blueprints.auth import auth_bp from wowstash.forms import Register, Login from wowstash.models import User from wowstash.factory import db, bcrypt +from wowstash.library.docker import docker @auth_bp.route("/register", methods=["GET", "POST"]) @@ -69,7 +70,7 @@ def login(): @auth_bp.route("/logout") def logout(): if current_user.is_authenticated: - current_user.kill_wallet() + docker.stop_container(current_user.wallet_container) current_user.clear_wallet_data() logout_user() return redirect(url_for('meta.index')) diff --git a/wowstash/blueprints/wallet/routes.py b/wowstash/blueprints/wallet/routes.py index aa45631..4e3de0f 100644 --- a/wowstash/blueprints/wallet/routes.py +++ b/wowstash/blueprints/wallet/routes.py @@ -1,4 +1,4 @@ -import subprocess +from time import sleep from io import BytesIO from base64 import b64encode from qrcode import make as qrcode_make @@ -9,6 +9,7 @@ from flask_login import login_required, current_user from socket import socket from datetime import datetime from wowstash.blueprints.wallet import wallet_bp +from wowstash.library.docker import docker from wowstash.library.jsonrpc import Wallet, to_atomic from wowstash.library.cache import cache from wowstash.forms import Send @@ -21,6 +22,7 @@ from wowstash import config @login_required def loading(): if current_user.wallet_connected and current_user.wallet_created: + sleep(1) return redirect(url_for('wallet.dashboard')) return render_template('wallet/loading.html') @@ -46,7 +48,7 @@ def dashboard(): for tx in transfers[type]: all_transfers.append(tx) balances = wallet.get_balances() - qr_uri = f'wownero:{address}?tx_description="{current_user.email}"' + qr_uri = f'wownero:{address}?tx_description={current_user.email}' address_qr = qrcode_make(qr_uri).save(_address_qr) qrcode = b64encode(_address_qr.getvalue()).decode() return render_template( @@ -63,42 +65,33 @@ def dashboard(): @login_required def connect(): if current_user.wallet_connected is False: - tcp = socket() - tcp.bind(('', 0)) - _, port = tcp.getsockname() - tcp.close() - command = f"""wownero-wallet-rpc \ - --detach \ - --non-interactive \ - --rpc-bind-port {port} \ - --wallet-file {config.WALLET_DIR}/{current_user.id}.wallet \ - --rpc-login {current_user.id}:{current_user.wallet_password} \ - --password {current_user.wallet_password} \ - --daemon-address {config.DAEMON_PROTO}://{config.DAEMON_HOST}:{config.DAEMON_PORT} \ - --daemon-login {config.DAEMON_USER}:{config.DAEMON_PASS} \ - --log-file {config.WALLET_DIR}/{current_user.id}.log - """ - proc = subprocess.Popen(command.split(), stdout=subprocess.PIPE) - outs, errs = proc.communicate() - # print(outs) - if proc.returncode == 0: - print(f'Successfully started RPC for {current_user}!') - current_user.wallet_connected = True - current_user.wallet_port = port - current_user.wallet_pid = proc.pid - current_user.wallet_connect_date = datetime.now() - db.session.commit() - - return "ok" + wallet = docker.start_wallet(current_user.id) + port = docker.get_port(wallet) + current_user.wallet_connected = docker.container_exists(wallet) + current_user.wallet_port = port + current_user.wallet_container = wallet + db.session.commit() + + return 'ok' + +@wallet_bp.route('/wallet/create') +@login_required +def create(): + if current_user.wallet_created is False: + docker.create_wallet(current_user.id) + current_user.wallet_created = True + db.session.commit() + + return 'ok' @wallet_bp.route('/wallet/status') @login_required def status(): data = { - "created": current_user.wallet_created, - "connected": current_user.wallet_connected, - "port": current_user.wallet_port, - "date": current_user.wallet_connect_date + 'created': current_user.wallet_created, + 'connected': current_user.wallet_connected, + 'port': current_user.wallet_port, + 'container': current_user.wallet_container } return jsonify(data) diff --git a/wowstash/factory.py b/wowstash/factory.py index 0279619..761c3bd 100644 --- a/wowstash/factory.py +++ b/wowstash/factory.py @@ -57,6 +57,7 @@ def create_app(): login_manager = LoginManager() login_manager.init_app(app) login_manager.login_view = 'auth.login' + login_manager.logout_view = 'auth.logout' @login_manager.user_loader def load_user(user_id): @@ -73,7 +74,16 @@ def create_app(): @app.template_filter('from_atomic') def from_atomic(a): from wowstash.library.jsonrpc import from_atomic - return from_atomic(a) + atomic = from_atomic(a) + if atomic == 0: + return 0 + else: + return float(atomic) + + @app.cli.command('clean_containers') + def clean_containers(): + from wowstash.library.docker import docker + docker.cleanup() # Routes from wowstash.blueprints.auth import auth_bp diff --git a/wowstash/library/docker.py b/wowstash/library/docker.py index 984110f..fde56da 100644 --- a/wowstash/library/docker.py +++ b/wowstash/library/docker.py @@ -9,7 +9,7 @@ class Docker(object): def __init__(self): self.client = from_env() self.wownero_image = getattr(config, 'WOWNERO_IMAGE', 'lalanza808/wownero') - self.wallet_dir = getattr(config, 'WALLET_DIR', './data/wallets') + self.wallet_dir = getattr(config, 'WALLET_DIR', '/tmp/wallets') self.listen_port = 34569 def create_wallet(self, user_id): @@ -45,6 +45,8 @@ class Docker(object): command = f"""wownero-wallet-rpc \ --non-interactive \ --rpc-bind-port {self.listen_port} \ + --rpc-bind-ip 0.0.0.0 \ + --confirm-external-bind \ --wallet-file /wallet/{u.id}.wallet \ --rpc-login {u.id}:{u.wallet_password} \ --password {u.wallet_password} \ @@ -60,7 +62,7 @@ class Docker(object): remove=True, detach=True, ports={ - f'{self.listen_port}/tcp': None + f'{self.listen_port}/tcp': ('127.0.0.1', None) }, volumes={ f'{self.wallet_dir}/{u.id}': { @@ -83,3 +85,18 @@ class Docker(object): return True except NotFound: return False + + def stop_container(self, container_id): + if self.container_exists(container_id): + c = self.client.containers.get(container_id) + c.stop() + + def cleanup(self): + users = User.query.all() + for u in users: + if u.wallet_container: + if not self.container_exists(u.wallet_container): + u.clear_wallet_data() + + +docker = Docker() diff --git a/wowstash/models.py b/wowstash/models.py index 32e91d4..7c3c9af 100644 --- a/wowstash/models.py +++ b/wowstash/models.py @@ -18,8 +18,7 @@ class User(db.Model): wallet_created = db.Column(db.Boolean, default=False) wallet_connected = db.Column(db.Boolean, default=False) wallet_port = db.Column(db.Integer, nullable=True) - wallet_pid = db.Column(db.Integer, nullable=True) - wallet_connect_date = db.Column(db.DateTime, nullable=True) + wallet_container = db.Column(db.String(30), nullable=True) @property def is_authenticated(self): @@ -40,17 +39,10 @@ class User(db.Model): def get_id(self): return self.id - def kill_wallet(self): - try: - kill(self.wallet_pid, 9) - except Exception as e: - print('could kill:', e) - def clear_wallet_data(self): self.wallet_connected = False self.wallet_port = None - self.wallet_pid = None - self.wallet_connect_date = None + self.wallet_container = None db.session.commit() def __repr__(self): diff --git a/wowstash/templates/wallet/loading.html b/wowstash/templates/wallet/loading.html index 2e85a2e..9f32c36 100644 --- a/wowstash/templates/wallet/loading.html +++ b/wowstash/templates/wallet/loading.html @@ -41,7 +41,7 @@ xhr.send(); } - {% if current_user.wallet_connected == False %} + {% if current_user.wallet_connected == False and current_user.wallet_created == True %} document.addEventListener("DOMContentLoaded", function(){ var xhr = new XMLHttpRequest(); xhr.open('GET', '{{ url_for("wallet.connect") }}'); @@ -49,6 +49,14 @@ }); {% endif %} + {% if current_user.wallet_connected == False and current_user.wallet_created == False %} + document.addEventListener("DOMContentLoaded", function(){ + var xhr = new XMLHttpRequest(); + xhr.open('GET', '{{ url_for("wallet.create") }}'); + xhr.send(); + }); + {% endif %} + window.setInterval(function(){ {% if current_user.wallet_connected == False %} check_wallet_status('connected');