diff --git a/app/Http/Controllers/Server/ServerController.php b/app/Http/Controllers/Server/ServerController.php index 81a7e20cf..ddf7cf2b1 100644 --- a/app/Http/Controllers/Server/ServerController.php +++ b/app/Http/Controllers/Server/ServerController.php @@ -58,8 +58,19 @@ class ServerController extends Controller public function getIndex(Request $request) { $server = Models\Server::getByUUID($request->route()->server); + $node = Models\Node::find($server->node); Javascript::put([ + 'server' => [ + 'uuid' => $server->uuid, + 'daemonSecret' => $server->daemonSecret, + 'username' => $server->username, + ], + 'node' => [ + 'scheme' => $node->scheme, + 'fqdn' => $node->fqdn, + 'daemonListen' => $node->daemonListen, + ], 'meta' => [ 'saveFile' => route('server.files.save', $server->uuidShort), 'csrfToken' => csrf_token(), @@ -69,7 +80,7 @@ class ServerController extends Controller return view('server.index', [ 'server' => $server, 'allocations' => Models\Allocation::where('assigned_to', $server->id)->orderBy('ip', 'asc')->orderBy('port', 'asc')->get(), - 'node' => Models\Node::find($server->node), + 'node' => $node, ]); } diff --git a/public/js/plugins/minecraft/eula.js b/public/js/plugins/minecraft/eula.js index 28fe082e0..7077288b4 100644 --- a/public/js/plugins/minecraft/eula.js +++ b/public/js/plugins/minecraft/eula.js @@ -18,7 +18,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. $(window).load(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) { swal({ title: 'EULA Acceptance', diff --git a/public/themes/pterodactyl/js/frontend/console.js b/public/themes/pterodactyl/js/frontend/console.js new file mode 100644 index 000000000..4ae2bf989 --- /dev/null +++ b/public/themes/pterodactyl/js/frontend/console.js @@ -0,0 +1,203 @@ +// Copyright (c) 2015 - 2016 Dane Everitt +// +// 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 CONSOLE_PUSH_COUNT = 50; +var CONSOLE_PUSH_FREQ = 200; + +(function initConsole() { + window.TerminalQueue = []; + window.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; + } + }); + + Socket.on('initial status', function (data) { + Terminal.clear(); + if (data.status === 1 || data.status === 2) { + Socket.emit('send server log'); + } + }); +})(); + +(function pushOutputQueue() { + if (TerminalQueue.length > CONSOLE_PUSH_COUNT) { + // console throttled warning show + } + + if (TerminalQueue.length > 0) { + for (var i = 0; i < CONSOLE_PUSH_COUNT && TerminalQueue.length > 0; i++) { + Terminal.echo(TerminalQueue[0]); + TerminalQueue.shift(); + } + } + + window.setTimeout(pushOutputQueue, CONSOLE_PUSH_FREQ); +})(); + +$(document).ready(function () { + $('[data-attr="power"]').click(function (event) { + Socket.emit('set status', $(this).data('action')); + }); + var ctc = $('#chart_cpu'); + var timeLabels = []; + var cpuData = []; + var CPUChart = new Chart(ctc, { + type: 'line', + data: { + labels: timeLabels, + datasets: [ + { + 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'); + 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) { + // Server is On or Starting + if(data == 1 || data == 2) { + $('[data-attr="power"][data-action="start"]').addClass('disabled'); + $('[data-attr="power"][data-action="stop"], [data-attr="power"][data-action="restart"]').removeClass('disabled'); + } else { + if (data == 0) { + $('[data-attr="power"][data-action="start"]').removeClass('disabled'); + } + $('[data-attr="power"][data-action="stop"], [data-attr="power"][data-action="restart"]').addClass('disabled'); + } + + if(data !== 0) { + $('[data-attr="power"][data-action="kill"]').removeClass('disabled'); + } else { + $('[data-attr="power"][data-action="kill"]').addClass('disabled'); + } + } +}); diff --git a/public/themes/pterodactyl/js/frontend/server.socket.js b/public/themes/pterodactyl/js/frontend/server.socket.js new file mode 100644 index 000000000..9d8f047c3 --- /dev/null +++ b/public/themes/pterodactyl/js/frontend/server.socket.js @@ -0,0 +1,69 @@ +// Copyright (c) 2015 - 2016 Dane Everitt +// +// 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. +(function initSocket() { + if (typeof io !== 'function') { + console.error('Socket.io is reqired to use this panel.'); + return; + } + + 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) { + console.error('Could not connect to socket.io.', err); + }); + + // Connected to Socket Successfully + Socket.on('connect', function () { + console.log('connected to socket'); + }); + + 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) { + switch (status) { + case 0: + $('#server_status_icon').html(' Offline'); + break; + case 1: + $('#server_status_icon').html(' Online'); + break; + case 2: + $('#server_status_icon').html(' Starting'); + break; + case 3: + $('#server_status_icon').html(' Stopping'); + break; + default: + break; + } +} diff --git a/resources/lang/en/server.php b/resources/lang/en/server.php new file mode 100644 index 000000000..568da2ec6 --- /dev/null +++ b/resources/lang/en/server.php @@ -0,0 +1,9 @@ + [ + 'title' => 'Viewing Server :name', + 'header' => 'Server Console', + 'header_sub' => 'Control your server in real time.', + ] +]; diff --git a/resources/themes/pterodactyl/base/index.blade.php b/resources/themes/pterodactyl/base/index.blade.php index 0ac1b3e08..88509988d 100644 --- a/resources/themes/pterodactyl/base/index.blade.php +++ b/resources/themes/pterodactyl/base/index.blade.php @@ -61,7 +61,7 @@ @foreach($servers as $server) {{ $server->uuidShort }} - {{ $server->name }} + {{ $server->name }} {{ $server->nodeName }} @if(!is_null($server->ip_alias)){{ $server->ip_alias }}@else{{ $server->ip }}@endif:{{ $server->port }} -- / {{ $server->memory === 0 ? '∞' : $server->memory }} MB diff --git a/resources/themes/pterodactyl/layouts/master.blade.php b/resources/themes/pterodactyl/layouts/master.blade.php index 6e63bdce0..0fb1451b3 100644 --- a/resources/themes/pterodactyl/layouts/master.blade.php +++ b/resources/themes/pterodactyl/layouts/master.blade.php @@ -101,7 +101,7 @@

{{ $server->name }}

- Online + Checking...
@endif @@ -129,12 +129,12 @@ @if (isset($server->name) && isset($node->name))
  • SERVER MANAGEMENT
  • -
  • +
  • Console
  • -
  • +
  • File Management @@ -248,6 +248,7 @@ {!! Theme::js('vendor/bootstrap/bootstrap.min.js') !!} {!! Theme::js('vendor/slimscroll/jquery.slimscroll.min.js') !!} {!! Theme::js('vendor/adminlte/app.min.js') !!} + {!! Theme::js('js/vendor/socketio/socket.io.min.js') !!} @show diff --git a/resources/themes/pterodactyl/server/index.blade.php b/resources/themes/pterodactyl/server/index.blade.php new file mode 100644 index 000000000..22924ec6e --- /dev/null +++ b/resources/themes/pterodactyl/server/index.blade.php @@ -0,0 +1,91 @@ +{{-- Copyright (c) 2015 - 2016 Dane Everitt --}} + +{{-- 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. --}} +@extends('layouts.master') + +@section('title') + {{ trans('server.index.title', [ 'name' => $server->name]) }} +@endsection + +@section('scripts') + @parent + {!! Theme::css('css/jquery.terminal.css') !!} +@endsection + +@section('content-header') +

    {{ trans('server.index.header') }}{{ trans('server.index.header_sub')}}

    +
    +@endsection + +@section('content') +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    Memory Usage

    +
    +
    + +
    +
    +
    +
    +
    +
    +

    CPU Usage

    +
    +
    + +
    +
    +
    +
    +@endsection + +@section('footer-scripts') + @parent + {!! Theme::js('js/frontend/server.socket.js') !!} + {!! Theme::js('js/jquery.mousewheel-min.js') !!} + {!! Theme::js('js/jquery.terminal-0.11.23.min.js') !!} + {!! Theme::js('js/unix_formatting.js') !!} + {!! Theme::js('js/frontend/console.js') !!} + {!! Theme::js('js/vendor/chartjs/chart.min.js') !!} + {!! Theme::js('js/vendor/jquery/jquery-dateFormat.min.js') !!} + @if($server->a_serviceFile === 'minecraft') + {!! Theme::js('js/plugins/minecraft/eula.js') !!} + @endif +@endsection