@@ -10,6 +10,7 @@ import (
10
10
"io/ioutil"
11
11
"net"
12
12
"net/http"
13
+ "os"
13
14
"path/filepath"
14
15
"strings"
15
16
"sync"
@@ -125,7 +126,7 @@ func (g *Shushtar) Run() error {
125
126
return fmt .Errorf ("please set a strong password for the UI, " +
126
127
"at least %d characters long" , uiPasswordMinLength )
127
128
}
128
-
129
+
129
130
// Initiate our listeners. For now, we only support listening on one
130
131
// port at a time because we can only pass in one pre-configured RPC
131
132
// listener into lnd.
@@ -427,7 +428,7 @@ func (g *Shushtar) startGrpcWebProxy() error {
427
428
if err != nil {
428
429
return fmt .Errorf ("could not load statik file system: %v" , err )
429
430
}
430
- staticFileServer := http .FileServer (statikFS )
431
+ staticFileServer := http .FileServer (& ClientRouteWrapper { statikFS } )
431
432
432
433
// Create the gRPC web proxy that connects to lnd internally using the
433
434
// admin macaroon and converts the browser's gRPC web calls into native
@@ -622,3 +623,22 @@ func readMacaroon(macPath string) (grpc.DialOption, error) {
622
623
cred := macaroons .NewMacaroonCredential (mac )
623
624
return grpc .WithPerRPCCredentials (cred ), nil
624
625
}
626
+
627
+ // ClientRouteWrapper is a wrapper around a FileSystem which properly handles
628
+ // URL routes that are defined in the client app but unknown to the backend
629
+ // http server
630
+ type ClientRouteWrapper struct {
631
+ assets http.FileSystem
632
+ }
633
+
634
+ // Open intercepts requests to open files. If the file does not exist and there
635
+ // is no file extension, then assume this is a client side route and return the
636
+ // contents of index.html
637
+ func (i * ClientRouteWrapper ) Open (name string ) (http.File , error ) {
638
+ ret , err := i .assets .Open (name )
639
+ if ! os .IsNotExist (err ) || filepath .Ext (name ) != "" {
640
+ return ret , err
641
+ }
642
+
643
+ return i .assets .Open ("/index.html" )
644
+ }
0 commit comments