[BS5] add host statistics
This commit is contained in:
parent
9747995510
commit
a3c0737ba8
@ -8,6 +8,7 @@ RUN apk add --update --no-cache python3 \
|
|||||||
py3-pip \
|
py3-pip \
|
||||||
openssl \
|
openssl \
|
||||||
tzdata \
|
tzdata \
|
||||||
|
py3-psutil \
|
||||||
&& pip3 install --upgrade pip \
|
&& pip3 install --upgrade pip \
|
||||||
docker \
|
docker \
|
||||||
flask \
|
flask \
|
||||||
|
@ -6,6 +6,7 @@ from flask import jsonify
|
|||||||
from flask import Response
|
from flask import Response
|
||||||
from flask import request
|
from flask import request
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
from datetime import datetime
|
||||||
import docker
|
import docker
|
||||||
import uuid
|
import uuid
|
||||||
import signal
|
import signal
|
||||||
@ -17,6 +18,7 @@ import ssl
|
|||||||
import socket
|
import socket
|
||||||
import subprocess
|
import subprocess
|
||||||
import traceback
|
import traceback
|
||||||
|
import psutil
|
||||||
|
|
||||||
docker_client = docker.DockerClient(base_url='unix://var/run/docker.sock', version='auto')
|
docker_client = docker.DockerClient(base_url='unix://var/run/docker.sock', version='auto')
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
@ -326,6 +328,48 @@ class container_post(Resource):
|
|||||||
else:
|
else:
|
||||||
return jsonify(type='danger', msg='command did not complete')
|
return jsonify(type='danger', msg='command did not complete')
|
||||||
|
|
||||||
|
class host_stats_get(Resource):
|
||||||
|
def get(self):
|
||||||
|
try:
|
||||||
|
system_time = datetime.now()
|
||||||
|
|
||||||
|
disk_io_before = psutil.disk_io_counters(perdisk=False)
|
||||||
|
net_io_before = psutil.net_io_counters(pernic=False)
|
||||||
|
time.sleep(1)
|
||||||
|
disk_io_after = psutil.disk_io_counters(perdisk=False)
|
||||||
|
net_io_after = psutil.net_io_counters(pernic=False)
|
||||||
|
|
||||||
|
disks_read_per_sec = disk_io_after.read_bytes - disk_io_before.read_bytes
|
||||||
|
disks_write_per_sec = disk_io_after.write_bytes - disk_io_before.write_bytes
|
||||||
|
net_recv_per_sec = net_io_after.bytes_recv - net_io_before.bytes_recv
|
||||||
|
net_sent_per_sec = net_io_after.bytes_sent - net_io_before.bytes_sent
|
||||||
|
|
||||||
|
|
||||||
|
host_stats = {
|
||||||
|
"cpu": {
|
||||||
|
"cores": psutil.cpu_count(),
|
||||||
|
"usage": psutil.cpu_percent()
|
||||||
|
},
|
||||||
|
"memory": {
|
||||||
|
"total": psutil.virtual_memory().total,
|
||||||
|
"usage": psutil.virtual_memory().percent,
|
||||||
|
"swap": psutil.swap_memory()
|
||||||
|
},
|
||||||
|
"disk": {
|
||||||
|
"read_bytes": disks_read_per_sec,
|
||||||
|
"write_bytes": disks_write_per_sec
|
||||||
|
},
|
||||||
|
"network": {
|
||||||
|
"bytes_recv": net_recv_per_sec,
|
||||||
|
"bytes_sent": net_sent_per_sec
|
||||||
|
},
|
||||||
|
"uptime": time.time() - psutil.boot_time(),
|
||||||
|
"system_time": system_time.strftime("%d.%m.%Y %H:%M:%S")
|
||||||
|
}
|
||||||
|
return host_stats
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify(type='danger', msg=str(e))
|
||||||
|
|
||||||
def exec_cmd_container(container, cmd, user, timeout=2, shell_cmd="/bin/bash"):
|
def exec_cmd_container(container, cmd, user, timeout=2, shell_cmd="/bin/bash"):
|
||||||
|
|
||||||
def recv_socket_data(c_socket, timeout):
|
def recv_socket_data(c_socket, timeout):
|
||||||
@ -406,6 +450,7 @@ def startFlaskAPI():
|
|||||||
api.add_resource(containers_get, '/containers/json')
|
api.add_resource(containers_get, '/containers/json')
|
||||||
api.add_resource(container_get, '/containers/<string:container_id>/json')
|
api.add_resource(container_get, '/containers/<string:container_id>/json')
|
||||||
api.add_resource(container_post, '/containers/<string:container_id>/<string:post_action>')
|
api.add_resource(container_post, '/containers/<string:container_id>/<string:post_action>')
|
||||||
|
api.add_resource(host_stats_get, '/host/stats')
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
api_thread = Thread(target=startFlaskAPI)
|
api_thread = Thread(target=startFlaskAPI)
|
||||||
|
@ -44,15 +44,22 @@ foreach ($containers as $container => $container_info) {
|
|||||||
$containers[$container]['State']['StartedAtHR'] = $started;
|
$containers[$container]['State']['StartedAtHR'] = $started;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get mailconf data
|
||||||
|
$hostname = getenv('MAILCOW_HOSTNAME');
|
||||||
|
$timezone = getenv('TZ');
|
||||||
|
|
||||||
$template = 'debug.twig';
|
$template = 'debug.twig';
|
||||||
$template_data = [
|
$template_data = [
|
||||||
'log_lines' => getenv('LOG_LINES'),
|
'log_lines' => getenv('LOG_LINES'),
|
||||||
'vmail_df' => $vmail_df,
|
'vmail_df' => $vmail_df,
|
||||||
|
'hostname' => $hostname,
|
||||||
|
'timezone' => $timezone,
|
||||||
'solr_status' => $solr_status,
|
'solr_status' => $solr_status,
|
||||||
'solr_uptime' => round($solr_status['status']['dovecot-fts']['uptime'] / 1000 / 60 / 60),
|
'solr_uptime' => round($solr_status['status']['dovecot-fts']['uptime'] / 1000 / 60 / 60),
|
||||||
'clamd_status' => $clamd_status,
|
'clamd_status' => $clamd_status,
|
||||||
'containers' => $containers,
|
'containers' => $containers,
|
||||||
'lang_admin' => json_encode($lang['admin']),
|
'lang_admin' => json_encode($lang['admin']),
|
||||||
|
'lang_debug' => json_encode($lang['debug']),
|
||||||
'lang_datatables' => json_encode($lang['datatables']),
|
'lang_datatables' => json_encode($lang['datatables']),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ function docker($action, $service_name = null, $attr1 = null, $attr2 = null, $ex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
break;
|
||||||
case 'containers':
|
case 'containers':
|
||||||
curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/containers/json');
|
curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/containers/json');
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||||||
@ -146,5 +147,23 @@ function docker($action, $service_name = null, $attr1 = null, $attr2 = null, $ex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'host_stats':
|
||||||
|
curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/host/stats');
|
||||||
|
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||||||
|
curl_setopt($curl, CURLOPT_POST, 0);
|
||||||
|
curl_setopt($curl, CURLOPT_TIMEOUT, $DOCKER_TIMEOUT);
|
||||||
|
$response = curl_exec($curl);
|
||||||
|
if ($response === false) {
|
||||||
|
$err = curl_error($curl);
|
||||||
|
curl_close($curl);
|
||||||
|
return $err;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
curl_close($curl);
|
||||||
|
$stats = json_decode($response, true);
|
||||||
|
if (!empty($stats)) return $stats;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,15 @@ $(document).ready(function() {
|
|||||||
$(this).text(started_local_date);
|
$(this).text(started_local_date);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// set default ChartJs Font Color
|
||||||
|
Chart.defaults.color = '#999';
|
||||||
|
// create net and disk charts
|
||||||
|
createNetAndDiskChart();
|
||||||
|
// check for new version
|
||||||
|
check_update(mailcow_info.version_tag, mailcow_info.project_url);
|
||||||
|
// update system stats
|
||||||
|
update_stats();
|
||||||
});
|
});
|
||||||
jQuery(function($){
|
jQuery(function($){
|
||||||
if (localStorage.getItem("current_page") === null) {
|
if (localStorage.getItem("current_page") === null) {
|
||||||
@ -998,3 +1007,233 @@ jQuery(function($){
|
|||||||
onVisible("[id^=rspamd_history]", () => draw_rspamd_history());
|
onVisible("[id^=rspamd_history]", () => draw_rspamd_history());
|
||||||
onVisible("[id^=rspamd_donut]", () => rspamd_pie_graph());
|
onVisible("[id^=rspamd_donut]", () => rspamd_pie_graph());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// update system stats - every 5 seconds if system & container tab is active
|
||||||
|
function update_stats(){
|
||||||
|
if (!$('#tab-containers').hasClass('active')) {
|
||||||
|
// tab not active - dont fetch stats - run again in n seconds
|
||||||
|
setTimeout(update_stats, 5000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.fetch("/api/v1/get/status/host", {method:'GET',cache:'no-cache'}).then(function(response) {
|
||||||
|
return response.json();
|
||||||
|
}).then(function(data) {
|
||||||
|
$("#host_date").text(data.system_time);
|
||||||
|
$("#host_uptime").text(formatUptime(data.uptime));
|
||||||
|
$("#host_cpu_cores").text(data.cpu.cores);
|
||||||
|
$("#host_cpu_usage").text(parseInt(data.cpu.usage).toString() + "%");
|
||||||
|
$("#host_memory_total").text((data.memory.total / (1024 ** 3)).toFixed(2).toString() + "GB");
|
||||||
|
$("#host_memory_usage").text(parseInt(data.memory.usage).toString() + "%");
|
||||||
|
|
||||||
|
var net_io_chart = Chart.getChart("net_io_chart");
|
||||||
|
var disk_io_chart = Chart.getChart("disk_io_chart");
|
||||||
|
|
||||||
|
net_io_chart.data.labels.push(data.system_time.split(" ")[1]);
|
||||||
|
if (net_io_chart.data.labels.length > 20) {
|
||||||
|
net_io_chart.data.labels.shift();
|
||||||
|
}
|
||||||
|
net_io_chart.data.datasets[0].data.push((data.network.bytes_recv / 1024).toFixed(4));
|
||||||
|
net_io_chart.data.datasets[1].data.push((data.network.bytes_sent / 1024).toFixed(4));
|
||||||
|
if (net_io_chart.data.datasets[0].data.length > 20) {
|
||||||
|
net_io_chart.data.datasets[0].data.shift();
|
||||||
|
}
|
||||||
|
if (net_io_chart.data.datasets[1].data.length > 20) {
|
||||||
|
net_io_chart.data.datasets[1].data.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
disk_io_chart.data.labels.push(data.system_time.split(" ")[1]);
|
||||||
|
if (disk_io_chart.data.labels.length > 20) {
|
||||||
|
disk_io_chart.data.labels.shift();
|
||||||
|
}
|
||||||
|
disk_io_chart.data.datasets[0].data.push((data.disk.read_bytes / 1024).toFixed(4));
|
||||||
|
disk_io_chart.data.datasets[1].data.push((data.disk.write_bytes / 1024).toFixed(4));
|
||||||
|
if (disk_io_chart.data.datasets[0].data.length > 20) {
|
||||||
|
disk_io_chart.data.datasets[0].data.shift();
|
||||||
|
}
|
||||||
|
if (disk_io_chart.data.datasets[1].data.length > 20) {
|
||||||
|
disk_io_chart.data.datasets[1].data.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
net_io_chart.update();
|
||||||
|
disk_io_chart.update();
|
||||||
|
|
||||||
|
// run again in n seconds
|
||||||
|
setTimeout(update_stats, 5000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// format hosts uptime seconds to readable string
|
||||||
|
function formatUptime(seconds){
|
||||||
|
seconds = Number(seconds);
|
||||||
|
var d = Math.floor(seconds / (3600*24));
|
||||||
|
var h = Math.floor(seconds % (3600*24) / 3600);
|
||||||
|
var m = Math.floor(seconds % 3600 / 60);
|
||||||
|
var s = Math.floor(seconds % 60);
|
||||||
|
|
||||||
|
var dFormat = d > 0 ? d + "D " : "";
|
||||||
|
var hFormat = h > 0 ? h + "H " : "";
|
||||||
|
var mFormat = m > 0 ? m + "M " : "";
|
||||||
|
var sFormat = s > 0 ? s + "S" : "";
|
||||||
|
return dFormat + hFormat + mFormat + sFormat;
|
||||||
|
}
|
||||||
|
// create network and disk chart
|
||||||
|
function createNetAndDiskChart(){
|
||||||
|
var net_io_ctx = document.getElementById("net_io_chart");
|
||||||
|
var disk_io_ctx = document.getElementById("disk_io_chart");
|
||||||
|
|
||||||
|
var dataNet = {
|
||||||
|
labels: [],
|
||||||
|
datasets: [{
|
||||||
|
label: "Recieve",
|
||||||
|
backgroundColor: "rgba(41, 187, 239, 0.3)",
|
||||||
|
borderColor: "rgba(41, 187, 239, 0.6)",
|
||||||
|
color: "#ff0000",
|
||||||
|
borderWidth: 2,
|
||||||
|
fill: true,
|
||||||
|
tension: 0.2,
|
||||||
|
data: []
|
||||||
|
}, {
|
||||||
|
label: "Sent",
|
||||||
|
backgroundColor: "rgba(239, 60, 41, 0.3)",
|
||||||
|
borderColor: "rgba(239, 60, 41, 0.6)",
|
||||||
|
borderWidth: 2,
|
||||||
|
fill: true,
|
||||||
|
tension: 0.2,
|
||||||
|
data: []
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
var optionsNet = {
|
||||||
|
scales: {
|
||||||
|
yAxis: {
|
||||||
|
min: 0,
|
||||||
|
grid: {
|
||||||
|
display: false
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
callback: function(i, index, ticks) {
|
||||||
|
// b
|
||||||
|
if (i < 1000) return i.toFixed(2).toString()+' B/s';
|
||||||
|
// b to kb
|
||||||
|
i = i / 1024;
|
||||||
|
if (i < 1000) return i.toFixed(2).toString()+' KB/s';
|
||||||
|
// kb to mb
|
||||||
|
i = i / 1024;
|
||||||
|
if (i < 1000) return i.toFixed(2).toString()+' MB/s';
|
||||||
|
// final mb to gb
|
||||||
|
return (i / 1024).toFixed(2).toString()+' GB/s';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
grid: {
|
||||||
|
display: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var dataDisk = {
|
||||||
|
labels: [],
|
||||||
|
datasets: [{
|
||||||
|
label: "Read",
|
||||||
|
backgroundColor: "rgba(41, 187, 239, 0.3)",
|
||||||
|
borderColor: "rgba(41, 187, 239, 0.6)",
|
||||||
|
color: "#ff0000",
|
||||||
|
borderWidth: 2,
|
||||||
|
fill: true,
|
||||||
|
tension: 0.2,
|
||||||
|
data: []
|
||||||
|
}, {
|
||||||
|
label: "Write",
|
||||||
|
backgroundColor: "rgba(239, 60, 41, 0.3)",
|
||||||
|
borderColor: "rgba(239, 60, 41, 0.6)",
|
||||||
|
borderWidth: 2,
|
||||||
|
fill: true,
|
||||||
|
tension: 0.2,
|
||||||
|
data: []
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
var optionsDisk = {
|
||||||
|
scales: {
|
||||||
|
yAxis: {
|
||||||
|
min: 0,
|
||||||
|
grid: {
|
||||||
|
display: false
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
callback: function(i, index, ticks) {
|
||||||
|
// b
|
||||||
|
if (i < 1000) return i.toFixed(2).toString()+' B/s';
|
||||||
|
// b to kb
|
||||||
|
i = i / 1024;
|
||||||
|
if (i < 1000) return i.toFixed(2).toString()+' KB/s';
|
||||||
|
// kb to mb
|
||||||
|
i = i / 1024;
|
||||||
|
if (i < 1000) return i.toFixed(2).toString()+' MB/s';
|
||||||
|
// final mb to gb
|
||||||
|
return (i / 1024).toFixed(2).toString()+' GB/s';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
grid: {
|
||||||
|
display: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var net_io_chart = new Chart(net_io_ctx, {
|
||||||
|
type: 'line',
|
||||||
|
data: dataNet,
|
||||||
|
options: optionsNet
|
||||||
|
});
|
||||||
|
var disk_io_chart = new Chart(disk_io_ctx, {
|
||||||
|
type: 'line',
|
||||||
|
data: dataDisk,
|
||||||
|
options: optionsDisk
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// check for mailcow updates
|
||||||
|
function check_update(current_version, github_repo_url){
|
||||||
|
var github_account = github_repo_url.split("/")[3];
|
||||||
|
var github_repo_name = github_repo_url.split("/")[4];
|
||||||
|
|
||||||
|
// get details about latest release
|
||||||
|
window.fetch("https://api.github.com/repos/"+github_account+"/"+github_repo_name+"/releases/latest", {method:'GET',cache:'no-cache'}).then(function(response) {
|
||||||
|
return response.json();
|
||||||
|
}).then(function(latest_data) {
|
||||||
|
// get details about current release
|
||||||
|
window.fetch("https://api.github.com/repos/"+github_account+"/"+github_repo_name+"/releases/tags/"+current_version, {method:'GET',cache:'no-cache'}).then(function(response) {
|
||||||
|
return response.json();
|
||||||
|
}).then(function(current_data) {
|
||||||
|
// compare releases
|
||||||
|
var date_current = new Date(current_data.created_at);
|
||||||
|
var date_latest = new Date(latest_data.created_at);
|
||||||
|
if (date_latest.getTime() <= date_current.getTime()){
|
||||||
|
// no update available
|
||||||
|
$("#mailcow_update").removeClass("text-warning text-danger").addClass("text-success");
|
||||||
|
$("#mailcow_update").html("<b>" + lang_debug.no_update_available + "</b>");
|
||||||
|
} else {
|
||||||
|
// update available
|
||||||
|
$("#mailcow_update").removeClass("text-danger text-success").addClass("text-warning");
|
||||||
|
$("#mailcow_update").html(
|
||||||
|
`<b>` + lang_debug.update_available + `
|
||||||
|
<a target="_blank" href="https://github.com/`+github_account+`/`+github_repo_name+`/releases/tag/`+latest_data.tag_name+`">`+latest_data.tag_name+`</a></b>`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
// err
|
||||||
|
console.log(err);
|
||||||
|
$("#mailcow_update").removeClass("text-success text-warning").addClass("text-danger");
|
||||||
|
$("#mailcow_update").html("<b>Could not check for an Update</b>");
|
||||||
|
});
|
||||||
|
}).catch(err => {
|
||||||
|
// err
|
||||||
|
console.log(err);
|
||||||
|
$("#mailcow_update").removeClass("text-success text-warning").addClass("text-danger");
|
||||||
|
$("#mailcow_update").html("<b>Could not check for an Update</b>");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -1474,29 +1474,33 @@ if (isset($_GET['query'])) {
|
|||||||
'used_percent' => $vmail_df[4]
|
'used_percent' => $vmail_df[4]
|
||||||
);
|
);
|
||||||
echo json_encode($temp, JSON_UNESCAPED_SLASHES);
|
echo json_encode($temp, JSON_UNESCAPED_SLASHES);
|
||||||
break;
|
break;
|
||||||
case "solr":
|
case "solr":
|
||||||
$solr_status = solr_status();
|
$solr_status = solr_status();
|
||||||
$solr_size = ($solr_status['status']['dovecot-fts']['index']['size']);
|
$solr_size = ($solr_status['status']['dovecot-fts']['index']['size']);
|
||||||
$solr_documents = ($solr_status['status']['dovecot-fts']['index']['numDocs']);
|
$solr_documents = ($solr_status['status']['dovecot-fts']['index']['numDocs']);
|
||||||
if (strtolower(getenv('SKIP_SOLR')) != 'n') {
|
if (strtolower(getenv('SKIP_SOLR')) != 'n') {
|
||||||
$solr_enabled = false;
|
$solr_enabled = false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$solr_enabled = true;
|
$solr_enabled = true;
|
||||||
}
|
}
|
||||||
echo json_encode(array(
|
echo json_encode(array(
|
||||||
'type' => 'info',
|
'type' => 'info',
|
||||||
'solr_enabled' => $solr_enabled,
|
'solr_enabled' => $solr_enabled,
|
||||||
'solr_size' => $solr_size,
|
'solr_size' => $solr_size,
|
||||||
'solr_documents' => $solr_documents
|
'solr_documents' => $solr_documents
|
||||||
));
|
));
|
||||||
break;
|
break;
|
||||||
case "version":
|
case "host":
|
||||||
echo json_encode(array(
|
$stats = docker("host_stats");
|
||||||
'version' => $GLOBALS['MAILCOW_GIT_VERSION']
|
echo json_encode($stats);
|
||||||
));
|
break;
|
||||||
break;
|
case "version":
|
||||||
|
echo json_encode(array(
|
||||||
|
'version' => $GLOBALS['MAILCOW_GIT_VERSION']
|
||||||
|
));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -494,6 +494,7 @@
|
|||||||
"containers_info": "Container-Information",
|
"containers_info": "Container-Information",
|
||||||
"container_running": "Läuft",
|
"container_running": "Läuft",
|
||||||
"container_stopped": "Angehalten",
|
"container_stopped": "Angehalten",
|
||||||
|
"current_time": "Systemzeit",
|
||||||
"disk_usage": "Festplattennutzung",
|
"disk_usage": "Festplattennutzung",
|
||||||
"docs": "Dokumente",
|
"docs": "Dokumente",
|
||||||
"external_logs": "Externe Logs",
|
"external_logs": "Externe Logs",
|
||||||
@ -504,6 +505,7 @@
|
|||||||
"log_info": "<p>mailcow <b>in-memory Logs</b> werden in Redis Listen gespeichert, die maximale Anzahl der Einträge pro Anwendung richtet sich nach LOG_LINES (%d).\r\n <br>In-memory Logs sind vergänglich und nicht zur ständigen Aufbewahrung bestimmt. Alle Anwendungen, die in-memory protokollieren, schreiben ebenso in den Docker Daemon.\r\n <br>Das in-memory Protokoll versteht sich als schnelle Übersicht zum Debugging eines Containers, für komplexere Protokolle sollte der Docker Daemon konsultiert werden.</p>\r\n <p><b>Externe Logs</b> werden via API externer Applikationen bezogen.</p>\r\n <p><b>Statische Logs</b> sind weitestgehend Aktivitätsprotokolle, die nicht in den Docker Daemon geschrieben werden, jedoch permanent verfügbar sein müssen (ausgeschlossen API Logs).</p>",
|
"log_info": "<p>mailcow <b>in-memory Logs</b> werden in Redis Listen gespeichert, die maximale Anzahl der Einträge pro Anwendung richtet sich nach LOG_LINES (%d).\r\n <br>In-memory Logs sind vergänglich und nicht zur ständigen Aufbewahrung bestimmt. Alle Anwendungen, die in-memory protokollieren, schreiben ebenso in den Docker Daemon.\r\n <br>Das in-memory Protokoll versteht sich als schnelle Übersicht zum Debugging eines Containers, für komplexere Protokolle sollte der Docker Daemon konsultiert werden.</p>\r\n <p><b>Externe Logs</b> werden via API externer Applikationen bezogen.</p>\r\n <p><b>Statische Logs</b> sind weitestgehend Aktivitätsprotokolle, die nicht in den Docker Daemon geschrieben werden, jedoch permanent verfügbar sein müssen (ausgeschlossen API Logs).</p>",
|
||||||
"login_time": "Zeit",
|
"login_time": "Zeit",
|
||||||
"logs": "Protokolle",
|
"logs": "Protokolle",
|
||||||
|
"memory": "Arbeitsspeicher",
|
||||||
"online_users": "Benutzer online",
|
"online_users": "Benutzer online",
|
||||||
"restart_container": "Neustart",
|
"restart_container": "Neustart",
|
||||||
"service": "Dienst",
|
"service": "Dienst",
|
||||||
@ -515,7 +517,10 @@
|
|||||||
"static_logs": "Statische Logs",
|
"static_logs": "Statische Logs",
|
||||||
"success": "Erfolg",
|
"success": "Erfolg",
|
||||||
"system_containers": "System & Container",
|
"system_containers": "System & Container",
|
||||||
|
"timezone": "Zeitzone",
|
||||||
"uptime": "Uptime",
|
"uptime": "Uptime",
|
||||||
|
"update_available": "Es ist ein Update verfügbar",
|
||||||
|
"no_update_available": "Das System ist auf aktuellem Stand",
|
||||||
"username": "Benutzername"
|
"username": "Benutzername"
|
||||||
},
|
},
|
||||||
"diagnostics": {
|
"diagnostics": {
|
||||||
|
@ -494,6 +494,7 @@
|
|||||||
"containers_info": "Container information",
|
"containers_info": "Container information",
|
||||||
"container_running": "Running",
|
"container_running": "Running",
|
||||||
"container_stopped": "Stopped",
|
"container_stopped": "Stopped",
|
||||||
|
"current_time": "System Time",
|
||||||
"disk_usage": "Disk usage",
|
"disk_usage": "Disk usage",
|
||||||
"docs": "Docs",
|
"docs": "Docs",
|
||||||
"external_logs": "External logs",
|
"external_logs": "External logs",
|
||||||
@ -504,6 +505,7 @@
|
|||||||
"log_info": "<p>mailcow <b>in-memory logs</b> are collected in Redis lists and trimmed to LOG_LINES (%d) every minute to reduce hammering.\r\n <br>In-memory logs are not meant to be persistent. All applications that log in-memory, also log to the Docker daemon and therefore to the default logging driver.\r\n <br>The in-memory log type should be used for debugging minor issues with containers.</p>\r\n <p><b>External logs</b> are collected via API of the given application.</p>\r\n <p><b>Static logs</b> are mostly activity logs, that are not logged to the Dockerd but still need to be persistent (except for API logs).</p>",
|
"log_info": "<p>mailcow <b>in-memory logs</b> are collected in Redis lists and trimmed to LOG_LINES (%d) every minute to reduce hammering.\r\n <br>In-memory logs are not meant to be persistent. All applications that log in-memory, also log to the Docker daemon and therefore to the default logging driver.\r\n <br>The in-memory log type should be used for debugging minor issues with containers.</p>\r\n <p><b>External logs</b> are collected via API of the given application.</p>\r\n <p><b>Static logs</b> are mostly activity logs, that are not logged to the Dockerd but still need to be persistent (except for API logs).</p>",
|
||||||
"login_time": "Time",
|
"login_time": "Time",
|
||||||
"logs": "Logs",
|
"logs": "Logs",
|
||||||
|
"memory": "Memory",
|
||||||
"online_users": "Users online",
|
"online_users": "Users online",
|
||||||
"restart_container": "Restart",
|
"restart_container": "Restart",
|
||||||
"service": "Service",
|
"service": "Service",
|
||||||
@ -515,7 +517,10 @@
|
|||||||
"static_logs": "Static logs",
|
"static_logs": "Static logs",
|
||||||
"success": "Success",
|
"success": "Success",
|
||||||
"system_containers": "System & Containers",
|
"system_containers": "System & Containers",
|
||||||
|
"timezone": "Timezone",
|
||||||
"uptime": "Uptime",
|
"uptime": "Uptime",
|
||||||
|
"update_available": "There is an update available",
|
||||||
|
"no_update_available": "The System is on the latest version",
|
||||||
"username": "Username"
|
"username": "Username"
|
||||||
},
|
},
|
||||||
"diagnostics": {
|
"diagnostics": {
|
||||||
|
@ -89,39 +89,6 @@
|
|||||||
<br>
|
<br>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<legend style="cursor:pointer;margin-top:40px" data-bs-target="#license" unselectable="on" data-bs-toggle="collapse">
|
|
||||||
<i style="font-size:10pt;" class="bi bi-plus-square"></i> {{ lang.admin.guid_and_license }}
|
|
||||||
</legend>
|
|
||||||
<hr />
|
|
||||||
<div id="license" class="collapse">
|
|
||||||
<form class="form-horizontal" autocapitalize="none" autocorrect="off" role="form" method="post">
|
|
||||||
<div class="row">
|
|
||||||
<label class="control-label col-sm-3" for="guid">{{ lang.admin.guid }}:</label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<div class="input-group">
|
|
||||||
<span class="input-group-text">
|
|
||||||
<i class="bi bi-suit-heart{% if gal.valid == true %}-fill text-danger{% endif %}"></i>
|
|
||||||
</span>
|
|
||||||
<input type="text" id="guid" class="form-control" value="{{ license_guid }}" readonly>
|
|
||||||
</div>
|
|
||||||
<p class="text-muted">
|
|
||||||
{{ lang.admin.customer_id }}: {{ gal.c|default('?')|raw }} -
|
|
||||||
{{ lang.admin.service_id }}: {{ gal.s|default('?')|raw }} -
|
|
||||||
{{ lang.admin.sal_level }}: {{ gal.m|default('?')|raw }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="offset-sm-3 col-sm-9">
|
|
||||||
<p class="text-muted">{{ lang.admin.license_info|raw }}</p>
|
|
||||||
<div class="btn-group">
|
|
||||||
<button class="btn btn-sm d-block d-sm-inline btn-success" name="license_validate_now" type="submit" href="#">{{ lang.admin.validate_license_now }}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<legend style="cursor:pointer;margin-top:20px" data-bs-target="#admin_api" unselectable="on" data-bs-toggle="collapse">
|
<legend style="cursor:pointer;margin-top:20px" data-bs-target="#admin_api" unselectable="on" data-bs-toggle="collapse">
|
||||||
<i style="font-size:10pt;" class="bi bi-plus-square"></i> API
|
<i style="font-size:10pt;" class="bi bi-plus-square"></i> API
|
||||||
</legend>
|
</legend>
|
||||||
|
@ -28,26 +28,125 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<div class="tab-content" style="padding-top:20px">
|
<div class="tab-content" style="padding-top:20px">
|
||||||
<div class="debug-log-info">{{ lang.debug.log_info|format(log_lines+1)|raw }}</div>
|
|
||||||
<div role="tabpanel" class="tab-pane active" id="tab-containers">
|
<div role="tabpanel" class="tab-pane active" id="tab-containers">
|
||||||
|
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h3 class="card-title">{{ lang.debug.disk_usage }}</h3>
|
<h3 class="card-title">mailcow</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-3">
|
<div class="col-sm-12 col-md-4 d-flex flex-column">
|
||||||
<p><i class="bi bi-hdd-fill"></i> {{ vmail_df[0] }}</p>
|
<img class="img-responsive" alt="mailcow-logo" src="{{ logo|default('/img/cow_mailcow.svg') }}" style="max-height: 200px;">
|
||||||
<p>{{ vmail_df[2] }} / {{ vmail_df[1] }} ({{ vmail_df[4] }})</p>
|
<div style="margin-top: 60px;">
|
||||||
|
<span class="d-block"><i class="bi bi-hdd-fill"></i> {{ vmail_df[0] }}</span>
|
||||||
|
<span class="d-block">{{ vmail_df[2] }} / {{ vmail_df[1] }} ({{ vmail_df[4] }})</span>
|
||||||
|
</div>
|
||||||
|
<div class="mt-2 mb-4">
|
||||||
|
<div class="progress">
|
||||||
|
<div class="progress-bar bg-info" role="progressbar" style="width:{{ vmail_df[4] }}"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-12 col-md-8">
|
||||||
<div class="progress">
|
<div class="table-responsive" style="margin-top: 10px;">
|
||||||
<div class="progress-bar bg-info" role="progressbar" style="width:{{ vmail_df[4] }}"></div>
|
<table class="table table-striped table-condensed">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Hostname</td>
|
||||||
|
<td><div>
|
||||||
|
<p><b>{{ hostname }}</b></p>
|
||||||
|
</div></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Version</td>
|
||||||
|
<td><div>
|
||||||
|
<p><b>{{ mailcow_info.version_tag }}</b></p>
|
||||||
|
<p id="mailcow_update"></p>
|
||||||
|
</div></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Changelog</td>
|
||||||
|
<td><a href="{{ mailcow_info.project_url }}/releases/tag/{{ mailcow_info.version_tag }}" target="_blank">
|
||||||
|
{{ mailcow_info.project_url }}/releases/tag/{{ mailcow_info.version_tag }}
|
||||||
|
</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ lang.debug.current_time }}</td>
|
||||||
|
<td id="host_date">-</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ lang.debug.timezone }}</td>
|
||||||
|
<td>{{ timezone }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ lang.debug.uptime }}</td>
|
||||||
|
<td id="host_uptime">-</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>CPU</td>
|
||||||
|
<td>
|
||||||
|
Cores <span id="host_cpu_cores">-</span><br />
|
||||||
|
Usage <span id="host_cpu_usage"></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ lang.debug.memory }}</td>
|
||||||
|
<td>
|
||||||
|
Total <span id="host_memory_total">-</span><br />
|
||||||
|
Usage <span id="host_memory_usage"></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<canvas id="net_io_chart" width="400" height="200"></canvas>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<canvas id="disk_io_chart" width="400" height="200"></canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<legend class="mt-4">
|
||||||
|
{{ lang.admin.guid_and_license }}
|
||||||
|
</legend>
|
||||||
|
<hr />
|
||||||
|
<div id="license">
|
||||||
|
<form class="form-horizontal" autocapitalize="none" autocorrect="off" role="form" method="post">
|
||||||
|
<div class="row">
|
||||||
|
<label class="control-label col-sm-3" for="guid">{{ lang.admin.guid }}:</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<div class="input-group">
|
||||||
|
<span class="input-group-text">
|
||||||
|
<i class="bi bi-suit-heart{% if gal.valid == true %}-fill text-danger{% endif %}"></i>
|
||||||
|
</span>
|
||||||
|
<input type="text" id="guid" class="form-control" value="{{ license_guid }}" readonly>
|
||||||
|
</div>
|
||||||
|
<p class="text-muted">
|
||||||
|
{{ lang.admin.customer_id }}: {{ gal.c|default('?')|raw }} -
|
||||||
|
{{ lang.admin.service_id }}: {{ gal.s|default('?')|raw }} -
|
||||||
|
{{ lang.admin.sal_level }}: {{ gal.m|default('?')|raw }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="offset-sm-3 col-sm-9">
|
||||||
|
<p class="text-muted">{{ lang.admin.license_info|raw }}</p>
|
||||||
|
<div class="btn-group">
|
||||||
|
<button class="btn btn-sm d-block d-sm-inline btn-success" name="license_validate_now" type="submit" href="#">{{ lang.admin.validate_license_now }}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h3 class="card-title">{{ lang.debug.solr_status }}</h3>
|
<h3 class="card-title">{{ lang.debug.solr_status }}</h3>
|
||||||
@ -124,6 +223,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div role="tabpanel" class="tab-pane" id="tab-postfix-logs">
|
<div role="tabpanel" class="tab-pane" id="tab-postfix-logs">
|
||||||
|
<div class="debug-log-info">{{ lang.debug.log_info|format(log_lines+1)|raw }}</div>
|
||||||
<div class="card panel-xs-lg">
|
<div class="card panel-xs-lg">
|
||||||
<div class="card-header d-flex">Postfix
|
<div class="card-header d-flex">Postfix
|
||||||
<div class="btn-group ms-auto">
|
<div class="btn-group ms-auto">
|
||||||
@ -139,6 +239,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div role="tabpanel" class="tab-pane" id="tab-ui">
|
<div role="tabpanel" class="tab-pane" id="tab-ui">
|
||||||
|
<div class="debug-log-info">{{ lang.debug.log_info|format(log_lines+1)|raw }}</div>
|
||||||
<div class="card panel-xs-lg">
|
<div class="card panel-xs-lg">
|
||||||
<div class="card-header d-flex"> Mailcow UI
|
<div class="card-header d-flex"> Mailcow UI
|
||||||
<div class="btn-group ms-auto">
|
<div class="btn-group ms-auto">
|
||||||
@ -154,6 +255,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div role="tabpanel" class="tab-pane" id="tab-sasl">
|
<div role="tabpanel" class="tab-pane" id="tab-sasl">
|
||||||
|
<div class="debug-log-info">{{ lang.debug.log_info|format(log_lines+1)|raw }}</div>
|
||||||
<div class="card panel-xs-lg">
|
<div class="card panel-xs-lg">
|
||||||
<div class="card-header d-flex">SASL
|
<div class="card-header d-flex">SASL
|
||||||
<div class="btn-group ms-auto">
|
<div class="btn-group ms-auto">
|
||||||
@ -169,6 +271,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div role="tabpanel" class="tab-pane" id="tab-dovecot-logs">
|
<div role="tabpanel" class="tab-pane" id="tab-dovecot-logs">
|
||||||
|
<div class="debug-log-info">{{ lang.debug.log_info|format(log_lines+1)|raw }}</div>
|
||||||
<div class="card panel-xs-lg">
|
<div class="card panel-xs-lg">
|
||||||
<div class="card-header d-flex">Dovecot
|
<div class="card-header d-flex">Dovecot
|
||||||
<div class="btn-group ms-auto">
|
<div class="btn-group ms-auto">
|
||||||
@ -184,6 +287,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div role="tabpanel" class="tab-pane" id="tab-sogo-logs">
|
<div role="tabpanel" class="tab-pane" id="tab-sogo-logs">
|
||||||
|
<div class="debug-log-info">{{ lang.debug.log_info|format(log_lines+1)|raw }}</div>
|
||||||
<div class="card panel-xs-lg">
|
<div class="card panel-xs-lg">
|
||||||
<div class="card-header d-flex">SOGo
|
<div class="card-header d-flex">SOGo
|
||||||
<div class="btn-group ms-auto">
|
<div class="btn-group ms-auto">
|
||||||
@ -199,6 +303,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div role="tabpanel" class="tab-pane" id="tab-netfilter-logs">
|
<div role="tabpanel" class="tab-pane" id="tab-netfilter-logs">
|
||||||
|
<div class="debug-log-info">{{ lang.debug.log_info|format(log_lines+1)|raw }}</div>
|
||||||
<div class="card panel-xs-lg">
|
<div class="card panel-xs-lg">
|
||||||
<div class="card-header d-flex">Netfilter
|
<div class="card-header d-flex">Netfilter
|
||||||
<div class="btn-group ms-auto">
|
<div class="btn-group ms-auto">
|
||||||
@ -214,6 +319,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div role="tabpanel" class="tab-pane" id="tab-rspamd-history">
|
<div role="tabpanel" class="tab-pane" id="tab-rspamd-history">
|
||||||
|
<div class="debug-log-info">{{ lang.debug.log_info|format(log_lines+1)|raw }}</div>
|
||||||
<div class="card panel-xs-lg">
|
<div class="card panel-xs-lg">
|
||||||
<div class="card-header d-flex">Rspamd history
|
<div class="card-header d-flex">Rspamd history
|
||||||
<div class="btn-group ms-auto">
|
<div class="btn-group ms-auto">
|
||||||
@ -234,6 +340,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div role="tabpanel" class="tab-pane" id="tab-autodiscover-logs">
|
<div role="tabpanel" class="tab-pane" id="tab-autodiscover-logs">
|
||||||
|
<div class="debug-log-info">{{ lang.debug.log_info|format(log_lines+1)|raw }}</div>
|
||||||
<div class="card panel-xs-lg">
|
<div class="card panel-xs-lg">
|
||||||
<div class="card-header d-flex">Autodiscover
|
<div class="card-header d-flex">Autodiscover
|
||||||
<div class="btn-group ms-auto">
|
<div class="btn-group ms-auto">
|
||||||
@ -249,6 +356,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div role="tabpanel" class="tab-pane" id="tab-watchdog-logs">
|
<div role="tabpanel" class="tab-pane" id="tab-watchdog-logs">
|
||||||
|
<div class="debug-log-info">{{ lang.debug.log_info|format(log_lines+1)|raw }}</div>
|
||||||
<div class="card panel-xs-lg">
|
<div class="card panel-xs-lg">
|
||||||
<div class="card-header d-flex">Watchdog
|
<div class="card-header d-flex">Watchdog
|
||||||
<div class="btn-group ms-auto">
|
<div class="btn-group ms-auto">
|
||||||
@ -264,6 +372,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div role="tabpanel" class="tab-pane" id="tab-acme-logs">
|
<div role="tabpanel" class="tab-pane" id="tab-acme-logs">
|
||||||
|
<div class="debug-log-info">{{ lang.debug.log_info|format(log_lines+1)|raw }}</div>
|
||||||
<div class="card panel-xs-lg">
|
<div class="card panel-xs-lg">
|
||||||
<div class="card-header d-flex">ACME
|
<div class="card-header d-flex">ACME
|
||||||
<div class="btn-group ms-auto">
|
<div class="btn-group ms-auto">
|
||||||
@ -279,6 +388,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div role="tabpanel" class="tab-pane" id="tab-api-logs">
|
<div role="tabpanel" class="tab-pane" id="tab-api-logs">
|
||||||
|
<div class="debug-log-info">{{ lang.debug.log_info|format(log_lines+1)|raw }}</div>
|
||||||
<div class="card panel-xs-lg">
|
<div class="card panel-xs-lg">
|
||||||
<div class="card-header d-flex">API
|
<div class="card-header d-flex">API
|
||||||
<div class="btn-group ms-auto">
|
<div class="btn-group ms-auto">
|
||||||
@ -294,6 +404,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div role="tabpanel" class="tab-pane" id="tab-api-rl">
|
<div role="tabpanel" class="tab-pane" id="tab-api-rl">
|
||||||
|
<div class="debug-log-info">{{ lang.debug.log_info|format(log_lines+1)|raw }}</div>
|
||||||
<div class="card panel-xs-lg">
|
<div class="card panel-xs-lg">
|
||||||
<div class="card-header d-flex">Ratelimits
|
<div class="card-header d-flex">Ratelimits
|
||||||
<div class="btn-group ms-auto">
|
<div class="btn-group ms-auto">
|
||||||
@ -315,6 +426,7 @@
|
|||||||
|
|
||||||
<script type='text/javascript'>
|
<script type='text/javascript'>
|
||||||
var lang = {{ lang_admin|raw }};
|
var lang = {{ lang_admin|raw }};
|
||||||
|
var lang_debug = {{ lang_debug|raw }};
|
||||||
var lang_datatables = {{ lang_datatables|raw }};
|
var lang_datatables = {{ lang_datatables|raw }};
|
||||||
var csrf_token = '{{ csrf_token }}';
|
var csrf_token = '{{ csrf_token }}';
|
||||||
var log_pagination_size = '{{ log_pagination_size }}';
|
var log_pagination_size = '{{ log_pagination_size }}';
|
||||||
|
@ -509,7 +509,7 @@ services:
|
|||||||
- watchdog
|
- watchdog
|
||||||
|
|
||||||
dockerapi-mailcow:
|
dockerapi-mailcow:
|
||||||
image: mailcow/dockerapi:1.42
|
image: mailcow/dockerapi:1.43
|
||||||
security_opt:
|
security_opt:
|
||||||
- label=disable
|
- label=disable
|
||||||
restart: always
|
restart: always
|
||||||
|
Loading…
Reference in New Issue
Block a user