From b6d29a33d24bc7f2443e29c2fe43859a2f6dce57 Mon Sep 17 00:00:00 2001 From: gerlofvanek Date: Tue, 10 Sep 2024 19:51:27 +0200 Subject: [PATCH 1/3] ui: Refactor offers page / Added TOR <> API / Cache Restructure / Refactor JS for the Offers page. Added TOR for chart/rates/coin prices/ API requests. New Chart with option of show volume(coin) / refresh chart (will clear cache). Added cache (10min) for all the API's or manual refresh. Disabled notifications show new offers (was spam). New bids and Bid Accepted still active. Various small fixes. --- basicswap/base.py | 51 +- basicswap/http_server.py | 18 +- basicswap/js_server.py | 24 +- basicswap/templates/footer.html | 1 + basicswap/templates/header.html | 67 +- basicswap/templates/offers.html | 2836 +++++++++++++++++-------------- basicswap/templates/unlock.html | 140 +- basicswap/ui/page_tor.py | 11 +- 8 files changed, 1725 insertions(+), 1423 deletions(-) diff --git a/basicswap/base.py b/basicswap/base.py index a7681f7..2b117eb 100644 --- a/basicswap/base.py +++ b/basicswap/base.py @@ -15,6 +15,9 @@ import logging import threading import traceback import subprocess +import urllib.request +import urllib.error +import json from sockshandler import SocksiPyHandler @@ -162,20 +165,42 @@ class BaseApp: socket.getaddrinfo = self.default_socket_getaddrinfo socket.setdefaulttimeout(self.default_socket_timeout) - def readURL(self, url: str, timeout: int = 120, headers=None) -> bytes: - open_handler = None - if self.use_tor_proxy: - open_handler = SocksiPyHandler(socks.PROXY_TYPE_SOCKS5, self.tor_proxy_host, self.tor_proxy_port) - opener = urllib.request.build_opener(open_handler) if self.use_tor_proxy else urllib.request.build_opener() - opener.addheaders = [('User-agent', 'Mozilla/5.0')] - request = urllib.request.Request(url, headers=headers) - return opener.open(request, timeout=timeout).read() - - def logException(self, message) -> None: - self.log.error(message) - if self.debug: - self.log.error(traceback.format_exc()) + def is_tor_available(self): + if not hasattr(self, 'use_tor_proxy'): + return False + if not self.use_tor_proxy: + return False + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + result = sock.connect_ex((self.tor_proxy_host, self.tor_proxy_port)) + sock.close() + return result == 0 + except: + return False + def readURL(self, url: str, timeout: int = 120, headers={}) -> bytes: + use_tor = self.is_tor_available() + try: + if use_tor: + proxy_handler = SocksiPyHandler(socks.PROXY_TYPE_SOCKS5, self.tor_proxy_host, self.tor_proxy_port) + opener = urllib.request.build_opener(proxy_handler) + else: + opener = urllib.request.build_opener() + + opener.addheaders = [(key, value) for key, value in headers.items()] + request = urllib.request.Request(url) + + with opener.open(request, timeout=timeout) as response: + return response.read() + except urllib.error.URLError as e: + if isinstance(e.reason, ConnectionRefusedError) and use_tor: + error_msg = f"Connection refused. Tor proxy might not be running. Error: {str(e)}" + else: + error_msg = f"URLError: {str(e)}" + except Exception as e: + error_msg = f"Unexpected error: {str(e)}" + return json.dumps({"Error": error_msg}).encode() + def torControl(self, query): try: command = 'AUTHENTICATE "{}"\r\n{}\r\nQUIT\r\n'.format(self.tor_control_password, query).encode('utf-8') diff --git a/basicswap/http_server.py b/basicswap/http_server.py index c4d6679..243c940 100644 --- a/basicswap/http_server.py +++ b/basicswap/http_server.py @@ -111,7 +111,6 @@ def parse_cmd(cmd: str, type_map: str): class HttpHandler(BaseHTTPRequestHandler): - def log_error(self, format, *args): super().log_message(format, *args) @@ -145,9 +144,12 @@ class HttpHandler(BaseHTTPRequestHandler): args_dict['use_tor_proxy'] = True # TODO: Cache value? try: - args_dict['tor_established'] = True if get_tor_established_state(swap_client) == '1' else False - except Exception: + tor_state = get_tor_established_state(swap_client) + args_dict['tor_established'] = True if tor_state == '1' else False + except Exception as e: + args_dict['tor_established'] = False if swap_client.debug: + swap_client.log.error(f"Error getting Tor state: {str(e)}") swap_client.log.error(traceback.format_exc()) if swap_client._show_notifications: @@ -409,12 +411,10 @@ class HttpHandler(BaseHTTPRequestHandler): swap_client = self.server.swap_client swap_client.checkSystemStatus() summary = swap_client.getSummary() - template = env.get_template('index.html') - return self.render_template(template, { - 'refresh': 30, - 'summary': summary, - 'use_tor_proxy': swap_client.use_tor_proxy - }) + self.send_response(302) + self.send_header('Location', '/offers') + self.end_headers() + return b'' def page_404(self, url_split): swap_client = self.server.swap_client diff --git a/basicswap/js_server.py b/basicswap/js_server.py index 0f8d303..94598d7 100644 --- a/basicswap/js_server.py +++ b/basicswap/js_server.py @@ -38,7 +38,6 @@ from .ui.util import ( from .ui.page_offers import postNewOffer from .protocols.xmr_swap_1 import recoverNoScriptTxnWithKey, getChainBSplitKey - def getFormData(post_string: str, is_json: bool): if post_string == '': raise ValueError('No post data') @@ -763,7 +762,26 @@ def js_help(self, url_split, post_string, is_json) -> bytes: for k in pages: commands.append(k) return bytes(json.dumps({'commands': commands}), 'UTF-8') - + +def js_readurl(self, url_split, post_string, is_json) -> bytes: + swap_client = self.server.swap_client + post_data = {} if post_string == '' else getFormData(post_string, is_json) + if have_data_entry(post_data, 'url'): + url = get_data_entry(post_data, 'url') + default_headers = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', + 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', + 'Accept-Language': 'en-US,en;q=0.5', + } + response = swap_client.readURL(url, headers=default_headers) + try: + error = json.loads(response.decode()) + if "Error" in error: + return json.dumps({"Error": error['Error']}).encode() + except json.JSONDecodeError: + pass + return response + raise ValueError('Requires URL.') pages = { 'coins': js_coins, @@ -789,9 +807,9 @@ pages = { 'unlock': js_unlock, 'lock': js_lock, 'help': js_help, + 'readurl': js_readurl, } - def js_url_to_function(url_split): if len(url_split) > 2: return pages.get(url_split[2], js_404) diff --git a/basicswap/templates/footer.html b/basicswap/templates/footer.html index 484d05c..a2c840e 100644 --- a/basicswap/templates/footer.html +++ b/basicswap/templates/footer.html @@ -11,6 +11,7 @@
BasicSwap Explained
Tutorials and Guides
Get Support
+
Terms and Conditions
diff --git a/basicswap/templates/header.html b/basicswap/templates/header.html index 180a47a..c4cc40f 100644 --- a/basicswap/templates/header.html +++ b/basicswap/templates/header.html @@ -519,38 +519,55 @@ {% if ws_url %} {% endif %} diff --git a/basicswap/templates/offers.html b/basicswap/templates/offers.html index 0d0b53d..c0d97f1 100644 --- a/basicswap/templates/offers.html +++ b/basicswap/templates/offers.html @@ -1,6 +1,5 @@ {% include 'header.html' %} {% from 'style.html' import breadcrumb_line_svg, place_new_offer_svg, page_back_svg, page_forwards_svg, filter_clear_svg, filter_apply_svg, input_arrow_down_svg, arrow_right_svg %} -
@@ -8,14 +7,13 @@
- - -
+
+
dots-red @@ -26,7 +24,7 @@

{{ page_type_description }}

@@ -34,802 +32,1243 @@
- {% include 'inc_messages.html' %} - - {% if show_chart %} -