mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2025-11-06 21:35:55 +01:00
add additional info to docker page
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
import os.path
|
import os.path
|
||||||
import sys
|
import sys
|
||||||
import django
|
import django
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from plogical.DockerSites import Docker_Sites
|
from plogical.DockerSites import Docker_Sites
|
||||||
|
|
||||||
@@ -1116,27 +1117,67 @@ class ContainerManager(multi.Thread):
|
|||||||
if admin.acl.adminStatus != 1:
|
if admin.acl.adminStatus != 1:
|
||||||
return ACLManager.loadError()
|
return ACLManager.loadError()
|
||||||
|
|
||||||
|
|
||||||
name = data['name']
|
name = data['name']
|
||||||
containerID = data['id']
|
containerID = data['id']
|
||||||
|
|
||||||
passdata = {}
|
# Create a Docker client
|
||||||
passdata["JobID"] = None
|
client = docker.from_env()
|
||||||
passdata['name'] = name
|
container = client.containers.get(containerID)
|
||||||
passdata['containerID'] = containerID
|
|
||||||
da = Docker_Sites(None, passdata)
|
|
||||||
retdata = da.ContainerInfo()
|
|
||||||
|
|
||||||
|
# Get detailed container info
|
||||||
|
container_info = container.attrs
|
||||||
|
|
||||||
data_ret = {'status': 1, 'error_message': 'None', 'data':retdata}
|
# Calculate uptime
|
||||||
|
started_at = container_info.get('State', {}).get('StartedAt', '')
|
||||||
|
if started_at:
|
||||||
|
started_time = datetime.strptime(started_at.split('.')[0], '%Y-%m-%dT%H:%M:%S')
|
||||||
|
uptime = datetime.now() - started_time
|
||||||
|
uptime_str = str(uptime).split('.')[0] # Format as HH:MM:SS
|
||||||
|
else:
|
||||||
|
uptime_str = "N/A"
|
||||||
|
|
||||||
|
# Get container details
|
||||||
|
details = {
|
||||||
|
'id': container.short_id,
|
||||||
|
'name': container.name,
|
||||||
|
'status': container.status,
|
||||||
|
'created': container_info.get('Created', ''),
|
||||||
|
'started_at': started_at,
|
||||||
|
'uptime': uptime_str,
|
||||||
|
'ports': container_info.get('NetworkSettings', {}).get('Ports', {}),
|
||||||
|
'volumes': container_info.get('Mounts', []),
|
||||||
|
'environment': self._mask_sensitive_env(container_info.get('Config', {}).get('Env', [])),
|
||||||
|
'memory_usage': container.stats(stream=False)['memory_stats'].get('usage', 0),
|
||||||
|
'cpu_usage': container.stats(stream=False)['cpu_stats']['cpu_usage'].get('total_usage', 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
data_ret = {'status': 1, 'error_message': 'None', 'data': [1, details]}
|
||||||
json_data = json.dumps(data_ret)
|
json_data = json.dumps(data_ret)
|
||||||
return HttpResponse(json_data)
|
return HttpResponse(json_data)
|
||||||
|
|
||||||
except BaseException as msg:
|
except BaseException as msg:
|
||||||
data_ret = {'removeImageStatus': 0, 'error_message': str(msg)}
|
data_ret = {'status': 0, 'error_message': str(msg)}
|
||||||
json_data = json.dumps(data_ret)
|
json_data = json.dumps(data_ret)
|
||||||
return HttpResponse(json_data)
|
return HttpResponse(json_data)
|
||||||
|
|
||||||
|
def _mask_sensitive_env(self, env_vars):
|
||||||
|
"""Helper method to mask sensitive data in environment variables"""
|
||||||
|
masked_vars = []
|
||||||
|
sensitive_keywords = ['password', 'secret', 'key', 'token', 'auth']
|
||||||
|
|
||||||
|
for var in env_vars:
|
||||||
|
if '=' in var:
|
||||||
|
name, value = var.split('=', 1)
|
||||||
|
# Check if this is a sensitive variable
|
||||||
|
if any(keyword in name.lower() for keyword in sensitive_keywords):
|
||||||
|
masked_vars.append(f"{name}=********")
|
||||||
|
else:
|
||||||
|
masked_vars.append(var)
|
||||||
|
else:
|
||||||
|
masked_vars.append(var)
|
||||||
|
|
||||||
|
return masked_vars
|
||||||
|
|
||||||
def getContainerApplog(self, userID=None, data=None):
|
def getContainerApplog(self, userID=None, data=None):
|
||||||
try:
|
try:
|
||||||
admin = Administrator.objects.get(pk=userID)
|
admin = Administrator.objects.get(pk=userID)
|
||||||
|
|||||||
@@ -7,6 +7,139 @@
|
|||||||
{% get_current_language as LANGUAGE_CODE %}
|
{% get_current_language as LANGUAGE_CODE %}
|
||||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.info-box {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 15px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
box-shadow: 0 1px 3px rgba(0,0,0,0.12);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-box:hover {
|
||||||
|
box-shadow: 0 3px 6px rgba(0,0,0,0.16);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-box h4 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 600;
|
||||||
|
border-bottom: 2px solid #f5f5f5;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
height: 20px;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
background-color: #2196F3;
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 20px;
|
||||||
|
transition: width 0.6s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar.high {
|
||||||
|
background-color: #f44336;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-success {
|
||||||
|
background-color: #4CAF50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-danger {
|
||||||
|
background-color: #F44336;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-warning {
|
||||||
|
background-color: #FF9800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-striped > tbody > tr:nth-of-type(odd) {
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table {
|
||||||
|
margin-bottom: 0;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table > thead > tr > th {
|
||||||
|
border-bottom: 2px solid #ddd;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table > tbody > tr > td {
|
||||||
|
vertical-align: middle;
|
||||||
|
padding: 12px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-responsive {
|
||||||
|
border: none;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Resource usage colors */
|
||||||
|
.progress-bar[aria-valuenow^="8"],
|
||||||
|
.progress-bar[aria-valuenow^="9"] {
|
||||||
|
background-color: #f44336;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar[aria-valuenow^="7"] {
|
||||||
|
background-color: #ff9800;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Container status colors */
|
||||||
|
.status-indicator {
|
||||||
|
display: inline-block;
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-running {
|
||||||
|
background-color: #4CAF50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-stopped {
|
||||||
|
background-color: #F44336;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tooltips */
|
||||||
|
[data-toggle="tooltip"] {
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive adjustments */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.info-box {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-responsive {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
$('[data-toggle="tooltip"]').tooltip();
|
$('[data-toggle="tooltip"]').tooltip();
|
||||||
@@ -216,88 +349,113 @@
|
|||||||
<div class="example-box-wrapper">
|
<div class="example-box-wrapper">
|
||||||
|
|
||||||
<div class="panel panel-body">
|
<div class="panel panel-body">
|
||||||
|
|
||||||
<h3 class="content-box-header">
|
<h3 class="content-box-header">
|
||||||
{% trans "Container Information" %}
|
{% trans "Container Information" %}
|
||||||
<img id="infoLoading" src="/static/images/loading.gif" style="display: none;">
|
<img id="infoLoading" src="/static/images/loading.gif" style="display: none;">
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
|
|
||||||
<div class="content-box-wrapper">
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="panel">
|
<div class="info-box">
|
||||||
<div class="panel-body">
|
<h4>Basic Information</h4>
|
||||||
<div class="example-box-wrapper">
|
<p><strong>Container ID:</strong> {$ cid $}</p>
|
||||||
<h3 class="title-hero">
|
<p><strong>Status:</strong> <span class="label" ng-class="{'label-success': status === 'running', 'label-danger': status === 'stopped', 'label-warning': status !== 'running' && status !== 'stopped'}">{$ status $}</span></p>
|
||||||
{% trans "Memory Usage" %}
|
<p><strong>Created:</strong> {$ created | date:'medium' $}</p>
|
||||||
</h3>
|
<p><strong>Uptime:</strong> {$ uptime $}</p>
|
||||||
<div class="progressbar" data-value="{$ appmemoryUsage $}">
|
|
||||||
<div class="progressbar-value bg-primary">
|
|
||||||
<div class="progress-overlay"></div>
|
|
||||||
<div class="progress-label" title="{$ appmemoryUsage $}%">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="info-box">
|
||||||
<h3 class="title-hero">
|
<h4>Resource Usage</h4>
|
||||||
{% trans "CPU Usage" %}
|
<div class="progress">
|
||||||
</h3>
|
<div class="progress-bar" role="progressbar" ng-style="{'width': memoryUsagePercent + '%'}" aria-valuenow="{$ memoryUsagePercent $}" aria-valuemin="0" aria-valuemax="100">
|
||||||
<div class="progressbar" data-value="{$ appcpuUsage $}">
|
Memory: {$ memoryUsagePercent | number:1 $}%
|
||||||
<div class="progressbar-value bg-primary">
|
|
||||||
<div class="progress-overlay"></div>
|
|
||||||
<div class="progress-label" title="{$ appcpuUsage $}"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="progress">
|
||||||
|
<div class="progress-bar" role="progressbar" ng-style="{'width': cpuUsagePercent + '%'}" aria-valuenow="{$ cpuUsagePercent $}" aria-valuemin="0" aria-valuemax="100">
|
||||||
|
CPU: {$ cpuUsagePercent | number:1 $}%
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="panel">
|
<div class="info-box">
|
||||||
<div class="panel-body">
|
<h4>Network & Ports</h4>
|
||||||
<div class="example-box-wrapper">
|
<div ng-if="ports && (ports | objLength) > 0">
|
||||||
<h3 class="title-hero" >Main Actions
|
<table class="table table-striped">
|
||||||
<img id="actionLoading" src="/static/images/loading.gif"
|
<thead>
|
||||||
style="display: none;width: 20px;">
|
<tr>
|
||||||
</h3>
|
<th>Container Port</th>
|
||||||
<b>Status:</b> <span ng-bind="status"></span>
|
<th>Host Binding</th>
|
||||||
<span ng-click='refreshStatus()' style="cursor:pointer;"
|
</tr>
|
||||||
class="pull-right"
|
</thead>
|
||||||
title="Refresh status"><i
|
<tbody>
|
||||||
class="fa fa-refresh btn-icon"></i></span>
|
<tr ng-repeat="(containerPort, hostBindings) in ports">
|
||||||
<hr>
|
<td>{$ containerPort $}</td>
|
||||||
<button ng-disabled="status=='running'" class="btn btn-primary"
|
<td>
|
||||||
ng-click="cAction('start')"><i
|
<span ng-repeat="binding in hostBindings">
|
||||||
class="fa fa-play btn-icon"></i> Start
|
{$ binding.HostIp || '0.0.0.0' $}:{$ binding.HostPort $}
|
||||||
</button>
|
</span>
|
||||||
<button ng-disabled="status!='running'" class="btn btn-primary"
|
</td>
|
||||||
ng-click="restarthStatus()"><i
|
</tr>
|
||||||
class="fa fa-refresh btn-icon"></i>
|
</tbody>
|
||||||
Restart
|
</table>
|
||||||
</button>
|
|
||||||
<button ng-disabled="status!='running'" class="btn btn-primary"
|
|
||||||
ng-click="StopContainerAPP()"><i
|
|
||||||
class="fa fa-stop btn-icon"></i> Stop
|
|
||||||
</button>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div ng-if="!ports || (ports | objLength) === 0">
|
||||||
|
<p>No ports exposed</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="info-box">
|
||||||
|
<h4>Volumes</h4>
|
||||||
|
<div ng-if="volumes && volumes.length > 0">
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Source</th>
|
||||||
|
<th>Destination</th>
|
||||||
|
<th>Mode</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="volume in volumes">
|
||||||
|
<td>{$ volume.Source $}</td>
|
||||||
|
<td>{$ volume.Destination $}</td>
|
||||||
|
<td>{$ volume.Mode $}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div ng-if="!volumes || volumes.length === 0">
|
||||||
|
<p>No volumes mounted</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="info-box">
|
||||||
|
<h4>Environment Variables</h4>
|
||||||
|
<div ng-if="environment && environment.length > 0">
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Variable</th>
|
||||||
|
<th>Value</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="env in environment">
|
||||||
|
<td>{$ env.split('=')[0] $}</td>
|
||||||
|
<td>{$ env.split('=')[1] $}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div ng-if="!environment || environment.length === 0">
|
||||||
|
<p>No environment variables set</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="Recreatedockerapp" class="modal fade" role="dialog">
|
<div id="Recreatedockerapp" class="modal fade" role="dialog">
|
||||||
|
|||||||
Reference in New Issue
Block a user