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.
This commit is contained in:
gerlofvanek
2024-09-10 19:51:27 +02:00
parent 003d7b85ab
commit b6d29a33d2
8 changed files with 1725 additions and 1423 deletions

View File

@@ -15,6 +15,9 @@ import logging
import threading import threading
import traceback import traceback
import subprocess import subprocess
import urllib.request
import urllib.error
import json
from sockshandler import SocksiPyHandler from sockshandler import SocksiPyHandler
@@ -162,19 +165,41 @@ class BaseApp:
socket.getaddrinfo = self.default_socket_getaddrinfo socket.getaddrinfo = self.default_socket_getaddrinfo
socket.setdefaulttimeout(self.default_socket_timeout) socket.setdefaulttimeout(self.default_socket_timeout)
def readURL(self, url: str, timeout: int = 120, headers=None) -> bytes: def is_tor_available(self):
open_handler = None if not hasattr(self, 'use_tor_proxy'):
if self.use_tor_proxy: return False
open_handler = SocksiPyHandler(socks.PROXY_TYPE_SOCKS5, self.tor_proxy_host, self.tor_proxy_port) if not self.use_tor_proxy:
opener = urllib.request.build_opener(open_handler) if self.use_tor_proxy else urllib.request.build_opener() return False
opener.addheaders = [('User-agent', 'Mozilla/5.0')] try:
request = urllib.request.Request(url, headers=headers) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
return opener.open(request, timeout=timeout).read() result = sock.connect_ex((self.tor_proxy_host, self.tor_proxy_port))
sock.close()
return result == 0
except:
return False
def logException(self, message) -> None: def readURL(self, url: str, timeout: int = 120, headers={}) -> bytes:
self.log.error(message) use_tor = self.is_tor_available()
if self.debug: try:
self.log.error(traceback.format_exc()) 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): def torControl(self, query):
try: try:

View File

@@ -111,7 +111,6 @@ def parse_cmd(cmd: str, type_map: str):
class HttpHandler(BaseHTTPRequestHandler): class HttpHandler(BaseHTTPRequestHandler):
def log_error(self, format, *args): def log_error(self, format, *args):
super().log_message(format, *args) super().log_message(format, *args)
@@ -145,9 +144,12 @@ class HttpHandler(BaseHTTPRequestHandler):
args_dict['use_tor_proxy'] = True args_dict['use_tor_proxy'] = True
# TODO: Cache value? # TODO: Cache value?
try: try:
args_dict['tor_established'] = True if get_tor_established_state(swap_client) == '1' else False tor_state = get_tor_established_state(swap_client)
except Exception: args_dict['tor_established'] = True if tor_state == '1' else False
except Exception as e:
args_dict['tor_established'] = False
if swap_client.debug: if swap_client.debug:
swap_client.log.error(f"Error getting Tor state: {str(e)}")
swap_client.log.error(traceback.format_exc()) swap_client.log.error(traceback.format_exc())
if swap_client._show_notifications: if swap_client._show_notifications:
@@ -409,12 +411,10 @@ class HttpHandler(BaseHTTPRequestHandler):
swap_client = self.server.swap_client swap_client = self.server.swap_client
swap_client.checkSystemStatus() swap_client.checkSystemStatus()
summary = swap_client.getSummary() summary = swap_client.getSummary()
template = env.get_template('index.html') self.send_response(302)
return self.render_template(template, { self.send_header('Location', '/offers')
'refresh': 30, self.end_headers()
'summary': summary, return b''
'use_tor_proxy': swap_client.use_tor_proxy
})
def page_404(self, url_split): def page_404(self, url_split):
swap_client = self.server.swap_client swap_client = self.server.swap_client

View File

