Merge branch 'develop' into feature/admin-retheme
# Conflicts: # public/themes/pterodactyl/css/pterodactyl.css
This commit is contained in:
commit
799f2ee6d9
22 changed files with 10923 additions and 528 deletions
10
.gitignore
vendored
10
.gitignore
vendored
|
@ -1,4 +1,14 @@
|
||||||
/vendor
|
/vendor
|
||||||
*.DS_Store*
|
*.DS_Store*
|
||||||
.env
|
.env
|
||||||
|
.vagrant/*
|
||||||
|
|
||||||
|
composer.lock
|
||||||
|
|
||||||
|
Homestead.yaml
|
||||||
|
Vagrantfile
|
||||||
|
Vagrantfile
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
yarn.lock
|
||||||
node_modules
|
node_modules
|
||||||
|
|
|
@ -17,9 +17,9 @@
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
// 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
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
$(window).load(function () {
|
$(document).ready(function () {
|
||||||
Socket.on('console', function (data) {
|
Socket.on('console', function (data) {
|
||||||
if (data.line.indexOf('You need to agree to the EULA in order to run the server') > -1) {
|
if (~data.line.indexOf('You need to agree to the EULA in order to run the server')) {
|
||||||
swal({
|
swal({
|
||||||
title: 'EULA Acceptance',
|
title: 'EULA Acceptance',
|
||||||
text: 'By pressing \'I Accept\' below you are indicating your agreement to the <a href="https://account.mojang.com/documents/minecraft_eula" target="_blank">Mojang EULA</a>.',
|
text: 'By pressing \'I Accept\' below you are indicating your agreement to the <a href="https://account.mojang.com/documents/minecraft_eula" target="_blank">Mojang EULA</a>.',
|
||||||
|
|
|
@ -245,3 +245,24 @@ span[aria-labelledby="select2-pUserId-container"] {
|
||||||
.nav-tabs-custom.nav-tabs-floating > .nav-tabs > li:first-child.active > a {
|
.nav-tabs-custom.nav-tabs-floating > .nav-tabs > li:first-child.active > a {
|
||||||
border-radius: 0 0 0 3px;
|
border-radius: 0 0 0 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.position-relative {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-notify {
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
bottom: 10px;
|
||||||
|
/* Browsers usually have a 17px scrollbar which is visible in the terminal */
|
||||||
|
padding: 7px 24px 7px 7px;
|
||||||
|
border-top-left-radius: 3px;
|
||||||
|
background: white;
|
||||||
|
color: black;
|
||||||
|
opacity: .5;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-notify:hover {
|
||||||
|
opacity: .9;
|
||||||
|
}
|
||||||
|
|
|
@ -17,60 +17,75 @@
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
// 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
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
$(document).ready(function () {
|
|
||||||
$('#close_reload').click(function () {
|
|
||||||
location.reload();
|
|
||||||
});
|
|
||||||
$('#do_2fa').submit(function (event) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
$.ajax({
|
var TwoFactorModal = (function () {
|
||||||
type: 'PUT',
|
|
||||||
url: Router.route('account.security.totp'),
|
function bindListeners() {
|
||||||
headers: {
|
$(document).ready(function () {
|
||||||
'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content'),
|
$('#close_reload').click(function () {
|
||||||
}
|
location.reload();
|
||||||
}).done(function (data) {
|
});
|
||||||
var image = new Image();
|
$('#do_2fa').submit(function (event) {
|
||||||
image.src = data.qrImage;
|
event.preventDefault();
|
||||||
$(image).load(function () {
|
|
||||||
$('#hide_img_load').slideUp(function () {
|
$.ajax({
|
||||||
$('#qr_image_insert').attr('src', image.src).slideDown();
|
type: 'PUT',
|
||||||
|
url: Router.route('account.security.totp'),
|
||||||
|
headers: {
|
||||||
|
'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content'),
|
||||||
|
}
|
||||||
|
}).done(function (data) {
|
||||||
|
var image = new Image();
|
||||||
|
image.src = data.qrImage;
|
||||||
|
$(image).load(function () {
|
||||||
|
$('#hide_img_load').slideUp(function () {
|
||||||
|
$('#qr_image_insert').attr('src', image.src).slideDown();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
$('#2fa_secret_insert').html(data.secret);
|
||||||
|
$('#open2fa').modal('show');
|
||||||
|
}).fail(function (jqXHR) {
|
||||||
|
alert('An error occured while attempting to load the 2FA setup modal. Please try again.');
|
||||||
|
console.error(jqXHR);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
$('#2fa_token_verify').submit(function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
$('#submit_action').html('<i class="fa fa-spinner fa-spin"></i> Submit').addClass('disabled');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
url: Router.route('account.security.totp'),
|
||||||
|
headers: {
|
||||||
|
'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content'),
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
token: $('#2fa_token').val()
|
||||||
|
}
|
||||||
|
}).done(function (data) {
|
||||||
|
$('#notice_box_2fa').hide();
|
||||||
|
if (data === 'true') {
|
||||||
|
$('#notice_box_2fa').html('<div class="alert alert-success">2-Factor Authentication has been enabled on your account. Press \'Close\' below to reload the page.</div>').slideDown();
|
||||||
|
} else {
|
||||||
|
$('#notice_box_2fa').html('<div class="alert alert-danger">The token provided was invalid.</div>').slideDown();
|
||||||
|
}
|
||||||
|
}).fail(function (jqXHR) {
|
||||||
|
$('#notice_box_2fa').html('<div class="alert alert-danger">There was an error while attempting to enable 2-Factor Authentication on this account.</div>').slideDown();
|
||||||
|
console.error(jqXHR);
|
||||||
|
}).always(function () {
|
||||||
|
$('#submit_action').html('Submit').removeClass('disabled');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
$('#2fa_secret_insert').html(data.secret);
|
|
||||||
$('#open2fa').modal('show');
|
|
||||||
}).fail(function (jqXHR) {
|
|
||||||
alert('An error occured while attempting to load the 2FA setup modal. Please try again.');
|
|
||||||
console.error(jqXHR);
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
});
|
return {
|
||||||
$('#2fa_token_verify').submit(function (event) {
|
init: function () {
|
||||||
event.preventDefault();
|
bindListeners();
|
||||||
$('#submit_action').html('<i class="fa fa-spinner fa-spin"></i> Submit').addClass('disabled');
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
type: 'POST',
|
|
||||||
url: Router.route('account.security.totp'),
|
|
||||||
headers: {
|
|
||||||
'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content'),
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
token: $('#2fa_token').val()
|
|
||||||
}
|
|
||||||
}).done(function (data) {
|
|
||||||
$('#notice_box_2fa').hide();
|
|
||||||
if (data === 'true') {
|
|
||||||
$('#notice_box_2fa').html('<div class="alert alert-success">2-Factor Authentication has been enabled on your account. Press \'Close\' below to reload the page.</div>').slideDown();
|
|
||||||
} else {
|
|
||||||
$('#notice_box_2fa').html('<div class="alert alert-danger">The token provided was invalid.</div>').slideDown();
|
|
||||||
}
|
|
||||||
}).fail(function (jqXHR) {
|
|
||||||
$('#notice_box_2fa').html('<div class="alert alert-danger">There was an error while attempting to enable 2-Factor Authentication on this account.</div>').slideDown();
|
|
||||||
console.error(jqXHR);
|
|
||||||
}).always(function () {
|
|
||||||
$('#submit_action').html('Submit').removeClass('disabled');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
TwoFactorModal.init();
|
||||||
|
|
|
@ -17,173 +17,203 @@
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
// 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
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
var CONSOLE_PUSH_COUNT = 50;
|
|
||||||
var CONSOLE_PUSH_FREQ = 200;
|
|
||||||
|
|
||||||
(function initConsole() {
|
var Console = (function () {
|
||||||
window.TerminalQueue = [];
|
var CONSOLE_PUSH_COUNT = 50;
|
||||||
window.Terminal = $('#terminal').terminal(function (command, term) {
|
var CONSOLE_PUSH_FREQ = 200;
|
||||||
Socket.emit('send command', command);
|
|
||||||
}, {
|
|
||||||
greetings: '',
|
|
||||||
name: Pterodactyl.server.uuid,
|
|
||||||
height: 450,
|
|
||||||
exit: false,
|
|
||||||
prompt: Pterodactyl.server.username + ':~$ ',
|
|
||||||
scrollOnEcho: false,
|
|
||||||
scrollBottomOffset: 5,
|
|
||||||
onBlur: function (terminal) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Socket.on('initial status', function (data) {
|
var terminalQueue;
|
||||||
Terminal.clear();
|
var terminal;
|
||||||
if (data.status === 1 || data.status === 2) {
|
|
||||||
Socket.emit('send server log');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
|
|
||||||
(function pushOutputQueue() {
|
var cpuChart;
|
||||||
if (TerminalQueue.length > CONSOLE_PUSH_COUNT) {
|
var cpuData;
|
||||||
// console throttled warning show
|
var memoryChart;
|
||||||
|
var memoryData;
|
||||||
|
var timeLabels;
|
||||||
|
|
||||||
|
var $terminalNotify;
|
||||||
|
|
||||||
|
function initConsole() {
|
||||||
|
terminalQueue = [];
|
||||||
|
terminal = $('#terminal').terminal(function (command, term) {
|
||||||
|
Socket.emit('send command', command);
|
||||||
|
}, {
|
||||||
|
greetings: '',
|
||||||
|
name: Pterodactyl.server.uuid,
|
||||||
|
height: 450,
|
||||||
|
exit: false,
|
||||||
|
prompt: Pterodactyl.server.username + ':~$ ',
|
||||||
|
scrollOnEcho: false,
|
||||||
|
scrollBottomOffset: 5,
|
||||||
|
onBlur: function (terminal) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$terminalNotify = $('#terminalNotify');
|
||||||
|
$terminalNotify.on('click', function () {
|
||||||
|
terminal.scroll_to_bottom();
|
||||||
|
$terminalNotify.addClass('hidden');
|
||||||
|
})
|
||||||
|
|
||||||
|
terminal.on('scroll', function () {
|
||||||
|
if (terminal.is_bottom()) {
|
||||||
|
$terminalNotify.addClass('hidden');
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TerminalQueue.length > 0) {
|
function initGraphs() {
|
||||||
for (var i = 0; i < CONSOLE_PUSH_COUNT && TerminalQueue.length > 0; i++) {
|
var ctc = $('#chart_cpu');
|
||||||
Terminal.echo(TerminalQueue[0]);
|
timeLabels = [];
|
||||||
TerminalQueue.shift();
|
cpuData = [];
|
||||||
}
|
cpuChart = new Chart(ctc, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: timeLabels,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: "Percent Use",
|
||||||
|
fill: false,
|
||||||
|
lineTension: 0.03,
|
||||||
|
backgroundColor: "#3c8dbc",
|
||||||
|
borderColor: "#3c8dbc",
|
||||||
|
borderCapStyle: 'butt',
|
||||||
|
borderDash: [],
|
||||||
|
borderDashOffset: 0.0,
|
||||||
|
borderJoinStyle: 'miter',
|
||||||
|
pointBorderColor: "#3c8dbc",
|
||||||
|
pointBackgroundColor: "#fff",
|
||||||
|
pointBorderWidth: 1,
|
||||||
|
pointHoverRadius: 5,
|
||||||
|
pointHoverBackgroundColor: "#3c8dbc",
|
||||||
|
pointHoverBorderColor: "rgba(220,220,220,1)",
|
||||||
|
pointHoverBorderWidth: 2,
|
||||||
|
pointRadius: 1,
|
||||||
|
pointHitRadius: 10,
|
||||||
|
data: cpuData,
|
||||||
|
spanGaps: false,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'CPU Usage (as Percent Total)'
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
animation: {
|
||||||
|
duration: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var ctm = $('#chart_memory');
|
||||||
|
memoryData = [];
|
||||||
|
memoryChart = new Chart(ctm, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: timeLabels,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: "Memory Use",
|
||||||
|
fill: false,
|
||||||
|
lineTension: 0.03,
|
||||||
|
backgroundColor: "#3c8dbc",
|
||||||
|
borderColor: "#3c8dbc",
|
||||||
|
borderCapStyle: 'butt',
|
||||||
|
borderDash: [],
|
||||||
|
borderDashOffset: 0.0,
|
||||||
|
borderJoinStyle: 'miter',
|
||||||
|
pointBorderColor: "#3c8dbc",
|
||||||
|
pointBackgroundColor: "#fff",
|
||||||
|
pointBorderWidth: 1,
|
||||||
|
pointHoverRadius: 5,
|
||||||
|
pointHoverBackgroundColor: "#3c8dbc",
|
||||||
|
pointHoverBorderColor: "rgba(220,220,220,1)",
|
||||||
|
pointHoverBorderWidth: 2,
|
||||||
|
pointRadius: 1,
|
||||||
|
pointHitRadius: 10,
|
||||||
|
data: memoryData,
|
||||||
|
spanGaps: false,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Memory Usage (in Megabytes)'
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
animation: {
|
||||||
|
duration: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
window.setTimeout(pushOutputQueue, CONSOLE_PUSH_FREQ);
|
function addSocketListeners() {
|
||||||
})();
|
// Update Listings on Initial Status
|
||||||
|
Socket.on('initial status', function (data) {
|
||||||
|
updateServerPowerControls(data.status);
|
||||||
|
|
||||||
$(document).ready(function () {
|
terminal.clear();
|
||||||
$('[data-attr="power"]').click(function (event) {
|
if (data.status === 1 || data.status === 2) {
|
||||||
if (! $(this).hasClass('disabled')) {
|
Socket.emit('send server log');
|
||||||
Socket.emit('set status', $(this).data('action'));
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update Listings on Status
|
||||||
|
Socket.on('status', function (data) {
|
||||||
|
updateServerPowerControls(data.status);
|
||||||
|
});
|
||||||
|
|
||||||
|
Socket.on('console', function (data) {
|
||||||
|
terminalQueue.push(data.line);
|
||||||
|
});
|
||||||
|
|
||||||
|
Socket.on('proc', function (proc) {
|
||||||
|
if (cpuData.length > 10) {
|
||||||
|
cpuData.shift();
|
||||||
|
memoryData.shift();
|
||||||
|
timeLabels.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
var cpuUse = (Pterodactyl.server.cpu > 0) ? parseFloat(((proc.data.cpu.total / Pterodactyl.server.cpu) * 100).toFixed(3).toString()) : proc.data.cpu.total;
|
||||||
|
cpuData.push(cpuUse);
|
||||||
|
memoryData.push(parseInt(proc.data.memory.total / (1024 * 1024)));
|
||||||
|
|
||||||
|
var m = new Date();
|
||||||
|
timeLabels.push($.format.date(new Date(), 'HH:mm:ss'));
|
||||||
|
|
||||||
|
cpuChart.update();
|
||||||
|
memoryChart.update();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function pushOutputQueue() {
|
||||||
|
if (terminalQueue.length > CONSOLE_PUSH_COUNT) {
|
||||||
|
// console throttled warning show
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
var ctc = $('#chart_cpu');
|
if (terminalQueue.length > 0) {
|
||||||
var timeLabels = [];
|
for (var i = 0; i < CONSOLE_PUSH_COUNT && terminalQueue.length > 0; i++) {
|
||||||
var cpuData = [];
|
terminal.echo(terminalQueue[0]);
|
||||||
var CPUChart = new Chart(ctc, {
|
terminalQueue.shift();
|
||||||
type: 'line',
|
}
|
||||||
data: {
|
|
||||||
labels: timeLabels,
|
// Show
|
||||||
datasets: [
|
if (!terminal.is_bottom()) {
|
||||||
{
|
$terminalNotify.removeClass('hidden');
|
||||||
label: "Percent Use",
|
|
||||||
fill: false,
|
|
||||||
lineTension: 0.03,
|
|
||||||
backgroundColor: "#00A1CB",
|
|
||||||
borderColor: "#00A1CB",
|
|
||||||
borderCapStyle: 'butt',
|
|
||||||
borderDash: [],
|
|
||||||
borderDashOffset: 0.0,
|
|
||||||
borderJoinStyle: 'miter',
|
|
||||||
pointBorderColor: "rgba(75,192,192,1)",
|
|
||||||
pointBackgroundColor: "#fff",
|
|
||||||
pointBorderWidth: 1,
|
|
||||||
pointHoverRadius: 5,
|
|
||||||
pointHoverBackgroundColor: "rgba(75,192,192,1)",
|
|
||||||
pointHoverBorderColor: "rgba(220,220,220,1)",
|
|
||||||
pointHoverBorderWidth: 2,
|
|
||||||
pointRadius: 1,
|
|
||||||
pointHitRadius: 10,
|
|
||||||
data: cpuData,
|
|
||||||
spanGaps: false,
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
title: {
|
|
||||||
display: true,
|
|
||||||
text: 'CPU Usage (as Percent Total)'
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
animation: {
|
|
||||||
duration: 1,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
var ctm = $('#chart_memory');
|
window.setTimeout(pushOutputQueue, CONSOLE_PUSH_FREQ);
|
||||||
var memoryData = [];
|
}
|
||||||
var MemoryChart = new Chart(ctm, {
|
|
||||||
type: 'line',
|
|
||||||
data: {
|
|
||||||
labels: timeLabels,
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: "Memory Use",
|
|
||||||
fill: false,
|
|
||||||
lineTension: 0.03,
|
|
||||||
backgroundColor: "#01A4A4",
|
|
||||||
borderColor: "#01A4A4",
|
|
||||||
borderCapStyle: 'butt',
|
|
||||||
borderDash: [],
|
|
||||||
borderDashOffset: 0.0,
|
|
||||||
borderJoinStyle: 'miter',
|
|
||||||
pointBorderColor: "rgba(75,192,192,1)",
|
|
||||||
pointBackgroundColor: "#fff",
|
|
||||||
pointBorderWidth: 1,
|
|
||||||
pointHoverRadius: 5,
|
|
||||||
pointHoverBackgroundColor: "rgba(75,192,192,1)",
|
|
||||||
pointHoverBorderColor: "rgba(220,220,220,1)",
|
|
||||||
pointHoverBorderWidth: 2,
|
|
||||||
pointRadius: 1,
|
|
||||||
pointHitRadius: 10,
|
|
||||||
data: memoryData,
|
|
||||||
spanGaps: false,
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
title: {
|
|
||||||
display: true,
|
|
||||||
text: 'Memory Usage (in Megabytes)'
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
animation: {
|
|
||||||
duration: 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Socket.on('proc', function (proc) {
|
|
||||||
if (cpuData.length > 10) {
|
|
||||||
cpuData.shift();
|
|
||||||
memoryData.shift();
|
|
||||||
timeLabels.shift();
|
|
||||||
}
|
|
||||||
|
|
||||||
var cpuUse = (Pterodactyl.server.cpu > 0) ? parseFloat(((proc.data.cpu.total / Pterodactyl.server.cpu) * 100).toFixed(3).toString()) : proc.data.cpu.total;
|
|
||||||
cpuData.push(cpuUse);
|
|
||||||
memoryData.push(parseInt(proc.data.memory.total / (1024 * 1024)));
|
|
||||||
|
|
||||||
var m = new Date();
|
|
||||||
timeLabels.push($.format.date(new Date(), 'HH:mm:ss'));
|
|
||||||
|
|
||||||
CPUChart.update();
|
|
||||||
MemoryChart.update();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update Listings on Initial Status
|
|
||||||
Socket.on('initial status', function (data) {
|
|
||||||
updateServerPowerControls(data.status);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update Listings on Status
|
|
||||||
Socket.on('status', function (data) {
|
|
||||||
updateServerPowerControls(data.status);
|
|
||||||
});
|
|
||||||
|
|
||||||
function updateServerPowerControls (data) {
|
function updateServerPowerControls (data) {
|
||||||
// Server is On or Starting
|
// Server is On or Starting
|
||||||
|
@ -203,4 +233,33 @@ $(document).ready(function () {
|
||||||
$('[data-attr="power"][data-action="kill"]').addClass('disabled');
|
$('[data-attr="power"][data-action="kill"]').addClass('disabled');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
init: function () {
|
||||||
|
|
||||||
|
initConsole();
|
||||||
|
pushOutputQueue();
|
||||||
|
initGraphs();
|
||||||
|
addSocketListeners();
|
||||||
|
|
||||||
|
$('[data-attr="power"]').click(function (event) {
|
||||||
|
if (! $(this).hasClass('disabled')) {
|
||||||
|
Socket.emit('set status', $(this).data('action'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getTerminal: function () {
|
||||||
|
return terminal
|
||||||
|
},
|
||||||
|
|
||||||
|
getTerminalQueue: function () {
|
||||||
|
return terminalQueue
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
Console.init();
|
||||||
});
|
});
|
||||||
|
|
|
@ -29,15 +29,22 @@ class ActionsClass {
|
||||||
this.element = undefined;
|
this.element = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
folder() {
|
folder(path) {
|
||||||
const nameBlock = $(this.element).find('td[data-identifier="name"]');
|
let inputValue
|
||||||
const currentName = decodeURIComponent(nameBlock.attr('data-name'));
|
if (path) {
|
||||||
const currentPath = decodeURIComponent(nameBlock.data('path'));
|
inputValue = path
|
||||||
|
} else {
|
||||||
|
const nameBlock = $(this.element).find('td[data-identifier="name"]');
|
||||||
|
const currentName = decodeURIComponent(nameBlock.data('name'));
|
||||||
|
const currentPath = decodeURIComponent(nameBlock.data('path'));
|
||||||
|
|
||||||
let inputValue = `${currentPath}${currentName}/`;
|
if ($(this.element).data('type') === 'file') {
|
||||||
if ($(this.element).data('type') === 'file') {
|
inputValue = currentPath;
|
||||||
inputValue = currentPath;
|
} else {
|
||||||
|
inputValue = `${currentPath}${currentName}/`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
swal({
|
swal({
|
||||||
type: 'input',
|
type: 'input',
|
||||||
title: 'Create Folder',
|
title: 'Create Folder',
|
||||||
|
|
|
@ -33,7 +33,7 @@ class ContextMenuClass {
|
||||||
$(document).find('#fileOptionMenu').remove();
|
$(document).find('#fileOptionMenu').remove();
|
||||||
if (!_.isNull(this.activeLine)) this.activeLine.removeClass('active');
|
if (!_.isNull(this.activeLine)) this.activeLine.removeClass('active');
|
||||||
|
|
||||||
let newFilePath = $('#headerTableRow').attr('data-currentDir');
|
let newFilePath = $('#file_listing').data('current-dir');
|
||||||
if (parent.data('type') === 'folder') {
|
if (parent.data('type') === 'folder') {
|
||||||
const nameBlock = parent.find('td[data-identifier="name"]');
|
const nameBlock = parent.find('td[data-identifier="name"]');
|
||||||
const currentName = decodeURIComponent(nameBlock.attr('data-name'));
|
const currentName = decodeURIComponent(nameBlock.attr('data-name'));
|
||||||
|
|
|
@ -44,6 +44,7 @@ class FileManager {
|
||||||
$('#load_files').slideUp(10).html(data).slideDown(10, () => {
|
$('#load_files').slideUp(10).html(data).slideDown(10, () => {
|
||||||
ContextMenu.run();
|
ContextMenu.run();
|
||||||
this.reloadFilesButton();
|
this.reloadFilesButton();
|
||||||
|
this.addFolderButton();
|
||||||
if (_.isFunction(next)) {
|
if (_.isFunction(next)) {
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
@ -82,6 +83,12 @@ class FileManager {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addFolderButton() {
|
||||||
|
$('i[data-action="add-folder"]').unbind().on('click', () => {
|
||||||
|
new ActionsClass().folder($('#file_listing').data('current-dir') || '/');
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
decodeHash() {
|
decodeHash() {
|
||||||
return decodeURIComponent(window.location.hash.substring(1));
|
return decodeURIComponent(window.location.hash.substring(1));
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,7 @@
|
||||||
window.onbeforeunload = function () {
|
window.onbeforeunload = function () {
|
||||||
return 'A file upload in in progress, are you sure you want to continue?';
|
return 'A file upload in in progress, are you sure you want to continue?';
|
||||||
};
|
};
|
||||||
event.file.meta.path = $('#headerTableRow').attr('data-currentdir');
|
event.file.meta.path = $('#file_listing').data('current-dir');
|
||||||
event.file.meta.identifier = Math.random().toString(36).slice(2);
|
event.file.meta.identifier = Math.random().toString(36).slice(2);
|
||||||
|
|
||||||
$('#append_files_to').append('<tr id="file-upload-' + event.file.meta.identifier +'"> \
|
$('#append_files_to').append('<tr id="file-upload-' + event.file.meta.identifier +'"> \
|
||||||
|
|
|
@ -17,83 +17,94 @@
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
// 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
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
(function initSocket() {
|
|
||||||
if (typeof $.notifyDefaults !== 'function') {
|
var Server = (function () {
|
||||||
console.error('Notify does not appear to be loaded.');
|
|
||||||
return;
|
function initSocket() {
|
||||||
|
if (typeof $.notifyDefaults !== 'function') {
|
||||||
|
console.error('Notify does not appear to be loaded.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof io !== 'function') {
|
||||||
|
console.error('Socket.io is reqired to use this panel.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$.notifyDefaults({
|
||||||
|
placement: {
|
||||||
|
from: 'bottom',
|
||||||
|
align: 'right'
|
||||||
|
},
|
||||||
|
newest_on_top: true,
|
||||||
|
delay: 2000,
|
||||||
|
animate: {
|
||||||
|
enter: 'animated zoomInDown',
|
||||||
|
exit: 'animated zoomOutDown'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var notifySocketError = false;
|
||||||
|
|
||||||
|
window.Socket = io(Pterodactyl.node.scheme + '://' + Pterodactyl.node.fqdn + ':' + Pterodactyl.node.daemonListen + '/ws/' + Pterodactyl.server.uuid, {
|
||||||
|
'query': 'token=' + Pterodactyl.server.daemonSecret,
|
||||||
|
});
|
||||||
|
|
||||||
|
Socket.io.on('connect_error', function (err) {
|
||||||
|
if(typeof notifySocketError !== 'object') {
|
||||||
|
notifySocketError = $.notify({
|
||||||
|
message: 'There was an error attempting to establish a WebSocket connection to the Daemon. This panel will not work as expected.<br /><br />' + err,
|
||||||
|
}, {
|
||||||
|
type: 'danger',
|
||||||
|
delay: 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Connected to Socket Successfully
|
||||||
|
Socket.on('connect', function () {
|
||||||
|
if (notifySocketError !== false) {
|
||||||
|
notifySocketError.close();
|
||||||
|
notifySocketError = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Socket.on('initial status', function (data) {
|
||||||
|
setStatusIcon(data.status);
|
||||||
|
});
|
||||||
|
|
||||||
|
Socket.on('status', function (data) {
|
||||||
|
setStatusIcon(data.status);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof io !== 'function') {
|
function setStatusIcon(status) {
|
||||||
console.error('Socket.io is reqired to use this panel.');
|
switch (status) {
|
||||||
return;
|
case 0:
|
||||||
|
$('#server_status_icon').html('<i class="fa fa-circle text-danger"></i> Offline');
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
$('#server_status_icon').html('<i class="fa fa-circle text-success"></i> Online');
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
$('#server_status_icon').html('<i class="fa fa-circle text-warning"></i> Starting');
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
$('#server_status_icon').html('<i class="fa fa-circle text-warning"></i> Stopping');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$.notifyDefaults({
|
return {
|
||||||
placement: {
|
init: function () {
|
||||||
from: 'bottom',
|
initSocket();
|
||||||
align: 'right'
|
|
||||||
},
|
},
|
||||||
newest_on_top: true,
|
|
||||||
delay: 2000,
|
|
||||||
animate: {
|
|
||||||
enter: 'animated zoomInDown',
|
|
||||||
exit: 'animated zoomOutDown'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var notifySocketError = false;
|
setStatusIcon: setStatusIcon,
|
||||||
|
}
|
||||||
|
|
||||||
window.Socket = io(Pterodactyl.node.scheme + '://' + Pterodactyl.node.fqdn + ':' + Pterodactyl.node.daemonListen + '/ws/' + Pterodactyl.server.uuid, {
|
|
||||||
'query': 'token=' + Pterodactyl.server.daemonSecret,
|
|
||||||
});
|
|
||||||
|
|
||||||
Socket.io.on('connect_error', function (err) {
|
|
||||||
if(typeof notifySocketError !== 'object') {
|
|
||||||
notifySocketError = $.notify({
|
|
||||||
message: 'There was an error attempting to establish a WebSocket connection to the Daemon. This panel will not work as expected.<br /><br />' + err,
|
|
||||||
}, {
|
|
||||||
type: 'danger',
|
|
||||||
delay: 0
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Connected to Socket Successfully
|
|
||||||
Socket.on('connect', function () {
|
|
||||||
if (notifySocketError !== false) {
|
|
||||||
notifySocketError.close();
|
|
||||||
notifySocketError = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Socket.on('initial status', function (data) {
|
|
||||||
setStatusIcon(data.status);
|
|
||||||
});
|
|
||||||
|
|
||||||
Socket.on('status', function (data) {
|
|
||||||
setStatusIcon(data.status);
|
|
||||||
});
|
|
||||||
|
|
||||||
Socket.on('console', function (data) {
|
|
||||||
TerminalQueue.push(data.line);
|
|
||||||
});
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
function setStatusIcon(status) {
|
Server.init();
|
||||||
switch (status) {
|
|
||||||
case 0:
|
|
||||||
$('#server_status_icon').html('<i class="fa fa-circle text-danger"></i> Offline');
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
$('#server_status_icon').html('<i class="fa fa-circle text-success"></i> Online');
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
$('#server_status_icon').html('<i class="fa fa-circle text-warning"></i> Starting');
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
$('#server_status_icon').html('<i class="fa fa-circle text-warning"></i> Stopping');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -17,14 +17,16 @@
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
// 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
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
var Status = {
|
|
||||||
0: 'Offline',
|
|
||||||
1: 'Online',
|
|
||||||
2: 'Starting',
|
|
||||||
3: 'Stopping'
|
|
||||||
};
|
|
||||||
|
|
||||||
(function updateServerStatus () {
|
var ServerList = (function () {
|
||||||
|
|
||||||
|
var Status = {
|
||||||
|
0: 'Offline',
|
||||||
|
1: 'Online',
|
||||||
|
2: 'Starting',
|
||||||
|
3: 'Stopping'
|
||||||
|
};
|
||||||
|
|
||||||
$('.dynamic-update').each(function (index, data) {
|
$('.dynamic-update').each(function (index, data) {
|
||||||
var element = $(this);
|
var element = $(this);
|
||||||
var serverShortUUID = $(this).data('server');
|
var serverShortUUID = $(this).data('server');
|
||||||
|
@ -67,16 +69,32 @@ var Status = {
|
||||||
if (cpuMax !== 0) {
|
if (cpuMax !== 0) {
|
||||||
currentCpu = parseFloat(((data.proc.cpu.total / cpuMax) * 100).toFixed(2).toString());
|
currentCpu = parseFloat(((data.proc.cpu.total / cpuMax) * 100).toFixed(2).toString());
|
||||||
}
|
}
|
||||||
element.find('[data-action="memory"]').html(parseInt(data.proc.memory.total / (1024 * 1024)));
|
if (data.status !== 0) {
|
||||||
element.find('[data-action="cpu"]').html(currentCpu);
|
var cpuMax = element.find('[data-action="cpu"]').data('cpumax');
|
||||||
} else {
|
var currentCpu = data.proc.cpu.total;
|
||||||
element.find('[data-action="memory"]').html('--');
|
if (cpuMax !== 0) {
|
||||||
element.find('[data-action="cpu"]').html('--');
|
currentCpu = parseFloat(((data.proc.cpu.total / cpuMax) * 100).toFixed(2).toString());
|
||||||
}
|
}
|
||||||
}).fail(function (jqXHR) {
|
element.find('[data-action="memory"]').html(parseInt(data.proc.memory.total / (1024 * 1024)));
|
||||||
console.error(jqXHR);
|
element.find('[data-action="cpu"]').html(currentCpu);
|
||||||
element.find('[data-action="status"]').html('<span class="label label-default">Error</span>');
|
} else {
|
||||||
|
element.find('[data-action="memory"]').html('--');
|
||||||
|
element.find('[data-action="cpu"]').html('--');
|
||||||
|
}
|
||||||
|
}).fail(function (jqXHR) {
|
||||||
|
console.error(jqXHR);
|
||||||
|
element.find('[data-action="status"]').html('<span class="label label-default">Error</span>');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
setTimeout(updateServerStatus, 10000);
|
||||||
setTimeout(updateServerStatus, 10000);
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
init: function () {
|
||||||
|
updateServerStatus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
ServerList.init();
|
||||||
|
|
|
@ -17,86 +17,99 @@
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
// 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
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
(function initTaskFunctions() {
|
|
||||||
$('[data-action="delete-task"]').click(function (event) {
|
var Tasks = (function () {
|
||||||
var self = $(this);
|
|
||||||
swal({
|
function initTaskFunctions() {
|
||||||
type: 'error',
|
$('[data-action="delete-task"]').click(function (event) {
|
||||||
title: 'Delete Task?',
|
var self = $(this);
|
||||||
text: 'Are you sure you want to delete this task? There is no undo.',
|
swal({
|
||||||
showCancelButton: true,
|
type: 'error',
|
||||||
allowOutsideClick: true,
|
title: 'Delete Task?',
|
||||||
closeOnConfirm: false,
|
text: 'Are you sure you want to delete this task? There is no undo.',
|
||||||
confirmButtonText: 'Delete Task',
|
showCancelButton: true,
|
||||||
confirmButtonColor: '#d9534f',
|
allowOutsideClick: true,
|
||||||
showLoaderOnConfirm: true
|
closeOnConfirm: false,
|
||||||
}, function () {
|
confirmButtonText: 'Delete Task',
|
||||||
$.ajax({
|
confirmButtonColor: '#d9534f',
|
||||||
method: 'DELETE',
|
showLoaderOnConfirm: true
|
||||||
url: Router.route('server.tasks.delete', {
|
}, function () {
|
||||||
server: Pterodactyl.server.uuidShort,
|
$.ajax({
|
||||||
id: self.data('id'),
|
method: 'DELETE',
|
||||||
}),
|
url: Router.route('server.tasks.delete', {
|
||||||
headers: {
|
server: Pterodactyl.server.uuidShort,
|
||||||
'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content'),
|
id: self.data('id'),
|
||||||
}
|
}),
|
||||||
}).done(function (data) {
|
headers: {
|
||||||
swal({
|
'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content'),
|
||||||
type: 'success',
|
}
|
||||||
title: '',
|
}).done(function (data) {
|
||||||
text: 'Task has been deleted.'
|
swal({
|
||||||
});
|
type: 'success',
|
||||||
self.parent().parent().slideUp();
|
title: '',
|
||||||
}).fail(function (jqXHR) {
|
text: 'Task has been deleted.'
|
||||||
console.error(jqXHR);
|
});
|
||||||
swal({
|
self.parent().parent().slideUp();
|
||||||
type: 'error',
|
}).fail(function (jqXHR) {
|
||||||
title: 'Whoops!',
|
console.error(jqXHR);
|
||||||
text: 'An error occured while attempting to delete this task.'
|
swal({
|
||||||
|
type: 'error',
|
||||||
|
title: 'Whoops!',
|
||||||
|
text: 'An error occured while attempting to delete this task.'
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
$('[data-action="toggle-task"]').click(function (event) {
|
||||||
$('[data-action="toggle-task"]').click(function (event) {
|
var self = $(this);
|
||||||
var self = $(this);
|
swal({
|
||||||
swal({
|
type: 'info',
|
||||||
type: 'info',
|
title: 'Toggle Task',
|
||||||
title: 'Toggle Task',
|
text: 'This will toggle the selected task.',
|
||||||
text: 'This will toggle the selected task.',
|
showCancelButton: true,
|
||||||
showCancelButton: true,
|
allowOutsideClick: true,
|
||||||
allowOutsideClick: true,
|
closeOnConfirm: false,
|
||||||
closeOnConfirm: false,
|
confirmButtonText: 'Continue',
|
||||||
confirmButtonText: 'Continue',
|
showLoaderOnConfirm: true
|
||||||
showLoaderOnConfirm: true
|
}, function () {
|
||||||
}, function () {
|
$.ajax({
|
||||||
$.ajax({
|
method: 'POST',
|
||||||
method: 'POST',
|
url: Router.route('server.tasks.toggle', {
|
||||||
url: Router.route('server.tasks.toggle', {
|
server: Pterodactyl.server.uuidShort,
|
||||||
server: Pterodactyl.server.uuidShort,
|
id: self.data('id'),
|
||||||
id: self.data('id'),
|
}),
|
||||||
}),
|
headers: {
|
||||||
headers: {
|
'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content'),
|
||||||
'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content'),
|
}
|
||||||
}
|
}).done(function (data) {
|
||||||
}).done(function (data) {
|
swal({
|
||||||
swal({
|
type: 'success',
|
||||||
type: 'success',
|
title: '',
|
||||||
title: '',
|
text: 'Task has been toggled.'
|
||||||
text: 'Task has been toggled.'
|
});
|
||||||
});
|
if (data.status !== 1) {
|
||||||
if (data.status !== 1) {
|
self.parent().parent().addClass('muted muted-hover');
|
||||||
self.parent().parent().addClass('muted muted-hover');
|
} else {
|
||||||
} else {
|
self.parent().parent().removeClass('muted muted-hover');
|
||||||
self.parent().parent().removeClass('muted muted-hover');
|
}
|
||||||
}
|
}).fail(function (jqXHR) {
|
||||||
}).fail(function (jqXHR) {
|
console.error(jqXHR);
|
||||||
console.error(jqXHR);
|
swal({
|
||||||
swal({
|
type: 'error',
|
||||||
type: 'error',
|
title: 'Whoops!',
|
||||||
title: 'Whoops!',
|
text: 'An error occured while attempting to toggle this task.'
|
||||||
text: 'An error occured while attempting to toggle this task.'
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
init: function () {
|
||||||
|
initTaskFunctions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
Tasks.init();
|
||||||
|
|
10220
public/themes/pterodactyl/vendor/jquery/jquery.js
vendored
Normal file
10220
public/themes/pterodactyl/vendor/jquery/jquery.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
4
public/themes/pterodactyl/vendor/jquery/jquery.min.js
vendored
Normal file
4
public/themes/pterodactyl/vendor/jquery/jquery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/themes/pterodactyl/vendor/jquery/jquery.min.map
vendored
Normal file
1
public/themes/pterodactyl/vendor/jquery/jquery.min.map
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -212,6 +212,7 @@ return [
|
||||||
'size' => 'Size',
|
'size' => 'Size',
|
||||||
'last_modified' => 'Last Modified',
|
'last_modified' => 'Last Modified',
|
||||||
'add_new' => 'Add New File',
|
'add_new' => 'Add New File',
|
||||||
|
'add_folder' => 'Add New Folder',
|
||||||
'edit' => [
|
'edit' => [
|
||||||
'header' => 'Edit File',
|
'header' => 'Edit File',
|
||||||
'header_sub' => 'Make modifications to a file from the web.',
|
'header_sub' => 'Make modifications to a file from the web.',
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
Copyright © 2015 - {{ date('Y') }} <a href="https://pterodactyl.io/" target="_blank">Pterodactyl Software</a>.<br />
|
Copyright © 2015 - {{ date('Y') }} <a href="https://pterodactyl.io/" target="_blank">Pterodactyl Software</a>.<br />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{!! Theme::js('js/vendor/jquery/jquery.min.js') !!}
|
{!! Theme::js('vendor/jquery/jquery.min.js') !!}
|
||||||
{!! Theme::js('vendor/bootstrap/bootstrap.min.js') !!}
|
{!! Theme::js('vendor/bootstrap/bootstrap.min.js') !!}
|
||||||
|
|
||||||
@if(config('app.phrase_in_context')) {!! Theme::js('js/phraseapp.js') !!} @endif
|
@if(config('app.phrase_in_context')) {!! Theme::js('js/phraseapp.js') !!} @endif
|
||||||
|
|
|
@ -65,7 +65,7 @@
|
||||||
</div>
|
</div>
|
||||||
@section('footer-scripts')
|
@section('footer-scripts')
|
||||||
{!! Theme::js('js/laroute.js') !!}
|
{!! Theme::js('js/laroute.js') !!}
|
||||||
{!! Theme::js('js/vendor/jquery/jquery.min.js') !!}
|
{!! Theme::js('vendor/jquery/jquery.min.js') !!}
|
||||||
{!! Theme::js('vendor/bootstrap/bootstrap.min.js') !!}
|
{!! Theme::js('vendor/bootstrap/bootstrap.min.js') !!}
|
||||||
{!! Theme::js('vendor/slimscroll/jquery.slimscroll.min.js') !!}
|
{!! Theme::js('vendor/slimscroll/jquery.slimscroll.min.js') !!}
|
||||||
{!! Theme::js('vendor/adminlte/app.min.js') !!}
|
{!! Theme::js('vendor/adminlte/app.min.js') !!}
|
||||||
|
|
|
@ -266,7 +266,7 @@
|
||||||
</div>
|
</div>
|
||||||
@section('footer-scripts')
|
@section('footer-scripts')
|
||||||
{!! Theme::js('js/laroute.js') !!}
|
{!! Theme::js('js/laroute.js') !!}
|
||||||
{!! Theme::js('js/vendor/jquery/jquery.min.js') !!}
|
{!! Theme::js('vendor/jquery/jquery.min.js') !!}
|
||||||
{!! Theme::js('vendor/sweetalert/sweetalert.min.js') !!}
|
{!! Theme::js('vendor/sweetalert/sweetalert.min.js') !!}
|
||||||
{!! Theme::js('vendor/bootstrap/bootstrap.min.js') !!}
|
{!! Theme::js('vendor/bootstrap/bootstrap.min.js') !!}
|
||||||
{!! Theme::js('vendor/slimscroll/jquery.slimscroll.min.js') !!}
|
{!! Theme::js('vendor/slimscroll/jquery.slimscroll.min.js') !!}
|
||||||
|
|
|
@ -38,8 +38,10 @@
|
||||||
<div class="col-xs-12">
|
<div class="col-xs-12">
|
||||||
<div class="box box-primary">
|
<div class="box box-primary">
|
||||||
<div class="overlay file-overlay"><i class="fa fa-refresh fa-spin"></i></div>
|
<div class="overlay file-overlay"><i class="fa fa-refresh fa-spin"></i></div>
|
||||||
<div class="box-body table-responsive no-padding" id="load_files">
|
<div id="load_files">
|
||||||
<div class="callout callout-info" style="margin:10px;">@lang('server.files.loading')</div>
|
<div class="box-body table-responsive no-padding">
|
||||||
|
<div class="callout callout-info" style="margin:10px;">@lang('server.files.loading')</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer with-border">
|
<div class="box-footer with-border">
|
||||||
<p class="text-muted small" style="margin: 0 0 2px;">@lang('server.files.path', ['path' => '<code>/home/container</code>', 'size' => '<code>' . $node->upload_size . ' MB</code>'])</p>
|
<p class="text-muted small" style="margin: 0 0 2px;">@lang('server.files.path', ['path' => '<code>/home/container</code>', 'size' => '<code>' . $node->upload_size . ' MB</code>'])</p>
|
||||||
|
|
|
@ -17,148 +17,151 @@
|
||||||
{{-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, --}}
|
{{-- 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 --}}
|
{{-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE --}}
|
||||||
{{-- SOFTWARE. --}}
|
{{-- SOFTWARE. --}}
|
||||||
<table class="table table-hover" id="file_listing">
|
|
||||||
<thead>
|
<div class="box-header">
|
||||||
<tr>
|
<i class="fa fa-refresh muted muted-hover use-pointer" data-action="reload-files"></i>
|
||||||
<th style="width:2%;text-align:center;" class="middle"><i class="fa fa-refresh muted muted-hover use-pointer" data-action="reload-files"></i></th>
|
<code class="box-title">/home/container{{ $directory['header'] }}</code>
|
||||||
<th style="width:55%">@lang('server.files.file_name')</th>
|
<a class="text-muted">
|
||||||
<th style="width:15%">@lang('server.files.size')</th>
|
<i class="fa fa-plus" data-action="add-folder" data-toggle="tooltip" data-placement="top" title="@lang('server.files.add_folder')"></i>
|
||||||
<th style="width:20%">@lang('server.files.last_modified')</th>
|
</a>
|
||||||
<th style="width:8%">
|
<div class="box-tools pull-right">
|
||||||
<label class="btn btn-primary btn-xs btn-file">
|
<a class="btn btn-primary btn-sm" href="/server/{{ $server->uuidShort }}/files/add/@if($directory['header'] !== '')?dir={{ $directory['header'] }}@endif">
|
||||||
Upload <input type="file" id="files_touch_target" style="display: none;"/>
|
<i class="fa fa-file"></i> Create File
|
||||||
</label>
|
</a>
|
||||||
</th>
|
<a class="btn btn-primary btn-sm btn-icon btn-file">
|
||||||
</tr>
|
<i class="fa fa-upload"></i> Upload <input type="file" id="files_touch_target" style="display: none;">
|
||||||
<tr id="headerTableRow" data-currentdir="{{ $directory['header'] }}">
|
</a>
|
||||||
<th class="middle"><i class="fa fa-folder-open"></i></th>
|
</div>
|
||||||
<th colspan="4" class="middle">
|
</div>
|
||||||
<code>/home/container{{ $directory['header'] }}</code>
|
<div class="box-body table-responsive no-padding">
|
||||||
<small>
|
<table class="table table-hover" id="file_listing" data-current-dir="{{ $directory['header'] }}">
|
||||||
<a href="/server/{{ $server->uuidShort }}/files/add/@if($directory['header'] !== '')?dir={{ $directory['header'] }}@endif" class="text-muted">
|
<thead>
|
||||||
<i class="fa fa-plus" data-toggle="tooltip" data-placement="top" title="@lang('server.files.add_new')"></i>
|
<tr>
|
||||||
</a>
|
<th style="width:2%;" class="middle"></th>
|
||||||
</small>
|
<th style="width:55%">@lang('server.files.file_name')</th>
|
||||||
</th>
|
<th style="width:15%" class="hidden-xs">@lang('server.files.size')</th>
|
||||||
</tr>
|
<th style="width:20%" class="hidden-xs">@lang('server.files.last_modified')</th>
|
||||||
</thead>
|
<th style="width:8%"></th>
|
||||||
<tbody id="append_files_to">
|
|
||||||
@if (isset($directory['first']) && $directory['first'] === true)
|
|
||||||
<tr data-type="disabled">
|
|
||||||
<td class="middle"><i class="fa fa-folder" style="margin-left: 0.859px;"></i></td>
|
|
||||||
<td><a href="/server/{{ $server->uuidShort }}/files" data-action="directory-view">←</a></td>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
@endif
|
</thead>
|
||||||
@if (isset($directory['show']) && $directory['show'] === true)
|
<tbody id="append_files_to">
|
||||||
<tr data-type="disabled">
|
@if (isset($directory['first']) && $directory['first'] === true)
|
||||||
<td class="middle"><i class="fa fa-folder" style="margin-left: 0.859px;"></i></td>
|
<tr data-type="disabled">
|
||||||
<td data-name="{{ rawurlencode($directory['link']) }}">
|
<td><i class="fa fa-folder" style="margin-left: 0.859px;"></i></td>
|
||||||
<a href="/server/{{ $server->uuidShort }}/files" data-action="directory-view">← {{ $directory['link_show'] }}</a>
|
<td><a href="/server/{{ $server->uuidShort }}/files" data-action="directory-view">←</a></a></td>
|
||||||
</td>
|
<td class="hidden-xs"></td>
|
||||||
<td></td>
|
<td class="hidden-xs"></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
</tr>
|
||||||
</tr>
|
@endif
|
||||||
@endif
|
@if (isset($directory['show']) && $directory['show'] === true)
|
||||||
@foreach ($folders as $folder)
|
<tr data-type="disabled">
|
||||||
<tr data-type="folder">
|
<td><i class="fa fa-folder" style="margin-left: 0.859px;"></i></td>
|
||||||
<td data-identifier="type" class="middle"><i class="fa fa-folder" style="margin-left: 0.859px;"></i></td>
|
<td data-name="{{ rawurlencode($directory['link']) }}">
|
||||||
<td data-identifier="name" data-name="{{ rawurlencode($folder['entry']) }}" data-path="@if($folder['directory'] !== ''){{ rawurlencode($folder['directory']) }}@endif/">
|
<a href="/server/{{ $server->uuidShort }}/files" data-action="directory-view">← {{ $directory['link_show'] }}</a>
|
||||||
<a href="/server/{{ $server->uuidShort }}/files" data-action="directory-view">{{ $folder['entry'] }}</a>
|
</td>
|
||||||
</td>
|
<td class="hidden-xs"></td>
|
||||||
<td data-identifier="size">{{ $folder['size'] }}</td>
|
<td class="hidden-xs"></td>
|
||||||
<td data-identifier="modified">
|
<td></td>
|
||||||
<?php $carbon = Carbon::createFromTimestamp($folder['date'])->timezone(env('APP_TIMEZONE', 'America/New_York')); ?>
|
</tr>
|
||||||
@if($carbon->diffInMinutes(Carbon::now()) > 60)
|
@endif
|
||||||
{{ $carbon->format('m/d/y H:i:s') }}
|
@foreach ($folders as $folder)
|
||||||
@elseif($carbon->diffInSeconds(Carbon::now()) < 5 || $carbon->isFuture())
|
<tr data-type="folder">
|
||||||
<em>@lang('server.files.seconds_ago')</em>
|
<td data-identifier="type" class="middle"><i class="fa fa-folder" style="margin-left: 0.859px;"></i></td>
|
||||||
@else
|
<td data-identifier="name" data-name="{{ rawurlencode($folder['entry']) }}" data-path="@if($folder['directory'] !== ''){{ rawurlencode($folder['directory']) }}@endif/">
|
||||||
{{ $carbon->diffForHumans() }}
|
<a href="/server/{{ $server->uuidShort }}/files" data-action="directory-view">{{ $folder['entry'] }}</a>
|
||||||
@endif
|
</td>
|
||||||
</td>
|
<td data-identifier="size" class="hidden-xs">{{ $folder['size'] }}</td>
|
||||||
<td><button class="btn btn-xxs btn-default disable-menu-hide" data-action="toggleMenu" style="padding:2px 6px 0px;"><i class="fa fa-ellipsis-h disable-menu-hide"></i></button></td>
|
<td data-identifier="modified" class="hidden-xs">
|
||||||
</tr>
|
<?php $carbon = Carbon::createFromTimestamp($folder['date'])->timezone(env('APP_TIMEZONE', 'America/New_York')); ?>
|
||||||
@endforeach
|
@if($carbon->diffInMinutes(Carbon::now()) > 60)
|
||||||
@foreach ($files as $file)
|
{{ $carbon->format('m/d/y H:i:s') }}
|
||||||
<tr data-type="file" data-mime="{{ $file['mime'] }}">
|
@elseif($carbon->diffInSeconds(Carbon::now()) < 5 || $carbon->isFuture())
|
||||||
<td data-identifier="type" class="middle">
|
<em>@lang('server.files.seconds_ago')</em>
|
||||||
{{-- oh boy --}}
|
@else
|
||||||
@if(in_array($file['mime'], [
|
{{ $carbon->diffForHumans() }}
|
||||||
'application/x-7z-compressed',
|
@endif
|
||||||
'application/zip',
|
</td>
|
||||||
'application/x-compressed-zip',
|
<td><button class="btn btn-xxs btn-default disable-menu-hide" data-action="toggleMenu" style="padding:2px 6px 0px;"><i class="fa fa-ellipsis-h disable-menu-hide"></i></button></td>
|
||||||
'application/x-tar',
|
</tr>
|
||||||
'application/x-gzip',
|
@endforeach
|
||||||
'application/x-bzip',
|
@foreach ($files as $file)
|
||||||
'application/x-bzip2',
|
<tr data-type="file" data-mime="{{ $file['mime'] }}">
|
||||||
'application/java-archive'
|
<td data-identifier="type" class="middle">
|
||||||
]))
|
{{-- oh boy --}}
|
||||||
<i class="fa fa-file-archive-o" style="margin-left: 2px;"></i>
|
@if(in_array($file['mime'], [
|
||||||
@elseif(in_array($file['mime'], [
|
'application/x-7z-compressed',
|
||||||
'application/json',
|
'application/zip',
|
||||||
'application/javascript',
|
'application/x-compressed-zip',
|
||||||
'application/xml',
|
'application/x-tar',
|
||||||
'application/xhtml+xml',
|
'application/x-gzip',
|
||||||
'text/xml',
|
'application/x-bzip',
|
||||||
'text/css',
|
'application/x-bzip2',
|
||||||
'text/html',
|
'application/java-archive'
|
||||||
'text/x-perl',
|
]))
|
||||||
'text/x-shellscript'
|
<i class="fa fa-file-archive-o" style="margin-left: 2px;"></i>
|
||||||
]))
|
@elseif(in_array($file['mime'], [
|
||||||
<i class="fa fa-file-code-o" style="margin-left: 2px;"></i>
|
'application/json',
|
||||||
@elseif(starts_with($file['mime'], 'image'))
|
'application/javascript',
|
||||||
<i class="fa fa-file-image-o" style="margin-left: 2px;"></i>
|
'application/xml',
|
||||||
@elseif(starts_with($file['mime'], 'video'))
|
'application/xhtml+xml',
|
||||||
<i class="fa fa-file-video-o" style="margin-left: 2px;"></i>
|
'text/xml',
|
||||||
@elseif(starts_with($file['mime'], 'video'))
|
'text/css',
|
||||||
<i class="fa fa-file-audio-o" style="margin-left: 2px;"></i>
|
'text/html',
|
||||||
@elseif(starts_with($file['mime'], 'application/vnd.ms-powerpoint'))
|
'text/x-perl',
|
||||||
<i class="fa fa-file-powerpoint-o" style="margin-left: 2px;"></i>
|
'text/x-shellscript'
|
||||||
@elseif(in_array($file['mime'], [
|
]))
|
||||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
<i class="fa fa-file-code-o" style="margin-left: 2px;"></i>
|
||||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
|
@elseif(starts_with($file['mime'], 'image'))
|
||||||
'application/msword'
|
<i class="fa fa-file-image-o" style="margin-left: 2px;"></i>
|
||||||
]) || starts_with($file['mime'], 'application/vnd.ms-word'))
|
@elseif(starts_with($file['mime'], 'video'))
|
||||||
<i class="fa fa-file-word-o" style="margin-left: 2px;"></i>
|
<i class="fa fa-file-video-o" style="margin-left: 2px;"></i>
|
||||||
@elseif(in_array($file['mime'], [
|
@elseif(starts_with($file['mime'], 'video'))
|
||||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
<i class="fa fa-file-audio-o" style="margin-left: 2px;"></i>
|
||||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
|
@elseif(starts_with($file['mime'], 'application/vnd.ms-powerpoint'))
|
||||||
]) || starts_with($file['mime'], 'application/vnd.ms-excel'))
|
<i class="fa fa-file-powerpoint-o" style="margin-left: 2px;"></i>
|
||||||
<i class="fa fa-file-excel-o" style="margin-left: 2px;"></i>
|
@elseif(in_array($file['mime'], [
|
||||||
@elseif($file['mime'] === 'application/pdf')
|
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||||
<i class="fa fa-file-pdf-o" style="margin-left: 2px;"></i>
|
'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
|
||||||
@else
|
'application/msword'
|
||||||
<i class="fa fa-file-text-o" style="margin-left: 2px;"></i>
|
]) || starts_with($file['mime'], 'application/vnd.ms-word'))
|
||||||
@endif
|
<i class="fa fa-file-word-o" style="margin-left: 2px;"></i>
|
||||||
</td>
|
@elseif(in_array($file['mime'], [
|
||||||
<td data-identifier="name" data-name="{{ rawurlencode($file['entry']) }}" data-path="@if($file['directory'] !== ''){{ rawurlencode($file['directory']) }}@endif/">
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||||
@if(in_array($file['mime'], $editableMime))
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
|
||||||
@can('edit-files', $server)
|
]) || starts_with($file['mime'], 'application/vnd.ms-excel'))
|
||||||
<a href="/server/{{ $server->uuidShort }}/files/edit/@if($file['directory'] !== ''){{ rawurlencode($file['directory']) }}/@endif{{ rawurlencode($file['entry']) }}" class="edit_file">{{ $file['entry'] }}</a>
|
<i class="fa fa-file-excel-o" style="margin-left: 2px;"></i>
|
||||||
|
@elseif($file['mime'] === 'application/pdf')
|
||||||
|
<i class="fa fa-file-pdf-o" style="margin-left: 2px;"></i>
|
||||||
|
@else
|
||||||
|
<i class="fa fa-file-text-o" style="margin-left: 2px;"></i>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
<td data-identifier="name" data-name="{{ rawurlencode($file['entry']) }}" data-path="@if($file['directory'] !== ''){{ rawurlencode($file['directory']) }}@endif/">
|
||||||
|
@if(in_array($file['mime'], $editableMime))
|
||||||
|
@can('edit-files', $server)
|
||||||
|
<a href="/server/{{ $server->uuidShort }}/files/edit/@if($file['directory'] !== ''){{ rawurlencode($file['directory']) }}/@endif{{ rawurlencode($file['entry']) }}" class="edit_file">{{ $file['entry'] }}</a>
|
||||||
|
@else
|
||||||
|
{{ $file['entry'] }}
|
||||||
|
@endcan
|
||||||
@else
|
@else
|
||||||
{{ $file['entry'] }}
|
{{ $file['entry'] }}
|
||||||
@endcan
|
@endif
|
||||||
@else
|
</td>
|
||||||
{{ $file['entry'] }}
|
<td data-identifier="size" class="hidden-xs">{{ $file['size'] }}</td>
|
||||||
@endif
|
<td data-identifier="modified" class="hidden-xs">
|
||||||
</td>
|
<?php $carbon = Carbon::createFromTimestamp($file['date'])->timezone(env('APP_TIMEZONE', 'America/New_York')); ?>
|
||||||
<td data-identifier="size">{{ $file['size'] }}</td>
|
@if($carbon->diffInMinutes(Carbon::now()) > 60)
|
||||||
<td data-identifier="modified">
|
{{ $carbon->format('m/d/y H:i:s') }}
|
||||||
<?php $carbon = Carbon::createFromTimestamp($file['date'])->timezone(env('APP_TIMEZONE', 'America/New_York')); ?>
|
@elseif($carbon->diffInSeconds(Carbon::now()) < 5 || $carbon->isFuture())
|
||||||
@if($carbon->diffInMinutes(Carbon::now()) > 60)
|
<em>@lang('server.files.seconds_ago')</em>
|
||||||
{{ $carbon->format('m/d/y H:i:s') }}
|
@else
|
||||||
@elseif($carbon->diffInSeconds(Carbon::now()) < 5 || $carbon->isFuture())
|
{{ $carbon->diffForHumans() }}
|
||||||
<em>@lang('server.files.seconds_ago')</em>
|
@endif
|
||||||
@else
|
</td>
|
||||||
{{ $carbon->diffForHumans() }}
|
<td><button class="btn btn-xxs btn-default disable-menu-hide" data-action="toggleMenu" style="padding:2px 6px 0px;"><i class="fa fa-ellipsis-h disable-menu-hide"></i></button></td>
|
||||||
@endif
|
</tr>
|
||||||
</td>
|
@endforeach
|
||||||
<td><button class="btn btn-xxs btn-default disable-menu-hide" data-action="toggleMenu" style="padding:2px 6px 0px;"><i class="fa fa-ellipsis-h disable-menu-hide"></i></button></td>
|
</tbody>
|
||||||
</tr>
|
</table>
|
||||||
@endforeach
|
</div>
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
|
@ -40,8 +40,11 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12">
|
<div class="col-xs-12">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-body">
|
<div class="box-body position-relative">
|
||||||
<div id="terminal" style="width:100%;"></div>
|
<div id="terminal" style="width:100%;"></div>
|
||||||
|
<div id="terminalNotify" class="terminal-notify hidden">
|
||||||
|
<i class="fa fa-bell"></i>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer text-center">
|
<div class="box-footer text-center">
|
||||||
@can('power-start', $server)<button class="btn btn-success disabled" data-attr="power" data-action="start">Start</button>@endcan
|
@can('power-start', $server)<button class="btn btn-success disabled" data-attr="power" data-action="start">Start</button>@endcan
|
||||||
|
|
Loading…
Reference in a new issue