6c5ef19c11c98ffc066f9dbe4d9d102410d25808
[project/luci.git] / applications / luci-app-simple-adblock / htdocs / luci-static / resources / simple-adblock / status.js
1 // Copyright 2022 Stan Grishin <stangri@melmac.ca>
2 // This code wouldn't have been possible without help from [@vsviridov](https://github.com/vsviridov)
3
4 "require ui";
5 "require rpc";
6 "require form";
7 "require baseclass";
8
9 var pkg = {
10 get Name() { return 'simple-adblock'; },
11 get URL() { return 'https://docs.openwrt.melmac.net/' + pkg.Name + '/'; },
12 };
13
14 var getInitList = rpc.declare({
15 object: "luci." + pkg.Name,
16 method: "getInitList",
17 params: ["name"],
18 });
19
20 var getInitStatus = rpc.declare({
21 object: "luci." + pkg.Name,
22 method: "getInitStatus",
23 params: ["name"],
24 });
25
26 var getPlatformSupport = rpc.declare({
27 object: "luci." + pkg.Name,
28 method: "getPlatformSupport",
29 params: ["name"],
30 });
31
32 var _setInitAction = rpc.declare({
33 object: "luci." + pkg.Name,
34 method: "setInitAction",
35 params: ["name", "action"],
36 expect: { result: false },
37 });
38
39 var RPC = {
40 listeners: [],
41 on: function on(event, callback) {
42 var pair = { event: event, callback: callback }
43 this.listeners.push(pair);
44 return function unsubscribe() {
45 this.listeners = this.listeners.filter(function (listener) {
46 return listener !== pair;
47 });
48 }.bind(this);
49 },
50 emit: function emit(event, data) {
51 this.listeners.forEach(function (listener) {
52 if (listener.event === event) {
53 listener.callback(data);
54 }
55 });
56 },
57 getInitList: function getInitList(name) {
58 getInitList(name).then(function (result) {
59 this.emit('getInitList', result);
60 }.bind(this));
61 },
62 getInitStatus: function getInitStatus(name) {
63 getInitStatus(name).then(function (result) {
64 this.emit('getInitStatus', result);
65 }.bind(this));
66 },
67 getPlatformSupport: function getPlatformSupport(name) {
68 getPlatformSupport(name).then(function (result) {
69 this.emit('getPlatformSupport', result);
70 }.bind(this));
71 },
72 setInitAction: function setInitAction(name, action) {
73 _setInitAction(name, action).then(function (result) {
74 this.emit('setInitAction', result);
75 }.bind(this));
76 },
77 }
78
79 var status = baseclass.extend({
80 render: function () {
81 return Promise.all([
82 L.resolveDefault(getInitStatus(pkg.Name), {}),
83 ]).then(function (data) {
84 var replyStatus = data[0];
85 var text ="";
86 var reply = replyStatus[pkg.Name];
87 var outputFile = reply.outputFile;
88 var outputCache = reply.outputCache;
89 var statusTable = {
90 statusNoInstall: _("%s is not installed or not found").format(pkg.Name),
91 statusStopped: _("Stopped"),
92 statusStarting: _("Starting"),
93 statusProcessing: _("Processing lists"),
94 statusRestarting: _("Restarting"),
95 statusForceReloading: _("Force Reloading"),
96 statusDownloading: _("Downloading lists"),
97 statusError: _("Error"),
98 statusWarning: _("Warning"),
99 statusFail: _("Fail"),
100 statusSuccess: _("Active")
101 };
102
103 var header = E('h2', {}, _("Simple AdBlock - Status"))
104 var statusTitle = E('label', { class: 'cbi-value-title' }, _("Service Status"));
105 if (reply.version) {
106 text += _("Version %s").format(reply.version) + " - ";
107 switch (reply.status) {
108 case 'statusSuccess':
109 text += statusTable[reply.status] + ".";
110 text += "<br />" + _("Blocking %s domains (with %s).").format(reply.entries, reply.dns);
111 if (reply.outputGzipExists) {
112 text += "<br />" + _("Compressed cache file created.");
113 }
114 if (reply.force_dns_active) {
115 text += "<br />" + _("Force DNS ports:");
116 reply.force_dns_ports.forEach(element => {
117 text += " " + element;
118 });
119 text += ".";
120 }
121 break;
122 case 'statusStopped':
123 if (reply.enabled) {
124 text += statusTable[reply.status] + ".";
125 }
126 else {
127 text += statusTable[reply.status] + _("disabled") + "."
128 }
129 if (reply.outputCacheExists) {
130 text += "<br />" + _("Cache file found.");
131 }
132 else if (reply.outputGzipExists) {
133 text += "<br />" + _("Compressed cache file found.");
134 }
135 break;
136 case 'statusRestarting':
137 case 'statusForceReloading':
138 case 'statusDownloading':
139 case 'statusProcessing':
140 text += statusTable[reply.status] + "...";
141 break;
142 default:
143 text += statusTable[reply.status] + ".";
144 break;
145 }
146 }
147 else {
148 text = _("Not installed or not found");
149 }
150 var statusText = E('div', {}, text);
151 var statusField = E('div', { class: 'cbi-value-field' }, statusText);
152 var statusDiv = E('div', { class: 'cbi-value' }, [statusTitle, statusField]);
153
154 var warningsDiv = [];
155 if (reply.warnings && reply.warnings.length) {
156 var warningTable = {
157 warningExternalDnsmasqConfig: _("use of external dnsmasq config file detected, please set '%s' option to '%s'").format("dns", "dnsmasq.conf"),
158 warningMissingRecommendedPackages: _("some recommended packages are missing")
159 }
160 var warningsTitle = E('label', { class: 'cbi-value-title' }, _("Service Warnings"));
161 var text = "";
162 (reply.warnings).forEach(element => {
163 text += (warningTable[element.id]).format(element.extra || ' ') + "<br />";
164 });
165 var warningsText = E('div', {}, text);
166 var warningsField = E('div', { class: 'cbi-value-field' }, warningsText);
167 warningsDiv = E('div', { class: 'cbi-value' }, [warningsTitle, warningsField]);
168 }
169
170 var errorsDiv = [];
171 if (reply.errors && reply.errors.length) {
172 var errorTable = {
173 errorConfigValidationFail: _("config (%s) validation failure!").format('/etc/config/' + pkg.Name),
174 errorServiceDisabled: _("%s is currently disabled").format(pkg.Name),
175 errorNoDnsmasqIpset: _("dnsmasq ipset support is enabled, but dnsmasq is either not installed or installed dnsmasq does not support ipset"),
176 errorNoIpset: _("dnsmasq ipset support is enabled, but ipset is either not installed or installed ipset does not support '%s' type").format("hash:net"),
177 errorNoDnsmasqNftset: _("dnsmasq nft set support is enabled, but dnsmasq is either not installed or installed dnsmasq does not support nft set"),
178 errorNoNft: _("dnsmasq nft sets support is enabled, but nft is not installed"),
179 errorNoWanGateway: _("the %s failed to discover WAN gateway").format(pkg.Name),
180 errorOutputDirCreate: _("failed to create directory for %s file"),
181 errorOutputFileCreate: _("failed to create '%s' file").format(outputFile),
182 errorFailDNSReload: _("failed to restart/reload DNS resolver"),
183 errorSharedMemory: _("failed to access shared memory"),
184 errorSorting: _("failed to sort data file"),
185 errorOptimization: _("failed to optimize data file"),
186 errorAllowListProcessing: _("failed to process allow-list"),
187 errorDataFileFormatting: _("failed to format data file"),
188 errorMovingDataFile: _("failed to move temporary data file to '%s'").format(outputFile),
189 errorCreatingCompressedCache: _("failed to create compressed cache"),
190 errorRemovingTempFiles: _("failed to remove temporary files"),
191 errorRestoreCompressedCache: _("failed to unpack compressed cache"),
192 errorRestoreCache: _("failed to move '%s' to '%s'").format(outputCache, outputFile),
193 errorOhSnap: _("failed to create block-list or restart DNS resolver"),
194 errorStopping: _("failed to stop %s").format(pkg.Name),
195 errorDNSReload: _("failed to reload/restart DNS resolver"),
196 errorDownloadingConfigUpdate: _("failed to download Config Update file"),
197 errorDownloadingList: _("failed to download"),
198 errorParsingConfigUpdate: _("failed to parse Config Update file"),
199 errorParsingList: _("failed to parse"),
200 errorNoSSLSupport: _("no HTTPS/SSL support on device"),
201 errorCreatingDirectory: _("failed to create output/cache/gzip file directory")
202 }
203 var errorsTitle = E('label', { class: 'cbi-value-title' }, _("Service Errors"));
204 var text = "";
205 (reply.errors).forEach(element => {
206 text += (errorTable[element.id]).format(element.extra || ' ') + "<br />";
207 });
208 var errorsText = E('div', {}, text);
209 var errorsField = E('div', { class: 'cbi-value-field' }, errorsText);
210 errorsDiv = E('div', { class: 'cbi-value' }, [errorsTitle, errorsField]);
211 }
212
213 var btn_gap = E('span', {}, '&#160;&#160;');
214 var btn_gap_long = E('span', {}, '&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;');
215
216 var btn_start = E('button', {
217 'class': 'btn cbi-button cbi-button-apply',
218 disabled: true,
219 click: function (ev) {
220 ui.showModal(null, [
221 E('p', { 'class': 'spinning' }, _('Starting %s service').format(pkg.Name))
222 ]);
223 return RPC.setInitAction(pkg.Name, 'start');
224 }
225 }, _('Start'));
226
227 var btn_action = E('button', {
228 'class': 'btn cbi-button cbi-button-apply',
229 disabled: true,
230 click: function (ev) {
231 ui.showModal(null, [
232 E('p', { 'class': 'spinning' }, _('Force re-downloading %s block lists').format(pkg.Name))
233 ]);
234 return RPC.setInitAction(pkg.Name, 'dl');
235 }
236 }, _('Force Re-Download'));
237
238 var btn_stop = E('button', {
239 'class': 'btn cbi-button cbi-button-reset',
240 disabled: true,
241 click: function (ev) {
242 ui.showModal(null, [
243 E('p', { 'class': 'spinning' }, _('Stopping %s service').format(pkg.Name))
244 ]);
245 return RPC.setInitAction(pkg.Name, 'stop');
246 }
247 }, _('Stop'));
248
249 var btn_enable = E('button', {
250 'class': 'btn cbi-button cbi-button-apply',
251 disabled: true,
252 click: function (ev) {
253 ui.showModal(null, [
254 E('p', { 'class': 'spinning' }, _('Enabling %s service').format(pkg.Name))
255 ]);
256 return RPC.setInitAction(pkg.Name, 'enable');
257 }
258 }, _('Enable'));
259
260 var btn_disable = E('button', {
261 'class': 'btn cbi-button cbi-button-reset',
262 disabled: true,
263 click: function (ev) {
264 ui.showModal(null, [
265 E('p', { 'class': 'spinning' }, _('Disabling %s service').format(pkg.Name))
266 ]);
267 return RPC.setInitAction(pkg.Name, 'disable');
268 }
269 }, _('Disable'));
270
271 if (reply.enabled) {
272 btn_enable.disabled = true;
273 btn_disable.disabled = false;
274 switch (reply.status) {
275 case 'statusSuccess':
276 btn_start.disabled = true;
277 btn_action.disabled = false;
278 btn_stop.disabled = false;
279 break;
280 case 'statusStopped':
281 btn_start.disabled = false;
282 btn_action.disabled = true;
283 btn_stop.disabled = true;
284 break;
285 default:
286 btn_start.disabled = false;
287 btn_action.disabled = true;
288 btn_stop.disabled = false;
289 btn_enable.disabled = true;
290 btn_disable.disabled = true;
291 break;
292 }
293 }
294 else {
295 btn_start.disabled = true;
296 btn_action.disabled = true;
297 btn_stop.disabled = true;
298 btn_enable.disabled = false;
299 btn_disable.disabled = true;
300 }
301
302 var buttonsDiv = [];
303 var buttonsTitle = E('label', { class: 'cbi-value-title' }, _("Service Control"))
304 var buttonsText = E('div', {}, [btn_start, btn_gap, btn_action, btn_gap, btn_stop, btn_gap_long, btn_enable, btn_gap, btn_disable]);
305 var buttonsField = E('div', { class: 'cbi-value-field' }, buttonsText);
306 if (reply.version) {
307 buttonsDiv = E('div', { class: 'cbi-value' }, [buttonsTitle, buttonsField]);
308 }
309
310 return E('div', {}, [header, statusDiv, warningsDiv, errorsDiv, buttonsDiv]);
311 });
312 },
313 });
314
315 RPC.on('setInitAction', function (reply) {
316 ui.hideModal();
317 location.reload();
318 });
319
320 return L.Class.extend({
321 status: status,
322 getPlatformSupport: getPlatformSupport
323 });