luci-app-adblock-fast: update to 1.0.0-4
[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 statusError: _("Error"),
116 statusWarning: _("Warning"),
117 statusFail: _("Fail"),
118 statusSuccess: _("Active"),
119 };
120
121 var header = E("h2", {}, _("AdBlock-Fast - Status"));
122 var statusTitle = E(
123 "label",
124 { class: "cbi-value-title" },
125 _("Service Status")
126 );
127 if (reply.status.version) {
128 text += _("Version %s").format(reply.status.version) + " - ";
129 switch (reply.status.status) {
130 case "statusSuccess":
131 text += statusTable[reply.status.status] + ".";
132 text +=
133 "<br />" +
134 _("Blocking %s domains (with %s).").format(
135 reply.status.entries,
136 reply.status.dns
137 );
138 if (reply.status.outputGzipExists) {
139 text += "<br />" + _("Compressed cache file created.");
140 }
141 if (reply.status.force_dns_active) {
142 text += "<br />" + _("Force DNS ports:");
143 reply.status.force_dns_ports.forEach((element) => {
144 text += " " + element;
145 });
146 text += ".";
147 }
148 break;
149 case "statusStopped":
150 if (reply.status.enabled) {
151 text += statusTable[reply.status.status] + ".";
152 } else {
153 text +=
154 statusTable[reply.status.status] +
155 " (" +
156 _("Disabled") +
157 ").";
158 }
159 if (reply.status.outputCacheExists) {
160 text += "<br />" + _("Cache file found.");
161 } else if (reply.status.outputGzipExists) {
162 text += "<br />" + _("Compressed cache file found.");
163 }
164 break;
165 case "statusRestarting":
166 case "statusForceReloading":
167 case "statusDownloading":
168 case "statusProcessing":
169 text += statusTable[reply.status.status] + "...";
170 break;
171 default:
172 text += statusTable[reply.status.status] + ".";
173 break;
174 }
175 } else {
176 text = _("Not installed or not found");
177 }
178 var statusText = E("div", {}, text);
179 var statusField = E("div", { class: "cbi-value-field" }, statusText);
180 var statusDiv = E("div", { class: "cbi-value" }, [
181 statusTitle,
182 statusField,
183 ]);
184
185 var warningsDiv = [];
186 if (reply.status.warnings && reply.status.warnings.length) {
187 var warningTable = {
188 warningExternalDnsmasqConfig: _(
189 "Use of external dnsmasq config file detected, please set '%s' option to '%s'"
190 ).format("dns", "dnsmasq.conf"),
191 warningMissingRecommendedPackages: _(
192 "Some recommended packages are missing"
193 ),
194 };
195 var warningsTitle = E(
196 "label",
197 { class: "cbi-value-title" },
198 _("Service Warnings")
199 );
200 var text = "";
201 reply.status.warnings.forEach((element) => {
202 text +=
203 warningTable[element.id].format(element.extra || " ") + "<br />";
204 });
205 var warningsText = E("div", {}, text);
206 var warningsField = E(
207 "div",
208 { class: "cbi-value-field" },
209 warningsText
210 );
211 warningsDiv = E("div", { class: "cbi-value" }, [
212 warningsTitle,
213 warningsField,
214 ]);
215 }
216
217 var errorsDiv = [];
218 if (reply.status.errors && reply.status.errors.length) {
219 var errorTable = {
220 errorConfigValidationFail: _(
221 "Config (%s) validation failure!"
222 ).format("/etc/config/" + pkg.Name),
223 errorServiceDisabled: _("%s is currently disabled").format(
224 pkg.Name
225 ),
226 errorNoDnsmasqIpset: _(
227 "The dnsmasq ipset support is enabled, but dnsmasq is either not installed or installed dnsmasq does not support ipset"
228 ),
229 errorNoIpset: _(
230 "The dnsmasq ipset support is enabled, but ipset is either not installed or installed ipset does not support '%s' type"
231 ).format("hash:net"),
232 errorNoDnsmasqNftset: _(
233 "The dnsmasq nft set support is enabled, but dnsmasq is either not installed or installed dnsmasq does not support nft set"
234 ),
235 errorNoNft: _(
236 "The dnsmasq nft sets support is enabled, but nft is not installed"
237 ),
238 errorNoWanGateway: _(
239 "The %s failed to discover WAN gateway"
240 ).format(pkg.Name),
241 errorOutputDirCreate: _("Failed to create directory for %s file"),
242 errorOutputFileCreate: _("Failed to create '%s' file").format(
243 outputFile
244 ),
245 errorFailDNSReload: _("Failed to restart/reload DNS resolver"),
246 errorSharedMemory: _("Failed to access shared memory"),
247 errorSorting: _("Failed to sort data file"),
248 errorOptimization: _("Failed to optimize data file"),
249 errorAllowListProcessing: _("Failed to process allow-list"),
250 errorDataFileFormatting: _("Failed to format data file"),
251 errorMovingDataFile: _(
252 "Failed to move temporary data file to '%s'"
253 ).format(outputFile),
254 errorCreatingCompressedCache: _(
255 "Failed to create compressed cache"
256 ),
257 errorRemovingTempFiles: _("Failed to remove temporary files"),
258 errorRestoreCompressedCache: _("Failed to unpack compressed cache"),
259 errorRestoreCache: _("Failed to move '%s' to '%s'").format(
260 outputCache,
261 outputFile
262 ),
263 errorOhSnap: _(
264 "Failed to create block-list or restart DNS resolver"
265 ),
266 errorStopping: _("Failed to stop %s").format(pkg.Name),
267 errorDNSReload: _("Failed to reload/restart DNS resolver"),
268 errorDownloadingConfigUpdate: _(
269 "Failed to download Config Update file"
270 ),
271 errorDownloadingList: _("Failed to download %s"),
272 errorParsingConfigUpdate: _("Failed to parse Config Update file"),
273 errorParsingList: _("Failed to parse %s"),
274 errorNoSSLSupport: _("No HTTPS/SSL support on device"),
275 errorCreatingDirectory: _(
276 "Failed to create output/cache/gzip file directory"
277 ),
278 errorDetectingFileType: _("Failed to detect format %s"),
279 errorNothingToDo: _(
280 "No blocked list URLs nor blocked-domains enabled"
281 ),
282 };
283 var errorsTitle = E(
284 "label",
285 { class: "cbi-value-title" },
286 _("Service Errors")
287 );
288 var text = "";
289 reply.status.errors.forEach((element) => {
290 text +=
291 errorTable[element.id].format(element.extra || " ") + "<br />";
292 });
293 text += _("Errors encountered, please check the %sREADME%s!").format(
294 '<a href="' + pkg.URL + '" target="_blank">',
295 "</a><br />"
296 );
297 var errorsText = E("div", {}, text);
298 var errorsField = E("div", { class: "cbi-value-field" }, errorsText);
299 errorsDiv = E("div", { class: "cbi-value" }, [
300 errorsTitle,
301 errorsField,
302 ]);
303 }
304
305 var btn_gap = E("span", {}, "&#160;&#160;");
306 var btn_gap_long = E(
307 "span",
308 {},
309 "&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;"
310 );
311
312 var btn_start = E(
313 "button",
314 {
315 class: "btn cbi-button cbi-button-apply",
316 disabled: true,
317 click: function (ev) {
318 ui.showModal(null, [
319 E(
320 "p",
321 { class: "spinning" },
322 _("Starting %s service").format(pkg.Name)
323 ),
324 ]);
325 return RPC.setInitAction(pkg.Name, "start");
326 },
327 },
328 _("Start")
329 );
330
331 var btn_action = E(
332 "button",
333 {
334 class: "btn cbi-button cbi-button-apply",
335 disabled: true,
336 click: function (ev) {
337 ui.showModal(null, [
338 E(
339 "p",
340 { class: "spinning" },
341 _("Force re-downloading %s block lists").format(pkg.Name)
342 ),
343 ]);
344 return RPC.setInitAction(pkg.Name, "dl");
345 },
346 },
347 _("Force Re-Download")
348 );
349
350 var btn_stop = E(
351 "button",
352 {
353 class: "btn cbi-button cbi-button-reset",
354 disabled: true,
355 click: function (ev) {
356 ui.showModal(null, [
357 E(
358 "p",
359 { class: "spinning" },
360 _("Stopping %s service").format(pkg.Name)
361 ),
362 ]);
363 return RPC.setInitAction(pkg.Name, "stop");
364 },
365 },
366 _("Stop")
367 );
368
369 var btn_enable = E(
370 "button",
371 {
372 class: "btn cbi-button cbi-button-apply",
373 disabled: true,
374 click: function (ev) {
375 ui.showModal(null, [
376 E(
377 "p",
378 { class: "spinning" },
379 _("Enabling %s service").format(pkg.Name)
380 ),
381 ]);
382 return RPC.setInitAction(pkg.Name, "enable");
383 },
384 },
385 _("Enable")
386 );
387
388 var btn_disable = E(
389 "button",
390 {
391 class: "btn cbi-button cbi-button-reset",
392 disabled: true,
393 click: function (ev) {
394 ui.showModal(null, [
395 E(
396 "p",
397 { class: "spinning" },
398 _("Disabling %s service").format(pkg.Name)
399 ),
400 ]);
401 return RPC.setInitAction(pkg.Name, "disable");
402 },
403 },
404 _("Disable")
405 );
406
407 if (reply.status.enabled) {
408 btn_enable.disabled = true;
409 btn_disable.disabled = false;
410 switch (reply.status.status) {
411 case "statusSuccess":
412 btn_start.disabled = true;
413 btn_action.disabled = false;
414 btn_stop.disabled = false;
415 break;
416 case "statusStopped":
417 btn_start.disabled = false;
418 btn_action.disabled = true;
419 btn_stop.disabled = true;
420 break;
421 default:
422 btn_start.disabled = false;
423 btn_action.disabled = true;
424 btn_stop.disabled = false;
425 btn_enable.disabled = true;
426 btn_disable.disabled = true;
427 break;
428 }
429 } else {
430 btn_start.disabled = true;
431 btn_action.disabled = true;
432 btn_stop.disabled = true;
433 btn_enable.disabled = false;
434 btn_disable.disabled = true;
435 }
436
437 var buttonsDiv = [];
438 var buttonsTitle = E(
439 "label",
440 { class: "cbi-value-title" },
441 _("Service Control")
442 );
443 var buttonsText = E("div", {}, [
444 btn_start,
445 btn_gap,
446 btn_action,
447 btn_gap,
448 btn_stop,
449 btn_gap_long,
450 btn_enable,
451 btn_gap,
452 btn_disable,
453 ]);
454 var buttonsField = E("div", { class: "cbi-value-field" }, buttonsText);
455 if (reply.status.version) {
456 buttonsDiv = E("div", { class: "cbi-value" }, [
457 buttonsTitle,
458 buttonsField,
459 ]);
460 }
461
462 return E("div", {}, [
463 header,
464 statusDiv,
465 warningsDiv,
466 errorsDiv,
467 buttonsDiv,
468 ]);
469 }
470 );
471 },
472 });
473
474 RPC.on("setInitAction", function (reply) {
475 ui.hideModal();
476 location.reload();
477 });
478
479 return L.Class.extend({
480 status: status,
481 getFileUrlFilesizes: getFileUrlFilesizes,
482 getPlatformSupport: getPlatformSupport,
483 });