diff --git a/basicswap/basicswap.py b/basicswap/basicswap.py index 61f61be..a37e7eb 100644 --- a/basicswap/basicswap.py +++ b/basicswap/basicswap.py @@ -6178,17 +6178,35 @@ class BasicSwap(BaseApp): finally: self.mxDB.release() - def listAllSMSGAddresses(self, addr_id=None): - filters = '' - if addr_id is not None: - filters += f' WHERE addr_id = {addr_id} ' - self.mxDB.acquire() - try: - session = scoped_session(self.session_factory) - rv = [] - query_str = f'SELECT addr_id, addr, use_type, active_ind, created_at, note, pubkey FROM smsgaddresses {filters} ORDER BY created_at' + def listAllSMSGAddresses(self, filters={}): + query_str = 'SELECT addr_id, addr, use_type, active_ind, created_at, note, pubkey FROM smsgaddresses' + query_str += ' WHERE active_ind = 1 ' + query_data = {} - q = session.execute(query_str) + if 'addr_id' in filters: + query_str += ' AND addr_id = :addr_id ' + query_data['addr_id'] = filters['addr_id'] + if 'addressnote' in filters: + query_str += ' AND note LIKE :note ' + query_data['note'] = '%' + filters['addressnote'] + '%' + if 'addr_type' in filters and filters['addr_type'] > -1: + query_str += ' AND use_type = :addr_type ' + query_data['addr_type'] = filters['addr_type'] + + sort_dir = filters.get('sort_dir', 'DESC').upper() + sort_by = filters.get('sort_by', 'created_at') + query_str += f' ORDER BY {sort_by} {sort_dir}' + limit = filters.get('limit', None) + if limit is not None: + query_str += f' LIMIT {limit}' + offset = filters.get('offset', None) + if offset is not None: + query_str += f' OFFSET {offset}' + + try: + session = self.openSession() + rv = [] + q = session.execute(query_str, query_data) for row in q: rv.append({ 'id': row[0], @@ -6201,9 +6219,27 @@ class BasicSwap(BaseApp): }) return rv finally: - session.close() - session.remove() - self.mxDB.release() + self.closeSession(session, commit=False) + + def listSmsgAddresses(self, use_type_str): + if use_type_str == 'offer_send_from': + use_type = AddressTypes.OFFER + elif use_type_str == 'offer_send_to': + use_type = AddressTypes.SEND_OFFER + elif use_type_str == 'bid': + use_type = AddressTypes.BID + else: + raise ValueError('Unknown address type') + + try: + session = self.openSession() + rv = [] + q = session.execute('SELECT sa.addr, ki.label FROM smsgaddresses AS sa LEFT JOIN knownidentities AS ki ON sa.addr = ki.address WHERE sa.use_type = {} AND sa.active_ind = 1 ORDER BY sa.addr_id DESC'.format(use_type)) + for row in q: + rv.append((row[0], row[1])) + return rv + finally: + self.closeSession(session, commit=False) def listAutomationStrategies(self, filters={}): try: @@ -6341,29 +6377,6 @@ class BasicSwap(BaseApp): session.remove() self.mxDB.release() - def listSmsgAddresses(self, use_type_str): - if use_type_str == 'offer_send_from': - use_type = AddressTypes.OFFER - elif use_type_str == 'offer_send_to': - use_type = AddressTypes.SEND_OFFER - elif use_type_str == 'bid': - use_type = AddressTypes.BID - else: - raise ValueError('Unknown address type') - - self.mxDB.acquire() - try: - session = scoped_session(self.session_factory) - rv = [] - q = session.execute('SELECT sa.addr, ki.label FROM smsgaddresses AS sa LEFT JOIN knownidentities AS ki ON sa.addr = ki.address WHERE sa.use_type = {} AND sa.active_ind = 1 ORDER BY sa.addr_id DESC'.format(use_type)) - for row in q: - rv.append((row[0], row[1])) - return rv - finally: - session.close() - session.remove() - self.mxDB.release() - def createCoinALockRefundSwipeTx(self, ci, bid, offer, xmr_swap, xmr_offer): self.log.debug('Creating %s lock refund swipe tx', ci.coin_name()) diff --git a/basicswap/http_server.py b/basicswap/http_server.py index cc3e37a..206e519 100644 --- a/basicswap/http_server.py +++ b/basicswap/http_server.py @@ -17,7 +17,6 @@ from jinja2 import Environment, PackageLoader from . import __version__ from .util import ( dumpj, - ensure, toBool, LockedCoinError, format_timestamp, @@ -29,7 +28,6 @@ from .chainparams import ( from .basicswap_util import ( strTxState, strBidState, - strAddressType, ) from .js_server import ( @@ -56,21 +54,12 @@ from .ui.page_wallet import page_wallets, page_wallet from .ui.page_settings import page_settings from .ui.page_encryption import page_changepassword, page_unlock, page_lock from .ui.page_identity import page_identity +from .ui.page_smsgaddresses import page_smsgaddresses env = Environment(loader=PackageLoader('basicswap', 'templates')) env.filters['formatts'] = format_timestamp -def validateTextInput(text, name, messages, max_length=None): - if max_length is not None and len(text) > max_length: - messages.append(f'Error: {name} is too long') - return False - if len(text) > 0 and all(c.isalnum() or c.isspace() for c in text) is False: - messages.append(f'Error: {name} must consist of only letters and digits') - return False - return True - - def extractDomain(url): return url.split('://', 1)[1].split('/', 1)[0] @@ -396,82 +385,6 @@ class HttpHandler(BaseHTTPRequestHandler): 'summary': summary, }) - def page_smsgaddresses(self, url_split, post_string): - swap_client = self.server.swap_client - swap_client.checkSystemStatus() - summary = swap_client.getSummary() - - page_data = {} - messages = [] - err_messages = [] - smsgaddresses = [] - - listaddresses = True - form_data = self.checkForm(post_string, 'smsgaddresses', err_messages) - if form_data: - edit_address_id = None - for key in form_data: - if key.startswith(b'editaddr_'): - edit_address_id = int(key.split(b'_')[1]) - break - if edit_address_id is not None: - listaddresses = False - page_data['edit_address'] = edit_address_id - page_data['addr_data'] = swap_client.listAllSMSGAddresses(addr_id=edit_address_id)[0] - elif b'saveaddr' in form_data: - edit_address_id = int(form_data[b'edit_address_id'][0].decode('utf-8')) - edit_addr = form_data[b'edit_address'][0].decode('utf-8') - active_ind = int(form_data[b'active_ind'][0].decode('utf-8')) - ensure(active_ind in (0, 1), 'Invalid sort by') - addressnote = '' if b'addressnote' not in form_data else form_data[b'addressnote'][0].decode('utf-8') - if not validateTextInput(addressnote, 'Address note', messages, max_length=30): - listaddresses = False - page_data['edit_address'] = edit_address_id - else: - swap_client.editSMSGAddress(edit_addr, active_ind=active_ind, addressnote=addressnote) - messages.append(f'Edited address {edit_addr}') - elif b'shownewaddr' in form_data: - listaddresses = False - page_data['new_address'] = True - elif b'showaddaddr' in form_data: - listaddresses = False - page_data['new_send_address'] = True - elif b'createnewaddr' in form_data: - addressnote = '' if b'addressnote' not in form_data else form_data[b'addressnote'][0].decode('utf-8') - if not validateTextInput(addressnote, 'Address note', messages, max_length=30): - listaddresses = False - page_data['new_address'] = True - else: - new_addr, pubkey = swap_client.newSMSGAddress(addressnote=addressnote) - messages.append(f'Created address {new_addr}, pubkey {pubkey}') - elif b'createnewsendaddr' in form_data: - pubkey_hex = form_data[b'addresspubkey'][0].decode('utf-8') - addressnote = '' if b'addressnote' not in form_data else form_data[b'addressnote'][0].decode('utf-8') - if not validateTextInput(addressnote, 'Address note', messages, max_length=30) or \ - not validateTextInput(pubkey_hex, 'Pubkey', messages, max_length=66): - listaddresses = False - page_data['new_send_address'] = True - else: - new_addr = swap_client.addSMSGAddress(pubkey_hex, addressnote=addressnote) - messages.append(f'Added address {new_addr}') - - if listaddresses is True: - smsgaddresses = swap_client.listAllSMSGAddresses() - network_addr = swap_client.network_addr - - for addr in smsgaddresses: - addr['type'] = strAddressType(addr['type']) - - template = env.get_template('smsgaddresses.html') - return self.render_template(template, { - 'messages': messages, - 'err_messages': err_messages, - 'data': page_data, - 'smsgaddresses': smsgaddresses, - 'network_addr': network_addr, - 'summary': summary, - }) - def page_shutdown(self, url_split, post_string): swap_client = self.server.swap_client @@ -611,7 +524,7 @@ class HttpHandler(BaseHTTPRequestHandler): if page == 'watched': return self.page_watched(url_split, post_string) if page == 'smsgaddresses': - return self.page_smsgaddresses(url_split, post_string) + return page_smsgaddresses(self, url_split, post_string) if page == 'identity': return page_identity(self, url_split, post_string) if page == 'tor': diff --git a/basicswap/templates/smsgaddresses.html b/basicswap/templates/smsgaddresses.html index 4680989..391f239 100644 --- a/basicswap/templates/smsgaddresses.html +++ b/basicswap/templates/smsgaddresses.html @@ -97,7 +97,7 @@
- +
@@ -186,6 +186,85 @@
{% else %} +
+
+ + + + + + + + + + + + + + + + + + + + +
Filter
Sort by: + + + +
Note +
Type + +
+
+
+
+
+
+
+ +
+
+
+

Page: {{ filters.page_no }}

+
+
+
+ +
+
+ +
+
+ +
+
+
+
@@ -200,7 +279,7 @@ - +
{{ network_addr }}{{ page_data.network_addr }} Network Address @@ -233,6 +312,7 @@ + {% endif %} @@ -244,4 +324,4 @@ {% include 'footer.html' %} - \ No newline at end of file + diff --git a/basicswap/ui/page_smsgaddresses.py b/basicswap/ui/page_smsgaddresses.py new file mode 100644 index 0000000..d98244f --- /dev/null +++ b/basicswap/ui/page_smsgaddresses.py @@ -0,0 +1,128 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2023 tecnovert +# Distributed under the MIT software license, see the accompanying +# file LICENSE or http://www.opensource.org/licenses/mit-license.php. + + +from .util import ( + PAGE_LIMIT, + get_data_entry, + have_data_entry, + get_data_entry_or, + validateTextInput, + set_pagination_filters, +) +from basicswap.util import ( + ensure, +) +from basicswap.basicswap_util import ( + AddressTypes, + strAddressType, +) + + +def page_smsgaddresses(self, url_split, post_string): + swap_client = self.server.swap_client + swap_client.checkSystemStatus() + summary = swap_client.getSummary() + + filters = { + 'page_no': 1, + 'limit': PAGE_LIMIT, + 'sort_by': 'created_at', + 'sort_dir': 'desc', + 'addr_type': -1, + } + + page_data = {} + messages = [] + err_messages = [] + smsgaddresses = [] + + listaddresses = True + form_data = self.checkForm(post_string, 'smsgaddresses', err_messages) + if form_data: + edit_address_id = None + for key in form_data: + if key.startswith(b'editaddr_'): + edit_address_id = int(key.split(b'_')[1]) + break + if edit_address_id is not None: + listaddresses = False + page_data['edit_address'] = edit_address_id + page_data['addr_data'] = swap_client.listAllSMSGAddresses({'addr_id': edit_address_id})[0] + elif have_data_entry(form_data, 'saveaddr'): + edit_address_id = int(get_data_entry(form_data, 'edit_address_id')) + edit_addr = get_data_entry(form_data, 'edit_address') + active_ind = int(get_data_entry(form_data, 'active_ind')) + ensure(active_ind in (0, 1), 'Invalid sort by') + addressnote = get_data_entry_or(form_data, 'addressnote', '') + if not validateTextInput(addressnote, 'Address note', err_messages, max_length=30): + listaddresses = False + page_data['edit_address'] = edit_address_id + else: + swap_client.editSMSGAddress(edit_addr, active_ind=active_ind, addressnote=addressnote) + messages.append(f'Edited address {edit_addr}') + elif have_data_entry(form_data, 'shownewaddr'): + listaddresses = False + page_data['new_address'] = True + elif have_data_entry(form_data, 'showaddaddr'): + listaddresses = False + page_data['new_send_address'] = True + elif have_data_entry(form_data, 'createnewaddr'): + addressnote = get_data_entry_or(form_data, 'addressnote', '') + if not validateTextInput(addressnote, 'Address note', err_messages, max_length=30): + listaddresses = False + page_data['new_address'] = True + else: + new_addr, pubkey = swap_client.newSMSGAddress(addressnote=addressnote) + messages.append(f'Created address {new_addr}, pubkey {pubkey}') + elif have_data_entry(form_data, 'createnewsendaddr'): + pubkey_hex = get_data_entry(form_data, 'addresspubkey') + addressnote = get_data_entry_or(form_data, 'addressnote', '') + if not validateTextInput(addressnote, 'Address note', messages, max_length=30) or \ + not validateTextInput(pubkey_hex, 'Pubkey', messages, max_length=66): + listaddresses = False + page_data['new_send_address'] = True + else: + new_addr = swap_client.addSMSGAddress(pubkey_hex, addressnote=addressnote) + messages.append(f'Added address {new_addr}') + + if not have_data_entry(form_data, 'clearfilters'): + if have_data_entry(form_data, 'sort_by'): + sort_by = get_data_entry(form_data, 'sort_by') + ensure(sort_by in ['created_at', 'rate'], 'Invalid sort by') + filters['sort_by'] = sort_by + if have_data_entry(form_data, 'sort_dir'): + sort_dir = get_data_entry(form_data, 'sort_dir') + ensure(sort_dir in ['asc', 'desc'], 'Invalid sort dir') + filters['sort_dir'] = sort_dir + if have_data_entry(form_data, 'filter_addressnote'): + addressnote = get_data_entry(form_data, 'filter_addressnote') + if validateTextInput(addressnote, 'Address note', err_messages, max_length=30): + filters['addressnote'] = addressnote + if have_data_entry(form_data, 'filter_addr_type'): + filters['addr_type'] = int(get_data_entry(form_data, 'filter_addr_type')) + + set_pagination_filters(form_data, filters) + + if listaddresses is True: + smsgaddresses = swap_client.listAllSMSGAddresses(filters) + + page_data['addr_types'] = [(int(t), strAddressType(t)) for t in AddressTypes] + page_data['network_addr'] = swap_client.network_addr + + for addr in smsgaddresses: + addr['type'] = strAddressType(addr['type']) + + template = self.server.env.get_template('smsgaddresses.html') + return self.render_template(template, { + 'messages': messages, + 'err_messages': err_messages, + 'filters': filters, + 'data': page_data, + 'smsgaddresses': smsgaddresses, + 'page_data': page_data, + 'summary': summary, + }) diff --git a/basicswap/ui/util.py b/basicswap/ui/util.py index e676f56..135202e 100644 --- a/basicswap/ui/util.py +++ b/basicswap/ui/util.py @@ -446,3 +446,13 @@ def checkAddressesOwned(swap_client, ci, wallet_info): wallet_info['deposit_address'] = 'Error: unowned address' elif swap_client._restrict_unknown_seed_wallets and not ci.knownWalletSeed(): wallet_info['deposit_address'] = 'WARNING: Unknown wallet seed' + + +def validateTextInput(text, name, messages, max_length=None): + if max_length is not None and len(text) > max_length: + messages.append(f'Error: {name} is too long') + return False + if len(text) > 0 and all(c.isalnum() or c.isspace() for c in text) is False: + messages.append(f'Error: {name} must consist of only letters and digits') + return False + return True diff --git a/doc/release-notes.md b/doc/release-notes.md index 5b13a8f..e7974ef 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -7,6 +7,7 @@ - Accepted bids will timeout if the peer does not respond within an hour after the bid expires. - Ensure messages are always sent from and to the expected addresses. +- ui: Add pagination and filters to smsgaddresses page 0.0.59