Merge pull request #1323 from yousong/shadowsocks-libev
[project/luci.git] / applications / luci-app-attendedsysupgrade / luasrc / view / attendedsysupgrade.htm
1 <%+header%>
2 <h2 name="content"><%:Attended Sysupgrade%></h2>
3 <div class="container">
4         <div style="display: none" id="update_info" class="alert-message info"></div>
5         <div style="display: none" id="update_error" class="alert-message danger"></div>
6 </div>
7 <input class="cbi-button" value="search for updates" onclick="update_request()" type="button" id="update_button">
8 <div style="display: none" id="packages" class="alert-message success"></div>
9 <div class="cbi-value" id="update_packages_container" style="display: block">
10         <label class="cbi-value-title" for="keep">search for package updates:</label>
11         <div class="cbi-value-field">
12                 <input type="checkbox" name="update_packages" id="update_packages" />
13         </div>
14 </div>
15 <div class="cbi-value" id="keep_container" style="display: none">
16         <label class="cbi-value-title" for="keep">keep settings:</label>
17         <div class="cbi-value-field">
18                 <input type="checkbox" name="keep" id="keep" checked="checked" />
19         </div>
20 </div>
21
22 <script type="text/javascript">
23
24 latest_version = "";
25 data = {};
26 ubus_counter = 1
27 origin = document.location.href.replace(location.pathname, "")
28 ubus_url = origin + "/ubus/"
29
30 // requests to the update server
31 function server_request(request_dict, path, callback) {
32         url = data.update_server + "/" + path
33         request_dict.distro = data.release.distribution;
34         request_dict.target = data.release.target.split("\/")[0];
35         request_dict.subtarget = data.release.target.split("\/")[1];
36         var xmlhttp = new XMLHttpRequest();
37         xmlhttp.open("POST", url, true);
38         xmlhttp.setRequestHeader("Content-type", "application/json");
39         xmlhttp.send(JSON.stringify(request_dict));
40         xmlhttp.onerror = function(e) {
41                 update_error("update server down")
42         }
43         xmlhttp.addEventListener('load', function(event) {
44                 callback(xmlhttp)
45         });
46 }
47
48 // requests ubus via rpcd
49 function ubus_request(command, argument, params, callback) {
50         request_data = {};
51         request_data.jsonrpc = "2.0";
52         request_data.id = ubus_counter;
53         request_data.method = "call";
54         request_data.params = [ data.ubus_rpc_session, command, argument, params ]
55         ubus_counter++
56         var xmlhttp = new XMLHttpRequest();
57         xmlhttp.open("POST", ubus_url, true);
58         xmlhttp.setRequestHeader("Content-type", "application/json");
59         xmlhttp.onerror = function(e) {
60                 setTimeout(back_online, 5000)
61         }
62         xmlhttp.addEventListener('load', function(event) {
63                 if(command === "uci") {
64                         ubus_request_callback_uci(xmlhttp, callback)
65                 } else {
66                         ubus_request_callback(xmlhttp, callback)
67                 }
68         });
69         xmlhttp.send(JSON.stringify(request_data));
70 }
71
72 // handle ubus_requests, set variables or perform functions
73 function ubus_request_callback(response_object, callback) {
74         if(response_object.status === 200) {
75                 console.log(callback)
76                 if(typeof callback === "string") {
77                         response_json = JSON.parse(response_object.responseText).result[1]
78                         if (callback == "release") {
79                                 latest_version = response_json.release.version
80                         }
81                         data[callback] = response_json[callback]
82                 } else {
83                         callback(response_object)
84                 }
85         } else {
86                 console.log(respons_object.responseText)
87         }
88 }
89
90 function ubus_request_callback_uci(response_object, callback) {
91         if(response_object.status === 200) {
92                 console.log(callback)
93                 response_json = JSON.parse(response_object.responseText).result[1].value
94                 data[callback] = response_json
95
96                 document.getElementById("update_packages").checked = data.update_packages;
97         } else {
98                 console.log(respons_object.responseText)
99         }
100 }
101
102 // initial setup, get system information
103 function setup() {
104         data["ubus_rpc_session"] = "<%=luci.dispatcher.context.authsession%>"
105         ubus_request("packagelist", "list", {}, "packagelist");
106         ubus_request("system", "board", {}, "release");
107         ubus_request("system", "board", {}, "board_name");
108         ubus_request("system", "board", {}, "model");
109         ubus_request("uci", "get", { "config": "attendedsysupgrade", "section": "updateserver", "option": "url" }, "update_server")
110         ubus_request("uci", "get", { "config": "attendedsysupgrade", "section": "updateclient", "option": "update_packages" }, "update_packages")
111 }
112
113 // shows notification if update is available
114 function update_info(info_output) {
115         document.getElementById("update_info").style.display = "block";
116         document.getElementById("update_info").innerHTML = info_output;
117 }
118
119 function update_error(error_output) {
120         document.getElementById("update_error").style.display = "block";
121         document.getElementById("update_error").innerHTML = error_output;
122         document.getElementById("update_info").style.display = "none";
123 }
124
125 // asks server for news updates, actually only based on relesae not packages
126 function update_request() {
127         console.log("update_request")
128         request_dict = {}
129         request_dict.version = data.release.version;
130         request_dict.packages = data.packagelist;
131         if (document.getElementById("update_packages").checked == 1) {
132                 request_dict.update_packages = 1
133         }
134         server_request(request_dict, "update-request", update_request_callback)
135 }
136
137 function update_request_callback(response_object) {
138         if (response_object.status === 500) {
139                 // python crashed
140                 update_error("internal server error, please try again later")
141                 console.log("update server issue")
142         } else if (response_object.status === 502) {
143                 // python part offline
144                 update_error("internal server error, please try again later")
145                 console.log("update server issue")
146         } else if (response_object.status === 503) {
147                 // handle overload
148                 update_error("server overloaded, retry in 5 minutes")
149                 console.log("server overloaded")
150                 setTimeout(update_request, 300000)
151         } else if (response_object.status === 201) {
152                 update_info("imagebuilder not ready, please wait")
153                 console.log("setting up imagebuilder")
154                 setTimeout(update_request, 5000)
155         } else if (response_object.status === 204) {
156                 // no updates
157                 update_info("no updates available")
158         } else if (response_object.status === 400) {
159                 // bad request
160                 console.log(response_object.responseText)
161                 response_object_content = JSON.parse(response_object.responseText)
162                 update_error(response_object_content.error)
163         } else if (response_object.status === 200) {
164                 // new release/updates
165                 response_object_content = JSON.parse(response_object.responseText)
166                 update_request_200(response_object_content)
167         }
168 }
169
170 function back_online() {
171         ubus_request("session", "login", {}, back_online_callback)
172 }
173
174 function back_online_callback(response_object) {
175         if (response_object.status != 200) {
176                 setTimeout(back_online, 5000)
177         } else {
178                 update_info("upgrade successfull!")
179                 document.getElementById("update_button").value = "reload page";
180                 document.getElementById("update_button").onclick = function() { location.reload(); }
181         }
182
183 }
184
185 function update_request_200(response_content) {
186         info_output = ""
187         if(response_content.version != undefined) {
188                 info_output += "<h3>new update available</h3>"
189                 info_output += data.release.version + " to " + response_content.version
190                 latest_version = response_content.version;
191         }
192         if(response_content.updates != undefined) {
193                 info_output += "<h3>package updates available</h3>"
194                 for (update in response_content.updates) {
195                         info_output += "<b>" + update + "</b>: " + response_content.updates[update][1] + " to " + response_content.updates[update][0] + "</br>"
196                 }
197         }
198         data.packages = response_content.packages
199         update_info(info_output)
200         document.getElementById("update_button").value = "request image";
201         document.getElementById("update_packages_container").style.display = "none";
202         document.getElementById("update_button").onclick = image_request;
203 }
204
205 // request the image, need merge with update_request
206 function image_request() {
207         console.log("image_request")
208         document.getElementById("update_packages_container").style.display = "none";
209         request_dict = {}
210         request_dict.version = latest_version;
211         request_dict.board = data.board_name
212         request_dict.packages = data.packages;
213         request_dict.model = data.model
214         server_request(request_dict, "image-request", image_request_handler)
215 }
216
217 function image_request_handler(response) {
218         if (response.status === 400) {
219                 response_content = JSON.parse(response.responseText)
220                 update_error(response_content.error)
221         } else if (response.status === 500) {
222                 image_request_500()
223         } else if (response.status === 503) {
224                 update_error("please wait. server overloaded")
225                 // handle overload
226                 setTimeout(image_request, 30000)
227         } else if (response.status === 201) {
228                 response_content = JSON.parse(response.responseText)
229                 if(response_content.queue != undefined) {
230                         // in queue
231                         update_info("please wait. you are in queue position " + response_content.queue)
232                         console.log("queued")
233                 } else {
234                         update_info("imagebuilder not ready, please wait")
235                         console.log("setting up imagebuilder")
236                 }
237                 setTimeout(image_request, 5000)
238         } else if (response.status === 206) {
239                 // building
240                 console.log("building")
241                 update_info("building image")
242                 setTimeout(image_request, 5000)
243         } else if (response.status === 200) {
244                 // ready to download
245                 response_content = JSON.parse(response.responseText)
246                 update_info("image created")
247                 document.getElementById("update_button").value = "sysupgrade"
248                 document.getElementById("update_button").onclick = function() {download_image(response_content.url); }
249                 document.getElementById("keep_container").style.display = "block";
250         }
251 }
252
253
254 // uploads received blob data to the server using cgi-io
255 function upload_image(blob) {
256         var upload_request = new XMLHttpRequest();
257         var form_data  = new FormData();
258
259         form_data.append("sessionid", data.ubus_rpc_session)
260         form_data.append("filename", "/tmp/sysupgrade.bin")
261         form_data.append("filemode", 755) // insecure?
262         form_data.append("filedata", blob)
263
264         upload_request.addEventListener('load', function(event) {
265                 // this checksum should be parsed
266                 document.getElementById("update_info").innerHTML = "flashing... please wait" // show fancy indicator http://www.ajaxload.info/
267
268                 ubus_request("attendedsysupgrade", "sysupgrade", { "keep_settings": document.getElementById("keep").checked }, 'done');
269         });
270
271         upload_request.addEventListener('error', function(event) {
272                 document.getElementById("update_info").innerHTML = "uploading failed, please retry"
273         });
274
275         upload_request.open('POST', origin + '/cgi-bin/cgi-upload');
276         upload_request.send(form_data);
277 }
278
279 // download image from server once the url was received by image_request
280 function download_image(url) {
281         console.log("download_image")
282         document.getElementById("update_button").value = "flashing..."
283         document.getElementById("update_button").disabled = true;
284         var download_request = new XMLHttpRequest();
285         download_request.open("GET", url);
286         download_request.responseType = "arraybuffer";
287
288         download_request.onload = function () {
289                 if (this.status === 200) {
290                         var blob = new Blob([download_request.response], {type: "application/octet-stream"});
291                         upload_image(blob)
292                 }
293         };
294         document.getElementById("update_info").innerHTML = "downloading image"
295         download_request.send();
296 }
297
298 document.onload = setup()
299 </script>
300
301 <%+footer%>