debug.js 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239
  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(){
  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. $("#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. var net_io_chart = Chart.getChart("net_io_chart");
  1007. var disk_io_chart = Chart.getChart("disk_io_chart");
  1008. net_io_chart.data.labels.push(data.system_time.split(" ")[1]);
  1009. if (net_io_chart.data.labels.length > 20) {
  1010. net_io_chart.data.labels.shift();
  1011. }
  1012. net_io_chart.data.datasets[0].data.push((data.network.bytes_recv / 1024).toFixed(4));
  1013. net_io_chart.data.datasets[1].data.push((data.network.bytes_sent / 1024).toFixed(4));
  1014. if (net_io_chart.data.datasets[0].data.length > 20) {
  1015. net_io_chart.data.datasets[0].data.shift();
  1016. }
  1017. if (net_io_chart.data.datasets[1].data.length > 20) {
  1018. net_io_chart.data.datasets[1].data.shift();
  1019. }
  1020. disk_io_chart.data.labels.push(data.system_time.split(" ")[1]);
  1021. if (disk_io_chart.data.labels.length > 20) {
  1022. disk_io_chart.data.labels.shift();
  1023. }
  1024. disk_io_chart.data.datasets[0].data.push((data.disk.read_bytes / 1024).toFixed(4));
  1025. disk_io_chart.data.datasets[1].data.push((data.disk.write_bytes / 1024).toFixed(4));
  1026. if (disk_io_chart.data.datasets[0].data.length > 20) {
  1027. disk_io_chart.data.datasets[0].data.shift();
  1028. }
  1029. if (disk_io_chart.data.datasets[1].data.length > 20) {
  1030. disk_io_chart.data.datasets[1].data.shift();
  1031. }
  1032. net_io_chart.update();
  1033. disk_io_chart.update();
  1034. // run again in n seconds
  1035. setTimeout(update_stats, 5000);
  1036. });
  1037. }
  1038. // format hosts uptime seconds to readable string
  1039. function formatUptime(seconds){
  1040. seconds = Number(seconds);
  1041. var d = Math.floor(seconds / (3600*24));
  1042. var h = Math.floor(seconds % (3600*24) / 3600);
  1043. var m = Math.floor(seconds % 3600 / 60);
  1044. var s = Math.floor(seconds % 60);
  1045. var dFormat = d > 0 ? d + "D " : "";
  1046. var hFormat = h > 0 ? h + "H " : "";
  1047. var mFormat = m > 0 ? m + "M " : "";
  1048. var sFormat = s > 0 ? s + "S" : "";
  1049. return dFormat + hFormat + mFormat + sFormat;
  1050. }
  1051. // create network and disk chart
  1052. function createNetAndDiskChart(){
  1053. var net_io_ctx = document.getElementById("net_io_chart");
  1054. var disk_io_ctx = document.getElementById("disk_io_chart");
  1055. var dataNet = {
  1056. labels: [],
  1057. datasets: [{
  1058. label: "Recieve",
  1059. backgroundColor: "rgba(41, 187, 239, 0.3)",
  1060. borderColor: "rgba(41, 187, 239, 0.6)",
  1061. color: "#ff0000",
  1062. borderWidth: 2,
  1063. fill: true,
  1064. tension: 0.2,
  1065. data: []
  1066. }, {
  1067. label: "Sent",
  1068. backgroundColor: "rgba(239, 60, 41, 0.3)",
  1069. borderColor: "rgba(239, 60, 41, 0.6)",
  1070. borderWidth: 2,
  1071. fill: true,
  1072. tension: 0.2,
  1073. data: []
  1074. }]
  1075. };
  1076. var optionsNet = {
  1077. scales: {
  1078. yAxis: {
  1079. min: 0,
  1080. grid: {
  1081. display: false
  1082. },
  1083. ticks: {
  1084. callback: function(i, index, ticks) {
  1085. // b
  1086. if (i < 1000) return i.toFixed(2).toString()+' B/s';
  1087. // b to kb
  1088. i = i / 1024;
  1089. if (i < 1000) return i.toFixed(2).toString()+' KB/s';
  1090. // kb to mb
  1091. i = i / 1024;
  1092. if (i < 1000) return i.toFixed(2).toString()+' MB/s';
  1093. // final mb to gb
  1094. return (i / 1024).toFixed(2).toString()+' GB/s';
  1095. }
  1096. }
  1097. },
  1098. xAxis: {
  1099. grid: {
  1100. display: false
  1101. }
  1102. }
  1103. }
  1104. };
  1105. var dataDisk = {
  1106. labels: [],
  1107. datasets: [{
  1108. label: "Read",
  1109. backgroundColor: "rgba(41, 187, 239, 0.3)",
  1110. borderColor: "rgba(41, 187, 239, 0.6)",
  1111. color: "#ff0000",
  1112. borderWidth: 2,
  1113. fill: true,
  1114. tension: 0.2,
  1115. data: []
  1116. }, {
  1117. label: "Write",
  1118. backgroundColor: "rgba(239, 60, 41, 0.3)",
  1119. borderColor: "rgba(239, 60, 41, 0.6)",
  1120. borderWidth: 2,
  1121. fill: true,
  1122. tension: 0.2,
  1123. data: []
  1124. }]
  1125. };
  1126. var optionsDisk = {
  1127. scales: {
  1128. yAxis: {
  1129. min: 0,
  1130. grid: {
  1131. display: false
  1132. },
  1133. ticks: {
  1134. callback: function(i, index, ticks) {
  1135. // b
  1136. if (i < 1000) return i.toFixed(2).toString()+' B/s';
  1137. // b to kb
  1138. i = i / 1024;
  1139. if (i < 1000) return i.toFixed(2).toString()+' KB/s';
  1140. // kb to mb
  1141. i = i / 1024;
  1142. if (i < 1000) return i.toFixed(2).toString()+' MB/s';
  1143. // final mb to gb
  1144. return (i / 1024).toFixed(2).toString()+' GB/s';
  1145. }
  1146. }
  1147. },
  1148. xAxis: {
  1149. grid: {
  1150. display: false
  1151. }
  1152. }
  1153. }
  1154. };
  1155. var net_io_chart = new Chart(net_io_ctx, {
  1156. type: 'line',
  1157. data: dataNet,
  1158. options: optionsNet
  1159. });
  1160. var disk_io_chart = new Chart(disk_io_ctx, {
  1161. type: 'line',
  1162. data: dataDisk,
  1163. options: optionsDisk
  1164. });
  1165. }
  1166. // check for mailcow updates
  1167. function check_update(current_version, github_repo_url){
  1168. var github_account = github_repo_url.split("/")[3];
  1169. var github_repo_name = github_repo_url.split("/")[4];
  1170. // get details about latest release
  1171. window.fetch("https://api.github.com/repos/"+github_account+"/"+github_repo_name+"/releases/latest", {method:'GET',cache:'no-cache'}).then(function(response) {
  1172. return response.json();
  1173. }).then(function(latest_data) {
  1174. // get details about current release
  1175. window.fetch("https://api.github.com/repos/"+github_account+"/"+github_repo_name+"/releases/tags/"+current_version, {method:'GET',cache:'no-cache'}).then(function(response) {
  1176. return response.json();
  1177. }).then(function(current_data) {
  1178. // compare releases
  1179. var date_current = new Date(current_data.created_at);
  1180. var date_latest = new Date(latest_data.created_at);
  1181. if (date_latest.getTime() <= date_current.getTime()){
  1182. // no update available
  1183. $("#mailcow_update").removeClass("text-warning text-danger").addClass("text-success");
  1184. $("#mailcow_update").html("<b>" + lang_debug.no_update_available + "</b>");
  1185. } else {
  1186. // update available
  1187. $("#mailcow_update").removeClass("text-danger text-success").addClass("text-warning");
  1188. $("#mailcow_update").html(
  1189. `<b>` + lang_debug.update_available + `
  1190. <a target="_blank" href="https://github.com/`+github_account+`/`+github_repo_name+`/releases/tag/`+latest_data.tag_name+`">`+latest_data.tag_name+`</a></b>`
  1191. );
  1192. }
  1193. }).catch(err => {
  1194. // err
  1195. console.log(err);
  1196. $("#mailcow_update").removeClass("text-success text-warning").addClass("text-danger");
  1197. $("#mailcow_update").html("<b>Could not check for an Update</b>");
  1198. });
  1199. }).catch(err => {
  1200. // err
  1201. console.log(err);
  1202. $("#mailcow_update").removeClass("text-success text-warning").addClass("text-danger");
  1203. $("#mailcow_update").html("<b>Could not check for an Update</b>");
  1204. });
  1205. }