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 }