added EventSource to show a thank you message

This commit is contained in:
michael1011 2018-03-26 23:54:12 +02:00
parent 58f432d04f
commit a4ad464710
No known key found for this signature in database
GPG Key ID: 84D249BA71685D46
6 changed files with 107 additions and 8 deletions

View File

@ -1,5 +1,10 @@
package backends package backends
import "github.com/donovanhide/eventsource"
// For callbacks when an invoice gets settled
type PublishInvoiceSettled func(invoice string, eventSrv *eventsource.Server)
type Backend interface { type Backend interface {
Connect() error Connect() error

View File

@ -4,7 +4,7 @@ import (
"context" "context"
"encoding/hex" "encoding/hex"
"errors" "errors"
"fmt" "github.com/donovanhide/eventsource"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
@ -90,7 +90,7 @@ func (lnd *LND) GetInvoice(message string, amount int64, expiry int64) (invoice
return response.PaymentRequest, err return response.PaymentRequest, err
} }
func (lnd *LND) SubscribeInvoices() error { func (lnd *LND) SubscribeInvoices(callback PublishInvoiceSettled, eventSrv *eventsource.Server) error {
stream, err := lnd.client.SubscribeInvoices(lnd.ctx, &lnrpc.InvoiceSubscription{}) stream, err := lnd.client.SubscribeInvoices(lnd.ctx, &lnrpc.InvoiceSubscription{})
if err != nil { if err != nil {
@ -101,7 +101,7 @@ func (lnd *LND) SubscribeInvoices() error {
go func() { go func() {
for { for {
in, streamErr := stream.Recv() invoice, streamErr := stream.Recv()
if streamErr == io.EOF { if streamErr == io.EOF {
err = errors.New("lost connection to LND gRPC") err = errors.New("lost connection to LND gRPC")
@ -119,8 +119,12 @@ func (lnd *LND) SubscribeInvoices() error {
return return
} }
fmt.Println(in) if invoice.Settled {
callback(invoice.PaymentRequest, eventSrv)
}
} }
}() }()
<-wait <-wait

View File

@ -115,6 +115,12 @@
float: right; float: right;
} }
#lightningTipThankYou {
margin-bottom: 0.2em;
display: block;
}
.spinner { .spinner {
width: 12px; width: 12px;
height: 12px; height: 12px;

View File

@ -5,6 +5,7 @@
<script async defer src="lightningTip.js"></script> <script async defer src="lightningTip.js"></script>
<script async defer src="https://cdn.rawgit.com/kazuhikoarase/qrcode-generator/886a247e/js/qrcode.js"></script> <script async defer src="https://cdn.rawgit.com/kazuhikoarase/qrcode-generator/886a247e/js/qrcode.js"></script>
<script async defer src="https://cdn.rawgit.com/emn178/js-sha256/189bb9b0/build/sha256.min.js"></script>
</head> </head>
<div id="lightningTip"> <div id="lightningTip">

View File

@ -11,7 +11,10 @@ var qrCode;
var defaultGetInvoice; var defaultGetInvoice;
// TODO: listen to eventsource and show tank you when invoice settled // TODO: show error when invoice expires
// TODO: maybe don't show full invoice
// TODO: proper url handling window.location.protocol + window.location.hostname + ":8081/getinvoice"
// TODO: show price in dollar?
function getInvoice() { function getInvoice() {
if (running === false) { if (running === false) {
running = true; running = true;
@ -34,6 +37,16 @@ function getInvoice() {
console.log("Got invoice: " + json.Invoice); console.log("Got invoice: " + json.Invoice);
console.log("Invoice expires in: " + json.Expiry); console.log("Invoice expires in: " + json.Expiry);
var hash = sha256(json.Invoice);
console.log("Got hash of invoice: " + hash);
// TODO: find alternative for Edge and IE
console.log("Starting listening for invoice to get settled");
listenInvoiceSettled(hash);
// Update UI
invoice = json.Invoice; invoice = json.Invoice;
var wrapper = document.getElementById("lightningTip"); var wrapper = document.getElementById("lightningTip");
@ -65,6 +78,8 @@ function getInvoice() {
} }
} catch (exception) { } catch (exception) {
console.error(exception);
showErrorMessage("Failed to reach backend"); showErrorMessage("Failed to reach backend");
} }
@ -72,7 +87,6 @@ function getInvoice() {
}; };
// TODO: proper url handling window.location.protocol + window.location.hostname + ":8081/getinvoice"
request.open("POST", "http://localhost:8081/getinvoice", true); request.open("POST", "http://localhost:8081/getinvoice", true);
request.send(data); request.send(data);
@ -99,6 +113,19 @@ function getInvoice() {
} }
function listenInvoiceSettled(hash) {
var eventSrc = new EventSource("http://localhost:8081/eventsource");
eventSrc.onmessage = function (event) {
if (event.data === hash) {
var wrapper = document.getElementById("lightningTip");
wrapper.innerHTML = "<p id=\"lightningTipLogo\">⚡</p>";
wrapper.innerHTML += "<a id='lightningTipThankYou'>Thank you for your tip!</a>";
}
};
}
function starTimer(duration, element) { function starTimer(duration, element) {
showTimer(duration, element); showTimer(duration, element);

View File

@ -1,14 +1,35 @@
package main package main
import ( import (
"crypto/sha256"
"encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/donovanhide/eventsource"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"os" "os"
"strconv" "strconv"
"time"
) )
const eventChannel = "invoiceSettled"
var eventSrv *eventsource.Server
var pendingInvoices []PendingInvoice
type PendingInvoice struct {
Invoice string
Hash string
Expiry time.Time
}
// To use the pendingInvoice type as event for the EventSource stream
func (pending PendingInvoice) Id() string { return "" }
func (pending PendingInvoice) Event() string { return "" }
func (pending PendingInvoice) Data() string { return pending.Hash }
type invoiceResponse struct { type invoiceResponse struct {
Invoice string Invoice string
Expiry int64 Expiry int64
@ -32,14 +53,18 @@ func main() {
err := backend.Connect() err := backend.Connect()
if err == nil { if err == nil {
log.Info("Starting EventSource stream")
eventSrv = eventsource.NewServer()
http.HandleFunc("/", notFoundHandler) http.HandleFunc("/", notFoundHandler)
http.HandleFunc("/getinvoice", getInvoiceHandler) http.HandleFunc("/getinvoice", getInvoiceHandler)
http.HandleFunc("/eventsource", eventSrv.Handler(eventChannel))
log.Info("Subscribing to invoices") log.Info("Subscribing to invoices")
go func() { go func() {
// TODO: let clients listen if their invoice was paid (eventsource) err = cfg.LND.SubscribeInvoices(publishInvoiceSettled, eventSrv)
err = cfg.LND.SubscribeInvoices()
if err != nil { if err != nil {
log.Error("Failed to subscribe to invoices: " + fmt.Sprint(err)) log.Error("Failed to subscribe to invoices: " + fmt.Sprint(err))
@ -68,6 +93,23 @@ func main() {
} }
// Callbacks when an invoice gets settled
func publishInvoiceSettled(invoice string, eventSrv *eventsource.Server) {
for index, pending := range pendingInvoices {
if pending.Invoice == invoice {
log.Info("Invoice settled: " + invoice)
eventSrv.Publish([]string{eventChannel}, pending)
pendingInvoices = append(pendingInvoices[:index], pendingInvoices[index+1:]...)
break
}
}
}
func getInvoiceHandler(writer http.ResponseWriter, request *http.Request) { func getInvoiceHandler(writer http.ResponseWriter, request *http.Request) {
errorMessage := "Could not parse values from request" errorMessage := "Could not parse values from request"
@ -89,8 +131,22 @@ func getInvoiceHandler(writer http.ResponseWriter, request *http.Request) {
logMessage += " with message \"" + body.Message + "\"" logMessage += " with message \"" + body.Message + "\""
} }
sha := sha256.New()
sha.Write([]byte(invoice))
hash := hex.EncodeToString(sha.Sum(nil))
expiryDuration := time.Duration(cfg.TipExpiry) * time.Second
log.Info(logMessage) log.Info(logMessage)
// TODO: check every minute or so if expired
pendingInvoices = append(pendingInvoices, PendingInvoice{
Invoice: invoice,
Hash: hash,
Expiry: time.Now().Add(expiryDuration),
})
writer.Write(marshalJson(invoiceResponse{ writer.Write(marshalJson(invoiceResponse{
Invoice: invoice, Invoice: invoice,
Expiry: cfg.TipExpiry, Expiry: cfg.TipExpiry,