mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2026-05-06 20:47:35 +02:00
ServerStatus
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
import os.path
|
||||
import sys
|
||||
import django
|
||||
|
||||
sys.path.append('/usr/local/CyberCP')
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
|
||||
django.setup()
|
||||
@@ -24,10 +25,11 @@ from serverStatus.serverStatusUtil import ServerStatusUtil
|
||||
import threading as multi
|
||||
from plogical.httpProc import httpProc
|
||||
|
||||
|
||||
# Use default socket to connect
|
||||
class ContainerManager(multi.Thread):
|
||||
|
||||
def __init__(self, name=None, function=None, request = None, templateName = None, data = None):
|
||||
def __init__(self, name=None, function=None, request=None, templateName=None, data=None):
|
||||
multi.Thread.__init__(self)
|
||||
self.name = name
|
||||
self.function = function
|
||||
@@ -47,7 +49,7 @@ class ContainerManager(multi.Thread):
|
||||
command = 'sudo systemctl restart gunicorn.socket'
|
||||
ProcessUtilities.executioner(command)
|
||||
except BaseException as msg:
|
||||
logging.CyberCPLogFileWriter.writeToFile( str(msg) + ' [ContainerManager.run]')
|
||||
logging.CyberCPLogFileWriter.writeToFile(str(msg) + ' [ContainerManager.run]')
|
||||
|
||||
@staticmethod
|
||||
def executioner(command, statusFile):
|
||||
@@ -99,8 +101,8 @@ class ContainerManager(multi.Thread):
|
||||
val = request.session['userID']
|
||||
admin = Administrator.objects.get(pk=val)
|
||||
proc = httpProc(request, 'dockerManager/images.html', {"type": admin.type,
|
||||
'image': image,
|
||||
'tag': tag})
|
||||
'image': image,
|
||||
'tag': tag})
|
||||
return proc.render()
|
||||
|
||||
envList = {};
|
||||
@@ -129,6 +131,56 @@ class ContainerManager(multi.Thread):
|
||||
proc = httpProc(request, template, Data, 'admin')
|
||||
return proc.render()
|
||||
|
||||
def createContainerV2(self, request=None, userID=None, data=None):
|
||||
client = docker.from_env()
|
||||
dockerAPI = docker.APIClient()
|
||||
|
||||
adminNames = ACLManager.loadAllUsers(userID)
|
||||
tag = request.GET.get('tag')
|
||||
image = request.GET.get('image')
|
||||
tag = tag.split(" (")[0]
|
||||
|
||||
if "/" in image:
|
||||
name = image.split("/")[0] + "." + image.split("/")[1]
|
||||
else:
|
||||
name = image
|
||||
|
||||
try:
|
||||
inspectImage = dockerAPI.inspect_image(image + ":" + tag)
|
||||
except docker.errors.ImageNotFound:
|
||||
val = request.session['userID']
|
||||
admin = Administrator.objects.get(pk=val)
|
||||
proc = httpProc(request, 'dockerManager/images.html', {"type": admin.type,
|
||||
'image': image,
|
||||
'tag': tag})
|
||||
return proc.render()
|
||||
|
||||
envList = {};
|
||||
if 'Env' in inspectImage['Config']:
|
||||
for item in inspectImage['Config']['Env']:
|
||||
if '=' in item:
|
||||
splitedItem = item.split('=', 1)
|
||||
print(splitedItem)
|
||||
envList[splitedItem[0]] = splitedItem[1]
|
||||
else:
|
||||
envList[item] = ""
|
||||
|
||||
portConfig = {};
|
||||
if 'ExposedPorts' in inspectImage['Config']:
|
||||
for item in inspectImage['Config']['ExposedPorts']:
|
||||
portDef = item.split('/')
|
||||
portConfig[portDef[0]] = portDef[1]
|
||||
|
||||
if image is None or image is '' or tag is None or tag is '':
|
||||
return redirect(loadImages)
|
||||
|
||||
Data = {"ownerList": adminNames, "image": image, "name": name, "tag": tag, "portConfig": portConfig,
|
||||
"envList": envList}
|
||||
|
||||
template = 'dockerManager/runContainerV2.html'
|
||||
proc = httpProc(request, template, Data, 'admin')
|
||||
return proc.render()
|
||||
|
||||
def loadContainerHome(self, request=None, userID=None, data=None):
|
||||
try:
|
||||
name = self.name
|
||||
@@ -192,6 +244,69 @@ class ContainerManager(multi.Thread):
|
||||
except BaseException as msg:
|
||||
return HttpResponse(str(msg))
|
||||
|
||||
def loadContainerHomeV2(self, request=None, userID=None, data=None):
|
||||
try:
|
||||
name = self.name
|
||||
|
||||
if ACLManager.checkContainerOwnership(name, userID) != 1:
|
||||
return ACLManager.loadError()
|
||||
|
||||
client = docker.from_env()
|
||||
dockerAPI = docker.APIClient()
|
||||
|
||||
try:
|
||||
container = client.containers.get(name)
|
||||
except docker.errors.NotFound as err:
|
||||
return HttpResponse("Container not found")
|
||||
|
||||
data = {}
|
||||
con = Containers.objects.get(name=name)
|
||||
data['name'] = name
|
||||
data['image'] = con.image + ":" + con.tag
|
||||
data['ports'] = json.loads(con.ports)
|
||||
data['cid'] = con.cid
|
||||
data['envList'] = json.loads(con.env)
|
||||
data['volList'] = json.loads(con.volumes)
|
||||
|
||||
stats = container.stats(decode=False, stream=False)
|
||||
logs = container.logs(stream=True)
|
||||
|
||||
data['status'] = container.status
|
||||
data['memoryLimit'] = con.memory
|
||||
if con.startOnReboot == 1:
|
||||
data['startOnReboot'] = 'true'
|
||||
data['restartPolicy'] = "Yes"
|
||||
else:
|
||||
data['startOnReboot'] = 'false'
|
||||
data['restartPolicy'] = "No"
|
||||
|
||||
if 'usage' in stats['memory_stats']:
|
||||
# Calculate Usage
|
||||
# Source: https://github.com/docker/docker/blob/28a7577a029780e4533faf3d057ec9f6c7a10948/api/client/stats.go#L309
|
||||
data['memoryUsage'] = (stats['memory_stats']['usage'] / stats['memory_stats']['limit']) * 100
|
||||
|
||||
try:
|
||||
cpu_count = len(stats["cpu_stats"]["cpu_usage"]["percpu_usage"])
|
||||
except:
|
||||
cpu_count = 0
|
||||
|
||||
data['cpuUsage'] = 0.0
|
||||
cpu_delta = float(stats["cpu_stats"]["cpu_usage"]["total_usage"]) - \
|
||||
float(stats["precpu_stats"]["cpu_usage"]["total_usage"])
|
||||
system_delta = float(stats["cpu_stats"]["system_cpu_usage"]) - \
|
||||
float(stats["precpu_stats"]["system_cpu_usage"])
|
||||
if system_delta > 0.0:
|
||||
data['cpuUsage'] = round(cpu_delta / system_delta * 100.0 * cpu_count, 3)
|
||||
else:
|
||||
data['memoryUsage'] = 0
|
||||
data['cpuUsage'] = 0
|
||||
|
||||
template = 'dockerManager/viewContainerV2.html'
|
||||
proc = httpProc(request, template, data, 'admin')
|
||||
return proc.render()
|
||||
except BaseException as msg:
|
||||
return HttpResponse(str(msg))
|
||||
|
||||
def listContainers(self, request=None, userID=None, data=None):
|
||||
client = docker.from_env()
|
||||
dockerAPI = docker.APIClient()
|
||||
@@ -235,6 +350,49 @@ class ContainerManager(multi.Thread):
|
||||
"showUnlistedContainer": showUnlistedContainer}, 'admin')
|
||||
return proc.render()
|
||||
|
||||
def listContainersV2(self, request=None, userID=None, data=None):
|
||||
client = docker.from_env()
|
||||
dockerAPI = docker.APIClient()
|
||||
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
containers = ACLManager.findAllContainers(currentACL, userID)
|
||||
|
||||
allContainers = client.containers.list()
|
||||
containersList = []
|
||||
showUnlistedContainer = True
|
||||
|
||||
# TODO: Add condition to show unlisted Containers only if user has admin level access
|
||||
|
||||
unlistedContainers = []
|
||||
for container in allContainers:
|
||||
if container.name not in containers:
|
||||
unlistedContainers.append(container)
|
||||
|
||||
if not unlistedContainers:
|
||||
showUnlistedContainer = False
|
||||
|
||||
adminNames = ACLManager.loadAllUsers(userID)
|
||||
|
||||
pages = float(len(containers)) / float(10)
|
||||
pagination = []
|
||||
|
||||
if pages <= 1.0:
|
||||
pages = 1
|
||||
pagination.append('<li><a href="\#"></a></li>')
|
||||
else:
|
||||
pages = ceil(pages)
|
||||
finalPages = int(pages) + 1
|
||||
|
||||
for i in range(1, finalPages):
|
||||
pagination.append('<li><a href="\#">' + str(i) + '</a></li>')
|
||||
|
||||
template = 'dockerManager/listContainersV2.html'
|
||||
proc = httpProc(request, template, {"pagination": pagination,
|
||||
"unlistedContainers": unlistedContainers,
|
||||
"adminNames": adminNames,
|
||||
"showUnlistedContainer": showUnlistedContainer}, 'admin')
|
||||
return proc.render()
|
||||
|
||||
def getContainerLogs(self, userID=None, data=None):
|
||||
try:
|
||||
name = data['name']
|
||||
@@ -299,7 +457,7 @@ class ContainerManager(multi.Thread):
|
||||
volumes = {}
|
||||
for index, volume in volList.items():
|
||||
volumes[volume['src']] = {'bind': volume['dest'],
|
||||
'mode': 'rw'}
|
||||
'mode': 'rw'}
|
||||
|
||||
## Create Configurations
|
||||
admin = Administrator.objects.get(userName=dockerOwner)
|
||||
@@ -772,6 +930,54 @@ class ContainerManager(multi.Thread):
|
||||
except BaseException as msg:
|
||||
return HttpResponse(str(msg))
|
||||
|
||||
def imagesV2(self, request=None, userID=None, data=None):
|
||||
try:
|
||||
admin = Administrator.objects.get(pk=userID)
|
||||
|
||||
client = docker.from_env()
|
||||
dockerAPI = docker.APIClient()
|
||||
|
||||
try:
|
||||
imageList = client.images.list()
|
||||
except docker.errors.APIError as err:
|
||||
return HttpResponse(str(err))
|
||||
|
||||
images = {}
|
||||
names = []
|
||||
|
||||
for image in imageList:
|
||||
try:
|
||||
name = image.attrs['RepoTags'][0].split(":")[0]
|
||||
if "/" in name:
|
||||
name2 = ""
|
||||
for item in name.split("/"):
|
||||
name2 += ":" + item
|
||||
else:
|
||||
name2 = name
|
||||
|
||||
tags = []
|
||||
for tag in image.tags:
|
||||
getTag = tag.split(":")
|
||||
if len(getTag) == 2:
|
||||
tags.append(getTag[1])
|
||||
print(tags)
|
||||
if name in names:
|
||||
images[name]['tags'].extend(tags)
|
||||
else:
|
||||
names.append(name)
|
||||
images[name] = {"name": name,
|
||||
"name2": name2,
|
||||
"tags": tags}
|
||||
except:
|
||||
continue
|
||||
|
||||
template = 'dockerManager/imagesV2.html'
|
||||
proc = httpProc(request, template, {"images": images, "test": ''}, 'admin')
|
||||
return proc.render()
|
||||
|
||||
except BaseException as msg:
|
||||
return HttpResponse(str(msg))
|
||||
|
||||
def manageImages(self, request=None, userID=None, data=None):
|
||||
try:
|
||||
|
||||
@@ -802,6 +1008,36 @@ class ContainerManager(multi.Thread):
|
||||
except BaseException as msg:
|
||||
return HttpResponse(str(msg))
|
||||
|
||||
def manageImagesV2(self, request=None, userID=None, data=None):
|
||||
try:
|
||||
|
||||
client = docker.from_env()
|
||||
dockerAPI = docker.APIClient()
|
||||
|
||||
imageList = client.images.list()
|
||||
|
||||
images = {}
|
||||
names = []
|
||||
|
||||
for image in imageList:
|
||||
try:
|
||||
name = image.attrs['RepoTags'][0].split(":")[0]
|
||||
if name in names:
|
||||
images[name]['tags'].extend(image.tags)
|
||||
else:
|
||||
names.append(name)
|
||||
images[name] = {"name": name,
|
||||
"tags": image.tags}
|
||||
except:
|
||||
continue
|
||||
|
||||
template = 'dockerManager/manageImagesV2.html'
|
||||
proc = httpProc(request, template, {"images": images}, 'admin')
|
||||
return proc.render()
|
||||
|
||||
except BaseException as msg:
|
||||
return HttpResponse(str(msg))
|
||||
|
||||
def getImageHistory(self, userID=None, data=None):
|
||||
try:
|
||||
|
||||
@@ -1076,4 +1312,4 @@ class ContainerManager(multi.Thread):
|
||||
except BaseException as msg:
|
||||
data_ret = {'getTagsStatus': 0, 'error_message': str(msg)}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
1055
dockerManager/static/dockerManager/dockerManagerV2.js
Normal file
1055
dockerManager/static/dockerManager/dockerManagerV2.js
Normal file
File diff suppressed because it is too large
Load Diff
67
dockerManager/templates/dockerManager/imagesV2.html
Normal file
67
dockerManager/templates/dockerManager/imagesV2.html
Normal file
@@ -0,0 +1,67 @@
|
||||
{% extends "baseTemplate/newBase.html" %}
|
||||
{% load i18n %}
|
||||
{% block titleNew %}{% trans "Home - CyberPanel" %}{% endblock %}
|
||||
{% block newContent %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
<div ng-controller="manageImagesV2" class="p-8">
|
||||
<div>
|
||||
<div class="flex justify-between items-center">
|
||||
<div>
|
||||
<div class="flex items-center">
|
||||
<p class="text-4xl font-bold">Create new container</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<a class="bg-orange-500 px-3 py-2 rounded-lg text-xl font-semibold text-white"
|
||||
href="{% url "manageImagesV2" %}">{% trans "Manage Images" %}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border px-8 py-2 mt-3">
|
||||
<div class="flex justify-between items-center py-4">
|
||||
<div class="flex items-center">
|
||||
<p class="text-xl font-bold">Locally Available Images</p>
|
||||
<img id="imageLoading" src="/static/images/loading.gif" style="display: none;">
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative py-5 overflow-x-auto">
|
||||
<table id="imageList" class="w-full text-sm text-left rtl:text-right">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
Name (Installed)
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
Tags
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
Action
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="border shadow-lg py-3 px-6 rounded-b-lg">
|
||||
{% for name, image in images.items %}
|
||||
<tr>
|
||||
<td class="px-6 py-4">{{ image.name }}</td>
|
||||
<td class="px-6 py-4">
|
||||
<select class="w-80 bg-gray-100 rounded px-2 py-1" id="{{ forloop.counter }}"
|
||||
ng-model="imageTag['{{ image.name2 }}']">
|
||||
{% for tag in image.tags %}
|
||||
<option>{{ tag }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<a class="bg-orange-500 px-3 py-1 rounded-lg font-semibold text-white cursor-pointer"
|
||||
ng-href="/docker/V2/runContainerV2/?image={{ image.name }}&tag={$ imageTag['{{ image.name2 }}'] $}">{% trans "Create" %}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
219
dockerManager/templates/dockerManager/listContainersV2.html
Normal file
219
dockerManager/templates/dockerManager/listContainersV2.html
Normal file
@@ -0,0 +1,219 @@
|
||||
{% extends "baseTemplate/newBase.html" %}
|
||||
{% load i18n %}
|
||||
{% block titleNew %}{% trans "Home - CyberPanel" %}{% endblock %}
|
||||
{% block newContent %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
<div ng-controller="listContainersV2" class="p-8">
|
||||
<div>
|
||||
<div class="flex justify-between items-center">
|
||||
<div>
|
||||
<div class="flex items-center">
|
||||
<p id="domainNamePage" class="text-4xl font-bold">List Containers</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<a class="bg-orange-500 px-3 py-2 rounded-lg text-xl font-semibold text-white"
|
||||
href="{% url "containerImage" %}">{% trans "Create" %}</a>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-xs text-gray-600 py-2 font-semibold">Manage containers on server</p>
|
||||
</div>
|
||||
<div class="border px-8 py-2">
|
||||
<div class="flex justify-between items-center py-4">
|
||||
<div class="flex items-center">
|
||||
<p class="text-xl font-bold">Containers</p>
|
||||
<img id="imageLoading" src="/static/images/loading.gif" style="display: none;">
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative py-5 overflow-x-auto">
|
||||
<table id="datatable-example" class="w-full text-sm text-left rtl:text-right">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
Name
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
Launch
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
Owner
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
Image
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
Tag
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
Actions
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody ng-repeat="web in ContainerList track by $index"
|
||||
class="border shadow-lg py-3 px-6 rounded-b-lg">
|
||||
<tr>
|
||||
<td ng-bind="web.name" class="px-6 py-4"></td>
|
||||
<td class="px-6 py-4"><a href="/docker/V2/view/{$ web.name $}"><img width="30px" height="30"
|
||||
class=""
|
||||
src="{% static 'baseTemplate/assets/image-resources/webPanel.png' %}"></a>
|
||||
</td>
|
||||
<td ng-bind="web.admin" class="px-6 py-4"></td>
|
||||
<td ng-bind="web.image" class="px-6 py-4"></td>
|
||||
<td ng-bind="web.tag" class="px-6 py-4"></td>
|
||||
<td class="px-6 py-4">
|
||||
<button ng-click="delContainer(web.name)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button onclick="return false;"
|
||||
ng-click="showLog(web.name)"
|
||||
href="#" data-modal-target="showLog" data-modal-toggle="showLog">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M2.25 12.75V12A2.25 2.25 0 0 1 4.5 9.75h15A2.25 2.25 0 0 1 21.75 12v.75m-8.69-6.44-2.12-2.12a1.5 1.5 0 0 0-1.061-.44H4.5A2.25 2.25 0 0 0 2.25 6v12a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18V9a2.25 2.25 0 0 0-2.25-2.25h-5.379a1.5 1.5 0 0 1-1.06-.44Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div id="showLog" tabindex="-1"
|
||||
class="hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full">
|
||||
<div class="relative p-4 w-full max-w-2xl max-h-full">
|
||||
<div class="relative bg-white shadow dark:bg-gray-700">
|
||||
<div class="flex items-center bg-blue-400 px-4 py-4">
|
||||
<p class="font-bold">{% trans "Container logs" %}</p>
|
||||
<button type="button" data-modal-toggle="showLog"
|
||||
class="absolute top-2 end-1 text-black bg-transparent hover:text-black rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center"
|
||||
data-modal-hide="showLog">
|
||||
<svg class="w-3 h-3" aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg" fill="none"
|
||||
viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round"
|
||||
stroke-linejoin="round" stroke-width="2"
|
||||
d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
||||
</svg>
|
||||
<span class="sr-only">Close modal</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="p-4 md:p-5">
|
||||
<textarea name="logs" class="border w-full" id="" cols="30" rows="10">{$ logs $}</textarea>
|
||||
</div>
|
||||
<div class="flex gap-1 justify-end py-2">
|
||||
<button ng-click="showLog('', true)" type="button"
|
||||
class="text-white bg-green-600 hover:bg-green-800 focus:ring-4 focus:outline-none focus:ring-green-300 dark:focus:ring-green-800 font-medium rounded-lg text-sm inline-flex items-center px-5 py-2.5 text-center me-2">{% trans "Refresh" %}</button>
|
||||
<button type="button"
|
||||
class="text-white bg-red-600 hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 dark:focus:ring-red-800 font-medium rounded-lg text-sm inline-flex items-center px-5 py-2.5 text-center me-2"
|
||||
data-modal-toggle="showLog"
|
||||
data-modal-hide="showLog">{% trans "Close" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="listFail" class="flex justify-center bg-red-500 rounded-lg text-white px-2 py-1 font-semibold">
|
||||
<p>{% trans "Error message:" %} {$ errorMessage $}</p>
|
||||
</div>
|
||||
<div>
|
||||
<nav>
|
||||
<ul class="flex justify-end">
|
||||
{% for items in pagination %}
|
||||
<li class="flex justify-end bg-gray-200 w-8 px-3 py-1"
|
||||
ng-click="getFurtherContainersFromDB({{ forloop.counter }})" id="webPages"><a
|
||||
href="">{{ forloop.counter }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
{% if showUnlistedContainer %}
|
||||
<h3 class="title-hero">
|
||||
{% trans "Unlisted Containers" %} <i class="fa fa-question-circle"
|
||||
title="{% trans "Containers listed below were either not created through panel or were not saved to database properly" %}"></i>
|
||||
</h3>
|
||||
<div class="relative py-5 overflow-x-auto">
|
||||
<table id="datatable-example" class="w-full text-sm text-left rtl:text-right">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
Name
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
Status
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
Actions
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="border shadow-lg py-3 px-6 rounded-b-lg">
|
||||
{% for container in unlistedContainers %}
|
||||
<tr>
|
||||
<td class="px-6 py-4">{{ container.name }}</td>
|
||||
<td class="px-6 py-4">{{ container.status }}</td>
|
||||
<td class="px-6 py-4">
|
||||
<button class="btn btn-primary"
|
||||
ng-click="delContainer('{{ container.name }}', true)"><i
|
||||
class="fa fa-trash"></i></button>
|
||||
<button class="btn btn-primary" ng-click="showLog('{{ container.name }}')"><i
|
||||
class="fa fa-file"></i></button>
|
||||
<button class="btn btn-primary" ng-click="assignContainer('{{ container.name }}')">
|
||||
<i
|
||||
class="fa fa-user"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<button onclick="return false;"
|
||||
ng-click="showLog(web.name)"
|
||||
href="#" data-modal-target="showLog" data-modal-toggle="showLog">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M2.25 12.75V12A2.25 2.25 0 0 1 4.5 9.75h15A2.25 2.25 0 0 1 21.75 12v.75m-8.69-6.44-2.12-2.12a1.5 1.5 0 0 0-1.061-.44H4.5A2.25 2.25 0 0 0 2.25 6v12a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18V9a2.25 2.25 0 0 0-2.25-2.25h-5.379a1.5 1.5 0 0 1-1.06-.44Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div id="showLog" tabindex="-1"
|
||||
class="hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full">
|
||||
<div class="relative p-4 w-full max-w-2xl max-h-full">
|
||||
<div class="relative bg-white shadow dark:bg-gray-700">
|
||||
<div class="flex items-center bg-blue-400 px-4 py-4">
|
||||
<p class="font-bold">{% trans "Container logs" %}</p>
|
||||
<button type="button" data-modal-toggle="showLog"
|
||||
class="absolute top-2 end-1 text-black bg-transparent hover:text-black rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center"
|
||||
data-modal-hide="showLog">
|
||||
<svg class="w-3 h-3" aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg" fill="none"
|
||||
viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round"
|
||||
stroke-linejoin="round" stroke-width="2"
|
||||
d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
||||
</svg>
|
||||
<span class="sr-only">Close modal</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="p-4 md:p-5">
|
||||
<textarea name="logs" class="border w-full" id="" cols="30" rows="10">{$ logs $}</textarea>
|
||||
</div>
|
||||
<div class="flex gap-1 justify-end py-2">
|
||||
<button ng-click="showLog('', true)" type="button"
|
||||
class="text-white bg-green-600 hover:bg-green-800 focus:ring-4 focus:outline-none focus:ring-green-300 dark:focus:ring-green-800 font-medium rounded-lg text-sm inline-flex items-center px-5 py-2.5 text-center me-2">{% trans "Refresh" %}</button>
|
||||
<button type="button"
|
||||
class="text-white bg-red-600 hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 dark:focus:ring-red-800 font-medium rounded-lg text-sm inline-flex items-center px-5 py-2.5 text-center me-2"
|
||||
data-modal-toggle="showLog"
|
||||
data-modal-hide="showLog">{% trans "Close" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
209
dockerManager/templates/dockerManager/manageImagesV2.html
Normal file
209
dockerManager/templates/dockerManager/manageImagesV2.html
Normal file
@@ -0,0 +1,209 @@
|
||||
{% extends "baseTemplate/newBase.html" %}
|
||||
{% load i18n %}
|
||||
{% block titleNew %}{% trans "Home - CyberPanel" %}{% endblock %}
|
||||
{% block newContent %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
<div ng-controller="manageImagesV2" class="p-8">
|
||||
<div>
|
||||
<div class="flex justify-between items-center">
|
||||
<div>
|
||||
<div class="flex items-center">
|
||||
<p id="domainNamePage" class="text-4xl font-bold">Manage Images</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<a class="bg-orange-500 px-3 py-2 rounded-lg text-xl font-semibold text-white"
|
||||
href="{% url "containerImageV2" %}">{% trans "Create Container" %}</a>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-xs text-gray-600 py-2 font-semibold">On this page you can manage docker images.</p>
|
||||
</div>
|
||||
<div class="border px-8 py-2">
|
||||
<div class="flex justify-between items-center py-4">
|
||||
<div class="flex items-center">
|
||||
<p class="text-xl font-bold">Images</p>
|
||||
<img id="imageLoading" src="/static/images/loading.gif" style="display: none;">
|
||||
</div>
|
||||
<div>
|
||||
<button ng-click="rmImage(0)" class="bg-orange-500 px-3 py-1 rounded-lg font-semibold text-white">
|
||||
Prune
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex py-2 px-6">
|
||||
<div>
|
||||
<p class="font-semibold w-60">Search Image</p>
|
||||
</div>
|
||||
<div>
|
||||
<input type="text" ng-change="searchImages()" ng-model="searchString"
|
||||
class="w-80 bg-gray-100 rounded px-2 py-1">
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative py-5 overflow-x-auto">
|
||||
<table id="searchResult" class="w-full text-sm text-left rtl:text-right">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
Name (search)
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
Tags
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
Action
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody ng-repeat="image in images track by $index"
|
||||
class="border shadow-lg py-3 px-6 rounded-b-lg">
|
||||
<tr>
|
||||
<td class="flex items-center px-6 py-4">
|
||||
<span ng-bind="image.name"></span>
|
||||
<span class="ml-1" ng-show="image.is_official == true"><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor"
|
||||
class="w-4 h-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/>
|
||||
</svg>
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<select ng-focus="loadTags($event)" ng-click="selectTag()" ng-model="imageTag[image.name2]"
|
||||
ng-options="tag for tag in tagList[image.name2]" ng-attr-id="{$ image.name2 $}"
|
||||
data-pageloaded='0' class="w-80 bg-gray-100 rounded px-2 py-1">
|
||||
</select>
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<a ng-click="pullImage(image.name, imageTag[image.name2])"
|
||||
class="bg-orange-500 px-3 py-1 rounded-lg font-semibold text-white cursor-pointer">{% trans "Pull" %}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="relative py-5 overflow-x-auto">
|
||||
<table id="imageList" class="w-full text-sm text-left rtl:text-right">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
Name (Locally Available)
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
Tags
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
Action
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="border shadow-lg py-3 px-6 rounded-b-lg">
|
||||
{% for name, image in images.items %}
|
||||
<tr>
|
||||
<td class="px-6 py-4">
|
||||
{{ image.name }}
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<select class="w-80 bg-gray-100 rounded px-2 py-1" id="{{ forloop.counter }}">
|
||||
{% for tag in image.tags %}
|
||||
<option>{{ tag }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<button onclick="return false;"
|
||||
ng-click="getHistory({{ forloop.counter }})"
|
||||
class="nav-link point-events"
|
||||
href="#" data-modal-target="history" data-modal-toggle="history">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"
|
||||
class="w-6 h-6">
|
||||
<path fill-rule="evenodd"
|
||||
d="M12 5.25c1.213 0 2.415.046 3.605.135a3.256 3.256 0 0 1 3.01 3.01c.044.583.077 1.17.1 1.759L17.03 8.47a.75.75 0 1 0-1.06 1.06l3 3a.75.75 0 0 0 1.06 0l3-3a.75.75 0 0 0-1.06-1.06l-1.752 1.751c-.023-.65-.06-1.296-.108-1.939a4.756 4.756 0 0 0-4.392-4.392 49.422 49.422 0 0 0-7.436 0A4.756 4.756 0 0 0 3.89 8.282c-.017.224-.033.447-.046.672a.75.75 0 1 0 1.497.092c.013-.217.028-.434.044-.651a3.256 3.256 0 0 1 3.01-3.01c1.19-.09 2.392-.135 3.605-.135Zm-6.97 6.22a.75.75 0 0 0-1.06 0l-3 3a.75.75 0 1 0 1.06 1.06l1.752-1.751c.023.65.06 1.296.108 1.939a4.756 4.756 0 0 0 4.392 4.392 49.413 49.413 0 0 0 7.436 0 4.756 4.756 0 0 0 4.392-4.392c.017-.223.032-.447.046-.672a.75.75 0 0 0-1.497-.092c-.013.217-.028.434-.044.651a3.256 3.256 0 0 1-3.01 3.01 47.953 47.953 0 0 1-7.21 0 3.256 3.256 0 0 1-3.01-3.01 47.759 47.759 0 0 1-.1-1.759L6.97 15.53a.75.75 0 0 0 1.06-1.06l-3-3Z"
|
||||
clip-rule="evenodd"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div id="history" tabindex="-1"
|
||||
class="hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full">
|
||||
<div class="relative p-4 w-full max-h-full">
|
||||
<div class="relative bg-white shadow dark:bg-gray-700">
|
||||
<div class="flex items-center bg-blue-400 px-4 py-4">
|
||||
<p class="font-bold">{% trans "Image history" %}</p>
|
||||
<button type="button" data-modal-toggle="history"
|
||||
class="absolute top-2 end-1 text-black bg-transparent hover:text-black rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center"
|
||||
data-modal-hide="history">
|
||||
<svg class="w-3 h-3" aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg" fill="none"
|
||||
viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round"
|
||||
stroke-linejoin="round" stroke-width="2"
|
||||
d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
||||
</svg>
|
||||
<span class="sr-only">Close modal</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="p-4 md:p-5">
|
||||
<div class="relative py-5 overflow-x-auto">
|
||||
<table id="datatable-example"
|
||||
class="w-full text-sm text-left rtl:text-right">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
ID
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
CreatedBy
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
Created
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
Comment
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
Size
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody ng-repeat="history in historyList track by $index"
|
||||
class="border shadow-lg py-3 px-6 rounded-b-lg">
|
||||
<tr>
|
||||
<td style="word-break: break-all;" ng-bind="history.Id"
|
||||
class="flex items-center px-6 py-4"></td>
|
||||
<td style="word-break: break-all;"
|
||||
ng-bind="history.CreatedBy" class="px-6 py-4"></td>
|
||||
<td ng-bind="history.Created" class="px-6 py-4"></td>
|
||||
<td ng-bind="history.Comment" class="px-6 py-4"></td>
|
||||
<td ng-bind="history.Size" class="px-6 py-4"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-1 justify-end py-2">
|
||||
<button type="button"
|
||||
class="text-white bg-red-600 hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 dark:focus:ring-red-800 font-medium rounded-lg text-sm inline-flex items-center px-5 py-2.5 text-center me-2"
|
||||
data-modal-toggle="history"
|
||||
data-modal-hide="history">{% trans "Close" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button ng-click="rmImage({{ forloop.counter }})">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"/>
|
||||
</svg>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
209
dockerManager/templates/dockerManager/runContainerV2.html
Normal file
209
dockerManager/templates/dockerManager/runContainerV2.html
Normal file
@@ -0,0 +1,209 @@
|
||||
{% extends "baseTemplate/newBase.html" %}
|
||||
{% load i18n %}
|
||||
{% block titleNew %}{% trans "Home - CyberPanel" %}{% endblock %}
|
||||
{% block newContent %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
<div ng-controller="runContainerV2" class="p-8">
|
||||
<div>
|
||||
<div class="flex justify-between items-center">
|
||||
<div>
|
||||
<div class="flex items-center">
|
||||
<p class="text-4xl font-bold">Run Container</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-xs text-gray-600 py-2 font-semibold">Modify parameters for your new container</p>
|
||||
</div>
|
||||
<div class="border px-8 py-2 mt-3">
|
||||
<div class="flex justify-between items-center py-4">
|
||||
<div class="flex items-center">
|
||||
<p class="text-xl font-bold">Container Details</p>
|
||||
<img ng-hide="containerCreationLoading"
|
||||
src="{% static 'images/loading.gif' %}">
|
||||
</div>
|
||||
</div>
|
||||
<div ng-hide="installationDetailsForm" class="flex py-2 px-6">
|
||||
<div>
|
||||
<p class="font-semibold w-60">Name</p>
|
||||
</div>
|
||||
<div ng-init="name='{{ name }}' ">
|
||||
<input name="name" type="text" class="w-80 bg-gray-100 rounded px-2 py-1" ng-model="name" required>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-hide="installationDetailsForm" class="flex py-2 px-6">
|
||||
<div>
|
||||
<p class="font-semibold w-60">Image</p>
|
||||
</div>
|
||||
<div ng-init="image='{{ image }}' ">
|
||||
<input name="image" type="text" class="w-80 bg-gray-100 rounded px-2 py-1" ng-model="image" required
|
||||
disabled="disabled">
|
||||
</div>
|
||||
</div>
|
||||
<div ng-hide="installationDetailsForm" class="flex py-2 px-6">
|
||||
<div>
|
||||
<p class="font-semibold w-60">Tag</p>
|
||||
</div>
|
||||
<div ng-init="tag='{{ tag }}' ">
|
||||
<input name="tag" type="text" class="w-80 bg-gray-100 rounded px-2 py-1" ng-model="tag" required
|
||||
disabled="disabled">
|
||||
</div>
|
||||
</div>
|
||||
<div ng-hide="installationDetailsForm" class="flex py-2 px-6">
|
||||
<div>
|
||||
<p class="font-semibold w-60">Select Owner</p>
|
||||
</div>
|
||||
<div>
|
||||
<select ng-model="dockerOwner" class="w-80 bg-gray-100 rounded px-2 py-1">
|
||||
{% for items in ownerList %}
|
||||
<option>{{ items }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-hide="installationDetailsForm" class="flex items-center py-2 px-6">
|
||||
<div>
|
||||
<p class="font-semibold w-60">Memory limit</p>
|
||||
</div>
|
||||
<div>
|
||||
<input name="memory" type="number" class="w-80 bg-gray-100 rounded px-2 py-1" ng-model="memory"
|
||||
required>
|
||||
</div>
|
||||
<div class="ml-2">
|
||||
<p>MB</p>
|
||||
</div>
|
||||
</div>
|
||||
{% for port, protocol in portConfig.items %}
|
||||
<div ng-hide="installationDetailsForm" class="flex py-2 px-6">
|
||||
<div>
|
||||
<p class="font-semibold w-60">Port</p>
|
||||
</div>
|
||||
<div ng-init="iport[{{ port }}]={{ port }} ">
|
||||
<input name="iport[{{ port }}]" type="text" class="w-24 bg-gray-100 rounded px-2 py-1"
|
||||
ng-model="iport[{{ port }}]" required disabled="disabled">
|
||||
</div>
|
||||
<div class="ml-2" ng-init="portType['{{ port }}']='{{ protocol }}'">
|
||||
<input name="portType['{{ port }}']" type="text" class="w-20 bg-gray-100 rounded px-2 py-1"
|
||||
ng-model="portType['{{ port }}']" required disabled="disabled">
|
||||
</div>
|
||||
<div class="flex items-center ml-2">
|
||||
<p class="font-semibold w-8">to</p>
|
||||
</div>
|
||||
<div>
|
||||
<input name="eport['{{ port }}']" type="number" class="w-24 bg-gray-100 rounded px-2 py-1"
|
||||
ng-model="eport['{{ port }}']" required>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<span ng-init="envList = {}"></span>
|
||||
{% for env, value in envList.items %}
|
||||
<span ng-init="envList[{{ forloop.counter0 }}] = {'name':'{{ env }}' , 'value':'{{ value }}'} "></span>
|
||||
{% endfor %}
|
||||
<div class="border">
|
||||
<div ng-hide="installationDetailsForm" class="flex justify-center mt-2">
|
||||
<p class="font-semibold">
|
||||
ENV
|
||||
</p>
|
||||
</div>
|
||||
<div ng-repeat="env in envList track by $index" ng-hide="installationDetailsForm"
|
||||
class="flex justify-center py-2 px-6">
|
||||
<div ng-show="$first">
|
||||
<p class="font-semibold"></p>
|
||||
</div>
|
||||
{# <p class="font-semibold w-60"></p>#}
|
||||
<div>
|
||||
<input name="$index" type="text" class="w-80 bg-gray-100 rounded px-2 py-1"
|
||||
ng-model="envList[$index].name" required>
|
||||
</div>
|
||||
<div class="ml-1">
|
||||
<input name="$index" type="text" class="w-80 bg-gray-100 rounded px-2 py-1"
|
||||
ng-model="envList[$index].value" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-center mt-2 mb-2" ng-hide="installationDetailsForm">
|
||||
<button type="button"
|
||||
class="bg-orange-500 px-3 py-1 rounded-lg font-semibold text-white cursor-pointer"
|
||||
ng-click="addEnvField()">Add more
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border mt-2">
|
||||
<div ng-hide="installationDetailsForm" class="flex justify-center mt-2">
|
||||
<p class="font-semibold">
|
||||
Map Volumes
|
||||
</p>
|
||||
</div>
|
||||
<div ng-repeat="volume in volList track by $index" ng-hide="installationDetailsForm"
|
||||
class="flex items-center py-2 px-6">
|
||||
<div>
|
||||
<p class="font-semibold w-28"></p>
|
||||
</div>
|
||||
{# <p class="font-semibold w-60"></p>#}
|
||||
<div>
|
||||
<input type="text" class="w-80 bg-gray-100 rounded px-2 py-1" ng-model="volList[$index].dest"
|
||||
placeholder="Destination" required>
|
||||
</div>
|
||||
<div class="ml-1">
|
||||
<input type="text" class="w-80 bg-gray-100 rounded px-2 py-1" ng-model="volList[$index].src"
|
||||
placeholder="Source" required>
|
||||
</div>
|
||||
<div class="ml-1" ng-show="$last">
|
||||
<div>
|
||||
<button type="button" ng-click="removeVolField()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="m9.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-center mt-2 mb-2" ng-hide="installationDetailsForm">
|
||||
<button type="button"
|
||||
class="bg-orange-500 px-3 py-1 rounded-lg font-semibold text-white cursor-pointer"
|
||||
ng-click="addVolField()">Add Field
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-hide="installationDetailsForm" class="flex justify-center mt-3">
|
||||
<button ng-click="createContainer()"
|
||||
class="bg-orange-500 px-3 py-2 rounded-lg text-xl font-semibold text-white">
|
||||
Create Container
|
||||
</button>
|
||||
</div>
|
||||
<div ng-hide="installationProgress" class="mt-2">
|
||||
<div class="flex justify-center font-bold text-xl">
|
||||
<h2>{$ currentStatus $}</h2>
|
||||
</div>
|
||||
<div class="w-full bg-gray-100 rounded-full mt-3">
|
||||
<div id="installProgress"
|
||||
class="bg-green-600 text-xs font-medium text-white text-center p-2 leading-none rounded-full"
|
||||
style="width:0%">
|
||||
</div>
|
||||
</div>
|
||||
<div ng-hide="errorMessageBox"
|
||||
class="flex mt-2 justify-center bg-red-500 rounded-lg text-white px-2 py-1 font-semibold">
|
||||
<p>{% trans "Error message:" %} {$ errorMessage $}</p>
|
||||
</div>
|
||||
<div ng-hide="success" class="flex mt-2 justify-center bg-green-500 px-2 rounded-lg py-1 font-semibold">
|
||||
<p>{% trans "Container succesfully created." %}</p>
|
||||
</div>
|
||||
<div ng-hide="couldNotConnect"
|
||||
class="flex mt-2 justify-center bg-red-500 rounded-lg text-white px-2 py-1 font-semibold">
|
||||
<p>{% trans "Could not connect to server. Please refresh this page." %}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="installationProgress" class="flex justify-center mt-3">
|
||||
<button ng-disabled="goBackDisable"
|
||||
ng-click="goBack()"
|
||||
class="bg-blue-500 px-3 py-2 rounded-lg text-xl font-semibold text-white">
|
||||
Go Back
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
438
dockerManager/templates/dockerManager/viewContainerV2.html
Normal file
438
dockerManager/templates/dockerManager/viewContainerV2.html
Normal file
@@ -0,0 +1,438 @@
|
||||
{% extends "baseTemplate/newBase.html" %}
|
||||
{% load i18n %}
|
||||
{% block titleNew %}{% trans "Home - CyberPanel" %}{% endblock %}
|
||||
{% block newContent %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
<div ng-controller="viewContainerV2" class="p-8">
|
||||
<div>
|
||||
<div class="flex justify-between items-center">
|
||||
<div>
|
||||
<div ng-init="cName='{{ name }}'" class="flex items-center">
|
||||
<p class="text-4xl font-bold">Manage Container</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-xs text-gray-600 py-2 font-semibold">Currently managing: {{ name }}</p>
|
||||
</div>
|
||||
<div class="border px-8 py-2 mt-3">
|
||||
<div class="flex items-center">
|
||||
<p class="text-xl font-bold">Container Information</p>
|
||||
<img id="infoLoading" src="/static/images/loading.gif" style="display: none;">
|
||||
</div>
|
||||
<div class="flex flex-col md:flex-col lg:flex-row justify-center gap-3">
|
||||
<div class="border w-full">
|
||||
<div class="mt-2">
|
||||
<div class="px-2 py-2">
|
||||
<p class="font-semibold">Memory Usage</p>
|
||||
</div>
|
||||
<div class="px-2">
|
||||
<div class="w-full bg-gray-200 dark:bg-gray-700">
|
||||
<div class="bg-blue-600 text-xs font-medium text-black text-center p-1 leading-none"
|
||||
style="width: {{ memoryUsage }}%"> {{ memoryUsage | floatformat:"2" }}%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-2 py-2">
|
||||
<p class="font-semibold">CPU Usage</p>
|
||||
</div>
|
||||
<div class="px-2">
|
||||
<div class="w-full bg-gray-200 dark:bg-gray-700">
|
||||
<div class="bg-blue-600 text-xs font-medium text-black text-center p-1 leading-none"
|
||||
style="width: {{ cpuUsage }}%"> {{ cpuUsage }}%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border w-full">
|
||||
<div class="p-3">
|
||||
{% trans "Container ID" %}: {{ cid }}
|
||||
<br>
|
||||
{% trans "Image" %}: {{ image }}
|
||||
<span ng-show="'{{ image }}' == 'unknown:unknown'"
|
||||
title="Actions involving container recreation cannot be executed">
|
||||
<i class="fa fa-warning btn-icon"></i>
|
||||
</span>
|
||||
<br>
|
||||
{% if ports %}
|
||||
{% trans "Ports" %}: <br>
|
||||
{% for iport, eport in ports.items %}
|
||||
{{ iport }} {% trans "to" %} {{ eport }}<br>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<div class="flex justify-center">
|
||||
<button onclick="return false;"
|
||||
ng-click="showLog(web.name)"
|
||||
href="#" data-modal-target="showLog" data-modal-toggle="showLog">
|
||||
<div class="flex items-center gap-1 text-white bg-red-600 hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 dark:focus:ring-red-800 font-medium rounded-lg text-sm inline-flex items-center px-5 py-2.5 text-center me-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M10.343 3.94c.09-.542.56-.94 1.11-.94h1.093c.55 0 1.02.398 1.11.94l.149.894c.07.424.384.764.78.93.398.164.855.142 1.205-.108l.737-.527a1.125 1.125 0 0 1 1.45.12l.773.774c.39.389.44 1.002.12 1.45l-.527.737c-.25.35-.272.806-.107 1.204.165.397.505.71.93.78l.893.15c.543.09.94.559.94 1.109v1.094c0 .55-.397 1.02-.94 1.11l-.894.149c-.424.07-.764.383-.929.78-.165.398-.143.854.107 1.204l.527.738c.32.447.269 1.06-.12 1.45l-.774.773a1.125 1.125 0 0 1-1.449.12l-.738-.527c-.35-.25-.806-.272-1.203-.107-.398.165-.71.505-.781.929l-.149.894c-.09.542-.56.94-1.11.94h-1.094c-.55 0-1.019-.398-1.11-.94l-.148-.894c-.071-.424-.384-.764-.781-.93-.398-.164-.854-.142-1.204.108l-.738.527c-.447.32-1.06.269-1.45-.12l-.773-.774a1.125 1.125 0 0 1-.12-1.45l.527-.737c.25-.35.272-.806.108-1.204-.165-.397-.506-.71-.93-.78l-.894-.15c-.542-.09-.94-.56-.94-1.109v-1.094c0-.55.398-1.02.94-1.11l.894-.149c.424-.07.765-.383.93-.78.165-.398.143-.854-.108-1.204l-.526-.738a1.125 1.125 0 0 1 .12-1.45l.773-.773a1.125 1.125 0 0 1 1.45-.12l.737.527c.35.25.807.272 1.204.107.397-.165.71-.505.78-.929l.15-.894Z"/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"/>
|
||||
</svg>
|
||||
<p>Settings</p>
|
||||
</div>
|
||||
</button>
|
||||
<div id="showLog" tabindex="-1"
|
||||
class="hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full">
|
||||
<div class="relative p-4 w-full max-w-2xl max-h-full">
|
||||
<div class="relative bg-white shadow dark:bg-gray-700">
|
||||
<div class="flex items-center bg-blue-400 px-4 py-4">
|
||||
<p class="font-bold">{% trans "Container Settings" %}</p>
|
||||
<img id="containerSettingLoading" src="/static/images/loading.gif"
|
||||
style="display: none;">
|
||||
<button type="button" data-modal-toggle="showLog"
|
||||
class="absolute top-2 end-1 text-black bg-transparent hover:text-black rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center"
|
||||
data-modal-hide="showLog">
|
||||
<svg class="w-3 h-3" aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg" fill="none"
|
||||
viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round"
|
||||
stroke-linejoin="round" stroke-width="2"
|
||||
d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
||||
</svg>
|
||||
<span class="sr-only">Close modal</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="p-4 md:p-5">
|
||||
<form name="containerSettingsForm" action="/">
|
||||
<div ng-hide="installationDetailsForm"
|
||||
class="flex items-center py-2 px-6">
|
||||
<div>
|
||||
<p class="font-semibold w-32">Memory limit</p>
|
||||
</div>
|
||||
<div ng-init="memory={{ memoryLimit }}">
|
||||
<input name="memory" type="number"
|
||||
class="w-80 bg-gray-100 rounded px-2 py-1"
|
||||
ng-model="memory"
|
||||
required>
|
||||
</div>
|
||||
<div class="ml-2">
|
||||
<p>MB</p>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-hide="installationDetailsForm"
|
||||
class="flex items-center py-2 px-6">
|
||||
<div>
|
||||
<p class="font-semibold w-32">Start on reboot</p>
|
||||
</div>
|
||||
<div ng-init="startOnReboot={{ startOnReboot }}">
|
||||
<input ng-model="startOnReboot" type="checkbox" value="">
|
||||
</div>
|
||||
</div>
|
||||
<div ng-hide="installationDetailsForm"
|
||||
class="flex items-center py-2 px-6">
|
||||
<div>
|
||||
<p class="font-semibold w-32">Confirmation</p>
|
||||
</div>
|
||||
<div>
|
||||
<input ng-model="envConfirmation" type="checkbox">
|
||||
</div>
|
||||
<div class="ml-2">
|
||||
<p>Editing ENV or Volume will recreate container.</p>
|
||||
</div>
|
||||
</div>
|
||||
<span ng-init="envList = {}"></span>
|
||||
{% for env, value in envList.items %}
|
||||
|
||||
<span ng-init="envList[{{ forloop.counter0 }}] = {'name':'{{ env }}' , 'value':'{{ value }}'} "></span>
|
||||
|
||||
{% endfor %}
|
||||
<div>
|
||||
<div ng-hide="installationDetailsForm"
|
||||
class="flex justify-center mt-2">
|
||||
<p class="font-semibold">
|
||||
ENV
|
||||
</p>
|
||||
</div>
|
||||
<div ng-repeat="env in envList track by $index"
|
||||
ng-hide="installationDetailsForm"
|
||||
class="flex justify-center py-2 px-6">
|
||||
<div ng-show="$first">
|
||||
<p class="font-semibold"></p>
|
||||
</div>
|
||||
{# <p class="font-semibold w-60"></p>#}
|
||||
<div>
|
||||
<input name="$index" ng-disabled="!envConfirmation"
|
||||
type="text"
|
||||
class="w-40 bg-gray-100 rounded px-2 py-1"
|
||||
ng-model="envList[$index].name"
|
||||
required>
|
||||
</div>
|
||||
<div class="ml-1">
|
||||
<input name="$index" ng-disabled="!envConfirmation"
|
||||
type="text"
|
||||
class="w-40 bg-gray-100 rounded px-2 py-1"
|
||||
ng-model="envList[$index].value"
|
||||
required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-center mt-2 mb-2">
|
||||
<button type="button" ng-disabled="!envConfirmation"
|
||||
class="bg-orange-500 px-3 py-1 rounded-lg font-semibold text-white cursor-pointer"
|
||||
ng-click="addEnvField()">Add more
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<span ng-init="volList = {}"></span>
|
||||
<span ng-init="volListNumber=1"></span>
|
||||
{% for key, value in volList.items %}
|
||||
<span ng-init="volList[{{ forloop.counter0 }}] = {'dest':'{{ value.bind }}' , 'src':'{{ key }}'}"></span>
|
||||
<span ng-init="volListNumber={{ forloop.counter0 }} + 1"></span>
|
||||
{% endfor %}
|
||||
<div>
|
||||
<div ng-hide="installationDetailsForm"
|
||||
class="flex justify-center mt-2">
|
||||
<p class="font-semibold">
|
||||
Map Volumes
|
||||
</p>
|
||||
</div>
|
||||
<div ng-repeat="volume in volList track by $index"
|
||||
ng-hide="installationDetailsForm"
|
||||
class="flex items-center py-2 px-6">
|
||||
<div>
|
||||
<p class="font-semibold w-28"></p>
|
||||
</div>
|
||||
{# <p class="font-semibold w-60"></p>#}
|
||||
<div>
|
||||
<input type="text" ng-disabled="!envConfirmation"
|
||||
class="w-80 bg-gray-100 rounded px-2 py-1"
|
||||
ng-model="volList[$index].dest"
|
||||
placeholder="Destination" required>
|
||||
</div>
|
||||
<div class="ml-1">
|
||||
<input type="text" ng-disabled="!envConfirmation"
|
||||
class="w-80 bg-gray-100 rounded px-2 py-1"
|
||||
ng-model="volList[$index].src" placeholder="Source"
|
||||
required>
|
||||
</div>
|
||||
<div class="ml-1" ng-show="$last">
|
||||
<div>
|
||||
<button type="button" ng-disabled="!envConfirmation"
|
||||
ng-click="removeVolField()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="m9.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-center mt-2 mb-2"
|
||||
ng-hide="installationDetailsForm">
|
||||
<button type="button" ng-disabled="!envConfirmation"
|
||||
class="bg-orange-500 px-3 py-1 rounded-lg font-semibold text-white cursor-pointer"
|
||||
ng-click="addVolField()">Add Field
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="flex gap-1 justify-end py-2">
|
||||
<button ng-click="saveSettings()" type="button"
|
||||
ng-disabled="savingSettings"
|
||||
class="text-white bg-green-600 hover:bg-green-800 focus:ring-4 focus:outline-none focus:ring-green-300 dark:focus:ring-green-800 font-medium rounded-lg text-sm inline-flex items-center px-5 py-2.5 text-center me-2">{% trans "Save" %}</button>
|
||||
<button type="button" ng-disabled="savingSettings"
|
||||
class="text-white bg-red-600 hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 dark:focus:ring-red-800 font-medium rounded-lg text-sm inline-flex items-center px-5 py-2.5 text-center me-2"
|
||||
data-modal-toggle="showLog"
|
||||
data-modal-hide="showLog">{% trans "Close" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="text-white bg-green-600 hover:bg-green-800 focus:ring-4 focus:outline-none focus:ring-green-300 dark:focus:ring-green-800 font-medium rounded-lg text-sm inline-flex items-center px-5 py-2.5 text-center me-2"
|
||||
ng-click="recreate()">
|
||||
Recreate
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border px-8 py-2 mt-3">
|
||||
<div class="flex items-center mb-2">
|
||||
<p class="text-xl font-bold">Actions</p>
|
||||
</div>
|
||||
<div class="flex flex-col md:flex-col lg:flex-row justify-center gap-3">
|
||||
<div class="border w-full p-3">
|
||||
<div class="flex justify-between">
|
||||
<div class="flex items-center">
|
||||
<p ng-init="status='{{ status }}'">Main Actions</p>
|
||||
<img id="actionLoading" src="/static/images/loading.gif"
|
||||
style="display: none;width: 20px;">
|
||||
</div>
|
||||
<div>
|
||||
<button ng-click='refreshStatus()'>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182m0-4.991v4.99"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<p class="font-bold">Status:</p>
|
||||
<span class="ml-1" ng-bind="status"></span>
|
||||
</div>
|
||||
<div class="flex justify-center mt-2">
|
||||
<div ng-disabled="status=='running'" ng-click="cAction('start')"
|
||||
class="flex cursor-pointer items-center text-white bg-green-600 hover:bg-green-800 focus:ring-4 focus:outline-none focus:ring-green-300 dark:focus:ring-green-800 font-medium rounded-lg text-sm inline-flex items-center px-2.5 py-1 text-center me-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M5.25 5.653c0-.856.917-1.398 1.667-.986l11.54 6.347a1.125 1.125 0 0 1 0 1.972l-11.54 6.347a1.125 1.125 0 0 1-1.667-.986V5.653Z"/>
|
||||
</svg>
|
||||
<p>Start</p>
|
||||
</div>
|
||||
<div ng-disabled="status!='running'" ng-click="cAction('restart')"
|
||||
class="flex cursor-pointer items-center text-white bg-blue-600 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 dark:focus:ring-blue-800 font-medium rounded-lg text-sm inline-flex items-center px-2.5 py-1 text-center me-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182m0-4.991v4.99"/>
|
||||
</svg>
|
||||
<p>Restart</p>
|
||||
</div>
|
||||
<div ng-disabled="status!='running'" ng-click="cAction('stop')"
|
||||
class="flex cursor-pointer items-center text-white bg-orange-600 hover:bg-orange-800 focus:ring-4 focus:outline-none focus:ring-orange-300 dark:focus:ring-orange-800 font-medium rounded-lg text-sm inline-flex items-center px-2.5 py-1 text-center me-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M5.25 7.5A2.25 2.25 0 0 1 7.5 5.25h9a2.25 2.25 0 0 1 2.25 2.25v9a2.25 2.25 0 0 1-2.25 2.25h-9a2.25 2.25 0 0 1-2.25-2.25v-9Z"/>
|
||||
</svg>
|
||||
<p>Stop</p>
|
||||
</div>
|
||||
<div ng-click="cRemove()"
|
||||
class="flex cursor-pointer items-center text-white bg-red-600 hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 dark:focus:ring-red-800 font-medium rounded-lg text-sm inline-flex items-center px-2.5 py-1 text-center me-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12"/>
|
||||
</svg>
|
||||
<p>Remove</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border w-full p-3">
|
||||
<div class="flex justify-between">
|
||||
<div class="flex items-center">
|
||||
<p ng-init="rPolicy='{{ restartPolicy }}'">Other Actions</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<p class="font-bold">Restart on system reboot:</p>
|
||||
<span class="ml-1" ng-bind="rPolicy"></span>
|
||||
</div>
|
||||
<div class="flex justify-center mt-2">
|
||||
<button>
|
||||
<a href="/docker/exportContainer/?name={{ name }}"
|
||||
class="flex cursor-pointer items-center text-white bg-orange-600 hover:bg-orange-800 focus:ring-4 focus:outline-none focus:ring-orange-300 dark:focus:ring-orange-800 font-medium rounded-lg text-sm inline-flex items-center px-2.5 py-1 text-center me-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M13.5 6H5.25A2.25 2.25 0 0 0 3 8.25v10.5A2.25 2.25 0 0 0 5.25 21h10.5A2.25 2.25 0 0 0 18 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25"/>
|
||||
</svg>
|
||||
<p>
|
||||
Export File
|
||||
</p>
|
||||
</a>
|
||||
</button>
|
||||
<button onclick="return false;"
|
||||
ng-disabled="loadingTop" ng-click="showTop()"
|
||||
href="#" data-modal-target="viewprocess" data-modal-toggle="viewprocess">
|
||||
<div class="flex cursor-pointer items-center text-white bg-orange-600 hover:bg-orange-800 focus:ring-4 focus:outline-none focus:ring-orange-300 dark:focus:ring-orange-800 font-medium rounded-lg text-sm inline-flex items-center px-2.5 py-1 text-center me-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z"/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"/>
|
||||
</svg>
|
||||
<p class="ml-1">
|
||||
View Process
|
||||
</p>
|
||||
</div>
|
||||
</button>
|
||||
<div id="viewprocess" tabindex="-1"
|
||||
class="hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full">
|
||||
<div class="relative p-4 w-full max-w-2xl max-h-full">
|
||||
<div class="relative bg-white shadow dark:bg-gray-700">
|
||||
<div class="flex items-center bg-blue-400 px-4 py-4">
|
||||
<p class="font-bold">{% trans "Container Processes" %}</p>
|
||||
<button type="button" data-modal-toggle="viewprocess"
|
||||
class="absolute top-2 end-1 text-black bg-transparent hover:text-black rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center"
|
||||
data-modal-hide="viewprocess">
|
||||
<svg class="w-3 h-3" aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg" fill="none"
|
||||
viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round"
|
||||
stroke-linejoin="round" stroke-width="2"
|
||||
d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
||||
</svg>
|
||||
<span class="sr-only">Close modal</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="p-4 md:p-5">
|
||||
<div class="relative py-5 overflow-x-auto">
|
||||
<table id="datatable-example"
|
||||
class="w-full text-sm text-left rtl:text-right">
|
||||
<thead>
|
||||
<tr>
|
||||
<th ng-repeat="item in topHead track by $index" class="px-6 py-3">
|
||||
{$ item $}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody ng-repeat="process in topProcesses track by $index"
|
||||
class="border shadow-lg py-3 px-6 rounded-b-lg">
|
||||
<tr>
|
||||
<td ng-repeat="item in process track by $index" class="px-6 py-4">
|
||||
{$ item $}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-1 justify-end py-2">
|
||||
<button ng-click="showTop()" type="button"
|
||||
ng-disabled="savingSettings"
|
||||
class="text-white bg-green-600 hover:bg-green-800 focus:ring-4 focus:outline-none focus:ring-green-300 dark:focus:ring-green-800 font-medium rounded-lg text-sm inline-flex items-center px-5 py-2.5 text-center me-2">{% trans "Refresh" %}</button>
|
||||
<button type="button" ng-disabled="savingSettings"
|
||||
class="text-white bg-red-600 hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 dark:focus:ring-red-800 font-medium rounded-lg text-sm inline-flex items-center px-5 py-2.5 text-center me-2"
|
||||
data-modal-toggle="viewprocess"
|
||||
data-modal-hide="viewprocess">{% trans "Close" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border px-8 py-2 mt-3">
|
||||
<div ng-init="loadLogs('{{ name }}')" class="flex justify-between items-center">
|
||||
<p class="text-xl font-bold">Logs</p>
|
||||
<span style="cursor:pointer;" class="pull-right" ng-click="loadLogs('{{ name }}')"></span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="w-6 h-6 cursor-pointer">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182m0-4.991v4.99"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<textarea name="logs" class="border w-full" id="" cols="30" rows="10">{$ logs $}</textarea><br>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -3,11 +3,13 @@ from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.loadDockerHome, name='dockerHome'),
|
||||
# url(r'^images', views.loadImages, name='loadImages'),
|
||||
# url(r'^images', views.loadImages, name='loadImages'),
|
||||
url(r'^getTags', views.getTags, name='getTags'),
|
||||
url(r'^runContainer', views.runContainer, name='runContainer'),
|
||||
url(r'^V2/runContainerV2', views.runContainerV2, name='runContainerV2'),
|
||||
url(r'^submitContainerCreation', views.submitContainerCreation, name='submitContainerCreation'),
|
||||
url(r'^listContainers', views.listContainers, name='listContainers'),
|
||||
url(r'^V2/listContainersV2', views.listContainersV2, name='listContainersV2'),
|
||||
url(r'^getContainerList', views.getContainerList, name='getContainerList'),
|
||||
url(r'^getContainerLogs', views.getContainerLogs, name='getContainerLogs'),
|
||||
url(r'^installImage', views.installImage, name='installImage'),
|
||||
@@ -20,10 +22,13 @@ urlpatterns = [
|
||||
url(r'^assignContainer', views.assignContainer, name='assignContainer'),
|
||||
url(r'^searchImage', views.searchImage, name='searchImage'),
|
||||
url(r'^manageImages', views.manageImages, name='manageImages'),
|
||||
url(r'^V2/manageImagesV2', views.manageImagesV2, name='manageImagesV2'),
|
||||
url(r'^getImageHistory', views.getImageHistory, name='getImageHistory'),
|
||||
url(r'^removeImage', views.removeImage, name='removeImage'),
|
||||
url(r'^recreateContainer', views.recreateContainer, name='recreateContainer'),
|
||||
url(r'^installDocker', views.installDocker, name='installDocker'),
|
||||
url(r'^images', views.images, name='containerImage'),
|
||||
url(r'^V2/imagesV2', views.imagesV2, name='containerImageV2'),
|
||||
url(r'^view/(?P<name>(.*))$', views.viewContainer, name='viewContainer'),
|
||||
]
|
||||
url(r'^V2/view/(?P<name>(.*))$', views.viewContainerV2, name='viewContainerV2'),
|
||||
]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from django.shortcuts import redirect, HttpResponse
|
||||
@@ -10,14 +10,14 @@ from .decorators import preDockerRun
|
||||
from plogical.acl import ACLManager
|
||||
import json
|
||||
|
||||
|
||||
# Create your views here.
|
||||
|
||||
# This function checks if user has admin permissions
|
||||
|
||||
def dockerPermission(request, userID, context):
|
||||
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
|
||||
|
||||
if currentACL['admin'] != 1:
|
||||
if request.method == "POST":
|
||||
return ACLManager.loadErrorJson()
|
||||
@@ -25,7 +25,8 @@ def dockerPermission(request, userID, context):
|
||||
return ACLManager.loadError()
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
@preDockerRun
|
||||
def loadDockerHome(request):
|
||||
userID = request.session['userID']
|
||||
@@ -34,6 +35,7 @@ def loadDockerHome(request):
|
||||
proc = httpProc(request, template, {"type": admin.type}, 'admin')
|
||||
return proc.render()
|
||||
|
||||
|
||||
def installDocker(request):
|
||||
try:
|
||||
|
||||
@@ -57,7 +59,8 @@ def installDocker(request):
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
@preDockerRun
|
||||
|
||||
@preDockerRun
|
||||
def installImage(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
@@ -75,7 +78,8 @@ def installImage(request):
|
||||
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
|
||||
|
||||
@preDockerRun
|
||||
def viewContainer(request, name):
|
||||
try:
|
||||
@@ -97,10 +101,35 @@ def viewContainer(request, name):
|
||||
return coreResult
|
||||
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
|
||||
@preDockerRun
|
||||
def getTags(request):
|
||||
def viewContainerV2(request, name):
|
||||
try:
|
||||
if not request.GET._mutable:
|
||||
request.GET._mutable = True
|
||||
request.GET['name'] = name
|
||||
|
||||
userID = request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
|
||||
if currentACL['admin'] == 1:
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
cm = ContainerManager(name)
|
||||
coreResult = cm.loadContainerHomeV2(request, userID)
|
||||
|
||||
return coreResult
|
||||
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
|
||||
@preDockerRun
|
||||
def getTags(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
@@ -117,9 +146,10 @@ def getTags(request):
|
||||
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
|
||||
|
||||
@preDockerRun
|
||||
def delContainer(request):
|
||||
def delContainer(request):
|
||||
try:
|
||||
|
||||
userID = request.session['userID']
|
||||
@@ -137,9 +167,10 @@ def delContainer(request):
|
||||
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
@preDockerRun
|
||||
def recreateContainer(request):
|
||||
|
||||
|
||||
@preDockerRun
|
||||
def recreateContainer(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
@@ -155,9 +186,10 @@ def recreateContainer(request):
|
||||
return coreResult
|
||||
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
@preDockerRun
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
|
||||
@preDockerRun
|
||||
def runContainer(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
@@ -167,13 +199,31 @@ def runContainer(request):
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
|
||||
cm = ContainerManager()
|
||||
return cm.createContainer(request, userID)
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
@preDockerRun
|
||||
|
||||
@preDockerRun
|
||||
def runContainerV2(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
|
||||
if currentACL['admin'] == 1:
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
cm = ContainerManager()
|
||||
return cm.createContainerV2(request, userID)
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
|
||||
@preDockerRun
|
||||
def listContainers(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
@@ -182,7 +232,18 @@ def listContainers(request):
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
@preDockerRun
|
||||
|
||||
@preDockerRun
|
||||
def listContainersV2(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
cm = ContainerManager()
|
||||
return cm.listContainersV2(request, userID)
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
|
||||
@preDockerRun
|
||||
def getContainerLogs(request):
|
||||
try:
|
||||
|
||||
@@ -199,9 +260,10 @@ def getContainerLogs(request):
|
||||
return coreResult
|
||||
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
@preDockerRun
|
||||
|
||||
@preDockerRun
|
||||
def submitContainerCreation(request):
|
||||
try:
|
||||
|
||||
@@ -221,7 +283,8 @@ def submitContainerCreation(request):
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
@preDockerRun
|
||||
|
||||
@preDockerRun
|
||||
def getContainerList(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
@@ -231,13 +294,14 @@ def getContainerList(request):
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
|
||||
cm = ContainerManager()
|
||||
return cm.getContainerList(userID, json.loads(request.body))
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
@preDockerRun
|
||||
|
||||
@preDockerRun
|
||||
def doContainerAction(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
@@ -247,15 +311,16 @@ def doContainerAction(request):
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
|
||||
cm = ContainerManager()
|
||||
coreResult = cm.doContainerAction(userID, json.loads(request.body))
|
||||
|
||||
|
||||
return coreResult
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
@preDockerRun
|
||||
|
||||
@preDockerRun
|
||||
def getContainerStatus(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
@@ -265,15 +330,16 @@ def getContainerStatus(request):
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
|
||||
cm = ContainerManager()
|
||||
coreResult = cm.getContainerStatus(userID, json.loads(request.body))
|
||||
|
||||
|
||||
return coreResult
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
@preDockerRun
|
||||
|
||||
@preDockerRun
|
||||
def exportContainer(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
@@ -283,15 +349,16 @@ def exportContainer(request):
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
|
||||
cm = ContainerManager()
|
||||
coreResult = cm.exportContainer(request, userID)
|
||||
|
||||
|
||||
return coreResult
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
@preDockerRun
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
|
||||
@preDockerRun
|
||||
def saveContainerSettings(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
@@ -301,15 +368,16 @@ def saveContainerSettings(request):
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
|
||||
cm = ContainerManager()
|
||||
coreResult = cm.saveContainerSettings(userID, json.loads(request.body))
|
||||
|
||||
|
||||
return coreResult
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
@preDockerRun
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
|
||||
@preDockerRun
|
||||
def getContainerTop(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
@@ -319,15 +387,16 @@ def getContainerTop(request):
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
|
||||
cm = ContainerManager()
|
||||
coreResult = cm.getContainerTop(userID, json.loads(request.body))
|
||||
|
||||
|
||||
return coreResult
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
@preDockerRun
|
||||
|
||||
@preDockerRun
|
||||
def assignContainer(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
@@ -337,15 +406,16 @@ def assignContainer(request):
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
|
||||
cm = ContainerManager()
|
||||
coreResult = cm.assignContainer(userID, json.loads(request.body))
|
||||
|
||||
|
||||
return coreResult
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
@preDockerRun
|
||||
|
||||
|
||||
@preDockerRun
|
||||
def searchImage(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
@@ -355,27 +425,42 @@ def searchImage(request):
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
|
||||
cm = ContainerManager()
|
||||
coreResult = cm.searchImage(userID, json.loads(request.body))
|
||||
|
||||
|
||||
return coreResult
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
@preDockerRun
|
||||
|
||||
|
||||
@preDockerRun
|
||||
def images(request):
|
||||
try:
|
||||
|
||||
userID = request.session['userID']
|
||||
cm = ContainerManager()
|
||||
coreResult = cm.images(request, userID)
|
||||
|
||||
|
||||
return coreResult
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
@preDockerRun
|
||||
|
||||
|
||||
@preDockerRun
|
||||
def imagesV2(request):
|
||||
try:
|
||||
|
||||
userID = request.session['userID']
|
||||
cm = ContainerManager()
|
||||
coreResult = cm.imagesV2(request, userID)
|
||||
|
||||
return coreResult
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
|
||||
@preDockerRun
|
||||
def manageImages(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
@@ -384,8 +469,20 @@ def manageImages(request):
|
||||
return coreResult
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
@preDockerRun
|
||||
|
||||
|
||||
@preDockerRun
|
||||
def manageImagesV2(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
cm = ContainerManager()
|
||||
coreResult = cm.manageImagesV2(request, userID)
|
||||
return coreResult
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
|
||||
@preDockerRun
|
||||
def getImageHistory(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
@@ -395,15 +492,16 @@ def getImageHistory(request):
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
|
||||
cm = ContainerManager()
|
||||
coreResult = cm.getImageHistory(userID, json.loads(request.body))
|
||||
|
||||
|
||||
return coreResult
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
@preDockerRun
|
||||
|
||||
|
||||
@preDockerRun
|
||||
def removeImage(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
@@ -413,10 +511,10 @@ def removeImage(request):
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
|
||||
cm = ContainerManager()
|
||||
coreResult = cm.removeImage(userID, json.loads(request.body))
|
||||
|
||||
|
||||
return coreResult
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
Reference in New Issue
Block a user