luci-app-banip: sync with forthcoming banIP 1.0
[project/luci.git] / applications / luci-app-adblock-fast / htdocs / luci-static / resources / adblock-fast / status.js
1 // Copyright MOSSDeF, 2023 Stan Grishin <stangri@melmac.ca>
2 // This code wouldn't have been possible without help from:
3 // - [@vsviridov](https://github.com/vsviridov)
4
5 "require ui";
6 "require rpc";
7 "require form";
8 "require baseclass";
9
10 var pkg = {
11 get Name() {
12 return "adblock-fast";
13 },
14 get URL() {
15 return "https://docs.openwrt.melmac.net/" + pkg.Name + "/";
16 },
17 };
18
19 var getFileUrlFilesizes = rpc.declare({
20 object: "luci." + pkg.Name,
21 method: "getFileUrlFilesizes",
22 params: ["name", "url"],
23 });
24
25 var getInitList = rpc.declare({
26 object: "luci." + pkg.Name,
27 method: "getInitList",
28 params: ["name"],
29 });
30
31 var getInitStatus = rpc.declare({
32 object: "luci." + pkg.Name,
33 method: "getInitStatus",
34 params: ["name"],
35 });
36
37 var getPlatformSupport = rpc.declare({
38 object: "luci." + pkg.Name,
39 method: "getPlatformSupport",
40 params: ["name"],
41 });
42
43 var _setInitAction = rpc.declare({
44 object: "luci." + pkg.Name,
45 method: "setInitAction",
46 params: ["name", "action"],
47 expect: { result: false },
48 });
49
50 var RPC = {
51 listeners: [],
52 on: function (event, callback) {
53 var pair = { event: event, callback: callback };
54 this.listeners.push(pair);
55 return function unsubscribe() {
56 this.listeners = this.listeners.filter(function (listener) {
57 return listener !== pair;
58 });
59 }.bind(this);
60 },
61 emit: function (event, data) {
62 this.listeners.forEach(function (listener) {
63 if (listener.event === event) {
64 listener.callback(data);
65 }
66 });
67 },
68 setInitAction: function (name, action) {
69 _setInitAction(name, action).then(
70 function (result) {
71 this.emit("setInitAction", result);
72 }.bind(this)
73 );
74 },
75 };
76
77 var status = baseclass.extend({
78 render: function () {
79 return Promise.all([L.resolveDefault(getInitStatus(pkg.Name), {})]).then(
80 function (data) {
81 var reply = {
82 status: (data[0] && data[0][pkg.Name]) || {
83 enabled: false,
84 status: null,
85 running: null,
86 version: null,
87 errors: [],
88 warnings: [],
89 force_dns_active: null,
90 force_dns_ports: [],
91 entries: null,
92 dns: null,
93 outputFile: null,
94 outputCache: null,
95 outputGzip: null,
96 outputFileExists: null,
97 outputCacheExists: null,
98 outputGzipExists: null,
99 leds: [],
100 },
101 };
102 var text = "";
103 var outputFile = reply.status.outputFile;
104 var outputCache = reply.status.outputCache;
105 var statusTable = {
106 statusNoInstall: _("%s is not installed or not found").format(
107 pkg.Name
108 ),
109 statusStopped: _("Stopped"),
110 statusStarting: _("Starting"),
111 statusProcessing: _("Processing lists"),
112 statusRestarting: _("Restarting"),
113 statusForceReloading: _("Force Reloading"),
114 statusDownloading: _("Downloading lists"),
115 statusFail: _("Failed to start"),
116 statusSuccess: _("Active"),
117 };
118
119 var header = E("h2", {}, _("AdBlock-Fast - Status"));
120 var statusTitle = E(
121 "label",
122 { class: "cbi-value-title" },
123 _("Service Status")
124 );
125 if (reply.status.version) {
126 text += _("Version %s").format(reply.status.version) + " - ";
127 switch (reply.status.status) {
128 case "statusSuccess":
129 text += statusTable[reply.status.status] + ".";
130 text +=
131 "<br />" +
132 _("Blocking %s domains (with %s).").format(
133 reply.status.entries,
134 reply.status.dns
135 );
136 if (reply.status.outputGzipExists) {
137 text += "<br />" + _("Compressed cache file created.");
138 }
139 if (reply.status.force_dns_active) {
140 text += "<br />" + _("Force DNS ports:");
141 reply.status.force_dns_ports.forEach((element) => {
142 text += " " + element;
143 });
144 text += ".";
145 }
146 break;
147 case "statusStopped":
148 if (reply.status.enabled) {
149 text += statusTable[reply.status.status] + ".";
150 } else {
151 text +=
152 statusTable[reply.status.status] +
153 " (" +
154 _("Disabled") +
155 ").";
156 }
157 if (reply.status.outputCacheExists) {
158 text += "<br />" + _("Cache file found.");
159 } else if (reply.status.outputGzipExists) {
160 text += "<br />" + _("Compressed cache file found.");
161 }
162 break;
163 case "statusRestarting":
164 case "statusForceReloading":
165 case "statusDownloading":
166 case "statusProcessing":
167 text += statusTable[reply.status.status] + "...";
168 break;
169 default:
170 text += statusTable[reply.status.status] + ".";
171 break;
172 }
173 } else {
174 text = _("Not installed or not found");
175 }
176 var statusText = E("div", {}, text);
177 var statusField = E("div", { class: "cbi-value-field" }, statusText);
178 var statusDiv = E("div", { class: "cbi-value" }, [
179 statusTitle,
180 statusField,
181 ]);
182
183 var warningsDiv = [];
184 if (reply.status.warnings && reply.status.warnings.length) {
185 var warningTable = {
186 warningExternalDnsmasqConfig: _(
187 "Use of external dnsmasq config file detected, please set '%s' option to '%s'"
188 ).format("dns", "dnsmasq.conf"),
189 warningMissingRecommendedPackages: _(
190 "Some recommended packages are missing"
191 ),
192 warningOutdatedLuciPackage: _(
193 "The WebUI application (luci-app-adblock-fast) is outdated, please update it"
194 ),
195 warningOutdatedPrincipalPackage: _(
196 "The principal package (adblock-fast) is outdated, please update it"
197 ),
198 warningInvalidCompressedCacheDir: _(
199 "Invalid compressed cache directory '%s'"
200 ),
201 warningFreeRamCheckFail: _("Can't detect free RAM"),
202 };
203 var warningsTitle = E(
204 "label",
205 { class: "cbi-value-title" },
206 _("Service Warnings")
207 );
208 var text = "";
209 reply.status.warnings.forEach((element) => {
210 if (element.id && warningTable[element.id])
211 text +=
212 warningTable[element.id].format(element.extra || " ") +
213 "<br />";
214 else text += _("Unknown warning") + "<br />";
215 });
216 var warningsText = E("div", {}, text);
217 var warningsField = E(
218 "div",
219 { class: "cbi-value-field" },
220 warningsText
221 );
222 warningsDiv = E("div", { class: "cbi-value" }, [
223 warningsTitle,
224 warningsField,
225 ]);
226 }
227
228 var errorsDiv = [];
229 if (reply.status.errors && reply.status.errors.length) {
230 var errorTable = {
231 errorConfigValidationFail: _(
232 "Config (%s) validation failure!"
233 ).format("/etc/config/" + pkg.Name),
234 errorServiceDisabled: _("%s is currently disabled").format(
235 pkg.Name
236 ),
237 errorNoDnsmasqIpset: _(
238 "The dnsmasq ipset support is enabled, but dnsmasq is either not installed or installed dnsmasq does not support ipset"
239 ),
240 errorNoIpset: _(
241 "The dnsmasq ipset support is enabled, but ipset is either not installed or installed ipset does not support '%s' type"
242 ).format("hash:net"),
243 errorNoDnsmasqNftset: _(
244 "The dnsmasq nft set support is enabled, but dnsmasq is either not installed or installed dnsmasq does not support nft set"
245 ),
246 errorNoNft: _(
247 "The dnsmasq nft sets support is enabled, but nft is not installed"
248 ),
249 errorNoWanGateway: _(
250 "The %s failed to discover WAN gateway"
251 ).format(pkg.Name),
252 errorOutputDirCreate: _("Failed to create directory for %s file"),
253 errorOutputFileCreate: _("Failed to create '%s' file").format(
254 outputFile
255 ),
256 errorFailDNSReload: _("Failed to restart/reload DNS resolver"),
257 errorSharedMemory: _("Failed to access shared memory"),
258 errorSorting: _("Failed to sort data file"),
259 errorOptimization: _("Failed to optimize data file"),
260 errorAllowListProcessing: _("Failed to process allow-list"),
261 errorDataFileFormatting: _("Failed to format data file"),
262 errorMovingDataFile: _(
263 "Failed to move temporary data file to '%s'"
264 ).format(outputFile),
265 errorCreatingCompressedCache: _(
266 "Failed to create compressed cache"
267 ),
268 errorRemovingTempFiles: _("Failed to remove temporary files"),
269 errorRestoreCompressedCache: _("Failed to unpack compressed cache"),
270 errorRestoreCache: _("Failed to move '%s' to '%s'").format(
271 outputCache,
272 outputFile
273 ),
274 errorOhSnap: _(
275 "Failed to create block-list or restart DNS resolver"
276 ),
277 errorStopping: _("Failed to stop %s").format(pkg.Name),
278 errorDNSReload: _("Failed to reload/restart DNS resolver"),
279 errorDownloadingConfigUpdate: _(
280 "Failed to download Config Update file"
281 ),
282 errorDownloadingList: _("Failed to download %s"),
283 errorParsingConfigUpdate: _("Failed to parse Config Update file"),
284 errorParsingList: _("Failed to parse %s"),
285 errorNoSSLSupport: _("No HTTPS/SSL support on device"),
286 errorCreatingDirectory: _(
287 "Failed to create output/cache/gzip file directory"
288 ),
289 errorDetectingFileType: _("Failed to detect format %s"),
290 errorNothingToDo: _(
291 "No blocked list URLs nor blocked-domains enabled"
292 ),
293 errorTooLittleRam: _(
294 "Free ram (%s) is not enough to process all enabled block-lists"
295 ),
296 };
297 var errorsTitle = E(
298 "label",
299 { class: "cbi-value-title" },
300 _("Service Errors")
301 );
302 var text = "";
303 reply.status.errors.forEach((element) => {
304 if (element.id && errorTable[element.id])
305 text +=
306 errorTable[element.id].format(element.extra || " ") + "!<br />";
307 else text += _("Unknown error") + "<br />";
308 });
309 text += _("Errors encountered, please check the %sREADME%s").format(
310 '<a href="' + pkg.URL + '" target="_blank">',
311 "</a>!<br />"
312 );
313 var errorsText = E("div", {}, text);
314 var errorsField = E("div", { class: "cbi-value-field" }, errorsText);
315 errorsDiv = E("div", { class: "cbi-value" }, [
316 errorsTitle,
317 errorsField,
318 ]);
319 }
320
321 var btn_gap = E("span", {}, "&#160;&#160;");
322 var btn_gap_long = E(
323 "span",
324 {},
325 "&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;"
326 );
327
328 var btn_start = E(
329 "button",
330 {
331 class: "btn cbi-button cbi-button-apply",
332 disabled: true,
333 click: function (ev) {
334 ui.showModal(null, [
335 E(
336 "p",
337 { class: "spinning" },
338 _("Starting %s service").format(pkg.Name)
339 ),
340 ]);
341 return RPC.setInitAction(pkg.Name, "start");
342 },
343 },
344 _("Start")
345 );
346
347 var btn_action_dl = E(
348 "button",
349 {
350 class: "btn cbi-button cbi-button-apply",
351 disabled: true,
352 click: function (ev) {
353 ui.showModal(null, [
354 E(
355 "p",
356 { class: "spinning" },
357 _("Force redownloading %s block lists").format(pkg.Name)
358 ),
359 ]);
360 return RPC.setInitAction(pkg.Name, "dl");
361 },
362 },
363 _("Redownload")
364 );
365
366 var btn_action_pause = E(
367 "button",
368 {
369 class: "btn cbi-button cbi-button-apply",
370 disabled: true,
371 click: function (ev) {
372 ui.showModal(null, [
373 E("p", { class: "spinning" }, _("Pausing %s").format(pkg.Name)),
374 ]);
375 return RPC.setInitAction(pkg.Name, "pause");
376 },
377 },
378 _("Pause")
379 );
380
381 var btn_stop = E(
382 "button",
383 {
384 class: "btn cbi-button cbi-button-reset",
385 disabled: true,
386 click: function (ev) {
387 ui.showModal(null, [
388 E(
389 "p",
390 { class: "spinning" },
391 _("Stopping %s service").format(pkg.Name)
392 ),
393 ]);
394 return RPC.setInitAction(pkg.Name, "stop");
395 },
396 },
397 _("Stop")
398 );
399
400 var btn_enable = E(
401 "button",
402 {
403 class: "btn cbi-button cbi-button-apply",
404 disabled: true,
405 click: function (ev) {
406 ui.showModal(null, [
407 E(
408 "p",
409 { class: "spinning" },
410 _("Enabling %s service").format(pkg.Name)
411 ),
412 ]);
413 return RPC.setInitAction(pkg.Name, "enable");
414 },
415 },
416 _("Enable")
417 );
418
419 var btn_disable = E(
420 "button",
421 {
422 class: "btn cbi-button cbi-button-reset",
423 disabled: true,
424 click: function (ev) {
425 ui.showModal(null, [
426 E(
427 "p",
428 { class: "spinning" },
429 _("Disabling %s service").format(pkg.Name)
430 ),
431 ]);
432 return RPC.setInitAction(pkg.Name, "disable");
433 },
434 },
435 _("Disable")
436 );
437
438 if (reply.status.enabled) {
439 btn_enable.disabled = true;
440 btn_disable.disabled = false;
441 switch (reply.status.status) {
442 case "statusSuccess":
443 btn_start.disabled = true;
444 btn_action_dl.disabled = false;
445 btn_action_pause.disabled = false;
446 btn_stop.disabled = false;
447 break;
448 case "statusStopped":
449 btn_start.disabled = false;
450 btn_action_dl.disabled = true;
451 btn_action_pause.disabled = true;
452 btn_stop.disabled = true;
453 break;
454 default:
455 btn_start.disabled = false;
456 btn_action_dl.disabled = true;
457 btn_action_pause.disabled = true;
458 btn_stop.disabled = false;
459 btn_enable.disabled = true;
460 btn_disable.disabled = true;
461 break;
462 }
463 } else {
464 btn_start.disabled = true;
465 btn_action_dl.disabled = true;
466 btn_action_pause.disabled = true;
467 btn_stop.disabled = true;
468 btn_enable.disabled = false;
469 btn_disable.disabled = true;
470 }
471
472 var buttonsDiv = [];
473 var buttonsTitle = E(
474 "label",
475 { class: "cbi-value-title" },
476 _("Service Control")
477 );
478 var buttonsText = E("div", {}, [
479 btn_start,
480 btn_gap,
481 // btn_action_pause,
482 // btn_gap,
483 btn_action_dl,
484 btn_gap,
485 btn_stop,
486 btn_gap_long,
487 btn_enable,
488 btn_gap,
489 btn_disable,
490 ]);
491 var buttonsField = E("div", { class: "cbi-value-field" }, buttonsText);
492 if (reply.status.version) {
493 buttonsDiv = E("div", { class: "cbi-value" }, [
494 buttonsTitle,
495 buttonsField,
496 ]);
497 }
498
499 return E("div", {}, [
500 header,
501 statusDiv,
502 warningsDiv,
503 errorsDiv,
504 buttonsDiv,
505 ]);
506 }
507 );
508 },
509 });
510
511 RPC.on("setInitAction", function (reply) {
512 ui.hideModal();
513 location.reload();
514 });
515
516 return L.Class.extend({
517 status: status,
518 getFileUrlFilesizes: getFileUrlFilesizes,
519 getPlatformSupport: getPlatformSupport,
520 });