blob: 10b1e8cf3f6c3f41b2d68ec05d00cc01db8949f4 [file] [log] [blame]
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001$(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(LOCALE, DATETIME_FORMAT);
11 $(this).text(started_local_date);
12 } else {
13 $(this).text('-');
14 }
15 }
16 });
17 // Parse general dates
18 $('.parse_date').each(function(i, parse_date) {
19 var started_date = new Date(Date.parse($(this).text()));
20 if (typeof started_date != 'NaN') {
21 var started_local_date = started_date.toLocaleDateString(LOCALE, DATETIME_FORMAT);
22 $(this).text(started_local_date);
23 }
24 });
25
26 // set update loop container list
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +010027 containersToUpdate = {};
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +010028 // set default ChartJs Font Color
29 Chart.defaults.color = '#999';
30 // create host cpu and mem charts
31 createHostCpuAndMemChart();
32 // check for new version
33 if (mailcow_info.branch === "master"){
34 check_update(mailcow_info.version_tag, mailcow_info.project_url);
35 }
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +010036 $("#mailcow_version").click(function(){
37 if (mailcow_cc_role !== "admin" && mailcow_cc_role !== "domainadmin" || mailcow_info.branch !== "master")
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +010038 return;
39
40 showVersionModal("Version " + mailcow_info.version_tag, mailcow_info.version_tag);
41 })
42 // get public ips
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +010043 $("#host_show_ip").click(function(){
44 $("#host_show_ip").find(".text").addClass("d-none");
45 $("#host_show_ip").find(".spinner-border").removeClass("d-none");
46
47 window.fetch("/api/v1/get/status/host/ip", { method:'GET', cache:'no-cache' }).then(function(response) {
48 return response.json();
49 }).then(function(data) {
50 console.log(data);
51
52 // display host ips
53 if (data.ipv4)
54 $("#host_ipv4").text(data.ipv4);
55 if (data.ipv6)
56 $("#host_ipv6").text(data.ipv6);
57
58 $("#host_show_ip").addClass("d-none");
59 $("#host_show_ip").find(".text").removeClass("d-none");
60 $("#host_show_ip").find(".spinner-border").addClass("d-none");
61 $("#host_ipv4").removeClass("d-none");
62 $("#host_ipv6").removeClass("d-none");
63 $("#host_ipv6").removeClass("text-danger");
64 $("#host_ipv4").addClass("d-block");
65 $("#host_ipv6").addClass("d-block");
66 }).catch(function(error){
67 console.log(error);
68
69 $("#host_ipv6").removeClass("d-none");
70 $("#host_ipv6").addClass("d-block");
71 $("#host_ipv6").addClass("text-danger");
72 $("#host_ipv6").text(lang_debug.error_show_ip);
73 $("#host_show_ip").find(".text").removeClass("d-none");
74 $("#host_show_ip").find(".spinner-border").addClass("d-none");
75 });
76 });
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +010077 update_container_stats();
78});
79jQuery(function($){
80 if (localStorage.getItem("current_page") === null) {
81 var current_page = {};
82 } else {
83 var current_page = JSON.parse(localStorage.getItem('current_page'));
84 }
85 // http://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery
86 var entityMap={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"};
87 function escapeHtml(n){return String(n).replace(/[&<>"'`=\/]/g,function(n){return entityMap[n]})}
88 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]}
89 function hashCode(t){for(var n=0,r=0;r<t.length;r++)n=t.charCodeAt(r)+((n<<5)-n);return n}
90 function intToRGB(t){var n=(16777215&t).toString(16).toUpperCase();return"00000".substring(0,6-n.length)+n}
91 $(".refresh_table").on('click', function(e) {
92 e.preventDefault();
93 var table_name = $(this).data('table');
94 $('#' + table_name).DataTable().ajax.reload();
95 });
96 function createSortableDate(td, cellData) {
97 $(td).attr({
98 "data-order": cellData,
99 "data-sort": cellData
100 });
101 $(td).html(convertTimestampToLocalFormat(cellData));
102 }
103 function draw_autodiscover_logs() {
104 // just recalc width if instance already exists
105 if ($.fn.DataTable.isDataTable('#autodiscover_log') ) {
106 $('#autodiscover_log').DataTable().columns.adjust().responsive.recalc();
107 return;
108 }
109
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100110 var table = $('#autodiscover_log').DataTable({
111 responsive: true,
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100112 processing: true,
113 serverSide: false,
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100114 stateSave: true,
115 pageLength: log_pagination_size,
116 dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
117 "tr" +
118 "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100119 language: lang_datatables,
120 order: [[0, 'desc']],
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100121 initComplete: function(){
122 hideTableExpandCollapseBtn('#tab-autodiscover-logs', '#autodiscover_log');
123 },
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100124 ajax: {
125 type: "GET",
126 url: "/api/v1/get/logs/autodiscover/100",
127 dataSrc: function(data){
128 return process_table_data(data, 'autodiscover_log');
129 }
130 },
131 columns: [
132 {
133 title: lang.time,
134 data: 'time',
135 defaultContent: '',
136 responsivePriority: 1,
137 createdCell: function(td, cellData) {
138 createSortableDate(td, cellData)
139 }
140 },
141 {
142 title: 'User-Agent',
143 data: 'ua',
144 defaultContent: '',
145 className: 'dtr-col-md',
146 responsivePriority: 5
147 },
148 {
149 title: 'Username',
150 data: 'user',
151 defaultContent: '',
152 responsivePriority: 4
153 },
154 {
155 title: 'IP',
156 data: 'ip',
157 defaultContent: '',
158 responsivePriority: 2
159 },
160 {
161 title: 'Service',
162 data: 'service',
163 defaultContent: '',
164 responsivePriority: 3
165 }
166 ]
167 });
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100168
169 table.on('responsive-resize', function (e, datatable, columns){
170 hideTableExpandCollapseBtn('#tab-autodiscover-logs', '#autodiscover_log');
171 });
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100172 }
173 function draw_postfix_logs() {
174 // just recalc width if instance already exists
175 if ($.fn.DataTable.isDataTable('#postfix_log') ) {
176 $('#postfix_log').DataTable().columns.adjust().responsive.recalc();
177 return;
178 }
179
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100180 var table = $('#postfix_log').DataTable({
181 responsive: true,
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100182 processing: true,
183 serverSide: false,
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100184 stateSave: true,
185 pageLength: log_pagination_size,
186 dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
187 "tr" +
188 "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100189 language: lang_datatables,
190 order: [[0, 'desc']],
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100191 initComplete: function(){
192 hideTableExpandCollapseBtn('#tab-postfix-logs', '#postfix_log');
193 },
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100194 ajax: {
195 type: "GET",
196 url: "/api/v1/get/logs/postfix",
197 dataSrc: function(data){
198 return process_table_data(data, 'general_syslog');
199 }
200 },
201 columns: [
202 {
203 title: lang.time,
204 data: 'time',
205 defaultContent: '',
206 createdCell: function(td, cellData) {
207 createSortableDate(td, cellData)
208 }
209 },
210 {
211 title: lang.priority,
212 data: 'priority',
213 defaultContent: ''
214 },
215 {
216 title: lang.message,
217 data: 'message',
218 defaultContent: '',
219 className: 'dtr-col-md text-break'
220 }
221 ]
222 });
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100223
224 table.on('responsive-resize', function (e, datatable, columns){
225 hideTableExpandCollapseBtn('#tab-postfix-logs', '#postfix_log');
226 });
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100227 }
228 function draw_watchdog_logs() {
229 // just recalc width if instance already exists
230 if ($.fn.DataTable.isDataTable('#watchdog_log') ) {
231 $('#watchdog_log').DataTable().columns.adjust().responsive.recalc();
232 return;
233 }
234
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100235 var table = $('#watchdog_log').DataTable({
236 responsive: true,
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100237 processing: true,
238 serverSide: false,
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100239 stateSave: true,
240 pageLength: log_pagination_size,
241 dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
242 "tr" +
243 "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100244 language: lang_datatables,
245 order: [[0, 'desc']],
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100246 initComplete: function(){
247 hideTableExpandCollapseBtn('#tab-watchdog-logs', '#watchdog_log');
248 },
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100249 ajax: {
250 type: "GET",
251 url: "/api/v1/get/logs/watchdog",
252 dataSrc: function(data){
253 return process_table_data(data, 'watchdog');
254 }
255 },
256 columns: [
257 {
258 title: lang.time,
259 data: 'time',
260 defaultContent: '',
261 createdCell: function(td, cellData) {
262 createSortableDate(td, cellData)
263 }
264 },
265 {
266 title: 'Service',
267 data: 'service',
268 defaultContent: ''
269 },
270 {
271 title: 'Trend',
272 data: 'trend',
273 defaultContent: ''
274 },
275 {
276 title: lang.message,
277 data: 'message',
278 defaultContent: ''
279 }
280 ]
281 });
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100282
283 table.on('responsive-resize', function (e, datatable, columns){
284 hideTableExpandCollapseBtn('#tab-watchdog-logs', '#watchdog_log');
285 });
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100286 }
287 function draw_api_logs() {
288 // just recalc width if instance already exists
289 if ($.fn.DataTable.isDataTable('#api_log') ) {
290 $('#api_log').DataTable().columns.adjust().responsive.recalc();
291 return;
292 }
293
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100294 var table = $('#api_log').DataTable({
295 responsive: true,
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100296 processing: true,
297 serverSide: false,
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100298 stateSave: true,
299 pageLength: log_pagination_size,
300 dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
301 "tr" +
302 "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100303 language: lang_datatables,
304 order: [[0, 'desc']],
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100305 initComplete: function(){
306 hideTableExpandCollapseBtn('#tab-api-logs', '#api_log');
307 },
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100308 ajax: {
309 type: "GET",
310 url: "/api/v1/get/logs/api",
311 dataSrc: function(data){
312 return process_table_data(data, 'apilog');
313 }
314 },
315 columns: [
316 {
317 title: lang.time,
318 data: 'time',
319 defaultContent: '',
320 createdCell: function(td, cellData) {
321 createSortableDate(td, cellData)
322 }
323 },
324 {
325 title: 'URI',
326 data: 'uri',
327 defaultContent: '',
328 className: 'dtr-col-md dtr-break-all'
329 },
330 {
331 title: 'Method',
332 data: 'method',
333 defaultContent: ''
334 },
335 {
336 title: 'IP',
337 data: 'remote',
338 defaultContent: ''
339 },
340 {
341 title: 'Data',
342 data: 'data',
343 defaultContent: '',
344 className: 'dtr-col-md dtr-break-all'
345 }
346 ]
347 });
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100348
349 table.on('responsive-resize', function (e, datatable, columns){
350 hideTableExpandCollapseBtn('#tab-api-logs', '#api_log');
351 });
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100352 }
353 function draw_rl_logs() {
354 // just recalc width if instance already exists
355 if ($.fn.DataTable.isDataTable('#rl_log') ) {
356 $('#rl_log').DataTable().columns.adjust().responsive.recalc();
357 return;
358 }
359
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100360 var table = $('#rl_log').DataTable({
361 responsive: true,
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100362 processing: true,
363 serverSide: false,
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100364 stateSave: true,
365 pageLength: log_pagination_size,
366 dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
367 "tr" +
368 "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100369 language: lang_datatables,
370 order: [[0, 'desc']],
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100371 initComplete: function(){
372 hideTableExpandCollapseBtn('#tab-rl-logs', '#rl_log');
373 },
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100374 ajax: {
375 type: "GET",
376 url: "/api/v1/get/logs/ratelimited",
377 dataSrc: function(data){
378 return process_table_data(data, 'rllog');
379 }
380 },
381 columns: [
382 {
383 title: ' ',
384 data: 'indicator',
385 defaultContent: ''
386 },
387 {
388 title: lang.time,
389 data: 'time',
390 defaultContent: '',
391 createdCell: function(td, cellData) {
392 createSortableDate(td, cellData)
393 }
394 },
395 {
396 title: lang.rate_name,
397 data: 'rl_name',
398 defaultContent: ''
399 },
400 {
401 title: lang.sender,
402 data: 'from',
403 defaultContent: ''
404 },
405 {
406 title: lang.recipients,
407 data: 'rcpt',
408 defaultContent: ''
409 },
410 {
411 title: lang.authed_user,
412 data: 'user',
413 defaultContent: ''
414 },
415 {
416 title: 'Msg ID',
417 data: 'message_id',
418 defaultContent: ''
419 },
420 {
421 title: 'Header From',
422 data: 'header_from',
423 defaultContent: ''
424 },
425 {
426 title: 'Subject',
427 data: 'header_subject',
428 defaultContent: ''
429 },
430 {
431 title: 'Hash',
432 data: 'rl_hash',
433 defaultContent: ''
434 },
435 {
436 title: 'Rspamd QID',
437 data: 'qid',
438 defaultContent: ''
439 },
440 {
441 title: 'IP',
442 data: 'ip',
443 defaultContent: ''
444 },
445 {
446 title: lang.action,
447 data: 'action',
448 defaultContent: ''
449 }
450 ]
451 });
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100452
453 table.on('responsive-resize', function (e, datatable, columns){
454 hideTableExpandCollapseBtn('#tab-rl-logs', '#rl_log');
455 });
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100456 }
457 function draw_ui_logs() {
458 // just recalc width if instance already exists
459 if ($.fn.DataTable.isDataTable('#ui_logs') ) {
460 $('#ui_logs').DataTable().columns.adjust().responsive.recalc();
461 return;
462 }
463
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100464 var table = $('#ui_logs').DataTable({
465 responsive: true,
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100466 processing: true,
467 serverSide: false,
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100468 stateSave: true,
469 pageLength: log_pagination_size,
470 dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
471 "tr" +
472 "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100473 language: lang_datatables,
474 order: [[0, 'desc']],
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100475 initComplete: function(){
476 hideTableExpandCollapseBtn('#tab-ui-logs', '#ui_logs');
477 },
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100478 ajax: {
479 type: "GET",
480 url: "/api/v1/get/logs/ui",
481 dataSrc: function(data){
482 return process_table_data(data, 'mailcow_ui');
483 }
484 },
485 columns: [
486 {
487 title: lang.time,
488 data: 'time',
489 defaultContent: '',
490 createdCell: function(td, cellData) {
491 createSortableDate(td, cellData)
492 }
493 },
494 {
495 title: 'Type',
496 data: 'type',
497 defaultContent: ''
498 },
499 {
500 title: 'Task',
501 data: 'task',
502 defaultContent: ''
503 },
504 {
505 title: 'User',
506 data: 'user',
507 defaultContent: '',
508 className: 'dtr-col-sm'
509 },
510 {
511 title: 'Role',
512 data: 'role',
513 defaultContent: '',
514 className: 'dtr-col-sm'
515 },
516 {
517 title: 'IP',
518 data: 'remote',
519 defaultContent: '',
520 className: 'dtr-col-md dtr-break-all'
521 },
522 {
523 title: lang.message,
524 data: 'msg',
525 defaultContent: '',
526 className: 'dtr-col-md dtr-break-all'
527 },
528 {
529 title: 'Call',
530 data: 'call',
531 defaultContent: '',
532 className: 'none dtr-col-md dtr-break-all'
533 }
534 ]
535 });
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100536
537 table.on('responsive-resize', function (e, datatable, columns){
538 hideTableExpandCollapseBtn('#tab-ui-logs', '#ui_log');
539 });
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100540 }
541 function draw_sasl_logs() {
542 // just recalc width if instance already exists
543 if ($.fn.DataTable.isDataTable('#sasl_logs') ) {
544 $('#sasl_logs').DataTable().columns.adjust().responsive.recalc();
545 return;
546 }
547
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100548 var table = $('#sasl_logs').DataTable({
549 responsive: true,
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100550 processing: true,
551 serverSide: false,
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100552 stateSave: true,
553 pageLength: log_pagination_size,
554 dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
555 "tr" +
556 "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100557 language: lang_datatables,
558 order: [[0, 'desc']],
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100559 initComplete: function(){
560 hideTableExpandCollapseBtn('#tab-sasl-logs', '#sasl_logs');
561 },
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100562 ajax: {
563 type: "GET",
564 url: "/api/v1/get/logs/sasl",
565 dataSrc: function(data){
566 return process_table_data(data, 'sasl_log_table');
567 }
568 },
569 columns: [
570 {
571 title: lang.username,
572 data: 'username',
573 defaultContent: ''
574 },
575 {
576 title: lang.service,
577 data: 'service',
578 defaultContent: ''
579 },
580 {
581 title: 'IP',
582 data: 'real_rip',
583 defaultContent: '',
584 className: 'dtr-col-md text-break'
585 },
586 {
587 title: lang.login_time,
588 data: 'datetime',
589 defaultContent: '',
590 createdCell: function(td, cellData) {
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100591 cellData = Math.floor((new Date(cellData.replace(/-/g, "/"))).getTime() / 1000);
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100592 createSortableDate(td, cellData)
593 }
594 }
595 ]
596 });
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100597
598 table.on('responsive-resize', function (e, datatable, columns){
599 hideTableExpandCollapseBtn('#tab-sasl-logs', '#sasl_logs');
600 });
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100601 }
602 function draw_acme_logs() {
603 // just recalc width if instance already exists
604 if ($.fn.DataTable.isDataTable('#acme_log') ) {
605 $('#acme_log').DataTable().columns.adjust().responsive.recalc();
606 return;
607 }
608
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100609 var table = $('#acme_log').DataTable({
610 responsive: true,
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100611 processing: true,
612 serverSide: false,
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100613 stateSave: true,
614 pageLength: log_pagination_size,
615 dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
616 "tr" +
617 "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100618 language: lang_datatables,
619 order: [[0, 'desc']],
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100620 initComplete: function(){
621 hideTableExpandCollapseBtn('#tab-acme-logs', '#acme_log');
622 },
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100623 ajax: {
624 type: "GET",
625 url: "/api/v1/get/logs/acme",
626 dataSrc: function(data){
627 return process_table_data(data, 'general_syslog');
628 }
629 },
630 columns: [
631 {
632 title: lang.time,
633 data: 'time',
634 defaultContent: '',
635 createdCell: function(td, cellData) {
636 createSortableDate(td, cellData)
637 }
638 },
639 {
640 title: lang.message,
641 data: 'message',
642 defaultContent: '',
643 className: 'dtr-col-md dtr-break-all'
644 }
645 ]
646 });
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100647
648 table.on('responsive-resize', function (e, datatable, columns){
649 hideTableExpandCollapseBtn('#tab-acme-logs', '#acme_log');
650 });
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100651 }
652 function draw_netfilter_logs() {
653 // just recalc width if instance already exists
654 if ($.fn.DataTable.isDataTable('#netfilter_log') ) {
655 $('#netfilter_log').DataTable().columns.adjust().responsive.recalc();
656 return;
657 }
658
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100659 var table = $('#netfilter_log').DataTable({
660 responsive: true,
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100661 processing: true,
662 serverSide: false,
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100663 stateSave: true,
664 pageLength: log_pagination_size,
665 dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
666 "tr" +
667 "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100668 language: lang_datatables,
669 order: [[0, 'desc']],
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100670 initComplete: function(){
671 hideTableExpandCollapseBtn('#tab-netfilter-logs', '#netfilter_log');
672 },
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100673 ajax: {
674 type: "GET",
675 url: "/api/v1/get/logs/netfilter",
676 dataSrc: function(data){
677 return process_table_data(data, 'general_syslog');
678 }
679 },
680 columns: [
681 {
682 title: lang.time,
683 data: 'time',
684 defaultContent: '',
685 createdCell: function(td, cellData) {
686 createSortableDate(td, cellData)
687 }
688 },
689 {
690 title: lang.priority,
691 data: 'priority',
692 defaultContent: ''
693 },
694 {
695 title: lang.message,
696 data: 'message',
697 defaultContent: '',
698 className: 'dtr-col-md text-break'
699 }
700 ]
701 });
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100702
703 table.on('responsive-resize', function (e, datatable, columns){
704 hideTableExpandCollapseBtn('#tab-netfilter-logs', '#netfilter_log');
705 });
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100706 }
707 function draw_sogo_logs() {
708 // just recalc width if instance already exists
709 if ($.fn.DataTable.isDataTable('#sogo_log') ) {
710 $('#sogo_log').DataTable().columns.adjust().responsive.recalc();
711 return;
712 }
713
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100714 var table = $('#sogo_log').DataTable({
715 responsive: true,
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100716 processing: true,
717 serverSide: false,
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100718 stateSave: true,
719 pageLength: log_pagination_size,
720 dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
721 "tr" +
722 "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100723 language: lang_datatables,
724 order: [[0, 'desc']],
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100725 initComplete: function(){
726 hideTableExpandCollapseBtn('#tab-sogo-logs', '#sogo_log');
727 },
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100728 ajax: {
729 type: "GET",
730 url: "/api/v1/get/logs/sogo",
731 dataSrc: function(data){
732 return process_table_data(data, 'general_syslog');
733 }
734 },
735 columns: [
736 {
737 title: lang.time,
738 data: 'time',
739 defaultContent: '',
740 createdCell: function(td, cellData) {
741 createSortableDate(td, cellData)
742 }
743 },
744 {
745 title: lang.priority,
746 data: 'priority',
747 defaultContent: ''
748 },
749 {
750 title: lang.message,
751 data: 'message',
752 defaultContent: '',
753 className: 'dtr-col-md text-break'
754 }
755 ]
756 });
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100757
758 table.on('responsive-resize', function (e, datatable, columns){
759 hideTableExpandCollapseBtn('#tab-sogo-logs', '#sogo_log');
760 });
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100761 }
762 function draw_dovecot_logs() {
763 // just recalc width if instance already exists
764 if ($.fn.DataTable.isDataTable('#dovecot_log') ) {
765 $('#dovecot_log').DataTable().columns.adjust().responsive.recalc();
766 return;
767 }
768
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100769 var table = $('#dovecot_log').DataTable({
770 responsive: true,
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100771 processing: true,
772 serverSide: false,
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100773 stateSave: true,
774 pageLength: log_pagination_size,
775 dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
776 "tr" +
777 "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100778 language: lang_datatables,
779 order: [[0, 'desc']],
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100780 initComplete: function(){
781 hideTableExpandCollapseBtn('#tab-dovecot-logs', '#dovecot_log');
782 },
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100783 ajax: {
784 type: "GET",
785 url: "/api/v1/get/logs/dovecot",
786 dataSrc: function(data){
787 return process_table_data(data, 'general_syslog');
788 }
789 },
790 columns: [
791 {
792 title: lang.time,
793 data: 'time',
794 defaultContent: '',
795 createdCell: function(td, cellData) {
796 createSortableDate(td, cellData)
797 }
798 },
799 {
800 title: lang.priority,
801 data: 'priority',
802 defaultContent: ''
803 },
804 {
805 title: lang.message,
806 data: 'message',
807 defaultContent: '',
808 className: 'dtr-col-md text-break'
809 }
810 ]
811 });
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100812
813 table.on('responsive-resize', function (e, datatable, columns){
814 hideTableExpandCollapseBtn('#tab-dovecot-logs', '#dovecot_log');
815 });
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100816 }
817 function rspamd_pie_graph() {
818 $.ajax({
819 url: '/api/v1/get/rspamd/actions',
820 async: true,
821 success: function(data){
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100822 var total = 0;
823 $(data).map(function(){total += this[1];});
824 var labels = $.makeArray($(data).map(function(){return this[0] + ' ' + Math.round(this[1]/total * 100) + '%';}));
825 var values = $.makeArray($(data).map(function(){return this[1];}));
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100826
827 var graphdata = {
828 labels: labels,
829 datasets: [{
830 data: values,
831 backgroundColor: ['#DC3023', '#59ABE3', '#FFA400', '#FFA400', '#26A65B']
832 }]
833 };
834
835 var options = {
836 responsive: true,
837 maintainAspectRatio: false,
838 plugins: {
839 datalabels: {
840 color: '#FFF',
841 font: {
842 weight: 'bold'
843 },
844 display: function(context) {
845 return context.dataset.data[context.dataIndex] !== 0;
846 },
847 formatter: function(value, context) {
848 return Math.round(value/total*100) + '%';
849 }
850 }
851 }
852 };
853 var chartcanvas = document.getElementById('rspamd_donut');
854 Chart.register('ChartDataLabels');
855 if(typeof chart == 'undefined') {
856 chart = new Chart(chartcanvas.getContext("2d"), {
857 plugins: [ChartDataLabels],
858 type: 'doughnut',
859 data: graphdata,
860 options: options
861 });
862 }
863 else {
864 chart.destroy();
865 chart = new Chart(chartcanvas.getContext("2d"), {
866 plugins: [ChartDataLabels],
867 type: 'doughnut',
868 data: graphdata,
869 options: options
870 });
871 }
872 }
873 });
874 }
875 function draw_rspamd_history() {
876 // just recalc width if instance already exists
877 if ($.fn.DataTable.isDataTable('#rspamd_history') ) {
878 $('#rspamd_history').DataTable().columns.adjust().responsive.recalc();
879 return;
880 }
881
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100882 var table = $('#rspamd_history').DataTable({
883 responsive: true,
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100884 processing: true,
885 serverSide: false,
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100886 stateSave: true,
887 pageLength: log_pagination_size,
888 dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
889 "tr" +
890 "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100891 language: lang_datatables,
892 order: [[0, 'desc']],
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100893 initComplete: function(){
894 hideTableExpandCollapseBtn('#tab-rspamd-logs', '#rspamd_history');
895 },
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100896 ajax: {
897 type: "GET",
898 url: "/api/v1/get/logs/rspamd-history",
899 dataSrc: function(data){
900 return process_table_data(data, 'rspamd_history');
901 }
902 },
903 columns: [
904 {
905 title: lang.time,
906 data: 'unix_time',
907 defaultContent: '',
908 createdCell: function(td, cellData) {
909 createSortableDate(td, cellData)
910 }
911 },
912 {
913 title: 'IP address',
914 data: 'ip',
915 defaultContent: ''
916 },
917 {
918 title: 'From',
919 data: 'sender_mime',
920 defaultContent: ''
921 },
922 {
923 title: 'To',
924 data: 'rcpt',
925 defaultContent: ''
926 },
927 {
928 title: 'Subject',
929 data: 'subject',
930 defaultContent: ''
931 },
932 {
933 title: 'Action',
934 data: 'action',
935 defaultContent: ''
936 },
937 {
938 title: 'Score',
939 data: 'score',
940 defaultContent: '',
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100941 class: 'text-nowrap',
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100942 createdCell: function(td, cellData) {
943 $(td).attr({
944 "data-order": cellData.sortBy,
945 "data-sort": cellData.sortBy
946 });
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100947 },
948 render: function (data) {
949 return data.value;
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100950 }
951 },
952 {
953 title: 'Symbols',
954 data: 'symbols',
955 defaultContent: '',
956 className: 'none dtr-col-md'
957 },
958 {
959 title: 'Msg size',
960 data: 'size',
961 defaultContent: ''
962 },
963 {
964 title: 'Scan Time',
965 data: 'scan_time',
966 defaultContent: '',
967 createdCell: function(td, cellData) {
968 $(td).attr({
969 "data-order": cellData.sortBy,
970 "data-sort": cellData.sortBy
971 });
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100972 },
973 render: function (data) {
974 return data.value;
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100975 }
976 },
977 {
978 title: 'ID',
979 data: 'message-id',
980 defaultContent: ''
981 },
982 {
983 title: 'Authenticated user',
984 data: 'user',
985 defaultContent: ''
986 }
987 ]
988 });
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +0100989
990 table.on('responsive-resize', function (e, datatable, columns){
991 hideTableExpandCollapseBtn('#tab-rspamd-history', '#rspamd_history');
992 });
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100993 }
994 function process_table_data(data, table) {
995 if (table == 'rspamd_history') {
996 $.each(data, function (i, item) {
997 if (item.rcpt_mime != "") {
998 item.rcpt = escapeHtml(item.rcpt_mime.join(", "));
999 }
1000 else {
1001 item.rcpt = escapeHtml(item.rcpt_smtp.join(", "));
1002 }
1003 item.symbols = Object.keys(item.symbols).sort(function (a, b) {
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001004 if (item.symbols[a].score === 0) return 1;
1005 if (item.symbols[b].score === 0) return -1;
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001006 if (item.symbols[b].score < 0 && item.symbols[a].score < 0) {
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001007 return item.symbols[a].score - item.symbols[b].score;
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001008 }
1009 if (item.symbols[b].score > 0 && item.symbols[a].score > 0) {
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001010 return item.symbols[b].score - item.symbols[a].score;
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001011 }
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001012 return item.symbols[b].score - item.symbols[a].score;
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001013 }).map(function(key) {
1014 var sym = item.symbols[key];
1015 if (sym.score < 0) {
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001016 sym.score_formatted = '(<span class="text-success"><b>' + sym.score + '</b></span>)';
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001017 }
1018 else if (sym.score === 0) {
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001019 sym.score_formatted = '(<span><b>' + sym.score + '</b></span>)';
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001020 }
1021 else {
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001022 sym.score_formatted = '(<span class="text-danger"><b>' + sym.score + '</b></span>)';
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001023 }
1024 var str = '<strong>' + key + '</strong> ' + sym.score_formatted;
1025 if (sym.options) {
1026 str += ' [' + escapeHtml(sym.options.join(", ")) + "]";
1027 }
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001028 return str;
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001029 }).join('<br>\n');
1030 item.subject = escapeHtml(item.subject);
1031 var scan_time = item.time_real.toFixed(3);
1032 if (item.time_virtual) {
1033 scan_time += ' / ' + item.time_virtual.toFixed(3);
1034 }
1035 item.scan_time = {
1036 "sortBy": item.time_real,
1037 "value": scan_time
1038 };
1039 if (item.action === 'clean' || item.action === 'no action') {
1040 item.action = "<div class='badge fs-6 bg-success'>" + item.action + "</div>";
1041 } else if (item.action === 'rewrite subject' || item.action === 'add header' || item.action === 'probable spam') {
1042 item.action = "<div class='badge fs-6 bg-warning'>" + item.action + "</div>";
1043 } else if (item.action === 'spam' || item.action === 'reject') {
1044 item.action = "<div class='badge fs-6 bg-danger'>" + item.action + "</div>";
1045 } else {
1046 item.action = "<div class='badge fs-6 bg-info'>" + item.action + "</div>";
1047 }
1048 var score_content;
1049 if (item.score < item.required_score) {
1050 score_content = "[ <span class='text-success'>" + item.score.toFixed(2) + " / " + item.required_score + "</span> ]";
1051 } else {
1052 score_content = "[ <span class='text-danger'>" + item.score.toFixed(2) + " / " + item.required_score + "</span> ]";
1053 }
1054 item.score = {
1055 "sortBy": item.score,
1056 "value": score_content
1057 };
1058 if (item.user == null) {
1059 item.user = "none";
1060 }
1061 });
1062 } else if (table == 'autodiscover_log') {
1063 $.each(data, function (i, item) {
1064 if (item.ua == null) {
1065 item.ua = 'unknown';
1066 } else {
1067 item.ua = escapeHtml(item.ua);
1068 }
1069 item.ua = '<span style="font-size:small">' + item.ua + '</span>';
1070 if (item.service == "activesync") {
1071 item.service = '<span class="badge fs-6 bg-info">ActiveSync</span>';
1072 }
1073 else if (item.service == "imap") {
1074 item.service = '<span class="badge fs-6 bg-success">IMAP, SMTP, Cal-/CardDAV</span>';
1075 }
1076 else {
1077 item.service = '<span class="badge fs-6 bg-danger">' + escapeHtml(item.service) + '</span>';
1078 }
1079 });
1080 } else if (table == 'watchdog') {
1081 $.each(data, function (i, item) {
1082 if (item.message == null) {
1083 item.message = 'Health level: ' + item.lvl + '% (' + item.hpnow + '/' + item.hptotal + ')';
1084 if (item.hpdiff < 0) {
1085 item.trend = '<span class="badge fs-6 bg-danger"><i class="bi bi-caret-down-fill"></i> ' + item.hpdiff + '</span>';
1086 }
1087 else if (item.hpdiff == 0) {
1088 item.trend = '<span class="badge fs-6 bg-info"><i class="bi bi-caret-right-fill"></i> ' + item.hpdiff + '</span>';
1089 }
1090 else {
1091 item.trend = '<span class="badge fs-6 bg-success"><i class="bi bi-caret-up-fill"></i> ' + item.hpdiff + '</span>';
1092 }
1093 }
1094 else {
1095 item.trend = '';
1096 item.service = '';
1097 }
1098 });
1099 } else if (table == 'mailcow_ui') {
1100 $.each(data, function (i, item) {
1101 if (item === null) { return true; }
1102 item.user = escapeHtml(item.user);
1103 item.call = escapeHtml(item.call);
1104 item.task = '<code>' + item.task + '</code>';
1105 item.type = '<span class="badge fs-6 bg-' + item.type + '">' + item.type + '</span>';
1106 });
1107 } else if (table == 'sasl_log_table') {
1108 $.each(data, function (i, item) {
1109 if (item === null) { return true; }
1110 item.username = escapeHtml(item.username);
1111 item.service = '<div class="badge fs-6 bg-secondary">' + item.service.toUpperCase() + '</div>';
1112 });
1113 } else if (table == 'general_syslog') {
1114 $.each(data, function (i, item) {
1115 if (item === null) { return true; }
1116 if (item.message.match("^base64,")) {
1117 try {
1118 item.message = atob(item.message.slice(7)).replace(/\\n/g, "<br />");
1119 } catch(e) {
1120 item.message = item.message.slice(7);
1121 }
1122 } else {
1123 item.message = escapeHtml(item.message);
1124 }
1125 item.call = escapeHtml(item.call);
1126 var danger_class = ["emerg", "alert", "crit", "err"];
1127 var warning_class = ["warning", "warn"];
1128 var info_class = ["notice", "info", "debug"];
1129 if (jQuery.inArray(item.priority, danger_class) !== -1) {
1130 item.priority = '<span class="badge fs-6 bg-danger">' + item.priority + '</span>';
1131 } else if (jQuery.inArray(item.priority, warning_class) !== -1) {
1132 item.priority = '<span class="badge fs-6 bg-warning">' + item.priority + '</span>';
1133 } else if (jQuery.inArray(item.priority, info_class) !== -1) {
1134 item.priority = '<span class="badge fs-6 bg-info">' + item.priority + '</span>';
1135 }
1136 });
1137 } else if (table == 'apilog') {
1138 $.each(data, function (i, item) {
1139 if (item === null) { return true; }
1140 if (item.method == 'GET') {
1141 item.method = '<span class="badge fs-6 bg-success">' + item.method + '</span>';
1142 } else if (item.method == 'POST') {
1143 item.method = '<span class="badge fs-6 bg-warning">' + item.method + '</span>';
1144 }
1145 item.data = escapeHtml(item.data);
1146 });
1147 } else if (table == 'rllog') {
1148 $.each(data, function (i, item) {
1149 if (item.user == null) {
1150 item.user = "none";
1151 }
1152 if (item.rl_hash == null) {
1153 item.rl_hash = "err";
1154 }
1155 item.indicator = '<span style="border-right:6px solid #' + intToRGB(hashCode(item.rl_hash)) + ';padding-left:5px;">&nbsp;</span>';
1156 if (item.rl_hash != 'err') {
1157 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>';
1158 }
1159 });
1160 }
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001161 return data;
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001162 };
1163 $('.add_log_lines').on('click', function (e) {
1164 e.preventDefault();
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001165 var log_table= $(this).data("table");
1166 var new_nrows = $(this).data("nrows");
1167 var post_process = $(this).data("post-process");
1168 var log_url = $(this).data("log-url");
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001169 if (log_table === undefined || new_nrows === undefined || post_process === undefined || log_url === undefined) {
1170 console.log("no data-table or data-nrows or log_url or data-post-process attr found");
1171 return;
1172 }
1173
1174 if (table = $('#' + log_table).DataTable()) {
1175 var heading = $('#' + log_table).closest('.card').find('.card-header');
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001176 var load_rows = (table.data().count() + 1) + '-' + (table.data().count() + new_nrows)
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001177
1178 $.get('/api/v1/get/logs/' + log_url + '/' + load_rows).then(function(data){
1179 if (data.length === undefined) { mailcow_alert_box(lang.no_new_rows, "info"); return; }
1180 var rows = process_table_data(data, post_process);
1181 var rows_now = (table.page.len() + data.length);
1182 $(heading).children('.table-lines').text(rows_now)
1183 mailcow_alert_box(data.length + lang.additional_rows, "success");
1184 table.rows.add(rows).draw();
1185 });
1186 }
1187 })
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001188 function hideTableExpandCollapseBtn(tab, table){
1189 if ($(table).hasClass('collapsed'))
1190 $(tab).find(".table_collapse_option").show();
1191 else
1192 $(tab).find(".table_collapse_option").hide();
1193 }
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001194
1195 // detect element visibility changes
1196 function onVisible(element, callback) {
1197 $(document).ready(function() {
1198 element_object = document.querySelector(element);
1199 if (element_object === null) return;
1200
1201 new IntersectionObserver((entries, observer) => {
1202 entries.forEach(entry => {
1203 if(entry.intersectionRatio > 0) {
1204 callback(element_object);
1205 }
1206 });
1207 }).observe(element_object);
1208 });
1209 }
1210 // Draw Table if tab is active
1211 onVisible("[id^=postfix_log]", () => draw_postfix_logs());
1212 onVisible("[id^=dovecot_log]", () => draw_dovecot_logs());
1213 onVisible("[id^=sogo_log]", () => draw_sogo_logs());
1214 onVisible("[id^=watchdog_log]", () => draw_watchdog_logs());
1215 onVisible("[id^=autodiscover_log]", () => draw_autodiscover_logs());
1216 onVisible("[id^=acme_log]", () => draw_acme_logs());
1217 onVisible("[id^=api_log]", () => draw_api_logs());
1218 onVisible("[id^=rl_log]", () => draw_rl_logs());
1219 onVisible("[id^=ui_logs]", () => draw_ui_logs());
1220 onVisible("[id^=sasl_logs]", () => draw_sasl_logs());
1221 onVisible("[id^=netfilter_log]", () => draw_netfilter_logs());
1222 onVisible("[id^=rspamd_history]", () => draw_rspamd_history());
1223 onVisible("[id^=rspamd_donut]", () => rspamd_pie_graph());
1224
1225
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001226 // start polling host stats if tab is active
1227 onVisible("[id^=tab-containers]", () => update_stats());
1228 // start polling container stats if collapse is active
1229 var containerElements = document.querySelectorAll(".container-details-collapse");
1230 for (let i = 0; i < containerElements.length; i++){
1231 new IntersectionObserver((entries, observer) => {
1232 entries.forEach(entry => {
1233 if(entry.intersectionRatio > 0) {
1234
1235 if (!containerElements[i].classList.contains("show")){
1236 var container = containerElements[i].id.replace("Collapse", "");
1237 var container_id = containerElements[i].getAttribute("data-id");
1238
1239 // check if chart exists or needs to be created
1240 if (!Chart.getChart(container + "_DiskIOChart"))
1241 createReadWriteChart(container + "_DiskIOChart", "Read", "Write");
1242 if (!Chart.getChart(container + "_NetIOChart"))
1243 createReadWriteChart(container + "_NetIOChart", "Recv", "Sent");
1244
1245 // add container to polling list
1246 containersToUpdate[container] = {
1247 id: container_id,
1248 state: "idle"
1249 }
1250
1251 // stop polling if collapse is closed
1252 containerElements[i].addEventListener('hidden.bs.collapse', function () {
1253 var diskIOCtx = Chart.getChart(container + "_DiskIOChart");
1254 var netIOCtx = Chart.getChart(container + "_NetIOChart");
1255
1256 diskIOCtx.data.datasets[0].data = [];
1257 diskIOCtx.data.datasets[1].data = [];
1258 diskIOCtx.data.labels = [];
1259 netIOCtx.data.datasets[0].data = [];
1260 netIOCtx.data.datasets[1].data = [];
1261 netIOCtx.data.labels = [];
1262
1263 diskIOCtx.update();
1264 netIOCtx.update();
1265
1266 delete containersToUpdate[container];
1267 });
1268 }
1269
1270 }
1271 });
1272 }).observe(containerElements[i]);
1273 }
1274});
1275
1276
1277// update system stats - every 5 seconds if system & container tab is active
1278function update_stats(timeout=5){
1279 if (!$('#tab-containers').hasClass('active')) {
1280 // tab not active - dont fetch stats - run again in n seconds
1281 return;
1282 }
1283
1284 window.fetch("/api/v1/get/status/host", {method:'GET',cache:'no-cache'}).then(function(response) {
1285 return response.json();
1286 }).then(function(data) {
1287 console.log(data);
1288
1289 if (data){
1290 // display table data
1291 $("#host_date").text(data.system_time);
1292 $("#host_uptime").text(formatUptime(data.uptime));
1293 $("#host_cpu_cores").text(data.cpu.cores);
1294 $("#host_cpu_usage").text(parseInt(data.cpu.usage).toString() + "%");
1295 $("#host_memory_total").text((data.memory.total / (1024 ** 3)).toFixed(2).toString() + "GB");
1296 $("#host_memory_usage").text(parseInt(data.memory.usage).toString() + "%");
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001297 if (data.architecture == "aarch64"){
1298 $("#host_architecture").html('<span data-bs-toggle="tooltip" data-bs-placement="top" title="' + lang_debug.wip +'">' + data.architecture + ' ⚠️</span>');
1299 }
1300 else {
1301 $("#host_architecture").html(data.architecture);
1302 }
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001303
1304 // update cpu and mem chart
1305 var cpu_chart = Chart.getChart("host_cpu_chart");
1306 var mem_chart = Chart.getChart("host_mem_chart");
1307
1308 cpu_chart.data.labels.push(data.system_time.split(" ")[1]);
1309 if (cpu_chart.data.labels.length > 30) cpu_chart.data.labels.shift();
1310 mem_chart.data.labels.push(data.system_time.split(" ")[1]);
1311 if (mem_chart.data.labels.length > 30) mem_chart.data.labels.shift();
1312
1313 cpu_chart.data.datasets[0].data.push(data.cpu.usage);
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001314 if (cpu_chart.data.datasets[0].data.length > 30) cpu_chart.data.datasets[0].data.shift();
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001315 mem_chart.data.datasets[0].data.push(data.memory.usage);
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001316 if (mem_chart.data.datasets[0].data.length > 30) mem_chart.data.datasets[0].data.shift();
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001317
1318 cpu_chart.update();
1319 mem_chart.update();
1320 }
1321
1322 // run again in n seconds
1323 setTimeout(update_stats, timeout * 1000);
1324 });
1325}
1326// update specific container stats - every n (default 5s) seconds
1327function update_container_stats(timeout=5){
1328
1329 if ($('#tab-containers').hasClass('active')) {
1330 for (let container in containersToUpdate){
1331 container_id = containersToUpdate[container].id;
1332 // check if container update stats is already running
1333 if (containersToUpdate[container].state == "running")
1334 continue;
1335 containersToUpdate[container].state = "running";
1336
1337
1338 window.fetch("/api/v1/get/status/container/" + container_id, {method:'GET',cache:'no-cache'}).then(function(response) {
1339 return response.json();
1340 }).then(function(data) {
1341 var diskIOCtx = Chart.getChart(container + "_DiskIOChart");
1342 var netIOCtx = Chart.getChart(container + "_NetIOChart");
1343
1344 console.log(container);
1345 console.log(data);
1346 prev_stats = null;
1347 if (data.length >= 2){
1348 prev_stats = data[data.length -2];
1349
1350 // hide spinners if we collected enough data
1351 $('#' + container + "_DiskIOChart").removeClass('d-none');
1352 $('#' + container + "_DiskIOChart").prev().addClass('d-none');
1353 $('#' + container + "_NetIOChart").removeClass('d-none');
1354 $('#' + container + "_NetIOChart").prev().addClass('d-none');
1355 }
1356
1357 data = data[data.length -1];
1358
1359 if (prev_stats != null){
1360 // calc time diff
1361 var time_diff = (new Date(data.read) - new Date(prev_stats.read)) / 1000;
1362
1363 // calc disk io b/s
1364 if ('io_service_bytes_recursive' in prev_stats.blkio_stats && prev_stats.blkio_stats.io_service_bytes_recursive !== null){
1365 var prev_read_bytes = 0;
1366 var prev_write_bytes = 0;
1367 for (var i = 0; i < prev_stats.blkio_stats.io_service_bytes_recursive.length; i++){
1368 if (prev_stats.blkio_stats.io_service_bytes_recursive[i].op == "read")
1369 prev_read_bytes = prev_stats.blkio_stats.io_service_bytes_recursive[i].value;
1370 else if (prev_stats.blkio_stats.io_service_bytes_recursive[i].op == "write")
1371 prev_write_bytes = prev_stats.blkio_stats.io_service_bytes_recursive[i].value;
1372 }
1373 var read_bytes = 0;
1374 var write_bytes = 0;
1375 for (var i = 0; i < data.blkio_stats.io_service_bytes_recursive.length; i++){
1376 if (data.blkio_stats.io_service_bytes_recursive[i].op == "read")
1377 read_bytes = data.blkio_stats.io_service_bytes_recursive[i].value;
1378 else if (data.blkio_stats.io_service_bytes_recursive[i].op == "write")
1379 write_bytes = data.blkio_stats.io_service_bytes_recursive[i].value;
1380 }
1381 var diff_bytes_read = (read_bytes - prev_read_bytes) / time_diff;
1382 var diff_bytes_write = (write_bytes - prev_write_bytes) / time_diff;
1383 }
1384
1385 // calc net io b/s
1386 if ('networks' in prev_stats){
1387 var prev_recv_bytes = 0;
1388 var prev_sent_bytes = 0;
1389 for (var key in prev_stats.networks){
1390 prev_recv_bytes += prev_stats.networks[key].rx_bytes;
1391 prev_sent_bytes += prev_stats.networks[key].tx_bytes;
1392 }
1393 var recv_bytes = 0;
1394 var sent_bytes = 0;
1395 for (var key in data.networks){
1396 recv_bytes += data.networks[key].rx_bytes;
1397 sent_bytes += data.networks[key].tx_bytes;
1398 }
1399 var diff_bytes_recv = (recv_bytes - prev_recv_bytes) / time_diff;
1400 var diff_bytes_sent = (sent_bytes - prev_sent_bytes) / time_diff;
1401 }
1402
1403 addReadWriteChart(diskIOCtx, diff_bytes_read, diff_bytes_write, "");
1404 addReadWriteChart(netIOCtx, diff_bytes_recv, diff_bytes_sent, "");
1405 }
1406
1407 // run again in n seconds
1408 containersToUpdate[container].state = "idle";
1409 }).catch(err => {
1410 console.log(err);
1411 });
1412 }
1413 }
1414
1415 // run again in n seconds
1416 setTimeout(update_container_stats, timeout * 1000);
1417}
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001418// format hosts uptime seconds to readable string
1419function formatUptime(seconds){
1420 seconds = Number(seconds);
1421 var d = Math.floor(seconds / (3600*24));
1422 var h = Math.floor(seconds % (3600*24) / 3600);
1423 var m = Math.floor(seconds % 3600 / 60);
1424 var s = Math.floor(seconds % 60);
1425
1426 var dFormat = d > 0 ? d + "D " : "";
1427 var hFormat = h > 0 ? h + "H " : "";
1428 var mFormat = m > 0 ? m + "M " : "";
1429 var sFormat = s > 0 ? s + "S" : "";
1430 return dFormat + hFormat + mFormat + sFormat;
1431}
1432// format bytes to readable string
1433function formatBytes(bytes){
1434 // b
1435 if (bytes < 1000) return bytes.toFixed(2).toString()+' B/s';
1436 // b to kb
1437 bytes = bytes / 1024;
1438 if (bytes < 1000) return bytes.toFixed(2).toString()+' KB/s';
1439 // kb to mb
1440 bytes = bytes / 1024;
1441 if (bytes < 1000) return bytes.toFixed(2).toString()+' MB/s';
1442 // final mb to gb
1443 return (bytes / 1024).toFixed(2).toString()+' GB/s';
1444}
1445// create read write line chart
1446function createReadWriteChart(chart_id, read_lable, write_lable){
1447 var ctx = document.getElementById(chart_id);
1448
1449 var dataNet = {
1450 labels: [],
1451 datasets: [{
1452 label: read_lable,
1453 backgroundColor: "rgba(41, 187, 239, 0.3)",
1454 borderColor: "rgba(41, 187, 239, 0.6)",
1455 pointRadius: 1,
1456 pointHitRadius: 6,
1457 borderWidth: 2,
1458 fill: true,
1459 tension: 0.2,
1460 data: []
1461 }, {
1462 label: write_lable,
1463 backgroundColor: "rgba(239, 60, 41, 0.3)",
1464 borderColor: "rgba(239, 60, 41, 0.6)",
1465 pointRadius: 1,
1466 pointHitRadius: 6,
1467 borderWidth: 2,
1468 fill: true,
1469 tension: 0.2,
1470 data: []
1471 }]
1472 };
1473 var optionsNet = {
1474 interaction: {
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001475 mode: 'index'
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001476 },
1477 scales: {
1478 yAxis: {
1479 min: 0,
1480 grid: {
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001481 display: false
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001482 },
1483 ticks: {
1484 callback: function(i, index, ticks) {
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001485 return formatBytes(i);
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001486 }
1487 }
1488 },
1489 xAxis: {
1490 grid: {
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001491 display: false
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001492 }
1493 }
1494 }
1495 };
1496
1497 return new Chart(ctx, {
1498 type: 'line',
1499 data: dataNet,
1500 options: optionsNet
1501 });
1502}
1503// add to read write line chart
1504function addReadWriteChart(chart_context, read_point, write_point, time, limit = 30){
1505 // push time label for x-axis
1506 chart_context.data.labels.push(time);
1507 if (chart_context.data.labels.length > limit) chart_context.data.labels.shift();
1508
1509 // push datapoints
1510 chart_context.data.datasets[0].data.push(read_point);
1511 chart_context.data.datasets[1].data.push(write_point);
1512 // shift data if more than 20 entires exists
1513 if (chart_context.data.datasets[0].data.length > limit) chart_context.data.datasets[0].data.shift();
1514 if (chart_context.data.datasets[1].data.length > limit) chart_context.data.datasets[1].data.shift();
1515
1516 chart_context.update();
1517}
1518// create host cpu and mem chart
1519function createHostCpuAndMemChart(){
1520 var cpu_ctx = document.getElementById("host_cpu_chart");
1521 var mem_ctx = document.getElementById("host_mem_chart");
1522
1523 var dataCpu = {
1524 labels: [],
1525 datasets: [{
1526 label: "CPU %",
1527 backgroundColor: "rgba(41, 187, 239, 0.3)",
1528 borderColor: "rgba(41, 187, 239, 0.6)",
1529 pointRadius: 1,
1530 pointHitRadius: 6,
1531 borderWidth: 2,
1532 fill: true,
1533 tension: 0.2,
1534 data: []
1535 }]
1536 };
1537 var optionsCpu = {
1538 interaction: {
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001539 mode: 'index'
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001540 },
1541 scales: {
1542 yAxis: {
1543 min: 0,
1544 grid: {
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001545 display: false
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001546 },
1547 ticks: {
1548 callback: function(i, index, ticks) {
1549 return i.toFixed(0).toString() + "%";
1550 }
1551 }
1552 },
1553 xAxis: {
1554 grid: {
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001555 display: false
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001556 }
1557 }
1558 }
1559 };
1560
1561 var dataMem = {
1562 labels: [],
1563 datasets: [{
1564 label: "MEM %",
1565 backgroundColor: "rgba(41, 187, 239, 0.3)",
1566 borderColor: "rgba(41, 187, 239, 0.6)",
1567 pointRadius: 1,
1568 pointHitRadius: 6,
1569 borderWidth: 2,
1570 fill: true,
1571 tension: 0.2,
1572 data: []
1573 }]
1574 };
1575 var optionsMem = {
1576 interaction: {
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001577 mode: 'index'
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001578 },
1579 scales: {
1580 yAxis: {
1581 min: 0,
1582 grid: {
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001583 display: false
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001584 },
1585 ticks: {
1586 callback: function(i, index, ticks) {
1587 return i.toFixed(0).toString() + "%";
1588 }
1589 }
1590 },
1591 xAxis: {
1592 grid: {
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001593 display: false
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001594 }
1595 }
1596 }
1597 };
1598
1599
1600 var net_io_chart = new Chart(cpu_ctx, {
1601 type: 'line',
1602 data: dataCpu,
1603 options: optionsCpu
1604 });
1605 var disk_io_chart = new Chart(mem_ctx, {
1606 type: 'line',
1607 data: dataMem,
1608 options: optionsMem
1609 });
1610}
1611// check for mailcow updates
1612function check_update(current_version, github_repo_url){
1613 if (!current_version || !github_repo_url) return false;
1614
1615 var github_account = github_repo_url.split("/")[3];
1616 var github_repo_name = github_repo_url.split("/")[4];
1617
1618 // get details about latest release
1619 window.fetch("https://api.github.com/repos/"+github_account+"/"+github_repo_name+"/releases/latest", {method:'GET',cache:'no-cache'}).then(function(response) {
1620 return response.json();
1621 }).then(function(latest_data) {
1622 // get details about current release
1623 window.fetch("https://api.github.com/repos/"+github_account+"/"+github_repo_name+"/releases/tags/"+current_version, {method:'GET',cache:'no-cache'}).then(function(response) {
1624 return response.json();
1625 }).then(function(current_data) {
1626 // compare releases
1627 var date_current = new Date(current_data.created_at);
1628 var date_latest = new Date(latest_data.created_at);
1629 if (date_latest.getTime() <= date_current.getTime()){
1630 // no update available
1631 $("#mailcow_update").removeClass("text-warning text-danger").addClass("text-success");
1632 $("#mailcow_update").html("<b>" + lang_debug.no_update_available + "</b>");
1633 } else {
1634 // update available
1635 $("#mailcow_update").removeClass("text-danger text-success").addClass("text-warning");
1636 $("#mailcow_update").html(lang_debug.update_available + ` <a href="#" id="mailcow_update_changelog">`+latest_data.tag_name+`</a>`);
1637 $("#mailcow_update_changelog").click(function(){
1638 if (mailcow_cc_role !== "admin" && mailcow_cc_role !== "domainadmin")
1639 return;
1640
1641 showVersionModal("New Release " + latest_data.tag_name, latest_data.tag_name);
1642 })
1643 }
1644 }).catch(err => {
1645 // err
1646 console.log(err);
1647 $("#mailcow_update").removeClass("text-success text-warning").addClass("text-danger");
1648 $("#mailcow_update").html("<b>"+ lang_debug.update_failed +"</b>");
1649 });
1650 }).catch(err => {
1651 // err
1652 console.log(err);
1653 $("#mailcow_update").removeClass("text-success text-warning").addClass("text-danger");
1654 $("#mailcow_update").html("<b>"+ lang_debug.update_failed +"</b>");
1655 });
1656}
1657// show version changelog modal
1658function showVersionModal(title, version){
1659 $.ajax({
1660 type: 'GET',
1661 url: 'https://api.github.com/repos/' + mailcow_info.project_owner + '/' + mailcow_info.project_repo + '/releases/tags/' + version,
1662 dataType: 'json',
1663 success: function (data) {
1664 var md = window.markdownit();
1665 var result = md.render(data.body);
1666 result = parseGithubMarkdownLinks(result);
1667
1668 $('#showVersionModal').find(".modal-title").html(title);
1669 $('#showVersionModal').find(".modal-body").html(`
1670 <h3>` + data.name + `</h3>
1671 <span class="mt-4">` + result + `</span>
1672 <span><b>Github Link:</b>
1673 <a target="_blank" href="https://github.com/` + mailcow_info.project_owner + `/` + mailcow_info.project_repo + `/releases/tag/` + version + `">` + version + `</a>
1674 </span>
1675 `);
1676
1677 new bootstrap.Modal(document.getElementById("showVersionModal"), {
1678 backdrop: 'static',
1679 keyboard: false
1680 }).show();
1681 }
1682 });
1683}
1684function parseGithubMarkdownLinks(inputText) {
1685 var replacedText, replacePattern1;
1686
1687 replacePattern1 = /(\b(https?):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
1688 replacedText = inputText.replace(replacePattern1, (matched, index, original, input_string) => {
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001689 if (matched.includes('github.com')){
1690 // return short link if it's github link
1691 last_uri_path = matched.split('/');
1692 last_uri_path = last_uri_path[last_uri_path.length - 1];
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001693
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001694 // adjust Full Changelog link to match last git version and new git version, if link is a compare link
1695 if (matched.includes('/compare/') && mailcow_info.last_version_tag !== ''){
1696 matched = matched.replace(last_uri_path, mailcow_info.last_version_tag + '...' + mailcow_info.version_tag);
1697 last_uri_path = mailcow_info.last_version_tag + '...' + mailcow_info.version_tag;
1698 }
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001699
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001700 return '<a href="' + matched + '" target="_blank">' + last_uri_path + '</a><br>';
1701 };
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001702
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001703 // if it's not a github link, return complete link
1704 return '<a href="' + matched + '" target="_blank">' + matched + '</a>';
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001705 });
1706
1707 return replacedText;
1708}
1709
1710function convertTimestampToLocalFormat(timestamp) {
1711 var date = new Date(timestamp ? timestamp * 1000 : 0);
1712 return date.toLocaleDateString(LOCALE, DATETIME_FORMAT);
1713}