debug.js 46 KB

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