]> git.ipfire.org Git - people/pmueller/ipfire-2.x.git/blob - html/html/include/pakfire.js
pakfire.cgi: Implement JavaScript log message display
[people/pmueller/ipfire-2.x.git] / html / html / include / pakfire.js
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;
35
36 // Status refresh helper
37 this._autoRefresh = {
38 delay: 1000, //Delay between requests (default: 1s)
39 jsonAction: 'getstatus', //CGI POST action parameter
40 timeout: 5000, //XHR timeout (5s)
41
42 delayTimer: null, //setTimeout reference
43 jqXHR: undefined, //jQuery.ajax promise reference
44 get runningDelay() { //Waiting for end of delay
45 return (this.delayTimer !== null);
46 },
47 get runningXHR() { //Waiting for CGI response
48 return (this.jqXHR && (this.jqXHR.state() === 'pending'));
49 },
50 get isRunning() {
51 return (this.runningDelay || this.runningXHR);
52 }
53 };
54 }
55
56 //### Public properties ###
57
58 // Pakfire is running (true/false)
59 set running(state) {
60 if(this._states.running !== state) {
61 this._states.running = state;
62 this._states_onChange('running');
63 }
64 }
65 get running() {
66 return this._states.running;
67 }
68
69 // Reboot needed (true/false)
70 set reboot(state) {
71 if(this._states.reboot !== state) {
72 this._states.reboot = state;
73 this._states_onChange('reboot');
74 }
75 }
76 get reboot() {
77 return this._states.reboot;
78 }
79
80 // Status refresh interval in ms
81 set refreshInterval(delay) {
82 if(delay < 500) {
83 delay = 500; //enforce reasonable minimum
84 }
85 this._autoRefresh.delay = delay;
86 }
87 get refreshInterval() {
88 return this._autoRefresh.delay;
89 }
90
91 // Document loaded (call once from jQuery.ready)
92 documentReady() {
93 // Status refresh late start
94 if(this.running && (! this._autoRefresh.isRunning)) {
95 this._autoRefresh_runNow();
96 }
97 }
98
99 //### Private properties ###
100
101 // Pakfire status change handler
102 // property: Affected status (running, reboot, ...)
103 _states_onChange(property) {
104 // Always update UI
105 if(this.running) {
106 $('#pflog-status').text(this.i18n.get('working'));
107 $('#pflog-action').empty();
108 } else {
109 $('#pflog-status').text(this.i18n.get('finished'));
110 if(this.reboot) { //Enable return or reboot links in UI
111 $('#pflog-action').html(this.i18n.get('link_reboot'));
112 } else {
113 $('#pflog-action').html(this.i18n.get('link_return'));
114 }
115 }
116
117 // Start/stop status refresh if Pakfire started/stopped
118 if(property === 'running') {
119 if(this.running) {
120 this._autoRefresh_runNow();
121 } else {
122 this._autoRefresh_clearSchedule();
123 }
124 }
125 }
126
127 //--- Status refresh scheduling functions ---
128
129 // Immediately perform AJAX status refresh request
130 _autoRefresh_runNow() {
131 if(this._autoRefresh.runningXHR) {
132 return; // Don't send multiple requests
133 }
134 this._autoRefresh_clearSchedule(); // Stop scheduled refresh, will send immediately
135
136 // Send AJAX request, attach listeners
137 this._autoRefresh.jqXHR = this._JSON_get(this._autoRefresh.jsonAction, this._autoRefresh.timeout);
138 this._autoRefresh.jqXHR.done(function() { // Request succeeded
139 if(this.running) { // Keep refreshing while Pakfire is running
140 this._autoRefresh_scheduleRun();
141 }
142 });
143 this._autoRefresh.jqXHR.fail(function() { // Request failed
144 this._autoRefresh_scheduleRun(); // Try refreshing until valid status is received
145 });
146 }
147
148 // Schedule next refresh
149 _autoRefresh_scheduleRun() {
150 if(this._autoRefresh.runningDelay || this._autoRefresh.runningXHR) {
151 return; // Refresh already scheduled or in progress
152 }
153 this._autoRefresh.delayTimer = window.setTimeout(function() {
154 this._autoRefresh.delayTimer = null;
155 this._autoRefresh_runNow();
156 }.bind(this), this._autoRefresh.delay);
157 }
158
159 // Stop scheduled refresh (can still be refreshed up to 1x if XHR is already sent)
160 _autoRefresh_clearSchedule() {
161 if(this._autoRefresh.runningDelay) {
162 window.clearTimeout(this._autoRefresh.delayTimer);
163 this._autoRefresh.delayTimer = null;
164 }
165 }
166
167 //--- JSON request & data handling ---
168
169 // Load JSON data from Pakfire CGI, using a POST request
170 // action: POST paramter "json-[action]"
171 // maxTime: XHR timeout, 0 = no timeout
172 _JSON_get(action, maxTime = 0) {
173 return $.ajax({
174 url: '/cgi-bin/pakfire.cgi',
175 method: 'POST',
176 timeout: maxTime,
177 context: this,
178 data: {'ACTION': `json-${action}`},
179 dataType: 'json' //automatically check and convert result
180 })
181 .done(function(response) {
182 this._JSON_process(action, response);
183 });
184 }
185
186 // Process successful response from Pakfire CGI
187 // action: POST paramter "json-[action]" used to send request
188 // data: JSON data object
189 _JSON_process(action, data) {
190 // Pakfire status refresh
191 if(action === this._autoRefresh.jsonAction) {
192 // Update status flags
193 this.running = (data['running'] != '0');
194 this.reboot = (data['reboot'] != '0');
195
196 // Update timer display
197 if(this.running && data['running_since']) {
198 $('#pflog-time').text(this.i18n.get('since') + data['running_since']);
199 } else {
200 $('#pflog-time').empty();
201 }
202
203 // Print log messages
204 let messages = "";
205 data['messages'].forEach(function(line) {
206 messages += `${line}\n`;
207 });
208 $('#pflog-messages').text(messages);
209 }
210 }
211 }
212
213 // Simple translation strings helper
214 // Format: {key: "translation"}
215 class PakfireI18N {
216 constructor() {
217 this._strings = Object.create(null); //Object without prototypes
218 }
219
220 // Get translation
221 get(key) {
222 if(Object.prototype.hasOwnProperty.call(this._strings, key)) {
223 return this._strings[key];
224 }
225 return `(undefined string '${key}')`;
226 }
227
228 // Load key/translation object
229 load(translations) {
230 if(translations instanceof Object) {
231 Object.assign(this._strings, translations);
232 }
233 }
234 }
235
236 //### Initialize Pakfire ###
237 const pakfire = new PakfireJS();
238
239 $(function() {
240 pakfire.documentReady();
241 });