diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 3956036a..a07fd9e7 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -63,6 +63,7 @@ services: - sqk_dir:/root/.sqk ports: - 8774:8774 + - 5000:5000 links: - "btcd:btcd" - "lnd:lnd" diff --git a/docker/frontend/Dockerfile b/docker/frontend/Dockerfile index 80f3654c..03b3c5e9 100644 --- a/docker/frontend/Dockerfile +++ b/docker/frontend/Dockerfile @@ -1,9 +1,17 @@ -FROM node:10-stretch +#FROM node:10-stretch +FROM ubuntu:20.04 ARG PORT +ENV DEBIAN_FRONTEND=noninteractive + RUN apt-get -qq update && apt-get -qq install -y \ - unzip + curl \ + unzip \ + nodejs \ + npm \ + git \ + git-core RUN npm install -g serve diff --git a/docker/sqkserver/Dockerfile b/docker/sqkserver/Dockerfile index e57ddc46..22eb1709 100644 --- a/docker/sqkserver/Dockerfile +++ b/docker/sqkserver/Dockerfile @@ -21,12 +21,17 @@ COPY squeakserver ./squeakserver COPY proto ./proto COPY setup.py setup.cfg MANIFEST.in README.md ./ +# Copy the frontend code +COPY frontend/squeak-node-frontend/build/ ./static/build + RUN python3 setup.py install EXPOSE 8774 EXPOSE 8994 EXPOSE 18774 EXPOSE 18994 +# Web server +EXPOSE 5000 # Copy the entrypoint script. COPY "docker/sqkserver/start-sqkserver.sh" . diff --git a/frontend/squeak-node-frontend/package.json b/frontend/squeak-node-frontend/package.json index e867e81e..f831d184 100644 --- a/frontend/squeak-node-frontend/package.json +++ b/frontend/squeak-node-frontend/package.json @@ -19,6 +19,7 @@ "grpc-web": "~1.2.0", "moment": "^2.27.0", "mui-datatables": "^2.6.4", + "protobufjs": "^6.10.1", "react": "^16.8.6", "react-apexcharts": "^1.3.3", "react-dom": "^16.8.6", diff --git a/frontend/squeak-node-frontend/src/squeakclient/requests.js b/frontend/squeak-node-frontend/src/squeakclient/requests.js index f4048d68..3d53b6e8 100644 --- a/frontend/squeak-node-frontend/src/squeakclient/requests.js +++ b/frontend/squeak-node-frontend/src/squeakclient/requests.js @@ -14,6 +14,12 @@ import { CloseChannelRequest, ChannelPoint, NewAddressRequest, + GetInfoResponse, + WalletBalanceResponse, + TransactionDetails, + ListPeersResponse, + ListChannelsResponse, + PendingChannelsResponse, } from "../proto/lnd_pb" import { GetSqueakProfileRequest, @@ -41,101 +47,211 @@ import { DeletePeerRequest, DeleteSqueakProfileRequest, DeleteSqueakRequest, + GetFollowedSqueakDisplaysReply, + SetSqueakProfileSharingReply, + GetSqueakProfileReply, + SetSqueakProfileFollowingReply, + SetSqueakProfileWhitelistedReply, } from "../proto/squeak_admin_pb" +function makeRequest(route, request, handleResponse) { + fetch('http://localhost:5000/' + route, { + method: 'post', + body: request.serializeBinary() + }).then(function(response) { + return response.arrayBuffer(); + }).then(function(data) { + handleResponse(data); + }); +} + +// export function getFollowedSqueakDisplaysRequest(handleResponse) { +// var request = new GetFollowedSqueakDisplaysRequest(); +// client.getFollowedSqueakDisplays(request, {}, (err, response) => { +// handleResponse(response.getSqueakDisplayEntriesList()) +// }); +// }; + export function getFollowedSqueakDisplaysRequest(handleResponse) { var request = new GetFollowedSqueakDisplaysRequest(); - client.getFollowedSqueakDisplays(request, {}, (err, response) => { - handleResponse(response.getSqueakDisplayEntriesList()) + makeRequest('getfollowedsqueakdisplays', request, (data) => { + var response = GetFollowedSqueakDisplaysReply.deserializeBinary(data); + handleResponse(response.getSqueakDisplayEntriesList()); }); }; +// export function lndGetInfoRequest(handleResponse) { +// var request = new GetInfoRequest(); +// client.lndGetInfo(request, {}, (err, response) => { +// handleResponse(response); +// }); +// }; export function lndGetInfoRequest(handleResponse) { var request = new GetInfoRequest(); - client.lndGetInfo(request, {}, (err, response) => { + makeRequest('lndgetinfo', request, (data) => { + var response = GetInfoResponse.deserializeBinary(data); handleResponse(response); }); }; +// export function lndWalletBalanceRequest(handleResponse) { +// var request = new WalletBalanceRequest(); +// client.lndWalletBalance(request, {}, (err, response) => { +// handleResponse(response); +// }); +// }; + export function lndWalletBalanceRequest(handleResponse) { - var request = new WalletBalanceRequest(); - client.lndWalletBalance(request, {}, (err, response) => { - handleResponse(response); - }); + var request = new WalletBalanceRequest(); + makeRequest('lndwalletbalance', request, (data) => { + var response = WalletBalanceResponse.deserializeBinary(data); + handleResponse(response); + }); }; +// export function lndGetTransactionsRequest(handleResponse) { +// var request = new GetTransactionsRequest(); +// client.lndGetTransactions(request, {}, (err, response) => { +// handleResponse(response.getTransactionsList()); +// }); +// }; + export function lndGetTransactionsRequest(handleResponse) { - var request = new GetTransactionsRequest(); - client.lndGetTransactions(request, {}, (err, response) => { - handleResponse(response.getTransactionsList()); - }); + var request = new GetTransactionsRequest(); + makeRequest('lndgettransactions', request, (data) => { + var response = TransactionDetails.deserializeBinary(data); + handleResponse(response.getTransactionsList()); + }); }; +// export function lndListPeersRequest(handleResponse) { +// var request = new ListPeersRequest(); +// client.lndListPeers(request, {}, (err, response) => { +// handleResponse(response.getPeersList()); +// }); +// }; + export function lndListPeersRequest(handleResponse) { var request = new ListPeersRequest(); - client.lndListPeers(request, {}, (err, response) => { + makeRequest('lndlistpeers', request, (data) => { + var response = ListPeersResponse.deserializeBinary(data); handleResponse(response.getPeersList()); }); }; +// export function lndListChannelsRequest(handleResponse) { +// var request = new ListChannelsRequest(); +// client.lndListChannels(request, {}, (err, response) => { +// if (err) { +// return; +// } +// handleResponse(response.getChannelsList()); +// }); +// }; + export function lndListChannelsRequest(handleResponse) { - var request = new ListChannelsRequest(); - client.lndListChannels(request, {}, (err, response) => { - if (err) { - return; - } - handleResponse(response.getChannelsList()); - }); + var request = new ListChannelsRequest(); + makeRequest('lndlistchannels', request, (data) => { + var response = ListChannelsResponse.deserializeBinary(data); + handleResponse(response.getChannelsList()); + }); }; +// export function lndPendingChannelsRequest(handleResponse) { +// var request = new PendingChannelsRequest(); +// client.lndPendingChannels(request, {}, (err, response) => { +// if (err) { +// return; +// } +// handleResponse(response); +// }); +// }; + export function lndPendingChannelsRequest(handleResponse) { - var request = new PendingChannelsRequest(); - client.lndPendingChannels(request, {}, (err, response) => { - if (err) { - return; - } - handleResponse(response); - }); + var request = new PendingChannelsRequest(); + makeRequest('lndpendingchannels', request, (data) => { + var response = PendingChannelsResponse.deserializeBinary(data); + handleResponse(response); + }); }; +// export function getSqueakProfileRequest(id, handleResponse) { +// var request = new GetSqueakProfileRequest(); +// request.setProfileId(id); +// client.getSqueakProfile(request, {}, (err, response) => { +// if (err) { +// return; +// } +// handleResponse(response.getSqueakProfile()) +// }); +// }; + export function getSqueakProfileRequest(id, handleResponse) { - var request = new GetSqueakProfileRequest(); - request.setProfileId(id); - client.getSqueakProfile(request, {}, (err, response) => { - if (err) { - return; - } - handleResponse(response.getSqueakProfile()) - }); + var request = new GetSqueakProfileRequest(); + request.setProfileId(id); + makeRequest('getsqueakprofile', request, (data) => { + var response = GetSqueakProfileReply.deserializeBinary(data); + handleResponse(response.getSqueakProfile()); + }); }; +// export function setSqueakProfileFollowingRequest(id, following, handleResponse) { +// var request = new SetSqueakProfileFollowingRequest(); +// request.setProfileId(id); +// request.setFollowing(following); +// client.setSqueakProfileFollowing(request, {}, (err, response) => { +// handleResponse(response); +// }); +// }; + export function setSqueakProfileFollowingRequest(id, following, handleResponse) { - var request = new SetSqueakProfileFollowingRequest(); - request.setProfileId(id); - request.setFollowing(following); - client.setSqueakProfileFollowing(request, {}, (err, response) => { - handleResponse(response); - }); + var request = new SetSqueakProfileFollowingRequest(); + request.setProfileId(id); + request.setFollowing(following); + makeRequest('setsqueakprofilefollowing', request, (data) => { + var response = SetSqueakProfileFollowingReply.deserializeBinary(data); + handleResponse(response); + }); }; +// export function setSqueakProfileSharingRequest(id, sharing, handleResponse) { +// var request = new SetSqueakProfileSharingRequest(); +// request.setProfileId(id); +// request.setSharing(sharing); +// client.setSqueakProfileSharing(request, {}, (err, response) => { +// handleResponse(response); +// }); +// }; + export function setSqueakProfileSharingRequest(id, sharing, handleResponse) { - var request = new SetSqueakProfileSharingRequest(); - request.setProfileId(id); - request.setSharing(sharing); - client.setSqueakProfileSharing(request, {}, (err, response) => { - handleResponse(response); - }); + var request = new SetSqueakProfileSharingRequest(); + request.setProfileId(id); + request.setSharing(sharing); + makeRequest('setsqueakprofilesharing', request, (data) => { + var response = SetSqueakProfileSharingReply.deserializeBinary(data); + handleResponse(response); + }); }; +// export function setSqueakProfileWhitelistedRequest(id, whitelisted, handleResponse) { +// var request = new SetSqueakProfileWhitelistedRequest() +// request.setProfileId(id); +// request.setWhitelisted(whitelisted); +// client.setSqueakProfileWhitelisted(request, {}, (err, response) => { +// handleResponse(response); +// }); +// }; + export function setSqueakProfileWhitelistedRequest(id, whitelisted, handleResponse) { - var request = new SetSqueakProfileWhitelistedRequest() - request.setProfileId(id); - request.setWhitelisted(whitelisted); - client.setSqueakProfileWhitelisted(request, {}, (err, response) => { - handleResponse(response); - }); + var request = new SetSqueakProfileWhitelistedRequest() + request.setProfileId(id); + request.setWhitelisted(whitelisted); + makeRequest('setsqueakprofilewhitelisted', request, (data) => { + var response = SetSqueakProfileWhitelistedReply.deserializeBinary(data); + handleResponse(response); + }); }; export function lndConnectPeerRequest(pubkey, host, handleResponse) { diff --git a/requirements.txt b/requirements.txt index 12063f9c..6d90706f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,5 @@ grpcio-tools psycopg2 requests SQLAlchemy +Flask +protobuf diff --git a/squeakserver/admin/squeak_admin_server_handler.py b/squeakserver/admin/squeak_admin_server_handler.py index 22cd44d3..2b4da6d3 100644 --- a/squeakserver/admin/squeak_admin_server_handler.py +++ b/squeakserver/admin/squeak_admin_server_handler.py @@ -1,7 +1,17 @@ +import sys import logging from squeakserver.common.lnd_lightning_client import LNDLightningClient from squeakserver.node.squeak_node import SqueakNode +from squeakserver.server.util import get_hash, get_replyto +from squeakserver.admin.util import squeak_entry_to_message +from squeakserver.admin.util import squeak_peer_to_message +from squeakserver.admin.util import squeak_profile_to_message +from squeakserver.admin.util import offer_entry_to_message +from squeakserver.admin.util import sent_payment_to_message + +from proto import squeak_admin_pb2, squeak_admin_pb2_grpc + logger = logging.getLogger(__name__) @@ -78,48 +88,69 @@ class SqueakAdminServerHandler(object): logger.info("Handle subscribe channel events") return self.lightning_client.subscribe_channel_events() - def handle_create_signing_profile(self, profile_name): + def handle_create_signing_profile(self, request): + profile_name = request.profile_name logger.info("Handle create signing profile with name: {}".format(profile_name)) profile_id = self.squeak_node.create_signing_profile(profile_name) logger.info("New profile_id: {}".format(profile_id)) - return profile_id - - def handle_create_contact_profile(self, profile_name, squeak_address): - logger.info( - "Handle create contact profile with name: {}, address: {}".format( - profile_name, squeak_address - ) + return squeak_admin_pb2.CreateSigningProfileReply( + profile_id=profile_id, ) + + def handle_create_contact_profile(self, request): + profile_name = request.profile_name + squeak_address = request.address + logger.info("Handle create contact profile with name: {}, address: {}".format( + profile_name, + squeak_address, + )) profile_id = self.squeak_node.create_contact_profile( profile_name, squeak_address ) logger.info("New profile_id: {}".format(profile_id)) - return profile_id + return squeak_admin_pb2.CreateContactProfileReply( + profile_id=profile_id, + ) - def handle_get_signing_profiles(self): + def handle_get_signing_profiles(self, request): logger.info("Handle get signing profiles.") profiles = self.squeak_node.get_signing_profiles() logger.info("Got number of signing profiles: {}".format(len(profiles))) - return profiles + profile_msgs = [ + squeak_profile_to_message(profile) for profile in profiles + ] + return squeak_admin_pb2.GetSigningProfilesReply(squeak_profiles=profile_msgs) - def handle_get_contact_profiles(self): + def handle_get_contact_profiles(self, request): logger.info("Handle get contact profiles.") profiles = self.squeak_node.get_contact_profiles() logger.info("Got number of contact profiles: {}".format(len(profiles))) - return profiles + profile_msgs = [ + squeak_profile_to_message(profile) for profile in profiles + ] + return squeak_admin_pb2.GetContactProfilesReply(squeak_profiles=profile_msgs) - def handle_get_squeak_profile(self, profile_id): + def handle_get_squeak_profile(self, request): + profile_id = request.profile_id logger.info("Handle get squeak profile with id: {}".format(profile_id)) squeak_profile = self.squeak_node.get_squeak_profile(profile_id) - return squeak_profile + if squeak_profile is None: + return None + squeak_profile_msg = squeak_profile_to_message(squeak_profile) + return squeak_admin_pb2.GetSqueakProfileReply( + squeak_profile=squeak_profile_msg, + ) - def handle_get_squeak_profile_by_address(self, address): + def handle_get_squeak_profile_by_address(self, request): + address = request.address logger.info("Handle get squeak profile with address: {}".format(address)) squeak_profile = self.squeak_node.get_squeak_profile_by_address(address) - logger.info("Got squeak profile by address: {}".format(squeak_profile)) - return squeak_profile + squeak_profile_msg = squeak_profile_to_message(squeak_profile) + return squeak_admin_pb2.GetSqueakProfileReply(squeak_profile=squeak_profile_msg) - def handle_set_squeak_profile_whitelisted(self, profile_id, whitelisted): + def handle_set_squeak_profile_whitelisted(self, request): + profile_id = request.profile_id + whitelisted = request.whitelisted logger.info( "Handle set squeak profile whitelisted with profile id: {}, whitelisted: {}".format( profile_id, @@ -127,8 +158,11 @@ class SqueakAdminServerHandler(object): ) ) self.squeak_node.set_squeak_profile_whitelisted(profile_id, whitelisted) + return squeak_admin_pb2.SetSqueakProfileWhitelistedReply() - def handle_set_squeak_profile_following(self, profile_id, following): + def handle_set_squeak_profile_following(self, request): + profile_id = request.profile_id + following = request.following logger.info( "Handle set squeak profile following with profile id: {}, following: {}".format( profile_id, @@ -136,8 +170,11 @@ class SqueakAdminServerHandler(object): ) ) self.squeak_node.set_squeak_profile_following(profile_id, following) + return squeak_admin_pb2.SetSqueakProfileFollowingReply() - def handle_set_squeak_profile_sharing(self, profile_id, sharing): + def handle_set_squeak_profile_sharing(self, request): + profile_id = request.profile_id + sharing = request.sharing logger.info( "Handle set squeak profile sharing with profile id: {}, sharing: {}".format( profile_id, @@ -145,31 +182,41 @@ class SqueakAdminServerHandler(object): ) ) self.squeak_node.set_squeak_profile_sharing(profile_id, sharing) + return squeak_admin_pb2.SetSqueakProfileSharingReply() - def handle_delete_squeak_profile(self, profile_id): + def handle_delete_squeak_profile(self, request): + profile_id = request.profile_id logger.info("Handle delete squeak profile with id: {}".format(profile_id)) self.squeak_node.delete_squeak_profile(profile_id) + return squeak_admin_pb2.DeleteSqueakProfileReply() - def handle_make_squeak(self, profile_id, content_str, replyto_hash): + def handle_make_squeak(self, request): + profile_id = request.profile_id + content_str = request.content + replyto_hash_str = request.replyto + replyto_hash = bytes.fromhex(replyto_hash_str) if replyto_hash_str else None logger.info("Handle make squeak profile with id: {}".format(profile_id)) inserted_squeak_hash = self.squeak_node.make_squeak( profile_id, content_str, replyto_hash ) - return inserted_squeak_hash + squeak_hash_str = inserted_squeak_hash.hex() + return squeak_admin_pb2.MakeSqueakReply( + squeak_hash=squeak_hash_str, + ) - def handle_get_squeak_display_entry(self, squeak_hash): + def handle_get_squeak_display_entry(self, request): + squeak_hash_str = request.squeak_hash + squeak_hash = bytes.fromhex(squeak_hash_str) logger.info("Handle get squeak display entry for hash: {}".format(squeak_hash)) squeak_entry_with_profile = self.squeak_node.get_squeak_entry_with_profile( squeak_hash ) - logger.info( - "Got squeak entry with profile for hash: {}".format( - squeak_entry_with_profile - ) + display_message = squeak_entry_to_message(squeak_entry_with_profile) + return squeak_admin_pb2.GetSqueakDisplayReply( + squeak_display_entry=display_message ) - return squeak_entry_with_profile - def handle_get_followed_squeak_display_entries(self): + def handle_get_followed_squeak_display_entries(self, request): logger.info("Handle get followed squeak display entries.") squeak_entries_with_profile = ( self.squeak_node.get_followed_squeak_entries_with_profile() @@ -179,11 +226,18 @@ class SqueakAdminServerHandler(object): len(squeak_entries_with_profile) ) ) - return squeak_entries_with_profile + squeak_display_msgs = [ + squeak_entry_to_message(entry) + for entry in squeak_entries_with_profile + ] + return squeak_admin_pb2.GetFollowedSqueakDisplaysReply( + squeak_display_entries=squeak_display_msgs + ) - def handle_get_squeak_display_entries_for_address( - self, address, min_block, max_block - ): + def handle_get_squeak_display_entries_for_address(self, request): + address = request.address + min_block = 0 + max_block = sys.maxsize logger.info("Handle get squeak display entries for address: {}".format(address)) squeak_entries_with_profile = ( self.squeak_node.get_squeak_entries_with_profile_for_address( @@ -197,9 +251,16 @@ class SqueakAdminServerHandler(object): len(squeak_entries_with_profile) ) ) - return squeak_entries_with_profile + squeak_display_msgs = [ + squeak_entry_to_message(entry) + for entry in squeak_entries_with_profile + ] + return squeak_admin_pb2.GetAddressSqueakDisplaysReply( + squeak_display_entries=squeak_display_msgs + ) - def handle_get_ancestor_squeak_display_entries(self, squeak_hash_str): + def handle_get_ancestor_squeak_display_entries(self, request): + squeak_hash_str = request.squeak_hash logger.info( "Handle get ancestor squeak display entries for squeak hash: {}".format( squeak_hash_str @@ -215,14 +276,26 @@ class SqueakAdminServerHandler(object): len(squeak_entries_with_profile) ) ) - return squeak_entries_with_profile + squeak_display_msgs = [ + squeak_entry_to_message(entry) + for entry in squeak_entries_with_profile + ] + return squeak_admin_pb2.GetAncestorSqueakDisplaysReply( + squeak_display_entries=squeak_display_msgs + ) - def handle_delete_squeak(self, squeak_hash): - logger.info("Handle delete squeak with hash: {}".format(squeak_hash)) + def handle_delete_squeak(self, request): + squeak_hash_str = request.squeak_hash + squeak_hash = bytes.fromhex(squeak_hash_str) + logger.info("Handle delete squeak with hash: {}".format(squeak_hash_str)) self.squeak_node.delete_squeak(squeak_hash) - logger.info("Deleted squeak entry with hash: {}".format(squeak_hash)) + logger.info("Deleted squeak entry with hash: {}".format(squeak_hash_str)) + return squeak_admin_pb2.DeleteSqueakReply() - def handle_create_peer(self, peer_name, host, port): + def handle_create_peer(self, request): + peer_name = request.peer_name if request.peer_name else None + host = request.host + port = request.port logger.info( "Handle create peer with name: {}, host: {}, port: {}".format( peer_name, @@ -235,19 +308,35 @@ class SqueakAdminServerHandler(object): host, port, ) - return peer_id + return squeak_admin_pb2.CreatePeerReply( + peer_id=peer_id, + ) - def handle_get_squeak_peer(self, peer_id): + def handle_get_squeak_peer(self, request): + peer_id = request.peer_id logger.info("Handle get squeak peer with id: {}".format(peer_id)) squeak_peer = self.squeak_node.get_peer(peer_id) - return squeak_peer + if squeak_peer is None: + return None + squeak_peer_msg = squeak_peer_to_message(squeak_peer) + return squeak_admin_pb2.GetPeerReply( + squeak_peer=squeak_peer_msg, + ) - def handle_get_squeak_peers(self): + def handle_get_squeak_peers(self, request): logger.info("Handle get squeak peers") squeak_peers = self.squeak_node.get_peers() - return squeak_peers + squeak_peer_msgs = [ + squeak_peer_to_message(squeak_peer) + for squeak_peer in squeak_peers + ] + return squeak_admin_pb2.GetPeersReply( + squeak_peers=squeak_peer_msgs, + ) - def handle_set_squeak_peer_downloading(self, peer_id, downloading): + def handle_set_squeak_peer_downloading(self, request): + peer_id = request.peer_id + downloading = request.downloading logger.info( "Handle set peer downloading with peer id: {}, downloading: {}".format( peer_id, @@ -255,8 +344,11 @@ class SqueakAdminServerHandler(object): ) ) self.squeak_node.set_peer_downloading(peer_id, downloading) + return squeak_admin_pb2.SetPeerDownloadingReply() - def handle_set_squeak_peer_uploading(self, peer_id, uploading): + def handle_set_squeak_peer_uploading(self, request): + peer_id = request.peer_id + uploading = request.uploading logger.info( "Handle set peer uploading with peer id: {}, uploading: {}".format( peer_id, @@ -264,31 +356,61 @@ class SqueakAdminServerHandler(object): ) ) self.squeak_node.set_peer_uploading(peer_id, uploading) + return squeak_admin_pb2.SetPeerUploadingReply() - def handle_delete_squeak_peer(self, peer_id): + def handle_delete_squeak_peer(self, request): + peer_id = request.peer_id logger.info("Handle delete squeak peer with id: {}".format(peer_id)) self.squeak_node.delete_peer(peer_id) + return squeak_admin_pb2.DeletePeerReply() - def handle_get_buy_offers(self, squeak_hash_str): + def handle_get_buy_offers(self, request): + squeak_hash_str = request.squeak_hash logger.info("Handle get buy offers for hash: {}".format(squeak_hash_str)) - return self.squeak_node.get_buy_offers_with_peer(squeak_hash_str) + offers = self.squeak_node.get_buy_offers_with_peer(squeak_hash_str) + offer_msgs = [offer_entry_to_message(offer) for offer in offers] + logger.info("Returning buy offers: {}".format(offer_msgs)) + return squeak_admin_pb2.GetBuyOffersReply( + offers=offer_msgs, + ) - def handle_get_buy_offer(self, offer_id): + def handle_get_buy_offer(self, request): + offer_id = request.offer_id logger.info("Handle get buy offer for hash: {}".format(offer_id)) - return self.squeak_node.get_buy_offer_with_peer(offer_id) + offer = self.squeak_node.get_buy_offer_with_peer(offer_id) + offer_msg = offer_entry_to_message(offer) + logger.info("Returning buy offer: {}".format(offer_msg)) + return squeak_admin_pb2.GetBuyOfferReply( + offer=offer_msg, + ) - def handle_sync_squeaks(self): + def handle_sync_squeaks(self, request): logger.info("Handle get sync squeaks") self.squeak_node.sync_squeaks() + return squeak_admin_pb2.SyncSqueaksReply() - def handle_pay_offer(self, offer_id): + def handle_pay_offer(self, request): + offer_id = request.offer_id logger.info("Handle pay offer for offer id: {}".format(offer_id)) - return self.squeak_node.pay_offer(offer_id) + sent_payment_id = self.squeak_node.pay_offer(offer_id) + return squeak_admin_pb2.PayOfferReply( + sent_payment_id=sent_payment_id, + ) - def handle_get_sent_payments(self): + def handle_get_sent_payments(self, request): logger.info("Handle get sent payments") - return self.squeak_node.get_sent_payments() + sent_payments = self.squeak_node.get_sent_payments() + sent_payment_msgs = [sent_payment_to_message(sent_payment) for sent_payment in sent_payments] + logger.info("Returning sent payments: {}".format(sent_payment_msgs)) + return squeak_admin_pb2.GetSentPaymentsReply( + sent_payments=sent_payment_msgs, + ) - def handle_get_sent_payment(self, sent_payment_id): + def handle_get_sent_payment(self, request): + sent_payment_id = request.sent_payment_id logger.info("Handle get sent payment with id: {}".format(sent_payment_id)) - return self.squeak_node.get_sent_payment(sent_payment_id) + sent_payment = self.squeak_node.get_sent_payment(sent_payment_id) + sent_payment_msg = sent_payment_to_message(sent_payment) + return squeak_admin_pb2.GetSentPaymentReply( + sent_payment=sent_payment_msg, + ) diff --git a/squeakserver/admin/squeak_admin_server_servicer.py b/squeakserver/admin/squeak_admin_server_servicer.py index bc06a8d5..59d46490 100644 --- a/squeakserver/admin/squeak_admin_server_servicer.py +++ b/squeakserver/admin/squeak_admin_server_servicer.py @@ -70,341 +70,98 @@ class SqueakAdminServerServicer(squeak_admin_pb2_grpc.SqueakAdminServicer): return self.handler.handle_lnd_subscribe_channel_events() def CreateSigningProfile(self, request, context): - profile_name = request.profile_name - profile_id = self.handler.handle_create_signing_profile(profile_name) - return squeak_admin_pb2.CreateSigningProfileReply( - profile_id=profile_id, - ) + return self.handler.handle_create_signing_profile(request) def CreateContactProfile(self, request, context): - profile_name = request.profile_name - squeak_address = request.address - profile_id = self.handler.handle_create_contact_profile( - profile_name, - squeak_address, - ) - return squeak_admin_pb2.CreateContactProfileReply( - profile_id=profile_id, - ) + return self.handler.handle_create_contact_profile(request) def GetSigningProfiles(self, request, context): - profiles = self.handler.handle_get_signing_profiles() - profile_msgs = [ - self._squeak_profile_to_message(profile) for profile in profiles - ] - return squeak_admin_pb2.GetSigningProfilesReply(squeak_profiles=profile_msgs) + return self.handler.handle_get_signing_profiles(request) def GetContactProfiles(self, request, context): - profiles = self.handler.handle_get_contact_profiles() - profile_msgs = [ - self._squeak_profile_to_message(profile) for profile in profiles - ] - return squeak_admin_pb2.GetContactProfilesReply(squeak_profiles=profile_msgs) + return self.handler.handle_get_contact_profiles(request) def GetSqueakProfile(self, request, context): - profile_id = request.profile_id - squeak_profile = self.handler.handle_get_squeak_profile(profile_id) - if squeak_profile is None: + reply = self.handler.handle_get_squeak_profile(request) + if reply is None: context.set_code(grpc.StatusCode.NOT_FOUND) context.set_details("Profile not found.") return squeak_admin_pb2.GetSqueakProfileReply() - squeak_profile_msg = self._squeak_profile_to_message(squeak_profile) - return squeak_admin_pb2.GetSqueakProfileReply( - squeak_profile=squeak_profile_msg, - ) + return reply def GetSqueakProfileByAddress(self, request, context): - address = request.address - squeak_profile = self.handler.handle_get_squeak_profile_by_address(address) - squeak_profile_msg = self._squeak_profile_to_message(squeak_profile) - return squeak_admin_pb2.GetSqueakProfileReply(squeak_profile=squeak_profile_msg) + return self.handler.handle_get_squeak_profile_by_address(request) def SetSqueakProfileWhitelisted(self, request, context): - profile_id = request.profile_id - whitelisted = request.whitelisted - self.handler.handle_set_squeak_profile_whitelisted(profile_id, whitelisted) - return squeak_admin_pb2.SetSqueakProfileWhitelistedReply() + return self.handler.handle_set_squeak_profile_whitelisted(request) def SetSqueakProfileFollowing(self, request, context): - profile_id = request.profile_id - following = request.following - self.handler.handle_set_squeak_profile_following(profile_id, following) - return squeak_admin_pb2.SetSqueakProfileFollowingReply() + return self.handler.handle_set_squeak_profile_following(request) def SetSqueakProfileSharing(self, request, context): - profile_id = request.profile_id - sharing = request.sharing - self.handler.handle_set_squeak_profile_sharing(profile_id, sharing) - return squeak_admin_pb2.SetSqueakProfileSharingReply() + return self.handler.handle_set_squeak_profile_sharing(request) def DeleteSqueakProfile(self, request, context): - profile_id = request.profile_id - self.handler.handle_delete_squeak_profile(profile_id) - return squeak_admin_pb2.DeleteSqueakProfileReply() + return self.handler.handle_delete_squeak_profile(request) def MakeSqueak(self, request, context): - profile_id = request.profile_id - content_str = request.content - replyto_hash_str = request.replyto - replyto_hash = bytes.fromhex(replyto_hash_str) if replyto_hash_str else None - squeak_hash = self.handler.handle_make_squeak( - profile_id, content_str, replyto_hash - ) - squeak_hash_str = squeak_hash.hex() - return squeak_admin_pb2.MakeSqueakReply( - squeak_hash=squeak_hash_str, - ) + return self.handler.handle_make_squeak(request) def GetSqueakDisplay(self, request, context): - squeak_hash_str = request.squeak_hash - squeak_hash = bytes.fromhex(squeak_hash_str) - squeak_entry_with_profile = self.handler.handle_get_squeak_display_entry( - squeak_hash - ) - display_message = self._squeak_entry_to_message(squeak_entry_with_profile) - return squeak_admin_pb2.GetSqueakDisplayReply( - squeak_display_entry=display_message - ) + return self.handler.handle_get_squeak_display_entry(request) def GetFollowedSqueakDisplays(self, request, context): - squeak_entries_with_profile = ( - self.handler.handle_get_followed_squeak_display_entries() - ) - squeak_display_msgs = [ - self._squeak_entry_to_message(entry) - for entry in squeak_entries_with_profile - ] - return squeak_admin_pb2.GetFollowedSqueakDisplaysReply( - squeak_display_entries=squeak_display_msgs - ) + return self.handler.handle_get_followed_squeak_display_entries(request) def GetAddressSqueakDisplays(self, request, context): - address = request.address - min_block = 0 - max_block = sys.maxsize - squeak_entries_with_profile = ( - self.handler.handle_get_squeak_display_entries_for_address( - address, - min_block, - max_block, - ) - ) - squeak_display_msgs = [ - self._squeak_entry_to_message(entry) - for entry in squeak_entries_with_profile - ] - return squeak_admin_pb2.GetAddressSqueakDisplaysReply( - squeak_display_entries=squeak_display_msgs - ) + return self.handler.handle_get_squeak_display_entries_for_address(request) def GetAncestorSqueakDisplays(self, request, context): - squeak_hash_str = request.squeak_hash - squeak_entries_with_profile = ( - self.handler.handle_get_ancestor_squeak_display_entries( - squeak_hash_str, - ) - ) - squeak_display_msgs = [ - self._squeak_entry_to_message(entry) - for entry in squeak_entries_with_profile - ] - return squeak_admin_pb2.GetAncestorSqueakDisplaysReply( - squeak_display_entries=squeak_display_msgs - ) + return self.handler.handle_get_ancestor_squeak_display_entries(request) def DeleteSqueak(self, request, context): - squeak_hash_str = request.squeak_hash - squeak_hash = bytes.fromhex(squeak_hash_str) - self.handler.handle_delete_squeak(squeak_hash) - return squeak_admin_pb2.DeleteSqueakReply() + return self.handler.handle_delete_squeak(request) def CreatePeer(self, request, context): - peer_name = request.peer_name if request.peer_name else None - host = request.host - port = request.port - peer_id = self.handler.handle_create_peer( - peer_name, - host, - port, - ) - return squeak_admin_pb2.CreatePeerReply( - peer_id=peer_id, - ) + return self.handler.handle_create_peer(request) def GetPeer(self, request, context): - peer_id = request.peer_id - squeak_peer = self.handler.handle_get_squeak_peer(peer_id) - if squeak_peer is None: + reply = self.handler.handle_get_squeak_peer(request) + if reply is None: context.set_code(grpc.StatusCode.NOT_FOUND) context.set_details("Peer not found.") return squeak_admin_pb2.GetPeerReply() - squeak_peer_msg = self._squeak_peer_to_message(squeak_peer) - return squeak_admin_pb2.GetPeerReply( - squeak_peer=squeak_peer_msg, - ) + return reply def GetPeers(self, request, context): - squeak_peers = self.handler.handle_get_squeak_peers() - squeak_peer_msgs = [ - self._squeak_peer_to_message(squeak_peer) for squeak_peer in squeak_peers - ] - return squeak_admin_pb2.GetPeersReply( - squeak_peers=squeak_peer_msgs, - ) + return self.handler.handle_get_squeak_peers(request) def SetPeerDownloading(self, request, context): - peer_id = request.peer_id - downloading = request.downloading - self.handler.handle_set_squeak_peer_downloading( - peer_id, - downloading, - ) - return squeak_admin_pb2.SetPeerDownloadingReply() + return self.handler.handle_set_squeak_peer_downloading(request) def SetPeerUploading(self, request, context): - peer_id = request.peer_id - uploading = request.uploading - self.handler.handle_set_squeak_peer_uploading( - peer_id, - uploading, - ) - return squeak_admin_pb2.SetPeerUploadingReply() + return self.handler.handle_set_squeak_peer_uploading(request) def DeletePeer(self, request, context): - peer_id = request.peer_id - self.handler.handle_delete_squeak_peer(peer_id) - return squeak_admin_pb2.DeletePeerReply() + return self.handler.handle_delete_squeak_peer(request) def GetBuyOffers(self, request, context): - squeak_hash_str = request.squeak_hash - offers = self.handler.handle_get_buy_offers(squeak_hash_str) - offer_msgs = [self._offer_entry_to_message(offer) for offer in offers] - logger.info("Returning buy offers: {}".format(offer_msgs)) - return squeak_admin_pb2.GetBuyOffersReply( - offers=offer_msgs, - ) + return self.handler.handle_get_buy_offers(request) def GetBuyOffer(self, request, context): - offer_id = request.offer_id - offer = self.handler.handle_get_buy_offer(offer_id) - offer_msg = self._offer_entry_to_message(offer) - logger.info("Returning buy offer: {}".format(offer_msg)) - return squeak_admin_pb2.GetBuyOfferReply( - offer=offer_msg, - ) + return self.handler.handle_get_buy_offer(request) def SyncSqueaks(self, request, context): - self.handler.handle_sync_squeaks() - return squeak_admin_pb2.SyncSqueaksReply() + return self.handler.handle_sync_squeaks(request) def PayOffer(self, request, context): - offer_id = request.offer_id - sent_payment_id = self.handler.handle_pay_offer(offer_id) - return squeak_admin_pb2.PayOfferReply( - sent_payment_id=sent_payment_id, - ) + return self.handler.handle_pay_offer(request) def GetSentPayments(self, request, context): - sent_payments = self.handler.handle_get_sent_payments() - sent_payment_msgs = [self._sent_payment_to_message(sent_payment) for sent_payment in sent_payments] - logger.info("Returning sent payments: {}".format(sent_payment_msgs)) - return squeak_admin_pb2.GetSentPaymentsReply( - sent_payments=sent_payment_msgs, - ) + return self.handler.handle_get_sent_payments(request) def GetSentPayment(self, request, context): - sent_payment_id = request.sent_payment_id - sent_payment = self.handler.handle_get_sent_payment(sent_payment_id) - sent_payment_msg = self._sent_payment_to_message(sent_payment) - return squeak_admin_pb2.GetSentPaymentReply( - sent_payment=sent_payment_msg, - ) - - def _squeak_entry_to_message(self, squeak_entry_with_profile): - if squeak_entry_with_profile is None: - return None - squeak_entry = squeak_entry_with_profile.squeak_entry - squeak = squeak_entry.squeak - block_header = squeak_entry.block_header - is_unlocked = squeak.HasDecryptionKey() - content_str = squeak.GetDecryptedContentStr() if is_unlocked else None - squeak_profile = squeak_entry_with_profile.squeak_profile - is_author_known = squeak_profile is not None - author_name = squeak_profile.profile_name if squeak_profile else None - author_address = str(squeak.GetAddress()) - is_reply = squeak.is_reply - reply_to = get_replyto(squeak).hex() if is_reply else None - return squeak_admin_pb2.SqueakDisplayEntry( - squeak_hash=get_hash(squeak).hex(), - is_unlocked=squeak.HasDecryptionKey(), - content_str=content_str, - block_height=squeak.nBlockHeight, - block_time=block_header.nTime, - is_author_known=is_author_known, - author_name=author_name, - author_address=author_address, - is_reply=is_reply, - reply_to=reply_to, - ) - - def _squeak_profile_to_message(self, squeak_profile): - if squeak_profile is None: - return None - has_private_key = squeak_profile.private_key is not None - return squeak_admin_pb2.SqueakProfile( - profile_id=squeak_profile.profile_id, - profile_name=squeak_profile.profile_name, - has_private_key=has_private_key, - address=squeak_profile.address, - sharing=squeak_profile.sharing, - following=squeak_profile.following, - whitelisted=squeak_profile.whitelisted, - ) - - def _squeak_peer_to_message(self, squeak_peer): - if squeak_peer is None: - return None - return squeak_admin_pb2.SqueakPeer( - peer_id=squeak_peer.peer_id, - peer_name=squeak_peer.peer_name, - host=squeak_peer.host, - port=squeak_peer.port, - uploading=squeak_peer.uploading, - downloading=squeak_peer.downloading, - ) - - def _offer_entry_to_message(self, offer_entry): - if offer_entry is None: - return None - offer = offer_entry.offer - peer = self._squeak_peer_to_message(offer_entry.peer) - return squeak_admin_pb2.OfferDisplayEntry( - offer_id=offer.offer_id, - squeak_hash=offer.squeak_hash, - amount=offer.price_msat, - node_pubkey=offer.destination, - node_host=offer.node_host, - node_port=offer.node_port, - peer=peer, - invoice_timestamp=offer.invoice_timestamp, - invoice_expiry=offer.invoice_expiry, - ) - - def _sent_payment_to_message(self, sent_payment): - if sent_payment is None: - return None - logger.info("sent_payment: {}".format(sent_payment)) - return squeak_admin_pb2.SentPayment( - sent_payment_id=sent_payment.sent_payment_id, - offer_id=sent_payment.offer_id, - peer_id=sent_payment.peer_id, - squeak_hash=sent_payment.squeak_hash, - preimage_hash=sent_payment.preimage_hash, - preimage=sent_payment.preimage, - amount=sent_payment.amount, - node_pubkey=sent_payment.node_pubkey, - preimage_is_valid=sent_payment.preimage_is_valid, - ) + return self.handler.handle_get_sent_payment(request) def serve(self): server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) diff --git a/squeakserver/admin/squeak_admin_web_service.py b/squeakserver/admin/squeak_admin_web_service.py new file mode 100644 index 00000000..00e2b011 --- /dev/null +++ b/squeakserver/admin/squeak_admin_web_service.py @@ -0,0 +1,142 @@ +import os +import logging + +from flask import Flask +from flask import request + +from google.protobuf import json_format +from google.protobuf import message + +from proto import squeak_admin_pb2, squeak_admin_pb2_grpc +from proto import lnd_pb2, lnd_pb2_grpc + + +logger = logging.getLogger(__name__) + + +def create_app(handler): + # create and configure the app + logger.info("Starting flask app from directory: {}".format(os.getcwd())) + app = Flask(__name__, static_folder='/app/static/build', static_url_path='/') + app.config.from_mapping( + SECRET_KEY='dev', + ) + + def handle_request(request_message, handle_rpc_request): + data = request.get_data() + request_message.ParseFromString(data) + reply = handle_rpc_request(request_message) + return reply.SerializeToString(reply) + + @app.route('/') + def index(): + logger.info("Getting index route.") + logger.info("os.getcwd(): {}".format(os.getcwd())) + logger.info("os.listdir(os.getcwd()): {}".format(os.listdir(os.getcwd()))) + return app.send_static_file('index.html') + + @app.route('/hello') + def hello_world(): + logger.info("Getting hello route.") + return 'Hello, World!' + + @app.route('/getfollowedsqueakdisplays', methods=["POST"]) + def getfollowedsqueakdisplays(): + return handle_request( + squeak_admin_pb2.GetFollowedSqueakDisplaysRequest(), + handler.handle_get_followed_squeak_display_entries, + ) + + @app.route('/lndgetinfo', methods=["POST"]) + def lndgetinfo(): + data = request.get_data() + req = lnd_pb2.GetInfoRequest() + req.ParseFromString(data) + reply = handler.handle_lnd_get_info() + reply_data = reply.SerializeToString(reply) + return reply_data + + @app.route('/lndwalletbalance', methods=["POST"]) + def lndwalletbalance(): + data = request.get_data() + req = lnd_pb2.WalletBalanceRequest() + req.ParseFromString(data) + reply = handler.handle_lnd_wallet_balance() + reply_data = reply.SerializeToString(reply) + return reply_data + + @app.route('/lndgettransactions', methods=["POST"]) + def lndgettransactions(): + data = request.get_data() + req = lnd_pb2.GetTransactionsRequest() + req.ParseFromString(data) + reply = handler.handle_lnd_get_transactions() + logger.info("lndgettransactions reply: {}".format(reply)) + reply_data = reply.SerializeToString(reply) + return reply_data + + @app.route('/lndlistpeers', methods=["POST"]) + def lndlistpeers(): + data = request.get_data() + req = lnd_pb2.ListPeersRequest() + req.ParseFromString(data) + reply = handler.handle_lnd_list_peers() + reply_data = reply.SerializeToString(reply) + return reply_data + + @app.route('/lndlistchannels', methods=["POST"]) + def lndlistchannels(): + data = request.get_data() + req = lnd_pb2.ListChannelsRequest() + req.ParseFromString(data) + reply = handler.handle_lnd_list_channels() + reply_data = reply.SerializeToString(reply) + return reply_data + + @app.route('/lndpendingchannels', methods=["POST"]) + def lndpendingchannels(): + data = request.get_data() + req = lnd_pb2.PendingChannelsRequest() + req.ParseFromString(data) + reply = handler.handle_lnd_pending_channels(req) + reply_data = reply.SerializeToString(reply) + return reply_data + + @app.route('/getsqueakprofile', methods=["POST"]) + def getsqueakprofile(): + return handle_request( + squeak_admin_pb2.GetSqueakProfileRequest(), + handler.handle_get_squeak_profile, + ) + + @app.route('/setsqueakprofilefollowing', methods=["POST"]) + def setsqueakprofilefollowing(): + return handle_request( + squeak_admin_pb2.SetSqueakProfileFollowingRequest(), + handler.handle_set_squeak_profile_following, + ) + + @app.route('/setsqueakprofilesharing', methods=["POST"]) + def setsqueakprofilesharing(): + return handle_request( + squeak_admin_pb2.SetSqueakProfileSharingRequest(), + handler.handle_set_squeak_profile_sharing, + ) + + @app.route('/setsqueakprofilewhitelisted', methods=["POST"]) + def setsqueakprofilewhitelisted(): + return handle_request( + squeak_admin_pb2.SetSqueakProfileWhitelistedRequest(), + handler.handle_set_squeak_profile_whitelisted, + ) + + return app + + +def run_app(handler): + app = create_app(handler) + app.run( + host="0.0.0.0", + port=5000, + debug=False, + ) diff --git a/squeakserver/admin/util.py b/squeakserver/admin/util.py new file mode 100644 index 00000000..3ece28de --- /dev/null +++ b/squeakserver/admin/util.py @@ -0,0 +1,89 @@ +from squeakserver.server.util import get_hash, get_replyto + +from proto import squeak_admin_pb2, squeak_admin_pb2_grpc + + +def squeak_entry_to_message(squeak_entry_with_profile): + if squeak_entry_with_profile is None: + return None + squeak_entry = squeak_entry_with_profile.squeak_entry + squeak = squeak_entry.squeak + block_header = squeak_entry.block_header + is_unlocked = squeak.HasDecryptionKey() + content_str = squeak.GetDecryptedContentStr() if is_unlocked else None + squeak_profile = squeak_entry_with_profile.squeak_profile + is_author_known = squeak_profile is not None + author_name = squeak_profile.profile_name if squeak_profile else None + author_address = str(squeak.GetAddress()) + is_reply = squeak.is_reply + reply_to = get_replyto(squeak).hex() if is_reply else None + return squeak_admin_pb2.SqueakDisplayEntry( + squeak_hash=get_hash(squeak).hex(), + is_unlocked=squeak.HasDecryptionKey(), + content_str=content_str, + block_height=squeak.nBlockHeight, + block_time=block_header.nTime, + is_author_known=is_author_known, + author_name=author_name, + author_address=author_address, + is_reply=is_reply, + reply_to=reply_to, + ) + +def squeak_profile_to_message(squeak_profile): + if squeak_profile is None: + return None + has_private_key = squeak_profile.private_key is not None + return squeak_admin_pb2.SqueakProfile( + profile_id=squeak_profile.profile_id, + profile_name=squeak_profile.profile_name, + has_private_key=has_private_key, + address=squeak_profile.address, + sharing=squeak_profile.sharing, + following=squeak_profile.following, + whitelisted=squeak_profile.whitelisted, + ) + +def squeak_peer_to_message(squeak_peer): + if squeak_peer is None: + return None + return squeak_admin_pb2.SqueakPeer( + peer_id=squeak_peer.peer_id, + peer_name=squeak_peer.peer_name, + host=squeak_peer.host, + port=squeak_peer.port, + uploading=squeak_peer.uploading, + downloading=squeak_peer.downloading, + ) + +def offer_entry_to_message(offer_entry): + if offer_entry is None: + return None + offer = offer_entry.offer + peer = squeak_peer_to_message(offer_entry.peer) + return squeak_admin_pb2.OfferDisplayEntry( + offer_id=offer.offer_id, + squeak_hash=offer.squeak_hash, + amount=offer.price_msat, + node_pubkey=offer.destination, + node_host=offer.node_host, + node_port=offer.node_port, + peer=peer, + invoice_timestamp=offer.invoice_timestamp, + invoice_expiry=offer.invoice_expiry, + ) + +def sent_payment_to_message(sent_payment): + if sent_payment is None: + return None + return squeak_admin_pb2.SentPayment( + sent_payment_id=sent_payment.sent_payment_id, + offer_id=sent_payment.offer_id, + peer_id=sent_payment.peer_id, + squeak_hash=sent_payment.squeak_hash, + preimage_hash=sent_payment.preimage_hash, + preimage=sent_payment.preimage, + amount=sent_payment.amount, + node_pubkey=sent_payment.node_pubkey, + preimage_is_valid=sent_payment.preimage_is_valid, + ) diff --git a/squeakserver/main.py b/squeakserver/main.py index bdb1ad5d..3dd2bec0 100644 --- a/squeakserver/main.py +++ b/squeakserver/main.py @@ -18,6 +18,8 @@ from squeakserver.node.squeak_node import SqueakNode from squeakserver.server.lightning_address import LightningAddressHostPort from squeakserver.server.squeak_server_handler import SqueakServerHandler from squeakserver.server.squeak_server_servicer import SqueakServerServicer +from squeakserver.admin.squeak_admin_web_service import run_app + logger = logging.getLogger(__name__) @@ -59,6 +61,11 @@ def load_admin_rpc_server(config, handler) -> SqueakAdminServerServicer: ) +def load_admin_web_server(config, handler): + # TODO + pass + + def load_network(config): return config["squeaknode"]["network"] @@ -142,6 +149,16 @@ def start_admin_rpc_server(rpc_server): thread.start() +def start_admin_web_server(handler): + logger.info("Calling start_admin_web_server...") + thread = threading.Thread( + target=run_app, + args=(handler,), + ) + thread.daemon = True + thread.start() + + def parse_args(): parser = argparse.ArgumentParser( description="squeakserver runs a node using squeak protocol. ", @@ -230,6 +247,9 @@ def run_server(config): admin_rpc_server = load_admin_rpc_server(config, admin_handler) start_admin_rpc_server(admin_rpc_server) + # start admin web server + start_admin_web_server(admin_handler) + # start rpc server handler = load_handler(squeak_node) server = load_rpc_server(config, handler)