Add admin web server (#310)

* Add basic flask web server

* Use ubuntu as docker base image for frontend docker build

* Got web frontend server running inside sqkserver

* Got client request throught flask working

* Refactor makerequest function in frontend

* Support sending request to flask server

* Use request and reply in flask for set profile sharing method

* Handle request object in flask for get followed squeaks route

* Use flask endpoint for lnd getinfo

* Use flask endpoint for walletbalance

* Use flask for gettransactions endpoint

* Use flask endpoint for lnd methods

* Add set profile properties methods to flask

* Move request, response protobuf to handler class

* Move request, reply inside handler for get signing profiles

* Move request, reply to handler for get contact profiles

* Move request, reply to handler for get squeak profile

* Move request reply for get profile by address to handler

* Move request reply to handler for set profile attributes

* Move request reply to handler for delete profile

* Move request reply for make squeak to handler

* Move request reply to handler for get squeak display

* Move request reply to get address squeak displays

* Move request reply to handler for ancestor squeaks

* Move request reply to handler for delete squeak

* Move request reply to handler for create peer and get peer

* Move request reply to handler for peer set attributes

* Move request reply to handler for more methods

* Move request reply to handler for another method

* Move request reply to handler for more methods

* Move more request reply to handler

* Move request reply to handler for all methods

* Remove message conversion helper methods from servicer class

* Remove duplicate protobuf conversion functions

* Fix flask handling of requests

* Remove old protobuf conversion methods in comments

* Use helper method to simplify web server routes functions
This commit is contained in:
Jonathan Zernik 2020-10-22 22:52:40 -04:00 committed by GitHub
parent 7ce8aff7c0
commit d3d7e6e604
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 653 additions and 390 deletions

View file

@ -63,6 +63,7 @@ services:
- sqk_dir:/root/.sqk
ports:
- 8774:8774
- 5000:5000
links:
- "btcd:btcd"
- "lnd:lnd"

View file

@ -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

View file

@ -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" .

View file

@ -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",

View file

@ -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) {

View file

@ -7,3 +7,5 @@ grpcio-tools
psycopg2
requests
SQLAlchemy
Flask
protobuf

View file

@ -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,
)

View file

@ -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))

View file

@ -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,
)

View file

@ -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,
)

View file

@ -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)