debug.js 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259
  1. $(document).ready(function() {
  2. // Parse seconds ago to date
  3. // Get "now" timestamp
  4. var ts_now = Math.round((new Date()).getTime() / 1000);
  5. $('.parse_s_ago').each(function(i, parse_s_ago) {
  6. var started_s_ago = parseInt($(this).text(), 10);
  7. if (typeof started_s_ago != 'NaN') {
  8. var started_date = new Date((ts_now - started_s_ago) * 1000);
  9. if (started_date instanceof Date && !isNaN(started_date)) {
  10. var started_local_date = started_date.toLocaleDateString(undefined, {
  11. year: "numeric",
  12. month: "2-digit",
  13. day: "2-digit",
  14. hour: "2-digit",
  15. minute: "2-digit",
  16. second: "2-digit"
  17. });
  18. $(this).text(started_local_date);
  19. } else {
  20. $(this).text('-');
  21. }
  22. }
  23. });
  24. // Parse general dates
  25. $('.parse_date').each(function(i, parse_date) {
  26. var started_date = new Date(Date.parse($(this).text()));
  27. if (typeof started_date != 'NaN') {
  28. var started_local_date = started_date.toLocaleDateString(undefined, {
  29. year: "numeric",
  30. month: "2-digit",
  31. day: "2-digit",
  32. hour: "2-digit",
  33. minute: "2-digit",
  34. second: "2-digit"
  35. });
  36. $(this).text(started_local_date);
  37. }
  38. });
  39. // set default ChartJs Font Color
  40. Chart.defaults.color = '#999';
  41. // create net and disk charts
  42. createNetAndDiskChart();
  43. // check for new version
  44. check_update(mailcow_info.version_tag, mailcow_info.project_url);
  45. });
  46. jQuery(function($){
  47. if (localStorage.getItem("current_page") === null) {
  48. var current_page = {};
  49. } else {
  50. var current_page = JSON.parse(localStorage.getItem('current_page'));
  51. }
  52. // http://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery
  53. var entityMap={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"};
  54. function escapeHtml(n){return String(n).replace(/[&<>"'`=\/]/g,function(n){return entityMap[n]})}
  55. function humanFileSize(i){if(Math.abs(i)<1024)return i+" B";var B=["KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"],e=-1;do{i/=1024,++e}while(Math.abs(i)>=1024&&e<B.length-1);return i.toFixed(1)+" "+B[e]}
  56. function hashCode(t){for(var n=0,r=0;r<t.length;r++)n=t.charCodeAt(r)+((n<<5)-n);return n}
  57. function intToRGB(t){var n=(16777215&t).toString(16).toUpperCase();return"00000".substring(0,6-n.length)+n}
  58. $(".refresh_table").on('click', function(e) {
  59. e.preventDefault();
  60. var table_name = $(this).data('table');
  61. $('#' + table_name).DataTable().ajax.reload();
  62. });
  63. function draw_autodiscover_logs() {
  64. // just recalc width if instance already exists
  65. if ($.fn.DataTable.isDataTable('#autodiscover_log') ) {
  66. $('#autodiscover_log').DataTable().columns.adjust().responsive.recalc();
  67. return;
  68. }
  69. $('#autodiscover_log').DataTable({
  70. processing: true,
  71. serverSide: false,
  72. language: lang_datatables,
  73. order: [[0, 'desc']],
  74. ajax: {
  75. type: "GET",
  76. url: "/api/v1/get/logs/autodiscover/100",
  77. dataSrc: function(data){
  78. return process_table_data(data, 'autodiscover_log');
  79. }
  80. },
  81. columns: [
  82. {
  83. title: lang.time,
  84. data: 'time',
  85. defaultContent: '',
  86. render: function(data, type){
  87. var date = new Date(data ? data * 1000 : 0);
  88. return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
  89. }
  90. },
  91. {
  92. title: 'User-Agent',
  93. data: 'ua',
  94. defaultContent: ''
  95. },
  96. {
  97. title: 'Username',
  98. data: 'user',
  99. defaultContent: ''
  100. },
  101. {
  102. title: 'IP',
  103. data: 'ip',
  104. defaultContent: ''
  105. },
  106. {
  107. title: 'Service',
  108. data: 'service',
  109. defaultContent: ''
  110. }
  111. ]
  112. });
  113. }
  114. function draw_postfix_logs() {
  115. // just recalc width if instance already exists
  116. if ($.fn.DataTable.isDataTable('#postfix_log') ) {
  117. $('#postfix_log').DataTable().columns.adjust().responsive.recalc();
  118. return;
  119. }
  120. $('#postfix_log').DataTable({
  121. processing: true,
  122. serverSide: false,
  123. language: lang_datatables,
  124. order: [[0, 'desc']],
  125. ajax: {
  126. type: "GET",
  127. url: "/api/v1/get/logs/postfix",
  128. dataSrc: function(data){
  129. return process_table_data(data, 'general_syslog');
  130. }
  131. },
  132. columns: [
  133. {
  134. title: lang.time,
  135. data: 'time',
  136. defaultContent: '',
  137. render: function(data, type){
  138. var date = new Date(data ? data * 1000 : 0);
  139. return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
  140. }
  141. },
  142. {
  143. title: lang.priority,
  144. data: 'priority',
  145. defaultContent: ''
  146. },
  147. {
  148. title: lang.message,
  149. data: 'message',
  150. defaultContent: ''
  151. }
  152. ]
  153. });
  154. }
  155. function draw_watchdog_logs() {
  156. // just recalc width if instance already exists
  157. if ($.fn.DataTable.isDataTable('#watchdog_log') ) {
  158. $('#watchdog_log').DataTable().columns.adjust().responsive.recalc();
  159. return;
  160. }
  161. $('#watchdog_log').DataTable({
  162. processing: true,
  163. serverSide: false,
  164. language: lang_datatables,
  165. order: [[0, 'desc']],
  166. ajax: {
  167. type: "GET",
  168. url: "/api/v1/get/logs/watchdog",
  169. dataSrc: function(data){
  170. return process_table_data(data, 'watchdog');
  171. }
  172. },
  173. columns: [
  174. {
  175. title: lang.time,
  176. data: 'time',
  177. defaultContent: '',
  178. render: function(data, type){
  179. var date = new Date(data ? data * 1000 : 0);
  180. return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
  181. }
  182. },
  183. {
  184. title: 'Service',
  185. data: 'service',
  186. defaultContent: ''
  187. },
  188. {
  189. title: 'Trend',
  190. data: 'trend',
  191. defaultContent: ''
  192. },
  193. {
  194. title: lang.message,
  195. data: 'message',
  196. defaultContent: ''
  197. }
  198. ]
  199. });
  200. }
  201. function draw_api_logs() {
  202. // just recalc width if instance already exists
  203. if ($.fn.DataTable.isDataTable('#api_log') ) {
  204. $('#api_log').DataTable().columns.adjust().responsive.recalc();
  205. return;
  206. }
  207. $('#api_log').DataTable({
  208. processing: true,
  209. serverSide: false,
  210. language: lang_datatables,
  211. order: [[0, 'desc']],
  212. ajax: {
  213. type: "GET",
  214. url: "/api/v1/get/logs/api",
  215. dataSrc: function(data){
  216. return process_table_data(data, 'apilog');
  217. }
  218. },
  219. columns: [
  220. {
  221. title: lang.time,
  222. data: 'time',
  223. defaultContent: '',
  224. render: function(data, type){
  225. var date = new Date(data ? data * 1000 : 0);
  226. return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
  227. }
  228. },
  229. {
  230. title: 'URI',
  231. data: 'uri',
  232. defaultContent: ''
  233. },
  234. {
  235. title: 'Method',
  236. data: 'method',
  237. defaultContent: ''
  238. },
  239. {
  240. title: 'IP',
  241. data: 'remote',
  242. defaultContent: ''
  243. },
  244. {
  245. title: 'Data',
  246. data: 'data',
  247. defaultContent: ''
  248. }
  249. ]
  250. });
  251. }
  252. function draw_rl_logs() {
  253. // just recalc width if instance already exists
  254. if ($.fn.DataTable.isDataTable('#rl_log') ) {
  255. $('#rl_log').DataTable().columns.adjust().responsive.recalc();
  256. return;
  257. }
  258. $('#rl_log').DataTable({
  259. processing: true,
  260. serverSide: false,
  261. language: lang_datatables,
  262. order: [[0, 'desc']],
  263. ajax: {
  264. type: "GET",
  265. url: "/api/v1/get/logs/ratelimited",
  266. dataSrc: function(data){
  267. return process_table_data(data, 'rllog');
  268. }
  269. },
  270. columns: [
  271. {
  272. title: ' ',
  273. data: 'indicator',
  274. defaultContent: ''
  275. },
  276. {
  277. title: lang.time,
  278. data: 'time',
  279. defaultContent: '',
  280. render: function(data, type){
  281. var date = new Date(data ? data * 1000 : 0);
  282. return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
  283. }
  284. },
  285. {
  286. title: lang.rate_name,
  287. data: 'rl_name',
  288. defaultContent: ''
  289. },
  290. {
  291. title: lang.sender,
  292. data: 'from',
  293. defaultContent: ''
  294. },
  295. {
  296. title: lang.recipients,
  297. data: 'rcpt',
  298. defaultContent: ''
  299. },
  300. {
  301. title: lang.authed_user,
  302. data: 'user',
  303. defaultContent: ''
  304. },
  305. {
  306. title: 'Msg ID',
  307. data: 'message_id',
  308. defaultContent: ''
  309. },
  310. {
  311. title: 'Header From',
  312. data: 'header_from',
  313. defaultContent: ''
  314. },
  315. {
  316. title: 'Subject',
  317. data: 'header_subject',
  318. defaultContent: ''
  319. },
  320. {
  321. title: 'Hash',
  322. data: 'rl_hash',
  323. defaultContent: ''
  324. },
  325. {
  326. title: 'Rspamd QID',
  327. data: 'qid',
  328. defaultContent: ''
  329. },
  330. {
  331. title: 'IP',
  332. data: 'ip',
  333. defaultContent: ''
  334. },
  335. {
  336. title: lang.action,
  337. data: 'action',
  338. defaultContent: ''
  339. }
  340. ]
  341. });
  342. }
  343. function draw_ui_logs() {
  344. // just recalc width if instance already exists
  345. if ($.fn.DataTable.isDataTable('#ui_logs') ) {
  346. $('#ui_logs').DataTable().columns.adjust().responsive.recalc();
  347. return;
  348. }
  349. $('#ui_logs').DataTable({
  350. processing: true,
  351. serverSide: false,
  352. language: lang_datatables,
  353. order: [[0, 'desc']],
  354. ajax: {
  355. type: "GET",
  356. url: "/api/v1/get/logs/ui",
  357. dataSrc: function(data){
  358. return process_table_data(data, 'mailcow_ui');
  359. }
  360. },
  361. columns: [
  362. {
  363. title: lang.time,
  364. data: 'time',
  365. defaultContent: '',
  366. render: function(data, type){
  367. var date = new Date(data ? data * 1000 : 0);
  368. return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
  369. }
  370. },
  371. {
  372. title: 'Type',
  373. data: 'type',
  374. defaultContent: ''
  375. },
  376. {
  377. title: 'Task',
  378. data: 'task',
  379. defaultContent: ''
  380. },
  381. {
  382. title: 'User',
  383. data: 'user',
  384. defaultContent: ''
  385. },
  386. {
  387. title: 'Role',
  388. data: 'role',
  389. defaultContent: ''
  390. },
  391. {
  392. title: 'IP',
  393. data: 'remote',
  394. defaultContent: ''
  395. },
  396. {
  397. title: lang.message,
  398. data: 'msg',
  399. defaultContent: ''
  400. },
  401. {
  402. title: 'Call',
  403. data: 'call',
  404. defaultContent: ''
  405. }
  406. ]
  407. });
  408. }
  409. function draw_sasl_logs() {
  410. // just recalc width if instance already exists
  411. if ($.fn.DataTable.isDataTable('#sasl_logs') ) {
  412. $('#sasl_logs').DataTable().columns.adjust().responsive.recalc();
  413. return;
  414. }
  415. $('#sasl_logs').DataTable({
  416. processing: true,
  417. serverSide: false,
  418. language: lang_datatables,
  419. order: [[0, 'desc']],
  420. ajax: {
  421. type: "GET",
  422. url: "/api/v1/get/logs/sasl",
  423. dataSrc: function(data){
  424. return process_table_data(data, 'sasl_log_table');
  425. }
  426. },
  427. columns: [
  428. {
  429. title: lang.username,
  430. data: 'username',
  431. defaultContent: ''
  432. },
  433. {
  434. title: lang.service,
  435. data: 'service',
  436. defaultContent: ''
  437. },
  438. {
  439. title: 'IP',
  440. data: 'real_rip',
  441. defaultContent: ''
  442. },
  443. {
  444. title: lang.login_time,
  445. data: 'datetime',
  446. defaultContent: '',
  447. render: function(data, type){
  448. var date = new Date(data.replace(/-/g, "/"));
  449. return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
  450. }
  451. }
  452. ]
  453. });
  454. }
  455. function draw_acme_logs() {
  456. // just recalc width if instance already exists
  457. if ($.fn.DataTable.isDataTable('#acme_log') ) {
  458. $('#acme_log').DataTable().columns.adjust().responsive.recalc();
  459. return;
  460. }
  461. $('#acme_log').DataTable({
  462. processing: true,
  463. serverSide: false,
  464. language: lang_datatables,
  465. order: [[0, 'desc']],
  466. ajax: {
  467. type: "GET",
  468. url: "/api/v1/get/logs/acme",
  469. dataSrc: function(data){
  470. return process_table_data(data, 'general_syslog');
  471. }
  472. },
  473. columns: [
  474. {
  475. title: lang.time,
  476. data: 'time',
  477. defaultContent: '',
  478. render: function(data, type){
  479. var date = new Date(data ? data * 1000 : 0);
  480. return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
  481. }
  482. },
  483. {
  484. title: lang.message,
  485. data: 'message',
  486. defaultContent: ''
  487. }
  488. ]
  489. });
  490. }
  491. function draw_netfilter_logs() {
  492. // just recalc width if instance already exists
  493. if ($.fn.DataTable.isDataTable('#netfilter_log') ) {
  494. $('#netfilter_log').DataTable().columns.adjust().responsive.recalc();
  495. return;
  496. }
  497. $('#netfilter_log').DataTable({
  498. processing: true,
  499. serverSide: false,
  500. language: lang_datatables,
  501. order: [[0, 'desc']],
  502. ajax: {
  503. type: "GET",
  504. url: "/api/v1/get/logs/netfilter",
  505. dataSrc: function(data){
  506. return process_table_data(data, 'general_syslog');
  507. }
  508. },
  509. columns: [
  510. {
  511. title: lang.time,
  512. data: 'time',
  513. defaultContent: '',
  514. render: function(data, type){
  515. var date = new Date(data ? data * 1000 : 0);
  516. return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
  517. }
  518. },
  519. {
  520. title: lang.priority,
  521. data: 'priority',
  522. defaultContent: ''
  523. },
  524. {
  525. title: lang.message,
  526. data: 'message',
  527. defaultContent: ''
  528. }
  529. ]
  530. });
  531. }
  532. function draw_sogo_logs() {
  533. // just recalc width if instance already exists
  534. if ($.fn.DataTable.isDataTable('#sogo_log') ) {
  535. $('#sogo_log').DataTable().columns.adjust().responsive.recalc();
  536. return;
  537. }
  538. $('#sogo_log').DataTable({
  539. processing: true,
  540. serverSide: false,
  541. language: lang_datatables,
  542. order: [[0, 'desc']],
  543. ajax: {
  544. type: "GET",
  545. url: "/api/v1/get/logs/sogo",
  546. dataSrc: function(data){
  547. return process_table_data(data, 'general_syslog');
  548. }
  549. },
  550. columns: [
  551. {
  552. title: lang.time,
  553. data: 'time',
  554. defaultContent: '',
  555. render: function(data, type){
  556. var date = new Date(data ? data * 1000 : 0);
  557. return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
  558. }
  559. },
  560. {
  561. title: lang.priority,
  562. data: 'priority',
  563. defaultContent: ''
  564. },
  565. {
  566. title: lang.message,
  567. data: 'message',
  568. defaultContent: ''
  569. }
  570. ]
  571. });
  572. }
  573. function draw_dovecot_logs() {
  574. // just recalc width if instance already exists
  575. if ($.fn.DataTable.isDataTable('#dovecot_log') ) {
  576. $('#dovecot_log').DataTable().columns.adjust().responsive.recalc();
  577. return;
  578. }
  579. $('#dovecot_log').DataTable({
  580. processing: true,
  581. serverSide: false,
  582. language: lang_datatables,
  583. order: [[0, 'desc']],
  584. ajax: {
  585. type: "GET",
  586. url: "/api/v1/get/logs/dovecot",
  587. dataSrc: function(data){
  588. return process_table_data(data, 'general_syslog');
  589. }
  590. },
  591. columns: [
  592. {
  593. title: lang.time,
  594. data: 'time',
  595. defaultContent: '',
  596. render: function(data, type){
  597. var date = new Date(data ? data * 1000 : 0);
  598. return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
  599. }
  600. },
  601. {
  602. title: lang.priority,
  603. data: 'priority',
  604. defaultContent: ''
  605. },
  606. {
  607. title: lang.message,
  608. data: 'message',
  609. defaultContent: ''
  610. }
  611. ]
  612. });
  613. }
  614. function rspamd_pie_graph() {
  615. $.ajax({
  616. url: '/api/v1/get/rspamd/actions',
  617. async: true,
  618. success: function(data){
  619. console.log(data);
  620. var total = 0;
  621. $(data).map(function(){total += this[1];});
  622. var labels = $.makeArray($(data).map(function(){return this[0] + ' ' + Math.round(this[1]/total * 100) + '%';}));
  623. var values = $.makeArray($(data).map(function(){return this[1];}));
  624. console.log(values);
  625. var graphdata = {
  626. labels: labels,
  627. datasets: [{
  628. data: values,
  629. backgroundColor: ['#DC3023', '#59ABE3', '#FFA400', '#FFA400', '#26A65B']
  630. }]
  631. };
  632. var options = {
  633. responsive: true,
  634. maintainAspectRatio: false,
  635. plugins: {
  636. datalabels: {
  637. color: '#FFF',
  638. font: {
  639. weight: 'bold'
  640. },
  641. display: function(context) {
  642. return context.dataset.data[context.dataIndex] !== 0;
  643. },
  644. formatter: function(value, context) {
  645. return Math.round(value/total*100) + '%';
  646. }
  647. }
  648. }
  649. };
  650. var chartcanvas = document.getElementById('rspamd_donut');
  651. Chart.register('ChartDataLabels');
  652. if(typeof chart == 'undefined') {
  653. chart = new Chart(chartcanvas.getContext("2d"), {
  654. plugins: [ChartDataLabels],
  655. type: 'doughnut',
  656. data: graphdata,
  657. options: options
  658. });
  659. }
  660. else {
  661. chart.destroy();
  662. chart = new Chart(chartcanvas.getContext("2d"), {
  663. plugins: [ChartDataLabels],
  664. type: 'doughnut',
  665. data: graphdata,
  666. options: options
  667. });
  668. }
  669. }
  670. });
  671. }
  672. function draw_rspamd_history() {
  673. // just recalc width if instance already exists
  674. if ($.fn.DataTable.isDataTable('#rspamd_history') ) {
  675. $('#rspamd_history').DataTable().columns.adjust().responsive.recalc();
  676. return;
  677. }
  678. $('#rspamd_history').DataTable({
  679. processing: true,
  680. serverSide: false,
  681. language: lang_datatables,
  682. ajax: {
  683. type: "GET",
  684. url: "/api/v1/get/logs/rspamd-history",
  685. dataSrc: function(data){
  686. return process_table_data(data, 'rspamd_history');
  687. }
  688. },
  689. columns: [
  690. {
  691. title: lang.time,
  692. data: 'time',
  693. defaultContent: '',
  694. render: function(data, type){
  695. var date = new Date(data ? data * 1000 : 0);
  696. return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
  697. }
  698. },
  699. {
  700. title: 'IP address',
  701. data: 'ip',
  702. defaultContent: ''
  703. },
  704. {
  705. title: 'From',
  706. data: 'sender_mime',
  707. defaultContent: ''
  708. },
  709. {
  710. title: 'To',
  711. data: 'rcpt',
  712. defaultContent: ''
  713. },
  714. {
  715. title: 'Subject',
  716. data: 'subject',
  717. defaultContent: ''
  718. },
  719. {
  720. title: 'Action',
  721. data: 'action',
  722. defaultContent: ''
  723. },
  724. {
  725. title: 'Score',
  726. data: 'score',
  727. defaultContent: ''
  728. },
  729. {
  730. title: 'Subject',
  731. data: 'header_subject',
  732. defaultContent: ''
  733. },
  734. {
  735. title: 'Symbols',
  736. data: 'symbols',
  737. defaultContent: ''
  738. },
  739. {
  740. title: 'Msg size',
  741. data: 'size',
  742. defaultContent: ''
  743. },
  744. {
  745. title: 'Scan Time',
  746. data: 'scan_time',
  747. defaultContent: ''
  748. },
  749. {
  750. title: 'ID',
  751. data: 'message-id',
  752. defaultContent: ''
  753. },
  754. {
  755. title: 'Authenticated user',
  756. data: 'user',
  757. defaultContent: ''
  758. }
  759. ]
  760. });
  761. }
  762. function process_table_data(data, table) {
  763. if (table == 'rspamd_history') {
  764. $.each(data, function (i, item) {
  765. if (item.rcpt_mime != "") {
  766. item.rcpt = escapeHtml(item.rcpt_mime.join(", "));
  767. }
  768. else {
  769. item.rcpt = escapeHtml(item.rcpt_smtp.join(", "));
  770. }
  771. item.symbols = Object.keys(item.symbols).sort(function (a, b) {
  772. if (item.symbols[a].score === 0) return 1
  773. if (item.symbols[b].score === 0) return -1
  774. if (item.symbols[b].score < 0 && item.symbols[a].score < 0) {
  775. return item.symbols[a].score - item.symbols[b].score
  776. }
  777. if (item.symbols[b].score > 0 && item.symbols[a].score > 0) {
  778. return item.symbols[b].score - item.symbols[a].score
  779. }
  780. return item.symbols[b].score - item.symbols[a].score
  781. }).map(function(key) {
  782. var sym = item.symbols[key];
  783. if (sym.score < 0) {
  784. sym.score_formatted = '(<span class="text-success"><b>' + sym.score + '</b></span>)'
  785. }
  786. else if (sym.score === 0) {
  787. sym.score_formatted = '(<span><b>' + sym.score + '</b></span>)'
  788. }
  789. else {
  790. sym.score_formatted = '(<span class="text-danger"><b>' + sym.score + '</b></span>)'
  791. }
  792. var str = '<strong>' + key + '</strong> ' + sym.score_formatted;
  793. if (sym.options) {
  794. str += ' [' + escapeHtml(sym.options.join(", ")) + "]";
  795. }
  796. return str
  797. }).join('<br>\n');
  798. item.subject = escapeHtml(item.subject);
  799. var scan_time = item.time_real.toFixed(3);
  800. if (item.time_virtual) {
  801. scan_time += ' / ' + item.time_virtual.toFixed(3);
  802. }
  803. item.scan_time = {
  804. "options": {
  805. "sortValue": item.time_real
  806. },
  807. "value": scan_time
  808. };
  809. if (item.action === 'clean' || item.action === 'no action') {
  810. item.action = "<div class='badge fs-6 bg-success'>" + item.action + "</div>";
  811. } else if (item.action === 'rewrite subject' || item.action === 'add header' || item.action === 'probable spam') {
  812. item.action = "<div class='badge fs-6 bg-warning'>" + item.action + "</div>";
  813. } else if (item.action === 'spam' || item.action === 'reject') {
  814. item.action = "<div class='badge fs-6 bg-danger'>" + item.action + "</div>";
  815. } else {
  816. item.action = "<div class='badge fs-6 bg-info'>" + item.action + "</div>";
  817. }
  818. var score_content;
  819. if (item.score < item.required_score) {
  820. score_content = "[ <span class='text-success'>" + item.score.toFixed(2) + " / " + item.required_score + "</span> ]";
  821. } else {
  822. score_content = "[ <span class='text-danger'>" + item.score.toFixed(2) + " / " + item.required_score + "</span> ]";
  823. }
  824. item.score = {
  825. "options": {
  826. "sortValue": item.score
  827. },
  828. "value": score_content
  829. };
  830. if (item.user == null) {
  831. item.user = "none";
  832. }
  833. });
  834. } else if (table == 'autodiscover_log') {
  835. $.each(data, function (i, item) {
  836. if (item.ua == null) {
  837. item.ua = 'unknown';
  838. } else {
  839. item.ua = escapeHtml(item.ua);
  840. }
  841. item.ua = '<span style="font-size:small">' + item.ua + '</span>';
  842. if (item.service == "activesync") {
  843. item.service = '<span class="badge fs-6 bg-info">ActiveSync</span>';
  844. }
  845. else if (item.service == "imap") {
  846. item.service = '<span class="badge fs-6 bg-success">IMAP, SMTP, Cal-/CardDAV</span>';
  847. }
  848. else {
  849. item.service = '<span class="badge fs-6 bg-danger">' + escapeHtml(item.service) + '</span>';
  850. }
  851. });
  852. } else if (table == 'watchdog') {
  853. $.each(data, function (i, item) {
  854. if (item.message == null) {
  855. item.message = 'Health level: ' + item.lvl + '% (' + item.hpnow + '/' + item.hptotal + ')';
  856. if (item.hpdiff < 0) {
  857. item.trend = '<span class="badge fs-6 bg-danger"><i class="bi bi-caret-down-fill"></i> ' + item.hpdiff + '</span>';
  858. }
  859. else if (item.hpdiff == 0) {
  860. item.trend = '<span class="badge fs-6 bg-info"><i class="bi bi-caret-right-fill"></i> ' + item.hpdiff + '</span>';
  861. }
  862. else {
  863. item.trend = '<span class="badge fs-6 bg-success"><i class="bi bi-caret-up-fill"></i> ' + item.hpdiff + '</span>';
  864. }
  865. }
  866. else {
  867. item.trend = '';
  868. item.service = '';
  869. }
  870. });
  871. } else if (table == 'mailcow_ui') {
  872. $.each(data, function (i, item) {
  873. if (item === null) { return true; }
  874. item.user = escapeHtml(item.user);
  875. item.call = escapeHtml(item.call);
  876. item.task = '<code>' + item.task + '</code>';
  877. item.type = '<span class="badge fs-6 bg-' + item.type + '">' + item.type + '</span>';
  878. });
  879. } else if (table == 'sasl_log_table') {
  880. $.each(data, function (i, item) {
  881. if (item === null) { return true; }
  882. item.username = escapeHtml(item.username);
  883. item.service = '<div class="badge fs-6 bg-secondary">' + item.service.toUpperCase() + '</div>';
  884. });
  885. } else if (table == 'general_syslog') {
  886. $.each(data, function (i, item) {
  887. if (item === null) { return true; }
  888. if (item.message.match("^base64,")) {
  889. try {
  890. item.message = atob(item.message.slice(7)).replace(/\\n/g, "<br />");
  891. } catch(e) {
  892. item.message = item.message.slice(7);
  893. }
  894. } else {
  895. item.message = escapeHtml(item.message);
  896. }
  897. item.call = escapeHtml(item.call);
  898. var danger_class = ["emerg", "alert", "crit", "err"];
  899. var warning_class = ["warning", "warn"];
  900. var info_class = ["notice", "info", "debug"];
  901. if (jQuery.inArray(item.priority, danger_class) !== -1) {
  902. item.priority = '<span class="badge fs-6 bg-danger">' + item.priority + '</span>';
  903. } else if (jQuery.inArray(item.priority, warning_class) !== -1) {
  904. item.priority = '<span class="badge fs-6 bg-warning">' + item.priority + '</span>';
  905. } else if (jQuery.inArray(item.priority, info_class) !== -1) {
  906. item.priority = '<span class="badge fs-6 bg-info">' + item.priority + '</span>';
  907. }
  908. });
  909. } else if (table == 'apilog') {
  910. $.each(data, function (i, item) {
  911. if (item === null) { return true; }
  912. if (item.method == 'GET') {
  913. item.method = '<span class="badge fs-6 bg-success">' + item.method + '</span>';
  914. } else if (item.method == 'POST') {
  915. item.method = '<span class="badge fs-6 bg-warning">' + item.method + '</span>';
  916. }
  917. item.data = escapeHtml(item.data);
  918. });
  919. } else if (table == 'rllog') {
  920. $.each(data, function (i, item) {
  921. if (item.user == null) {
  922. item.user = "none";
  923. }
  924. if (item.rl_hash == null) {
  925. item.rl_hash = "err";
  926. }
  927. item.indicator = '<span style="border-right:6px solid #' + intToRGB(hashCode(item.rl_hash)) + ';padding-left:5px;">&nbsp;</span>';
  928. if (item.rl_hash != 'err') {
  929. item.action = '<a href="#" data-action="delete_selected" data-id="single-hash" data-api-url="delete/rlhash" data-item="' + encodeURI(item.rl_hash) + '" class="btn btn-xs btn-danger"><i class="bi bi-trash"></i> ' + lang.reset_limit + '</a>';
  930. }
  931. });
  932. }
  933. return data
  934. };
  935. $('.add_log_lines').on('click', function (e) {
  936. e.preventDefault();
  937. var log_table= $(this).data("table")
  938. var new_nrows = $(this).data("nrows")
  939. var post_process = $(this).data("post-process")
  940. var log_url = $(this).data("log-url")
  941. if (log_table === undefined || new_nrows === undefined || post_process === undefined || log_url === undefined) {
  942. console.log("no data-table or data-nrows or log_url or data-post-process attr found");
  943. return;
  944. }
  945. // BUG TODO: loading 100 results in loading 10 - loading 1000 results in loading 100
  946. if (table = $('#' + log_table).DataTable()) {
  947. var heading = $('#' + log_table).closest('.card').find('.card-header');
  948. var load_rows = (table.page.len() + 1) + '-' + (table.page.len() + new_nrows)
  949. $.get('/api/v1/get/logs/' + log_url + '/' + load_rows).then(function(data){
  950. if (data.length === undefined) { mailcow_alert_box(lang.no_new_rows, "info"); return; }
  951. var rows = process_table_data(data, post_process);
  952. var rows_now = (table.page.len() + data.length);
  953. $(heading).children('.table-lines').text(rows_now)
  954. mailcow_alert_box(data.length + lang.additional_rows, "success");
  955. table.rows.add(rows).draw();
  956. });
  957. }
  958. })
  959. // detect element visibility changes
  960. function onVisible(element, callback) {
  961. $(document).ready(function() {
  962. element_object = document.querySelector(element);
  963. if (element_object === null) return;
  964. new IntersectionObserver((entries, observer) => {
  965. entries.forEach(entry => {
  966. if(entry.intersectionRatio > 0) {
  967. callback(element_object);
  968. }
  969. });
  970. }).observe(element_object);
  971. });
  972. }
  973. // Draw Table if tab is active
  974. onVisible("[id^=postfix_log]", () => draw_postfix_logs());
  975. onVisible("[id^=dovecot_log]", () => draw_dovecot_logs());
  976. onVisible("[id^=sogo_log]", () => draw_sogo_logs());
  977. onVisible("[id^=watchdog_log]", () => draw_watchdog_logs());
  978. onVisible("[id^=autodiscover_log]", () => draw_autodiscover_logs());
  979. onVisible("[id^=acme_log]", () => draw_acme_logs());
  980. onVisible("[id^=api_log]", () => draw_api_logs());
  981. onVisible("[id^=rl_log]", () => draw_rl_logs());
  982. onVisible("[id^=ui_logs]", () => draw_ui_logs());
  983. onVisible("[id^=sasl_logs]", () => draw_sasl_logs());
  984. onVisible("[id^=netfilter_log]", () => draw_netfilter_logs());
  985. onVisible("[id^=rspamd_history]", () => draw_rspamd_history());
  986. onVisible("[id^=rspamd_donut]", () => rspamd_pie_graph());
  987. // start polling stats if tab is active
  988. onVisible("[id^=tab-containers]", () => update_stats());
  989. });
  990. // update system stats - every 5 seconds if system & container tab is active
  991. function update_stats(prev_stats = null){
  992. if (!$('#tab-containers').hasClass('active')) {
  993. // tab not active - dont fetch stats - run again in n seconds
  994. return;
  995. }
  996. window.fetch("/api/v1/get/status/host", {method:'GET',cache:'no-cache'}).then(function(response) {
  997. return response.json();
  998. }).then(function(data) {
  999. // display table data
  1000. $("#host_date").text(data.system_time);
  1001. $("#host_uptime").text(formatUptime(data.uptime));
  1002. $("#host_cpu_cores").text(data.cpu.cores);
  1003. $("#host_cpu_usage").text(parseInt(data.cpu.usage).toString() + "%");
  1004. $("#host_memory_total").text((data.memory.total / (1024 ** 3)).toFixed(2).toString() + "GB");
  1005. $("#host_memory_usage").text(parseInt(data.memory.usage).toString() + "%");
  1006. // display network and disk i/o
  1007. if (prev_stats != null){
  1008. // get chart instances by elemId
  1009. var net_io_chart = Chart.getChart("net_io_chart");
  1010. var disk_io_chart = Chart.getChart("disk_io_chart");
  1011. // calc time diff
  1012. var time_diff = (new Date(data.system_time) - new Date(prev_stats.system_time)) / 1000;
  1013. // push time label for x-axis
  1014. net_io_chart.data.labels.push(data.system_time.split(" ")[1]);
  1015. // shift data if more than 20 entires exists
  1016. if (net_io_chart.data.labels.length > 20) net_io_chart.data.labels.shift();
  1017. var diff_bytes_recv = (data.network.bytes_recv_total - prev_stats.network.bytes_recv_total) / time_diff;
  1018. var diff_bytes_sent = (data.network.bytes_sent_total - prev_stats.network.bytes_sent_total) / time_diff;
  1019. net_io_chart.data.datasets[0].data.push(diff_bytes_recv);
  1020. net_io_chart.data.datasets[1].data.push(diff_bytes_sent);
  1021. // shift data if more than 20 entires exists
  1022. if (net_io_chart.data.datasets[0].data.length > 20) net_io_chart.data.datasets[0].data.shift();
  1023. if (net_io_chart.data.datasets[1].data.length > 20) net_io_chart.data.datasets[1].data.shift();
  1024. // push time label for x-axis
  1025. disk_io_chart.data.labels.push(data.system_time.split(" ")[1]);
  1026. // shift data if more than 20 entires exists
  1027. if (disk_io_chart.data.labels.length > 20) disk_io_chart.data.labels.shift();
  1028. var diff_bytes_read = (data.disk.bytes_read_total - prev_stats.disk.bytes_read_total) / time_diff;
  1029. var diff_bytes_write = (data.disk.bytes_write_total - prev_stats.disk.bytes_write_total) / time_diff;
  1030. disk_io_chart.data.datasets[0].data.push(diff_bytes_read);
  1031. disk_io_chart.data.datasets[1].data.push(diff_bytes_write);
  1032. // shift data if more than 20 entires exists
  1033. if (disk_io_chart.data.datasets[0].data.length > 20) disk_io_chart.data.datasets[0].data.shift();
  1034. if (disk_io_chart.data.datasets[1].data.length > 20) disk_io_chart.data.datasets[1].data.shift();
  1035. // update charts
  1036. net_io_chart.update();
  1037. disk_io_chart.update();
  1038. }
  1039. // run again in n seconds
  1040. prev_stats = data;
  1041. setTimeout(update_stats(prev_stats), 2500);
  1042. });
  1043. }
  1044. // format hosts uptime seconds to readable string
  1045. function formatUptime(seconds){
  1046. seconds = Number(seconds);
  1047. var d = Math.floor(seconds / (3600*24));
  1048. var h = Math.floor(seconds % (3600*24) / 3600);
  1049. var m = Math.floor(seconds % 3600 / 60);
  1050. var s = Math.floor(seconds % 60);
  1051. var dFormat = d > 0 ? d + "D " : "";
  1052. var hFormat = h > 0 ? h + "H " : "";
  1053. var mFormat = m > 0 ? m + "M " : "";
  1054. var sFormat = s > 0 ? s + "S" : "";
  1055. return dFormat + hFormat + mFormat + sFormat;
  1056. }
  1057. // format bytes to readable string
  1058. function formatBytes(bytes){
  1059. // b
  1060. if (bytes < 1000) return bytes.toFixed(2).toString()+' B/s';
  1061. // b to kb
  1062. bytes = bytes / 1024;
  1063. if (bytes < 1000) return bytes.toFixed(2).toString()+' KB/s';
  1064. // kb to mb
  1065. bytes = bytes / 1024;
  1066. if (bytes < 1000) return bytes.toFixed(2).toString()+' MB/s';
  1067. // final mb to gb
  1068. return (bytes / 1024).toFixed(2).toString()+' GB/s';
  1069. }
  1070. // create network and disk chart
  1071. function createNetAndDiskChart(){
  1072. var net_io_ctx = document.getElementById("net_io_chart");
  1073. var disk_io_ctx = document.getElementById("disk_io_chart");
  1074. var dataNet = {
  1075. labels: [],
  1076. datasets: [{
  1077. label: "Recieve",
  1078. backgroundColor: "rgba(41, 187, 239, 0.3)",
  1079. borderColor: "rgba(41, 187, 239, 0.6)",
  1080. pointRadius: 1,
  1081. pointHitRadius: 6,
  1082. borderWidth: 2,
  1083. fill: true,
  1084. tension: 0.2,
  1085. data: []
  1086. }, {
  1087. label: "Sent",
  1088. backgroundColor: "rgba(239, 60, 41, 0.3)",
  1089. borderColor: "rgba(239, 60, 41, 0.6)",
  1090. pointRadius: 1,
  1091. pointHitRadius: 6,
  1092. borderWidth: 2,
  1093. fill: true,
  1094. tension: 0.2,
  1095. data: []
  1096. }]
  1097. };
  1098. var optionsNet = {
  1099. interaction: {
  1100. mode: 'index'
  1101. },
  1102. scales: {
  1103. yAxis: {
  1104. min: 0,
  1105. grid: {
  1106. display: false
  1107. },
  1108. ticks: {
  1109. callback: function(i, index, ticks) {
  1110. return formatBytes(i);
  1111. }
  1112. }
  1113. },
  1114. xAxis: {
  1115. grid: {
  1116. display: false
  1117. }
  1118. }
  1119. }
  1120. };
  1121. var dataDisk = {
  1122. labels: [],
  1123. datasets: [{
  1124. label: "Read",
  1125. backgroundColor: "rgba(41, 187, 239, 0.3)",
  1126. borderColor: "rgba(41, 187, 239, 0.6)",
  1127. pointRadius: 1,
  1128. pointHitRadius: 6,
  1129. borderWidth: 2,
  1130. fill: true,
  1131. tension: 0.2,
  1132. data: []
  1133. }, {
  1134. label: "Write",
  1135. backgroundColor: "rgba(239, 60, 41, 0.3)",
  1136. borderColor: "rgba(239, 60, 41, 0.6)",
  1137. pointRadius: 1,
  1138. pointHitRadius: 6,
  1139. borderWidth: 2,
  1140. fill: true,
  1141. tension: 0.2,
  1142. data: []
  1143. }]
  1144. };
  1145. var optionsDisk = {
  1146. interaction: {
  1147. mode: 'index'
  1148. },
  1149. scales: {
  1150. yAxis: {
  1151. min: 0,
  1152. grid: {
  1153. display: false
  1154. },
  1155. ticks: {
  1156. callback: function(i, index, ticks) {
  1157. return formatBytes(i);
  1158. }
  1159. }
  1160. },
  1161. xAxis: {
  1162. grid: {
  1163. display: false
  1164. }
  1165. }
  1166. }
  1167. };
  1168. var net_io_chart = new Chart(net_io_ctx, {
  1169. type: 'line',
  1170. data: dataNet,
  1171. options: optionsNet
  1172. });
  1173. var disk_io_chart = new Chart(disk_io_ctx, {
  1174. type: 'line',
  1175. data: dataDisk,
  1176. options: optionsDisk
  1177. });
  1178. }
  1179. // check for mailcow updates
  1180. function check_update(current_version, github_repo_url){
  1181. var github_account = github_repo_url.split("/")[3];
  1182. var github_repo_name = github_repo_url.split("/")[4];
  1183. // get details about latest release
  1184. window.fetch("https://api.github.com/repos/"+github_account+"/"+github_repo_name+"/releases/latest", {method:'GET',cache:'no-cache'}).then(function(response) {
  1185. return response.json();
  1186. }).then(function(latest_data) {
  1187. // get details about current release
  1188. window.fetch("https://api.github.com/repos/"+github_account+"/"+github_repo_name+"/releases/tags/"+current_version, {method:'GET',cache:'no-cache'}).then(function(response) {
  1189. return response.json();
  1190. }).then(function(current_data) {
  1191. // compare releases
  1192. var date_current = new Date(current_data.created_at);
  1193. var date_latest = new Date(latest_data.created_at);
  1194. if (date_latest.getTime() <= date_current.getTime()){
  1195. // no update available
  1196. $("#mailcow_update").removeClass("text-warning text-danger").addClass("text-success");
  1197. $("#mailcow_update").html("<b>" + lang_debug.no_update_available + "</b>");
  1198. } else {
  1199. // update available
  1200. $("#mailcow_update").removeClass("text-danger text-success").addClass("text-warning");
  1201. $("#mailcow_update").html(
  1202. `<b>` + lang_debug.update_available + `
  1203. <a target="_blank" href="https://github.com/`+github_account+`/`+github_repo_name+`/releases/tag/`+latest_data.tag_name+`">`+latest_data.tag_name+`</a></b>`
  1204. );
  1205. }
  1206. }).catch(err => {
  1207. // err
  1208. console.log(err);
  1209. $("#mailcow_update").removeClass("text-success text-warning").addClass("text-danger");
  1210. $("#mailcow_update").html("<b>"+ lang_debug.update_failed +"</b>");
  1211. });
  1212. }).catch(err => {
  1213. // err
  1214. console.log(err);
  1215. $("#mailcow_update").removeClass("text-success text-warning").addClass("text-danger");
  1216. $("#mailcow_update").html("<b>"+ lang_debug.update_failed +"</b>");
  1217. });
  1218. }