gopkg.in/hugelgupf/u-root.v2@v2.0.0-20180831055005-3f8fdb0ce09d/examples/sos/server.go (about)

     1  // Copyright 2018 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"encoding/json"
     9  	"html/template"
    10  	"io/ioutil"
    11  	"log"
    12  	"net/http"
    13  
    14  	"github.com/gorilla/mux"
    15  	"github.com/u-root/u-root/pkg/sos"
    16  )
    17  
    18  const (
    19  	// it's ugly, but we have to define a basic HTML string to fall back on if our .html is
    20  	// missing. If you are implementing a simple service, you may be able to get away with
    21  	// only this. example_sos.html is exactly the same as this string, except we use divs
    22  	// and the CSS stylesheet instead of tables, with more documentation.
    23  	DefHtmlPage = `
    24      <head>
    25      <style>
    26        table {
    27          font-family: arial, sans-serif;
    28          border-collapse: collapse;
    29          width: 100%;
    30        }
    31        td, th {
    32          border: 1px solid #dddddd;
    33          text-align: left;
    34          padding: 8px;
    35        }
    36        input {
    37          font-size: 120%;
    38        }
    39      </style>
    40      <script>
    41        function sendString() {
    42          ex = document.getElementById("field").value
    43          fetch("http://localhost:{{.Port}}/your_url_here_1", {
    44            method: 'Post',
    45            headers: {
    46              'Accept': 'application/json',
    47              'Content-Type': 'application/json'
    48            },
    49            body: JSON.stringify({
    50              Example: ex
    51            })
    52          })
    53          .then(r => r.json())
    54          .then( s => {
    55            if (s !== null) {
    56              alert(s.Error);
    57              window.location.reload();
    58            }
    59            else {
    60              window.location.reload();
    61            }
    62          })
    63          .catch(err => alert(err))
    64        }
    65  
    66        function sendEmpty() {
    67          fetch("http://localhost:{{.Port}}/your_url_here_2", {
    68            method: 'Post'
    69          })
    70          .then(r => r.json())
    71          .then( s => {
    72            if (s !== null) {
    73              alert(s.Error);
    74              window.location.reload();
    75            }
    76            else {
    77              window.location.reload();
    78            }
    79          })
    80          .catch(err => alert(err))
    81        }
    82      </script>
    83      </head>
    84      <body>
    85        {{$example := .Example}}
    86        <h1>Example</h1>
    87        <table style="width:100%">
    88          <tr>
    89            <td><input type="text" id="field"  class="text" value="{{$example}}"></td>
    90            <td><input type="submit" class="btn" value="Submit" onclick=sendString()></td>
    91            <td><input type="submit" class="btn" value="Clear" onclick=sendEmpty()></td>
    92          </tr>
    93        </table>
    94      </body>
    95    `
    96  )
    97  
    98  var Port uint
    99  
   100  // our server contains an instance of our service.
   101  type ExampleServer struct {
   102  	service *ExampleService
   103  }
   104  
   105  // displayStateHandle renders our webpage with the data from our service
   106  // * update your service (if required).
   107  // * copy your service's fields to a data struct, with the port we got from SoS.
   108  // * load the HTML file. If it doesn't exist, use the HTML string defined above.
   109  // * Render the HTML with our data.
   110  func (es *ExampleServer) displayStateHandle(w http.ResponseWriter, r *http.Request) {
   111  	// if your service has an update function, call it here to refresh its fields.
   112  	// es.service.Update()
   113  	exampleData := struct {
   114  		Example string
   115  		Port    uint
   116  	}{es.service.Example, Port}
   117  	var tmpl *template.Template
   118  	file, err := ioutil.ReadFile(sos.HTMLPath("example.html"))
   119  	if err == nil {
   120  		html := string(file)
   121  		tmpl = template.Must(template.New("SoS").Parse(html))
   122  	} else {
   123  		tmpl = template.Must(template.New("SoS").Parse(DefHtmlPage))
   124  	}
   125  	tmpl.Execute(w, exampleData)
   126  }
   127  
   128  // define a JsonMsg struct for easily passing around our data. Though it isn't
   129  // necessary in this example, once you have multiple values associated with your
   130  // service it makes things much easier. This JSON must be defined exactly as it is
   131  // in the HTML file.
   132  type ExampleJsonMsg struct {
   133  	Example string
   134  }
   135  
   136  // this is a basic JSON handler function, dealing with string passed back as JSON.
   137  // * decode the returned JSON message and store the fields in our JsonMsg struct,
   138  //   with proper error checking.
   139  // * call the associated service function, passing in this JsonMsg, with proper error checking.
   140  func (es *ExampleServer) yourHandleFunc1(w http.ResponseWriter, r *http.Request) {
   141  	var msg ExampleJsonMsg
   142  	decoder := json.NewDecoder(r.Body)
   143  	defer r.Body.Close()
   144  	if err := decoder.Decode(&msg); err != nil {
   145  		log.Printf("error: %v", err)
   146  		w.WriteHeader(http.StatusBadRequest)
   147  		json.NewEncoder(w).Encode(struct{ Error string }{err.Error()})
   148  		return
   149  	}
   150  	if err := es.service.ExampleServiceFunc1(msg); err != nil {
   151  		log.Printf("error: %v", err)
   152  		w.WriteHeader(http.StatusInternalServerError)
   153  		json.NewEncoder(w).Encode(struct{ Error string }{err.Error()})
   154  		return
   155  	}
   156  	json.NewEncoder(w).Encode(nil)
   157  }
   158  
   159  // if your service function doesn't require input, i.e. a button press, you can omit the
   160  // JSON decoding step.
   161  func (es *ExampleServer) yourHandleFunc2(w http.ResponseWriter, r *http.Request) {
   162  	if err := es.service.ExampleServiceFunc2(); err != nil {
   163  		log.Printf("error: %v", err)
   164  		w.WriteHeader(http.StatusInternalServerError)
   165  		json.NewEncoder(w).Encode(struct{ Error string }{err.Error()})
   166  		return
   167  	}
   168  	json.NewEncoder(w).Encode(nil)
   169  }
   170  
   171  // define our router
   172  // * the "/" handler is required, as it is what updates our service and renders the webpage.
   173  // * if you plan on using the Material Design CSS in pkg/sos/html/css, you need to define
   174  //   the pathPrefix for it.
   175  // * All other handlers are user-defined.
   176  func (es *ExampleServer) buildRouter() *mux.Router {
   177  	r := mux.NewRouter()
   178  	r.HandleFunc("/", es.displayStateHandle).Methods("GET")
   179  	r.PathPrefix("/css/").Handler(http.StripPrefix("/css/", http.FileServer(http.Dir(sos.HTMLPath("css")))))
   180  	r.HandleFunc("/your_url_here_1", es.yourHandleFunc1).Methods("POST")
   181  	r.HandleFunc("/your_url_here_2", es.yourHandleFunc2).Methods("POST")
   182  	return r
   183  }
   184  
   185  // start our server
   186  // * get a listener and an open port from the SoS
   187  // * build our router (see above) and add our service to the SoS table, with the label "example"
   188  func (es *ExampleServer) Start() {
   189  	listener, port, err := sos.GetListener()
   190  	if err != nil {
   191  		log.Fatalf("error: %v", err)
   192  	}
   193  	Port = port
   194  	sos.StartServiceServer(es.buildRouter(), "example", listener, Port)
   195  }
   196  
   197  // build a new server with the service passed in.
   198  func NewExampleServer(service *ExampleService) *ExampleServer {
   199  	return &ExampleServer{
   200  		service: service,
   201  	}
   202  }