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