mirror of
https://github.com/unraid/webgui.git
synced 2026-04-24 02:58:57 -05:00
repo reorg
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
Title="Add Container"
|
||||
Cond="(pgrep('dockerd')!==false)"
|
||||
Markdown="false"
|
||||
---
|
||||
<?PHP
|
||||
/* Copyright 2005-2020, Lime Technology
|
||||
* Copyright 2014-2020, Guilherme Jardim, Eric Schultz, Jon Panozzo.
|
||||
* Copyright 2012-2020, Bergware International.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
// add docker translations (if needed)
|
||||
if (substr($_SERVER['REQUEST_URI'],0,7) != '/Docker') {
|
||||
$docker = "$docroot/languages/$locale/docker.dot";
|
||||
if (file_exists($docker)) $language = array_merge($language,unserialize(file_get_contents($docker)));
|
||||
}
|
||||
eval('?>'.parse_file("$docroot/plugins/dynamix.docker.manager/include/CreateDocker.php"));
|
||||
?>
|
||||
@@ -0,0 +1,25 @@
|
||||
Menu="Tasks:60"
|
||||
Type="xmenu"
|
||||
Code="e90b"
|
||||
Lock="true"
|
||||
Cond="exec(\"grep -o '^DOCKER_ENABLED=.yes' /boot/config/docker.cfg 2>/dev/null\")"
|
||||
---
|
||||
<?PHP
|
||||
/* Copyright 2005-2021, Lime Technology
|
||||
* Copyright 2014-2021, Guilherme Jardim, Eric Schultz, Jon Panozzo.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
if ($var['fsState'] != 'Started') {
|
||||
echo "<div class='notice shift'>_(Array must be **Started** to view Docker containers)_.</div>";
|
||||
} elseif (!is_file('/var/run/dockerd.pid') || (!is_dir('/proc/'.@file_get_contents('/var/run/dockerd.pid')))) {
|
||||
echo "<div class='notice shift'>_(Docker Service failed to start)_.</div>";
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,193 @@
|
||||
Menu="Docker:1"
|
||||
Title="Docker Containers"
|
||||
Tag="cubes"
|
||||
Cond="is_file('/var/run/dockerd.pid')"
|
||||
Markdown="false"
|
||||
Nchan="docker_load:stop"
|
||||
---
|
||||
<?PHP
|
||||
/* Copyright 2005-2023, Lime Technology
|
||||
* Copyright 2014-2023, Guilherme Jardim, Eric Schultz, Jon Panozzo.
|
||||
* Copyright 2012-2023, Bergware International.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
|
||||
|
||||
$width = in_array($theme,['white','black']) ? -58: -44;
|
||||
$top = in_array($theme,['white','black']) ? 40 : 20;
|
||||
$busy = "<i class='fa fa-spin fa-circle-o-notch'></i> "._('Please wait')."... "._('starting up containers');
|
||||
$cpus = cpu_list();
|
||||
?>
|
||||
<link type="text/css" rel="stylesheet" href="<?autov('/webGui/styles/jquery.ui.css')?>">
|
||||
<link type="text/css" rel="stylesheet" href="<?autov('/webGui/styles/jquery.switchbutton.css')?>">
|
||||
<link type="text/css" rel="stylesheet" href="<?autov("/plugins/dynamix.docker.manager/styles/style-$theme.css")?>">
|
||||
<style>
|
||||
.basic{display:block}
|
||||
.advanced{display:none;white-space:nowrap}
|
||||
.log{cursor:zoom-in}
|
||||
.exec{cursor:pointer}
|
||||
table#docker_containers{text-align:left}
|
||||
th.five{width:5%}
|
||||
th.nine{width:9%}
|
||||
th.load{width:140px}
|
||||
input.wait{width:24px;margin:0 4px;padding:0 5px;border:none;box-shadow:none;background-color:transparent}
|
||||
table tbody td{line-height:normal}
|
||||
i.mover{margin-right:8px;display:none}
|
||||
#resetsort{margin-left:12px;display:inline-block;width:32px}
|
||||
</style>
|
||||
<table id="docker_containers" class="tablesorter shift">
|
||||
<thead><tr><th><a id="resetsort" class="nohand" onclick="resetSorting()" title="_(Reset sorting)_"><i class="fa fa-th-list"></i></a>_(Application)_</th><th>_(Version)_</th><th>_(Network)_</th><th>_(Port Mappings)_ <small>(_(App to Host)_)</small></th><th>_(Volume Mappings)_ <small>(_(App to Host)_)</small></th><th class="load advanced">_(CPU & Memory load)_</th><th class="nine">_(Autostart)_</th><th class="five">_(Uptime)_</th></tr></thead>
|
||||
<tbody id="docker_list"><tr><td colspan='8'></td></tr></tbody>
|
||||
</table>
|
||||
<input type="button" onclick="addContainer()" value="_(Add Container)_" style="display:none">
|
||||
<input type="button" onclick="startAll()" value="_(Start All)_" style="display:none">
|
||||
<input type="button" onclick="stopAll()" value="_(Stop All)_" style="display:none">
|
||||
<input type="button" onclick="pauseAll()" value="_(Pause All)_" style="display:none">
|
||||
<input type="button" onclick="resumeAll()" value="_(Resume All)_" style="display:none">
|
||||
<input type="button" onclick="checkAll()" value="_(Check for Updates)_" id="checkAll" style="display:none">
|
||||
<input type="button" onclick="updateAll()" value="_(Update All)_" id="updateAll" style="display:none">
|
||||
<input type="button" onclick="contSizes()" value="_(Container Size)_" style="display:none">
|
||||
<div id="iframe-popup" style="display:none;-webkit-overflow-scrolling:touch;"></div>
|
||||
|
||||
<script src="<?autov('/webGui/javascript/jquery.switchbutton.js')?>"></script>
|
||||
<script src="<?autov('/plugins/dynamix.docker.manager/javascript/docker.js')?>"></script>
|
||||
<script>
|
||||
var docker = [];
|
||||
<?if (!$tabbed):?>
|
||||
$('.title').append("<span id='busy' class='red-text strong' style='display:none;margin-left:40px'><?=$busy?></span>");
|
||||
<?else:?>
|
||||
$('.tabs').append("<span id='busy' class='red-text strong' style='display:none;position:relative;top:<?=$top?>px;left:40px;font-size:1.4rem;letter-spacing:2px'><?=$busy?></span>");
|
||||
<?endif;?>
|
||||
<?if (_var($display,'resize')):?>
|
||||
function resize() {
|
||||
$('#docker_list').height(Math.max(window.innerHeight-340,330));
|
||||
$('#docker_containers thead,#docker_containers tbody').removeClass('fixed');
|
||||
$('#docker_containers thead tr th').each(function(){$(this).width($(this).width());});
|
||||
$('#docker_containers tbody tr td').each(function(){$(this).width($(this).width());});
|
||||
$('#docker_containers thead,#docker_containers tbody').addClass('fixed');
|
||||
}
|
||||
<?endif;?>
|
||||
function resetSorting() {
|
||||
if ($.cookie('lockbutton')==null) return;
|
||||
$('input[type=button]').prop('disabled',true);
|
||||
$.post('/plugins/dynamix.docker.manager/include/UserPrefs.php',{reset:true},function(){loadlist();});
|
||||
}
|
||||
function listview() {
|
||||
var more = $.cookie('docker_listview_mode')=='advanced';
|
||||
<?if(($dockercfg['DOCKER_READMORE']??'yes') === 'yes'):?>
|
||||
$('.docker_readmore').readmore({maxHeight:32,moreLink:"<a href='#' style='text-align:center'><i class='fa fa-chevron-down'></i></a>",lessLink:"<a href='#' style='text-align:center'><i class='fa fa-chevron-up'></i></a>"});
|
||||
<?endif;?>
|
||||
$('input.autostart').each(function(){
|
||||
var wait = $('#'+$(this).prop('id').replace('auto','wait'));
|
||||
var auto = $(this).prop('checked');
|
||||
if (auto && more) wait.show(); else wait.hide();
|
||||
});
|
||||
}
|
||||
function LockButton() {
|
||||
if ($.cookie('lockbutton')==null) {
|
||||
$.cookie('lockbutton','lockbutton');
|
||||
$('#resetsort').removeClass('nohand').addClass('hand');
|
||||
$('i.mover').show();
|
||||
$('#docker_list .sortable').css({'cursor':'move'});
|
||||
<?if ($themes1):?>
|
||||
$('div.nav-item.LockButton').find('a').prop('title',"_(Lock sortable items)_");
|
||||
$('div.nav-item.LockButton').find('b').removeClass('icon-u-lock green-text').addClass('icon-u-lock-open red-text');
|
||||
<?endif;?>
|
||||
$('div.nav-item.LockButton').find('span').text("_(Lock sortable items)_");
|
||||
$('#docker_list').sortable({helper:'clone',items:'.sortable',cursor:'grab',axis:'y',containment:'parent',cancel:'span.docker_readmore,input',delay:100,opacity:0.5,zIndex:9999,forcePlaceholderSize:true,
|
||||
update:function(e,ui){
|
||||
var row = $('#docker_list').find('tr:first');
|
||||
var names = ''; var index = '';
|
||||
row.parent().children().find('td.ct-name').each(function(){names+=$(this).find('.appname').text()+';';index+=$(this).parent().parent().children().index($(this).parent())+';';});
|
||||
$.post('/plugins/dynamix.docker.manager/include/UserPrefs.php',{names:names,index:index});
|
||||
}});
|
||||
} else {
|
||||
$.removeCookie('lockbutton');
|
||||
$('#resetsort').removeClass('hand').addClass('nohand');
|
||||
$('i.mover').hide();
|
||||
$('#docker_list .sortable').css({'cursor':'default'});
|
||||
<?if ($themes1):?>
|
||||
$('div.nav-item.LockButton').find('a').prop('title',"_(Unlock sortable items)_");
|
||||
$('div.nav-item.LockButton').find('b').removeClass('icon-u-lock-open red-text').addClass('icon-u-lock green-text');
|
||||
<?endif;?>
|
||||
$('div.nav-item.LockButton').find('span').text("_(Unlock sortable items)_");
|
||||
$('#docker_list').sortable('destroy');
|
||||
}
|
||||
}
|
||||
function loadlist(init) {
|
||||
timers.docker = setTimeout(function(){$('div.spinner.fixed').show('slow');},500);
|
||||
$.get('/plugins/dynamix.docker.manager/include/DockerContainers.php',function(d) {
|
||||
clearTimeout(timers.docker);
|
||||
var data = d.split(/\0/);
|
||||
$('#docker_list').html(data[0]);
|
||||
$('head').append('<script>'+data[1]+'<\/script>');
|
||||
<?if (_var($display,'resize')):?>
|
||||
resize();
|
||||
if (init) $(window).bind('resize',function(){resize();});
|
||||
<?endif;?>
|
||||
$('.iconstatus').each(function(){
|
||||
if ($(this).hasClass('stopped')) $('div.'+$(this).prop('id')).hide();
|
||||
});
|
||||
$('.autostart').switchButton({labels_placement:'right', on_label:"_(On)_", off_label:"_(Off)_"});
|
||||
$('.autostart').change(function(){
|
||||
var more = $.cookie('docker_listview_mode')=='advanced';
|
||||
var wait = $('#'+$(this).prop('id').replace('auto','wait'));
|
||||
var auto = $(this).prop('checked');
|
||||
if (auto && more) wait.show(); else wait.hide();
|
||||
$.post('/plugins/dynamix.docker.manager/include/UpdateConfig.php',{action:'autostart',container:$(this).attr('container'),auto:auto,wait:wait.find('input.wait').val()});
|
||||
});
|
||||
$('input.wait').change(function(){
|
||||
$.post('/plugins/dynamix.docker.manager/include/UpdateConfig.php',{action:'wait',container:$(this).attr('container'),wait:$(this).val()});
|
||||
});
|
||||
if ($.cookie('docker_listview_mode')=='advanced') {$('.advanced').show(); $('.basic').hide();}
|
||||
$('input[type=button]').prop('disabled',false).show('slow');
|
||||
var update = false, rebuild = false;
|
||||
for (var i=0,ct; ct=docker[i]; i++) {
|
||||
if (ct.update==1) update = true;
|
||||
if (ct.update==2) rebuild = true;
|
||||
}
|
||||
listview();
|
||||
$('div.spinner.fixed').hide('slow');
|
||||
if (data[2]==1) {$('#busy').show(); setTimeout(loadlist,5000);} else if ($('#busy').is(':visible')) {$('#busy').hide(); setTimeout(loadlist,3000);}
|
||||
if (!update) $('input#updateAll').prop('disabled',true);
|
||||
if (rebuild) rebuildAll();
|
||||
});
|
||||
}
|
||||
function contSizes() {
|
||||
// show spinner over window
|
||||
$('div.spinner.fixed').css({'z-index':'100000'}).show();
|
||||
openPlugin('container_size', "_(Container Size)_");
|
||||
}
|
||||
var dockerload = new NchanSubscriber('/sub/dockerload',{subscriber:'websocket'});
|
||||
dockerload.on('message', function(msg){
|
||||
var data = msg.split('\n');
|
||||
for (var i=0,row; row=data[i]; i++) {
|
||||
var id = row.split(';');
|
||||
var w1 = Math.round(Math.min(id[1].slice(0,-1)/<?=count($cpus)*count(preg_split('/[,-]/',$cpus[0]))?>,100)*100)/100+'%';
|
||||
$('.cpu-'+id[0]).text(w1.replace('.','<?=_var($display,'number','.,')[0]?>'));
|
||||
$('.mem-'+id[0]).text(id[2]);
|
||||
$('#cpu-'+id[0]).css('width',w1);
|
||||
}
|
||||
});
|
||||
$(function() {
|
||||
$(".tabs").append('<span class="status"><span><input type="checkbox" class="advancedview"></span></span>');
|
||||
$('.advancedview').switchButton({labels_placement:'left', on_label:"_(Advanced View)_", off_label:"_(Basic View)_", checked:$.cookie('docker_listview_mode')=='advanced'});
|
||||
$('.advancedview').change(function(){
|
||||
$('.advanced').toggle('slow');
|
||||
$('.basic').toggle('slow');
|
||||
$.cookie('docker_listview_mode',$('.advancedview').is(':checked')?'advanced':'basic',{expires:3650});
|
||||
listview();
|
||||
});
|
||||
$.removeCookie('lockbutton');
|
||||
loadlist(true);
|
||||
dockerload.start();
|
||||
});
|
||||
</script>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
||||
EXTENDED DOCKER CONFIGURATION PAGE
|
||||
Copyright (C) 2014 Guilherme Jardim
|
||||
|
||||
|
||||
FORKED FROM:
|
||||
**Copyright 2005-2014 Lime Technology.**
|
||||
|
||||
This Software is licensed under [GPL version 2](http://www.gnu.org/licenses/gpl-2.0.html).
|
||||
|
||||
Unraid is a registered trademark of [Lime Technology, Inc.](http://lime-technology.com).
|
||||
|
||||
This file shall be included in all copies or substantial portions of the Software.
|
||||
@@ -0,0 +1,25 @@
|
||||
Title="Update Container"
|
||||
Cond="(pgrep('dockerd')!==false)"
|
||||
Markdown="false"
|
||||
---
|
||||
<?PHP
|
||||
/* Copyright 2005-2020, Lime Technology
|
||||
* Copyright 2014-2020, Guilherme Jardim, Eric Schultz, Jon Panozzo.
|
||||
* Copyright 2012-2020, Bergware International.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
// add docker translations (if needed)
|
||||
if (substr($_SERVER['REQUEST_URI'],0,7) != '/Docker') {
|
||||
$docker = "$docroot/languages/$locale/docker.dot";
|
||||
if (file_exists($docker)) $language = array_merge($language,unserialize(file_get_contents($docker)));
|
||||
}
|
||||
eval('?>'.parse_file("$docroot/plugins/dynamix.docker.manager/include/CreateDocker.php"));
|
||||
?>
|
||||
@@ -0,0 +1,10 @@
|
||||
DOCKER_ENABLED="no"
|
||||
DOCKER_IMAGE_SIZE="20"
|
||||
DOCKER_LOG_ROTATION="yes"
|
||||
DOCKER_LOG_SIZE="50m"
|
||||
DOCKER_LOG_FILES="1"
|
||||
DOCKER_AUTHORING_MODE="no"
|
||||
DOCKER_USER_NETWORKS="remove"
|
||||
DOCKER_ALLOW_ACCESS=""
|
||||
DOCKER_TIMEOUT=10
|
||||
DOCKER_READMORE="yes"
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,33 @@
|
||||
<?PHP
|
||||
/* Copyright 2005-2022, Lime Technology
|
||||
* Copyright 2014-2022, Guilherme Jardim, Eric Schultz, Jon Panozzo.
|
||||
* Copyright 2012-2022, Bergware International.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
|
||||
|
||||
$user_prefs = $dockerManPaths['user-prefs'];
|
||||
$action = $_POST['action'];
|
||||
$status = $action=='start' ? 'exited' : ($action=='unpause' ? 'paused' : 'running');
|
||||
$containers = DockerUtil::docker("ps -a --filter status='$status' --format='{{.Names}}'",true);
|
||||
|
||||
if (file_exists($user_prefs)) {
|
||||
$prefs = parse_ini_file($user_prefs); $sort = [];
|
||||
foreach ($containers as $ct) $sort[] = array_search($ct,$prefs) ?? 999;
|
||||
array_multisort($sort, ($action=='start'?SORT_ASC:SORT_DESC), SORT_NUMERIC, $containers);
|
||||
}
|
||||
|
||||
foreach ($containers as $ct) {
|
||||
DockerUtil::docker("$action $ct >/dev/null");
|
||||
addRoute($ct);
|
||||
}
|
||||
?>
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,160 @@
|
||||
<?PHP
|
||||
/* Copyright 2005-2023, Lime Technology
|
||||
* Copyright 2014-2023, Guilherme Jardim, Eric Schultz, Jon Panozzo.
|
||||
* Copyright 2012-2023, Bergware International.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
|
||||
require_once "$docroot/webGui/include/Helpers.php";
|
||||
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
|
||||
|
||||
// add translations
|
||||
$_SERVER['REQUEST_URI'] = 'docker';
|
||||
require_once "$docroot/webGui/include/Translations.php";
|
||||
|
||||
$DockerClient = new DockerClient();
|
||||
$DockerTemplates = new DockerTemplates();
|
||||
$containers = $DockerClient->getDockerContainers();
|
||||
$images = $DockerClient->getDockerImages();
|
||||
$user_prefs = $dockerManPaths['user-prefs'];
|
||||
$autostart_file = $dockerManPaths['autostart-file'];
|
||||
|
||||
if (!$containers && !$images) {
|
||||
echo "<tr><td colspan='7' style='text-align:center;padding-top:12px'>"._('No Docker containers installed')."</td></tr>";
|
||||
return;
|
||||
}
|
||||
|
||||
if (file_exists($user_prefs)) {
|
||||
$prefs = @parse_ini_file($user_prefs) ?: [];
|
||||
$sort = [];
|
||||
foreach ($containers as $ct) $sort[] = array_search($ct['Name'],$prefs);
|
||||
array_multisort($sort,SORT_NUMERIC,$containers);
|
||||
unset($sort);
|
||||
}
|
||||
|
||||
// Read container info
|
||||
$allInfo = $DockerTemplates->getAllInfo();
|
||||
$docker = [];
|
||||
$null = '0.0.0.0';
|
||||
|
||||
$autostart = @file($autostart_file,FILE_IGNORE_NEW_LINES) ?: [];
|
||||
$names = array_map('var_split',$autostart);
|
||||
|
||||
function my_lang_time($text) {
|
||||
[$number, $text] = my_explode(' ',$text,2);
|
||||
return sprintf(_("%s $text"),$number);
|
||||
}
|
||||
function my_lang_log($text) {
|
||||
global $language;
|
||||
if (isset($language['healthy'])) $text = str_replace('healthy',$language['healthy'],$text);
|
||||
if (isset($language['Exited'])) $text = str_replace('Exited',$language['Exited'],$text);
|
||||
if (strpos($text,'ago')!==false) {
|
||||
[$t1,$t2] = my_explode(') ',$text);
|
||||
return $t1.'): '.my_lang_time($t2);
|
||||
}
|
||||
return _(_($text),2);
|
||||
}
|
||||
foreach ($containers as $ct) {
|
||||
$name = $ct['Name'];
|
||||
$id = $ct['Id'];
|
||||
$info = &$allInfo[$name];
|
||||
$running = $info['running'] ? 1 : 0;
|
||||
$paused = $info['paused'] ? 1 : 0;
|
||||
$is_autostart = $info['autostart'] ? 'true':'false';
|
||||
$updateStatus = substr($ct['NetworkMode'],-4)==':???' ? 2 : ($info['updated']=='true' ? 0 : ($info['updated']=='false' ? 1 : 3));
|
||||
$template = $info['template']??'';
|
||||
$shell = $info['shell']??'';
|
||||
$webGui = html_entity_decode($info['url']??'');
|
||||
$support = html_entity_decode($info['Support']??'');
|
||||
$project = html_entity_decode($info['Project']??'');
|
||||
$registry = html_entity_decode($info['registry']??'');
|
||||
$donateLink = html_entity_decode($info['DonateLink']??'');
|
||||
$readme = html_entity_decode($info['ReadMe']??'');
|
||||
$menu = sprintf("onclick=\"addDockerContainerContext('%s','%s','%s',%s,%s,%s,%s,'%s','%s','%s','%s','%s','%s', '%s','%s')\"", addslashes($name), addslashes($ct['ImageId']), addslashes($template), $running, $paused, $updateStatus, $is_autostart, addslashes($webGui), $shell, $id, addslashes($support), addslashes($project),addslashes($registry),addslashes($donateLink),addslashes($readme));
|
||||
$docker[] = "docker.push({name:'$name',id:'$id',state:$running,pause:$paused,update:$updateStatus});";
|
||||
$shape = $running ? ($paused ? 'pause' : 'play') : 'square';
|
||||
$status = $running ? ($paused ? 'paused' : 'started') : 'stopped';
|
||||
$color = $status=='started' ? 'green-text' : ($status=='paused' ? 'orange-text' : 'red-text');
|
||||
$update = $updateStatus==1 ? 'blue-text' : '';
|
||||
$icon = $info['icon'] ?: '/plugins/dynamix.docker.manager/images/question.png';
|
||||
$image = substr($icon,-4)=='.png' ? "<img src='$icon?".filemtime("$docroot{$info['icon']}")."' class='img' onerror=this.src='/plugins/dynamix.docker.manager/images/question.png';>" : (substr($icon,0,5)=='icon-' ? "<i class='$icon img'></i>" : "<i class='fa fa-$icon img'></i>");
|
||||
$wait = var_split($autostart[array_search($name,$names)]??'',1);
|
||||
$ports = [];
|
||||
foreach ($ct['Ports'] as $port) {
|
||||
$intern = $running ? ($ct['NetworkMode']=='host' ? $host : _var($port,'IP')) : $null;
|
||||
$extern = $running ? (_var($port,'NAT') ? $host : $intern) : $null;
|
||||
$ports[] = sprintf('%s:%s/%s<i class="fa fa-arrows-h" style="margin:0 6px"></i>%s:%s', $intern, _var($port,'PrivatePort'), strtoupper(_var($port,'Type')), $extern, _var($port,'PublicPort'));
|
||||
}
|
||||
$paths = [];
|
||||
$ct['Volumes'] = is_array($ct['Volumes']) ? $ct['Volumes'] : [];
|
||||
foreach ($ct['Volumes'] as $mount) {
|
||||
[$host_path,$container_path,$access_mode] = my_explode(':',$mount,3);
|
||||
$paths[] = sprintf('%s<i class="fa fa-%s" style="margin:0 6px"></i>%s', htmlspecialchars($container_path), $access_mode=='ro'?'long-arrow-left':'arrows-h', htmlspecialchars($host_path));
|
||||
}
|
||||
echo "<tr class='sortable'><td class='ct-name' style='width:220px;padding:8px'><i class='fa fa-arrows-v mover orange-text'></i>";
|
||||
if ($template) {
|
||||
$appname = "<a class='exec' onclick=\"editContainer('".addslashes(htmlspecialchars($name))."','".addslashes(htmlspecialchars($template))."')\">".htmlspecialchars($name)."</a>";
|
||||
} else {
|
||||
$appname = htmlspecialchars($name);
|
||||
}
|
||||
echo "<span class='outer'><span id='$id' $menu class='hand'>$image</span><span class='inner'><span class='appname $update'>$appname</span><br><i id='load-$id' class='fa fa-$shape $status $color'></i><span class='state'>"._($status)."</span></span></span>";
|
||||
echo "<div class='advanced' style='margin-top:8px'>"._('Container ID').": $id<br>";
|
||||
if ($ct['BaseImage']) echo "<i class='fa fa-cubes' style='margin-right:5px'></i>".htmlspecialchars(${ct['BaseImage']})."<br>";
|
||||
echo _('By').": ";
|
||||
$registry = $info['registry'];
|
||||
[$author,$version] = my_explode(':',$ct['Image']);
|
||||
if ($registry) {
|
||||
echo "<a href='".htmlspecialchars($registry)."' target='_blank'>".htmlspecialchars(compress($author,24))."</a>";
|
||||
} else {
|
||||
echo htmlspecialchars(compress($author,24));
|
||||
}
|
||||
echo "</div></td><td class='updatecolumn'>";
|
||||
switch ($updateStatus) {
|
||||
case 0:
|
||||
echo "<span class='green-text' style='white-space:nowrap;'><i class='fa fa-check fa-fw'></i> "._('up-to-date')."</span>";
|
||||
echo "<div class='advanced'><a class='exec' onclick=\"updateContainer('".addslashes(htmlspecialchars($name))."');\"><span style='white-space:nowrap;'><i class='fa fa-cloud-download fa-fw'></i> "._('force update')."</span></a></div>";
|
||||
break;
|
||||
case 1:
|
||||
echo "<div class='advanced'><span class='orange-text' style='white-space:nowrap;'><i class='fa fa-flash fa-fw'></i> "._('update ready')."</span></div>";
|
||||
echo "<a class='exec' onclick=\"updateContainer('".addslashes(htmlspecialchars($name))."');\"><span style='white-space:nowrap;'><i class='fa fa-cloud-download fa-fw'></i> "._('apply update')."</span></a>";
|
||||
break;
|
||||
case 2:
|
||||
echo "<div class='advanced'><span class='orange-text' style='white-space:nowrap;'><i class='fa fa-flash fa-fw'></i> "._('rebuild ready')."</span></div>";
|
||||
echo "<a class='exec'><span style='white-space:nowrap;'><i class='fa fa-recycle fa-fw'></i> "._('rebuilding')."</span></a>";
|
||||
break;
|
||||
default:
|
||||
echo "<span class='orange-text' style='white-space:nowrap;'><i class='fa fa-unlink'></i> "._('not available')."</span>";
|
||||
echo "<div class='advanced'><a class='exec' onclick=\"updateContainer('".addslashes(htmlspecialchars($name))."');\"><span style='white-space:nowrap;'><i class='fa fa-cloud-download fa-fw'></i> "._('force update')."</span></a></div>";
|
||||
break;
|
||||
}
|
||||
echo "<div class='advanced'><i class='fa fa-info-circle fa-fw'></i> ".compress(_($version),12,0)."</div></td>";
|
||||
echo "<td>{$ct['NetworkMode']}</td>";
|
||||
echo "<td style='white-space:nowrap'><span class='docker_readmore'>".implode('<br>',$ports)."</span></td>";
|
||||
echo "<td style='word-break:break-all'><span class='docker_readmore'>".implode('<br>',$paths)."</span></td>";
|
||||
echo "<td class='advanced'><span class='cpu-$id'>0%</span><div class='usage-disk mm'><span id='cpu-$id' style='width:0'></span><span></span></div>";
|
||||
echo "<br><span class='mem-$id'>0 / 0</span></td>";
|
||||
echo "<td><input type='checkbox' id='$id-auto' class='autostart' container='".htmlspecialchars($name)."'".($info['autostart'] ? ' checked':'').">";
|
||||
echo "<span id='$id-wait' style='float:right;display:none'>"._('wait')."<input class='wait' container='".htmlspecialchars($name)."' type='number' value='$wait' placeholder='0' title=\""._('seconds')."\"></span></td>";
|
||||
echo "<td><div style='white-space:nowrap'>".htmlspecialchars(str_replace('Up',_('Uptime').':',my_lang_log($ct['Status'])))."<div style='margin-top:4px'>"._('Created').": ".htmlspecialchars(my_lang_time($ct['Created']))."</div></div></td></tr>";
|
||||
}
|
||||
foreach ($images as $image) {
|
||||
if (count($image['usedBy'])) continue;
|
||||
$id = $image['Id'];
|
||||
$menu = sprintf("onclick=\"addDockerImageContext('%s','%s')\"", $id, implode(',',$image['Tags']));
|
||||
echo "<tr class='advanced'><td style='width:220px;padding:8px'>";
|
||||
echo "<span class='outer apps'><span id='$id' $menu class='hand'><img src='/webGui/images/disk.png' class='img'></span><span class='inner'>("._('orphan image').")<br><i class='fa fa-square stopped grey-text'></i><span class='state'>"._('stopped')."</span></span></span>";
|
||||
echo "</td><td colspan='6'>"._('Image ID').": $id<br>";
|
||||
echo implode(', ',$image['Tags']);
|
||||
echo "</td><td>"._('Created')." ".htmlspecialchars(_($image['Created'],0))."</td></tr>";
|
||||
}
|
||||
echo "\0".implode($docker)."\0".(pgrep('rc.docker')!==false ? 1:0);
|
||||
?>
|
||||
@@ -0,0 +1,22 @@
|
||||
<?PHP
|
||||
/* Copyright 2005-2022, Lime Technology
|
||||
* Copyright 2014-2022, Guilherme Jardim, Eric Schultz, Jon Panozzo.
|
||||
* Copyright 2012-2022, Bergware International.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
|
||||
|
||||
$ncsi = exec("wget --spider --no-check-certificate -nv -T10 -t1 https://www.msftncsi.com/ncsi.txt 2>&1|grep -o 'OK'")=='OK';
|
||||
$DockerTemplates = new DockerTemplates();
|
||||
if ($ncsi) $DockerTemplates->downloadTemplates();
|
||||
$DockerTemplates->getAllInfo($ncsi,$ncsi);
|
||||
?>
|
||||
@@ -0,0 +1,73 @@
|
||||
<?PHP
|
||||
/* Copyright 2005-2023, Lime Technology
|
||||
* Copyright 2014-2023, Guilherme Jardim, Eric Schultz, Jon Panozzo.
|
||||
* Copyright 2012-2023, Bergware International.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
|
||||
require_once "$docroot/webGui/include/Secure.php";
|
||||
require_once "$docroot/webGui/include/Wrappers.php";
|
||||
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
|
||||
|
||||
// add translations
|
||||
$_SERVER['REQUEST_URI'] = 'docker';
|
||||
require_once "$docroot/webGui/include/Translations.php";
|
||||
|
||||
$DockerClient = new DockerClient();
|
||||
$action = unscript(_var($_REQUEST,'action'));
|
||||
$container = unbundle(_var($_REQUEST,'container'));
|
||||
$name = unscript(_var($_REQUEST,'name'));
|
||||
$image = unscript(_var($_REQUEST,'image'));
|
||||
$arrResponse = ['error' => _('Missing parameters')];
|
||||
|
||||
switch ($action) {
|
||||
case 'start':
|
||||
if ($container) $arrResponse = ['success' => $DockerClient->startContainer($container)];
|
||||
break;
|
||||
case 'pause':
|
||||
if ($container) $arrResponse = ['success' => $DockerClient->pauseContainer($container)];
|
||||
break;
|
||||
case 'stop':
|
||||
if ($container) $arrResponse = ['success' => $DockerClient->stopContainer($container)];
|
||||
break;
|
||||
case 'resume':
|
||||
if ($container) $arrResponse = ['success' => $DockerClient->resumeContainer($container)];
|
||||
break;
|
||||
case 'restart':
|
||||
if ($container) $arrResponse = ['success' => $DockerClient->restartContainer($container)];
|
||||
break;
|
||||
case 'remove_container':
|
||||
if ($container) $arrResponse = ['success' => $DockerClient->removeContainer($name, $container, 1)];
|
||||
break;
|
||||
case 'remove_image':
|
||||
if ($image) $arrResponse = ['success' => $DockerClient->removeImage($image)];
|
||||
break;
|
||||
case 'remove_all':
|
||||
if ($container && $image) {
|
||||
// first: try to remove container
|
||||
$ret = $DockerClient->removeContainer($name, $container, 2);
|
||||
if ($ret === true) {
|
||||
// next: try to remove image
|
||||
$arrResponse = ['success' => $DockerClient->removeImage($image)];
|
||||
} else {
|
||||
// error: container failed to remove
|
||||
$arrResponse = ['success' => $ret];
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$arrResponse = ['error' => _('Unknown action')." '$action'"];
|
||||
break;
|
||||
}
|
||||
|
||||
header('Content-Type: application/json');
|
||||
die(json_encode($arrResponse));
|
||||
@@ -0,0 +1,533 @@
|
||||
<?PHP
|
||||
/* Copyright 2005-2023, Lime Technology
|
||||
* Copyright 2014-2023, Guilherme Jardim, Eric Schultz, Jon Panozzo.
|
||||
* Copyright 2012-2023, Bergware International.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
function addRoute($ct) {
|
||||
// add static route(s) for remote WireGuard access
|
||||
[$pid,$net] = array_pad(explode(' ',exec("docker inspect --format='{{.State.Pid}} {{.NetworkSettings.Networks}}' $ct")),2,'');
|
||||
$net = substr($net,4,strpos($net,':')-4);
|
||||
if (!$pid || $net != 'br0') return;
|
||||
$thisip = ipaddr();
|
||||
foreach (glob('/etc/wireguard/wg*.cfg') as $cfg) {
|
||||
$network = exec("grep -Pom1 '^Network:0=\"\\K[^\"]+' $cfg");
|
||||
if ($network) exec("nsenter -n -t $pid ip -4 route add $network via $thisip 2>/dev/null");
|
||||
}
|
||||
}
|
||||
|
||||
function xml_encode($string) {
|
||||
return htmlspecialchars($string, ENT_XML1, 'UTF-8');
|
||||
}
|
||||
|
||||
function xml_decode($string) {
|
||||
return strval(html_entity_decode($string, ENT_XML1, 'UTF-8'));
|
||||
}
|
||||
|
||||
function postToXML($post, $setOwnership=false) {
|
||||
$dom = new domDocument;
|
||||
$dom->appendChild($dom->createElement("Container"));
|
||||
$xml = simplexml_import_dom($dom);
|
||||
$xml['version'] = 2;
|
||||
$xml->Name = xml_encode(preg_replace('/\s+/', '', $post['contName']));
|
||||
$xml->Repository = xml_encode(trim($post['contRepository']));
|
||||
$xml->Registry = xml_encode(trim($post['contRegistry']));
|
||||
$xml->Network = xml_encode($post['contNetwork']);
|
||||
$xml->MyIP = xml_encode($post['contMyIP']);
|
||||
$xml->Shell = xml_encode($post['contShell']);
|
||||
$xml->Privileged = strtolower($post['contPrivileged']??'')=='on' ? 'true' : 'false';
|
||||
$xml->Support = xml_encode($post['contSupport']);
|
||||
$xml->Project = xml_encode($post['contProject']);
|
||||
$xml->Overview = xml_encode($post['contOverview']);
|
||||
$xml->Category = xml_encode($post['contCategory']);
|
||||
$xml->WebUI = xml_encode(trim($post['contWebUI']));
|
||||
$xml->TemplateURL = xml_encode($post['contTemplateURL']);
|
||||
$xml->Icon = xml_encode(trim($post['contIcon']));
|
||||
$xml->ExtraParams = xml_encode($post['contExtraParams']);
|
||||
$xml->PostArgs = xml_encode($post['contPostArgs']);
|
||||
$xml->CPUset = xml_encode($post['contCPUset']);
|
||||
$xml->DateInstalled = xml_encode(time());
|
||||
$xml->DonateText = xml_encode($post['contDonateText']);
|
||||
$xml->DonateLink = xml_encode($post['contDonateLink']);
|
||||
$xml->Requires = xml_encode($post['contRequires']);
|
||||
|
||||
$size = is_array($post['confName']) ? count($post['confName']) : 0;
|
||||
for ($i = 0; $i < $size; $i++) {
|
||||
$Type = $post['confType'][$i];
|
||||
$config = $xml->addChild('Config', xml_encode($post['confValue'][$i]));
|
||||
$config['Name'] = xml_encode($post['confName'][$i]);
|
||||
$config['Target'] = xml_encode($post['confTarget'][$i]);
|
||||
$config['Default'] = xml_encode($post['confDefault'][$i]);
|
||||
$config['Mode'] = xml_encode($post['confMode'][$i]);
|
||||
$config['Description'] = xml_encode($post['confDescription'][$i]);
|
||||
$config['Type'] = xml_encode($post['confType'][$i]);
|
||||
$config['Display'] = xml_encode($post['confDisplay'][$i]);
|
||||
$config['Required'] = xml_encode($post['confRequired'][$i]);
|
||||
$config['Mask'] = xml_encode($post['confMask'][$i]);
|
||||
}
|
||||
$dom = new DOMDocument('1.0');
|
||||
$dom->preserveWhiteSpace = false;
|
||||
$dom->formatOutput = true;
|
||||
$dom->loadXML($xml->asXML());
|
||||
return $dom->saveXML();
|
||||
}
|
||||
|
||||
function xmlToVar($xml) {
|
||||
global $subnet;
|
||||
$xml = is_file($xml) ? simplexml_load_file($xml) : simplexml_load_string($xml);
|
||||
$out = [];
|
||||
$out['Name'] = preg_replace('/\s+/', '', xml_decode($xml->Name));
|
||||
$out['Repository'] = xml_decode($xml->Repository);
|
||||
$out['Registry'] = xml_decode($xml->Registry);
|
||||
$out['Network'] = xml_decode($xml->Network);
|
||||
$out['MyIP'] = xml_decode($xml->MyIP ?? '');
|
||||
$out['Shell'] = xml_decode($xml->Shell ?? 'sh');
|
||||
$out['Privileged'] = xml_decode($xml->Privileged);
|
||||
$out['Support'] = xml_decode($xml->Support);
|
||||
$out['Project'] = xml_decode($xml->Project);
|
||||
$out['Overview'] = stripslashes(xml_decode($xml->Overview));
|
||||
$out['Category'] = xml_decode($xml->Category);
|
||||
$out['WebUI'] = xml_decode($xml->WebUI);
|
||||
$out['TemplateURL'] = xml_decode($xml->TemplateURL);
|
||||
$out['Icon'] = xml_decode($xml->Icon);
|
||||
$out['ExtraParams'] = xml_decode($xml->ExtraParams);
|
||||
$out['PostArgs'] = xml_decode($xml->PostArgs);
|
||||
$out['CPUset'] = xml_decode($xml->CPUset);
|
||||
$out['DonateText'] = xml_decode($xml->DonateText);
|
||||
$out['DonateLink'] = xml_decode($xml->DonateLink);
|
||||
$out['Requires'] = xml_decode($xml->Requires);
|
||||
$out['Config'] = [];
|
||||
if (isset($xml->Config)) {
|
||||
foreach ($xml->Config as $config) {
|
||||
$c = [];
|
||||
$c['Value'] = strlen(xml_decode($config)) ? xml_decode($config) : xml_decode($config['Default']);
|
||||
foreach ($config->attributes() as $key => $value) {
|
||||
$value = xml_decode($value);
|
||||
if ($key == 'Mode') {
|
||||
switch (xml_decode($config['Type'])) {
|
||||
case 'Path':
|
||||
$value = in_array(strtolower($value),['rw','rw,slave','rw,shared','ro','ro,slave','ro,shared']) ? $value : "rw";
|
||||
break;
|
||||
case 'Port':
|
||||
$value = in_array(strtolower($value),['tcp','udp']) ? $value : "tcp";
|
||||
break;
|
||||
}
|
||||
}
|
||||
$c[$key] = strip_tags(html_entity_decode($value));
|
||||
}
|
||||
$out['Config'][] = $c;
|
||||
}
|
||||
}
|
||||
// some xml templates advertise as V2 but omit the new <Network> element
|
||||
// check for and use the V1 <Networking> element when this occurs
|
||||
if (empty($out['Network']) && isset($xml->Networking->Mode)) {
|
||||
$out['Network'] = xml_decode($xml->Networking->Mode);
|
||||
}
|
||||
// check if network exists
|
||||
if (!key_exists($out['Network'],$subnet)) $out['Network'] = 'none';
|
||||
// V1 compatibility
|
||||
if ($xml['version'] != '2') {
|
||||
if (isset($xml->Description)) {
|
||||
$out['Overview'] = stripslashes(xml_decode($xml->Description));
|
||||
}
|
||||
if (isset($xml->Networking->Publish->Port)) {
|
||||
$portNum = 0;
|
||||
foreach ($xml->Networking->Publish->Port as $port) {
|
||||
if (empty(xml_decode($port->ContainerPort))) continue;
|
||||
$portNum += 1;
|
||||
$out['Config'][] = [
|
||||
'Name' => "Host Port {$portNum}",
|
||||
'Target' => xml_decode($port->ContainerPort),
|
||||
'Default' => xml_decode($port->HostPort),
|
||||
'Value' => xml_decode($port->HostPort),
|
||||
'Mode' => xml_decode($port->Protocol) ? xml_decode($port->Protocol) : "tcp",
|
||||
'Description' => '',
|
||||
'Type' => 'Port',
|
||||
'Display' => 'always',
|
||||
'Required' => 'true',
|
||||
'Mask' => 'false'
|
||||
];
|
||||
}
|
||||
}
|
||||
if (isset($xml->Data->Volume)) {
|
||||
$volNum = 0;
|
||||
foreach ($xml->Data->Volume as $vol) {
|
||||
if (empty(xml_decode($vol->ContainerDir))) continue;
|
||||
$volNum += 1;
|
||||
$out['Config'][] = [
|
||||
'Name' => "Host Path {$volNum}",
|
||||
'Target' => xml_decode($vol->ContainerDir),
|
||||
'Default' => xml_decode($vol->HostDir),
|
||||
'Value' => xml_decode($vol->HostDir),
|
||||
'Mode' => xml_decode($vol->Mode) ? xml_decode($vol->Mode) : "rw",
|
||||
'Description' => '',
|
||||
'Type' => 'Path',
|
||||
'Display' => 'always',
|
||||
'Required' => 'true',
|
||||
'Mask' => 'false'
|
||||
];
|
||||
}
|
||||
}
|
||||
if (isset($xml->Environment->Variable)) {
|
||||
$varNum = 0;
|
||||
foreach ($xml->Environment->Variable as $varitem) {
|
||||
if (empty(xml_decode($varitem->Name))) continue;
|
||||
$varNum += 1;
|
||||
$out['Config'][] = [
|
||||
'Name' => "Key {$varNum}",
|
||||
'Target' => xml_decode($varitem->Name),
|
||||
'Default' => xml_decode($varitem->Value),
|
||||
'Value' => xml_decode($varitem->Value),
|
||||
'Mode' => '',
|
||||
'Description' => '',
|
||||
'Type' => 'Variable',
|
||||
'Display' => 'always',
|
||||
'Required' => 'false',
|
||||
'Mask' => 'false'
|
||||
];
|
||||
}
|
||||
}
|
||||
if (isset($xml->Labels->Variable)) {
|
||||
$varNum = 0;
|
||||
foreach ($xml->Labels->Variable as $varitem) {
|
||||
if (empty(xml_decode($varitem->Name))) continue;
|
||||
$varNum += 1;
|
||||
$out['Config'][] = [
|
||||
'Name' => "Label {$varNum}",
|
||||
'Target' => xml_decode($varitem->Name),
|
||||
'Default' => xml_decode($varitem->Value),
|
||||
'Value' => xml_decode($varitem->Value),
|
||||
'Mode' => '',
|
||||
'Description' => '',
|
||||
'Type' => 'Label',
|
||||
'Display' => 'always',
|
||||
'Required' => 'false',
|
||||
'Mask' => 'false'
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
xmlSecurity($out);
|
||||
return $out;
|
||||
}
|
||||
|
||||
function xmlSecurity(&$template) {
|
||||
foreach ($template as &$element) {
|
||||
if (is_array($element)) {
|
||||
xmlSecurity($element);
|
||||
} else {
|
||||
if (is_string($element)) {
|
||||
$tempElement = htmlspecialchars_decode($element);
|
||||
$tempElement = str_replace("[","<",$tempElement);
|
||||
$tempElement = str_replace("]",">",$tempElement);
|
||||
if (preg_match('#<script(.*?)>(.*?)</script>#is',$tempElement) || preg_match('#<iframe(.*?)>(.*?)</iframe>#is',$tempElement) || (stripos($tempElement,"<link") !== false) ) {
|
||||
$element = "REMOVED";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function xmlToCommand($xml, $create_paths=false) {
|
||||
global $docroot, $var, $cfg, $driver;
|
||||
$xml = xmlToVar($xml);
|
||||
$cmdName = strlen($xml['Name']) ? '--name='.escapeshellarg($xml['Name']) : '';
|
||||
$cmdPrivileged = strtolower($xml['Privileged'])=='true' ? '--privileged=true' : '';
|
||||
$cmdNetwork = preg_match('/\-\-net(work)?=/',$xml['ExtraParams']) ? "" : '--net='.escapeshellarg(strtolower($xml['Network']));
|
||||
$cmdMyIP = '';
|
||||
foreach (explode(' ',str_replace(',',' ',$xml['MyIP'])) as $myIP) if ($myIP) $cmdMyIP .= (strpos($myIP,':')?'--ip6=':'--ip=').escapeshellarg($myIP).' ';
|
||||
$cmdCPUset = strlen($xml['CPUset']) ? '--cpuset-cpus='.escapeshellarg($xml['CPUset']) : '';
|
||||
$Volumes = [''];
|
||||
$Ports = [''];
|
||||
$Variables = [''];
|
||||
$Labels = [''];
|
||||
$Devices = [''];
|
||||
// Bind Time
|
||||
$Variables[] = 'TZ="'.$var['timeZone'].'"';
|
||||
// Add HOST_OS variable
|
||||
$Variables[] = 'HOST_OS="Unraid"';
|
||||
// Add HOST_HOSTNAME variable
|
||||
$Variables[] = 'HOST_HOSTNAME="'.$var['NAME'].'"';
|
||||
// Add HOST_CONTAINERNAME variable
|
||||
$Variables[] = 'HOST_CONTAINERNAME="'.$xml['Name'].'"';
|
||||
// Docker labels for WebUI and Icon
|
||||
$Labels[] = 'net.unraid.docker.managed=dockerman';
|
||||
if (strlen($xml['WebUI'])) $Labels[] = 'net.unraid.docker.webui='.escapeshellarg($xml['WebUI']);
|
||||
if (strlen($xml['Icon'])) $Labels[] = 'net.unraid.docker.icon='.escapeshellarg($xml['Icon']);
|
||||
|
||||
foreach ($xml['Config'] as $key => $config) {
|
||||
$confType = strtolower(strval($config['Type']));
|
||||
$hostConfig = strlen($config['Value']) ? $config['Value'] : $config['Default'];
|
||||
$containerConfig = strval($config['Target']);
|
||||
$Mode = strval($config['Mode']);
|
||||
if ($confType != "device" && !strlen($containerConfig)) continue;
|
||||
if ($confType == "path") {
|
||||
$Volumes[] = escapeshellarg($hostConfig).':'.escapeshellarg($containerConfig).':'.escapeshellarg($Mode);
|
||||
if (!file_exists($hostConfig) && $create_paths) {
|
||||
@mkdir($hostConfig, 0777, true);
|
||||
@chown($hostConfig, 99);
|
||||
@chgrp($hostConfig, 100);
|
||||
}
|
||||
} elseif ($confType == 'port') {
|
||||
switch ($driver[$xml['Network']]) {
|
||||
case 'host':
|
||||
case 'macvlan':
|
||||
case 'ipvlan':
|
||||
// Export ports as variable if network is set to host or macvlan or ipvlan
|
||||
$Variables[] = strtoupper(escapeshellarg($Mode.'_PORT_'.$containerConfig).'='.escapeshellarg($hostConfig));
|
||||
break;
|
||||
case 'bridge':
|
||||
// Export ports as port if network is set to (custom) bridge
|
||||
$Ports[] = escapeshellarg($hostConfig.':'.$containerConfig.'/'.$Mode);
|
||||
break;
|
||||
case 'none':
|
||||
// No export of ports if network is set to none
|
||||
}
|
||||
} elseif ($confType == "label") {
|
||||
$Labels[] = escapeshellarg($containerConfig).'='.escapeshellarg($hostConfig);
|
||||
} elseif ($confType == "variable") {
|
||||
$Variables[] = escapeshellarg($containerConfig).'='.escapeshellarg($hostConfig);
|
||||
} elseif ($confType == "device") {
|
||||
$Devices[] = escapeshellarg($hostConfig);
|
||||
}
|
||||
}
|
||||
$logSize = $logFile = '';
|
||||
if (($cfg['DOCKER_LOG_ROTATION']??'')=='yes') {
|
||||
$logSize = $cfg['DOCKER_LOG_SIZE'] ?? '10m';
|
||||
$logSize = "--log-opt max-size='$logSize'";
|
||||
$logFile = $cfg['DOCKER_LOG_FILES'] ?? '1';
|
||||
$logFile = "--log-opt max-file='$logFile'";
|
||||
}
|
||||
$cmd = sprintf($docroot.'/plugins/dynamix.docker.manager/scripts/docker create %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s',
|
||||
$cmdName, $cmdNetwork, $cmdMyIP, $cmdCPUset, $logSize, $logFile, $cmdPrivileged, implode(' -e ', $Variables), implode(' -l ', $Labels), implode(' -p ', $Ports), implode(' -v ', $Volumes), implode(' --device=', $Devices), $xml['ExtraParams'], escapeshellarg($xml['Repository']), $xml['PostArgs']);
|
||||
return [preg_replace('/\s\s+/', ' ', $cmd), $xml['Name'], $xml['Repository']];
|
||||
}
|
||||
function stopContainer($name, $t=false, $echo=true) {
|
||||
global $DockerClient;
|
||||
$waitID = mt_rand();
|
||||
if ($echo) {
|
||||
echo "<p class=\"logLine\" id=\"logBody\"></p>";
|
||||
echo "<script>addLog('<fieldset style=\"margin-top:1px;\" class=\"CMD\"><legend>"._('Stopping container').": ",addslashes(htmlspecialchars($name)),"</legend><p class=\"logLine\" id=\"logBody\"></p><span id=\"wait{$waitID}\">"._('Please wait')." </span></fieldset>');show_Wait($waitID);</script>\n";
|
||||
@flush();
|
||||
}
|
||||
$retval = $DockerClient->stopContainer($name, $t);
|
||||
$out = ($retval === true) ? _('Successfully stopped container')." '$name'" : _('Error').": ".$retval;
|
||||
if ($echo) {
|
||||
echo "<script>stop_Wait($waitID);addLog('<b>",addslashes(htmlspecialchars($out)),"</b>');</script>\n";
|
||||
@flush();
|
||||
}
|
||||
}
|
||||
|
||||
function removeContainer($name, $cache=false, $echo=true) {
|
||||
global $DockerClient;
|
||||
$waitID = mt_rand();
|
||||
if ($echo) {
|
||||
echo "<p class=\"logLine\" id=\"logBody\"></p>";
|
||||
echo "<script>addLog('<fieldset style=\"margin-top:1px;\" class=\"CMD\"><legend>"._('Removing container').": ",addslashes(htmlspecialchars($name)),"</legend><p class=\"logLine\" id=\"logBody\"></p><span id=\"wait{$waitID}\">"._('Please wait')." </span></fieldset>');show_Wait($waitID);</script>\n";
|
||||
@flush();
|
||||
}
|
||||
$retval = $DockerClient->removeContainer($name, false, $cache);
|
||||
$out = ($retval === true) ? _('Successfully removed container')." '$name'" : _('Error').": ".$retval;
|
||||
if ($echo) {
|
||||
echo "<script>stop_Wait($waitID);addLog('<b>",addslashes(htmlspecialchars($out)),"</b>');</script>\n";
|
||||
@flush();
|
||||
}
|
||||
}
|
||||
|
||||
function removeImage($image, $echo=true) {
|
||||
global $DockerClient;
|
||||
$waitID = mt_rand();
|
||||
if ($echo) {
|
||||
echo "<p class=\"logLine\" id=\"logBody\"></p>";
|
||||
echo "<script>addLog('<fieldset style=\"margin-top:1px;\" class=\"CMD\"><legend>"._('Removing orphan image').": ",addslashes(htmlspecialchars($image)),"</legend><p class=\"logLine\" id=\"logBody\"></p><span id=\"wait{$waitID}\">"._('Please wait')." </span></fieldset>');show_Wait($waitID);</script>\n";
|
||||
@flush();
|
||||
}
|
||||
$retval = $DockerClient->removeImage($image);
|
||||
$out = ($retval === true) ? _('Successfully removed orphan image')." '$image'" : _('Error').": ".$retval;
|
||||
if ($echo) {
|
||||
echo "<script>stop_Wait($waitID);addLog('<b>",addslashes(htmlspecialchars($out))."</b>');</script>\n";
|
||||
@flush();
|
||||
}
|
||||
}
|
||||
|
||||
function pullImage($name, $image, $echo=true) {
|
||||
global $DockerClient, $DockerTemplates, $DockerUpdate;
|
||||
$waitID = mt_rand();
|
||||
if (!preg_match("/:\S+$/", $image)) $image .= ":latest";
|
||||
if ($echo) {
|
||||
echo "<p class=\"logLine\" id=\"logBody\"></p>";
|
||||
echo "<script>addLog('<fieldset style=\"margin-top:1px;\" class=\"CMD\"><legend>"._('Pulling image').": ",addslashes(htmlspecialchars($image)),"</legend><p class=\"logLine\" id=\"logBody\"></p><span id=\"wait{$waitID}\">"._('Please wait')." </span></fieldset>');show_Wait($waitID);</script>\n";
|
||||
@flush();
|
||||
}
|
||||
$alltotals = [];
|
||||
$laststatus = [];
|
||||
$strError = '';
|
||||
$DockerClient->pullImage($image, function ($line) use (&$alltotals, &$laststatus, &$waitID, &$strError, $image, $DockerClient, $DockerUpdate, $echo) {
|
||||
$cnt = json_decode($line, true);
|
||||
$id = $cnt['id'] ?? '';
|
||||
$status = $cnt['status'] ?? '';
|
||||
if (isset($cnt['error'])) $strError = $cnt['error'];
|
||||
if ($waitID !== false) {
|
||||
if ($echo) {
|
||||
echo "<script>stop_Wait($waitID);</script>\n";
|
||||
@flush();
|
||||
}
|
||||
$waitID = false;
|
||||
}
|
||||
if (empty($status)) return;
|
||||
if (!empty($id)) {
|
||||
if (!empty($cnt['progressDetail']) && !empty($cnt['progressDetail']['total'])) {
|
||||
$alltotals[$id] = $cnt['progressDetail']['total'];
|
||||
}
|
||||
if (empty($laststatus[$id])) {
|
||||
$laststatus[$id] = '';
|
||||
}
|
||||
switch ($status) {
|
||||
case 'Waiting':
|
||||
// Omit
|
||||
break;
|
||||
case 'Downloading':
|
||||
if ($laststatus[$id] != $status) {
|
||||
if ($echo) echo "<script>addToID('$id','",addslashes(htmlspecialchars($status)),"');</script>\n";
|
||||
}
|
||||
$total = $cnt['progressDetail']['total'];
|
||||
$current = $cnt['progressDetail']['current'];
|
||||
if ($total > 0) {
|
||||
$percentage = round(($current / $total) * 100);
|
||||
if ($echo) echo "<script>progress('$id',' ",$percentage,"% ",_('of')," ",$DockerClient->formatBytes($total),"');</script>\n";
|
||||
} else {
|
||||
// Docker must not know the total download size (http-chunked or something?)
|
||||
// just show the current download progress without the percentage
|
||||
$alltotals[$id] = $current;
|
||||
if ($echo) echo "<script>progress('$id',' ",$DockerClient->formatBytes($current),"');</script>\n";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if ($laststatus[$id] == "Downloading") {
|
||||
if ($echo) echo "<script>progress('$id',' 100% ",_('of')," ",$DockerClient->formatBytes($alltotals[$id]),"');</script>\n";
|
||||
}
|
||||
if ($laststatus[$id] != $status) {
|
||||
if ($echo) echo "<script>addToID('",($id=='latest'?mt_rand():$id),"','",addslashes(htmlspecialchars($status)),"');</script>\n";
|
||||
}
|
||||
break;
|
||||
}
|
||||
$laststatus[$id] = $status;
|
||||
} else {
|
||||
if (strpos($status, 'Status: ') === 0) {
|
||||
if ($echo) echo "<script>addLog('",addslashes(htmlspecialchars($status)),"');</script>\n";
|
||||
}
|
||||
if (strpos($status, 'Digest: ') === 0) {
|
||||
$DockerUpdate->setUpdateStatus($image, substr($status, 8));
|
||||
}
|
||||
}
|
||||
if ($echo) @flush();
|
||||
});
|
||||
if ($echo) {
|
||||
echo "<script>addLog('<br><b>",_('TOTAL DATA PULLED'),":</b> ",$DockerClient->formatBytes(array_sum($alltotals)),"');</script>\n";
|
||||
@flush();
|
||||
}
|
||||
if (!empty($strError)) {
|
||||
if ($echo) {
|
||||
echo "<script>addLog('<br><span class=\"error\"><b>",_('Error'),":</b> ",addslashes(htmlspecialchars($strError)),"</span>');</script>\n";
|
||||
@flush();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function execCommand($command, $echo=true) {
|
||||
$waitID = mt_rand();
|
||||
if ($echo) {
|
||||
[$cmd,$args] = explode(' ',$command,2);
|
||||
echo '<p class="logLine" id="logBody"></p>';
|
||||
echo '<script>addLog(\'<fieldset style="margin-top:1px;" class="CMD"><legend>',_('Command execution'),'</legend>';
|
||||
echo basename($cmd),' ',str_replace(" -","<br> -",addslashes(htmlspecialchars($args))),'<br>';
|
||||
echo '<span id="wait'.$waitID.'">',_('Please wait').' </span>';
|
||||
echo '<p class="logLine" id="logBody"></p></fieldset>\');show_Wait('.$waitID.');</script>';
|
||||
@flush();
|
||||
}
|
||||
$proc = popen("$command 2>&1",'r');
|
||||
while ($out = fgets($proc)) {
|
||||
$out = preg_replace("%[\t\n\x0B\f\r]+%", '', $out);
|
||||
if ($echo) {
|
||||
echo '<script>addLog("',htmlspecialchars($out),'");</script>';
|
||||
@flush();
|
||||
}
|
||||
}
|
||||
$retval = pclose($proc);
|
||||
if ($echo) echo '<script>stop_Wait('.$waitID.');</script>';
|
||||
$out = $retval ? _('The command failed').'.' : _('The command finished successfully').'!';
|
||||
if ($echo) echo '<script>addLog(\'<br><b>',$out,'</b>\');</script>';
|
||||
return $retval===0;
|
||||
}
|
||||
|
||||
function getXmlVal($xml, $element, $attr=null, $pos=0) {
|
||||
$xml = (is_file($xml)) ? simplexml_load_file($xml) : simplexml_load_string($xml);
|
||||
$element = $xml->xpath("//$element")[$pos];
|
||||
return isset($element) ? (isset($element[$attr]) ? strval($element[$attr]) : strval($element)) : "";
|
||||
}
|
||||
|
||||
function setXmlVal(&$xml, $value, $el, $attr=null, $pos=0) {
|
||||
$xml = (is_file($xml)) ? simplexml_load_file($xml) : simplexml_load_string($xml);
|
||||
$element = $xml->xpath("//$el")[$pos];
|
||||
if (!isset($element)) $element = $xml->addChild($el);
|
||||
if ($attr) {
|
||||
$element[$attr] = $value;
|
||||
} else {
|
||||
$element->{0} = $value;
|
||||
}
|
||||
$dom = new DOMDocument('1.0');
|
||||
$dom->preserveWhiteSpace = false;
|
||||
$dom->formatOutput = true;
|
||||
$dom->loadXML($xml->asXML());
|
||||
$xml = $dom->saveXML();
|
||||
}
|
||||
|
||||
function getAllocations() {
|
||||
global $DockerClient, $host;
|
||||
|
||||
$ports = [];
|
||||
foreach ($DockerClient->getDockerContainers() as $ct) {
|
||||
$list = $port = [];
|
||||
$nat = $ip = false;
|
||||
$list['Name'] = $ct['Name'];
|
||||
foreach ($ct['Ports'] as $tmp) {
|
||||
$nat = $tmp['NAT'];
|
||||
$ip = $tmp['IP'];
|
||||
$port[] = $tmp['PublicPort'];
|
||||
}
|
||||
sort($port);
|
||||
$ip = $ct['NetworkMode']=='host'||$nat ? $host : ($ip ?: DockerUtil::myIP($ct['Name']) ?: '0.0.0.0');
|
||||
$list['Port'] = "<span class='net'>{$ct['NetworkMode']}</span><span class='ip'>$ip</span>".(implode(', ',array_unique($port)) ?: '???');
|
||||
$ports[] = $list;
|
||||
}
|
||||
return $ports;
|
||||
}
|
||||
|
||||
function getCurlHandle($url, $method='GET') {
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_FRESH_CONNECT, true);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 45);
|
||||
curl_setopt($ch, CURLOPT_ENCODING, "");
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($ch, CURLOPT_REFERER, "");
|
||||
if ($method === 'HEAD') {
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'HEAD');
|
||||
curl_setopt($ch, CURLOPT_HEADER, true);
|
||||
curl_setopt($ch, CURLOPT_NOBODY, true);
|
||||
}
|
||||
return $ch;
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,44 @@
|
||||
<?PHP
|
||||
/* Copyright 2005-2018, Lime Technology
|
||||
* Copyright 2012-2018, Bergware International.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
function ports_only($key) {
|
||||
return preg_match('/_(ETH|BR|BOND)[0-9]/',$key);
|
||||
}
|
||||
$ports = array_filter($_POST,'ports_only',ARRAY_FILTER_USE_KEY);
|
||||
$purge = [];
|
||||
foreach ($ports as $port => $val) {
|
||||
$port = explode('_',$port,3)[2];
|
||||
if (!in_array($port,$purge)) $purge[] = $port;
|
||||
}
|
||||
foreach ($purge as $port) {
|
||||
switch (substr($port,0,2)) {
|
||||
case 'ET':
|
||||
$A1 = str_replace('ETH','BR',$port);
|
||||
$A2 = str_replace('ETH','BOND',$port);
|
||||
break;
|
||||
case 'BR':
|
||||
$A1 = str_replace('BR','ETH',$port);
|
||||
$A2 = str_replace('BR','BOND',$port);
|
||||
break;
|
||||
case 'BO':
|
||||
$A1 = str_replace('BOND','BR',$port);
|
||||
$A2 = str_replace('BOND','ETH',$port);
|
||||
break;
|
||||
}
|
||||
unset($keys["DOCKER_AUTO_$A1"], $keys["DOCKER_AUTO_$A2"]);
|
||||
unset($keys["DOCKER_DHCP_$A1"], $keys["DOCKER_DHCP6_$A1"], $keys["DOCKER_DHCP_$A2"], $keys["DOCKER_DHCP6_$A2"]);
|
||||
unset($keys["DOCKER_SUBNET_$A1"], $keys["DOCKER_SUBNET6_$A1"], $keys["DOCKER_SUBNET_$A2"], $keys["DOCKER_SUBNET6_$A2"]);
|
||||
unset($keys["DOCKER_GATEWAY_$A1"], $keys["DOCKER_GATEWAY6_$A1"], $keys["DOCKER_GATEWAY_$A2"], $keys["DOCKER_GATEWAY6_$A2"]);
|
||||
unset($keys["DOCKER_RANGE_$A1"], $keys["DOCKER_RANGE6_$A1"], $keys["DOCKER_RANGE_$A2"], $keys["DOCKER_RANGE6_$A2"]);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,69 @@
|
||||
<?PHP
|
||||
/* Copyright 2005-2021, Lime Technology
|
||||
* Copyright 2014-2021, Guilherme Jardim, Eric Schultz, Jon Panozzo.
|
||||
* Copyright 2012-2021, Bergware International.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
|
||||
|
||||
$autostart_file = $dockerManPaths['autostart-file'];
|
||||
$template_repos = $dockerManPaths['template-repos'];
|
||||
$user_prefs = $dockerManPaths['user-prefs'];
|
||||
|
||||
switch ($_POST['action']) {
|
||||
case 'autostart':
|
||||
// update container autostart setting
|
||||
$container = urldecode(($_POST['container']));
|
||||
$wait = $_POST['wait'];
|
||||
$item = rtrim("$container $wait");
|
||||
$autostart = @file($autostart_file, FILE_IGNORE_NEW_LINES) ?: [];
|
||||
$key = array_search($item, $autostart);
|
||||
if ($_POST['auto']=='true') {
|
||||
if ($key===false) $autostart[] = $item;
|
||||
} else {
|
||||
unset($autostart[$key]);
|
||||
}
|
||||
if ($autostart) {
|
||||
if (file_exists($user_prefs)) {
|
||||
$prefs = parse_ini_file($user_prefs); $sort = [];
|
||||
foreach ($autostart as $ct) $sort[] = array_search(var_split($ct),$prefs) ?? 999;
|
||||
array_multisort($sort,$autostart);
|
||||
} else {
|
||||
natcasesort($autostart);
|
||||
}
|
||||
file_put_contents($autostart_file, implode("\n", $autostart)."\n");
|
||||
} else @unlink($autostart_file);
|
||||
break;
|
||||
case 'wait':
|
||||
// update wait period used after container autostart
|
||||
$container = urldecode(($_POST['container']));
|
||||
$wait = $_POST['wait'];
|
||||
$item = rtrim("$container $wait");
|
||||
$autostart = file($autostart_file, FILE_IGNORE_NEW_LINES) ?: [];
|
||||
$names = array_map('var_split', $autostart);
|
||||
$autostart[array_search($container,$names)] = $item;
|
||||
file_put_contents($autostart_file, implode("\n", $autostart)."\n");
|
||||
break;
|
||||
case 'templates':
|
||||
// update template
|
||||
readfile("$docroot/update.htm");
|
||||
file_put_contents($template_repos, $_POST['template_repos']);
|
||||
$DockerTemplates = new DockerTemplates();
|
||||
$DockerTemplates->downloadTemplates();
|
||||
break;
|
||||
case 'exist':
|
||||
// docker file or folder exists?
|
||||
$file = $_POST['name'];
|
||||
if (substr($file,0,5)=='/mnt/') echo file_exists($file) ? 0 : 1;
|
||||
break;
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,45 @@
|
||||
<?PHP
|
||||
/* Copyright 2005-2018, Lime Technology
|
||||
* Copyright 2015-2018, Derek Macias, Eric Schultz, Jon Panozzo.
|
||||
* Copyright 2012-2018, Bergware International.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
|
||||
|
||||
$autostart_file = $dockerManPaths['autostart-file'];
|
||||
$user_prefs = $dockerManPaths['user-prefs'];
|
||||
|
||||
if (isset($_POST['reset'])) {
|
||||
@unlink($user_prefs);
|
||||
if (file_exists($autostart_file)) {
|
||||
$allAutoStart = file($autostart_file, FILE_IGNORE_NEW_LINES);
|
||||
natcasesort($allAutoStart);
|
||||
file_put_contents($autostart_file, implode(PHP_EOL, $allAutoStart).PHP_EOL);
|
||||
}
|
||||
} else {
|
||||
$names = explode(';',$_POST['names']);
|
||||
$index = explode(';',$_POST['index']);
|
||||
$save = []; $i = 0;
|
||||
|
||||
foreach ($names as $name) if ($name) $save[] = $index[$i++]."=\"".$name."\""; else $i++;
|
||||
file_put_contents($user_prefs, implode("\n",$save)."\n");
|
||||
|
||||
// sort containers for start-up
|
||||
if (file_exists($autostart_file)) {
|
||||
$prefs = parse_ini_file($user_prefs); $sort = [];
|
||||
$allAutoStart = file($autostart_file, FILE_IGNORE_NEW_LINES);
|
||||
foreach ($allAutoStart as $ct) $sort[] = array_search(explode(' ',$ct)[0],$prefs) ?? 999;
|
||||
array_multisort($sort,SORT_NUMERIC,$allAutoStart);
|
||||
file_put_contents($autostart_file, implode(PHP_EOL, $allAutoStart).PHP_EOL);
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,155 @@
|
||||
var pathNum = 2;
|
||||
var portNum = 0;
|
||||
var varNum = 0;
|
||||
var currentPath = "/mnt/";
|
||||
|
||||
if (!String.prototype.format) {
|
||||
String.prototype.format = function() {
|
||||
var args = arguments;
|
||||
return this.replace(/{(\d+)}/g, function(match, number) {
|
||||
return typeof args[number] != 'undefined' ? args[number] : match;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function rmTemplate(tmpl) {
|
||||
var name = tmpl.split(/[\/]+/).pop();
|
||||
swal({title:"Are you sure?",text:"Remove template: "+name,type:"warning",html:true,showCancelButton:true},function(){$("#rmTemplate").val(tmpl);$("#formTemplate").submit();});
|
||||
}
|
||||
|
||||
function toggleBrowser(N) {
|
||||
var el = $('#fileTree' + N);
|
||||
if (el.is(':visible')) {
|
||||
hideBrowser(N);
|
||||
} else {
|
||||
$( el ).fileTree({
|
||||
root: currentPath,
|
||||
filter: 'HIDE_FILES_FILTER'
|
||||
},
|
||||
function(file) {},
|
||||
function(folder) {
|
||||
$("#hostPath" + N).val(folder);
|
||||
});
|
||||
$( el ).slideDown('fast');
|
||||
}
|
||||
}
|
||||
|
||||
function hideBrowser(N) {
|
||||
$("#fileTree" + N).slideUp('fast', function () {
|
||||
$(this).html("");
|
||||
});
|
||||
}
|
||||
|
||||
function addPort(frm) {
|
||||
portNum++;
|
||||
var hostPort = $("#hostPort1");
|
||||
var containerPort = $("#containerPort1");
|
||||
var portProtocol = $("#portProtocol1");
|
||||
|
||||
var select = "";
|
||||
if (portProtocol.val() == "udp"){
|
||||
select = "selected";
|
||||
}
|
||||
|
||||
var row = [
|
||||
'<tr id="portNum{0}" style="display: none;">',
|
||||
'<td>',
|
||||
'<input type="number" min="1" max="65535" name="containerPort[]" value="{2}" class="textPort" title="Set the port your app uses inside the container.">',
|
||||
'</td>',
|
||||
'<td>',
|
||||
'<input type="number" min="1" max="65535" name="hostPort[]" value="{1}" class="textPort" title="Set the port you use to interact with the app.">',
|
||||
'</td>',
|
||||
'<td>',
|
||||
'<select name="portProtocol[]">',
|
||||
'<option value="tcp">tcp</option>',
|
||||
'<option value="udp" {3}>udp</option>',
|
||||
'</select>',
|
||||
'</td>',
|
||||
'<td>',
|
||||
'<input type="button" value="Remove" onclick="removePort({0});">',
|
||||
'</td>',
|
||||
'</tr>'
|
||||
].join('').format(portNum, hostPort.val(), containerPort.val(), select);
|
||||
|
||||
$(row).appendTo('#portRows').fadeIn("fast");
|
||||
hostPort.val('');
|
||||
containerPort.val('');
|
||||
portProtocol.val('tcp');
|
||||
}
|
||||
|
||||
function removePort(rnum) {
|
||||
$('#portNum' + rnum).fadeOut("fast", function() { $(this).remove(); });
|
||||
}
|
||||
|
||||
function addPath(frm) {
|
||||
pathNum++;
|
||||
var hostPath = $("#hostPath1");
|
||||
var containerPath = $("#containerPath1");
|
||||
var hostWritable = $("#hostWritable1");
|
||||
|
||||
var select = "";
|
||||
if (hostWritable.val() == "ro"){
|
||||
select = "selected";
|
||||
}
|
||||
|
||||
var row = [
|
||||
'<tr id="pathNum{0}" style="display: none;">',
|
||||
'<td>',
|
||||
'<input type="text" name="containerPath[]" value="{2}" class="textPath" onclick="hideBrowser({0});" title="The directory your app uses inside the container. Ex: /config">',
|
||||
'</td>',
|
||||
'<td>',
|
||||
'<input type="text" id="hostPath{0}" name="hostPath[]" value="{1}" class="textPath" onclick="toggleBrowser({0});" title="The directory in your array the app have access to. Ex: /mnt/user/Movies"/>',
|
||||
'<div id="fileTree{0}" class="fileTree"></div>',
|
||||
'</td>',
|
||||
'<td>',
|
||||
'<select name="hostWritable[]">',
|
||||
'<option value="rw">Read/Write</option>',
|
||||
'<option value="ro" {3}>Read Only</option>',
|
||||
'</select>',
|
||||
'</td>',
|
||||
'<td>',
|
||||
'<input type="button" value="Remove" onclick="removePath({0});"></td></tr>',
|
||||
'</td>',
|
||||
'</tr>'
|
||||
].join('').format(pathNum, hostPath.val(), containerPath.val(), select);
|
||||
|
||||
$(row).appendTo('#pathRows tbody').fadeIn("fast");
|
||||
hostPath.val('');
|
||||
containerPath.val('');
|
||||
hostWritable.val('rw');
|
||||
}
|
||||
|
||||
function removePath(rnum) {
|
||||
$('#pathNum' + rnum).fadeOut("fast", function() { $(this).remove(); });
|
||||
}
|
||||
|
||||
function addEnv(frm) {
|
||||
varNum++;
|
||||
var VariableName = $("#VariableName1");
|
||||
var VariableValue = $("#VariableValue1");
|
||||
|
||||
var row = [
|
||||
'<tr id="varNum{0}" style="display: none;">',
|
||||
'<td>',
|
||||
'<input type="text" name="VariableName[]" value="{1}" class="textEnv">',
|
||||
'</td>',
|
||||
'<td>',
|
||||
'<input type="text" name="VariableValue[]" value="{2}" class="textEnv">',
|
||||
'<input type="button" value="Remove" onclick="removeEnv({0});">',
|
||||
'</td>',
|
||||
'</tr>'
|
||||
].join('').format(varNum, VariableName.val(), VariableValue.val());
|
||||
|
||||
$(row).appendTo('#envRows tbody').fadeIn("fast");
|
||||
VariableName.val('');
|
||||
VariableValue.val('');
|
||||
}
|
||||
|
||||
function removeEnv(rnum) {
|
||||
$('#varNum' + rnum).fadeOut("fast", function() { $(this).remove(); });
|
||||
}
|
||||
|
||||
function toggleMode(){
|
||||
$("#toggleMode").toggleClass("fa-toggle-off fa-toggle-on");
|
||||
$(".additionalFields").slideToggle();
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
var eventURL = '/plugins/dynamix.docker.manager/include/Events.php';
|
||||
|
||||
function addDockerContainerContext(container, image, template, started, paused, update, autostart, webui, shell, id, Support, Project, Registry, donateLink, ReadMe) {
|
||||
var opts = [];
|
||||
context.settings({right:false,above:false});
|
||||
if (started && !paused) {
|
||||
if (webui !== '' && webui != '#') opts.push({text:_('WebUI'), icon:'fa-globe', href:webui, target:'_blank'});
|
||||
opts.push({text:_('Console'), icon:'fa-terminal', action:function(e){e.preventDefault(); openTerminal('docker',container,shell);}});
|
||||
opts.push({divider:true});
|
||||
}
|
||||
if (update==1) {
|
||||
opts.push({text:_('Update'), icon:'fa-cloud-download', action:function(e){e.preventDefault(); updateContainer(container);}});
|
||||
opts.push({divider:true});
|
||||
}
|
||||
if (started) {
|
||||
if (paused) {
|
||||
opts.push({text:_('Resume'), icon:'fa-play', action:function(e){e.preventDefault(); eventControl({action:'resume', container:id}, 'loadlist');}});
|
||||
} else {
|
||||
opts.push({text:_('Stop'), icon:'fa-stop', action:function(e){e.preventDefault(); eventControl({action:'stop', container:id}, 'loadlist');}});
|
||||
opts.push({text:_('Pause'), icon:'fa-pause', action:function(e){e.preventDefault(); eventControl({action:'pause', container:id}, 'loadlist');}});
|
||||
}
|
||||
opts.push({text:_('Restart'), icon:'fa-refresh', action:function(e){e.preventDefault(); eventControl({action:'restart', container:id}, 'loadlist');}});
|
||||
} else {
|
||||
opts.push({text:_('Start'), icon:'fa-play', action:function(e){e.preventDefault(); eventControl({action:'start', container:id}, 'loadlist');}});
|
||||
}
|
||||
opts.push({divider:true});
|
||||
opts.push({text:_('Logs'), icon:'fa-navicon', action:function(e){e.preventDefault(); openTerminal('docker',container,'.log');}});
|
||||
if (template) {
|
||||
opts.push({text:_('Edit'), icon:'fa-wrench', action:function(e){e.preventDefault(); editContainer(container, template);}});
|
||||
}
|
||||
opts.push({text:_('Remove'), icon:'fa-trash', action:function(e){e.preventDefault(); rmContainer(container, image, id);}});
|
||||
if (ReadMe||Project||Support||Registry) {
|
||||
opts.push({divider:true});
|
||||
}
|
||||
if (ReadMe) {
|
||||
opts.push({text:_('Read Me First'), icon:'fa-book', href:ReadMe, target:'_blank'});
|
||||
}
|
||||
if (Project) {
|
||||
opts.push({text:_('Project Page'), icon:'fa-life-ring', href:Project, target:'_blank'});
|
||||
}
|
||||
if (Support) {
|
||||
opts.push({text:_('Support'), icon:'fa-question', href:Support, target:'_blank'});
|
||||
}
|
||||
if (Registry) {
|
||||
opts.push({text:_('More Info'),icon:'fa-info-circle', href:Registry, target:'_blank'});
|
||||
}
|
||||
if (donateLink) {
|
||||
opts.push({divider:true});
|
||||
opts.push({text:_('Donate'),icon:'fa-external-link', href:donateLink,target:'_blank'});
|
||||
}
|
||||
context.attach('#'+id, opts);
|
||||
}
|
||||
function addDockerImageContext(image, imageTag) {
|
||||
var opts = [];
|
||||
opts.push({text:_('Remove'), icon:'fa-trash', action:function(e){e.preventDefault(); rmImage(image, imageTag);}});
|
||||
context.attach('#'+image, opts);
|
||||
}
|
||||
function popupWithIframe(title, cmd, reload, func) {
|
||||
pauseEvents();
|
||||
$('#iframe-popup').html('<iframe id="myIframe" frameborder="0" scrolling="yes" width="100%" height="99%"></iframe>');
|
||||
$('#iframe-popup').dialog({
|
||||
autoOpen:true,
|
||||
title:title,
|
||||
draggable:true,
|
||||
width: Math.min(Math.max(window.innerWidth/2,900),1600),
|
||||
height: Math.max(window.innerHeight*3/5,600),
|
||||
resizable:true,
|
||||
modal:true,
|
||||
show:{effect:'fade', duration:250},
|
||||
hide:{effect:'fade', duration:250},
|
||||
open:function(ev, ui) {
|
||||
$('#myIframe').attr('src', cmd);
|
||||
},
|
||||
close:function(event, ui) {
|
||||
if (reload && !$('#myIframe').contents().find('#canvas').length) {
|
||||
if (func) setTimeout(func+'()',0); else location = window.location.href;
|
||||
} else {
|
||||
resumeEvents();
|
||||
}
|
||||
}
|
||||
});
|
||||
$(".ui-dialog .ui-dialog-titlebar").addClass('menu');
|
||||
$('.ui-dialog .ui-dialog-titlebar-close').text('X').prop('title',_('Close'));
|
||||
$(".ui-dialog .ui-dialog-title").css({'text-align':'center','width':'100%'});
|
||||
$(".ui-dialog .ui-dialog-content").css({'padding-top':'15px','vertical-align':'bottom'});
|
||||
}
|
||||
function execUpContainer(container) {
|
||||
var title = _('Updating the container')+': '+container;
|
||||
var cmd = '/plugins/dynamix.docker.manager/include/CreateDocker.php?updateContainer=true&ct[]='+encodeURIComponent(container);
|
||||
popupWithIframe(title, cmd, true, 'loadlist');
|
||||
}
|
||||
function addContainer() {
|
||||
var path = location.pathname;
|
||||
var x = path.indexOf('?');
|
||||
if (x!=-1) path = path.substring(0,x);
|
||||
location = path+'/AddContainer';
|
||||
}
|
||||
function editContainer(container, template) {
|
||||
var path = location.pathname;
|
||||
var x = path.indexOf('?');
|
||||
if (x!=-1) path = path.substring(0, x);
|
||||
location = path+'/UpdateContainer?xmlTemplate=edit:'+template;
|
||||
}
|
||||
function updateContainer(container) {
|
||||
swal({
|
||||
title:_('Are you sure?'),text:_('Update container')+': '+container, type:'warning',html:true,showCancelButton:true,closeOnConfirm:false,confirmButtonText:_('Yes, update it!'),cancelButtonText:_('Cancel')
|
||||
},function(){
|
||||
openDocker('update_container '+encodeURIComponent(container),_('Updating the container'),'','loadlist');
|
||||
});
|
||||
}
|
||||
function rmContainer(container, image, id) {
|
||||
var body = _('Remove container')+': '+container+'<br><br><label><input id="removeimagechk" type="checkbox" checked style="display:inline;width:unset;height:unset;margin-top:unset;margin-bottom:unset">'+_('also remove image')+'</label>';
|
||||
$('input[type=button]').prop('disabled',true);
|
||||
swal({
|
||||
title:_('Are you sure?'),text:body,type:'warning',html:true,showCancelButton:true,confirmButtonText:_('Yes, delete it!'),cancelButtonText:_('Cancel'),showLoaderOnConfirm:true
|
||||
},function(c){
|
||||
if (!c) {setTimeout(loadlist); return;}
|
||||
$('div.spinner.fixed').show('slow');
|
||||
if ($('#removeimagechk').prop('checked')) {
|
||||
eventControl({action:'remove_all', container:id, name:container, image:image},'loadlist');
|
||||
} else {
|
||||
eventControl({action:'remove_container', container:id, name:container},'loadlist');
|
||||
}
|
||||
});
|
||||
}
|
||||
function rmImage(image, imageName) {
|
||||
var body = _('Remove image')+': '+$('<textarea />').html(imageName).text();
|
||||
$('input[type=button]').prop('disabled',true);
|
||||
swal({
|
||||
title:_('Are you sure?'),text:body,type:'warning',html:true,showCancelButton:true,confirmButtonText:_('Yes, delete it!'),cancelButtonText:_('Cancel'),showLoaderOnConfirm:true
|
||||
},function(c){
|
||||
if (!c) {setTimeout(loadlist,0); return;}
|
||||
$('div.spinner.fixed').show('slow');
|
||||
eventControl({action:'remove_image', image:image},'loadlist');
|
||||
});
|
||||
}
|
||||
function eventControl(params, spin) {
|
||||
if (spin) $('#'+params['container']).parent().find('i').removeClass('fa-play fa-square fa-pause').addClass('fa-refresh fa-spin');
|
||||
$.post(eventURL, params, function(data) {
|
||||
$('div.spinner.fixed').hide('slow');
|
||||
if (data.success === true) {
|
||||
if (spin) setTimeout(spin+'()',500); else location=window.location.href;
|
||||
} else {
|
||||
setTimeout(function(){
|
||||
swal({
|
||||
title:_('Execution error'),text:data.success,type:'error',html:true,confirmButtonText:_('Ok')
|
||||
},function(){
|
||||
if (spin) setTimeout(spin+'()',500); else location=window.location.href;
|
||||
});
|
||||
},100);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
function startAll() {
|
||||
$('input[type=button]').prop('disabled',true);
|
||||
for (var i=0,ct; ct=docker[i]; i++) if (ct.state==0) $('#'+ct.id).parent().find('i').removeClass('fa-square').addClass('fa-refresh fa-spin');
|
||||
$.post('/plugins/dynamix.docker.manager/include/ContainerManager.php',{action:'start'},function(){loadlist();});
|
||||
}
|
||||
function stopAll() {
|
||||
$('input[type=button]').prop('disabled',true);
|
||||
for (var i=0,ct; ct=docker[i]; i++) if (ct.state==1) $('#'+ct.id).parent().find('i').removeClass('fa-play fa-pause').addClass('fa-refresh fa-spin');
|
||||
$.post('/plugins/dynamix.docker.manager/include/ContainerManager.php',{action:'stop'},function(){loadlist();});
|
||||
}
|
||||
function pauseAll() {
|
||||
$('input[type=button]').prop('disabled',true);
|
||||
for (var i=0,ct; ct=docker[i]; i++) if (ct.state==1 && ct.pause==0) $('#'+ct.id).parent().find('i').removeClass('fa-play').addClass('fa-refresh fa-spin');
|
||||
$.post('/plugins/dynamix.docker.manager/include/ContainerManager.php',{action:'pause'},function(){loadlist();});
|
||||
}
|
||||
function resumeAll() {
|
||||
$('input[type=button]').prop('disabled',true);
|
||||
for (var i=0,ct; ct=docker[i]; i++) if (ct.state==1 && ct.pause==1) $('#'+ct.id).parent().find('i').removeClass('fa-pause').addClass('fa-refresh fa-spin');
|
||||
$.post('/plugins/dynamix.docker.manager/include/ContainerManager.php',{action:'unpause'},function(){loadlist();});
|
||||
}
|
||||
function checkAll() {
|
||||
$('input[type=button]').prop('disabled',true);
|
||||
$('.updatecolumn').html('<span style="color:#267CA8"><i class="fa fa-refresh fa-spin"></i> '+_('checking')+'...</span>');
|
||||
$.post('/plugins/dynamix.docker.manager/include/DockerUpdate.php',{},function(){loadlist();});
|
||||
}
|
||||
function updateAll() {
|
||||
$('input[type=button]').prop('disabled',true);
|
||||
var ct = [];
|
||||
for (var i=0,d; d=docker[i]; i++) if (d.update==1) ct.push(encodeURIComponent(d.name));
|
||||
openDocker('update_container '+ct.join('*'),_('Updating all Containers'),'','loadlist');
|
||||
}
|
||||
function rebuildAll() {
|
||||
$('input[type=button]').prop('disabled',true);
|
||||
$('div.spinner.fixed').show('slow');
|
||||
var ct = [];
|
||||
for (var i=0,d; d=docker[i]; i++) if (d.update==2) ct.push(encodeURIComponent(d.name));
|
||||
$.get('/plugins/dynamix.docker.manager/include/CreateDocker.php',{updateContainer:true,mute:true,ct},function(){loadlist();});
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,548 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta http-equiv="Content-Security-Policy" content="block-all-mixed-content">
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
<meta name="viewport" content="width=1300">
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
<meta name="referrer" content="same-origin">
|
||||
<style>
|
||||
/************************
|
||||
/
|
||||
/ Fonts
|
||||
/
|
||||
/************************/
|
||||
@font-face{font-family:clear-sans;font-weight:normal;font-style:normal; src:url('/webGui/styles/clear-sans.woff?v=20220513') format('woff')}
|
||||
@font-face{font-family:clear-sans;font-weight:bold;font-style:normal; src:url('/webGui/styles/clear-sans-bold.woff?v=20220513') format('woff')}
|
||||
@font-face{font-family:clear-sans;font-weight:normal;font-style:italic; src:url('/webGui/styles/clear-sans-italic.woff?v=20220513') format('woff')}
|
||||
@font-face{font-family:clear-sans;font-weight:bold;font-style:italic; src:url('/webGui/styles/clear-sans-bold-italic.woff?v=20220513') format('woff')}
|
||||
@font-face{font-family:bitstream;font-weight:normal;font-style:normal; src:url('/webGui/styles/bitstream.woff?v=20220513') format('woff')}
|
||||
@font-face{font-family:bitstream;font-weight:bold;font-style:normal; src:url('/webGui/styles/bitstream-bold.woff?v=20220513') format('woff')}
|
||||
@font-face{font-family:bitstream;font-weight:normal;font-style:italic; src:url('/webGui/styles/bitstream-italic.woff?v=20220513') format('woff')}
|
||||
@font-face{font-family:bitstream;font-weight:bold;font-style:italic; src:url('/webGui/styles/bitstream-bold-italic.woff?v=20220513') format('woff')}
|
||||
|
||||
html{font-family:clear-sans;height:100%}
|
||||
body{font-size:1.3rem;padding:0;margin:0;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}
|
||||
.logLine{color:#1c1c1c;font-family:bitstream;font-size:1.2rem;line-height:1.6rem;margin:0 8px;padding:0;text-align:left}
|
||||
.logLine.spacing{margin:10px}
|
||||
input[type=button],input[type=reset],input[type=submit],button,button[type=button],a.button{font-family:clear-sans;font-size:1.1rem;text-transform:uppercase;margin:0;padding:9px 18px;text-decoration:none;white-space:nowrap;cursor:pointer;outline:none;border-radius:4px;border:0;color:#f15a2c;background:-webkit-gradient(linear,left top,right top,from(#e22828),to(#ff8c2f)) 0 0 no-repeat,-webkit-gradient(linear,left top,right top,from(#e22828),to(#ff8c2f)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#e22828),to(#e22828)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#ff8c2f),to(#ff8c2f)) 100% 100% no-repeat;background:linear-gradient(90deg,#e22828 0,#ff8c2f) 0 0 no-repeat,linear-gradient(90deg,#e22828 0,#ff8c2f) 0 100% no-repeat,linear-gradient(0deg,#e22828 0,#e22828) 0 100% no-repeat,linear-gradient(0deg,#ff8c2f 0,#ff8c2f) 100% 100% no-repeat;background-size:100% 2px,100% 2px,2px 100%,2px 100%}
|
||||
input:hover[type=button],input:hover[type=reset],input:hover[type=submit],button:hover,button:hover[type=button],a.button:hover{color:#f2f2f2;background:-webkit-gradient(linear,left top,right top,from(#e22828),to(#ff8c2f));background:linear-gradient(90deg,#e22828 0,#ff8c2f)}
|
||||
input[type=button][disabled],input[type=reset][disabled],input[type=submit][disabled],button[disabled],button[type=button][disabled],a.button[disabled]
|
||||
input:hover[type=button][disabled],input:hover[type=reset][disabled],input:hover[type=submit][disabled],button:hover[disabled],button:hover[type=button][disabled],a.button:hover[disabled]
|
||||
input:active[type=button][disabled],input:active[type=reset][disabled],input:active[type=submit][disabled],button:active[disabled],button:active[type=button][disabled],a.button:active[disabled]
|
||||
{cursor:default;color:#808080;background:-webkit-gradient(linear,left top,right top,from(#404040),to(#808080)) 0 0 no-repeat,-webkit-gradient(linear,left top,right top,from(#404040),to(#808080)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#404040),to(#404040)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#808080),to(#808080)) 100% 100% no-repeat;background:linear-gradient(90deg,#404040 0,#808080) 0 0 no-repeat,linear-gradient(90deg,#404040 0,#808080) 0 100% no-repeat,linear-gradient(0deg,#404040 0,#404040) 0 100% no-repeat,linear-gradient(0deg,#808080 0,#808080) 100% 100% no-repeat;background-size:100% 2px,100% 2px,2px 100%,2px 100%}
|
||||
p.centered{text-align:center}
|
||||
span.error{color:#D8000C;background-color:#FFBABA;display:block;width:100%}
|
||||
span.warn{color:#9F6000;background-color:#FEEFB3;display:block;width:100%}
|
||||
span.system{color:#00529B;background-color:#BDE5F8;display:block;width:100%}
|
||||
span.array{color:#4F8A10;background-color:#DFF2BF;display:block;width:100%}
|
||||
span.login{color:#D63301;background-color:#FFCCBA;display:block;width:100%}
|
||||
span.label{padding:4px 8px;margin-right:10px;border-radius:4px;display:inline;width:auto}
|
||||
legend{font-size:1.1rem;font-weight:bold}
|
||||
#content{margin:10;padding:0}
|
||||
</style>
|
||||
<script>
|
||||
/*
|
||||
This script block has the following license.
|
||||
Original: https://github.com/drudru/ansi_up Version: 5.0.1
|
||||
Modified by: https://github.com/nysos3 for use in unRaid
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2011 Dru Nelson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) {
|
||||
if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; }
|
||||
return cooked;
|
||||
};
|
||||
|
||||
var PacketKind;
|
||||
(function (PacketKind) {
|
||||
PacketKind[PacketKind["EOS"] = 0] = "EOS";
|
||||
PacketKind[PacketKind["Text"] = 1] = "Text";
|
||||
PacketKind[PacketKind["Incomplete"] = 2] = "Incomplete";
|
||||
PacketKind[PacketKind["ESC"] = 3] = "ESC";
|
||||
PacketKind[PacketKind["Unknown"] = 4] = "Unknown";
|
||||
PacketKind[PacketKind["SGR"] = 5] = "SGR";
|
||||
PacketKind[PacketKind["OSCURL"] = 6] = "OSCURL";
|
||||
})(PacketKind || (PacketKind = {}));
|
||||
|
||||
var AnsiUp = (function() {
|
||||
function AnsiUp() {
|
||||
this.VERSION = "5.0.1";
|
||||
this.setup_palettes();
|
||||
this._use_classes = false;
|
||||
this.bold = false;
|
||||
this.fg = this.bg = null;
|
||||
this._buffer = '';
|
||||
this._url_whitelist = { 'http': 1, 'https': 1 };
|
||||
}
|
||||
Object.defineProperty(AnsiUp.prototype, "use_classes", {
|
||||
get: function() {
|
||||
return this._use_classes;
|
||||
},
|
||||
set: function(arg) {
|
||||
this._use_classes = arg;
|
||||
},
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
Object.defineProperty(AnsiUp.prototype, "url_whitelist", {
|
||||
get: function() {
|
||||
return this._url_whitelist;
|
||||
},
|
||||
set: function(arg) {
|
||||
this._url_whitelist = arg;
|
||||
},
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
AnsiUp.prototype.setup_palettes = function() {
|
||||
var _this = this;
|
||||
this.ansi_colors = [
|
||||
[
|
||||
{ rgb: [0, 0, 0], class_name: "ansi-black" },
|
||||
{ rgb: [187, 0, 0], class_name: "ansi-red" },
|
||||
{ rgb: [0, 187, 0], class_name: "ansi-green" },
|
||||
{ rgb: [187, 187, 0], class_name: "ansi-yellow" },
|
||||
{ rgb: [0, 0, 187], class_name: "ansi-blue" },
|
||||
{ rgb: [187, 0, 187], class_name: "ansi-magenta" },
|
||||
{ rgb: [0, 187, 187], class_name: "ansi-cyan" },
|
||||
{ rgb: [255, 255, 255], class_name: "ansi-white" }
|
||||
],
|
||||
[
|
||||
{ rgb: [85, 85, 85], class_name: "ansi-bright-black" },
|
||||
{ rgb: [255, 85, 85], class_name: "ansi-bright-red" },
|
||||
{ rgb: [0, 255, 0], class_name: "ansi-bright-green" },
|
||||
{ rgb: [255, 255, 85], class_name: "ansi-bright-yellow" },
|
||||
{ rgb: [85, 85, 255], class_name: "ansi-bright-blue" },
|
||||
{ rgb: [255, 85, 255], class_name: "ansi-bright-magenta" },
|
||||
{ rgb: [85, 255, 255], class_name: "ansi-bright-cyan" },
|
||||
{ rgb: [255, 255, 255], class_name: "ansi-bright-white" }
|
||||
]
|
||||
];
|
||||
this.palette_256 = [];
|
||||
this.ansi_colors.forEach(function(palette) {
|
||||
palette.forEach(function(rec) {
|
||||
_this.palette_256.push(rec);
|
||||
});
|
||||
});
|
||||
var levels = [0, 95, 135, 175, 215, 255];
|
||||
for (var r = 0; r < 6; ++r) {
|
||||
for (var g = 0; g < 6; ++g) {
|
||||
for (var b = 0; b < 6; ++b) {
|
||||
var col = { rgb: [levels[r], levels[g], levels[b]], class_name: 'truecolor' };
|
||||
this.palette_256.push(col);
|
||||
}
|
||||
}
|
||||
}
|
||||
var grey_level = 8;
|
||||
for (var i = 0; i < 24; ++i, grey_level += 10) {
|
||||
var gry = { rgb: [grey_level, grey_level, grey_level], class_name: 'truecolor' };
|
||||
this.palette_256.push(gry);
|
||||
}
|
||||
};
|
||||
AnsiUp.prototype.escape_txt_for_html = function(txt) {
|
||||
return txt.replace(/[&<>"']/gm, function(str) {
|
||||
if (str === "&")
|
||||
return "&";
|
||||
if (str === "<")
|
||||
return "<";
|
||||
if (str === ">")
|
||||
return ">";
|
||||
if (str === "\"")
|
||||
return """;
|
||||
if (str === "'")
|
||||
return "'";
|
||||
});
|
||||
};
|
||||
AnsiUp.prototype.append_buffer = function(txt) {
|
||||
var str = this._buffer + txt;
|
||||
this._buffer = str;
|
||||
};
|
||||
AnsiUp.prototype.get_next_packet = function() {
|
||||
var pkt = {
|
||||
kind: PacketKind.EOS,
|
||||
text: '',
|
||||
url: ''
|
||||
};
|
||||
var len = this._buffer.length;
|
||||
if (len == 0)
|
||||
return pkt;
|
||||
var pos = this._buffer.indexOf("\x1B");
|
||||
if (pos == -1) {
|
||||
pkt.kind = PacketKind.Text;
|
||||
pkt.text = this._buffer;
|
||||
this._buffer = '';
|
||||
return pkt;
|
||||
}
|
||||
if (pos > 0) {
|
||||
pkt.kind = PacketKind.Text;
|
||||
pkt.text = this._buffer.slice(0, pos);
|
||||
this._buffer = this._buffer.slice(pos);
|
||||
return pkt;
|
||||
}
|
||||
if (pos == 0) {
|
||||
if (len == 1) {
|
||||
pkt.kind = PacketKind.Incomplete;
|
||||
return pkt;
|
||||
}
|
||||
var next_char = this._buffer.charAt(1);
|
||||
if ((next_char != '[') && (next_char != ']')) {
|
||||
pkt.kind = PacketKind.ESC;
|
||||
pkt.text = this._buffer.slice(0, 1);
|
||||
this._buffer = this._buffer.slice(1);
|
||||
return pkt;
|
||||
}
|
||||
if (next_char == '[') {
|
||||
if (!this._csi_regex) {
|
||||
this._csi_regex = rgx(__makeTemplateObject(["\n ^ # beginning of line\n #\n # First attempt\n (?: # legal sequence\n \u001B[ # CSI\n ([<-?]?) # private-mode char\n ([d;]*) # any digits or semicolons\n ([ -/]? # an intermediate modifier\n [@-~]) # the command\n )\n | # alternate (second attempt)\n (?: # illegal sequence\n \u001B[ # CSI\n [ -~]* # anything legal\n ([\0-\u001F:]) # anything illegal\n )\n "], ["\n ^ # beginning of line\n #\n # First attempt\n (?: # legal sequence\n \\x1b\\[ # CSI\n ([\\x3c-\\x3f]?) # private-mode char\n ([\\d;]*) # any digits or semicolons\n ([\\x20-\\x2f]? # an intermediate modifier\n [\\x40-\\x7e]) # the command\n )\n | # alternate (second attempt)\n (?: # illegal sequence\n \\x1b\\[ # CSI\n [\\x20-\\x7e]* # anything legal\n ([\\x00-\\x1f:]) # anything illegal\n )\n "]));
|
||||
}
|
||||
var match = this._buffer.match(this._csi_regex);
|
||||
if (match === null) {
|
||||
pkt.kind = PacketKind.Incomplete;
|
||||
return pkt;
|
||||
}
|
||||
if (match[4]) {
|
||||
pkt.kind = PacketKind.ESC;
|
||||
pkt.text = this._buffer.slice(0, 1);
|
||||
this._buffer = this._buffer.slice(1);
|
||||
return pkt;
|
||||
}
|
||||
if ((match[1] != '') || (match[3] != 'm'))
|
||||
pkt.kind = PacketKind.Unknown;
|
||||
else
|
||||
pkt.kind = PacketKind.SGR;
|
||||
pkt.text = match[2];
|
||||
var rpos = match[0].length;
|
||||
this._buffer = this._buffer.slice(rpos);
|
||||
return pkt;
|
||||
}
|
||||
if (next_char == ']') {
|
||||
if (len < 4) {
|
||||
pkt.kind = PacketKind.Incomplete;
|
||||
return pkt;
|
||||
}
|
||||
if ((this._buffer.charAt(2) != '8')
|
||||
|| (this._buffer.charAt(3) != ';')) {
|
||||
pkt.kind = PacketKind.ESC;
|
||||
pkt.text = this._buffer.slice(0, 1);
|
||||
this._buffer = this._buffer.slice(1);
|
||||
return pkt;
|
||||
}
|
||||
if (!this._osc_st) {
|
||||
this._osc_st = rgxG(__makeTemplateObject(["\n (?: # legal sequence\n (\u001B\\) # ESC | # alternate\n (\u0007) # BEL (what xterm did)\n )\n | # alternate (second attempt)\n ( # illegal sequence\n [\0-\u0006] # anything illegal\n | # alternate\n [\b-\u001A] # anything illegal\n | # alternate\n [\u001C-\u001F] # anything illegal\n )\n "], ["\n (?: # legal sequence\n (\\x1b\\\\) # ESC \\\n | # alternate\n (\\x07) # BEL (what xterm did)\n )\n | # alternate (second attempt)\n ( # illegal sequence\n [\\x00-\\x06] # anything illegal\n | # alternate\n [\\x08-\\x1a] # anything illegal\n | # alternate\n [\\x1c-\\x1f] # anything illegal\n )\n "]));
|
||||
}
|
||||
this._osc_st.lastIndex = 0;
|
||||
{
|
||||
var match_1 = this._osc_st.exec(this._buffer);
|
||||
if (match_1 === null) {
|
||||
pkt.kind = PacketKind.Incomplete;
|
||||
return pkt;
|
||||
}
|
||||
if (match_1[3]) {
|
||||
pkt.kind = PacketKind.ESC;
|
||||
pkt.text = this._buffer.slice(0, 1);
|
||||
this._buffer = this._buffer.slice(1);
|
||||
return pkt;
|
||||
}
|
||||
}
|
||||
{
|
||||
var match_2 = this._osc_st.exec(this._buffer);
|
||||
if (match_2 === null) {
|
||||
pkt.kind = PacketKind.Incomplete;
|
||||
return pkt;
|
||||
}
|
||||
if (match_2[3]) {
|
||||
pkt.kind = PacketKind.ESC;
|
||||
pkt.text = this._buffer.slice(0, 1);
|
||||
this._buffer = this._buffer.slice(1);
|
||||
return pkt;
|
||||
}
|
||||
}
|
||||
if (!this._osc_regex) {
|
||||
this._osc_regex = rgx(__makeTemplateObject(["\n ^ # beginning of line\n #\n \u001B]8; # OSC Hyperlink\n [ -:<-~]* # params (excluding ;)\n ; # end of params\n ([!-~]{0,512}) # URL capture\n (?: # ST\n (?:\u001B\\) # ESC | # alternate\n (?:\u0007) # BEL (what xterm did)\n )\n ([ -~]+) # TEXT capture\n \u001B]8;; # OSC Hyperlink End\n (?: # ST\n (?:\u001B\\) # ESC | # alternate\n (?:\u0007) # BEL (what xterm did)\n )\n "], ["\n ^ # beginning of line\n #\n \\x1b\\]8; # OSC Hyperlink\n [\\x20-\\x3a\\x3c-\\x7e]* # params (excluding ;)\n ; # end of params\n ([\\x21-\\x7e]{0,512}) # URL capture\n (?: # ST\n (?:\\x1b\\\\) # ESC \\\n | # alternate\n (?:\\x07) # BEL (what xterm did)\n )\n ([\\x20-\\x7e]+) # TEXT capture\n \\x1b\\]8;; # OSC Hyperlink End\n (?: # ST\n (?:\\x1b\\\\) # ESC \\\n | # alternate\n (?:\\x07) # BEL (what xterm did)\n )\n "]));
|
||||
}
|
||||
var match = this._buffer.match(this._osc_regex);
|
||||
if (match === null) {
|
||||
pkt.kind = PacketKind.ESC;
|
||||
pkt.text = this._buffer.slice(0, 1);
|
||||
this._buffer = this._buffer.slice(1);
|
||||
return pkt;
|
||||
}
|
||||
pkt.kind = PacketKind.OSCURL;
|
||||
pkt.url = match[1];
|
||||
pkt.text = match[2];
|
||||
var rpos = match[0].length;
|
||||
this._buffer = this._buffer.slice(rpos);
|
||||
return pkt;
|
||||
}
|
||||
}
|
||||
};
|
||||
AnsiUp.prototype.ansi_to_html = function(txt) {
|
||||
this.append_buffer(txt);
|
||||
var blocks = [];
|
||||
while (true) {
|
||||
var packet = this.get_next_packet();
|
||||
if ((packet.kind == PacketKind.EOS)
|
||||
|| (packet.kind == PacketKind.Incomplete))
|
||||
break;
|
||||
if ((packet.kind == PacketKind.ESC)
|
||||
|| (packet.kind == PacketKind.Unknown))
|
||||
continue;
|
||||
if (packet.kind == PacketKind.Text)
|
||||
blocks.push(this.transform_to_html(this.with_state(packet)));
|
||||
else if (packet.kind == PacketKind.SGR)
|
||||
this.process_ansi(packet);
|
||||
else if (packet.kind == PacketKind.OSCURL)
|
||||
blocks.push(this.process_hyperlink(packet));
|
||||
}
|
||||
return blocks.join("");
|
||||
};
|
||||
AnsiUp.prototype.with_state = function(pkt) {
|
||||
return { bold: this.bold, fg: this.fg, bg: this.bg, text: pkt.text };
|
||||
};
|
||||
AnsiUp.prototype.process_ansi = function(pkt) {
|
||||
var sgr_cmds = pkt.text.split(';');
|
||||
while (sgr_cmds.length > 0) {
|
||||
var sgr_cmd_str = sgr_cmds.shift();
|
||||
var num = parseInt(sgr_cmd_str, 10);
|
||||
if (isNaN(num) || num === 0) {
|
||||
this.fg = this.bg = null;
|
||||
this.bold = false;
|
||||
}
|
||||
else if (num === 1) {
|
||||
this.bold = true;
|
||||
}
|
||||
else if (num === 22) {
|
||||
this.bold = false;
|
||||
}
|
||||
else if (num === 39) {
|
||||
this.fg = null;
|
||||
}
|
||||
else if (num === 49) {
|
||||
this.bg = null;
|
||||
}
|
||||
else if ((num >= 30) && (num < 38)) {
|
||||
this.fg = this.ansi_colors[0][(num - 30)];
|
||||
}
|
||||
else if ((num >= 40) && (num < 48)) {
|
||||
this.bg = this.ansi_colors[0][(num - 40)];
|
||||
}
|
||||
else if ((num >= 90) && (num < 98)) {
|
||||
this.fg = this.ansi_colors[1][(num - 90)];
|
||||
}
|
||||
else if ((num >= 100) && (num < 108)) {
|
||||
this.bg = this.ansi_colors[1][(num - 100)];
|
||||
}
|
||||
else if (num === 38 || num === 48) {
|
||||
if (sgr_cmds.length > 0) {
|
||||
var is_foreground = (num === 38);
|
||||
var mode_cmd = sgr_cmds.shift();
|
||||
if (mode_cmd === '5' && sgr_cmds.length > 0) {
|
||||
var palette_index = parseInt(sgr_cmds.shift(), 10);
|
||||
if (palette_index >= 0 && palette_index <= 255) {
|
||||
if (is_foreground)
|
||||
this.fg = this.palette_256[palette_index];
|
||||
else
|
||||
this.bg = this.palette_256[palette_index];
|
||||
}
|
||||
}
|
||||
if (mode_cmd === '2' && sgr_cmds.length > 2) {
|
||||
var r = parseInt(sgr_cmds.shift(), 10);
|
||||
var g = parseInt(sgr_cmds.shift(), 10);
|
||||
var b = parseInt(sgr_cmds.shift(), 10);
|
||||
if ((r >= 0 && r <= 255) && (g >= 0 && g <= 255) && (b >= 0 && b <= 255)) {
|
||||
var c = { rgb: [r, g, b], class_name: 'truecolor' };
|
||||
if (is_foreground)
|
||||
this.fg = c;
|
||||
else
|
||||
this.bg = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
AnsiUp.prototype.transform_to_html = function(fragment) {
|
||||
var txt = fragment.text;
|
||||
if (txt.length === 0)
|
||||
return txt;
|
||||
// txt = this.escape_txt_for_html(txt);
|
||||
if (!fragment.bold && fragment.fg === null && fragment.bg === null)
|
||||
return txt;
|
||||
var styles = [];
|
||||
var classes = [];
|
||||
var fg = fragment.fg;
|
||||
var bg = fragment.bg;
|
||||
if (fragment.bold)
|
||||
styles.push('font-weight:bold');
|
||||
if (!this._use_classes) {
|
||||
if (fg)
|
||||
styles.push("color:rgb(" + fg.rgb.join(',') + ")");
|
||||
if (bg)
|
||||
styles.push("background-color:rgb(" + bg.rgb + ")");
|
||||
}
|
||||
else {
|
||||
if (fg) {
|
||||
if (fg.class_name !== 'truecolor') {
|
||||
classes.push(fg.class_name + "-fg");
|
||||
}
|
||||
else {
|
||||
styles.push("color:rgb(" + fg.rgb.join(',') + ")");
|
||||
}
|
||||
}
|
||||
if (bg) {
|
||||
if (bg.class_name !== 'truecolor') {
|
||||
classes.push(bg.class_name + "-bg");
|
||||
}
|
||||
else {
|
||||
styles.push("background-color:rgb(" + bg.rgb.join(',') + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
var class_string = '';
|
||||
var style_string = '';
|
||||
if (classes.length)
|
||||
class_string = " class=\"" + classes.join(' ') + "\"";
|
||||
if (styles.length)
|
||||
style_string = " style=\"" + styles.join(';') + "\"";
|
||||
return "<span" + style_string + class_string + ">" + txt + "</span>";
|
||||
};
|
||||
;
|
||||
AnsiUp.prototype.process_hyperlink = function(pkt) {
|
||||
var parts = pkt.url.split(':');
|
||||
if (parts.length < 1)
|
||||
return '';
|
||||
if (!this._url_whitelist[parts[0]])
|
||||
return '';
|
||||
// var result = "<a href=\"" + this.escape_txt_for_html(pkt.url) + "\">" + this.escape_txt_for_html(pkt.text) + "</a>";
|
||||
var result = "<a href=\"" + this.escape_txt_for_html(pkt.url) + "\">" + pkt.text + "</a>";
|
||||
return result;
|
||||
};
|
||||
return AnsiUp;
|
||||
}());
|
||||
function rgx(tmplObj) {
|
||||
var subst = [];
|
||||
for (var _i = 1; _i < arguments.length; _i++) {
|
||||
subst[_i - 1] = arguments[_i];
|
||||
}
|
||||
var regexText = tmplObj.raw[0];
|
||||
var wsrgx = /^\s+|\s+\n|\s*#[\s\S]*?\n|\n/gm;
|
||||
var txt2 = regexText.replace(wsrgx, '');
|
||||
return new RegExp(txt2);
|
||||
}
|
||||
function rgxG(tmplObj) {
|
||||
var subst = [];
|
||||
for (var _i = 1; _i < arguments.length; _i++) {
|
||||
subst[_i - 1] = arguments[_i];
|
||||
}
|
||||
var regexText = tmplObj.raw[0];
|
||||
var wsrgx = /^\s+|\s+\n|\s*#[\s\S]*?\n|\n/gm;
|
||||
var txt2 = regexText.replace(wsrgx, '');
|
||||
return new RegExp(txt2, 'g');
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
var dots = [];
|
||||
var span = [];
|
||||
var ansi_up = new AnsiUp;
|
||||
|
||||
function show_Wait(id) {
|
||||
span[id] = document.getElementById("wait" + id);
|
||||
dots[id] = setInterval(function() {
|
||||
if (((span[id].innerHTML+='.').match(/\./g)||[]).length > 9) {
|
||||
span[id].innerHTML = span[id].innerHTML.replace(/\.+$/,'');
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
||||
function stop_Wait(id) {
|
||||
span[id].innerHTML = '';
|
||||
clearInterval( dots[id] );
|
||||
}
|
||||
|
||||
function addLog(logLine) {
|
||||
var elms = document.getElementsByClassName('logLine');
|
||||
logLine = ansi_up.ansi_to_html(logLine);
|
||||
if (elms.length) {
|
||||
var el = elms[elms.length-1];
|
||||
el.innerHTML += logLine + "<br>";
|
||||
}
|
||||
window.scrollTo(0, document.body.scrollHeight);
|
||||
}
|
||||
|
||||
function loadLog(container, since) {
|
||||
var httpRequest = new XMLHttpRequest();
|
||||
httpRequest.onreadystatechange = function() {
|
||||
if (httpRequest.readyState === 4 && httpRequest.status === 200) {
|
||||
parseScript(httpRequest.responseText);
|
||||
}
|
||||
};
|
||||
httpRequest.open('GET', location.protocol+'//'+location.host+location.pathname+'?action=log&container='+encodeURIComponent(container)+'&since='+encodeURIComponent(since));
|
||||
httpRequest.send();
|
||||
}
|
||||
|
||||
function parseScript(_source) {
|
||||
var source = _source;
|
||||
var scripts = [];
|
||||
while(source.indexOf("<script") > -1 || source.indexOf("</script") > -1) {
|
||||
var s = source.indexOf("<script");
|
||||
var s_e = source.indexOf(">", s);
|
||||
var e = source.indexOf("</script", s);
|
||||
var e_e = source.indexOf(">", e);
|
||||
scripts.push(source.substring(s_e+1, e));
|
||||
source = source.substring(0, s) + source.substring(e_e+1);
|
||||
}
|
||||
for (var i=0; i<scripts.length; i++) {
|
||||
try {
|
||||
eval(scripts[i]);
|
||||
} catch(ex) {}
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
function progress(id, prog) {
|
||||
var elms = document.getElementsByClassName(id+'_progress');
|
||||
if (elms.length) {
|
||||
elms[elms.length-1].textContent = prog;
|
||||
}
|
||||
}
|
||||
|
||||
function addToID(id, m) {
|
||||
var elms = document.getElementById(id);
|
||||
if (elms === null) {
|
||||
addLog('<span id=\"'+id+'\">IMAGE ID ['+id+']: <span class=\"content\">'+m+'</span><span class=\"'+id+'_progress\"></span>. </span>');
|
||||
} else {
|
||||
var elms_content = elms.getElementsByClassName("content");
|
||||
if (!elms_content.length || elms_content[elms_content.length-1].textContent != m) {
|
||||
elms.innerHTML += '<span class=\"content\">'+m+'</span><span class=\"'+id+'_progress\"></span>. ';
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="content"><p class="logLine" id="logBody"></p></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
while :; do
|
||||
curl -sfd "$(docker stats --no-stream --format='{{.ID}};{{.CPUPerc}};{{.MemUsage}}')" --unix-socket /var/run/nginx.socket http://localhost/pub/dockerload?buffer_length=0 >/dev/null 2>&1
|
||||
done
|
||||
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/php -q
|
||||
<?PHP
|
||||
/* Copyright 2005-2023, Lime Technology
|
||||
* Copyright 2012-2023, Bergware International.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
|
||||
require_once "$docroot/webGui/include/Wrappers.php";
|
||||
extract(parse_plugin_cfg('dynamix',true));
|
||||
|
||||
// add translations
|
||||
$_SERVER['REQUEST_URI'] = 'docker';
|
||||
$login_locale = _var($display,'locale');
|
||||
require_once "$docroot/webGui/include/Translations.php";
|
||||
|
||||
$unit = ['B','kB','MB','GB','TB','PB','EB','ZB','YB'];
|
||||
$list = [];
|
||||
|
||||
function write(...$messages){
|
||||
$com = curl_init();
|
||||
curl_setopt_array($com,[
|
||||
CURLOPT_URL => 'http://localhost/pub/plugins?buffer_length=1',
|
||||
CURLOPT_UNIX_SOCKET_PATH => '/var/run/nginx.socket',
|
||||
CURLOPT_POST => 1,
|
||||
CURLOPT_RETURNTRANSFER => true
|
||||
]);
|
||||
foreach ($messages as $message) {
|
||||
curl_setopt($com, CURLOPT_POSTFIELDS, $message);
|
||||
curl_exec($com);
|
||||
}
|
||||
curl_close($com);
|
||||
}
|
||||
function autoscale($value) {
|
||||
global $unit;
|
||||
$size = count($unit);
|
||||
$base = $value ? floor(log($value, 1000)) : 0;
|
||||
if ($base>=$size) $base = $size-1;
|
||||
$value /= pow(1000, $base);
|
||||
$decimals = $base ? ($value>=100 ? 0 : ($value>=10 ? 1 : (round($value*100)%100===0 ? 0 : 2))) : 0;
|
||||
return number_format($value, $decimals, '.', $value>9999 ? ',' : '').' '.$unit[$base];
|
||||
}
|
||||
function align($text, $w=25) {
|
||||
return $text.str_repeat(' ',$w-min(strlen($text),$w-1));
|
||||
}
|
||||
function gap($text) {
|
||||
return preg_replace('/([kMGTPEZY]?B)$/'," $1",$text);
|
||||
}
|
||||
function byteval($data) {
|
||||
global $unit;
|
||||
[$value,$base] = explode(' ',gap($data));
|
||||
return $value*pow(1000,array_search($base,$unit));
|
||||
}
|
||||
|
||||
exec("docker ps -sa --format='{{.Names}}|{{.Size}}'",$container);
|
||||
foreach ($container as $ct) {
|
||||
[$name,$size] = explode('|',$ct);
|
||||
[$writable,$dummy,$total] = explode(' ',str_replace(['(',')'],'',$size));
|
||||
$list[] = ['name' => $name, 'total' => byteval($total), 'writable' => byteval($writable), 'log' => (exec("docker inspect --format='{{.LogPath}}' $name|xargs du -b 2>/dev/null |cut -f1")) ?: "0"];
|
||||
}
|
||||
$sum = ['total' => 0, 'writable' => 0, 'log' => 0];
|
||||
array_multisort(array_column($list,'total'),SORT_DESC,$list); // sort on container size
|
||||
$i = 0;
|
||||
write(align(_('Name'),48).align(_('Container')).align(_('Writable'))._('Log')."\n");
|
||||
foreach ($list as $ct) {
|
||||
write(($i++==0 ? '<hr>':'').align($ct['name'],48).align(autoscale($ct['total'])).align(autoscale($ct['writable'])).autoscale($ct['log'])."\n");
|
||||
$sum['total'] += $ct['total'];
|
||||
$sum['writable'] += $ct['writable'];
|
||||
$sum['log'] += $ct['log'];
|
||||
}
|
||||
write("<hr>".align(_('Total size'),48).align(autoscale($sum['total'])).align(autoscale($sum['writable'])).autoscale($sum['log'])."\n",'_DONE_','');
|
||||
?>
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
# Just a wrapper.
|
||||
exec /usr/bin/docker "$@"
|
||||
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
# Invoked after docker image loopback mounted but before docker is started
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
# delete the docker image file or folder
|
||||
if [[ -f /boot/config/docker.cfg ]]; then
|
||||
rm -f /var/local/emhttp/plugins/dynamix.docker.manager/docker.json
|
||||
. /boot/config/docker.cfg
|
||||
if [[ -f $DOCKER_IMAGE_FILE ]]; then
|
||||
echo "Deleting $DOCKER_IMAGE_FILE ..."
|
||||
rm -f "$DOCKER_IMAGE_FILE"
|
||||
elif [[ -d $DOCKER_IMAGE_FILE ]]; then
|
||||
echo "Deleting $DOCKER_IMAGE_FILE ..."
|
||||
rm -rf "$DOCKER_IMAGE_FILE"
|
||||
fi
|
||||
fi
|
||||
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/php -q
|
||||
<?PHP
|
||||
/* Copyright 2015-2023, Lime Technology
|
||||
* Copyright 2015-2016, Guilherme Jardim, Eric Schultz, Jon Panozzo.
|
||||
* Copyright 2012-2023, Bergware International.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
$cfgfile = "/boot/config/docker.cfg";
|
||||
$cfg_defaults = [
|
||||
"DOCKER_ENABLED" => "no",
|
||||
"DOCKER_IMAGE_FILE" => "/mnt/user/system/docker/docker.img",
|
||||
"DOCKER_IMAGE_SIZE" => "20",
|
||||
"DOCKER_APP_CONFIG_PATH" => "/mnt/user/appdata/",
|
||||
"DOCKER_APP_UNRAID_PATH" => "",
|
||||
"DOCKER_READMORE" => "yes"
|
||||
];
|
||||
|
||||
$cfg_new = $cfg_defaults;
|
||||
if (file_exists($cfgfile)) {
|
||||
$cfg_old = parse_ini_file($cfgfile);
|
||||
if (!empty($cfg_old)) {
|
||||
$cfg_new = array_merge($cfg_defaults, $cfg_old);
|
||||
if (empty(array_diff($cfg_new, $cfg_old))) unset($cfg_new);
|
||||
}
|
||||
}
|
||||
if (isset($cfg_new)) {
|
||||
$tmp = '';
|
||||
foreach ($cfg_new as $key => $value) $tmp .= "$key=\"$value\"\n";
|
||||
file_put_contents($cfgfile, $tmp);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/php -q
|
||||
<?PHP
|
||||
/* Copyright 2005-2023, Lime Technology
|
||||
* Copyright 2014-2023, Guilherme Jardim, Eric Schultz, Jon Panozzo.
|
||||
* Copyright 2012-2023, Bergware International.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
|
||||
require_once "$docroot/webGui/include/Wrappers.php";
|
||||
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
|
||||
extract(parse_plugin_cfg('dynamix', true));
|
||||
|
||||
// Multi-language support
|
||||
$_SERVER['REQUEST_URI'] = "scripts";
|
||||
$login_locale = _var($display,'locale');
|
||||
require_once "$docroot/webGui/include/Translations.php";
|
||||
|
||||
exec("pgrep docker", $pid);
|
||||
if (count($pid) == 1) exit(0);
|
||||
|
||||
$DockerClient = new DockerClient();
|
||||
$DockerTemplates = new DockerTemplates();
|
||||
|
||||
foreach ($argv as $arg) {
|
||||
switch ($arg) {
|
||||
case '-v' : $DockerTemplates->verbose = true; break;
|
||||
case 'check': $check = true; break;
|
||||
case 'nonotify': $nonotify = true; break;}
|
||||
}
|
||||
|
||||
if (!isset($check)) {
|
||||
echo " Updating templates... ";
|
||||
$DockerTemplates->downloadTemplates();
|
||||
echo " Updating info... ";
|
||||
$DockerTemplates->getAllInfo(true);
|
||||
echo " Done.";
|
||||
} else {
|
||||
$notify = "$docroot/webGui/scripts/notify";
|
||||
$var = @parse_ini_file("/var/local/emhttp/var.ini") ?: [];
|
||||
$server = strtoupper(_var($var,'NAME','tower'));
|
||||
$output = _var($notify,'docker_notify');
|
||||
$info = $DockerTemplates->getAllInfo(true);
|
||||
foreach ($DockerClient->getDockerContainers() as $ct) {
|
||||
$name = $ct['Name'];
|
||||
$image = $ct['Image'];
|
||||
if ($info[$name]['updated'] == "false") {
|
||||
$updateStatus = is_file($dockerManPaths['update-status']) ? json_decode(file_get_contents($dockerManPaths['update-status']),true) : [];
|
||||
$new = str_replace('sha256:', '', $updateStatus[$image]['remote']);
|
||||
$new = substr($new, 0, 4).'..'.substr($new, -4, 4);
|
||||
if ( ! isset($nonotify) ) {
|
||||
$event = str_replace("'","'",_("Docker")." - $name [$new]");
|
||||
$subject = str_replace("'","'",sprintf(_("Notice [%s] - Version update %s"),$server,$new));
|
||||
$description = str_replace("'","'",sprintf(_("A new version of %s is available"),$name));
|
||||
exec("$notify -e ".escapeshellarg($event)." -s ".escapeshellarg($subject)." -d ".escapeshellarg($description)." -i ".escapeshellarg("normal $output")." -l '/Docker' -x");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
exit(0);
|
||||
?>
|
||||
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/php -q
|
||||
<?PHP
|
||||
function startsWith($haystack, $needle) {
|
||||
if (!is_string($haystack) || !is_string($needle)) return false;
|
||||
return $needle === "" || strripos($haystack, $needle, -strlen($haystack)) !== false;
|
||||
}
|
||||
|
||||
$xmlFiles = glob("/boot/config/plugins/dockerMan/templates-user/*.xml");
|
||||
|
||||
foreach ($xmlFiles as $file) {
|
||||
unset($changeFlag);
|
||||
$xml = @simplexml_load_file($file);
|
||||
if (!$xml) continue;
|
||||
|
||||
foreach ($xml->Config as $id => $config) {
|
||||
if (startsWith((string)$config->attributes()->Description,"Container ".(string)$config->attributes()->Type)) {
|
||||
$config->attributes()->Description = "";
|
||||
$changeFlag = true;
|
||||
}
|
||||
}
|
||||
if (isset($changeFlag)) {
|
||||
copy($file,"$file.bak");
|
||||
$dom = new DOMDocument('1.0');
|
||||
$dom->preserveWhiteSpace = false;
|
||||
$dom->formatOutput = true;
|
||||
$dom->loadXML($xml->asXML());
|
||||
file_put_contents($file,$dom->saveXML());
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,197 @@
|
||||
#!/usr/bin/php -q
|
||||
<?PHP
|
||||
/* Copyright 2005-2023, Lime Technology
|
||||
* Copyright 2012-2023, Bergware International.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
|
||||
require_once "$docroot/webGui/include/Wrappers.php";
|
||||
extract(parse_plugin_cfg('dynamix',true));
|
||||
|
||||
// add translations
|
||||
$_SERVER['REQUEST_URI'] = '';
|
||||
$login_locale = _var($display,'locale');
|
||||
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
|
||||
|
||||
$var = parse_ini_file('/var/local/emhttp/var.ini');
|
||||
$DockerClient = new DockerClient();
|
||||
$DockerUpdate = new DockerUpdate();
|
||||
$DockerTemplates = new DockerTemplates();
|
||||
|
||||
$custom = DockerUtil::custom();
|
||||
$subnet = DockerUtil::network($custom);
|
||||
$cpus = DockerUtil::cpus();
|
||||
|
||||
function write(...$messages){
|
||||
$com = curl_init();
|
||||
curl_setopt_array($com,[
|
||||
CURLOPT_URL => 'http://localhost/pub/docker?buffer_length=1',
|
||||
CURLOPT_UNIX_SOCKET_PATH => '/var/run/nginx.socket',
|
||||
CURLOPT_POST => 1,
|
||||
CURLOPT_RETURNTRANSFER => true
|
||||
]);
|
||||
foreach ($messages as $message) {
|
||||
curl_setopt($com, CURLOPT_POSTFIELDS, $message);
|
||||
curl_exec($com);
|
||||
}
|
||||
curl_close($com);
|
||||
}
|
||||
function stopContainer_nchan($name) {
|
||||
global $DockerClient;
|
||||
$waitID = mt_rand();
|
||||
write("<p class='logLine'></p>","addLog\0<fieldset class='docker'><legend>"._('Stopping container').": ".htmlspecialchars($name)."</legend><p class='logLine'></p><span id='wait-$waitID'>"._('Please wait')." </span></fieldset>","show_Wait\0$waitID");
|
||||
$retval = $DockerClient->stopContainer($name);
|
||||
$out = ($retval === true) ? _('Successfully stopped container').": $name" : _('Error').": ".$retval;
|
||||
write("stop_Wait\0$waitID","addLog\0<b>".htmlspecialchars($out)."</b>");
|
||||
}
|
||||
function removeContainer_nchan($name) {
|
||||
global $DockerClient;
|
||||
$waitID = mt_rand();
|
||||
write("<p class='logLine'></p>","addLog\0<fieldset class='docker'><legend>"._('Removing container').": ".htmlspecialchars($name)."</legend><p class='logLine'></p><span id='wait-$waitID'>"._('Please wait')." </span></fieldset>","show_Wait\0$waitID");
|
||||
$retval = $DockerClient->removeContainer($name);
|
||||
$out = ($retval === true) ? _('Successfully removed container').": $name" : _('Error').": ".$retval;
|
||||
write("stop_Wait\0$waitID","addLog\0<b>".htmlspecialchars($out)."</b>");
|
||||
}
|
||||
function removeImage_nchan($image) {
|
||||
global $DockerClient;
|
||||
$waitID = mt_rand();
|
||||
write("<p class='logLine'></p>","addLog\0<fieldset class='docker'><legend>"._('Removing orphan image').": ".htmlspecialchars($image)."</legend><p class='logLine'></p><span id='wait-$waitID'>"._('Please wait')." </span></fieldset>","show_Wait\0$waitID");
|
||||
$retval = $DockerClient->removeImage($image);
|
||||
$out = ($retval === true) ? _('Successfully removed orphan image').": $image" : _('Error').": ".$retval;
|
||||
write("stop_Wait\0$waitID","addLog\0<b>".htmlspecialchars($out)."</b>");
|
||||
}
|
||||
function pullImage_nchan($name, $image) {
|
||||
global $DockerClient, $DockerTemplates, $DockerUpdate;
|
||||
$waitID = mt_rand();
|
||||
if (!preg_match("/:\S+$/", $image)) $image .= ":latest";
|
||||
write("<p class='logLine'></p>","addLog\0<fieldset class='docker'><legend>"._('Pulling image').": ".htmlspecialchars($image)."</legend><p class='logLine'></p><span id='wait-$waitID'>"._('Please wait')." </span></fieldset>","show_Wait\0$waitID");
|
||||
$alltotals = [];
|
||||
$laststatus = [];
|
||||
$strError = '';
|
||||
$DockerClient->pullImage($image, function ($line) use (&$alltotals, &$laststatus, &$waitID, &$strError, $image, $DockerClient, $DockerUpdate) {
|
||||
$cnt = json_decode($line, true);
|
||||
$id = $cnt['id'] ?? '';
|
||||
$status = $cnt['status'] ?? '';
|
||||
if (isset($cnt['error'])) $strError = $cnt['error'];
|
||||
if ($waitID !== false) {
|
||||
write("stop_Wait\0$waitID");
|
||||
$waitID = false;
|
||||
}
|
||||
if (empty($status)) return;
|
||||
if (!empty($id)) {
|
||||
if (!empty($cnt['progressDetail']) && !empty($cnt['progressDetail']['total'])) {
|
||||
$alltotals[$id] = $cnt['progressDetail']['total'];
|
||||
}
|
||||
if (empty($laststatus[$id])) {
|
||||
$laststatus[$id] = '';
|
||||
}
|
||||
switch ($status) {
|
||||
case 'Waiting':
|
||||
// Omit
|
||||
break;
|
||||
case 'Downloading':
|
||||
if ($laststatus[$id] != $status) {
|
||||
write("addToID\0$id\0".htmlspecialchars($status));
|
||||
}
|
||||
$total = $cnt['progressDetail']['total'];
|
||||
$current = $cnt['progressDetail']['current'];
|
||||
if ($total > 0) {
|
||||
$percentage = round(($current / $total) * 100);
|
||||
write("progress\0$id\0 ".$percentage."% "._('of')." ".$DockerClient->formatBytes($total));
|
||||
} else {
|
||||
// Docker must not know the total download size (http-chunked or something?)
|
||||
// just show the current download progress without the percentage
|
||||
$alltotals[$id] = $current;
|
||||
write("progress\0$id\0".$DockerClient->formatBytes($current));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if ($laststatus[$id] == "Downloading") {
|
||||
write("progress\0$id\0 100% "._('of')." ".$DockerClient->formatBytes($alltotals[$id]));
|
||||
}
|
||||
if ($laststatus[$id] != $status) {
|
||||
write("addToID\0".($id=='latest'?mt_rand():$id)."\0".htmlspecialchars($status));
|
||||
}
|
||||
break;
|
||||
}
|
||||
$laststatus[$id] = $status;
|
||||
} else {
|
||||
if (strpos($status, 'Status: ') === 0) {
|
||||
write("addLog\0".htmlspecialchars($status));
|
||||
}
|
||||
if (strpos($status, 'Digest: ') === 0) {
|
||||
$DockerUpdate->setUpdateStatus($image, substr($status,8));
|
||||
}
|
||||
}
|
||||
});
|
||||
write("addLog\0<br><b>"._('TOTAL DATA PULLED').":</b> ".$DockerClient->formatBytes(array_sum($alltotals)));
|
||||
if (!empty($strError)) {
|
||||
write("addLog\0<br><span class='error'><b>"._('Error').":</b> ".htmlspecialchars($strError)."</span>");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function execCommand_nchan($command) {
|
||||
$waitID = mt_rand();
|
||||
[$cmd,$args] = explode(' ',$command,2);
|
||||
write("<p class='logLine'></p>","addLog\0<fieldset class='docker'><legend>"._('Command execution')."</legend>".basename($cmd).' '.str_replace(" -","<br> -",htmlspecialchars($args))."<br><span id='wait-$waitID'>"._('Please wait')." </span><p class='logLine'></p></fieldset>","show_Wait\0$waitID");
|
||||
$proc = popen("$command 2>&1",'r');
|
||||
while ($out = fgets($proc)) {
|
||||
$out = preg_replace("%[\t\n\x0B\f\r]+%", '',$out);
|
||||
write("addLog\0".htmlspecialchars($out));
|
||||
}
|
||||
$retval = pclose($proc);
|
||||
$out = $retval ? _('The command failed').'.' : _('The command finished successfully').'!';
|
||||
write("stop_Wait\0$waitID","addLog\0<br><b>$out</b>");
|
||||
return $retval===0;
|
||||
}
|
||||
|
||||
$style = ["<style>"];
|
||||
$style[] = ".logLine{font-family:bitstream!important;font-size:1.2rem!important;margin:0;padding:0}";
|
||||
$style[] = "fieldset.docker{border:solid thin;margin-top:8px}";
|
||||
$style[] = "legend{font-size:1.1rem!important;font-weight:bold}";
|
||||
$style[] = "</style>";
|
||||
|
||||
write(implode($style)."<p class='logLine'></p>");
|
||||
foreach (explode('*',rawurldecode($argv[1])) as $value) {
|
||||
$tmpl = $DockerTemplates->getUserTemplate($value);
|
||||
if (!$tmpl) {
|
||||
write("addLog\0"._('Configuration not found').". "._('Was this container created using this plugin')."?");
|
||||
continue;
|
||||
}
|
||||
$xml = file_get_contents($tmpl);
|
||||
[$cmd, $Name, $Repository] = xmlToCommand($tmpl);
|
||||
$Registry = getXmlVal($xml, "Registry");
|
||||
$oldImageID = $DockerClient->getImageID($Repository);
|
||||
// pull image
|
||||
if (!pullImage_nchan($Name, $Repository)) continue;
|
||||
$oldContainerInfo = $DockerClient->getContainerDetails($Name);
|
||||
// determine if the container is still running
|
||||
$startContainer = false;
|
||||
if (!empty($oldContainerInfo) && !empty($oldContainerInfo['State']) && !empty($oldContainerInfo['State']['Running'])) {
|
||||
// since container was already running, put it back it to a running state after update
|
||||
$cmd = str_replace('/docker create ', '/docker run -d ', $cmd);
|
||||
$startContainer = true;
|
||||
// attempt graceful stop of container first
|
||||
stopContainer_nchan($Name);
|
||||
}
|
||||
// force kill container if still running after 10 seconds
|
||||
if (empty($_GET['communityApplications'])) removeContainer_nchan($Name);
|
||||
execCommand_nchan($cmd);
|
||||
if ($startContainer) addRoute($Name); // add route for remote WireGuard access
|
||||
$DockerClient->flushCaches();
|
||||
$newImageID = $DockerClient->getImageID($Repository);
|
||||
// remove old orphan image since it's no longer used by this container
|
||||
if ($oldImageID && $oldImageID != $newImageID) removeImage_nchan($oldImageID);
|
||||
}
|
||||
write('_DONE_','');
|
||||
?>
|
||||
@@ -0,0 +1,62 @@
|
||||
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset button{
|
||||
font-family:clear-sans;font-size:1.1rem;font-weight:bold;letter-spacing:2px;text-transform:uppercase;margin:10px 12px 10px 0;padding:9px 18px;text-decoration:none;white-space:nowrap;cursor:pointer;outline:none;border-radius:4px;border:0;color:#ff8c2f;background:-webkit-gradient(linear,left top,right top,from(#e22828),to(#ff8c2f)) 0 0 no-repeat,-webkit-gradient(linear,left top,right top,from(#e22828),to(#ff8c2f)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#e22828),to(#e22828)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#ff8c2f),to(#ff8c2f)) 100% 100% no-repeat;background:linear-gradient(90deg,#e22828 0,#ff8c2f) 0 0 no-repeat,linear-gradient(90deg,#e22828 0,#ff8c2f) 0 100% no-repeat,linear-gradient(0deg,#e22828 0,#e22828) 0 100% no-repeat,linear-gradient(0deg,#ff8c2f 0,#ff8c2f) 100% 100% no-repeat;background-size:100% 2px,100% 2px,2px 100%,2px 100%
|
||||
}
|
||||
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset button:hover{
|
||||
color:#f2f2f2;background:-webkit-gradient(linear,left top,right top,from(#e22828),to(#ff8c2f));background:linear-gradient(90deg,#e22828 0,#ff8c2f)
|
||||
}
|
||||
.ui-dropdownchecklist .ui-state-default{
|
||||
background:#f2f2f2;
|
||||
border:none;
|
||||
box-shadow:none;
|
||||
outline:none;
|
||||
cursor:pointer;
|
||||
height:2.2rem;
|
||||
line-height:2.2rem;
|
||||
}
|
||||
.ui-dropdownchecklist-group{
|
||||
font-weight:normal;
|
||||
font-style:italic;
|
||||
padding:1px 9px 1px 8px;
|
||||
}
|
||||
.ui-dropdownchecklist-selector{
|
||||
border:1px solid #1c1c1c;
|
||||
display:inline-block;
|
||||
cursor:pointer;
|
||||
padding:1px 9px 1px 8px;
|
||||
}
|
||||
.ui-dropdownchecklist-selector-wrapper{
|
||||
vertical-align:middle;
|
||||
font-size:0;
|
||||
}
|
||||
.ui-widget-header{
|
||||
border:none;
|
||||
background:#e3e3e3;
|
||||
color:#1c1c1c;
|
||||
font-weight:bold;
|
||||
}
|
||||
.ui-widget-content{
|
||||
border:1px solid #1c1c1c;
|
||||
background:#f2f2f2;
|
||||
color:#1c1c1c;
|
||||
|
||||
.ui-state-active{
|
||||
background:#e8e8e8;
|
||||
}
|
||||
.ui-dropdownchecklist-dropcontainer{
|
||||
background:#f2f2f2;
|
||||
border:1px solid #1c1c1c;
|
||||
}
|
||||
.ui-state-disabled{
|
||||
color:#1c1c1c;border-color:#a2a2a2;background:#e8e8e8;opacity:0.5;
|
||||
}
|
||||
.ui-dropdownchecklist-indent{
|
||||
padding-left:7px;
|
||||
}
|
||||
.ui-dropdownchecklist-text{
|
||||
color:#1c1c1c;
|
||||
font-size:1.3rem;
|
||||
}
|
||||
.ui-dropdownchecklist .ui-widget-content .ui-state-default{
|
||||
background:#f2f2f2;
|
||||
border:0px;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset button{
|
||||
font-family:clear-sans;font-size:1.1rem;font-weight:bold;letter-spacing:2px;text-transform:uppercase;margin:10px 12px 10px 0;padding:9px 18px;text-decoration:none;white-space:nowrap;cursor:pointer;outline:none;border-radius:4px;border:0;color:#ff8c2f;background:-webkit-gradient(linear,left top,right top,from(#e22828),to(#ff8c2f)) 0 0 no-repeat,-webkit-gradient(linear,left top,right top,from(#e22828),to(#ff8c2f)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#e22828),to(#e22828)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#ff8c2f),to(#ff8c2f)) 100% 100% no-repeat;background:linear-gradient(90deg,#e22828 0,#ff8c2f) 0 0 no-repeat,linear-gradient(90deg,#e22828 0,#ff8c2f) 0 100% no-repeat,linear-gradient(0deg,#e22828 0,#e22828) 0 100% no-repeat,linear-gradient(0deg,#ff8c2f 0,#ff8c2f) 100% 100% no-repeat;background-size:100% 2px,100% 2px,2px 100%,2px 100%
|
||||
}
|
||||
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset button:hover{
|
||||
color:#f2f2f2;background:-webkit-gradient(linear,left top,right top,from(#e22828),to(#ff8c2f));background:linear-gradient(90deg,#e22828 0,#ff8c2f)
|
||||
}
|
||||
.ui-dropdownchecklist .ui-state-default{
|
||||
background:#1c1c1c;
|
||||
border:none;
|
||||
box-shadow:none;
|
||||
outline:none;
|
||||
cursor:pointer;
|
||||
height:2.2rem;
|
||||
line-height:2.2rem;
|
||||
}
|
||||
.ui-dropdownchecklist-group{
|
||||
font-weight:normal;
|
||||
font-style:italic;
|
||||
padding:1px 9px 1px 8px;
|
||||
}
|
||||
.ui-dropdownchecklist-selector{
|
||||
border:1px solid #e5e5e5;
|
||||
display:inline-block;
|
||||
cursor:pointer;
|
||||
padding:1px 9px 1px 8px;
|
||||
}
|
||||
.ui-dropdownchecklist-selector-wrapper{
|
||||
vertical-align:middle;
|
||||
font-size:0;
|
||||
}
|
||||
.ui-widget-header{
|
||||
border:none;
|
||||
background:#2b2b2b;
|
||||
color:#f2f2f2;
|
||||
font-weight:bold;
|
||||
}
|
||||
.ui-widget-content{
|
||||
border:1px solid #e5e5e5;
|
||||
background:#1c1c1c;
|
||||
color:#f2f2f2;
|
||||
|
||||
.ui-state-active{
|
||||
background:#262626;
|
||||
}
|
||||
.ui-dropdownchecklist-dropcontainer{
|
||||
background:#1c1c1c;
|
||||
border:1px solid #e5e5e5;
|
||||
}
|
||||
.ui-state-disabled{
|
||||
color:#f2f2f2;border-color:#6c6c6c;background:#262626;opacity:0.5;
|
||||
}
|
||||
.ui-dropdownchecklist-indent{
|
||||
padding-left:7px;
|
||||
}
|
||||
.ui-dropdownchecklist-text{
|
||||
color:#f2f2f2;
|
||||
font-size:1.3rem;
|
||||
}
|
||||
.ui-dropdownchecklist .ui-widget-content .ui-state-default{
|
||||
background:#1c1c1c;
|
||||
border:0px;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset button{
|
||||
font-family:clear-sans;font-size:1.1rem;font-weight:bold;letter-spacing:2px;text-transform:uppercase;margin:10px 12px 10px 0;padding:9px 18px;text-decoration:none;white-space:nowrap;cursor:pointer;outline:none;border-radius:4px;border:0;color:#ff8c2f;background:-webkit-gradient(linear,left top,right top,from(#e22828),to(#ff8c2f)) 0 0 no-repeat,-webkit-gradient(linear,left top,right top,from(#e22828),to(#ff8c2f)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#e22828),to(#e22828)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#ff8c2f),to(#ff8c2f)) 100% 100% no-repeat;background:linear-gradient(90deg,#e22828 0,#ff8c2f) 0 0 no-repeat,linear-gradient(90deg,#e22828 0,#ff8c2f) 0 100% no-repeat,linear-gradient(0deg,#e22828 0,#e22828) 0 100% no-repeat,linear-gradient(0deg,#ff8c2f 0,#ff8c2f) 100% 100% no-repeat;background-size:100% 2px,100% 2px,2px 100%,2px 100%
|
||||
}
|
||||
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset button:hover{
|
||||
color:#f2f2f2;background:-webkit-gradient(linear,left top,right top,from(#e22828),to(#ff8c2f));background:linear-gradient(90deg,#e22828 0,#ff8c2f)
|
||||
}
|
||||
.ui-dropdownchecklist .ui-state-default{
|
||||
background:#1c1c1c;
|
||||
border:none;
|
||||
box-shadow:none;
|
||||
outline:none;
|
||||
cursor:pointer;
|
||||
height:2.2rem;
|
||||
line-height:2.2rem;
|
||||
}
|
||||
.ui-dropdownchecklist-group{
|
||||
font-weight:normal;
|
||||
font-style:italic;
|
||||
padding:1px 9px 1px 8px;
|
||||
}
|
||||
.ui-dropdownchecklist-selector{
|
||||
border:1px solid #e5e5e5;
|
||||
display:inline-block;
|
||||
cursor:pointer;
|
||||
padding:1px 9px 1px 8px;
|
||||
}
|
||||
.ui-dropdownchecklist-selector-wrapper{
|
||||
vertical-align:middle;
|
||||
font-size:0;
|
||||
}
|
||||
.ui-widget-header{
|
||||
border:none;
|
||||
background:#2b2b2b;
|
||||
color:#f2f2f2;
|
||||
font-weight:bold;
|
||||
}
|
||||
.ui-widget-content{
|
||||
border:1px solid #e5e5e5;
|
||||
background:#1c1c1c;
|
||||
color:#f2f2f2;
|
||||
|
||||
.ui-state-active{
|
||||
background:#262626;
|
||||
}
|
||||
.ui-dropdownchecklist-dropcontainer{
|
||||
background:#1c1c1c;
|
||||
border:1px solid #e5e5e5;
|
||||
}
|
||||
.ui-state-disabled{
|
||||
color:#f2f2f2;border-color:#6c6c6c;background:#262626;opacity:0.5;
|
||||
}
|
||||
.ui-dropdownchecklist-indent{
|
||||
padding-left:7px;
|
||||
}
|
||||
.ui-dropdownchecklist-text{
|
||||
color:#f2f2f2;
|
||||
font-size:1.3rem;
|
||||
}
|
||||
.ui-dropdownchecklist .ui-widget-content .ui-state-default{
|
||||
background:#1c1c1c;
|
||||
border:0px;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset button{
|
||||
font-family:clear-sans;font-size:1.1rem;font-weight:bold;letter-spacing:2px;text-transform:uppercase;margin:10px 12px 10px 0;padding:9px 18px;text-decoration:none;white-space:nowrap;cursor:pointer;outline:none;border-radius:4px;border:0;color:#ff8c2f;background:-webkit-gradient(linear,left top,right top,from(#e22828),to(#ff8c2f)) 0 0 no-repeat,-webkit-gradient(linear,left top,right top,from(#e22828),to(#ff8c2f)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#e22828),to(#e22828)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#ff8c2f),to(#ff8c2f)) 100% 100% no-repeat;background:linear-gradient(90deg,#e22828 0,#ff8c2f) 0 0 no-repeat,linear-gradient(90deg,#e22828 0,#ff8c2f) 0 100% no-repeat,linear-gradient(0deg,#e22828 0,#e22828) 0 100% no-repeat,linear-gradient(0deg,#ff8c2f 0,#ff8c2f) 100% 100% no-repeat;background-size:100% 2px,100% 2px,2px 100%,2px 100%
|
||||
}
|
||||
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset button:hover{
|
||||
color:#f2f2f2;background:-webkit-gradient(linear,left top,right top,from(#e22828),to(#ff8c2f));background:linear-gradient(90deg,#e22828 0,#ff8c2f)
|
||||
}
|
||||
.ui-dropdownchecklist .ui-state-default{
|
||||
background:#f2f2f2;
|
||||
border:none;
|
||||
box-shadow:none;
|
||||
outline:none;
|
||||
cursor:pointer;
|
||||
height:2.2rem;
|
||||
line-height:2.2rem;
|
||||
}
|
||||
.ui-dropdownchecklist-group{
|
||||
font-weight:normal;
|
||||
font-style:italic;
|
||||
padding:1px 9px 1px 8px;
|
||||
}
|
||||
.ui-dropdownchecklist-selector{
|
||||
border:1px solid #1c1c1c;
|
||||
display:inline-block;
|
||||
cursor:pointer;
|
||||
padding:1px 9px 1px 8px;
|
||||
}
|
||||
.ui-dropdownchecklist-selector-wrapper{
|
||||
vertical-align:middle;
|
||||
font-size:0;
|
||||
}
|
||||
.ui-widget-header{
|
||||
border:none;
|
||||
background:#e3e3e3;
|
||||
color:#1c1c1c;
|
||||
font-weight:bold;
|
||||
}
|
||||
.ui-widget-content{
|
||||
border:1px solid #1c1c1c;
|
||||
background:#f2f2f2;
|
||||
color:#1c1c1c;
|
||||
|
||||
.ui-state-active{
|
||||
background:#e8e8e8;
|
||||
}
|
||||
.ui-dropdownchecklist-dropcontainer{
|
||||
background:#f2f2f2;
|
||||
border:1px solid #1c1c1c;
|
||||
}
|
||||
.ui-state-disabled{
|
||||
color:#1c1c1c;border-color:#a2a2a2;background:#e8e8e8;opacity:0.5;
|
||||
}
|
||||
.ui-dropdownchecklist-indent{
|
||||
padding-left:7px;
|
||||
}
|
||||
.ui-dropdownchecklist-text{
|
||||
color:#1c1c1c;
|
||||
font-size:1.3rem;
|
||||
}
|
||||
.ui-dropdownchecklist .ui-widget-content .ui-state-default{
|
||||
background:#f2f2f2;
|
||||
border:0px;
|
||||
}
|
||||
Reference in New Issue
Block a user