diff --git a/graphql/assets.go b/graphql/assets.go
new file mode 100644
index 0000000000..c5ef26b151
--- /dev/null
+++ b/graphql/assets.go
@@ -0,0 +1,307 @@
+// Code generated by go-bindata. DO NOT EDIT.
+// sources:
+// assets/index.html
+
+package graphql
+
+import (
+ "crypto/sha256"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+ "time"
+)
+
+type asset struct {
+ bytes []byte
+ info os.FileInfo
+ digest [sha256.Size]byte
+}
+
+type bindataFileInfo struct {
+ name string
+ size int64
+ mode os.FileMode
+ modTime time.Time
+}
+
+func (fi bindataFileInfo) Name() string {
+ return fi.name
+}
+func (fi bindataFileInfo) Size() int64 {
+ return fi.size
+}
+func (fi bindataFileInfo) Mode() os.FileMode {
+ return fi.mode
+}
+func (fi bindataFileInfo) ModTime() time.Time {
+ return fi.modTime
+}
+func (fi bindataFileInfo) IsDir() bool {
+ return false
+}
+func (fi bindataFileInfo) Sys() interface{} {
+ return nil
+}
+
+//nolint:misspell
+var _indexHtml = []byte(`
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
+`)
+
+func indexHtmlBytes() ([]byte, error) {
+ return _indexHtml, nil
+}
+
+func indexHtml() (*asset, error) {
+ bytes, err := indexHtmlBytes()
+ if err != nil {
+ return nil, err
+ }
+
+ info := bindataFileInfo{name: "index.html", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
+ a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xb5, 0x7c, 0xd8, 0x5f, 0x37, 0x8c, 0xdb, 0x23, 0xab, 0x1d, 0xaa, 0x10, 0x69, 0x10, 0xea, 0xd7, 0xcd, 0xc9, 0x12, 0xd7, 0x4e, 0x4, 0x72, 0x14, 0x37, 0x8a, 0x48, 0x73, 0xfb, 0x94, 0xb, 0x36}}
+ return a, nil
+}
+
+// Asset loads and returns the asset for the given name.
+// It returns an error if the asset could not be found or
+// could not be loaded.
+func Asset(name string) ([]byte, error) {
+ canonicalName := strings.Replace(name, "\\", "/", -1)
+ if f, ok := _bindata[canonicalName]; ok {
+ a, err := f()
+ if err != nil {
+ return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
+ }
+ return a.bytes, nil
+ }
+ return nil, fmt.Errorf("Asset %s not found", name)
+}
+
+// AssetString returns the asset contents as a string (instead of a []byte).
+func AssetString(name string) (string, error) {
+ data, err := Asset(name)
+ return string(data), err
+}
+
+// MustAsset is like Asset but panics when Asset would return an error.
+// It simplifies safe initialization of global variables.
+func MustAsset(name string) []byte {
+ a, err := Asset(name)
+ if err != nil {
+ panic("asset: Asset(" + name + "): " + err.Error())
+ }
+
+ return a
+}
+
+// MustAssetString is like AssetString but panics when Asset would return an
+// error. It simplifies safe initialization of global variables.
+func MustAssetString(name string) string {
+ return string(MustAsset(name))
+}
+
+// AssetInfo loads and returns the asset info for the given name.
+// It returns an error if the asset could not be found or
+// could not be loaded.
+func AssetInfo(name string) (os.FileInfo, error) {
+ canonicalName := strings.Replace(name, "\\", "/", -1)
+ if f, ok := _bindata[canonicalName]; ok {
+ a, err := f()
+ if err != nil {
+ return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
+ }
+ return a.info, nil
+ }
+ return nil, fmt.Errorf("AssetInfo %s not found", name)
+}
+
+// AssetDigest returns the digest of the file with the given name. It returns an
+// error if the asset could not be found or the digest could not be loaded.
+func AssetDigest(name string) ([sha256.Size]byte, error) {
+ canonicalName := strings.Replace(name, "\\", "/", -1)
+ if f, ok := _bindata[canonicalName]; ok {
+ a, err := f()
+ if err != nil {
+ return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s can't read by error: %v", name, err)
+ }
+ return a.digest, nil
+ }
+ return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s not found", name)
+}
+
+// Digests returns a map of all known files and their checksums.
+func Digests() (map[string][sha256.Size]byte, error) {
+ mp := make(map[string][sha256.Size]byte, len(_bindata))
+ for name := range _bindata {
+ a, err := _bindata[name]()
+ if err != nil {
+ return nil, err
+ }
+ mp[name] = a.digest
+ }
+ return mp, nil
+}
+
+// AssetNames returns the names of the assets.
+func AssetNames() []string {
+ names := make([]string, 0, len(_bindata))
+ for name := range _bindata {
+ names = append(names, name)
+ }
+ return names
+}
+
+// _bindata is a table, holding each asset generator, mapped to its name.
+var _bindata = map[string]func() (*asset, error){
+ "index.html": indexHtml,
+}
+
+// AssetDir returns the file names below a certain
+// directory embedded in the file by go-bindata.
+// For example if you run go-bindata on data/... and data contains the
+// following hierarchy:
+// data/
+// foo.txt
+// img/
+// a.png
+// b.png
+// then AssetDir("data") would return []string{"foo.txt", "img"},
+// AssetDir("data/img") would return []string{"a.png", "b.png"},
+// AssetDir("foo.txt") and AssetDir("notexist") would return an error, and
+// AssetDir("") will return []string{"data"}.
+func AssetDir(name string) ([]string, error) {
+ node := _bintree
+ if len(name) != 0 {
+ canonicalName := strings.Replace(name, "\\", "/", -1)
+ pathList := strings.Split(canonicalName, "/")
+ for _, p := range pathList {
+ node = node.Children[p]
+ if node == nil {
+ return nil, fmt.Errorf("Asset %s not found", name)
+ }
+ }
+ }
+ if node.Func != nil {
+ return nil, fmt.Errorf("Asset %s not found", name)
+ }
+ rv := make([]string, 0, len(node.Children))
+ for childName := range node.Children {
+ rv = append(rv, childName)
+ }
+ return rv, nil
+}
+
+type bintree struct {
+ Func func() (*asset, error)
+ Children map[string]*bintree
+}
+
+var _bintree = &bintree{nil, map[string]*bintree{
+ "index.html": {indexHtml, map[string]*bintree{}},
+}}
+
+// RestoreAsset restores an asset under the given directory.
+func RestoreAsset(dir, name string) error {
+ data, err := Asset(name)
+ if err != nil {
+ return err
+ }
+ info, err := AssetInfo(name)
+ if err != nil {
+ return err
+ }
+ err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
+ if err != nil {
+ return err
+ }
+ err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
+ if err != nil {
+ return err
+ }
+ return os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
+}
+
+// RestoreAssets restores an asset under the given directory recursively.
+func RestoreAssets(dir, name string) error {
+ children, err := AssetDir(name)
+ // File
+ if err != nil {
+ return RestoreAsset(dir, name)
+ }
+ // Dir
+ for _, child := range children {
+ err = RestoreAssets(dir, filepath.Join(name, child))
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func _filePath(dir, name string) string {
+ canonicalName := strings.Replace(name, "\\", "/", -1)
+ return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...)
+}
diff --git a/graphql/assets/index.html b/graphql/assets/index.html
new file mode 100644
index 0000000000..f0953783a6
--- /dev/null
+++ b/graphql/assets/index.html
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
diff --git a/graphql/graphiql.go b/graphql/graphiql.go
index e72609254e..339638cb7d 100644
--- a/graphql/graphiql.go
+++ b/graphql/graphiql.go
@@ -22,10 +22,17 @@
package graphql
+//go:generate go-bindata -nometadata -o assets.go -prefix assets -nocompress -pkg graphql assets/index.html
+//go:generate sh -c "sed 's#var _indexHtml#//nolint:misspell\\\n' assets.go > assets.go.tmp && mv assets.go.tmp assets.go"
+//go:generate gofmt -w -s -l assets.go
+
import (
"bytes"
"fmt"
"net/http"
+ "strings"
+
+ "github.com/ethereum/go-ethereum/log"
)
// GraphiQL is an in-browser IDE for exploring GraphiQL APIs.
@@ -52,44 +59,18 @@ func (h GraphiQL) ServeHTTP(w http.ResponseWriter, r *http.Request) {
respond(w, errorJSON("only GET requests are supported"), http.StatusMethodNotAllowed)
return
}
-
- w.Write(graphiql)
+ path := r.URL.String()
+ if path == "/" {
+ path = "/index.html"
+ }
+ blob, err := Asset(path[1:])
+ if err != nil {
+ log.Warn("Failed to load the asset", "path", path, "err", err)
+ http.Error(w, "not found", http.StatusNotFound)
+ return
+ }
+ if strings.HasSuffix(path, ".css") {
+ w.Header().Add("Content-Type", "text/css")
+ }
+ _, _ = w.Write(blob)
}
-
-var graphiql = []byte(`
-
-
-
-
-
-
-
-
-
-
- Loading...
-
-
-
-`)