mirror of
https://github.com/basicswap/basicswap.git
synced 2025-01-31 06:45:51 +00:00
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:
parent
003d7b85ab
commit
b6d29a33d2
8 changed files with 1725 additions and 1423 deletions
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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-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://basicswapdex.com/terms" target="_blank">Terms and Conditions</a></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-1/4 px-4"> </div>
|
||||
|
|
|
@ -519,38 +519,55 @@
|
|||
</section>
|
||||
{% if ws_url %}
|
||||
<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.classList.add('floatright');
|
||||
messages = document.createElement('ul');
|
||||
messages.setAttribute('id', 'ul_updates');
|
||||
ws.onmessage = function(event) {
|
||||
let json = JSON.parse(event.data);
|
||||
let event_message = 'Unknown event';
|
||||
if (json['event'] == 'new_offer') {
|
||||
floating_div.classList.add('floatright');
|
||||
messages = document.createElement('ul');
|
||||
messages.setAttribute('id', 'ul_updates');
|
||||
|
||||
floating_div.appendChild(messages);
|
||||
|
||||
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>';
|
||||
}
|
||||
else
|
||||
if (json['event'] == 'new_bid') {
|
||||
should_display = true;
|
||||
}
|
||||
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>';
|
||||
}
|
||||
else
|
||||
if (json['event'] == 'bid_accepted') {
|
||||
should_display = true;
|
||||
}
|
||||
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>';
|
||||
}
|
||||
let messages = document.getElementById('ul_updates'),
|
||||
message = document.createElement('li');
|
||||
message.innerHTML = event_message;
|
||||
messages.appendChild(message);
|
||||
};
|
||||
floating_div.appendChild(messages);
|
||||
document.body.appendChild(floating_div);
|
||||
function closeAlert(event){
|
||||
should_display = true;
|
||||
}
|
||||
|
||||
if (should_display) {
|
||||
let messages = document.getElementById('ul_updates'),
|
||||
message = document.createElement('li');
|
||||
message.innerHTML = event_message;
|
||||
messages.appendChild(message);
|
||||
}
|
||||
};
|
||||
|
||||
document.body.appendChild(floating_div);
|
||||
|
||||
function closeAlert(event){
|
||||
let element = event.target;
|
||||
while(element.nodeName !== "BUTTON"){
|
||||
element = element.parentNode;
|
||||
element = element.parentNode;
|
||||
}
|
||||
element.parentNode.parentNode.removeChild(element.parentNode);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endif %}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -6,25 +6,25 @@
|
|||
{% if refresh %}
|
||||
<meta http-equiv="refresh" content="{{ refresh }}">
|
||||
{% 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/tailwind.min.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/libs/flowbite.js"></script>
|
||||
<script>
|
||||
const isDarkMode =
|
||||
localStorage.getItem('color-theme') === 'dark' ||
|
||||
(!localStorage.getItem('color-theme') &&
|
||||
window.matchMedia('(prefers-color-scheme: dark)').matches);
|
||||
<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/style.css" rel="stylesheet">
|
||||
<script src="/static/js/main.js"></script>
|
||||
<script src="/static/js/libs/flowbite.js"></script>
|
||||
<script>
|
||||
const isDarkMode =
|
||||
localStorage.getItem('color-theme') === 'dark' ||
|
||||
(!localStorage.getItem('color-theme') &&
|
||||
window.matchMedia('(prefers-color-scheme: dark)').matches);
|
||||
|
||||
if (!localStorage.getItem('color-theme')) {
|
||||
localStorage.setItem('color-theme', isDarkMode ? 'dark' : 'light');
|
||||
}
|
||||
if (!localStorage.getItem('color-theme')) {
|
||||
localStorage.setItem('color-theme', isDarkMode ? 'dark' : 'light');
|
||||
}
|
||||
|
||||
document.documentElement.classList.toggle('dark', isDarkMode);
|
||||
</script>
|
||||
document.documentElement.classList.toggle('dark', isDarkMode);
|
||||
</script>
|
||||
<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>
|
||||
<body class="dark:bg-gray-700">
|
||||
<section class="py-24 md:py-32">
|
||||
|
@ -32,10 +32,10 @@
|
|||
<div class="max-w-sm mx-auto">
|
||||
<div class="mb-3 text-center">
|
||||
<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-dark.svg" class="h-20 imageshow light-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">
|
||||
</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 %}
|
||||
<section class="py-4" id="messages_{{ m[0] }}" role="alert">
|
||||
<div class="container px-4 mx-auto">
|
||||
|
@ -77,7 +77,7 @@
|
|||
</div>
|
||||
<form method="post" autocomplete="off">
|
||||
<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="absolute inset-y-0 right-0 flex items-center px-2">
|
||||
<input class="hidden js-password-toggle" id="toggle" type="checkbox" />
|
||||
|
@ -94,41 +94,40 @@
|
|||
</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>
|
||||
<p class="text-center">
|
||||
<span class="text-xs font-medium dark:text-white" contenteditable="false">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>
|
||||
<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">Help / Tutorials</a>
|
||||
</p>
|
||||
<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>
|
||||
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
window.onload = () => {
|
||||
toggleImages();
|
||||
};
|
||||
|
||||
document.getElementById('theme-toggle').addEventListener('click', () => {
|
||||
toggleImages();
|
||||
});
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Password toggle functionality
|
||||
const passwordToggle = document.querySelector('.js-password-toggle');
|
||||
if (passwordToggle) {
|
||||
passwordToggle.addEventListener('change', function() {
|
||||
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() {
|
||||
const html = document.querySelector('html');
|
||||
const darkImages = document.querySelectorAll('.dark-image');
|
||||
|
@ -148,17 +147,8 @@ passwordToggle.addEventListener('change', function() {
|
|||
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) {
|
||||
if (theme === 'light') {
|
||||
document.documentElement.classList.remove('dark');
|
||||
|
@ -169,17 +159,33 @@ passwordToggle.addEventListener('change', function() {
|
|||
}
|
||||
}
|
||||
|
||||
document.getElementById('theme-toggle').addEventListener('click', () => {
|
||||
if (localStorage.getItem('color-theme') === 'dark') {
|
||||
setTheme('light');
|
||||
} else {
|
||||
setTheme('dark');
|
||||
}
|
||||
themeToggleDarkIcon.classList.toggle('hidden');
|
||||
themeToggleLightIcon.classList.toggle('hidden');
|
||||
toggleImages();
|
||||
});
|
||||
// Initialize theme
|
||||
const themeToggle = document.getElementById('theme-toggle');
|
||||
const themeToggleDarkIcon = document.getElementById('theme-toggle-dark-icon');
|
||||
const themeToggleLightIcon = document.getElementById('theme-toggle-light-icon');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
if (themeToggle && themeToggleDarkIcon && themeToggleLightIcon) {
|
||||
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>
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2022 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
def extract_data(bytes_in):
|
||||
if bytes_in is None:
|
||||
return None
|
||||
str_in = bytes_in.decode('utf-8')
|
||||
start = str_in.find('=')
|
||||
if start < 0:
|
||||
|
@ -15,37 +16,29 @@ def extract_data(bytes_in):
|
|||
return None
|
||||
return str_in[start: end]
|
||||
|
||||
|
||||
def get_tor_established_state(swap_client):
|
||||
rv = swap_client.torControl('GETINFO status/circuit-established')
|
||||
return extract_data(rv)
|
||||
|
||||
|
||||
def page_tor(self, url_split, post_string):
|
||||
swap_client = self.server.swap_client
|
||||
summary = swap_client.getSummary()
|
||||
|
||||
page_data = {}
|
||||
|
||||
try:
|
||||
page_data['circuit_established'] = get_tor_established_state(swap_client)
|
||||
except Exception:
|
||||
page_data['circuit_established'] = 'error'
|
||||
|
||||
try:
|
||||
rv = swap_client.torControl('GETINFO traffic/read')
|
||||
page_data['bytes_written'] = extract_data(rv)
|
||||
except Exception:
|
||||
page_data['bytes_written'] = 'error'
|
||||
|
||||
try:
|
||||
rv = swap_client.torControl('GETINFO traffic/written')
|
||||
page_data['bytes_read'] = extract_data(rv)
|
||||
except Exception:
|
||||
page_data['bytes_read'] = 'error'
|
||||
|
||||
messages = []
|
||||
|
||||
template = self.server.env.get_template('tor.html')
|
||||
return self.render_template(template, {
|
||||
'messages': messages,
|
||||
|
|
Loading…
Reference in a new issue