github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/graphql/graphiql.go (about)

     1  // The MIT License (MIT)
     2  //
     3  // Copyright (c) 2016 Muhammed Thanish
     4  //
     5  // Permission is hereby granted, free of charge, to any person obtaining a copy
     6  // of this software and associated documentation files (the "Software"), to deal
     7  // in the Software without restriction, including without limitation the rights
     8  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     9  // copies of the Software, and to permit persons to whom the Software is
    10  // furnished to do so, subject to the following conditions:
    11  //
    12  // The above copyright notice and this permission notice shall be included in all
    13  // copies or substantial portions of the Software.
    14  //
    15  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    16  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    17  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    18  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    19  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    20  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    21  // SOFTWARE.
    22  
    23  package graphql
    24  
    25  import (
    26  	"bytes"
    27  	"context"
    28  	"fmt"
    29  	"html/template"
    30  	"net/http"
    31  
    32  	"github.com/kisexp/xdchain/plugin/security"
    33  	"github.com/kisexp/xdchain/rpc"
    34  )
    35  
    36  // GraphiQL is an in-browser IDE for exploring GraphiQL APIs.
    37  // This handler returns GraphiQL when requested.
    38  //
    39  // For more information, see https://github.com/graphql/graphiql.
    40  // Quorum
    41  // 1. Introduce 2 fields to support rendering additional HTML snippets
    42  type GraphiQL struct {
    43  	authManagerFunc security.AuthenticationManagerDeferFunc
    44  	isMPS           bool
    45  }
    46  
    47  func respond(w http.ResponseWriter, body []byte, code int) {
    48  	w.Header().Set("Content-Type", "application/json; charset=utf-8")
    49  	w.Header().Set("X-Content-Type-Options", "nosniff")
    50  	w.WriteHeader(code)
    51  	_, _ = w.Write(body)
    52  }
    53  
    54  func errorJSON(msg string) []byte {
    55  	buf := bytes.Buffer{}
    56  	fmt.Fprintf(&buf, `{"error": "%s"}`, msg)
    57  	return buf.Bytes()
    58  }
    59  
    60  func (h GraphiQL) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    61  	if r.Method != "GET" {
    62  		respond(w, errorJSON("only GET requests are supported"), http.StatusMethodNotAllowed)
    63  		return
    64  	}
    65  	w.Header().Set("Content-Type", "text/html")
    66  	html, err := h.addQuorumHTML(graphiql)
    67  	if err != nil {
    68  		respond(w, errorJSON("unable to add Quorum-specific HTML"), http.StatusInternalServerError)
    69  		return
    70  	}
    71  	w.Write(html)
    72  }
    73  
    74  func (h GraphiQL) addQuorumHTML(g []byte) ([]byte, error) {
    75  	tmpl, err := template.New("Quorum").Parse(string(g))
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  	authManager, err := h.authManagerFunc()
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  	authManagerEnabled, err := authManager.IsEnabled(context.Background())
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  	data := struct {
    88  		ShowPSI         bool
    89  		ShowAccessToken bool
    90  		AuthHeader      string
    91  		PSIHeader       string
    92  	}{
    93  		ShowPSI:         h.isMPS,
    94  		ShowAccessToken: authManagerEnabled,
    95  		AuthHeader:      rpc.HttpAuthorizationHeader,
    96  		PSIHeader:       rpc.HttpPrivateStateIdentifierHeader,
    97  	}
    98  	var buf bytes.Buffer
    99  	if err := tmpl.Execute(&buf, data); err != nil {
   100  		return nil, err
   101  	}
   102  	return buf.Bytes(), nil
   103  }
   104  
   105  var graphiql = []byte(`
   106  <!DOCTYPE html>
   107  <html>
   108  	<head>
   109  		<link
   110                  rel="icon"
   111                  type="image/png"
   112                  href="data:image/png;base64,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"
   113          />
   114          <link
   115                  rel="stylesheet"
   116                  href="https://cdnjs.cloudflare.com/ajax/libs/graphiql/0.13.0/graphiql.css"
   117                  integrity="sha384-Qua2xoKBxcHOg1ivsKWo98zSI5KD/UuBpzMIg8coBd4/jGYoxeozCYFI9fesatT0"
   118                  crossorigin="anonymous"
   119          />
   120          <script
   121                  src="https://cdnjs.cloudflare.com/ajax/libs/fetch/3.0.0/fetch.min.js"
   122                  integrity="sha384-5B8/4F9AQqp/HCHReGLSOWbyAOwnJsPrvx6C0+VPUr44Olzi99zYT1xbVh+ZanQJ"
   123                  crossorigin="anonymous"
   124          ></script>
   125          <script
   126                  src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.5/umd/react.production.min.js"
   127                  integrity="sha384-dOCiLz3nZfHiJj//EWxjwSKSC6Z1IJtyIEK/b/xlHVNdVLXDYSesoxiZb94bbuGE"
   128                  crossorigin="anonymous"
   129          ></script>
   130          <script
   131                  src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.5/umd/react-dom.production.min.js"
   132                  integrity="sha384-QI+ql5f+khgo3mMdCktQ3E7wUKbIpuQo8S5rA/3i1jg2rMsloCNyiZclI7sFQUGN"
   133                  crossorigin="anonymous"
   134          ></script>
   135          <script
   136                  src="https://cdnjs.cloudflare.com/ajax/libs/graphiql/0.13.0/graphiql.min.js"
   137                  integrity="sha384-roSmzNmO4zJK9X4lwggDi4/oVy+9V4nlS1+MN8Taj7tftJy1GvMWyAhTNXdC/fFR"
   138                  crossorigin="anonymous"
   139          ></script>
   140  {{- if or .ShowAccessToken .ShowPSI }}
   141  		<style>
   142  			#quorum-addon {
   143  				padding: 10px;
   144  				border-bottom: 1px solid #d6d6d6;
   145  			}
   146  			#quorum-addon .field {
   147  				display: block;
   148  				margin-bottom: 2px;
   149  			}
   150  			#quorum-addon abbr {
   151  				text-decoration: unset;
   152  				border-radius: 50%;
   153  				border: 1px solid #9c9c9c;
   154  				color: #9c9c9c;
   155  			}
   156  			#quorum-addon table {
   157  				border: none;
   158  			}
   159  			#quorum-addon table td {
   160  				vertical-align: top;
   161  			}
   162  			#quorum-addon input[type="text"], textarea {
   163  				padding: 3px;
   164  				border: 1px solid #d6d6d6;
   165  				display: block;
   166  			}
   167  		</style>
   168  {{- end }}
   169  	</head>
   170  	<body style="width: 100%; height: 100%; margin: 0; overflow: hidden;">
   171  {{- if or .ShowAccessToken .ShowPSI }}
   172  		<div id="quorum-addon">
   173  			<table>
   174  				<tr>
   175  {{- if .ShowAccessToken }}
   176  					<td><span class="field">Access Token <abbr title="The value is obtained by authenticating against the authorization server.">&nbsp;?&nbsp;</abbr></span>
   177  					<textarea id="access-token" rows="3" cols="40"></textarea></td>
   178  {{- end }}
   179  {{- if .ShowPSI }}
   180  					<td><span class="field">Target PSI <abbr title="The value indicates which private state is going to be used. Only required if querying private payloads.">&nbsp;?&nbsp;</abbr></span>
   181  					<input id="psi" type="text"/></td>
   182  {{- end }}
   183  				</tr>
   184  			</table>
   185  		</div>
   186  {{- end }}
   187  		<div id="graphiql" style="height: 100vh;">Loading...</div>
   188  
   189  		<script>
   190  			function fetchGQL(params) {
   191  				return fetch("/graphql", {
   192  {{- if or .ShowAccessToken .ShowPSI }}
   193  					headers: {
   194  {{- if .ShowAccessToken }}
   195  						"{{ .AuthHeader }}" : "Bearer " +  document.getElementById("access-token").value,
   196  {{- end }}
   197  {{- if .ShowPSI }}
   198  						"{{ .PSIHeader }}" : document.getElementById("psi").value,
   199  {{- end }}
   200  					},
   201  {{- end }}
   202  					method: "post",
   203  					body: JSON.stringify(params),
   204  					credentials: "include",
   205  				}).then(function (resp) {
   206  					return resp.text();
   207  				}).then(function (body) {
   208  					try {
   209  						return JSON.parse(body);
   210  					} catch (error) {
   211  						return body;
   212  					}
   213  				});
   214  			}
   215  			ReactDOM.render(
   216  				React.createElement(GraphiQL, {fetcher: fetchGQL}),
   217  				document.getElementById("graphiql")
   218  			)
   219  		</script>
   220  	</body>
   221  </html>
   222  `)