@@ -38,7 +38,6 @@ from .ui.util import (
from .ui.page_offers import postNewOffer from .ui.page_offers import postNewOffer
from .protocols.xmr_swap_1 import recoverNoScriptTxnWithKey, getChainBSplitKey from .protocols.xmr_swap_1 import recoverNoScriptTxnWithKey, getChainBSplitKey
def getFormData(post_string: str, is_json: bool): def getFormData(post_string: str, is_json: bool):
if post_string == '': if post_string == '':
raise ValueError('No post data') raise ValueError('No post data')
@@ -764,6 +763,25 @@ def js_help(self, url_split, post_string, is_json) -> bytes:
commands.append(k) commands.append(k)
return bytes(json.dumps({'commands': commands}), 'UTF-8') 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 = { pages = {
'coins': js_coins, 'coins': js_coins,
@@ -789,9 +807,9 @@ pages = {
'unlock': js_unlock, 'unlock': js_unlock,
'lock': js_lock, 'lock': js_lock,
'help': js_help, 'help': js_help,
'readurl': js_readurl,
} }
def js_url_to_function(url_split): def js_url_to_function(url_split):
if len(url_split) > 2: if len(url_split) > 2:
return pages.get(url_split[2], js_404) return pages.get(url_split[2], js_404)

View File

@@ -11,6 +11,7 @@
<div class="w-full md:w-auto p-3 md:py-0 md:px-6"><a class="inline-block text-coolGray-500 dark:text-gray-300 font-medium" href="https://academy.particl.io/en/latest/basicswap-dex/basicswap_explained.html" target="_blank">BasicSwap Explained</a></div> <div class="w-full md:w-auto p-3 md:py-0 md:px-6"><a class="inline-block text-coolGray-500 dark:text-gray-300 font-medium" href="https://academy.particl.io/en/latest/basicswap-dex/basicswap_explained.html" target="_blank">BasicSwap Explained</a></div>
<div class="w-full md:w-auto p-3 md:py-0 md:px-6"><a class="inline-block text-coolGray-500 dark:text-gray-300 font-medium" href="https://academy.particl.io/en/latest/basicswap-guides/basicswapguides_installation.html" target="_blank">Tutorials and Guides</a></div> <div class="w-full md:w-auto p-3 md:py-0 md:px-6"><a class="inline-block text-coolGray-500 dark:text-gray-300 font-medium" href="https://academy.particl.io/en/latest/basicswap-guides/basicswapguides_installation.html" target="_blank">Tutorials and Guides</a></div>
<div class="w-full md:w-auto p-3 md:py-0 md:px-6"><a class="inline-block text-coolGray-500 dark:text-gray-300 font-medium" href="https://academy.particl.io/en/latest/faq/get_support.html" target="_blank">Get Support</a></div> <div class="w-full md:w-auto p-3 md:py-0 md:px-6"><a class="inline-block text-coolGray-500 dark:text-gray-300 font-medium" href="https://academy.particl.io/en/latest/faq/get_support.html" target="_blank">Get Support</a></div>
<div class="w-full md:w-auto p-3 md:py-0 md:px-6"><a class="inline-block text-coolGray-500 dark:text-gray-300 font-medium" href="https://basicswapdex.com/terms" target="_blank">Terms and Conditions</a></div>
</div> </div>
</div> </div>
<div class="w-full md:w-1/4 px-4"> </div> <div class="w-full md:w-1/4 px-4"> </div>

View File

@@ -519,38 +519,55 @@
</section> </section>
{% if ws_url %} {% if ws_url %}
<script> <script>
var ws = new WebSocket("{{ ws_url }}"), // Configuration object
const notificationConfig = {
showNewOffers: false,
showNewBids: true,
showBidAccepted: true
};
var ws = new WebSocket("{{ ws_url }}"),
floating_div = document.createElement('div'); floating_div = document.createElement('div');
floating_div.classList.add('floatright'); floating_div.classList.add('floatright');
messages = document.createElement('ul'); messages = document.createElement('ul');
messages.setAttribute('id', 'ul_updates'); messages.setAttribute('id', 'ul_updates');
ws.onmessage = function(event) {
let json = JSON.parse(event.data); floating_div.appendChild(messages);
let event_message = 'Unknown event';
if (json['event'] == 'new_offer') { ws.onmessage = function(event) {
let json = JSON.parse(event.data);
let event_message = 'Unknown event';
let should_display = false;
if (json['event'] == 'new_offer' && notificationConfig.showNewOffers) {
event_message = '<div id="hide"><div id="toast-success" class="flex items-center p-4 mb-4 w-full max-w-xs text-gray-500 bg-white rounded-lg shadow" role="alert"><div class="inline-flex flex-shrink-0 justify-center items-center w-10 h-10 bg-blue-500 rounded-lg"><svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" height="18" width="18" viewBox="0 0 24 24"><g stroke-linecap="round" stroke-width="2" fill="none" stroke="#ffffff" stroke-linejoin="round"><circle cx="5" cy="5" r="4"></circle> <circle cx="19" cy="19" r="4"></circle> <polyline data-cap="butt" points="13,5 21,5 21,11 " stroke="#ffffff"></polyline> <polyline data-cap="butt" points="11,19 3,19 3,13 " stroke="#ffffff"></polyline> <polyline points=" 16,2 13,5 16,8 " stroke="#ffffff"></polyline> <polyline points=" 8,16 11,19 8,22 " stroke="#ffffff"></polyline></g></svg></div><div class="uppercase w-40 ml-3 text-sm font-semibold text-gray-900">New network <a class="underline" href=/offer/' + json['offer_id'] + '>offer</a></div><button type="button" onclick="closeAlert(event)" class="ml-auto -mx-1.5 -my-1.5 bg-white text-gray-400 hover:text-gray-900 rounded-lg focus:ring-0 focus:outline-none focus:ring-gray-300 p-1.5 hover:bg-gray-100 inline-flex h-8 w-8" data-dismiss="#toast-success" aria-label="Close"><span class="sr-only">Close</span><svg aria-hidden="true" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg></button></div></div>'; event_message = '<div id="hide"><div id="toast-success" class="flex items-center p-4 mb-4 w-full max-w-xs text-gray-500 bg-white rounded-lg shadow" role="alert"><div class="inline-flex flex-shrink-0 justify-center items-center w-10 h-10 bg-blue-500 rounded-lg"><svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" height="18" width="18" viewBox="0 0 24 24"><g stroke-linecap="round" stroke-width="2" fill="none" stroke="#ffffff" stroke-linejoin="round"><circle cx="5" cy="5" r="4"></circle> <circle cx="19" cy="19" r="4"></circle> <polyline data-cap="butt" points="13,5 21,5 21,11 " stroke="#ffffff"></polyline> <polyline data-cap="butt" points="11,19 3,19 3,13 " stroke="#ffffff"></polyline> <polyline points=" 16,2 13,5 16,8 " stroke="#ffffff"></polyline> <polyline points=" 8,16 11,19 8,22 " stroke="#ffffff"></polyline></g></svg></div><div class="uppercase w-40 ml-3 text-sm font-semibold text-gray-900">New network <a class="underline" href=/offer/' + json['offer_id'] + '>offer</a></div><button type="button" onclick="closeAlert(event)" class="ml-auto -mx-1.5 -my-1.5 bg-white text-gray-400 hover:text-gray-900 rounded-lg focus:ring-0 focus:outline-none focus:ring-gray-300 p-1.5 hover:bg-gray-100 inline-flex h-8 w-8" data-dismiss="#toast-success" aria-label="Close"><span class="sr-only">Close</span><svg aria-hidden="true" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg></button></div></div>';
} should_display = true;
else }
if (json['event'] == 'new_bid') { else if (json['event'] == 'new_bid' && notificationConfig.showNewBids) {
event_message = '<div id="hide"><div id="toast-success" class="flex items-center p-4 mb-4 w-full max-w-xs text-gray-500 bg-white rounded-lg shadow" role="alert"><div class="inline-flex flex-shrink-0 justify-center items-center w-10 h-10 bg-violet-500 rounded-lg"><svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" height="18" width="18" viewBox="0 0 24 24"><g stroke-linecap="round" stroke-width="2" fill="none" stroke="#ffffff" stroke-linejoin="round"><rect x="9.843" y="5.379" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -0.7635 13.1569)" width="11.314" height="4.243"></rect> <polyline points="3,23 3,19 15,19 15,23 "></polyline> <line x1="4" y1="15" x2="1" y2="15" stroke="#ffffff"></line> <line x1="5.757" y1="10.757" x2="3.636" y2="8.636" stroke="#ffffff"></line> <line x1="1" y1="23" x2="17" y2="23"></line> <line x1="17" y1="9" x2="23" y2="15"></line></g></svg></div><div class="uppercase w-40 ml-3 text-sm font-normal"><span class="mb-1 text-sm font-semibold text-gray-900"><a class="underline" href=/bid/' + json['bid_id'] + '>New bid</a> on <a class="underline" href=/offer/' + json['offer_id'] + '>offer</a></span></div><button type="button" onclick="closeAlert(event)" class="ml-auto -mx-1.5 -my-1.5 bg-white text-gray-400 hover:text-gray-900 rounded-lg focus:ring-0 focus:outline-nonefocus:ring-gray-300 p-1.5 hover:bg-gray-100 inline-flex h-8 w-8" data-dismiss="#toast-success" aria-label="Close"><svg aria-hidden="true" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg></button></div></div>'; event_message = '<div id="hide"><div id="toast-success" class="flex items-center p-4 mb-4 w-full max-w-xs text-gray-500 bg-white rounded-lg shadow" role="alert"><div class="inline-flex flex-shrink-0 justify-center items-center w-10 h-10 bg-violet-500 rounded-lg"><svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" height="18" width="18" viewBox="0 0 24 24"><g stroke-linecap="round" stroke-width="2" fill="none" stroke="#ffffff" stroke-linejoin="round"><rect x="9.843" y="5.379" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -0.7635 13.1569)" width="11.314" height="4.243"></rect> <polyline points="3,23 3,19 15,19 15,23 "></polyline> <line x1="4" y1="15" x2="1" y2="15" stroke="#ffffff"></line> <line x1="5.757" y1="10.757" x2="3.636" y2="8.636" stroke="#ffffff"></line> <line x1="1" y1="23" x2="17" y2="23"></line> <line x1="17" y1="9" x2="23" y2="15"></line></g></svg></div><div class="uppercase w-40 ml-3 text-sm font-normal"><span class="mb-1 text-sm font-semibold text-gray-900"><a class="underline" href=/bid/' + json['bid_id'] + '>New bid</a> on <a class="underline" href=/offer/' + json['offer_id'] + '>offer</a></span></div><button type="button" onclick="closeAlert(event)" class="ml-auto -mx-1.5 -my-1.5 bg-white text-gray-400 hover:text-gray-900 rounded-lg focus:ring-0 focus:outline-nonefocus:ring-gray-300 p-1.5 hover:bg-gray-100 inline-flex h-8 w-8" data-dismiss="#toast-success" aria-label="Close"><svg aria-hidden="true" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg></button></div></div>';
} should_display = true;
else }
if (json['event'] == 'bid_accepted') { else if (json['event'] == 'bid_accepted' && notificationConfig.showBidAccepted) {
event_message = '<div id="hide"><div id="toast-success" class="flex items-center p-4 mb-4 w-full max-w-xs text-gray-500 bg-white rounded-lg shadow" role="alert"><div class="inline-flex flex-shrink-0 justify-center items-center w-10 h-10 bg-violet-500 rounded-lg"><svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" height="18" width="18" viewBox="0 0 24 24"><g fill="#ffffff"><path d="M8.5,20a1.5,1.5,0,0,1-1.061-.439L.379,12.5,2.5,10.379l6,6,13-13L23.621,5.5,9.561,19.561A1.5,1.5,0,0,1,8.5,20Z" fill="#ffffff"></path></g></svg></div><div class="uppercase w-40 ml-3 text-sm font-semibold text-gray-900"><a class="underline" href=/bid/' + json['bid_id'] + '>Bid</a> accepted</div><button type="button" onclick="closeAlert(event)" class="ml-auto -mx-1.5 -my-1.5 bg-white text-gray-400 hover:text-gray-900 rounded-lg focus:ring-0 focus:outline-none focus:ring-gray-300 p-1.5 hover:bg-gray-100 inline-flex h-8 w-8" data-dismiss="#toast-success" aria-label="Close"><span class="sr-only">Close</span><svg aria-hidden="true" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg></button></div></div>'; event_message = '<div id="hide"><div id="toast-success" class="flex items-center p-4 mb-4 w-full max-w-xs text-gray-500 bg-white rounded-lg shadow" role="alert"><div class="inline-flex flex-shrink-0 justify-center items-center w-10 h-10 bg-violet-500 rounded-lg"><svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" height="18" width="18" viewBox="0 0 24 24"><g fill="#ffffff"><path d="M8.5,20a1.5,1.5,0,0,1-1.061-.439L.379,12.5,2.5,10.379l6,6,13-13L23.621,5.5,9.561,19.561A1.5,1.5,0,0,1,8.5,20Z" fill="#ffffff"></path></g></svg></div><div class="uppercase w-40 ml-3 text-sm font-semibold text-gray-900"><a class="underline" href=/bid/' + json['bid_id'] + '>Bid</a> accepted</div><button type="button" onclick="closeAlert(event)" class="ml-auto -mx-1.5 -my-1.5 bg-white text-gray-400 hover:text-gray-900 rounded-lg focus:ring-0 focus:outline-none focus:ring-gray-300 p-1.5 hover:bg-gray-100 inline-flex h-8 w-8" data-dismiss="#toast-success" aria-label="Close"><span class="sr-only">Close</span><svg aria-hidden="true" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg></button></div></div>';
} should_display = true;
let messages = document.getElementById('ul_updates'), }
message = document.createElement('li');
message.innerHTML = event_message; if (should_display) {
messages.appendChild(message); let messages = document.getElementById('ul_updates'),
}; message = document.createElement('li');
floating_div.appendChild(messages); message.innerHTML = event_message;
document.body.appendChild(floating_div); messages.appendChild(message);
function closeAlert(event){ }
};
document.body.appendChild(floating_div);
function closeAlert(event){
let element = event.target; let element = event.target;
while(element.nodeName !== "BUTTON"){ while(element.nodeName !== "BUTTON"){
element = element.parentNode; element = element.parentNode;
} }
element.parentNode.parentNode.removeChild(element.parentNode); element.parentNode.parentNode.removeChild(element.parentNode);
} }
</script> </script>
{% endif %} {% endif %}

File diff suppressed because it is too large Load Diff

View File

@@ -6,25 +6,25 @@
{% if refresh %} {% if refresh %}
<meta http-equiv="refresh" content="{{ refresh }}"> <meta http-equiv="refresh" content="{{ refresh }}">
{% endif %} {% endif %}
<link type="text/css" media="all" href="/static/css/libs/flowbite.min.css" rel="stylesheet" /> <link type="text/css" media="all" href="/static/css/libs/flowbite.min.css" rel="stylesheet" />
<link type="text/css" media="all" href="/static/css/libs/tailwind.min.css" rel="stylesheet"> <link type="text/css" media="all" href="/static/css/libs/tailwind.min.css" rel="stylesheet">
<link type="text/css" media="all" href="/static/css/style.css" rel="stylesheet"> <link type="text/css" media="all" href="/static/css/style.css" rel="stylesheet">
<script src="/static/js/main.js"></script> <script src="/static/js/main.js"></script>
<script src="/static/js/libs/flowbite.js"></script> <script src="/static/js/libs/flowbite.js"></script>
<script> <script>
const isDarkMode = const isDarkMode =
localStorage.getItem('color-theme') === 'dark' || localStorage.getItem('color-theme') === 'dark' ||
(!localStorage.getItem('color-theme') && (!localStorage.getItem('color-theme') &&
window.matchMedia('(prefers-color-scheme: dark)').matches); window.matchMedia('(prefers-color-scheme: dark)').matches);
if (!localStorage.getItem('color-theme')) { if (!localStorage.getItem('color-theme')) {
localStorage.setItem('color-theme', isDarkMode ? 'dark' : 'light'); localStorage.setItem('color-theme', isDarkMode ? 'dark' : 'light');
} }
document.documentElement.classList.toggle('dark', isDarkMode); document.documentElement.classList.toggle('dark', isDarkMode);
</script> </script>
<link rel=icon sizes="32x32" type="image/png" href="/static/images/favicon/favicon-32.png"> <link rel=icon sizes="32x32" type="image/png" href="/static/images/favicon/favicon-32.png">
<title>(BSX) BasicSwap - v{{ version }}</title> <title>(BSX) BasicSwap - v{{ version }}</title>
</head> </head>
<body class="dark:bg-gray-700"> <body class="dark:bg-gray-700">
<section class="py-24 md:py-32"> <section class="py-24 md:py-32">
@@ -32,10 +32,10 @@
<div class="max-w-sm mx-auto"> <div class="max-w-sm mx-auto">
<div class="mb-3 text-center"> <div class="mb-3 text-center">
<a class="inline-block mb-6" href="#"> <a class="inline-block mb-6" href="#">
<img src="/static/images/logos/basicswap-logo.svg" class="h-20 imageshow dark-image"> <img src="/static/images/logos/basicswap-logo.svg" class="h-20 imageshow dark-image">
<img src="/static/images/logos/basicswap-logo-dark.svg" class="h-20 imageshow light-image"> <img src="/static/images/logos/basicswap-logo-dark.svg" class="h-20 imageshow light-image">
</a> </a>
<p class="text-lg text-coolGray-500 font-medium mb-6 dark:text-white" contenteditable="false">Unlock your wallets</p> <p class="text-lg text-coolGray-500 font-medium mb-6 dark:text-white">Unlock your wallets</p>
{% for m in messages %} {% for m in messages %}
<section class="py-4" id="messages_{{ m[0] }}" role="alert"> <section class="py-4" id="messages_{{ m[0] }}" role="alert">
<div class="container px-4 mx-auto"> <div class="container px-4 mx-auto">
@@ -77,7 +77,7 @@
</div> </div>
<form method="post" autocomplete="off"> <form method="post" autocomplete="off">
<div class="mb-4"> <div class="mb-4">
<label class="block mb-2 text-coolGray-800 font-medium dark:text-white" for="" contenteditable="false">Your Password</label> <label class="block mb-2 text-coolGray-800 font-medium dark:text-white" for="">Your Password</label>
<div class="relative w-full"> <div class="relative w-full">
<div class="absolute inset-y-0 right-0 flex items-center px-2"> <div class="absolute inset-y-0 right-0 flex items-center px-2">
<input class="hidden js-password-toggle" id="toggle" type="checkbox" /> <input class="hidden js-password-toggle" id="toggle" type="checkbox" />
@@ -94,41 +94,40 @@
</div> </div>
<button type="submit" name="unlock" value="Unlock" class="appearance-none focus:outline-none inline-block py-3 px-7 mb-6 w-full text-base text-blue-50 font-medium text-center leading-6 bg-blue-500 hover:bg-blue-600 focus:ring-0 rounded-md shadow-sm">Unlock</button> <button type="submit" name="unlock" value="Unlock" class="appearance-none focus:outline-none inline-block py-3 px-7 mb-6 w-full text-base text-blue-50 font-medium text-center leading-6 bg-blue-500 hover:bg-blue-600 focus:ring-0 rounded-md shadow-sm">Unlock</button>
<p class="text-center"> <p class="text-center">
<span class="text-xs font-medium dark:text-white" contenteditable="false">Need help?</span> <span class="text-xs font-medium dark:text-white">Need help?</span>
<a class="inline-block text-xs font-medium text-blue-500 hover:text-blue-600 hover:underline" href="https://academy.particl.io/en/latest/faq/get_support.html" target="_blank" contenteditable="false">Help / Tutorials</a> <a class="inline-block text-xs font-medium text-blue-500 hover:text-blue-600 hover:underline" href="https://academy.particl.io/en/latest/faq/get_support.html" target="_blank">Help / Tutorials</a>
</p> </p>
<p class="text-center"> <p class="text-center">
<span class="text-xs font-medium text-coolGray-500 dark:text-gray-500" contenteditable="false">{{ title }}</span> <span class="text-xs font-medium text-coolGray-500 dark:text-gray-500">{{ title }}</span>
</p> </p>
<input type="hidden" name="formid" value="{{ form_id }}"> <input type="hidden" name="formid" value="{{ form_id }}">
</form> </form>
</div> </div>
</div> </div>
</section> </section>
<script>
const passwordToggle = document.querySelector('.js-password-toggle')
passwordToggle.addEventListener('change', function() {
const password = document.querySelector('.js-password'),
passwordLabel = document.querySelector('.js-password-label')
if (password.type === 'password') {
password.type = 'text'
passwordLabel.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24"><g fill="#8896ab"><path d="M23.444,10.239a22.936,22.936,0,0,0-2.492-2.948l-4.021,4.021A5.026,5.026,0,0,1,17,12a5,5,0,0,1-5,5,5.026,5.026,0,0,1-.688-.069L8.055,20.188A10.286,10.286,0,0,0,12,21c5.708,0,9.905-5.062,11.445-7.24A3.058,3.058,0,0,0,23.444,10.239Z" fill="#8896ab"></path><path d="M12,3C6.292,3,2.1,8.062.555,10.24a3.058,3.058,0,0,0,0,3.52h0a21.272,21.272,0,0,0,4.784,4.9l3.124-3.124a5,5,0,0,1,7.071-7.072L8.464,15.536l10.2-10.2A11.484,11.484,0,0,0,12,3Z" fill="#8896ab"></path><path data-color="color-2" d="M1,24a1,1,0,0,1-.707-1.707l22-22a1,1,0,0,1,1.414,1.414l-22,22A1,1,0,0,1,1,24Z"></path></g></svg>'
} else {
password.type = 'password'
passwordLabel.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24"><g fill="#8896ab" ><path d="M23.444,10.239C21.905,8.062,17.708,3,12,3S2.1,8.062.555,10.24a3.058,3.058,0,0,0,0,3.52h0C2.1,15.938,6.292,21,12,21s9.905-5.062,11.445-7.24A3.058,3.058,0,0,0,23.444,10.239ZM12,17a5,5,0,1,1,5-5A5,5,0,0,1,12,17Z" fill="#8896ab"></path></g></svg>'
}
password.focus()
})
</script>
<script> <script>
window.onload = () => { document.addEventListener('DOMContentLoaded', () => {
toggleImages(); // Password toggle functionality
}; const passwordToggle = document.querySelector('.js-password-toggle');
if (passwordToggle) {
document.getElementById('theme-toggle').addEventListener('click', () => { passwordToggle.addEventListener('change', function() {
toggleImages(); const password = document.querySelector('.js-password');
}); const passwordLabel = document.querySelector('.js-password-label');
if (password && passwordLabel) {
if (password.type === 'password') {
password.type = 'text';
passwordLabel.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24"><g fill="#8896ab"><path d="M23.444,10.239a22.936,22.936,0,0,0-2.492-2.948l-4.021,4.021A5.026,5.026,0,0,1,17,12a5,5,0,0,1-5,5,5.026,5.026,0,0,1-.688-.069L8.055,20.188A10.286,10.286,0,0,0,12,21c5.708,0,9.905-5.062,11.445-7.24A3.058,3.058,0,0,0,23.444,10.239Z" fill="#8896ab"></path><path d="M12,3C6.292,3,2.1,8.062.555,10.24a3.058,3.058,0,0,0,0,3.52h0a21.272,21.272,0,0,0,4.784,4.9l3.124-3.124a5,5,0,0,1,7.071-7.072L8.464,15.536l10.2-10.2A11.484,11.484,0,0,0,12,3Z" fill="#8896ab"></path><path data-color="color-2" d="M1,24a1,1,0,0,1-.707-1.707l22-22a1,1,0,0,1,1.414,1.414l-22,22A1,1,0,0,1,1,24Z"></path></g></svg>';
} else {
password.type = 'password';
passwordLabel.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24"><g fill="#8896ab" ><path d="M23.444,10.239C21.905,8.062,17.708,3,12,3S2.1,8.062.555,10.24a3.058,3.058,0,0,0,0,3.52h0C2.1,15.938,6.292,21,12,21s9.905-5.062,11.445-7.24A3.058,3.058,0,0,0,23.444,10.239ZM12,17a5,5,0,1,1,5-5A5,5,0,0,1,12,17Z" fill="#8896ab"></path></g></svg>';
}
password.focus();
}
});
}
// Image toggling function
function toggleImages() { function toggleImages() {
const html = document.querySelector('html'); const html = document.querySelector('html');
const darkImages = document.querySelectorAll('.dark-image'); const darkImages = document.querySelectorAll('.dark-image');
@@ -148,17 +147,8 @@ passwordToggle.addEventListener('change', function() {
img.style.display = display; img.style.display = display;
}); });
} }
</script>
<script>
var themeToggleDarkIcon = document.getElementById('theme-toggle-dark-icon');
var themeToggleLightIcon = document.getElementById('theme-toggle-light-icon');
if (localStorage.getItem('color-theme') === 'dark' || (!('color-theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
themeToggleLightIcon.classList.remove('hidden');
} else {
themeToggleDarkIcon.classList.remove('hidden');
}
// Theme toggle functionality
function setTheme(theme) { function setTheme(theme) {
if (theme === 'light') { if (theme === 'light') {
document.documentElement.classList.remove('dark'); document.documentElement.classList.remove('dark');
@@ -169,17 +159,33 @@ passwordToggle.addEventListener('change', function() {
} }
} }
document.getElementById('theme-toggle').addEventListener('click', () => { // Initialize theme
if (localStorage.getItem('color-theme') === 'dark') { const themeToggle = document.getElementById('theme-toggle');
setTheme('light'); const themeToggleDarkIcon = document.getElementById('theme-toggle-dark-icon');
} else { const themeToggleLightIcon = document.getElementById('theme-toggle-light-icon');
setTheme('dark');
}
themeToggleDarkIcon.classList.toggle('hidden');
themeToggleLightIcon.classList.toggle('hidden');
toggleImages();
});
</script> if (themeToggle && themeToggleDarkIcon && themeToggleLightIcon) {
</body> if (localStorage.getItem('color-theme') === 'dark' || (!('color-theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
themeToggleLightIcon.classList.remove('hidden');
} else {
themeToggleDarkIcon.classList.remove('hidden');
}
themeToggle.addEventListener('click', () => {
if (localStorage.getItem('color-theme') === 'dark') {
setTheme('light');
} else {
setTheme('dark');
}
themeToggleDarkIcon.classList.toggle('hidden');
themeToggleLightIcon.classList.toggle('hidden');
toggleImages();
});
}
// Call toggleImages on load
toggleImages();
});
</script>
</body>
</html> </html>

View File

@@ -1,10 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2022 tecnovert # Copyright (c) 2022 tecnovert
# Distributed under the MIT software license, see the accompanying # Distributed under the MIT software license, see the accompanying
# file LICENSE or http://www.opensource.org/licenses/mit-license.php. # file LICENSE or http://www.opensource.org/licenses/mit-license.php.
def extract_data(bytes_in): def extract_data(bytes_in):
if bytes_in is None:
return None
str_in = bytes_in.decode('utf-8') str_in = bytes_in.decode('utf-8')
start = str_in.find('=') start = str_in.find('=')
if start < 0: if start < 0:
@@ -15,37 +16,29 @@ def extract_data(bytes_in):
return None return None
return str_in[start: end] return str_in[start: end]
def get_tor_established_state(swap_client): def get_tor_established_state(swap_client):
rv = swap_client.torControl('GETINFO status/circuit-established') rv = swap_client.torControl('GETINFO status/circuit-established')
return extract_data(rv) return extract_data(rv)
def page_tor(self, url_split, post_string): def page_tor(self, url_split, post_string):
swap_client = self.server.swap_client swap_client = self.server.swap_client
summary = swap_client.getSummary() summary = swap_client.getSummary()
page_data = {} page_data = {}
try: try:
page_data['circuit_established'] = get_tor_established_state(swap_client) page_data['circuit_established'] = get_tor_established_state(swap_client)
except Exception: except Exception:
page_data['circuit_established'] = 'error' page_data['circuit_established'] = 'error'
try: try:
rv = swap_client.torControl('GETINFO traffic/read') rv = swap_client.torControl('GETINFO traffic/read')
page_data['bytes_written'] = extract_data(rv) page_data['bytes_written'] = extract_data(rv)
except Exception: except Exception:
page_data['bytes_written'] = 'error' page_data['bytes_written'] = 'error'
try: try:
rv = swap_client.torControl('GETINFO traffic/written') rv = swap_client.torControl('GETINFO traffic/written')
page_data['bytes_read'] = extract_data(rv) page_data['bytes_read'] = extract_data(rv)
except Exception: except Exception:
page_data['bytes_read'] = 'error' page_data['bytes_read'] = 'error'
messages = [] messages = []
template = self.server.env.get_template('tor.html') template = self.server.env.get_template('tor.html')
return self.render_template(template, { return self.render_template(template, {
'messages': messages, 'messages': messages,