]>
Commit | Line | Data |
---|---|---|
db9ee62e LAH |
1 | /*############################################################################# |
2 | # # | |
3 | # IPFire.org - A linux based firewall # | |
4 | # Copyright (C) 2007-2021 IPFire Team <info@ipfire.org> # | |
5 | # # | |
6 | # This program is free software: you can redistribute it and/or modify # | |
7 | # it under the terms of the GNU General Public License as published by # | |
8 | # the Free Software Foundation, either version 3 of the License, or # | |
9 | # (at your option) any later version. # | |
10 | # # | |
11 | # This program is distributed in the hope that it will be useful, # | |
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of # | |
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # | |
14 | # GNU General Public License for more details. # | |
15 | # # | |
16 | # You should have received a copy of the GNU General Public License # | |
17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. # | |
18 | # # | |
19 | #############################################################################*/ | |
20 | ||
21 | "use strict"; | |
22 | ||
23 | // Pakfire Javascript functions (requires jQuery) | |
24 | class PakfireJS { | |
25 | constructor() { | |
26 | //--- Public properties --- | |
27 | // Translation strings | |
28 | this.i18n = new PakfireI18N(); | |
29 | ||
30 | //--- Private properties --- | |
31 | // Status flags (access outside constructor only with setter/getter) | |
32 | this._states = Object.create(null); | |
33 | this._states.running = false; | |
34 | this._states.reboot = false; | |
4d70f591 | 35 | this._states.failure = false; |
db9ee62e LAH |
36 | |
37 | // Status refresh helper | |
38 | this._autoRefresh = { | |
4d70f591 | 39 | delay: 1000, //Delay between requests (minimum: 500, default: 1s) |
db9ee62e | 40 | jsonAction: 'getstatus', //CGI POST action parameter |
4d70f591 | 41 | timeout: 5000, //XHR timeout (0 to disable, default: 5s) |
db9ee62e LAH |
42 | |
43 | delayTimer: null, //setTimeout reference | |
44 | jqXHR: undefined, //jQuery.ajax promise reference | |
45 | get runningDelay() { //Waiting for end of delay | |
46 | return (this.delayTimer !== null); | |
47 | }, | |
48 | get runningXHR() { //Waiting for CGI response | |
49 | return (this.jqXHR && (this.jqXHR.state() === 'pending')); | |
50 | }, | |
51 | get isRunning() { | |
52 | return (this.runningDelay || this.runningXHR); | |
53 | } | |
54 | }; | |
4d70f591 LAH |
55 | |
56 | // Return to main screen helper | |
57 | this._pageReload = { | |
58 | delay: 1000, //Delay before page reload (default: 1s) | |
59 | enabled: false, //Reload disabled by default | |
60 | ||
61 | delayTimer: null, //setTimeout reference | |
62 | get isTriggered() { //Reload timer started | |
63 | return (this.delayTimer !== null); | |
64 | } | |
65 | }; | |
db9ee62e LAH |
66 | } |
67 | ||
68 | //### Public properties ### | |
69 | ||
4d70f591 LAH |
70 | // Note on using the status flags |
71 | // running: Pakfire is performing a task. | |
72 | // Writing "true" activates the periodic AJAX/JSON status polling, writing "false" stops polling. | |
73 | // When the task has been completed, status polling stops and this returns to "false". | |
74 | // The page can then be reloaded to go back to the main screen. Writing "false" does not trigger a reload. | |
75 | // "refreshInterval" and "setupPageReload" can be used to adjust the respective behaviour. | |
76 | // reboot: An update requires a reboot. | |
77 | // If set to "true", a link to the reboot menu is shown after the task is completed. | |
78 | // failure: An error has occured. | |
79 | // To display the error log, the page does not return to the main screen. | |
80 | ||
db9ee62e LAH |
81 | // Pakfire is running (true/false) |
82 | set running(state) { | |
83 | if(this._states.running !== state) { | |
84 | this._states.running = state; | |
85 | this._states_onChange('running'); | |
86 | } | |
87 | } | |
88 | get running() { | |
89 | return this._states.running; | |
90 | } | |
91 | ||
92 | // Reboot needed (true/false) | |
93 | set reboot(state) { | |
94 | if(this._states.reboot !== state) { | |
95 | this._states.reboot = state; | |
96 | this._states_onChange('reboot'); | |
97 | } | |
98 | } | |
99 | get reboot() { | |
100 | return this._states.reboot; | |
101 | } | |
102 | ||
4d70f591 LAH |
103 | // Error encountered (true/false) |
104 | set failure(state) { | |
105 | if(this._states.failure !== state) { | |
106 | this._states.failure = state; | |
107 | this._states_onChange('failure'); | |
108 | } | |
109 | } | |
110 | get failure() { | |
111 | return this._states.failure; | |
112 | } | |
113 | ||
db9ee62e LAH |
114 | // Status refresh interval in ms |
115 | set refreshInterval(delay) { | |
116 | if(delay < 500) { | |
117 | delay = 500; //enforce reasonable minimum | |
118 | } | |
119 | this._autoRefresh.delay = delay; | |
120 | } | |
121 | get refreshInterval() { | |
122 | return this._autoRefresh.delay; | |
123 | } | |
124 | ||
4d70f591 LAH |
125 | // Configure page reload after successful task (returns to main screen) |
126 | // delay: In ms | |
127 | setupPageReload(enabled, delay) { | |
128 | if(delay < 0) { | |
129 | delay = 0; | |
130 | } | |
131 | this._pageReload.delay = delay; | |
132 | this._pageReload.enabled = enabled; | |
133 | } | |
134 | ||
db9ee62e LAH |
135 | // Document loaded (call once from jQuery.ready) |
136 | documentReady() { | |
137 | // Status refresh late start | |
138 | if(this.running && (! this._autoRefresh.isRunning)) { | |
139 | this._autoRefresh_runNow(); | |
140 | } | |
141 | } | |
142 | ||
4d70f591 LAH |
143 | // Reload entire CGI page (clears POST/GET data from history) |
144 | documentReload() { | |
145 | let url = window.location.origin + window.location.pathname; | |
146 | window.location.replace(url); | |
147 | } | |
148 | ||
db9ee62e LAH |
149 | //### Private properties ### |
150 | ||
151 | // Pakfire status change handler | |
152 | // property: Affected status (running, reboot, ...) | |
153 | _states_onChange(property) { | |
154 | // Always update UI | |
155 | if(this.running) { | |
156 | $('#pflog-status').text(this.i18n.get('working')); | |
157 | $('#pflog-action').empty(); | |
158 | } else { | |
4d70f591 LAH |
159 | if(this.failure) { |
160 | $('#pflog-status').text(this.i18n.get('finished error')); | |
161 | } else { | |
162 | $('#pflog-status').text(this.i18n.get('finished')); | |
163 | } | |
db9ee62e | 164 | if(this.reboot) { //Enable return or reboot links in UI |
4d70f591 | 165 | $('#pflog-action').html(this.i18n.get('link_return') + " • " + this.i18n.get('link_reboot')); |
db9ee62e LAH |
166 | } else { |
167 | $('#pflog-action').html(this.i18n.get('link_return')); | |
168 | } | |
169 | } | |
170 | ||
171 | // Start/stop status refresh if Pakfire started/stopped | |
172 | if(property === 'running') { | |
173 | if(this.running) { | |
174 | this._autoRefresh_runNow(); | |
175 | } else { | |
176 | this._autoRefresh_clearSchedule(); | |
177 | } | |
178 | } | |
4d70f591 LAH |
179 | |
180 | // Always stay in the log viewer if Pakfire failed | |
181 | if(property === 'failure') { | |
182 | if(this.failure) { | |
183 | this._pageReload_cancel(); | |
184 | } | |
185 | } | |
db9ee62e LAH |
186 | } |
187 | ||
188 | //--- Status refresh scheduling functions --- | |
189 | ||
190 | // Immediately perform AJAX status refresh request | |
191 | _autoRefresh_runNow() { | |
192 | if(this._autoRefresh.runningXHR) { | |
193 | return; // Don't send multiple requests | |
194 | } | |
195 | this._autoRefresh_clearSchedule(); // Stop scheduled refresh, will send immediately | |
196 | ||
197 | // Send AJAX request, attach listeners | |
198 | this._autoRefresh.jqXHR = this._JSON_get(this._autoRefresh.jsonAction, this._autoRefresh.timeout); | |
199 | this._autoRefresh.jqXHR.done(function() { // Request succeeded | |
200 | if(this.running) { // Keep refreshing while Pakfire is running | |
201 | this._autoRefresh_scheduleRun(); | |
202 | } | |
203 | }); | |
204 | this._autoRefresh.jqXHR.fail(function() { // Request failed | |
205 | this._autoRefresh_scheduleRun(); // Try refreshing until valid status is received | |
206 | }); | |
207 | } | |
208 | ||
209 | // Schedule next refresh | |
210 | _autoRefresh_scheduleRun() { | |
211 | if(this._autoRefresh.runningDelay || this._autoRefresh.runningXHR) { | |
212 | return; // Refresh already scheduled or in progress | |
213 | } | |
214 | this._autoRefresh.delayTimer = window.setTimeout(function() { | |
215 | this._autoRefresh.delayTimer = null; | |
216 | this._autoRefresh_runNow(); | |
217 | }.bind(this), this._autoRefresh.delay); | |
218 | } | |
219 | ||
220 | // Stop scheduled refresh (can still be refreshed up to 1x if XHR is already sent) | |
221 | _autoRefresh_clearSchedule() { | |
222 | if(this._autoRefresh.runningDelay) { | |
223 | window.clearTimeout(this._autoRefresh.delayTimer); | |
224 | this._autoRefresh.delayTimer = null; | |
225 | } | |
226 | } | |
227 | ||
4d70f591 LAH |
228 | // Start delayed page reload to return to main screen |
229 | _pageReload_trigger() { | |
230 | if((! this._pageReload.enabled) || this._pageReload.isTriggered) { | |
231 | return; // Disabled or already started | |
232 | } | |
233 | this._pageReload.delayTimer = window.setTimeout(function() { | |
234 | this._pageReload.delayTimer = null; | |
235 | this.documentReload(); | |
236 | }.bind(this), this._pageReload.delay); | |
237 | } | |
238 | ||
239 | // Stop scheduled reload | |
240 | _pageReload_cancel() { | |
241 | if(this._pageReload.isTriggered) { | |
242 | window.clearTimeout(this._pageReload.delayTimer); | |
243 | this._pageReload.delayTimer = null; | |
244 | } | |
245 | } | |
246 | ||
db9ee62e LAH |
247 | //--- JSON request & data handling --- |
248 | ||
249 | // Load JSON data from Pakfire CGI, using a POST request | |
250 | // action: POST paramter "json-[action]" | |
251 | // maxTime: XHR timeout, 0 = no timeout | |
252 | _JSON_get(action, maxTime = 0) { | |
253 | return $.ajax({ | |
254 | url: '/cgi-bin/pakfire.cgi', | |
255 | method: 'POST', | |
256 | timeout: maxTime, | |
257 | context: this, | |
258 | data: {'ACTION': `json-${action}`}, | |
259 | dataType: 'json' //automatically check and convert result | |
260 | }) | |
261 | .done(function(response) { | |
262 | this._JSON_process(action, response); | |
263 | }); | |
264 | } | |
265 | ||
266 | // Process successful response from Pakfire CGI | |
267 | // action: POST paramter "json-[action]" used to send request | |
268 | // data: JSON data object | |
269 | _JSON_process(action, data) { | |
270 | // Pakfire status refresh | |
271 | if(action === this._autoRefresh.jsonAction) { | |
272 | // Update status flags | |
273 | this.running = (data['running'] != '0'); | |
274 | this.reboot = (data['reboot'] != '0'); | |
4d70f591 | 275 | this.failure = (data['failure'] != '0'); |
db9ee62e LAH |
276 | |
277 | // Update timer display | |
278 | if(this.running && data['running_since']) { | |
4d70f591 | 279 | $('#pflog-time').text(this.i18n.get('since') + " " + data['running_since']); |
db9ee62e LAH |
280 | } else { |
281 | $('#pflog-time').empty(); | |
282 | } | |
283 | ||
284 | // Print log messages | |
285 | let messages = ""; | |
286 | data['messages'].forEach(function(line) { | |
287 | messages += `${line}\n`; | |
288 | }); | |
289 | $('#pflog-messages').text(messages); | |
4d70f591 LAH |
290 | |
291 | // Pakfire finished without errors, return to main screen | |
292 | if((! this.running) && (! this.failure)) { | |
293 | this._pageReload_trigger(); | |
294 | } | |
db9ee62e LAH |
295 | } |
296 | } | |
297 | } | |
298 | ||
299 | // Simple translation strings helper | |
300 | // Format: {key: "translation"} | |
301 | class PakfireI18N { | |
302 | constructor() { | |
303 | this._strings = Object.create(null); //Object without prototypes | |
304 | } | |
305 | ||
306 | // Get translation | |
307 | get(key) { | |
308 | if(Object.prototype.hasOwnProperty.call(this._strings, key)) { | |
309 | return this._strings[key]; | |
310 | } | |
311 | return `(undefined string '${key}')`; | |
312 | } | |
313 | ||
314 | // Load key/translation object | |
315 | load(translations) { | |
316 | if(translations instanceof Object) { | |
317 | Object.assign(this._strings, translations); | |
318 | } | |
319 | } | |
320 | } | |
321 | ||
322 | //### Initialize Pakfire ### | |
323 | const pakfire = new PakfireJS(); | |
324 | ||
325 | $(function() { | |
326 | pakfire.documentReady(); | |
327 | }); |