dnsreport.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. 'use strict';
  2. 'require view';
  3. 'require fs';
  4. 'require ui';
  5. /*
  6. button handling
  7. */
  8. function handleAction(ev) {
  9. if (ev.target && ev.target.getAttribute('name') === 'blacklist') {
  10. L.ui.showModal(_('Add Blacklist Domain'), [
  11. E('p', _('Add this (sub-)domain to your local blacklist.')),
  12. E('div', { 'class': 'left', 'style': 'display:flex; flex-direction:column' }, [
  13. E('label', { 'class': 'cbi-input-text', 'style': 'padding-top:.5em' }, [
  14. E('input', { 'class': 'cbi-input-text', 'style': 'width:300px', 'spellcheck': 'false', 'id': 'blacklist', 'value': ev.target.getAttribute('value') }, [])
  15. ])
  16. ]),
  17. E('div', { 'class': 'right' }, [
  18. E('button', {
  19. 'class': 'btn cbi-button',
  20. 'click': L.hideModal
  21. }, _('Cancel')),
  22. ' ',
  23. E('button', {
  24. 'class': 'btn cbi-button-action',
  25. 'click': ui.createHandlerFn(this, function(ev) {
  26. L.resolveDefault(fs.read_direct('/etc/adblock/adblock.blacklist'), '')
  27. .then(function(res) {
  28. var domain = document.getElementById('blacklist').value.trim().toLowerCase().replace(/[^a-z0-9\.\-]/g,'');
  29. var pattern = new RegExp('^' + domain.replace(/[\.]/g,'\\.') + '$', 'm');
  30. if (res.search(pattern) === -1) {
  31. var blacklist = res + domain + '\n';
  32. fs.write('/etc/adblock/adblock.blacklist', blacklist);
  33. ui.addNotification(null, E('p', _('Blacklist changes have been saved. Refresh your adblock lists that changes take effect.')), 'info');
  34. }
  35. L.hideModal();
  36. });
  37. })
  38. }, _('Save'))
  39. ])
  40. ]);
  41. document.getElementById('blacklist').focus();
  42. }
  43. if (ev.target && ev.target.getAttribute('name') === 'whitelist') {
  44. L.ui.showModal(_('Add Whitelist Domain'), [
  45. E('p', _('Add this (sub-)domain to your local whitelist.')),
  46. E('div', { 'class': 'left', 'style': 'display:flex; flex-direction:column' }, [
  47. E('label', { 'class': 'cbi-input-text', 'style': 'padding-top:.5em' }, [
  48. E('input', { 'class': 'cbi-input-text', 'style': 'width:300px', 'spellcheck': 'false', 'id': 'whitelist', 'value': ev.target.getAttribute('value') }, [])
  49. ])
  50. ]),
  51. E('div', { 'class': 'right' }, [
  52. E('button', {
  53. 'class': 'btn cbi-button',
  54. 'click': L.hideModal
  55. }, _('Cancel')),
  56. ' ',
  57. E('button', {
  58. 'class': 'btn cbi-button-action',
  59. 'click': ui.createHandlerFn(this, function(ev) {
  60. L.resolveDefault(fs.read_direct('/etc/adblock/adblock.whitelist'), '')
  61. .then(function(res) {
  62. var domain = document.getElementById('whitelist').value.trim().toLowerCase().replace(/[^a-z0-9\.\-]/g,'');
  63. var pattern = new RegExp('^' + domain.replace(/[\.]/g,'\\.') + '$', 'm');
  64. if (res.search(pattern) === -1) {
  65. var whitelist = res + domain + '\n';
  66. fs.write('/etc/adblock/adblock.whitelist', whitelist);
  67. ui.addNotification(null, E('p', _('Whitelist changes have been saved. Refresh your adblock lists that changes take effect.')), 'info');
  68. }
  69. L.hideModal();
  70. });
  71. })
  72. }, _('Save'))
  73. ])
  74. ]);
  75. document.getElementById('whitelist').focus();
  76. }
  77. if (ev === 'query') {
  78. L.ui.showModal(_('Blocklist Query'), [
  79. E('p', _('Query active blocklists and backups for a specific domain.')),
  80. E('div', { 'class': 'left', 'style': 'display:flex; flex-direction:column' }, [
  81. E('label', { 'style': 'padding-top:.5em', 'id': 'run' }, [
  82. E('input', {
  83. 'class': 'cbi-input-text',
  84. 'placeholder': 'google.com',
  85. 'style': 'width:300px',
  86. 'spellcheck': 'false',
  87. 'id': 'search'
  88. })
  89. ])
  90. ]),
  91. E('div', { 'class': 'left', 'style': 'display:flex; flex-direction:column' }, [
  92. '\xa0',
  93. E('h5', _('Result')),
  94. E('textarea', {
  95. 'id': 'result',
  96. 'style': 'width: 100% !important; padding: 5px; font-family: monospace',
  97. 'readonly': 'readonly',
  98. 'wrap': 'off',
  99. 'rows': 20
  100. })
  101. ]),
  102. E('div', { 'class': 'right' }, [
  103. E('button', {
  104. 'class': 'btn cbi-button',
  105. 'click': L.hideModal
  106. }, _('Cancel')),
  107. ' ',
  108. E('button', {
  109. 'class': 'btn cbi-button-action',
  110. 'click': ui.createHandlerFn(this, function(ev) {
  111. var domain = document.getElementById('search').value.trim().toLowerCase().replace(/[^a-z0-9\.\-]/g,'');
  112. if (domain) {
  113. document.getElementById('run').classList.add("spinning");
  114. document.getElementById('search').value = domain;
  115. document.getElementById('result').textContent = 'The query is running, please wait...';
  116. L.resolveDefault(fs.exec_direct('/etc/init.d/adblock', ['query', domain])).then(function(res) {
  117. var result = document.getElementById('result');
  118. if (res) {
  119. result.textContent = res.trim();
  120. } else {
  121. result.textContent = _('No Query results!');
  122. }
  123. document.getElementById('run').classList.remove("spinning");
  124. document.getElementById('search').value = '';
  125. })
  126. }
  127. document.getElementById('search').focus();
  128. })
  129. }, _('Query'))
  130. ])
  131. ]);
  132. document.getElementById('search').focus();
  133. }
  134. if (ev === 'refresh') {
  135. L.ui.showModal(_('Refresh DNS Report'), [
  136. E('div', { 'class': 'left', 'style': 'display:flex; flex-direction:column' }, [
  137. E('label', { 'class': 'cbi-input-select', 'style': 'padding-top:.5em' }, [
  138. E('select', { 'class': 'cbi-input-select', 'id': 'top_count' }, [
  139. E('option', { 'value': '10' }, '10'),
  140. E('option', { 'value': '20' }, '20'),
  141. E('option', { 'value': '30' }, '30'),
  142. E('option', { 'value': '40' }, '40'),
  143. E('option', { 'value': '50' }, '50')
  144. ]),
  145. '\xa0\xa0\xa0',
  146. _('max. top statistics')
  147. ])
  148. ]),
  149. E('div', { 'class': 'left', 'style': 'display:flex; flex-direction:column' }, [
  150. E('label', { 'class': 'cbi-input-select', 'style': 'padding-top:.5em' }, [
  151. E('select', { 'class': 'cbi-input-select', 'id': 'res_count' }, [
  152. E('option', { 'value': '50' }, '50'),
  153. E('option', { 'value': '100' }, '100'),
  154. E('option', { 'value': '150' }, '150'),
  155. E('option', { 'value': '250' }, '250'),
  156. E('option', { 'value': '500' }, '500')
  157. ]),
  158. '\xa0\xa0\xa0',
  159. _('max. result set size')
  160. ])
  161. ]),
  162. E('label', { 'class': 'cbi-input-text', 'style': 'padding-top:.5em' }, [
  163. E('input', { 'class': 'cbi-input-text', 'spellcheck': 'false', 'id': 'search' }, [
  164. ]),
  165. '\xa0\xa0\xa0',
  166. _('Filter criteria like date, domain or client (optional)')
  167. ]),
  168. E('div', { 'class': 'right' }, [
  169. E('button', {
  170. 'class': 'btn cbi-button',
  171. 'click': L.hideModal
  172. }, _('Cancel')),
  173. ' ',
  174. E('button', {
  175. 'class': 'btn cbi-button-action',
  176. 'id': 'refresh',
  177. 'click': ui.createHandlerFn(this, async function(ev) {
  178. var top_count = document.getElementById('top_count').value;
  179. var res_count = document.getElementById('res_count').value;
  180. var search = document.getElementById('search').value.trim().replace(/[^\w\.\-\:]/g,'') || '+';
  181. L.resolveDefault(fs.exec_direct('/etc/init.d/adblock', ['report', 'gen', top_count, res_count, search]),'');
  182. var running = 1;
  183. while (running === 1) {
  184. await new Promise(r => setTimeout(r, 1000));
  185. L.resolveDefault(fs.read_direct('/var/run/adblock.pid')).then(function(res) {
  186. if (!res) {
  187. running = 0;
  188. }
  189. })
  190. }
  191. L.hideModal();
  192. location.reload();
  193. })
  194. }, _('Refresh'))
  195. ])
  196. ]);
  197. document.getElementById('refresh').focus();
  198. }
  199. }
  200. return view.extend({
  201. load: function() {
  202. return L.resolveDefault(fs.exec_direct('/etc/init.d/adblock', ['report', 'json', '10', '50', '+']),'');
  203. },
  204. render: function(dnsreport) {
  205. if (!dnsreport) {
  206. dnsreport = '{}';
  207. };
  208. var content;
  209. content = JSON.parse(dnsreport);
  210. var rows_top = [];
  211. var tbl_top = E('table', { 'class': 'table', 'id': 'top_10' }, [
  212. E('tr', { 'class': 'tr table-titles' }, [
  213. E('th', { 'class': 'th right' }, _('Count')),
  214. E('th', { 'class': 'th' }, _('Clients')),
  215. E('th', { 'class': 'th right' }, _('Count')),
  216. E('th', { 'class': 'th' }, _('Domains')),
  217. E('th', { 'class': 'th right' }, _('Count')),
  218. E('th', { 'class': 'th' }, _('Blocked Domains'))
  219. ])
  220. ]);
  221. var max = 0;
  222. if (content.top_clients && content.top_domains && content.top_blocked) {
  223. max = Math.max(content.top_clients.length, content.top_domains.length, content.top_blocked.length);
  224. }
  225. for (var i = 0; i < max; i++) {
  226. var a_cnt = '\xa0', a_addr = '\xa0', b_cnt = '\xa0', b_addr = '\xa0', c_cnt = '\xa0', c_addr = '\xa0';
  227. if (content.top_clients[i]) {
  228. a_cnt = content.top_clients[i].count;
  229. }
  230. if (content.top_clients[i]) {
  231. a_addr = content.top_clients[i].address;
  232. }
  233. if (content.top_domains[i]) {
  234. b_cnt = content.top_domains[i].count;
  235. }
  236. if (content.top_domains[i]) {
  237. b_addr = '<a href="https://duckduckgo.com/?q=' + encodeURIComponent(content.top_domains[i].address) + '&amp;k1=-1&amp;km=l&amp;kh=1" target="_blank" rel="noreferrer noopener" title="Domain Lookup">' + content.top_domains[i].address + '</a>';
  238. }
  239. if (content.top_blocked[i]) {
  240. c_cnt = content.top_blocked[i].count;
  241. }
  242. if (content.top_blocked[i]) {
  243. c_addr = '<a href="https://duckduckgo.com/?q=' + encodeURIComponent(content.top_blocked[i].address) + '&amp;k1=-1&amp;km=l&amp;kh=1" target="_blank" rel="noreferrer noopener" title="Domain Lookup">' + content.top_blocked[i].address + '</a>';
  244. }
  245. rows_top.push([
  246. a_cnt,
  247. a_addr,
  248. b_cnt,
  249. b_addr,
  250. c_cnt,
  251. c_addr
  252. ]);
  253. }
  254. cbi_update_table(tbl_top, rows_top);
  255. var rows_requests = [];
  256. var tbl_requests = E('table', { 'class': 'table', 'id': 'requests' }, [
  257. E('tr', { 'class': 'tr table-titles' }, [
  258. E('th', { 'class': 'th' }, _('Date')),
  259. E('th', { 'class': 'th' }, _('Time')),
  260. E('th', { 'class': 'th' }, _('Client')),
  261. E('th', { 'class': 'th' }, _('Domain')),
  262. E('th', { 'class': 'th' }, _('Answer')),
  263. E('th', { 'class': 'th' }, _('Action'))
  264. ])
  265. ]);
  266. max = 0;
  267. if (content.requests) {
  268. var button;
  269. max = content.requests.length;
  270. for (var i = 0; i < max; i++) {
  271. if (content.requests[i].rc === 'NX') {
  272. button = E('button', {
  273. 'class': 'btn cbi-button cbi-button-positive',
  274. 'style': 'word-break: inherit',
  275. 'name': 'whitelist',
  276. 'value': content.requests[i].domain,
  277. 'click': handleAction
  278. }, [ _('Whitelist...') ]);
  279. } else {
  280. button = E('button', {
  281. 'class': 'btn cbi-button cbi-button-negative',
  282. 'style': 'word-break: inherit',
  283. 'name': 'blacklist',
  284. 'value': content.requests[i].domain,
  285. 'click': handleAction
  286. }, [ _('Blacklist...') ]);
  287. }
  288. rows_requests.push([
  289. content.requests[i].date,
  290. content.requests[i].time,
  291. content.requests[i].client,
  292. '<a href="https://duckduckgo.com/?q=' + encodeURIComponent(content.requests[i].domain) + '&amp;k1=-1&amp;km=l&amp;kh=1" target="_blank" rel="noreferrer noopener" title="Domain Lookup">' + content.requests[i].domain + '</a>',
  293. content.requests[i].rc,
  294. button
  295. ]);
  296. }
  297. }
  298. cbi_update_table(tbl_requests, rows_requests);
  299. return E('div', { 'class': 'cbi-map', 'id': 'map' }, [
  300. E('div', { 'class': 'cbi-section' }, [
  301. E('p', _('This tab shows the last generated DNS Report, press the \'Refresh\' button to get a current one.')),
  302. E('p', '\xa0'),
  303. E('div', { 'class': 'cbi-value' }, [
  304. E('div', { 'class': 'cbi-value-title', 'style': 'float:left;width:230px' }, _('Start Timestamp')),
  305. E('div', { 'class': 'cbi-value-title', 'id': 'start', 'style': 'float:left;color:#37c' }, (content.start_date || '-') + ', ' + (content.start_time || '-'))
  306. ]),
  307. E('div', { 'class': 'cbi-value' }, [
  308. E('div', { 'class': 'cbi-value-title', 'style': 'float:left;width:230px' }, _('End Timestamp')),
  309. E('div', { 'class': 'cbi-value-title', 'id': 'end', 'style': 'float:left;color:#37c' }, (content.end_date || '-') + ', ' + (content.end_time || '-'))
  310. ]),
  311. E('div', { 'class': 'cbi-value' }, [
  312. E('div', { 'class': 'cbi-value-title', 'style': 'float:left;width:230px' }, _('Total DNS Requests')),
  313. E('div', { 'class': 'cbi-value-title', 'id': 'total', 'style': 'float:left;color:#37c' }, content.total || '-')
  314. ]),
  315. E('div', { 'class': 'cbi-value' }, [
  316. E('div', { 'class': 'cbi-value-title', 'style': 'float:left;width:230px' }, _('Blocked DNS Requests')),
  317. E('div', { 'class': 'cbi-value-title', 'id': 'blocked', 'style': 'float:left;color:#37c' }, (content.blocked || '-') + ' (' + (content.percent || '-') + ')')
  318. ]),
  319. E('div', { 'class': 'right' }, [
  320. E('button', {
  321. 'class': 'btn cbi-button cbi-button-apply',
  322. 'click': ui.createHandlerFn(this, function() {
  323. return handleAction('query');
  324. })
  325. }, [ _('Blocklist Query...') ]),
  326. '\xa0\xa0\xa0',
  327. E('button', {
  328. 'class': 'btn cbi-button cbi-button-positive',
  329. 'click': ui.createHandlerFn(this, function() {
  330. return handleAction('refresh');
  331. })
  332. }, [ _('Refresh...') ])
  333. ]),
  334. ]),
  335. E('div', { 'class': 'cbi-section' }, [
  336. E('div', { 'class': 'left' }, [
  337. E('h3', _('Top Statistics')),
  338. tbl_top
  339. ])
  340. ]),
  341. E('br'),
  342. E('div', { 'class': 'cbi-section' }, [
  343. E('div', { 'class': 'left' }, [
  344. E('h3', _('Latest DNS Requests')),
  345. tbl_requests
  346. ])
  347. ])
  348. ]);
  349. },
  350. handleSaveApply: null,
  351. handleSave: null,
  352. handleReset: null
  353. });