[L6] Move all of the template files into the new correct location

This commit is contained in:
Dane Everitt 2019-09-04 21:19:52 -07:00
parent 1c5b9dbb87
commit c97461d602
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
83 changed files with 0 additions and 0 deletions

View file

@ -0,0 +1,103 @@
@extends('layouts.admin')
@section('title')
Application API
@endsection
@section('content-header')
<h1>Application API<small>Control access credentials for managing this Panel via the API.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li class="active">Application API</li>
</ol>
@endsection
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Credentials List</h3>
<div class="box-tools">
<a href="{{ route('admin.api.new') }}" class="btn btn-sm btn-primary">Create New</a>
</div>
</div>
<div class="box-body table-responsive no-padding">
<table class="table table-hover">
<tr>
<th>Key</th>
<th>Memo</th>
<th>Last Used</th>
<th>Created</th>
<th></th>
</tr>
@foreach($keys as $key)
<tr>
<td><code>{{ $key->identifier }}{{ decrypt($key->token) }}</code></td>
<td>{{ $key->memo }}</td>
<td>
@if(!is_null($key->last_used_at))
@datetimeHuman($key->last_used_at)
@else
&mdash;
@endif
</td>
<td>@datetimeHuman($key->created_at)</td>
<td>
<a href="#" data-action="revoke-key" data-attr="{{ $key->identifier }}">
<i class="fa fa-trash-o text-danger"></i>
</a>
</td>
</tr>
@endforeach
</table>
</div>
</div>
</div>
</div>
@endsection
@section('footer-scripts')
@parent
<script>
$(document).ready(function() {
$('[data-action="revoke-key"]').click(function (event) {
var self = $(this);
event.preventDefault();
swal({
type: 'error',
title: 'Revoke API Key',
text: 'Once this API key is revoked any applications currently using it will stop working.',
showCancelButton: true,
allowOutsideClick: true,
closeOnConfirm: false,
confirmButtonText: 'Revoke',
confirmButtonColor: '#d9534f',
showLoaderOnConfirm: true
}, function () {
$.ajax({
method: 'DELETE',
url: Router.route('admin.api.delete', { identifier: self.data('attr') }),
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}'
}
}).done(function () {
swal({
type: 'success',
title: '',
text: 'API Key has been revoked.'
});
self.parent().parent().slideUp();
}).fail(function (jqXHR) {
console.error(jqXHR);
swal({
type: 'error',
title: 'Whoops!',
text: 'An error occurred while attempting to revoke this key.'
});
});
});
});
});
</script>
@endsection

View file

@ -0,0 +1,70 @@
@extends('layouts.admin')
@section('title')
Application API
@endsection
@section('content-header')
<h1>Application API<small>Create a new application API key.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li><a href="{{ route('admin.api.index') }}">Application API</a></li>
<li class="active">New Credentials</li>
</ol>
@endsection
@section('content')
<div class="row">
<form method="POST" action="{{ route('admin.api.new') }}">
<div class="col-sm-8 col-xs-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Select Permissions</h3>
</div>
<div class="box-body table-responsive no-padding">
<table class="table table-hover">
@foreach($resources as $resource)
<tr>
<td class="col-sm-3 strong">{{ str_replace('_', ' ', title_case($resource)) }}</td>
<td class="col-sm-3 radio radio-primary text-center">
<input type="radio" id="r_{{ $resource }}" name="r_{{ $resource }}" value="{{ $permissions['r'] }}">
<label for="r_{{ $resource }}">Read</label>
</td>
<td class="col-sm-3 radio radio-primary text-center">
<input type="radio" id="rw_{{ $resource }}" name="r_{{ $resource }}" value="{{ $permissions['rw'] }}">
<label for="rw_{{ $resource }}">Read &amp; Write</label>
</td>
<td class="col-sm-3 radio text-center">
<input type="radio" id="n_{{ $resource }}" name="r_{{ $resource }}" value="{{ $permissions['n'] }}" checked>
<label for="n_{{ $resource }}">None</label>
</td>
</tr>
@endforeach
</table>
</div>
</div>
</div>
<div class="col-sm-4 col-xs-12">
<div class="box box-primary">
<div class="box-body">
<div class="form-group">
<label class="control-label" for="memoField">Description <span class="field-required"></span></label>
<input id="memoField" type="text" name="memo" class="form-control">
</div>
<p class="text-muted">Once you have assigned permissions and created this set of credentials you will be unable to come back and edit it. If you need to make changes down the road you will need to create a new set of credentials.</p>
</div>
<div class="box-footer">
{{ csrf_field() }}
<button type="submit" class="btn btn-success btn-sm pull-right">Create Credentials</button>
</div>
</div>
</div>
</form>
</div>
@endsection
@section('footer-scripts')
@parent
<script>
</script>
@endsection

View file

@ -0,0 +1,135 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
Database Hosts
@endsection
@section('content-header')
<h1>Database Hosts<small>Database hosts that servers can have databases created on.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li class="active">Database Hosts</li>
</ol>
@endsection
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Host List</h3>
<div class="box-tools">
<button class="btn btn-sm btn-primary" data-toggle="modal" data-target="#newHostModal">Create New</button>
</div>
</div>
<div class="box-body table-responsive no-padding">
<table class="table table-hover">
<tbody>
<tr>
<th>ID</th>
<th>Name</th>
<th>Host</th>
<th>Port</th>
<th>Username</th>
<th class="text-center">Databases</th>
<th class="text-center">Node</th>
</tr>
@foreach ($hosts as $host)
<tr>
<td><code>{{ $host->id }}</code></td>
<td><a href="{{ route('admin.databases.view', $host->id) }}">{{ $host->name }}</a></td>
<td><code>{{ $host->host }}</code></td>
<td><code>{{ $host->port }}</code></td>
<td>{{ $host->username }}</td>
<td class="text-center">{{ $host->databases_count }}</td>
<td class="text-center">
@if(! is_null($host->node))
<a href="{{ route('admin.nodes.view', $host->node->id) }}">{{ $host->node->name }}</a>
@else
<span class="label label-default">None</span>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="modal fade" id="newHostModal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<form action="{{ route('admin.databases') }}" method="POST">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">Create New Database Host</h4>
</div>
<div class="modal-body">
<div class="form-group">
<label for="pName" class="form-label">Name</label>
<input type="text" name="name" id="pName" class="form-control" />
<p class="text-muted small">A short identifier used to distinguish this location from others. Must be between 1 and 60 characters, for example, <code>us.nyc.lvl3</code>.</p>
</div>
<div class="row">
<div class="col-md-6">
<label for="pHost" class="form-label">Host</label>
<input type="text" name="host" id="pHost" class="form-control" />
<p class="text-muted small">The IP address or FQDN that should be used when attempting to connect to this MySQL host <em>from the panel</em> to add new databases.</p>
</div>
<div class="col-md-6">
<label for="pPort" class="form-label">Port</label>
<input type="text" name="port" id="pPort" class="form-control" value="3306"/>
<p class="text-muted small">The port that MySQL is running on for this host.</p>
</div>
</div>
<div class="row">
<div class="col-md-6">
<label for="pUsername" class="form-label">Username</label>
<input type="text" name="username" id="pUsername" class="form-control" />
<p class="text-muted small">The username of an account that has enough permissions to create new users and databases on the system.</p>
</div>
<div class="col-md-6">
<label for="pPassword" class="form-label">Password</label>
<input type="password" name="password" id="pPassword" class="form-control" />
<p class="text-muted small">The password to the account defined.</p>
</div>
</div>
<div class="form-group">
<label for="pNodeId" class="form-label">Linked Node</label>
<select name="node_id" id="pNodeId" class="form-control">
<option value="">None</option>
@foreach($locations as $location)
<optgroup label="{{ $location->short }}">
@foreach($location->nodes as $node)
<option value="{{ $node->id }}">{{ $node->name }}</option>
@endforeach
</optgroup>
@endforeach
</select>
<p class="text-muted small">This setting does nothing other than default to this database host when adding a database to a server on the selected node.</p>
</div>
</div>
<div class="modal-footer">
<p class="text-danger small text-left">The account defined for this database host <strong>must</strong> have the <code>WITH GRANT OPTION</code> permission. If the defined account does not have this permission requests to create databases <em>will</em> fail. <strong>Do not use the same account details for MySQL that you have defined for this panel.</strong></p>
{!! csrf_field() !!}
<button type="button" class="btn btn-default btn-sm pull-left" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-success btn-sm">Create</button>
</div>
</form>
</div>
</div>
</div>
@endsection
@section('footer-scripts')
@parent
<script>
$('#pNodeId').select2();
</script>
@endsection

View file

@ -0,0 +1,134 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
Database Hosts &rarr; View &rarr; {{ $host->name }}
@endsection
@section('content-header')
<h1>{{ $host->name }}<small>Viewing associated databases and details for this database host.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li><a href="{{ route('admin.databases') }}">Database Hosts</a></li>
<li class="active">{{ $host->name }}</li>
</ol>
@endsection
@section('content')
<form action="{{ route('admin.databases.view', $host->id) }}" method="POST">
<div class="row">
<div class="col-sm-6">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Host Details</h3>
</div>
<div class="box-body">
<div class="form-group">
<label for="pName" class="form-label">Name</label>
<input type="text" id="pName" name="name" class="form-control" value="{{ old('name', $host->name) }}" />
</div>
<div class="form-group">
<label for="pHost" class="form-label">Host</label>
<input type="text" id="pHost" name="host" class="form-control" value="{{ old('host', $host->host) }}" />
<p class="text-muted small">The IP address or FQDN that should be used when attempting to connect to this MySQL host <em>from the panel</em> to add new databases.</p>
</div>
<div class="form-group">
<label for="pPort" class="form-label">Port</label>
<input type="text" id="pPort" name="port" class="form-control" value="{{ old('port', $host->port) }}" />
<p class="text-muted small">The port that MySQL is running on for this host.</p>
</div>
<div class="form-group">
<label for="pNodeId" class="form-label">Linked Node</label>
<select name="node_id" id="pNodeId" class="form-control">
<option value="0">None</option>
@foreach($locations as $location)
<optgroup label="{{ $location->short }}">
@foreach($location->nodes as $node)
<option value="{{ $node->id }}" {{ $host->node_id !== $node->id ?: 'selected' }}>{{ $node->name }}</option>
@endforeach
</optgroup>
@endforeach
</select>
<p class="text-muted small">This setting does nothing other than default to this database host when adding a database to a server on the selected node.</p>
</div>
</div>
</div>
</div>
<div class="col-sm-6">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">User Details</h3>
</div>
<div class="box-body">
<div class="form-group">
<label for="pUsername" class="form-label">Username</label>
<input type="text" name="username" id="pUsername" class="form-control" value="{{ old('username', $host->username) }}" />
<p class="text-muted small">The username of an account that has enough permissions to create new users and databases on the system.</p>
</div>
<div class="form-group">
<label for="pPassword" class="form-label">Password</label>
<input type="password" name="password" id="pPassword" class="form-control" />
<p class="text-muted small">The password to the account defined. Leave blank to continue using the assigned password.</p>
</div>
<hr />
<p class="text-danger small text-left">The account defined for this database host <strong>must</strong> have the <code>WITH GRANT OPTION</code> permission. If the defined account does not have this permission requests to create databases <em>will</em> fail. <strong>Do not use the same account details for MySQL that you have defined for this panel.</strong></p>
</div>
<div class="box-footer">
{!! csrf_field() !!}
<button name="_method" value="PATCH" class="btn btn-sm btn-primary pull-right">Save</button>
<button name="_method" value="DELETE" class="btn btn-sm btn-danger pull-left muted muted-hover"><i class="fa fa-trash-o"></i></button>
</div>
</div>
</div>
</div>
</form>
<div class="row">
<div class="col-xs-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Databases</h3>
</div>
<div class="box-body table-responsive no-padding">
<table class="table table-hover">
<tr>
<th>Server</th>
<th>Database Name</th>
<th>Username</th>
<th>Connections From</th>
<th></th>
</tr>
@foreach($databases as $database)
<tr>
<td class="middle"><a href="{{ route('admin.servers.view', $database->getRelation('server')->id) }}">{{ $database->getRelation('server')->name }}</a></td>
<td class="middle">{{ $database->database }}</td>
<td class="middle">{{ $database->username }}</td>
<td class="middle">{{ $database->remote }}</td>
<td class="text-center">
<a href="{{ route('admin.servers.view.database', $database->getRelation('server')->id) }}">
<button class="btn btn-xs btn-primary">Manage</button>
</a>
</td>
</tr>
@endforeach
</table>
</div>
@if($databases->hasPages())
<div class="box-footer with-border">
<div class="col-md-12 text-center">{!! $databases->render() !!}</div>
</div>
@endif
</div>
</div>
</div>
@endsection
@section('footer-scripts')
@parent
<script>
$('#pNodeId').select2();
</script>
@endsection

View file

@ -0,0 +1,155 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
Nests &rarr; New Egg
@endsection
@section('content-header')
<h1>New Egg<small>Create a new Egg to assign to servers.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li><a href="{{ route('admin.nests') }}">Nests</a></li>
<li class="active">New Egg</li>
</ol>
@endsection
@section('content')
<form action="{{ route('admin.nests.egg.new') }}" method="POST">
<div class="row">
<div class="col-xs-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Configuration</h3>
</div>
<div class="box-body">
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label for="pNestId" class="form-label">Associated Nest</label>
<div>
<select name="nest_id" id="pNestId">
@foreach($nests as $nest)
<option value="{{ $nest->id }}" {{ old('nest_id') != $nest->id ?: 'selected' }}>{{ $nest->name }} &lt;{{ $nest->author }}&gt;</option>
@endforeach
</select>
<p class="text-muted small">Think of a Nest as a category. You can put multiple Eggs in a nest, but consider putting only Eggs that are related to each other in each Nest.</p>
</div>
</div>
<div class="form-group">
<label for="pName" class="form-label">Name</label>
<input type="text" id="pName" name="name" value="{{ old('name') }}" class="form-control" />
<p class="text-muted small">A simple, human-readable name to use as an identifier for this Egg. This is what users will see as their game server type.</p>
</div>
<div class="form-group">
<label for="pDescription" class="form-label">Description</label>
<textarea id="pDescription" name="description" class="form-control" rows="8">{{ old('description') }}</textarea>
<p class="text-muted small">A description of this Egg.</p>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="pDockerImage" class="control-label">Docker Image</label>
<input type="text" id="pDockerImage" name="docker_image" value="{{ old('docker_image') }}" placeholder="quay.io/pterodactyl/service" class="form-control" />
<p class="text-muted small">The default docker image that should be used for new servers using this Egg. This can be changed per-server.</p>
</div>
<div class="form-group">
<label for="pStartup" class="control-label">Startup Command</label>
<textarea id="pStartup" name="startup" class="form-control" rows="14">{{ old('startup') }}</textarea>
<p class="text-muted small">The default startup command that should be used for new servers created with this Egg. You can change this per-server as needed.</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-xs-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Process Management</h3>
</div>
<div class="box-body">
<div class="row">
<div class="col-xs-12">
<div class="alert alert-warning">
<p>All fields are required unless you select a separate option from the 'Copy Settings From' dropdown, in which case fields may be left blank to use the values from that option.</p>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="pConfigFrom" class="form-label">Copy Settings From</label>
<select name="config_from" id="pConfigFrom" class="form-control">
<option value="">None</option>
</select>
<p class="text-muted small">If you would like to default to settings from another Egg select it from the dropdown above.</p>
</div>
<div class="form-group">
<label for="pConfigStop" class="form-label">Stop Command</label>
<input type="text" id="pConfigStop" name="config_stop" class="form-control" value="{{ old('config_stop') }}" />
<p class="text-muted small">The command that should be sent to server processes to stop them gracefully. If you need to send a <code>SIGINT</code> you should enter <code>^C</code> here.</p>
</div>
<div class="form-group">
<label for="pConfigLogs" class="form-label">Log Configuration</label>
<textarea data-action="handle-tabs" id="pConfigLogs" name="config_logs" class="form-control" rows="6">{{ old('config_logs') }}</textarea>
<p class="text-muted small">This should be a JSON representation of where log files are stored, and whether or not the daemon should be creating custom logs.</p>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="pConfigFiles" class="form-label">Configuration Files</label>
<textarea data-action="handle-tabs" id="pConfigFiles" name="config_files" class="form-control" rows="6">{{ old('config_files') }}</textarea>
<p class="text-muted small">This should be a JSON representation of configuration files to modify and what parts should be changed.</p>
</div>
<div class="form-group">
<label for="pConfigStartup" class="form-label">Start Configuration</label>
<textarea data-action="handle-tabs" id="pConfigStartup" name="config_startup" class="form-control" rows="6">{{ old('config_startup') }}</textarea>
<p class="text-muted small">This should be a JSON representation of what values the daemon should be looking for when booting a server to determine completion.</p>
</div>
</div>
</div>
</div>
<div class="box-footer">
{!! csrf_field() !!}
<button type="submit" class="btn btn-success btn-sm pull-right">Create</button>
</div>
</div>
</div>
</div>
</form>
@endsection
@section('footer-scripts')
@parent
{!! Theme::js('vendor/lodash/lodash.js') !!}
<script>
$(document).ready(function() {
$('#pNestId').select2().change();
$('#pConfigFrom').select2();
});
$('#pNestId').on('change', function (event) {
$('#pConfigFrom').html('<option value="">None</option>').select2({
data: $.map(_.get(Pterodactyl.nests, $(this).val() + '.eggs', []), function (item) {
return {
id: item.id,
text: item.name + ' <' + item.author + '>',
};
}),
});
});
$('textarea[data-action="handle-tabs"]').on('keydown', function(event) {
if (event.keyCode === 9) {
event.preventDefault();
var curPos = $(this)[0].selectionStart;
var prepend = $(this).val().substr(0, curPos);
var append = $(this).val().substr(curPos);
$(this).val(prepend + ' ' + append);
}
});
</script>
@endsection

View file

@ -0,0 +1,122 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
Nests &rarr; Egg: {{ $egg->name }} &rarr; Scripts
@endsection
@section('content-header')
<h1>{{ $egg->name }}<small>Manage install and upgrade scripts for this Egg.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li><a href="{{ route('admin.nests') }}">Service</a></li>
<li><a href="{{ route('admin.nests.view', $egg->nest->id) }}">{{ $egg->nest->name }}</a></li>
<li class="active">{{ $egg->name }}</li>
</ol>
@endsection
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="nav-tabs-custom nav-tabs-floating">
<ul class="nav nav-tabs">
<li><a href="{{ route('admin.nests.egg.view', $egg->id) }}">Configuration</a></li>
<li><a href="{{ route('admin.nests.egg.variables', $egg->id) }}">Variables</a></li>
<li class="active"><a href="{{ route('admin.nests.egg.scripts', $egg->id) }}">Scripts</a></li>
</ul>
</div>
</div>
</div>
<form action="{{ route('admin.nests.egg.scripts', $egg->id) }}" method="POST">
<div class="row">
<div class="col-xs-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Install Script</h3>
</div>
@if(! is_null($egg->copyFrom))
<div class="box-body">
<div class="callout callout-warning no-margin">
This service option is copying installation scripts and container options from <a href="{{ route('admin.nests.egg.view', $egg->copyFrom->id) }}">{{ $egg->copyFrom->name }}</a>. Any changes you make to this script will not apply unless you select "None" from the dropdown box below.
</div>
</div>
@endif
<div class="box-body no-padding">
<div id="editor_install"style="height:300px">{{ $egg->script_install }}</div>
</div>
<div class="box-body">
<div class="row">
<div class="form-group col-sm-4">
<label class="control-label">Copy Script From</label>
<select id="pCopyScriptFrom" name="copy_script_from">
<option value="">None</option>
@foreach($copyFromOptions as $opt)
<option value="{{ $opt->id }}" {{ $egg->copy_script_from !== $opt->id ?: 'selected' }}>{{ $opt->name }}</option>
@endforeach
</select>
<p class="text-muted small">If selected, script above will be ignored and script from selected option will be used in place.</p>
</div>
<div class="form-group col-sm-4">
<label class="control-label">Script Container</label>
<input type="text" name="script_container" class="form-control" value="{{ $egg->script_container }}" />
<p class="text-muted small">Docker container to use when running this script for the server.</p>
</div>
<div class="form-group col-sm-4">
<label class="control-label">Script Entrypoint Command</label>
<input type="text" name="script_entry" class="form-control" value="{{ $egg->script_entry }}" />
<p class="text-muted small">The entrypoint command to use for this script.</p>
</div>
</div>
<div class="row">
<div class="col-xs-12 text-muted">
The following service options rely on this script:
@if(count($relyOnScript) > 0)
@foreach($relyOnScript as $rely)
<a href="{{ route('admin.nests.egg.view', $rely->id) }}">
<code>{{ $rely->name }}</code>@if(!$loop->last),&nbsp;@endif
</a>
@endforeach
@else
<em>none</em>
@endif
</div>
</div>
</div>
<div class="box-footer">
{!! csrf_field() !!}
<textarea name="script_install" class="hidden"></textarea>
<button type="submit" name="_method" value="PATCH" class="btn btn-primary btn-sm pull-right">Save</button>
</div>
</div>
</div>
</div>
</form>
@endsection
@section('footer-scripts')
@parent
{!! Theme::js('vendor/ace/ace.js') !!}
{!! Theme::js('vendor/ace/ext-modelist.js') !!}
<script>
$(document).ready(function () {
$('#pCopyScriptFrom').select2();
const InstallEditor = ace.edit('editor_install');
const Modelist = ace.require('ace/ext/modelist')
InstallEditor.setTheme('ace/theme/chrome');
InstallEditor.getSession().setMode('ace/mode/sh');
InstallEditor.getSession().setUseWrapMode(true);
InstallEditor.setShowPrintMargin(false);
$('form').on('submit', function (e) {
$('textarea[name="script_install"]').val(InstallEditor.getValue());
});
});
</script>
@endsection

View file

@ -0,0 +1,161 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
Egg &rarr; {{ $egg->name }} &rarr; Variables
@endsection
@section('content-header')
<h1>{{ $egg->name }}<small>Managing variables for this Egg.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li><a href="{{ route('admin.nests') }}">Nests</a></li>
<li><a href="{{ route('admin.nests.view', $egg->nest->id) }}">{{ $egg->nest->name }}</a></li>
<li><a href="{{ route('admin.nests.egg.view', $egg->id) }}">{{ $egg->name }}</a></li>
<li class="active">Variables</li>
</ol>
@endsection
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="nav-tabs-custom nav-tabs-floating">
<ul class="nav nav-tabs">
<li><a href="{{ route('admin.nests.egg.view', $egg->id) }}">Configuration</a></li>
<li class="active"><a href="{{ route('admin.nests.egg.variables', $egg->id) }}">Variables</a></li>
<li><a href="{{ route('admin.nests.egg.scripts', $egg->id) }}">Scripts</a></li>
</ul>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="box no-border">
<div class="box-body">
<a href="#" class="btn btn-sm btn-success pull-right" data-toggle="modal" data-target="#newVariableModal">Create New Variable</a>
</div>
</div>
</div>
</div>
<div class="row">
@foreach($egg->variables as $variable)
<div class="col-sm-6">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ $variable->name }}</h3>
</div>
<form action="{{ route('admin.nests.egg.variables.edit', ['id' => $egg->id, 'variable' => $variable->id]) }}" method="POST">
<div class="box-body">
<div class="form-group">
<label class="form-label">Name</label>
<input type="text" name="name" value="{{ $variable->name }}" class="form-control" />
</div>
<div class="form-group">
<label class="form-label">Description</label>
<textarea name="description" class="form-control" rows="3">{{ $variable->description }}</textarea>
</div>
<div class="row">
<div class="form-group col-md-6">
<label class="form-label">Environment Variable</label>
<input type="text" name="env_variable" value="{{ $variable->env_variable }}" class="form-control" />
</div>
<div class="form-group col-md-6">
<label class="form-label">Default Value</label>
<input type="text" name="default_value" value="{{ $variable->default_value }}" class="form-control" />
</div>
<div class="col-xs-12">
<p class="text-muted small">This variable can be accessed in the startup command by using <code>{{ $variable->env_variable }}</code>.</p>
</div>
</div>
<div class="form-group">
<label class="form-label">Permissions</label>
<select name="options[]" class="pOptions form-control" multiple>
<option value="user_viewable" {{ (! $variable->user_viewable) ?: 'selected' }}>Users Can View</option>
<option value="user_editable" {{ (! $variable->user_editable) ?: 'selected' }}>Users Can Edit</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Input Rules</label>
<input type="text" name="rules" class="form-control" value="{{ $variable->rules }}" />
<p class="text-muted small">These rules are defined using standard <a href="https://laravel.com/docs/5.7/validation#available-validation-rules" target="_blank">Laravel Framework validation rules</a>.</p>
</div>
</div>
<div class="box-footer">
{!! csrf_field() !!}
<button class="btn btn-sm btn-primary pull-right" name="_method" value="PATCH" type="submit">Save</button>
<button class="btn btn-sm btn-danger pull-left muted muted-hover" data-action="delete" name="_method" value="DELETE" type="submit"><i class="fa fa-trash-o"></i></button>
</div>
</form>
</div>
</div>
@endforeach
</div>
<div class="modal fade" id="newVariableModal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">Create New Egg Variable</h4>
</div>
<form action="{{ route('admin.nests.egg.variables', $egg->id) }}" method="POST">
<div class="modal-body">
<div class="form-group">
<label class="control-label">Name <span class="field-required"></span></label>
<input type="text" name="name" class="form-control" value="{{ old('name') }}"/>
</div>
<div class="form-group">
<label class="control-label">Description</label>
<textarea name="description" class="form-control" rows="3">{{ old('description') }}</textarea>
</div>
<div class="row">
<div class="form-group col-md-6">
<label class="control-label">Environment Variable <span class="field-required"></span></label>
<input type="text" name="env_variable" class="form-control" value="{{ old('env_variable') }}" />
</div>
<div class="form-group col-md-6">
<label class="control-label">Default Value</label>
<input type="text" name="default_value" class="form-control" value="{{ old('default_value') }}" />
</div>
<div class="col-xs-12">
<p class="text-muted small">This variable can be accessed in the startup command by entering <code>@{{environment variable value}}</code>.</p>
</div>
</div>
<div class="form-group">
<label class="control-label">Permissions</label>
<select name="options[]" class="pOptions form-control" multiple>
<option value="user_viewable">Users Can View</option>
<option value="user_editable">Users Can Edit</option>
</select>
</div>
<div class="form-group">
<label class="control-label">Input Rules <span class="field-required"></span></label>
<input type="text" name="rules" class="form-control" value="{{ old('rules', 'required|string|max:20') }}" placeholder="required|string|max:20" />
<p class="text-muted small">These rules are defined using standard <a href="https://laravel.com/docs/5.7/validation#available-validation-rules" target="_blank">Laravel Framework validation rules</a>.</p>
</div>
</div>
<div class="modal-footer">
{!! csrf_field() !!}
<button type="button" class="btn btn-default pull-left" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Create Variable</button>
</div>
</form>
</div>
</div>
</div>
@endsection
@section('footer-scripts')
@parent
<script>
$('.pOptions').select2();
$('[data-action="delete"]').on('mouseenter', function (event) {
$(this).find('i').html(' Delete Variable');
}).on('mouseleave', function (event) {
$(this).find('i').html('');
});
</script>
@endsection

View file

@ -0,0 +1,200 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
Nests &rarr; Egg: {{ $egg->name }}
@endsection
@section('content-header')
<h1>{{ $egg->name }}<small>{{ str_limit($egg->description, 50) }}</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li><a href="{{ route('admin.nests') }}">Nests</a></li>
<li><a href="{{ route('admin.nests.view', $egg->nest->id) }}">{{ $egg->nest->name }}</a></li>
<li class="active">{{ $egg->name }}</li>
</ol>
@endsection
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="nav-tabs-custom nav-tabs-floating">
<ul class="nav nav-tabs">
<li class="active"><a href="{{ route('admin.nests.egg.view', $egg->id) }}">Configuration</a></li>
<li><a href="{{ route('admin.nests.egg.variables', $egg->id) }}">Variables</a></li>
<li><a href="{{ route('admin.nests.egg.scripts', $egg->id) }}">Scripts</a></li>
</ul>
</div>
</div>
<div class="col-xs-12">
<div class="alert alert-info">
<strong>Notice:</strong> Editing an Egg or any of the Process Management fields <em>requires</em> that each Daemon be rebooted in order to apply the changes.
</div>
</div>
</div>
<form action="{{ route('admin.nests.egg.view', $egg->id) }}" enctype="multipart/form-data" method="POST">
<div class="row">
<div class="col-xs-12">
<div class="box box-danger">
<div class="box-body">
<div class="row">
<div class="col-xs-8">
<div class="form-group no-margin-bottom">
<label for="pName" class="control-label">Egg File</label>
<div>
<input type="file" name="import_file" class="form-control" style="border: 0;margin-left:-10px;" />
<p class="text-muted small no-margin-bottom">If you would like to replace settings for this Egg by uploading a new JSON file, simply select it here and press "Update Egg". This will not change any existing startup strings or Docker images for existing servers.</p>
</div>
</div>
</div>
<div class="col-xs-4">
{!! csrf_field() !!}
<button type="submit" name="_method" value="PUT" class="btn btn-sm btn-danger pull-right">Update Egg</button>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
<form action="{{ route('admin.nests.egg.view', $egg->id) }}" method="POST">
<div class="row">
<div class="col-xs-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Configuration</h3>
</div>
<div class="box-body">
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label for="pName" class="control-label">Name <span class="field-required"></span></label>
<input type="text" id="pName" name="name" value="{{ $egg->name }}" class="form-control" />
<p class="text-muted small">A simple, human-readable name to use as an identifier for this Egg.</p>
</div>
<div class="form-group">
<label for="pUuid" class="control-label">UUID</label>
<input type="text" id="pUuid" readonly value="{{ $egg->uuid }}" class="form-control" />
<p class="text-muted small">This is the globally unique identifier for this Egg which the Daemon uses as an identifier.</p>
</div>
<div class="form-group">
<label for="pAuthor" class="control-label">Author</label>
<input type="text" id="pAuthor" readonly value="{{ $egg->author }}" class="form-control" />
<p class="text-muted small">The author of this version of the Egg. Uploading a new Egg configuration from a different author will change this.</p>
</div>
<div class="form-group">
<label for="pDockerImage" class="control-label">Docker Image <span class="field-required"></span></label>
<input type="text" id="pDockerImage" name="docker_image" value="{{ $egg->docker_image }}" class="form-control" />
<p class="text-muted small">The default docker image that should be used for new servers using this Egg. This can be changed per-server as needed.</p>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="pDescription" class="control-label">Description <span class="field-required"></span></label>
<textarea id="pDescription" name="description" class="form-control" rows="6">{{ $egg->description }}</textarea>
<p class="text-muted small">A description of this Egg that will be displayed throughout the Panel as needed.</p>
</div>
<div class="form-group">
<label for="pStartup" class="control-label">Startup Command <span class="field-required"></span></label>
<textarea id="pStartup" name="startup" class="form-control" rows="6">{{ $egg->startup }}</textarea>
<p class="text-muted small">The default startup command that should be used for new servers using this Egg.</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-xs-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Process Management</h3>
</div>
<div class="box-body">
<div class="row">
<div class="col-xs-12">
<div class="alert alert-warning">
<p>The following configuration options should not be edited unless you understand how this system works. If wrongly modified it is possible for the daemon to break.</p>
<p>All fields are required unless you select a separate option from the 'Copy Settings From' dropdown, in which case fields may be left blank to use the values from that Egg.</p>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="pConfigFrom" class="form-label">Copy Settings From</label>
<select name="config_from" id="pConfigFrom" class="form-control">
<option value="">None</option>
@foreach($egg->nest->eggs as $o)
<option value="{{ $o->id }}" {{ ($egg->config_from !== $o->id) ?: 'selected' }}>{{ $o->name }} &lt;{{ $o->author }}&gt;</option>
@endforeach
</select>
<p class="text-muted small">If you would like to default to settings from another Egg select it from the menu above.</p>
</div>
<div class="form-group">
<label for="pConfigStop" class="form-label">Stop Command</label>
<input type="text" id="pConfigStop" name="config_stop" class="form-control" value="{{ $egg->config_stop }}" />
<p class="text-muted small">The command that should be sent to server processes to stop them gracefully. If you need to send a <code>SIGINT</code> you should enter <code>^C</code> here.</p>
</div>
<div class="form-group">
<label for="pConfigLogs" class="form-label">Log Configuration</label>
<textarea data-action="handle-tabs" id="pConfigLogs" name="config_logs" class="form-control" rows="6">{{ ! is_null($egg->config_logs) ? json_encode(json_decode($egg->config_logs), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) : '' }}</textarea>
<p class="text-muted small">This should be a JSON representation of where log files are stored, and whether or not the daemon should be creating custom logs.</p>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="pConfigFiles" class="form-label">Configuration Files</label>
<textarea data-action="handle-tabs" id="pConfigFiles" name="config_files" class="form-control" rows="6">{{ ! is_null($egg->config_files) ? json_encode(json_decode($egg->config_files), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) : '' }}</textarea>
<p class="text-muted small">This should be a JSON representation of configuration files to modify and what parts should be changed.</p>
</div>
<div class="form-group">
<label for="pConfigStartup" class="form-label">Start Configuration</label>
<textarea data-action="handle-tabs" id="pConfigStartup" name="config_startup" class="form-control" rows="6">{{ ! is_null($egg->config_startup) ? json_encode(json_decode($egg->config_startup), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) : '' }}</textarea>
<p class="text-muted small">This should be a JSON representation of what values the daemon should be looking for when booting a server to determine completion.</p>
</div>
</div>
</div>
</div>
<div class="box-footer">
{!! csrf_field() !!}
<button type="submit" name="_method" value="PATCH" class="btn btn-primary btn-sm pull-right">Save</button>
<a href="{{ route('admin.nests.egg.export', ['option' => $egg->id]) }}" class="btn btn-sm btn-info pull-right" style="margin-right:10px;">Export</a>
<button id="deleteButton" type="submit" name="_method" value="DELETE" class="btn btn-danger btn-sm muted muted-hover">
<i class="fa fa-trash-o"></i>
</button>
</div>
</div>
</div>
<div class="col-xs-12">
<div class="alert alert-info">
<strong>Notice:</strong> Editing an Egg or any of the Process Management fields <em>requires</em> that each Daemon be rebooted in order to apply the changes.
</div>
</div>
</div>
</form>
@endsection
@section('footer-scripts')
@parent
<script>
$('#pConfigFrom').select2();
$('#deleteButton').on('mouseenter', function (event) {
$(this).find('i').html(' Delete Egg');
}).on('mouseleave', function (event) {
$(this).find('i').html('');
});
$('textarea[data-action="handle-tabs"]').on('keydown', function(event) {
if (event.keyCode === 9) {
event.preventDefault();
var curPos = $(this)[0].selectionStart;
var prepend = $(this).val().substr(0, curPos);
var append = $(this).val().substr(curPos);
$(this).val(prepend + ' ' + append);
}
});
</script>
@endsection

View file

@ -0,0 +1,58 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
Administration
@endsection
@section('content-header')
<h1>Administrative Overview<small>A quick glance at your system.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li class="active">Index</li>
</ol>
@endsection
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="box
@if($version->isLatestPanel())
box-success
@else
box-danger
@endif
">
<div class="box-header with-border">
<h3 class="box-title">System Information</h3>
</div>
<div class="box-body">
@if ($version->isLatestPanel())
You are running Pterodactyl Panel version <code>{{ config('app.version') }}</code>. Your panel is up-to-date!
@else
Your panel is <strong>not up-to-date!</strong> The latest version is <a href="https://github.com/Pterodactyl/Panel/releases/v{{ $version->getPanel() }}" target="_blank"><code>{{ $version->getPanel() }}</code></a> and you are currently running version <code>{{ config('app.version') }}</code>.
@endif
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-6 col-sm-3 text-center">
<a href="{{ $version->getDiscord() }}"><button class="btn btn-warning" style="width:100%;"><i class="fa fa-fw fa-support"></i> Get Help <small>(via Discord)</small></button></a>
</div>
<div class="col-xs-6 col-sm-3 text-center">
<a href="https://docs.pterodactyl.io"><button class="btn btn-primary" style="width:100%;"><i class="fa fa-fw fa-link"></i> Documentation</button></a>
</div>
<div class="clearfix visible-xs-block">&nbsp;</div>
<div class="col-xs-6 col-sm-3 text-center">
<a href="https://github.com/Pterodactyl/Panel"><button class="btn btn-primary" style="width:100%;"><i class="fa fa-fw fa-support"></i> Github</button></a>
</div>
<div class="col-xs-6 col-sm-3 text-center">
<a href="https://donorbox.org/pterodactyl"><button class="btn btn-success" style="width:100%;"><i class="fa fa-fw fa-money"></i> Support the Project</button></a>
</div>
</div>
@endsection

View file

@ -0,0 +1,86 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
Locations
@endsection
@section('content-header')
<h1>Locations<small>All locations that nodes can be assigned to for easier categorization.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li class="active">Locations</li>
</ol>
@endsection
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Location List</h3>
<div class="box-tools">
<button class="btn btn-sm btn-primary" data-toggle="modal" data-target="#newLocationModal">Create New</button>
</div>
</div>
<div class="box-body table-responsive no-padding">
<table class="table table-hover">
<tbody>
<tr>
<th>ID</th>
<th>Short Code</th>
<th>Description</th>
<th class="text-center">Nodes</th>
<th class="text-center">Servers</th>
</tr>
@foreach ($locations as $location)
<tr>
<td><code>{{ $location->id }}</code></td>
<td><a href="{{ route('admin.locations.view', $location->id) }}">{{ $location->short }}</a></td>
<td>{{ $location->long }}</td>
<td class="text-center">{{ $location->nodes_count }}</td>
<td class="text-center">{{ $location->servers_count }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="modal fade" id="newLocationModal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<form action="{{ route('admin.locations') }}" method="POST">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">Create Location</h4>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-12">
<label for="pShortModal" class="form-label">Short Code</label>
<input type="text" name="short" id="pShortModal" class="form-control" />
<p class="text-muted small">A short identifier used to distinguish this location from others. Must be between 1 and 60 characters, for example, <code>us.nyc.lvl3</code>.</p>
</div>
<div class="col-md-12">
<label for="pLongModal" class="form-label">Description</label>
<textarea name="long" id="pLongModal" class="form-control" rows="4"></textarea>
<p class="text-muted small">A longer description of this location. Must be less than 255 characters.</p>
</div>
</div>
</div>
<div class="modal-footer">
{!! csrf_field() !!}
<button type="button" class="btn btn-default btn-sm pull-left" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-success btn-sm">Create</button>
</div>
</form>
</div>
</div>
</div>
@endsection

View file

@ -0,0 +1,74 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
Locations &rarr; View &rarr; {{ $location->short }}
@endsection
@section('content-header')
<h1>{{ $location->short }}<small>{{ str_limit($location->long, 75) }}</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li><a href="{{ route('admin.locations') }}">Locations</a></li>
<li class="active">{{ $location->short }}</li>
</ol>
@endsection
@section('content')
<div class="row">
<div class="col-sm-6">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Location Details</h3>
</div>
<form action="{{ route('admin.locations.view', $location->id) }}" method="POST">
<div class="box-body">
<div class="form-group">
<label for="pShort" class="form-label">Short Code</label>
<input type="text" id="pShort" name="short" class="form-control" value="{{ $location->short }}" />
</div>
<div class="form-group">
<label for="pLong" class="form-label">Description</label>
<textarea id="pLong" name="long" class="form-control" rows="4">{{ $location->long }}</textarea>
</div>
</div>
<div class="box-footer">
{!! csrf_field() !!}
{!! method_field('PATCH') !!}
<button name="action" value="edit" class="btn btn-sm btn-primary pull-right">Save</button>
<button name="action" value="delete" class="btn btn-sm btn-danger pull-left muted muted-hover"><i class="fa fa-trash-o"></i></button>
</div>
</form>
</div>
</div>
<div class="col-sm-6">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Nodes</h3>
</div>
<div class="box-body table-responsive no-padding">
<table class="table table-hover">
<tr>
<th>ID</th>
<th>Name</th>
<th>FQDN</th>
<th>Servers</th>
</tr>
@foreach($location->nodes as $node)
<tr>
<td><code>{{ $node->id }}</code></td>
<td><a href="{{ route('admin.nodes.view', $node->id) }}">{{ $node->name }}</a></td>
<td><code>{{ $node->fqdn }}</code></td>
<td>{{ $node->servers->count() }}</td>
</tr>
@endforeach
</table>
</div>
</div>
</div>
</div>
@endsection

View file

@ -0,0 +1,109 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
Nests
@endsection
@section('content-header')
<h1>Nests<small>All nests currently available on this system.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li class="active">Nests</li>
</ol>
@endsection
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="alert alert-danger">
Eggs are a powerful feature of Pterodactyl Panel that allow for extreme flexibility and configuration. Please note that while powerful, modifying an egg wrongly can very easily brick your servers and cause more problems. Please avoid editing our default eggs those provided by <code>support@pterodactyl.io</code> unless you are absolutely sure of what you are doing.
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Configured Nests</h3>
<div class="box-tools">
<a href="#" class="btn btn-sm btn-success" data-toggle="modal" data-target="#importServiceOptionModal" role="button"><i class="fa fa-upload"></i> Import Egg</a>
<a href="{{ route('admin.nests.new') }}" class="btn btn-primary btn-sm">Create New</a>
</div>
</div>
<div class="box-body table-responsive no-padding">
<table class="table table-hover">
<tr>
<th>ID</th>
<th>Name</th>
<th>Description</th>
<th class="text-center">Eggs</th>
<th class="text-center">Packs</th>
<th class="text-center">Servers</th>
</tr>
@foreach($nests as $nest)
<tr>
<td class="middle"><code>{{ $nest->id }}</code></td>
<td class="middle"><a href="{{ route('admin.nests.view', $nest->id) }}" data-toggle="tooltip" data-placement="right" title="{{ $nest->author }}">{{ $nest->name }}</a></td>
<td class="col-xs-6 middle">{{ $nest->description }}</td>
<td class="text-center middle">{{ $nest->eggs_count }}</td>
<td class="text-center middle">{{ $nest->packs_count }}</td>
<td class="text-center middle">{{ $nest->servers_count }}</td>
</tr>
@endforeach
</table>
</div>
</div>
</div>
</div>
<div class="modal fade" tabindex="-1" role="dialog" id="importServiceOptionModal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">Import an Egg</h4>
</div>
<form action="{{ route('admin.nests.egg.import') }}" enctype="multipart/form-data" method="POST">
<div class="modal-body">
<div class="form-group">
<label class="control-label" for="pImportFile">Egg File <span class="field-required"></span></label>
<div>
<input id="pImportFile" type="file" name="import_file" class="form-control" accept="application/json" />
<p class="small text-muted">Select the <code>.json</code> file for the new egg that you wish to import.</p>
</div>
</div>
<div class="form-group">
<label class="control-label" for="pImportToNest">Associated Nest <span class="field-required"></span></label>
<div>
<select id="pImportToNest" name="import_to_nest">
@foreach($nests as $nest)
<option value="{{ $nest->id }}">{{ $nest->name }} &lt;{{ $nest->author }}&gt;</option>
@endforeach
</select>
<p class="small text-muted">Select the nest that this egg will be associated with from the dropdown. If you wish to associate it with a new nest you will need to create that nest before continuing.</p>
</div>
</div>
</div>
<div class="modal-footer">
{{ csrf_field() }}
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Import</button>
</div>
</form>
</div>
</div>
</div>
@endsection
@section('footer-scripts')
@parent
<script>
$(document).ready(function() {
$('#pImportToNest').select2();
});
</script>
@endsection

View file

@ -0,0 +1,52 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
New Nest
@endsection
@section('content-header')
<h1>New Nest<small>Configure a new nest to deploy to all nodes.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li><a href="{{ route('admin.nests') }}">Nests</a></li>
<li class="active">New</li>
</ol>
@endsection
@section('content')
<form action="{{ route('admin.nests.new') }}" method="POST">
<div class="row">
<div class="col-md-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">New Nest</h3>
</div>
<div class="box-body">
<div class="form-group">
<label class="control-label">Name</label>
<div>
<input type="text" name="name" class="form-control" value="{{ old('name') }}" />
<p class="text-muted"><small>This should be a descriptive category name that encompasses all of the eggs within the nest.</small></p>
</div>
</div>
<div class="form-group">
<label class="control-label">Description</label>
<div>
<textarea name="description" class="form-control" rows="6">{{ old('description') }}</textarea>
</div>
</div>
</div>
<div class="box-footer">
{!! csrf_field() !!}
<button type="submit" class="btn btn-primary pull-right">Save</button>
</div>
</div>
</div>
</div>
</form>
@endsection

View file

@ -0,0 +1,122 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
Nests &rarr; {{ $nest->name }}
@endsection
@section('content-header')
<h1>{{ $nest->name }}<small>{{ str_limit($nest->description, 50) }}</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li><a href="{{ route('admin.nests') }}">Nests</a></li>
<li class="active">{{ $nest->name }}</li>
</ol>
@endsection
@section('content')
<div class="row">
<form action="{{ route('admin.nests.view', $nest->id) }}" method="POST">
<div class="col-md-6">
<div class="box">
<div class="box-body">
<div class="form-group">
<label class="control-label">Name <span class="field-required"></span></label>
<div>
<input type="text" name="name" class="form-control" value="{{ $nest->name }}" />
<p class="text-muted"><small>This should be a descriptive category name that encompasses all of the options within the service.</small></p>
</div>
</div>
<div class="form-group">
<label class="control-label">Description <span class="field-required"></span></label>
<div>
<textarea name="description" class="form-control" rows="7">{{ $nest->description }}</textarea>
</div>
</div>
</div>
<div class="box-footer">
{!! csrf_field() !!}
<button type="submit" name="_method" value="PATCH" class="btn btn-primary btn-sm pull-right">Save</button>
<button id="deleteButton" type="submit" name="_method" value="DELETE" class="btn btn-sm btn-danger muted muted-hover"><i class="fa fa-trash-o"></i></button>
</div>
</div>
</div>
</form>
<div class="col-md-6">
<div class="box">
<div class="box-body">
<div class="form-group">
<label class="control-label">Nest ID</label>
<div>
<input type="text" readonly class="form-control" value="{{ $nest->id }}" />
<p class="text-muted small">A unique ID used for identification of this nest internally and through the API.</p>
</div>
</div>
<div class="form-group">
<label class="control-label">Author</label>
<div>
<input type="text" readonly class="form-control" value="{{ $nest->author }}" />
<p class="text-muted small">The author of this service option. Please direct questions and issues to them unless this is an official option authored by <code>support@pterodactyl.io</code>.</p>
</div>
</div>
<div class="form-group">
<label class="control-label">UUID</label>
<div>
<input type="text" readonly class="form-control" value="{{ $nest->uuid }}" />
<p class="text-muted small">A UUID that all servers using this option are assigned for identification purposes.</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Nest Eggs</h3>
</div>
<div class="box-body table-responsive no-padding">
<table class="table table-hover">
<tr>
<th>ID</th>
<th>Name</th>
<th>Description</th>
<th class="text-center">Servers</th>
<th class="text-center"></th>
</tr>
@foreach($nest->eggs as $egg)
<tr>
<td class="align-middle"><code>{{ $egg->id }}</code></td>
<td class="align-middle"><a href="{{ route('admin.nests.egg.view', $egg->id) }}" data-toggle="tooltip" data-placement="right" title="{{ $egg->author }}">{{ $egg->name }}</a></td>
<td class="col-xs-8 align-middle">{!! $egg->description !!}</td>
<td class="text-center align-middle"><code>{{ $egg->servers->count() }}</code></td>
<td class="align-middle">
<a href="{{ route('admin.nests.egg.export', ['egg' => $egg->id]) }}"><i class="fa fa-download"></i></a>
</td>
</tr>
@endforeach
</table>
</div>
<div class="box-footer">
<a href="{{ route('admin.nests.egg.new') }}"><button class="btn btn-success btn-sm pull-right">New Egg</button></a>
</div>
</div>
</div>
</div>
@endsection
@section('footer-scripts')
@parent
<script>
$('#deleteButton').on('mouseenter', function (event) {
$(this).find('i').html(' Delete Nest');
}).on('mouseleave', function (event) {
$(this).find('i').html('');
});
</script>
@endsection

View file

@ -0,0 +1,112 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
List Nodes
@endsection
@section('scripts')
@parent
{!! Theme::css('vendor/fontawesome/animation.min.css') !!}
@endsection
@section('content-header')
<h1>Nodes<small>All nodes available on the system.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li class="active">Nodes</li>
</ol>
@endsection
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Node List</h3>
<div class="box-tools">
<form action="{{ route('admin.nodes') }}" method="GET">
<div class="input-group input-group-sm">
<input type="text" name="query" class="form-control pull-right" style="width:30%;" value="{{ request()->input('query') }}" placeholder="Search Nodes">
<div class="input-group-btn">
<button type="submit" class="btn btn-default"><i class="fa fa-search"></i></button>
<a href="{{ route('admin.nodes.new') }}"><button type="button" class="btn btn-sm btn-primary" style="border-radius: 0 3px 3px 0;margin-left:-1px;">Create New</button></a>
</div>
</div>
</form>
</div>
</div>
<div class="box-body table-responsive no-padding">
<table class="table table-hover">
<tbody>
<tr>
<th></th>
<th>Name</th>
<th>Location</th>
<th>Memory</th>
<th>Disk</th>
<th class="text-center">Servers</th>
<th class="text-center">SSL</th>
<th class="text-center">Public</th>
</tr>
@foreach ($nodes as $node)
<tr>
<td class="text-center text-muted left-icon" data-action="ping" data-secret="{{ $node->daemonSecret }}" data-location="{{ $node->scheme }}://{{ $node->fqdn }}:{{ $node->daemonListen }}/v1"><i class="fa fa-fw fa-refresh fa-spin"></i></td>
<td>{!! $node->maintenance_mode ? '<span class="label label-warning"><i class="fa fa-wrench"></i></span> ' : '' !!}<a href="{{ route('admin.nodes.view', $node->id) }}">{{ $node->name }}</a></td>
<td>{{ $node->location->short }}</td>
<td>{{ $node->memory }} MB</td>
<td>{{ $node->disk }} MB</td>
<td class="text-center">{{ $node->servers_count }}</td>
<td class="text-center" style="color:{{ ($node->scheme === 'https') ? '#50af51' : '#d9534f' }}"><i class="fa fa-{{ ($node->scheme === 'https') ? 'lock' : 'unlock' }}"></i></td>
<td class="text-center"><i class="fa fa-{{ ($node->public) ? 'eye' : 'eye-slash' }}"></i></td>
</tr>
@endforeach
</tbody>
</table>
</div>
@if($nodes->hasPages())
<div class="box-footer with-border">
<div class="col-md-12 text-center">{!! $nodes->appends(['query' => Request::input('query')])->render() !!}</div>
</div>
@endif
</div>
</div>
</div>
@endsection
@section('footer-scripts')
@parent
<script>
(function pingNodes() {
$('td[data-action="ping"]').each(function(i, element) {
$.ajax({
type: 'GET',
url: $(element).data('location'),
headers: {
'X-Access-Token': $(element).data('secret'),
},
timeout: 5000
}).done(function (data) {
$(element).find('i').tooltip({
title: 'v' + data.version,
});
$(element).removeClass('text-muted').find('i').removeClass().addClass('fa fa-fw fa-heartbeat faa-pulse animated').css('color', '#50af51');
}).fail(function (error) {
var errorText = 'Error connecting to node! Check browser console for details.';
try {
errorText = error.responseJSON.errors[0].detail || errorText;
} catch (ex) {}
$(element).removeClass('text-muted').find('i').removeClass().addClass('fa fa-fw fa-heart-o').css('color', '#d9534f');
$(element).find('i').tooltip({ title: errorText });
});
}).promise().done(function () {
setTimeout(pingNodes, 10000);
});
})();
</script>
@endsection

View file

@ -0,0 +1,180 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
Nodes &rarr; New
@endsection
@section('content-header')
<h1>New Node<small>Create a new local or remote node for servers to be installed to.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li><a href="{{ route('admin.nodes') }}">Nodes</a></li>
<li class="active">New</li>
</ol>
@endsection
@section('content')
<form action="{{ route('admin.nodes.new') }}" method="POST">
<div class="row">
<div class="col-sm-6">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Basic Details</h3>
</div>
<div class="box-body">
<div class="form-group">
<label for="pName" class="form-label">Name</label>
<input type="text" name="name" id="pName" class="form-control" value="{{ old('name') }}"/>
<p class="text-muted small">Character limits: <code>a-zA-Z0-9_.-</code> and <code>[Space]</code> (min 1, max 100 characters).</p>
</div>
<div class="form-group">
<label for="pDescription" class="form-label">Description</label>
<textarea name="description" id="pDescription" rows="4" class="form-control">{{ old('description') }}</textarea>
</div>
<div class="form-group">
<label for="pLocationId" class="form-label">Location</label>
<select name="location_id" id="pLocationId">
@foreach($locations as $location)
<option value="{{ $location->id }}" {{ $location->id != old('location_id') ?: 'selected' }}>{{ $location->short }}</option>
@endforeach
</select>
</div>
<div class="form-group">
<label class="form-label">Node Visibility</label>
<div>
<div class="radio radio-success radio-inline">
<input type="radio" id="pPublicTrue" value="1" name="public" checked>
<label for="pPublicTrue"> Public </label>
</div>
<div class="radio radio-danger radio-inline">
<input type="radio" id="pPublicFalse" value="0" name="public">
<label for="pPublicFalse"> Private </label>
</div>
</div>
<p class="text-muted small">By setting a node to <code>private</code> you will be denying the ability to auto-deploy to this node.
</div>
<div class="form-group">
<label for="pFQDN" class="form-label">FQDN</label>
<input type="text" name="fqdn" id="pFQDN" class="form-control" value="{{ old('fqdn') }}"/>
<p class="text-muted small">Please enter domain name (e.g <code>node.example.com</code>) to be used for connecting to the daemon. An IP address may be used <em>only</em> if you are not using SSL for this node.</p>
</div>
<div class="form-group">
<label class="form-label">Communicate Over SSL</label>
<div>
<div class="radio radio-success radio-inline">
<input type="radio" id="pSSLTrue" value="https" name="scheme" checked>
<label for="pSSLTrue"> Use SSL Connection</label>
</div>
<div class="radio radio-danger radio-inline">
<input type="radio" id="pSSLFalse" value="http" name="scheme" @if(request()->isSecure()) disabled @endif>
<label for="pSSLFalse"> Use HTTP Connection</label>
</div>
</div>
@if(request()->isSecure())
<p class="text-danger small">Your Panel is currently configured to use a secure connection. In order for browsers to connect to your node it <strong>must</strong> use a SSL connection.</p>
@else
<p class="text-muted small">In most cases you should select to use a SSL connection. If using an IP Address or you do not wish to use SSL at all, select a HTTP connection.</p>
@endif
</div>
<div class="form-group">
<label class="form-label">Behind Proxy</label>
<div>
<div class="radio radio-success radio-inline">
<input type="radio" id="pProxyFalse" value="0" name="behind_proxy" checked>
<label for="pProxyFalse"> Not Behind Proxy </label>
</div>
<div class="radio radio-info radio-inline">
<input type="radio" id="pProxyTrue" value="1" name="behind_proxy">
<label for="pProxyTrue"> Behind Proxy </label>
</div>
</div>
<p class="text-muted small">If you are running the daemon behind a proxy such as Cloudflare, select this to have the daemon skip looking for certificates on boot.</p>
</div>
</div>
</div>
</div>
<div class="col-sm-6">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Configuration</h3>
</div>
<div class="box-body">
<div class="row">
<div class="form-group col-xs-12">
<label for="pDaemonBase" class="form-label">Daemon Server File Directory</label>
<input type="text" name="daemonBase" id="pDaemonBase" class="form-control" value="/srv/daemon-data" />
<p class="text-muted small">Enter the directory where server files should be stored. <strong>If you use OVH you should check your partition scheme. You may need to use <code>/home/daemon-data</code> to have enough space.</strong></p>
</div>
<div class="form-group col-md-6">
<label for="pMemory" class="form-label">Total Memory</label>
<div class="input-group">
<input type="text" name="memory" data-multiplicator="true" class="form-control" id="pMemory" value="{{ old('memory') }}"/>
<span class="input-group-addon">MB</span>
</div>
</div>
<div class="form-group col-md-6">
<label for="pMemoryOverallocate" class="form-label">Memory Over-Allocation</label>
<div class="input-group">
<input type="text" name="memory_overallocate" class="form-control" id="pMemoryOverallocate" value="{{ old('memory_overallocate') }}"/>
<span class="input-group-addon">%</span>
</div>
</div>
<div class="col-md-12">
<p class="text-muted small">Enter the total amount of memory available for new servers. If you would like to allow overallocation of memory enter the percentage that you want to allow. To disable checking for overallocation enter <code>-1</code> into the field. Entering <code>0</code> will prevent creating new servers if it would put the node over the limit.</p>
</div>
</div>
<div class="row">
<div class="form-group col-md-6">
<label for="pDisk" class="form-label">Total Disk Space</label>
<div class="input-group">
<input type="text" name="disk" data-multiplicator="true" class="form-control" id="pDisk" value="{{ old('disk') }}"/>
<span class="input-group-addon">MB</span>
</div>
</div>
<div class="form-group col-md-6">
<label for="pDiskOverallocate" class="form-label">Disk Over-Allocation</label>
<div class="input-group">
<input type="text" name="disk_overallocate" class="form-control" id="pDiskOverallocate" value="{{ old('disk_overallocate') }}"/>
<span class="input-group-addon">%</span>
</div>
</div>
<div class="col-md-12">
<p class="text-muted small">Enter the total amount of disk space available for new servers. If you would like to allow overallocation of disk space enter the percentage that you want to allow. To disable checking for overallocation enter <code>-1</code> into the field. Entering <code>0</code> will prevent creating new servers if it would put the node over the limit.</p>
</div>
</div>
<div class="row">
<div class="form-group col-md-6">
<label for="pDaemonListen" class="form-label">Daemon Port</label>
<input type="text" name="daemonListen" class="form-control" id="pDaemonListen" value="8080" />
</div>
<div class="form-group col-md-6">
<label for="pDaemonSFTP" class="form-label">Daemon SFTP Port</label>
<input type="text" name="daemonSFTP" class="form-control" id="pDaemonSFTP" value="2022" />
</div>
<div class="col-md-12">
<p class="text-muted small">The daemon runs its own SFTP management container and does not use the SSHd process on the main physical server. <Strong>Do not use the same port that you have assigned for your physical server's SSH process.</strong> If you will be running the daemon behind CloudFlare&reg; you should set the daemon port to <code>8443</code> to allow websocket proxying over SSL.</p>
</div>
</div>
</div>
<div class="box-footer">
{!! csrf_field() !!}
<button type="submit" class="btn btn-success pull-right">Create Node</button>
</div>
</div>
</div>
</div>
</form>
@endsection
@section('footer-scripts')
@parent
<script>
$('#pLocationId').select2();
</script>
@endsection

View file

@ -0,0 +1,365 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
{{ $node->name }}: Allocations
@endsection
@section('content-header')
<h1>{{ $node->name }}<small>Control allocations available for servers on this node.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li><a href="{{ route('admin.nodes') }}">Nodes</a></li>
<li><a href="{{ route('admin.nodes.view', $node->id) }}">{{ $node->name }}</a></li>
<li class="active">Allocations</li>
</ol>
@endsection
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="nav-tabs-custom nav-tabs-floating">
<ul class="nav nav-tabs">
<li><a href="{{ route('admin.nodes.view', $node->id) }}">About</a></li>
<li><a href="{{ route('admin.nodes.view.settings', $node->id) }}">Settings</a></li>
<li><a href="{{ route('admin.nodes.view.configuration', $node->id) }}">Configuration</a></li>
<li class="active"><a href="{{ route('admin.nodes.view.allocation', $node->id) }}">Allocation</a></li>
<li><a href="{{ route('admin.nodes.view.servers', $node->id) }}">Servers</a></li>
</ul>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-8">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Existing Allocations</h3>
</div>
<div class="box-body table-responsive no-padding" style="overflow-x: visible">
<table class="table table-hover" style="margin-bottom:0;">
<tr>
<th>
<input type="checkbox" class="select-all-files hidden-xs" data-action="selectAll">
</th>
<th>IP Address <i class="fa fa-fw fa-minus-square" style="font-weight:normal;color:#d9534f;cursor:pointer;" data-toggle="modal" data-target="#allocationModal"></i></th>
<th>IP Alias</th>
<th>Port</th>
<th>Assigned To</th>
<th>
<div class="btn-group hidden-xs">
<button type="button" id="mass_actions" class="btn btn-sm btn-default dropdown-toggle disabled"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">@lang('server.allocations.mass_actions') <span class="caret"></span>
</button>
<ul class="dropdown-menu dropdown-massactions">
<li><a href="#" id="selective-deletion" data-action="selective-deletion">@lang('server.allocations.delete') <i class="fa fa-fw fa-trash-o"></i></a></li>
</ul>
</div>
</th>
</tr>
@foreach($node->allocations as $allocation)
<tr>
<td class="middle min-size" data-identifier="type">
@if(is_null($allocation->server_id))
<input type="checkbox" class="select-file hidden-xs" data-action="addSelection">
@else
<input disabled="disabled" type="checkbox" class="select-file hidden-xs" data-action="addSelection">
@endif
</td>
<td class="col-sm-3 middle" data-identifier="ip">{{ $allocation->ip }}</td>
<td class="col-sm-3 middle">
<input class="form-control input-sm" type="text" value="{{ $allocation->ip_alias }}" data-action="set-alias" data-id="{{ $allocation->id }}" placeholder="none" />
<span class="input-loader"><i class="fa fa-refresh fa-spin fa-fw"></i></span>
</td>
<td class="col-sm-2 middle" data-identifier="port">{{ $allocation->port }}</td>
<td class="col-sm-3 middle">
@if(! is_null($allocation->server))
<a href="{{ route('admin.servers.view', $allocation->server_id) }}">{{ $allocation->server->name }}</a>
@endif
</td>
<td class="col-sm-1 middle">
@if(is_null($allocation->server_id))
<button data-action="deallocate" data-id="{{ $allocation->id }}" class="btn btn-sm btn-danger"><i class="fa fa-trash-o"></i></button>
@else
<button class="btn btn-sm disabled"><i class="fa fa-trash-o"></i></button>
@endif
</td>
</tr>
@endforeach
</table>
</div>
@if($node->allocations->hasPages())
<div class="box-footer text-center">
{{ $node->allocations->render() }}
</div>
@endif
</div>
</div>
<div class="col-sm-4">
<form action="{{ route('admin.nodes.view.allocation', $node->id) }}" method="POST">
<div class="box box-success">
<div class="box-header with-border">
<h3 class="box-title">Assign New Allocations</h3>
</div>
<div class="box-body">
<div class="form-group">
<label for="pAllocationIP" class="control-label">IP Address</label>
<div>
<select class="form-control" name="allocation_ip" id="pAllocationIP" multiple>
@foreach($allocations as $allocation)
<option value="{{ $allocation->ip }}">{{ $allocation->ip }}</option>
@endforeach
</select>
<p class="text-muted small">Enter an IP address to assign ports to here.</p>
</div>
</div>
<div class="form-group">
<label for="pAllocationIP" class="control-label">IP Alias</label>
<div>
<input type="text" id="pAllocationAlias" class="form-control" name="allocation_alias" placeholder="alias" />
<p class="text-muted small">If you would like to assign a default alias to these allocations enter it here.</p>
</div>
</div>
<div class="form-group">
<label for="pAllocationPorts" class="control-label">Ports</label>
<div>
<select class="form-control" name="allocation_ports[]" id="pAllocationPorts" multiple></select>
<p class="text-muted small">Enter individual ports or port ranges here separated by commas or spaces.</p>
</div>
</div>
</div>
<div class="box-footer">
{!! csrf_field() !!}
<button type="submit" class="btn btn-success btn-sm pull-right">Submit</button>
</div>
</div>
</form>
</div>
</div>
<div class="modal fade" id="allocationModal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">Delete Allocations for IP Block</h4>
</div>
<form action="{{ route('admin.nodes.view.allocation.removeBlock', $node->id) }}" method="POST">
<div class="modal-body">
<div class="row">
<div class="col-md-12">
<select class="form-control" name="ip">
@foreach($allocations as $allocation)
<option value="{{ $allocation->ip }}">{{ $allocation->ip }}</option>
@endforeach
</select>
</div>
</div>
</div>
<div class="modal-footer">
{{{ csrf_field() }}}
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-danger">Delete Allocations</button>
</div>
</form>
</div>
</div>
</div>
@endsection
@section('footer-scripts')
@parent
<script>
$('[data-action="addSelection"]').on('click', function () {
updateMassActions();
});
$('[data-action="selectAll"]').on('click', function () {
$('input.select-file').not(':disabled').prop('checked', function (i, val) {
return !val;
});
updateMassActions();
});
$('[data-action="selective-deletion"]').on('mousedown', function () {
deleteSelected();
});
$('#pAllocationIP').select2({
tags: true,
maximumSelectionLength: 1,
selectOnClose: true,
tokenSeparators: [',', ' '],
});
$('#pAllocationPorts').select2({
tags: true,
selectOnClose: true,
tokenSeparators: [',', ' '],
});
$('button[data-action="deallocate"]').click(function (event) {
event.preventDefault();
var element = $(this);
var allocation = $(this).data('id');
swal({
title: '',
text: 'Are you sure you want to delete this allocation?',
type: 'warning',
showCancelButton: true,
allowOutsideClick: true,
closeOnConfirm: false,
confirmButtonText: 'Delete',
confirmButtonColor: '#d9534f',
showLoaderOnConfirm: true
}, function () {
$.ajax({
method: 'DELETE',
url: Router.route('admin.nodes.view.allocation.removeSingle', { node: Pterodactyl.node.id, allocation: allocation }),
headers: { 'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content') },
}).done(function (data) {
element.parent().parent().addClass('warning').delay(100).fadeOut();
swal({ type: 'success', title: 'Port Deleted!' });
}).fail(function (jqXHR) {
console.error(jqXHR);
swal({
title: 'Whoops!',
text: jqXHR.responseJSON.error,
type: 'error'
});
});
});
});
var typingTimer;
$('input[data-action="set-alias"]').keyup(function () {
clearTimeout(typingTimer);
$(this).parent().removeClass('has-error has-success');
typingTimer = setTimeout(sendAlias, 250, $(this));
});
var fadeTimers = [];
function sendAlias(element) {
element.parent().find('.input-loader').show();
clearTimeout(fadeTimers[element.data('id')]);
$.ajax({
method: 'POST',
url: Router.route('admin.nodes.view.allocation.setAlias', { id: Pterodactyl.node.id }),
headers: { 'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content') },
data: {
alias: element.val(),
allocation_id: element.data('id'),
}
}).done(function () {
element.parent().addClass('has-success');
}).fail(function (jqXHR) {
console.error(jqXHR);
element.parent().addClass('has-error');
}).always(function () {
element.parent().find('.input-loader').hide();
fadeTimers[element.data('id')] = setTimeout(clearHighlight, 2500, element);
});
}
function clearHighlight(element) {
element.parent().removeClass('has-error has-success');
}
function updateMassActions() {
if ($('input.select-file:checked').length > 0) {
$('#mass_actions').removeClass('disabled');
} else {
$('#mass_actions').addClass('disabled');
}
}
function deleteSelected() {
var selectedIds = [];
var selectedItems = [];
var selectedItemsElements = [];
$('input.select-file:checked').each(function () {
var $parent = $($(this).closest('tr'));
var id = $parent.find('[data-action="deallocate"]').data('id');
var $ip = $parent.find('td[data-identifier="ip"]');
var $port = $parent.find('td[data-identifier="port"]');
var block = `${$ip.text()}:${$port.text()}`;
selectedIds.push({
id: id
});
selectedItems.push(block);
selectedItemsElements.push($parent);
});
if (selectedItems.length !== 0) {
var formattedItems = "";
var i = 0;
$.each(selectedItems, function (key, value) {
formattedItems += ("<code>" + value + "</code>, ");
i++;
return i < 5;
});
formattedItems = formattedItems.slice(0, -2);
if (selectedItems.length > 5) {
formattedItems += ', and ' + (selectedItems.length - 5) + ' other(s)';
}
swal({
type: 'warning',
title: '',
text: 'Are you sure you want to delete the following allocations: ' + formattedItems + '?',
html: true,
showCancelButton: true,
showConfirmButton: true,
closeOnConfirm: false,
showLoaderOnConfirm: true
}, function () {
$.ajax({
method: 'DELETE',
url: Router.route('admin.nodes.view.allocation.removeMultiple', {
node: Pterodactyl.node.id
}),
headers: {'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content')},
data: JSON.stringify({
allocations: selectedIds
}),
contentType: 'application/json',
processData: false
}).done(function () {
$('#file_listing input:checked').each(function () {
$(this).prop('checked', false);
});
$.each(selectedItemsElements, function () {
$(this).addClass('warning').delay(200).fadeOut();
});
swal({
type: 'success',
title: 'Allocations Deleted'
});
}).fail(function (jqXHR) {
console.error(jqXHR);
swal({
type: 'error',
title: 'Whoops!',
html: true,
text: 'An error occurred while attempting to delete these allocations. Please try again.',
});
});
});
} else {
swal({
type: 'warning',
title: '',
text: 'Please select allocation(s) to delete.',
});
}
}
</script>
@endsection

View file

@ -0,0 +1,87 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
{{ $node->name }}: Configuration
@endsection
@section('content-header')
<h1>{{ $node->name }}<small>Your daemon configuration file.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li><a href="{{ route('admin.nodes') }}">Nodes</a></li>
<li><a href="{{ route('admin.nodes.view', $node->id) }}">{{ $node->name }}</a></li>
<li class="active">Configuration</li>
</ol>
@endsection
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="nav-tabs-custom nav-tabs-floating">
<ul class="nav nav-tabs">
<li><a href="{{ route('admin.nodes.view', $node->id) }}">About</a></li>
<li><a href="{{ route('admin.nodes.view.settings', $node->id) }}">Settings</a></li>
<li class="active"><a href="{{ route('admin.nodes.view.configuration', $node->id) }}">Configuration</a></li>
<li><a href="{{ route('admin.nodes.view.allocation', $node->id) }}">Allocation</a></li>
<li><a href="{{ route('admin.nodes.view.servers', $node->id) }}">Servers</a></li>
</ul>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-8">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Configuration File</h3>
</div>
<div class="box-body">
<pre class="no-margin">{{ $node->getConfigurationAsJson(true) }}</pre>
</div>
<div class="box-footer">
<p class="no-margin">This file should be placed in your daemon's <code>config</code> directory in a file called <code>core.json</code>.</p>
</div>
</div>
</div>
<div class="col-sm-4">
<div class="box box-success">
<div class="box-header with-border">
<h3 class="box-title">Auto-Deploy</h3>
</div>
<div class="box-body">
<p class="text-muted small">To simplify the configuration of nodes it is possible to fetch the config from the panel. A token is required for this process. The button below will generate a token and provide you with the commands necessary for automatic configuration of the node. <em>Tokens are only valid for 5 minutes.</em></p>
</div>
<div class="box-footer">
<button type="button" id="configTokenBtn" class="btn btn-sm btn-default" style="width:100%;">Generate Token</button>
</div>
</div>
</div>
</div>
@endsection
@section('footer-scripts')
@parent
<script>
$('#configTokenBtn').on('click', function (event) {
$.getJSON('{{ route('admin.nodes.view.configuration.token', $node->id) }}').done(function (data) {
swal({
type: 'success',
title: 'Token created.',
text: 'Your token will expire <strong>in 5 minutes.</strong><br /><br />' +
'<p>To auto-configure your node run the following command:<br /><small><pre>npm run configure -- --panel-url {{ config('app.url') }} --token ' + data.token + '</pre></small></p>',
html: true
})
}).fail(function () {
swal({
title: 'Error',
text: 'Something went wrong creating your token.',
type: 'error'
});
});
});
</script>
@endsection

View file

@ -0,0 +1,172 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
{{ $node->name }}
@endsection
@section('content-header')
<h1>{{ $node->name }}<small>A quick overview of your node.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li><a href="{{ route('admin.nodes') }}">Nodes</a></li>
<li class="active">{{ $node->name }}</li>
</ol>
@endsection
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="nav-tabs-custom nav-tabs-floating">
<ul class="nav nav-tabs">
<li class="active"><a href="{{ route('admin.nodes.view', $node->id) }}">About</a></li>
<li><a href="{{ route('admin.nodes.view.settings', $node->id) }}">Settings</a></li>
<li><a href="{{ route('admin.nodes.view.configuration', $node->id) }}">Configuration</a></li>
<li><a href="{{ route('admin.nodes.view.allocation', $node->id) }}">Allocation</a></li>
<li><a href="{{ route('admin.nodes.view.servers', $node->id) }}">Servers</a></li>
</ul>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-8">
<div class="row">
<div class="col-xs-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Information</h3>
</div>
<div class="box-body table-responsive no-padding">
<table class="table table-hover">
<tr>
<td>Daemon Version</td>
<td><code data-attr="info-version"><i class="fa fa-refresh fa-fw fa-spin"></i></code> (Latest: <code>{{ $version->getDaemon() }}</code>)</td>
</tr>
<tr>
<td>System Information</td>
<td data-attr="info-system"><i class="fa fa-refresh fa-fw fa-spin"></i></td>
</tr>
<tr>
<td>Total CPU Cores</td>
<td data-attr="info-cpus"><i class="fa fa-refresh fa-fw fa-spin"></i></td>
</tr>
</table>
</div>
</div>
</div>
@if ($node->description)
<div class="col-xs-12">
<div class="box box-default">
<div class="box-header with-border">
Description
</div>
<div class="box-body table-responsive">
<pre>{{ $node->description }}</pre>
</div>
</div>
</div>
@endif
<div class="col-xs-12">
<div class="box box-danger">
<div class="box-header with-border">
<h3 class="box-title">Delete Node</h3>
</div>
<div class="box-body">
<p class="no-margin">Deleting a node is a irreversible action and will immediately remove this node from the panel. There must be no servers associated with this node in order to continue.</p>
</div>
<div class="box-footer">
<form action="{{ route('admin.nodes.view.delete', $node->id) }}" method="POST">
{!! csrf_field() !!}
{!! method_field('DELETE') !!}
<button type="submit" class="btn btn-danger btn-sm pull-right" {{ ($node->servers_count < 1) ?: 'disabled' }}>Yes, Delete This Node</button>
</form>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-4">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">At-a-Glance</h3>
</div>
<div class="box-body">
<div class="row">
@if($node->maintenance_mode)
<div class="col-sm-12">
<div class="info-box bg-grey">
<span class="info-box-icon"><i class="ion ion-wrench"></i></span>
<div class="info-box-content" style="padding: 23px 10px 0;">
<span class="info-box-text">This node is under</span>
<span class="info-box-number">Maintenance</span>
</div>
</div>
</div>
@endif
<div class="col-sm-12">
<div class="info-box bg-{{ $stats['disk']['css'] }}">
<span class="info-box-icon"><i class="ion ion-ios-folder-outline"></i></span>
<div class="info-box-content" style="padding: 15px 10px 0;">
<span class="info-box-text">Disk Space Allocated</span>
<span class="info-box-number">{{ $stats['disk']['value'] }} / {{ $stats['disk']['max'] }} Mb</span>
<div class="progress">
<div class="progress-bar" style="width: {{ $stats['disk']['percent'] }}%"></div>
</div>
</div>
</div>
</div>
<div class="col-sm-12">
<div class="info-box bg-{{ $stats['memory']['css'] }}">
<span class="info-box-icon"><i class="ion ion-ios-barcode-outline"></i></span>
<div class="info-box-content" style="padding: 15px 10px 0;">
<span class="info-box-text">Memory Allocated</span>
<span class="info-box-number">{{ $stats['memory']['value'] }} / {{ $stats['memory']['max'] }} Mb</span>
<div class="progress">
<div class="progress-bar" style="width: {{ $stats['memory']['percent'] }}%"></div>
</div>
</div>
</div>
</div>
<div class="col-sm-12">
<div class="info-box bg-gray">
<span class="info-box-icon"><i class="ion ion-social-buffer-outline"></i></span>
<div class="info-box-content" style="padding: 23px 10px 0;">
<span class="info-box-text">Total Servers</span>
<span class="info-box-number">{{ $node->servers_count }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
@section('footer-scripts')
@parent
<script>
(function getInformation() {
$.ajax({
method: 'GET',
url: '{{ $node->scheme }}://{{ $node->fqdn }}:{{ $node->daemonListen }}/v1',
timeout: 5000,
headers: {
'X-Access-Token': '{{ $node->daemonSecret }}'
},
}).done(function (data) {
$('[data-attr="info-version"]').html(data.version);
$('[data-attr="info-system"]').html(data.system.type + '(' + data.system.arch + ') <code>' + data.system.release + '</code>');
$('[data-attr="info-cpus"]').html(data.system.cpus);
}).fail(function (jqXHR) {
}).always(function() {
setTimeout(getInformation, 10000);
});
})();
</script>
@endsection

View file

@ -0,0 +1,81 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
{{ $node->name }}: Servers
@endsection
@section('content-header')
<h1>{{ $node->name }}<small>All servers currently assigned to this node.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li><a href="{{ route('admin.nodes') }}">Nodes</a></li>
<li><a href="{{ route('admin.nodes.view', $node->id) }}">{{ $node->name }}</a></li>
<li class="active">Servers</li>
</ol>
@endsection
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="nav-tabs-custom nav-tabs-floating">
<ul class="nav nav-tabs">
<li><a href="{{ route('admin.nodes.view', $node->id) }}">About</a></li>
<li><a href="{{ route('admin.nodes.view.settings', $node->id) }}">Settings</a></li>
<li><a href="{{ route('admin.nodes.view.configuration', $node->id) }}">Configuration</a></li>
<li><a href="{{ route('admin.nodes.view.allocation', $node->id) }}">Allocation</a></li>
<li class="active"><a href="{{ route('admin.nodes.view.servers', $node->id) }}">Servers</a></li>
</ul>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Process Manager</h3>
</div>
<div class="box-body table-responsive no-padding">
<table class="table table-hover">
<tr>
<th>ID</th>
<th>Server Name</th>
<th>Owner</th>
<th>Service</th>
<th class="text-center">Memory</th>
<th class="text-center">Disk</th>
<th class="text-center">CPU</th>
<th class="text-center">Status</th>
</tr>
@foreach($servers as $server)
<tr data-server="{{ $server->uuid }}">
<td><code>{{ $server->uuidShort }}</code></td>
<td><a href="{{ route('admin.servers.view', $server->id) }}">{{ $server->name }}</a></td>
<td><a href="{{ route('admin.users.view', $server->owner_id) }}">{{ $server->user->username }}</a></td>
<td>{{ $server->nest->name }} ({{ $server->egg->name }})</td>
<td class="text-center"><span data-action="memory">--</span> / {{ $server->memory === 0 ? '∞' : $server->memory }} MB</td>
<td class="text-center"><span data-action="disk">--</span> / {{ $server->disk === 0 ? '∞' : $server->disk }} MB </td>
<td class="text-center"><span data-action="cpu" data-cpumax="{{ $server->cpu }}">--</span> %</td>
<td class="text-center" data-action="status">--</td>
</tr>
@endforeach
</table>
@if($servers->hasPages())
<div class="box-footer with-border">
<div class="col-md-12 text-center">{!! $servers->render() !!}</div>
</div>
@endif
</div>
</div>
</div>
</div>
@endsection
@section('footer-scripts')
@parent
{!! Theme::js('js/admin/node/view-servers.js') !!}
@endsection

View file

@ -0,0 +1,245 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
{{ $node->name }}: Settings
@endsection
@section('content-header')
<h1>{{ $node->name }}<small>Configure your node settings.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li><a href="{{ route('admin.nodes') }}">Nodes</a></li>
<li><a href="{{ route('admin.nodes.view', $node->id) }}">{{ $node->name }}</a></li>
<li class="active">Settings</li>
</ol>
@endsection
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="nav-tabs-custom nav-tabs-floating">
<ul class="nav nav-tabs">
<li><a href="{{ route('admin.nodes.view', $node->id) }}">About</a></li>
<li class="active"><a href="{{ route('admin.nodes.view.settings', $node->id) }}">Settings</a></li>
<li><a href="{{ route('admin.nodes.view.configuration', $node->id) }}">Configuration</a></li>
<li><a href="{{ route('admin.nodes.view.allocation', $node->id) }}">Allocation</a></li>
<li><a href="{{ route('admin.nodes.view.servers', $node->id) }}">Servers</a></li>
</ul>
</div>
</div>
</div>
<form action="{{ route('admin.nodes.view.settings', $node->id) }}" method="POST">
<div class="row">
<div class="col-sm-6">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Settings</h3>
</div>
<div class="box-body row">
<div class="form-group col-xs-12">
<label for="name" class="control-label">Node Name</label>
<div>
<input type="text" autocomplete="off" name="name" class="form-control" value="{{ old('name', $node->name) }}" />
<p class="text-muted"><small>Character limits: <code>a-zA-Z0-9_.-</code> and <code>[Space]</code> (min 1, max 100 characters).</small></p>
</div>
</div>
<div class="form-group col-xs-12">
<label for="description" class="control-label">Description</label>
<div>
<textarea name="description" id="description" rows="4" class="form-control">{{ $node->description }}</textarea>
</div>
</div>
<div class="form-group col-xs-12">
<label for="name" class="control-label">Location</label>
<div>
<select name="location_id" class="form-control">
@foreach($locations as $location)
<option value="{{ $location->id }}" {{ (old('location_id', $node->location_id) === $location->id) ? 'selected' : '' }}>{{ $location->long }} ({{ $location->short }})</option>
@endforeach
</select>
</div>
</div>
<div class="form-group col-xs-12">
<label for="public" class="control-label">Allow Automatic Allocation <sup><a data-toggle="tooltip" data-placement="top" title="Allow automatic allocation to this Node?">?</a></sup></label>
<div>
<input type="radio" name="public" value="1" {{ (old('public', $node->public)) ? 'checked' : '' }} id="public_1" checked> <label for="public_1" style="padding-left:5px;">Yes</label><br />
<input type="radio" name="public" value="0" {{ (old('public', $node->public)) ? '' : 'checked' }} id="public_0"> <label for="public_0" style="padding-left:5px;">No</label>
</div>
</div>
<div class="form-group col-xs-12">
<label for="fqdn" class="control-label">Fully Qualified Domain Name</label>
<div>
<input type="text" autocomplete="off" name="fqdn" class="form-control" value="{{ old('fqdn', $node->fqdn) }}" />
</div>
<p class="text-muted"><small>Please enter domain name (e.g <code>node.example.com</code>) to be used for connecting to the daemon. An IP address may only be used if you are not using SSL for this node.
<a tabindex="0" data-toggle="popover" data-trigger="focus" title="Why do I need a FQDN?" data-content="In order to secure communications between your server and this node we use SSL. We cannot generate a SSL certificate for IP Addresses, and as such you will need to provide a FQDN.">Why?</a>
</small></p>
</div>
<div class="form-group col-xs-12">
<label class="form-label"><span class="label label-warning"><i class="fa fa-power-off"></i></span> Communicate Over SSL</label>
<div>
<div class="radio radio-success radio-inline">
<input type="radio" id="pSSLTrue" value="https" name="scheme" {{ (old('scheme', $node->scheme) === 'https') ? 'checked' : '' }}>
<label for="pSSLTrue"> Use SSL Connection</label>
</div>
<div class="radio radio-danger radio-inline">
<input type="radio" id="pSSLFalse" value="http" name="scheme" {{ (old('scheme', $node->scheme) !== 'https') ? 'checked' : '' }}>
<label for="pSSLFalse"> Use HTTP Connection</label>
</div>
</div>
<p class="text-muted small">In most cases you should select to use a SSL connection. If using an IP Address or you do not wish to use SSL at all, select a HTTP connection.</p>
</div>
<div class="form-group col-xs-12">
<label class="form-label"><span class="label label-warning"><i class="fa fa-power-off"></i></span> Behind Proxy</label>
<div>
<div class="radio radio-success radio-inline">
<input type="radio" id="pProxyFalse" value="0" name="behind_proxy" {{ (old('behind_proxy', $node->behind_proxy) == false) ? 'checked' : '' }}>
<label for="pProxyFalse"> Not Behind Proxy </label>
</div>
<div class="radio radio-info radio-inline">
<input type="radio" id="pProxyTrue" value="1" name="behind_proxy" {{ (old('behind_proxy', $node->behind_proxy) == true) ? 'checked' : '' }}>
<label for="pProxyTrue"> Behind Proxy </label>
</div>
</div>
<p class="text-muted small">If you are running the daemon behind a proxy such as Cloudflare, select this to have the daemon skip looking for certificates on boot.</p>
</div>
<div class="form-group col-xs-12">
<label class="form-label"><span class="label label-warning"><i class="fa fa-wrench"></i></span> Maintenance Mode</label>
<div>
<div class="radio radio-success radio-inline">
<input type="radio" id="pMaintenanceFalse" value="0" name="maintenance_mode" {{ (old('behind_proxy', $node->maintenance_mode) == false) ? 'checked' : '' }}>
<label for="pMaintenanceFalse"> Disabled</label>
</div>
<div class="radio radio-warning radio-inline">
<input type="radio" id="pMaintenanceTrue" value="1" name="maintenance_mode" {{ (old('behind_proxy', $node->maintenance_mode) == true) ? 'checked' : '' }}>
<label for="pMaintenanceTrue"> Enabled</label>
</div>
</div>
<p class="text-muted small">If the node is marked as 'Under Maintenance' users won't be able to access servers that are on this node.</p>
</div>
</div>
</div>
</div>
<div class="col-sm-6">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Allocation Limits</h3>
</div>
<div class="box-body row">
<div class="col-xs-12">
<div class="row">
<div class="form-group col-xs-6">
<label for="memory" class="control-label">Total Memory</label>
<div class="input-group">
<input type="text" name="memory" class="form-control" data-multiplicator="true" value="{{ old('memory', $node->memory) }}"/>
<span class="input-group-addon">MB</span>
</div>
</div>
<div class="form-group col-xs-6">
<label for="memory_overallocate" class="control-label">Overallocate</label>
<div class="input-group">
<input type="text" name="memory_overallocate" class="form-control" value="{{ old('memory_overallocate', $node->memory_overallocate) }}"/>
<span class="input-group-addon">%</span>
</div>
</div>
</div>
<p class="text-muted small">Enter the total amount of memory available on this node for allocation to servers. You may also provide a percentage that can allow allocation of more than the defined memory.</p>
</div>
<div class="col-xs-12">
<div class="row">
<div class="form-group col-xs-6">
<label for="disk" class="control-label">Disk Space</label>
<div class="input-group">
<input type="text" name="disk" class="form-control" data-multiplicator="true" value="{{ old('disk', $node->disk) }}"/>
<span class="input-group-addon">MB</span>
</div>
</div>
<div class="form-group col-xs-6">
<label for="disk_overallocate" class="control-label">Overallocate</label>
<div class="input-group">
<input type="text" name="disk_overallocate" class="form-control" value="{{ old('disk_overallocate', $node->disk_overallocate) }}"/>
<span class="input-group-addon">%</span>
</div>
</div>
</div>
<p class="text-muted small">Enter the total amount of disk space available on this node for server allocation. You may also provide a percentage that will determine the amount of disk space over the set limit to allow.</p>
</div>
</div>
</div>
</div>
<div class="col-sm-6">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">General Configuration</h3>
</div>
<div class="box-body row">
<div class="form-group col-xs-12">
<label for="disk_overallocate" class="control-label">Maximum Web Upload Filesize</label>
<div class="input-group">
<input type="text" name="upload_size" class="form-control" value="{{ old('upload_size', $node->upload_size) }}"/>
<span class="input-group-addon">MB</span>
</div>
<p class="text-muted"><small>Enter the maximum size of files that can be uploaded through the web-based file manager.</small></p>
</div>
<div class="col-xs-12">
<div class="row">
<div class="form-group col-md-6">
<label for="daemonListen" class="control-label"><span class="label label-warning"><i class="fa fa-power-off"></i></span> Daemon Port</label>
<div>
<input type="text" name="daemonListen" class="form-control" value="{{ old('daemonListen', $node->daemonListen) }}"/>
</div>
</div>
<div class="form-group col-md-6">
<label for="daemonSFTP" class="control-label"><span class="label label-warning"><i class="fa fa-power-off"></i></span> Daemon SFTP Port</label>
<div>
<input type="text" name="daemonSFTP" class="form-control" value="{{ old('daemonSFTP', $node->daemonSFTP) }}"/>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<p class="text-muted"><small>The daemon runs its own SFTP management container and does not use the SSHd process on the main physical server. <Strong>Do not use the same port that you have assigned for your physical server's SSH process.</strong></small></p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-xs-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Save Settings</h3>
</div>
<div class="box-body row">
<div class="form-group col-sm-6">
<div>
<input type="checkbox" name="reset_secret" id="reset_secret" /> <label for="reset_secret" class="control-label">Reset Daemon Master Key</label>
</div>
<p class="text-muted"><small>Resetting the daemon master key will void any request coming from the old key. This key is used for all sensitive operations on the daemon including server creation and deletion. We suggest changing this key regularly for security.</small></p>
</div>
</div>
<div class="box-footer">
{!! method_field('PATCH') !!}
{!! csrf_field() !!}
<button type="submit" class="btn btn-primary pull-right">Save Changes</button>
</div>
</div>
</div>
</div>
</form>
@endsection
@section('footer-scripts')
@parent
<script>
$('[data-toggle="popover"]').popover({
placement: 'auto'
});
$('select[name="location_id"]').select2();
</script>
@endsection

View file

@ -0,0 +1,70 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
List Packs
@endsection
@section('content-header')
<h1>Packs<small>All service packs available on the system.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li class="active">Packs</li>
</ol>
@endsection
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Pack List</h3>
<div class="box-tools">
<form action="{{ route('admin.packs') }}" method="GET">
<div class="input-group input-group-sm">
<input type="text" name="query" class="form-control pull-right" style="width:30%;" value="{{ request()->input('query') }}" placeholder="Search Packs">
<div class="input-group-btn">
<button type="submit" class="btn btn-default"><i class="fa fa-search"></i></button>
<a href="{{ route('admin.packs.new') }}"><button type="button" class="btn btn-sm btn-primary" style="border-radius: 0 3px 3px 0;margin-left:-1px;">Create New</button></a>
</div>
</div>
</form>
</div>
</div>
<div class="box-body table-responsive no-padding">
<table class="table table-hover">
<tbody>
<tr>
<th>ID</th>
<th>Pack Name</th>
<th>Version</th>
<th>Description</th>
<th>Egg</th>
<th class="text-center">Servers</th>
</tr>
@foreach ($packs as $pack)
<tr>
<td class="middle" data-toggle="tooltip" data-placement="right" title="{{ $pack->uuid }}"><code>{{ $pack->id }}</code></td>
<td class="middle"><a href="{{ route('admin.packs.view', $pack->id) }}">{{ $pack->name }}</a></td>
<td class="middle"><code>{{ $pack->version }}</code></td>
<td class="col-md-6">{{ str_limit($pack->description, 150) }}</td>
<td class="middle"><a href="{{ route('admin.nests.egg.view', $pack->egg->id) }}">{{ $pack->egg->name }}</a></td>
<td class="middle text-center">{{ $pack->servers_count }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@if ($packs->hasPages())
<div class="box-footer with-border">
<div class="col-md-12 text-center">{!! $packs->appends(['query' => Request::input('query')])->render() !!}</div>
</div>
@endif
</div>
</div>
</div>
@endsection

View file

@ -0,0 +1,47 @@
<div class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<form action="{{ route('admin.packs.new') }}" method="POST" enctype="multipart/form-data">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">Install Pack from Template</h4>
</div>
<div class="modal-body">
<div class="well" style="margin-bottom:0">
<div class="row">
<div class="col-md-12">
<label for="pEggIdModal" class="form-label">Associated Egg:</label>
<select id="pEggIdModal" name="egg_id" class="form-control">
@foreach($nests as $nest)
<optgroup label="{{ $nest->name }}">
@foreach($nest->eggs as $egg)
<option value="{{ $egg->id }}">{{ $egg->name }}</option>
@endforeach
</optgroup>
@endforeach
</select>
<p class="text-muted small">The Egg that this pack is associated with. Only servers that are assigned this Egg will be able to access this pack.</p>
</div>
</div>
<div class="row" style="margin-top:15px;">
<div class="col-md-12">
<div class="row">
<div class="form-group col-md-12">
<label class="control-label">Package Archive:</label>
<input name="file_upload" type="file" accept=".zip,.json, application/json, application/zip" />
<p class="text-muted"><small>This file should be either the <code>.json</code> template file, or a <code>.zip</code> pack archive containing <code>archive.tar.gz</code> and <code>import.json</code> within.<br /><br />This server is currently configured with the following limits: <code>upload_max_filesize={{ ini_get('upload_max_filesize') }}</code> and <code>post_max_size={{ ini_get('post_max_size') }}</code>. If your file is larger than either of those values this request will fail.</small></p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
{!! csrf_field() !!}
<button type="submit" name="action" value="from_template" class="btn btn-primary btn-sm">Install</button>
<button type="button" class="btn btn-default btn-sm pull-left" data-dismiss="modal">Cancel</button>
</div>
</form>
</div>
</div>
</div>

View file

@ -0,0 +1,144 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
Packs &rarr; New
@endsection
@section('content-header')
<h1>New Pack<small>Create a new pack on the system.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li><a href="{{ route('admin.packs') }}">Packs</a></li>
<li class="active">New</li>
</ol>
@endsection
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="nav-tabs-custom nav-tabs-floating">
<ul class="nav nav-tabs">
<li class="active"><a href="{{ route('admin.packs.new') }}">Configure Manually</a></li>
<li><a href="#modal" id="toggleModal">Install From Template</a></li>
</ul>
</div>
</div>
</div>
<form action="{{ route('admin.packs.new') }}" method="POST" enctype="multipart/form-data">
<div class="row">
<div class="col-md-6">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Pack Details</h3>
</div>
<div class="box-body">
<div class="form-group">
<label for="pName" class="form-label">Name</label>
<input name="name" type="text" id="pName" class="form-control" value="{{ old('name') }}" />
<p class="text-muted small">A short but descriptive name of what this pack is. For example, <code>Counter Strike: Source</code> if it is a Counter Strike package.</p>
</div>
<div class="form-group">
<label for="pDescription" class="form-label">Description</label>
<textarea name="description" id="pDescription" class="form-control" rows="8">{{ old('description') }}</textarea>
</div>
<div class="form-group">
<label for="pVersion" class="form-label">Version</label>
<input type="text" name="version" id="pVersion" class="form-control" value="{{ old('version') }}" />
<p class="text-muted small">The version of this package, or the version of the files contained within the package.</p>
</div>
<div class="form-group">
<label for="pEggId" class="form-label">Associated Egg</label>
<select id="pEggId" name="egg_id" class="form-control">
@foreach($nests as $nest)
<optgroup label="{{ $nest->name }}">
@foreach($nest->eggs as $egg)
<option value="{{ $egg->id }}">{{ $egg->name }}</option>
@endforeach
</optgroup>
@endforeach
</select>
<p class="text-muted small">The option that this pack is associated with. Only servers that are assigned this option will be able to access this pack.</p>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Pack Configuration</h3>
</div>
<div class="box-body">
<div class="form-group">
<div class="checkbox checkbox-primary no-margin-bottom">
<input id="pSelectable" name="selectable" type="checkbox" value="1" checked/>
<label for="pSelectable">
Selectable
</label>
</div>
<p class="text-muted small">Check this box if user should be able to select this pack to install on their servers.</p>
</div>
<div class="form-group">
<div class="checkbox checkbox-primary no-margin-bottom">
<input id="pVisible" name="visible" type="checkbox" value="1" checked/>
<label for="pVisible">
Visible
</label>
</div>
<p class="text-muted small">Check this box if this pack is visible in the dropdown menu. If this pack is assigned to a server it will be visible regardless of this setting.</p>
</div>
<div class="form-group">
<div class="checkbox checkbox-warning no-margin-bottom">
<input id="pLocked" name="locked" type="checkbox" value="1"/>
<label for="pLocked">
Locked
</label>
</div>
<p class="text-muted small">Check this box if servers assigned this pack should not be able to switch to a different pack.</p>
</div>
<hr />
<div class="form-group no-margin-bottom">
<label for="pFileUpload" class="form-label">Pack Archive</label>
<input type="file" accept=".tar.gz, application/gzip" name="file_upload" class="well well-sm" style="width:100%"/>
<p class="text-muted small">This package file must be a <code>.tar.gz</code> archive of pack files to be decompressed into the server folder.</p>
<p class="text-muted small">If your file is larger than <code>50MB</code> it is recommended to upload it using SFTP. Once you have added this pack to the system, a path will be provided where you should upload the file.</p>
<div class="callout callout-info callout-slim no-margin-bottom">
<p class="text-muted small"><strong>This server is currently configured with the following limits:</strong><br /><code>upload_max_filesize={{ ini_get('upload_max_filesize') }}</code><br /><code>post_max_size={{ ini_get('post_max_size') }}</code><br /><br />If your file is larger than either of those values this request will fail.</p>
</div>
</div>
</div>
<div class="box-footer with-border">
{!! csrf_field() !!}
<button class="btn btn-sm btn-success pull-right" type="submit">Create Pack</button>
</div>
</div>
</div>
</div>
</form>
@endsection
@section('footer-scripts')
@parent
<script>
$('#pEggId').select2();
$('#toggleModal').on('click', function (event) {
event.preventDefault();
$.ajax({
method: 'GET',
url: Router.route('admin.packs.new.template'),
headers: { 'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content') },
}).fail(function (jqXhr) {
console.error(jqXhr);
alert('There was an error trying to create the upload modal.');
}).done(function (data) {
$(data).modal();
$('#pEggIdModal').select2();
});
});
</script>
@endsection

View file

@ -0,0 +1,154 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
Packs &rarr; View &rarr; {{ $pack->name }}
@endsection
@section('content-header')
<h1>{{ $pack->name }}<small>{{ str_limit($pack->description, 60) }}</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li><a href="{{ route('admin.packs') }}">Packs</a></li>
<li class="active">{{ $pack->name }}</li>
</ol>
@endsection
@section('content')
<form action="{{ route('admin.packs.view', $pack->id) }}" method="POST">
<div class="row">
<div class="col-md-6">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Pack Details</h3>
</div>
<div class="box-body">
<div class="form-group">
<label for="pName" class="form-label">Name</label>
<input name="name" type="text" id="pName" class="form-control" value="{{ $pack->name }}" />
<p class="text-muted small">A short but descriptive name of what this pack is. For example, <code>Counter Strike: Source</code> if it is a Counter Strike package.</p>
</div>
<div class="form-group">
<label for="pDescription" class="form-label">Description</label>
<textarea name="description" id="pDescription" class="form-control" rows="8">{{ $pack->description }}</textarea>
</div>
<div class="form-group">
<label for="pVersion" class="form-label">Version</label>
<input type="text" name="version" id="pVersion" class="form-control" value="{{ $pack->version }}" />
<p class="text-muted small">The version of this package, or the version of the files contained within the package.</p>
</div>
<div class="form-group">
<label class="form-label">Storage Location</label>
<input type="text" class="form-control" readonly value="{{ storage_path('app/packs/' . $pack->uuid) }}">
<p class="text-muted small">If you would like to modify the stored pack you will need to upload a new <code>archive.tar.gz</code> to the location defined above.</p>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Pack Configuration</h3>
</div>
<div class="box-body">
<div class="form-group">
<label for="pEggId" class="form-label">Associated Option</label>
<select id="pEggId" name="egg_id" class="form-control">
@foreach($nests as $nest)
<optgroup label="{{ $nest->name }}">
@foreach($nest->eggs as $egg)
<option value="{{ $egg->id }}" {{ $pack->egg_id !== $egg->id ?: 'selected' }}>{{ $egg->name }}</option>
@endforeach
</optgroup>
@endforeach
</select>
<p class="text-muted small">The option that this pack is associated with. Only servers that are assigned this option will be able to access this pack. This assigned option <em>cannot</em> be changed if servers are attached to this pack.</p>
</div>
<div class="form-group">
<div class="checkbox checkbox-primary no-margin-bottom">
<input id="pSelectable" name="selectable" type="checkbox" value="1" {{ ! $pack->selectable ?: 'checked' }}/>
<label for="pSelectable">
Selectable
</label>
</div>
<p class="text-muted small">Check this box if user should be able to select this pack to install on their servers.</p>
</div>
<div class="form-group">
<div class="checkbox checkbox-primary no-margin-bottom">
<input id="pVisible" name="visible" type="checkbox" value="1" {{ ! $pack->visible ?: 'checked' }}/>
<label for="pVisible">
Visible
</label>
</div>
<p class="text-muted small">Check this box if this pack is visible in the dropdown menu. If this pack is assigned to a server it will be visible regardless of this setting.</p>
</div>
<div class="form-group">
<div class="checkbox checkbox-warning no-margin-bottom">
<input id="pLocked" name="locked" type="checkbox" value="1" {{ ! $pack->locked ?: 'checked' }}/>
<label for="pLocked">
Locked
</label>
</div>
<p class="text-muted small">Check this box if servers assigned this pack should not be able to switch to a different pack.</p>
</div>
</div>
<div class="box-footer with-border">
{!! csrf_field() !!}
<button name="_method" value="PATCH" class="btn btn-sm btn-primary pull-right" type="submit">Save</button>
<button name="_method" value="DELETE" class="btn btn-sm btn-danger pull-left muted muted-hover" type="submit"><i class="fa fa-trash-o"></i></button>
</div>
</div>
</div>
</div>
</form>
<div class="row">
<div class="col-xs-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Servers Using This Pack</h3>
</div>
<div class="box-body no-padding table-responsive">
<table class="table table-hover">
<tr>
<th>ID</th>
<th>Server Name</th>
<th>Node</th>
<th>Owner</th>
</tr>
@foreach($pack->servers as $server)
<tr>
<td><code>{{ $server->uuidShort }}</code></td>
<td><a href="{{ route('admin.servers.view', $server->id) }}">{{ $server->name }}</a></td>
<td><a href="{{ route('admin.nodes.view', $server->node->id) }}">{{ $server->node->name }}</a></td>
<td><a href="{{ route('admin.users.view', $server->user->id) }}">{{ $server->user->email }}</a></td>
</tr>
@endforeach
</table>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-6 col-md-5 col-md-offset-7 col-xs-offset-6">
<form action="{{ route('admin.packs.view.export', $pack->id) }}" method="POST">
{!! csrf_field() !!}
<button type="submit" class="btn btn-sm btn-success pull-right">Export</button>
</form>
<form action="{{ route('admin.packs.view.export', ['id' => $pack->id, 'files' => 'with-files']) }}" method="POST">
{!! csrf_field() !!}
<button type="submit" class="btn btn-sm pull-right muted muted-hover" style="margin-right:10px;">Export with Archive</button>
</form>
</div>
</div>
@endsection
@section('footer-scripts')
@parent
<script>
$('#pEggId').select2();
</script>
@endsection

View file

@ -0,0 +1,95 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
List Servers
@endsection
@section('content-header')
<h1>Servers<small>All servers available on the system.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li class="active">Servers</li>
</ol>
@endsection
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Server List</h3>
<div class="box-tools">
<form action="{{ route('admin.servers') }}" method="GET">
<div class="input-group input-group-sm">
<input type="text" name="query" class="form-control pull-right" style="width:30%;" value="{{ request()->input('query') }}" placeholder="Search Servers">
<div class="input-group-btn">
<button type="submit" class="btn btn-default"><i class="fa fa-search"></i></button>
<a href="{{ route('admin.servers.new') }}"><button type="button" class="btn btn-sm btn-primary" style="border-radius: 0 3px 3px 0;margin-left:-1px;">Create New</button></a>
</div>
</div>
</form>
</div>
</div>
<div class="box-body table-responsive no-padding">
<table class="table table-hover">
<tbody>
<tr>
<th>Server Name</th>
<th>UUID</th>
<th>Owner</th>
<th>Node</th>
<th>Connection</th>
<th></th>
<th></th>
</tr>
@foreach ($servers as $server)
<tr data-server="{{ $server->uuidShort }}">
<td><a href="{{ route('admin.servers.view', $server->id) }}">{{ $server->name }}</a></td>
<td><code title="{{ $server->uuid }}">{{ $server->uuid }}</code></td>
<td><a href="{{ route('admin.users.view', $server->user->id) }}">{{ $server->user->username }}</a></td>
<td><a href="{{ route('admin.nodes.view', $server->node->id) }}">{{ $server->node->name }}</a></td>
<td>
<code>{{ $server->allocation->alias }}:{{ $server->allocation->port }}</code>
</td>
<td class="text-center">
@if($server->suspended)
<span class="label bg-maroon">Suspended</span>
@elseif(! $server->installed)
<span class="label label-warning">Installing</span>
@else
<span class="label label-success">Active</span>
@endif
</td>
<td class="text-center">
<a class="btn btn-xs btn-default" href="{{ route('server.index', $server->uuidShort) }}"><i class="fa fa-wrench"></i></a>
<a class="btn btn-xs btn-default console-popout" href="{{ route('server.console', $server->uuidShort) }}"><i class="fa fa-terminal"></i></a>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@if($servers->hasPages())
<div class="box-footer with-border">
<div class="col-md-12 text-center">{!! $servers->appends(['query' => Request::input('query')])->render() !!}</div>
</div>
@endif
</div>
</div>
</div>
@endsection
@section('footer-scripts')
@parent
<script>
$('.console-popout').on('click', function (event) {
event.preventDefault();
window.open($(this).attr('href'), 'Pterodactyl Console', 'width=800,height=400');
});
</script>
@endsection

View file

@ -0,0 +1,263 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
New Server
@endsection
@section('content-header')
<h1>Create Server<small>Add a new server to the panel.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li><a href="{{ route('admin.servers') }}">Servers</a></li>
<li class="active">Create Server</li>
</ol>
@endsection
@section('content')
<form action="{{ route('admin.servers.new') }}" method="POST">
<div class="row">
<div class="col-xs-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Core Details</h3>
</div>
<div class="box-body row">
<div class="col-md-6">
<div class="form-group">
<label for="pName">Server Name</label>
<input type="text" class="form-control" id="pName" name="name" value="{{ old('name') }}" placeholder="Server Name">
<p class="small text-muted no-margin">Character limits: <code>a-z A-Z 0-9 _ - .</code> and <code>[Space]</code> (max 200 characters).</p>
</div>
<div class="form-group">
<label for="pUserId">Server Owner</label>
<select class="form-control" style="padding-left:0;" name="owner_id" id="pUserId"></select>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="description" class="control-label">Server Description</label>
<textarea name="description" rows="3" class="form-control">{{ old('description') }}</textarea>
<p class="text-muted small">A brief description of this server.</p>
</div>
<div class="form-group">
<div class="checkbox checkbox-primary no-margin-bottom">
<input id="pStartOnCreation" name="start_on_completion" type="checkbox" value="1" checked />
<label for="pStartOnCreation" class="strong">Start Server when Installed</label>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="box">
<div class="overlay" id="allocationLoader" style="display:none;"><i class="fa fa-refresh fa-spin"></i></div>
<div class="box-header with-border">
<h3 class="box-title">Allocation Management</h3>
</div>
<div class="box-body row">
<div class="form-group col-sm-4">
<label for="pNodeId">Node</label>
<select name="node_id" id="pNodeId" class="form-control">
@foreach($locations as $location)
<optgroup label="{{ $location->long }} ({{ $location->short }})">
@foreach($location->nodes as $node)
<option value="{{ $node->id }}"
@if($location->id === old('location_id')) selected @endif
>{{ $node->name }}</option>
@endforeach
</optgroup>
@endforeach
</select>
<p class="small text-muted no-margin">The node which this server will be deployed to.</p>
</div>
<div class="form-group col-sm-4">
<label for="pAllocation">Default Allocation</label>
<select name="allocation_id" id="pAllocation" class="form-control"></select>
<p class="small text-muted no-margin">The main allocation that will be assigned to this server.</p>
</div>
<div class="form-group col-sm-4">
<label for="pAllocationAdditional">Additional Allocation(s)</label>
<select name="allocation_additional[]" id="pAllocationAdditional" class="form-control" multiple></select>
<p class="small text-muted no-margin">Additional allocations to assign to this server on creation.</p>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="box">
<div class="overlay" id="allocationLoader" style="display:none;"><i class="fa fa-refresh fa-spin"></i></div>
<div class="box-header with-border">
<h3 class="box-title">Application Feature Limits</h3>
</div>
<div class="box-body row">
<div class="form-group col-xs-6">
<label for="cpu" class="control-label">Database Limit</label>
<div>
<input type="text" name="database_limit" class="form-control" value="{{ old('database_limit', 0) }}"/>
</div>
<p class="text-muted small">The total number of databases a user is allowed to create for this server. Leave blank to allow unlimited.</p>
</div>
<div class="form-group col-xs-6">
<label for="cpu" class="control-label">Allocation Limit</label>
<div>
<input type="text" name="allocation_limit" class="form-control" value="{{ old('allocation_limit', 0) }}"/>
</div>
<p class="text-muted small">The total number of allocations a user is allowed to create for this server. Leave blank to allow unlimited.</p>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Resource Management</h3>
</div>
<div class="box-body row">
<div class="form-group col-sm-4">
<label for="pMemory">Memory</label>
<div class="input-group">
<input type="text" value="{{ old('memory') }}" class="form-control" name="memory" id="pMemory" />
<span class="input-group-addon">MB</span>
</div>
</div>
<div class="form-group col-sm-4">
<label for="pSwap">Swap</label>
<div class="input-group">
<input type="text" value="{{ old('swap', 0) }}" class="form-control" name="swap" id="pSwap" />
<span class="input-group-addon">MB</span>
</div>
</div>
</div>
<div class="box-footer no-border no-pad-top no-pad-bottom">
<p class="text-muted small">If you do not want to assign swap space to a server, simply put <code>0</code> for the value, or <code>-1</code> to allow unlimited swap space. If you want to disable memory limiting on a server, simply enter <code>0</code> into the memory field.<p>
</div>
<div class="box-body row">
<div class="form-group col-sm-4">
<label for="pDisk">Disk Space</label>
<div class="input-group">
<input type="text" class="form-control" value="{{ old('disk') }}" name="disk" id="pDisk" />
<span class="input-group-addon">MB</span>
</div>
</div>
<div class="form-group col-sm-4">
<label for="pCPU">CPU Limit</label>
<div class="input-group">
<input type="text" class="form-control" value="{{ old('cpu', 0) }}" name="cpu" id="pCPU" />
<span class="input-group-addon">%</span>
</div>
</div>
<div class="form-group col-sm-4">
<label for="pIO">Block IO Weight</label>
<div class="input-group">
<input type="text" class="form-control" value="{{ old('io', 500) }}" name="io" id="pIO" />
<span class="input-group-addon">I/O</span>
</div>
</div>
</div>
<div class="box-footer no-border no-pad-top no-pad-bottom">
<p class="text-muted small">If you do not want to limit CPU usage, set the value to <code>0</code>. To determine a value, take the number of <em>physical</em> cores and multiply it by 100. For example, on a quad core system <code>(4 * 100 = 400)</code> there is <code>400%</code> available. To limit a server to using half of a single core, you would set the value to <code>50</code>. To allow a server to use up to two physical cores, set the value to <code>200</code>. BlockIO should be a value between <code>10</code> and <code>1000</code>. Please see <a href="https://docs.docker.com/engine/reference/run/#/block-io-bandwidth-blkio-constraint" target="_blank">this documentation</a> for more information about it.<p>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Nest Configuration</h3>
</div>
<div class="box-body row">
<div class="form-group col-xs-12">
<label for="pNestId">Nest</label>
<select name="nest_id" id="pNestId" class="form-control">
@foreach($nests as $nest)
<option value="{{ $nest->id }}"
@if($nest->id === old('nest_id'))
selected="selected"
@endif
>{{ $nest->name }}</option>
@endforeach
</select>
<p class="small text-muted no-margin">Select the Nest that this server will be grouped under.</p>
</div>
<div class="form-group col-xs-12">
<label for="pEggId">Egg</label>
<select name="egg_id" id="pEggId" class="form-control"></select>
<p class="small text-muted no-margin">Select the Egg that will define how this server should operate.</p>
</div>
<div class="form-group col-xs-12">
<label for="pPackId">Data Pack</label>
<select name="pack_id" id="pPackId" class="form-control"></select>
<p class="small text-muted no-margin">Select a data pack to be automatically installed on this server when first created.</p>
</div>
<div class="form-group col-xs-12">
<div class="checkbox checkbox-primary no-margin-bottom">
<input id="pSkipScripting" name="skip_scripts" type="checkbox" value="1" />
<label for="pSkipScripting" class="strong">Skip Egg Install Script</label>
</div>
<p class="small text-muted no-margin">If the selected Egg has an install script attached to it, the script will run during install after the pack is installed. If you would like to skip this step, check this box.</p>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Docker Configuration</h3>
</div>
<div class="box-body row">
<div class="form-group col-xs-12">
<label for="pDefaultContainer">Docker Image</label>
<input id="pDefaultContainer" name="image" value="{{ old('image') }}" class="form-control" />
<p class="small text-muted no-margin">This is the default Docker image that will be used to run this server.</p>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Startup Configuration</h3>
</div>
<div class="box-body row">
<div class="form-group col-xs-12">
<label for="pStartup">Startup Command</label>
<input type="text" id="pStartup" value="{{ old('startup') }}" class="form-control" name="startup" />
<p class="small text-muted no-margin">The following data substitutes are available for the startup command: <code>@{{SERVER_MEMORY}}</code>, <code>@{{SERVER_IP}}</code>, and <code>@{{SERVER_PORT}}</code>. They will be replaced with the allocated memory, server IP, and server port respectively.</p>
</div>
</div>
<div class="box-header with-border" style="margin-top:-10px;">
<h3 class="box-title">Service Variables</h3>
</div>
<div class="box-body row" id="appendVariablesTo"></div>
<div class="box-footer">
{!! csrf_field() !!}
<input type="submit" class="btn btn-success pull-right" value="Create Server" />
</div>
</div>
</div>
</div>
</form>
@endsection
@section('footer-scripts')
@parent
{!! Theme::js('vendor/lodash/lodash.js') !!}
{!! Theme::js('js/admin/new-server.js') !!}
@endsection

View file

@ -0,0 +1,195 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
Server {{ $server->name }}: Build Details
@endsection
@section('content-header')
<h1>{{ $server->name }}<small>Control allocations and system resources for this server.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li><a href="{{ route('admin.servers') }}">Servers</a></li>
<li><a href="{{ route('admin.servers.view', $server->id) }}">{{ $server->name }}</a></li>
<li class="active">Build Configuration</li>
</ol>
@endsection
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="nav-tabs-custom nav-tabs-floating">
<ul class="nav nav-tabs">
<li><a href="{{ route('admin.servers.view', $server->id) }}">About</a></li>
@if($server->installed === 1)
<li><a href="{{ route('admin.servers.view.details', $server->id) }}">Details</a></li>
<li class="active"><a href="{{ route('admin.servers.view.build', $server->id) }}">Build Configuration</a></li>
<li><a href="{{ route('admin.servers.view.startup', $server->id) }}">Startup</a></li>
<li><a href="{{ route('admin.servers.view.database', $server->id) }}">Database</a></li>
<li><a href="{{ route('admin.servers.view.manage', $server->id) }}">Manage</a></li>
@endif
<li class="tab-danger"><a href="{{ route('admin.servers.view.delete', $server->id) }}">Delete</a></li>
<li class="tab-success"><a href="{{ route('server.index', $server->uuidShort) }}"><i class="fa fa-external-link"></i></a></li>
</ul>
</div>
</div>
</div>
<div class="row">
<form action="{{ route('admin.servers.view.build', $server->id) }}" method="POST">
<div class="col-sm-5">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">System Resources</h3>
</div>
<div class="box-body">
<div class="form-group">
<label for="memory" class="control-label">Allocated Memory</label>
<div class="input-group">
<input type="text" name="memory" data-multiplicator="true" class="form-control" value="{{ old('memory', $server->memory) }}"/>
<span class="input-group-addon">MB</span>
</div>
<p class="text-muted small">The maximum amount of memory allowed for this container. Setting this to <code>0</code> will allow unlimited memory in a container.</p>
</div>
<div class="form-group">
<label for="swap" class="control-label">Allocated Swap</label>
<div class="input-group">
<input type="text" name="swap" data-multiplicator="true" class="form-control" value="{{ old('swap', $server->swap) }}"/>
<span class="input-group-addon">MB</span>
</div>
<p class="text-muted small">Setting this to <code>0</code> will disable swap space on this server. Setting to <code>-1</code> will allow unlimited swap.</p>
</div>
<div class="form-group">
<label for="cpu" class="control-label">CPU Limit</label>
<div class="input-group">
<input type="text" name="cpu" class="form-control" value="{{ old('cpu', $server->cpu) }}"/>
<span class="input-group-addon">%</span>
</div>
<p class="text-muted small">Each <em>physical</em> core on the system is considered to be <code>100%</code>. Setting this value to <code>0</code> will allow a server to use CPU time without restrictions.</p>
</div>
<div class="form-group">
<label for="io" class="control-label">Block IO Proportion</label>
<div>
<input type="text" name="io" class="form-control" value="{{ old('io', $server->io) }}"/>
</div>
<p class="text-muted small">Changing this value can have negative effects on all containers on the system. We strongly recommend leaving this value as <code>500</code>.</p>
</div>
<div class="form-group">
<label for="cpu" class="control-label">Disk Space Limit</label>
<div class="input-group">
<input type="text" name="disk" class="form-control" value="{{ old('disk', $server->disk) }}"/>
<span class="input-group-addon">MB</span>
</div>
<p class="text-muted small">This server will not be allowed to boot if it is using more than this amount of space. If a server goes over this limit while running it will be safely stopped and locked until enough space is available.</p>
</div>
<div class="form-group">
<label for="cpu" class="control-label">OOM Killer</label>
<div>
<div class="radio radio-danger radio-inline">
<input type="radio" id="pOomKillerEnabled" value="0" name="oom_disabled" @if(!$server->oom_disabled)checked @endif>
<label for="pOomKillerEnabled">Enabled</label>
</div>
<div class="radio radio-success radio-inline">
<input type="radio" id="pOomKillerDisabled" value="1" name="oom_disabled" @if($server->oom_disabled)checked @endif>
<label for="pOomKillerDisabled">Disabled</label>
</div>
<p class="text-muted small">
Enabling OOM killer may cause server processes to exit unexpectedly.
</p>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-7">
<div class="row">
<div class="col-xs-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Application Feature Limits</h3>
</div>
<div class="box-body">
<div class="row">
<div class="form-group col-xs-6">
<label for="cpu" class="control-label">Database Limit</label>
<div>
<input type="text" name="database_limit" class="form-control" value="{{ old('database_limit', $server->database_limit) }}"/>
</div>
<p class="text-muted small">The total number of databases a user is allowed to create for this server. Leave blank to allow unlimited.</p>
</div>
<div class="form-group col-xs-6">
<label for="cpu" class="control-label">Allocation Limit</label>
<div>
<input type="text" name="allocation_limit" class="form-control" value="{{ old('allocation_limit', $server->allocation_limit) }}"/>
</div>
<p class="text-muted small"><strong>This feature is not currently implemented.</strong> The total number of allocations a user is allowed to create for this server. Leave blank to allow unlimited.</p>
</div>
</div>
</div>
</div>
</div>
<div class="col-xs-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Allocation Management</h3>
</div>
<div class="box-body">
<div class="form-group">
<label for="pAllocation" class="control-label">Game Port</label>
<select id="pAllocation" name="allocation_id" class="form-control">
@foreach ($assigned as $assignment)
<option value="{{ $assignment->id }}"
@if($assignment->id === $server->allocation_id)
selected="selected"
@endif
>{{ $assignment->alias }}:{{ $assignment->port }}</option>
@endforeach
</select>
<p class="text-muted small">The default connection address that will be used for this game server.</p>
</div>
<div class="form-group">
<label for="pAddAllocations" class="control-label">Assign Additional Ports</label>
<div>
<select name="add_allocations[]" class="form-control" multiple id="pAddAllocations">
@foreach ($unassigned as $assignment)
<option value="{{ $assignment->id }}">{{ $assignment->alias }}:{{ $assignment->port }}</option>
@endforeach
</select>
</div>
<p class="text-muted small">Please note that due to software limitations you cannot assign identical ports on different IPs to the same server.</p>
</div>
<div class="form-group">
<label for="pRemoveAllocations" class="control-label">Remove Additional Ports</label>
<div>
<select name="remove_allocations[]" class="form-control" multiple id="pRemoveAllocations">
@foreach ($assigned as $assignment)
<option value="{{ $assignment->id }}">{{ $assignment->alias }}:{{ $assignment->port }}</option>
@endforeach
</select>
</div>
<p class="text-muted small">Simply select which ports you would like to remove from the list above. If you want to assign a port on a different IP that is already in use you can select it from the left and delete it here.</p>
</div>
</div>
<div class="box-footer">
{!! csrf_field() !!}
<button type="submit" class="btn btn-primary pull-right">Update Build Configuration</button>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
@endsection
@section('footer-scripts')
@parent
<script>
$('#pAddAllocations').select2();
$('#pRemoveAllocations').select2();
$('#pAllocation').select2();
</script>
@endsection

View file

@ -0,0 +1,180 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
Server {{ $server->name }}: Databases
@endsection
@section('content-header')
<h1>{{ $server->name }}<small>Manage server databases.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li><a href="{{ route('admin.servers') }}">Servers</a></li>
<li><a href="{{ route('admin.servers.view', $server->id) }}">{{ $server->name }}</a></li>
<li class="active">Databases</li>
</ol>
@endsection
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="nav-tabs-custom nav-tabs-floating">
<ul class="nav nav-tabs">
<li><a href="{{ route('admin.servers.view', $server->id) }}">About</a></li>
@if($server->installed === 1)
<li><a href="{{ route('admin.servers.view.details', $server->id) }}">Details</a></li>
<li><a href="{{ route('admin.servers.view.build', $server->id) }}">Build Configuration</a></li>
<li><a href="{{ route('admin.servers.view.startup', $server->id) }}">Startup</a></li>
<li class="active"><a href="{{ route('admin.servers.view.database', $server->id) }}">Database</a></li>
<li><a href="{{ route('admin.servers.view.manage', $server->id) }}">Manage</a></li>
@endif
<li class="tab-danger"><a href="{{ route('admin.servers.view.delete', $server->id) }}">Delete</a></li>
<li class="tab-success"><a href="{{ route('server.index', $server->uuidShort) }}"><i class="fa fa-external-link"></i></a></li>
</ul>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-7">
<div class="alert alert-info">
Database passwords can be viewed when <a href="{{ route('server.databases.index', ['server' => $server->uuidShort]) }}">visiting this server</a> on the front-end.
</div>
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Active Databases</h3>
</div>
<div class="box-body table-responsible no-padding">
<table class="table table-hover">
<tr>
<th>Database</th>
<th>Username</th>
<th>Connections From</th>
<th>Host</th>
<th></th>
</tr>
@foreach($server->databases as $database)
<tr>
<td>{{ $database->database }}</td>
<td>{{ $database->username }}</td>
<td>{{ $database->remote }}</td>
<td><code>{{ $database->host->host }}:{{ $database->host->port }}</code></td>
<td class="text-center">
<button data-action="reset-password" data-id="{{ $database->id }}" class="btn btn-xs btn-primary"><i class="fa fa-refresh"></i></button>
<button data-action="remove" data-id="{{ $database->id }}" class="btn btn-xs btn-danger"><i class="fa fa-trash"></i></button>
</td>
</tr>
@endforeach
</table>
</div>
</div>
</div>
<div class="col-sm-5">
<div class="box box-success">
<div class="box-header with-border">
<h3 class="box-title">Create New Database</h3>
</div>
<form action="{{ route('admin.servers.view.database', $server->id) }}" method="POST">
<div class="box-body">
<div class="form-group">
<label for="pDatabaseHostId" class="control-label">Database Host</label>
<select id="pDatabaseHostId" name="database_host_id" class="form-control">
@foreach($hosts as $host)
<option value="{{ $host->id }}">{{ $host->name }}</option>
@endforeach
</select>
<p class="text-muted small">Select the host database server that this database should be created on.</p>
</div>
<div class="form-group">
<label for="pDatabaseName" class="control-label">Database</label>
<div class="input-group">
<span class="input-group-addon">s{{ $server->id }}_</span>
<input id="pDatabaseName" type="text" name="database" class="form-control" placeholder="database" />
</div>
</div>
<div class="form-group">
<label for="pRemote" class="control-label">Connections</label>
<input id="pRemote" type="text" name="remote" class="form-control" value="%" />
<p class="text-muted small">This should reflect the IP address that connections are allowed from. Uses standard MySQL notation. If unsure leave as <code>%</code>.</p>
</div>
</div>
<div class="box-footer">
{!! csrf_field() !!}
<p class="text-muted small no-margin">A username and password for this database will be randomly generated after form submission.</p>
<input type="submit" class="btn btn-sm btn-success pull-right" value="Create Database" />
</div>
</form>
</div>
</div>
</div>
@endsection
@section('footer-scripts')
@parent
<script>
$('#pDatabaseHost').select2();
$('[data-action="remove"]').click(function (event) {
event.preventDefault();
var self = $(this);
swal({
title: '',
type: 'warning',
text: 'Are you sure that you want to delete this database? There is no going back, all data will immediately be removed.',
showCancelButton: true,
confirmButtonText: 'Delete',
confirmButtonColor: '#d9534f',
closeOnConfirm: false,
showLoaderOnConfirm: true,
}, function () {
$.ajax({
method: 'DELETE',
url: Router.route('admin.servers.view.database.delete', { server: '{{ $server->id }}', database: self.data('id') }),
headers: { 'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content') },
}).done(function () {
self.parent().parent().slideUp();
swal.close();
}).fail(function (jqXHR) {
console.error(jqXHR);
swal({
type: 'error',
title: 'Whoops!',
text: (typeof jqXHR.responseJSON.error !== 'undefined') ? jqXHR.responseJSON.error : 'An error occurred while processing this request.'
});
});
});
});
$('[data-action="reset-password"]').click(function (e) {
e.preventDefault();
var block = $(this);
$(this).addClass('disabled').find('i').addClass('fa-spin');
$.ajax({
type: 'PATCH',
url: Router.route('admin.servers.view.database', { server: '{{ $server->id }}' }),
headers: { 'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content') },
data: { database: $(this).data('id') },
}).done(function (data) {
swal({
type: 'success',
title: '',
text: 'The password for this database has been reset.',
});
}).fail(function(jqXHR, textStatus, errorThrown) {
console.error(jqXHR);
var error = 'An error occurred while trying to process this request.';
if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {
error = jqXHR.responseJSON.error;
}
swal({
type: 'error',
title: 'Whoops!',
text: error
});
}).always(function () {
block.removeClass('disabled').find('i').removeClass('fa-spin');
});
});
</script>
@endsection

View file

@ -0,0 +1,98 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
Server {{ $server->name }}: Delete
@endsection
@section('content-header')
<h1>{{ $server->name }}<small>Delete this server from the panel.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li><a href="{{ route('admin.servers') }}">Servers</a></li>
<li><a href="{{ route('admin.servers.view', $server->id) }}">{{ $server->name }}</a></li>
<li class="active">Delete</li>
</ol>
@endsection
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="nav-tabs-custom nav-tabs-floating">
<ul class="nav nav-tabs">
<li><a href="{{ route('admin.servers.view', $server->id) }}">About</a></li>
@if($server->installed === 1)
<li><a href="{{ route('admin.servers.view.details', $server->id) }}">Details</a></li>
<li><a href="{{ route('admin.servers.view.build', $server->id) }}">Build Configuration</a></li>
<li><a href="{{ route('admin.servers.view.startup', $server->id) }}">Startup</a></li>
<li><a href="{{ route('admin.servers.view.database', $server->id) }}">Database</a></li>
<li><a href="{{ route('admin.servers.view.manage', $server->id) }}">Manage</a></li>
@endif
<li class="tab-danger active"><a href="{{ route('admin.servers.view.delete', $server->id) }}">Delete</a></li>
<li class="tab-success"><a href="{{ route('server.index', $server->uuidShort) }}"><i class="fa fa-external-link"></i></a></li>
</ul>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Safely Delete Server</h3>
</div>
<div class="box-body">
<p>This action will attempt to delete the server from both the panel and daemon. If either one reports an error the action will be cancelled.</p>
<p class="text-danger small">Deleting a server is an irreversible action. <strong>All server data</strong> (including files and users) will be removed from the system.</p>
</div>
<div class="box-footer">
<form action="{{ route('admin.servers.view.delete', $server->id) }}" method="POST">
{!! csrf_field() !!}
<button type="submit" class="btn btn-danger">Safely Delete This Server</button>
</form>
</div>
</div>
</div>
<div class="col-md-6">
<div class="box box-danger">
<div class="box-header with-border">
<h3 class="box-title">Force Delete Server</h3>
</div>
<div class="box-body">
<p>This action will attempt to delete the server from both the panel and daemon. If the daemon does not respond, or reports an error the deletion will continue.</p>
<p class="text-danger small">Deleting a server is an irreversible action. <strong>All server data</strong> (including files and users) will be removed from the system. This method may leave dangling files on your daemon if it reports an error.</p>
</div>
<div class="box-footer">
<form action="{{ route('admin.servers.view.delete', $server->id) }}" method="POST">
{!! csrf_field() !!}
<input type="hidden" name="force_delete" value="1" />
<button type="submit" class="btn btn-danger">Forcibly Delete This Server</button>
</form>
</div>
</div>
</div>
</div>
@endsection
@section('footer-scripts')
@parent
<script>
$('form[data-action="delete"]').submit(function (event) {
event.preventDefault();
swal({
title: '',
type: 'warning',
text: 'Are you sure that you want to delete this server? There is no going back, all data will immediately be removed.',
showCancelButton: true,
confirmButtonText: 'Delete',
confirmButtonColor: '#d9534f',
closeOnConfirm: false
}, function () {
event.target.submit();
});
});
</script>
@endsection

View file

@ -0,0 +1,137 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
Server {{ $server->name }}: Details
@endsection
@section('content-header')
<h1>{{ $server->name }}<small>Edit details for this server including owner and container.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li><a href="{{ route('admin.servers') }}">Servers</a></li>
<li><a href="{{ route('admin.servers.view', $server->id) }}">{{ $server->name }}</a></li>
<li class="active">Details</li>
</ol>
@endsection
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="nav-tabs-custom nav-tabs-floating">
<ul class="nav nav-tabs">
<li><a href="{{ route('admin.servers.view', $server->id) }}">About</a></li>
@if($server->installed === 1)
<li class="active"><a href="{{ route('admin.servers.view.details', $server->id) }}">Details</a></li>
<li><a href="{{ route('admin.servers.view.build', $server->id) }}">Build Configuration</a></li>
<li><a href="{{ route('admin.servers.view.startup', $server->id) }}">Startup</a></li>
<li><a href="{{ route('admin.servers.view.database', $server->id) }}">Database</a></li>
<li><a href="{{ route('admin.servers.view.manage', $server->id) }}">Manage</a></li>
@endif
<li class="tab-danger"><a href="{{ route('admin.servers.view.delete', $server->id) }}">Delete</a></li>
<li class="tab-success"><a href="{{ route('server.index', $server->uuidShort) }}"><i class="fa fa-external-link"></i></a></li>
</ul>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Base Information</h3>
</div>
<form action="{{ route('admin.servers.view.details', $server->id) }}" method="POST">
<div class="box-body">
<div class="form-group">
<label for="name" class="control-label">Server Name <span class="field-required"></span></label>
<input type="text" name="name" value="{{ old('name', $server->name) }}" class="form-control" />
<p class="text-muted small">Character limits: <code>a-zA-Z0-9_-</code> and <code>[Space]</code> (max 35 characters).</p>
</div>
<div class="form-group">
<label for="external_id" class="control-label">External Identifier</label>
<input type="text" name="external_id" value="{{ old('external_id', $server->external_id) }}" class="form-control" />
<p class="text-muted small">Leave empty to not assign an external identifier for this server. The external ID should be unique to this server and not be in use by any other servers.</p>
</div>
<div class="form-group">
<label for="pUserId" class="control-label">Server Owner <span class="field-required"></span></label>
<select name="owner_id" class="form-control" id="pUserId">
<option value="{{ $server->owner_id }}" selected>{{ $server->user->email }}</option>
</select>
<p class="text-muted small">You can change the owner of this server by changing this field to an email matching another use on this system. If you do this a new daemon security token will be generated automatically.</p>
</div>
<div class="form-group">
<label for="description" class="control-label">Server Description</label>
<textarea name="description" rows="3" class="form-control">{{ old('description', $server->description) }}</textarea>
<p class="text-muted small">A brief description of this server.</p>
</div>
</div>
<div class="box-footer">
{!! csrf_field() !!}
{!! method_field('PATCH') !!}
<input type="submit" class="btn btn-sm btn-primary" value="Update Details" />
</div>
</form>
</div>
</div>
</div>
@endsection
@section('footer-scripts')
@parent
<script>
$('#pUserId').select2({
ajax: {
url: Router.route('admin.users.json'),
dataType: 'json',
delay: 250,
data: function (params) {
return {
q: params.term, // search term
page: params.page,
};
},
processResults: function (data, params) {
return { results: data };
},
cache: true,
},
escapeMarkup: function (markup) { return markup; },
minimumInputLength: 2,
templateResult: function (data) {
if (data.loading) return data.text;
return '<div class="user-block"> \
<img class="img-circle img-bordered-xs" src="https://www.gravatar.com/avatar/' + data.md5 + '?s=120" alt="User Image"> \
<span class="username"> \
<a href="#">' + data.name_first + ' ' + data.name_last +'</a> \
</span> \
<span class="description"><strong>' + data.email + '</strong> - ' + data.username + '</span> \
</div>';
},
templateSelection: function (data) {
if (typeof data.name_first === 'undefined') {
data = {
md5: '{{ md5(strtolower($server->user->email)) }}',
name_first: '{{ $server->user->name_first }}',
name_last: '{{ $server->user->name_last }}',
email: '{{ $server->user->email }}',
id: {{ $server->owner_id }}
};
}
return '<div> \
<span> \
<img class="img-rounded img-bordered-xs" src="https://www.gravatar.com/avatar/' + data.md5 + '?s=120" style="height:28px;margin-top:-4px;" alt="User Image"> \
</span> \
<span style="padding-left:5px;"> \
' + data.name_first + ' ' + data.name_last + ' (<strong>' + data.email + '</strong>) \
</span> \
</div>';
}
});
</script>
@endsection

View file

@ -0,0 +1,164 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
Server {{ $server->name }}
@endsection
@section('content-header')
<h1>{{ $server->name }}<small>{{ str_limit($server->description) }}</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li><a href="{{ route('admin.servers') }}">Servers</a></li>
<li class="active">{{ $server->name }}</li>
</ol>
@endsection
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="nav-tabs-custom nav-tabs-floating">
<ul class="nav nav-tabs">
<li class="active"><a href="{{ route('admin.servers.view', $server->id) }}">About</a></li>
@if($server->installed === 1)
<li><a href="{{ route('admin.servers.view.details', $server->id) }}">Details</a></li>
<li><a href="{{ route('admin.servers.view.build', $server->id) }}">Build Configuration</a></li>
<li><a href="{{ route('admin.servers.view.startup', $server->id) }}">Startup</a></li>
<li><a href="{{ route('admin.servers.view.database', $server->id) }}">Database</a></li>
<li><a href="{{ route('admin.servers.view.manage', $server->id) }}">Manage</a></li>
@endif
<li class="tab-danger"><a href="{{ route('admin.servers.view.delete', $server->id) }}">Delete</a></li>
<li class="tab-success"><a href="{{ route('server.index', $server->uuidShort) }}"><i class="fa fa-external-link"></i></a></li>
</ul>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-8">
<div class="row">
<div class="col-xs-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Information</h3>
</div>
<div class="box-body table-responsive no-padding">
<table class="table table-hover">
<tr>
<td>Internal Identifier</td>
<td><code>{{ $server->id }}</code></td>
</tr>
<tr>
<td>External Identifier</td>
@if(is_null($server->external_id))
<td><span class="label label-default">Not Set</span></td>
@else
<td><code>{{ $server->external_id }}</code></td>
@endif
</tr>
<tr>
<td>UUID / Docker Container ID</td>
<td><code>{{ $server->uuid }}</code></td>
</tr>
<tr>
<td>Service</td>
<td>
<a href="{{ route('admin.nests.view', $server->nest_id) }}">{{ $server->nest->name }}</a> ::
<a href="{{ route('admin.nests.egg.view', $server->egg_id) }}">{{ $server->egg->name }}</a>
</td>
</tr>
<tr>
<td>Name</td>
<td>{{ $server->name }}</td>
</tr>
<tr>
<td>Memory</td>
<td><code>{{ $server->memory }}MB</code> / <code data-toggle="tooltip" data-placement="top" title="Swap Space">{{ $server->swap }}MB</code></td>
</tr>
<tr>
<td>Disk Space</td>
<td><code>{{ $server->disk }}MB</code></td>
</tr>
<tr>
<td>Block IO Weight</td>
<td><code>{{ $server->io }}</code></td>
</tr>
<tr>
<td>CPU Limit</td>
<td><code>{{ $server->cpu }}%</code></td>
</tr>
<tr>
<td>Default Connection</td>
<td><code>{{ $server->allocation->ip }}:{{ $server->allocation->port }}</code></td>
</tr>
<tr>
<td>Connection Alias</td>
<td>
@if($server->allocation->alias !== $server->allocation->ip)
<code>{{ $server->allocation->alias }}:{{ $server->allocation->port }}</code>
@else
<span class="label label-default">No Alias Assigned</span>
@endif
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-4">
<div class="box box-primary">
<div class="box-body" style="padding-bottom: 0px;">
<div class="row">
@if($server->suspended)
<div class="col-sm-12">
<div class="small-box bg-yellow">
<div class="inner">
<h3 class="no-margin">Suspended</h3>
</div>
</div>
</div>
@endif
@if($server->installed !== 1)
<div class="col-sm-12">
<div class="small-box {{ (! $server->installed) ? 'bg-blue' : 'bg-maroon' }}">
<div class="inner">
<h3 class="no-margin">{{ (! $server->installed) ? 'Installing' : 'Install Failed' }}</h3>
</div>
</div>
</div>
@endif
<div class="col-sm-12">
<div class="small-box bg-gray">
<div class="inner">
<h3>{{ str_limit($server->user->username, 16) }}</h3>
<p>Server Owner</p>
</div>
<div class="icon"><i class="fa fa-user"></i></div>
<a href="{{ route('admin.users.view', $server->user->id) }}" class="small-box-footer">
More info <i class="fa fa-arrow-circle-right"></i>
</a>
</div>
</div>
<div class="col-sm-12">
<div class="small-box bg-gray">
<div class="inner">
<h3>{{ str_limit($server->node->name, 16) }}</h3>
<p>Server Node</p>
</div>
<div class="icon"><i class="fa fa-codepen"></i></div>
<a href="{{ route('admin.nodes.view', $server->node->id) }}" class="small-box-footer">
More info <i class="fa fa-arrow-circle-right"></i>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection

View file

@ -0,0 +1,132 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
Server {{ $server->name }}: Manage
@endsection
@section('content-header')
<h1>{{ $server->name }}<small>Additional actions to control this server.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li><a href="{{ route('admin.servers') }}">Servers</a></li>
<li><a href="{{ route('admin.servers.view', $server->id) }}">{{ $server->name }}</a></li>
<li class="active">Manage</li>
</ol>
@endsection
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="nav-tabs-custom nav-tabs-floating">
<ul class="nav nav-tabs">
<li><a href="{{ route('admin.servers.view', $server->id) }}">About</a></li>
@if($server->installed === 1)
<li><a href="{{ route('admin.servers.view.details', $server->id) }}">Details</a></li>
<li><a href="{{ route('admin.servers.view.build', $server->id) }}">Build Configuration</a></li>
<li><a href="{{ route('admin.servers.view.startup', $server->id) }}">Startup</a></li>
<li><a href="{{ route('admin.servers.view.database', $server->id) }}">Database</a></li>
<li class="active"><a href="{{ route('admin.servers.view.manage', $server->id) }}">Manage</a></li>
@endif
<li class="tab-danger"><a href="{{ route('admin.servers.view.delete', $server->id) }}">Delete</a></li>
<li class="tab-success"><a href="{{ route('server.index', $server->uuidShort) }}"><i class="fa fa-external-link"></i></a></li>
</ul>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-4">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Reinstall Server</h3>
</div>
<div class="box-body">
<p>This will reinstall the server with the assigned pack and service scripts. <strong>Danger!</strong> This could overwrite server data.</p>
</div>
<div class="box-footer">
@if($server->installed === 1)
<form action="{{ route('admin.servers.view.manage.reinstall', $server->id) }}" method="POST">
{!! csrf_field() !!}
<button type="submit" class="btn btn-danger">Reinstall Server</button>
</form>
@else
<button class="btn btn-danger disabled">Server Must Install Properly to Reinstall</button>
@endif
</div>
</div>
</div>
<div class="col-sm-4">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Install Status</h3>
</div>
<div class="box-body">
<p>If you need to change the install status from uninstalled to installed, or vice versa, you may do so with the button below.</p>
</div>
<div class="box-footer">
<form action="{{ route('admin.servers.view.manage.toggle', $server->id) }}" method="POST">
{!! csrf_field() !!}
<button type="submit" class="btn btn-primary">Toggle Install Status</button>
</form>
</div>
</div>
</div>
<div class="col-sm-4">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Rebuild Container</h3>
</div>
<div class="box-body">
<p>This will trigger a rebuild of the server container when it next starts up. This is useful if you modified the server configuration file manually, or something just didn't work out correctly.</p>
</div>
<div class="box-footer">
<form action="{{ route('admin.servers.view.manage.rebuild', $server->id) }}" method="POST">
{!! csrf_field() !!}
<button type="submit" class="btn btn-default">Rebuild Server Container</button>
</form>
</div>
</div>
</div>
@if(! $server->suspended)
<div class="col-sm-4">
<div class="box box-warning">
<div class="box-header with-border">
<h3 class="box-title">Suspend Server</h3>
</div>
<div class="box-body">
<p>This will suspend the server, stop any running processes, and immediately block the user from being able to access their files or otherwise manage the server through the panel or API.</p>
</div>
<div class="box-footer">
<form action="{{ route('admin.servers.view.manage.suspension', $server->id) }}" method="POST">
{!! csrf_field() !!}
<input type="hidden" name="action" value="suspend" />
<button type="submit" class="btn btn-warning">Suspend Server</button>
</form>
</div>
</div>
</div>
@else
<div class="col-sm-4">
<div class="box box-success">
<div class="box-header with-border">
<h3 class="box-title">Unsuspend Server</h3>
</div>
<div class="box-body">
<p>This will unsuspend the server and restore normal user access.</p>
</div>
<div class="box-footer">
<form action="{{ route('admin.servers.view.manage.suspension', $server->id) }}" method="POST">
{!! csrf_field() !!}
<input type="hidden" name="action" value="unsuspend" />
<button type="submit" class="btn btn-success">Unsuspend Server</button>
</form>
</div>
</div>
</div>
@endif
</div>
@endsection

View file

@ -0,0 +1,212 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
Server {{ $server->name }}: Startup
@endsection
@section('content-header')
<h1>{{ $server->name }}<small>Control startup command as well as variables.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li><a href="{{ route('admin.servers') }}">Servers</a></li>
<li><a href="{{ route('admin.servers.view', $server->id) }}">{{ $server->name }}</a></li>
<li class="active">Startup</li>
</ol>
@endsection
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="nav-tabs-custom nav-tabs-floating">
<ul class="nav nav-tabs">
<li><a href="{{ route('admin.servers.view', $server->id) }}">About</a></li>
@if($server->installed === 1)
<li><a href="{{ route('admin.servers.view.details', $server->id) }}">Details</a></li>
<li><a href="{{ route('admin.servers.view.build', $server->id) }}">Build Configuration</a></li>
<li class="active"><a href="{{ route('admin.servers.view.startup', $server->id) }}">Startup</a></li>
<li><a href="{{ route('admin.servers.view.database', $server->id) }}">Database</a></li>
<li><a href="{{ route('admin.servers.view.manage', $server->id) }}">Manage</a></li>
@endif
<li class="tab-danger"><a href="{{ route('admin.servers.view.delete', $server->id) }}">Delete</a></li>
<li class="tab-success"><a href="{{ route('server.index', $server->uuidShort) }}"><i class="fa fa-external-link"></i></a></li>
</ul>
</div>
</div>
</div>
<form action="{{ route('admin.servers.view.startup', $server->id) }}" method="POST">
<div class="row">
<div class="col-xs-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Startup Command Modification</h3>
</div>
<div class="box-body">
<label for="pStartup" class="form-label">Startup Command</label>
<input id="pStartup" name="startup" class="form-control" type="text" value="{{ old('startup', $server->startup) }}" />
<p class="small text-muted">Edit your server's startup command here. The following variables are available by default: <code>@{{SERVER_MEMORY}}</code>, <code>@{{SERVER_IP}}</code>, and <code>@{{SERVER_PORT}}</code>.</p>
</div>
<div class="box-body">
<label for="pDefaultStartupCommand" class="form-label">Default Service Start Command</label>
<input id="pDefaultStartupCommand" class="form-control" type="text" readonly />
</div>
<div class="box-footer">
{!! csrf_field() !!}
<button type="submit" class="btn btn-primary btn-sm pull-right">Save Modifications</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Service Configuration</h3>
</div>
<div class="box-body row">
<div class="col-xs-12">
<p class="small text-danger">
Changing any of the below values will result in the server processing a re-install command. The server will be stopped and will then proceed.
If you are changing the pack, existing data <em>may</em> be overwritten. If you would like the service scripts to not run, ensure the box is checked at the bottom.
</p>
<p class="small text-danger">
<strong>This is a destructive operation in many cases. This server will be stopped immediately in order for this action to proceed.</strong>
</p>
</div>
<div class="form-group col-xs-12">
<label for="pNestId">Nest</label>
<select name="nest_id" id="pNestId" class="form-control">
@foreach($nests as $nest)
<option value="{{ $nest->id }}"
@if($nest->id === $server->nest_id)
selected
@endif
>{{ $nest->name }}</option>
@endforeach
</select>
<p class="small text-muted no-margin">Select the Nest that this server will be grouped into.</p>
</div>
<div class="form-group col-xs-12">
<label for="pEggId">Egg</label>
<select name="egg_id" id="pEggId" class="form-control"></select>
<p class="small text-muted no-margin">Select the Egg that will provide processing data for this server.</p>
</div>
<div class="form-group col-xs-12">
<label for="pPackId">Data Pack</label>
<select name="pack_id" id="pPackId" class="form-control"></select>
<p class="small text-muted no-margin">Select a data pack to be automatically installed on this server when first created.</p>
</div>
<div class="form-group col-xs-12">
<div class="checkbox checkbox-primary no-margin-bottom">
<input id="pSkipScripting" name="skip_scripts" type="checkbox" value="1" @if($server->skip_scripts) checked @endif />
<label for="pSkipScripting" class="strong">Skip Egg Install Script</label>
</div>
<p class="small text-muted no-margin">If the selected Egg has an install script attached to it, the script will run during install after the pack is installed. If you would like to skip this step, check this box.</p>
</div>
</div>
</div>
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Docker Container Configuration</h3>
</div>
<div class="box-body">
<div class="form-group">
<label for="pDockerImage" class="control-label">Image</label>
<input type="text" name="docker_image" id="pDockerImage" value="{{ $server->image }}" class="form-control" />
<p class="text-muted small">The Docker image to use for this server. The default image for the selected egg is <code id="setDefaultImage"></code>.</p>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="row" id="appendVariablesTo"></div>
</div>
</div>
</form>
@endsection
@section('footer-scripts')
@parent
{!! Theme::js('vendor/lodash/lodash.js') !!}
<script>
$(document).ready(function () {
$('#pPackId').select2({placeholder: 'Select a Service Pack'});
$('#pEggId').select2({placeholder: 'Select a Nest Egg'}).on('change', function () {
var selectedEgg = _.isNull($(this).val()) ? $(this).find('option').first().val() : $(this).val();
var parentChain = _.get(Pterodactyl.nests, $("#pNestId").val());
var objectChain = _.get(parentChain, 'eggs.' + selectedEgg);
$('#setDefaultImage').html(_.get(objectChain, 'docker_image', 'undefined'));
$('#pDockerImage').val(_.get(objectChain, 'docker_image', 'undefined'));
if (objectChain.id === parseInt(Pterodactyl.server.egg_id)) {
$('#pDockerImage').val(Pterodactyl.server.image);
}
if (!_.get(objectChain, 'startup', false)) {
$('#pDefaultStartupCommand').val(_.get(parentChain, 'startup', 'ERROR: Startup Not Defined!'));
} else {
$('#pDefaultStartupCommand').val(_.get(objectChain, 'startup'));
}
$('#pPackId').html('').select2({
data: [{id: '0', text: 'No Service Pack'}].concat(
$.map(_.get(objectChain, 'packs', []), function (item, i) {
return {
id: item.id,
text: item.name + ' (' + item.version + ')',
};
})
),
});
if (Pterodactyl.server.pack_id !== null) {
$('#pPackId').val(Pterodactyl.server.pack_id);
}
$('#appendVariablesTo').html('');
$.each(_.get(objectChain, 'variables', []), function (i, item) {
var setValue = _.get(Pterodactyl.server_variables, item.env_variable, item.default_value);
var isRequired = (item.required === 1) ? '<span class="label label-danger">Required</span> ' : '';
var dataAppend = ' \
<div class="col-xs-12"> \
<div class="box"> \
<div class="box-header with-border"> \
<h3 class="box-title">' + isRequired + item.name + '</h3> \
</div> \
<div class="box-body"> \
<input name="environment[' + item.env_variable + ']" class="form-control" type="text" id="egg_variable_' + item.env_variable + '" /> \
<p class="no-margin small text-muted">' + item.description + '</p> \
</div> \
<div class="box-footer"> \
<p class="no-margin text-muted small"><strong>Startup Command Variable:</strong> <code>' + item.env_variable + '</code></p> \
<p class="no-margin text-muted small"><strong>Input Rules:</strong> <code>' + item.rules + '</code></p> \
</div> \
</div> \
</div>';
$('#appendVariablesTo').append(dataAppend).find('#egg_variable_' + item.env_variable).val(setValue);
});
});
$('#pNestId').select2({placeholder: 'Select a Nest'}).on('change', function () {
$('#pEggId').html('').select2({
data: $.map(_.get(Pterodactyl.nests, $(this).val() + '.eggs', []), function (item) {
return {
id: item.id,
text: item.name,
};
}),
});
if (_.isObject(_.get(Pterodactyl.nests, $(this).val() + '.eggs.' + Pterodactyl.server.egg_id))) {
$('#pEggId').val(Pterodactyl.server.egg_id);
}
$('#pEggId').change();
}).change();
});
</script>
@endsection

View file

@ -0,0 +1,117 @@
@extends('layouts.admin')
@include('partials/admin.settings.nav', ['activeTab' => 'advanced'])
@section('title')
Advanced Settings
@endsection
@section('content-header')
<h1>Advanced Settings<small>Configure advanced settings for Pterodactyl.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li class="active">Settings</li>
</ol>
@endsection
@section('content')
@yield('settings::nav')
<div class="row">
<div class="col-xs-12">
<form action="" method="POST">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">reCAPTCHA</h3>
</div>
<div class="box-body">
<div class="row">
<div class="form-group col-md-4">
<label class="control-label">Status</label>
<div>
<select class="form-control" name="recaptcha:enabled">
<option value="true">Enabled</option>
<option value="false" @if(old('recaptcha:enabled', config('recaptcha.enabled')) == '0') selected @endif>Disabled</option>
</select>
<p class="text-muted small">If enabled, login forms and password reset forms will do a silent captcha check and display a visible captcha if needed.</p>
</div>
</div>
<div class="form-group col-md-4">
<label class="control-label">Site Key</label>
<div>
<input type="text" required class="form-control" name="recaptcha:website_key" value="{{ old('recaptcha:website_key', config('recaptcha.website_key')) }}">
</div>
</div>
<div class="form-group col-md-4">
<label class="control-label">Secret Key</label>
<div>
<input type="text" required class="form-control" name="recaptcha:secret_key" value="{{ old('recaptcha:secret_key', config('recaptcha.secret_key')) }}">
<p class="text-muted small">Used for communication between your site and Google. Be sure to keep it a secret.</p>
</div>
</div>
</div>
@if($showRecaptchaWarning)
<div class="row">
<div class="col-xs-12">
<div class="alert alert-warning no-margin">
You are currently using reCAPTCHA keys that were shipped with this Panel. For improved security it is recommended to <a href="https://www.google.com/recaptcha/admin">generate new invisible reCAPTCHA keys</a> that tied specifically to your website.
</div>
</div>
</div>
@endif
</div>
</div>
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">HTTP Connections</h3>
</div>
<div class="box-body">
<div class="row">
<div class="form-group col-md-6">
<label class="control-label">Connection Timeout</label>
<div>
<input type="number" required class="form-control" name="pterodactyl:guzzle:connect_timeout" value="{{ old('pterodactyl:guzzle:connect_timeout', config('pterodactyl.guzzle.connect_timeout')) }}">
<p class="text-muted small">The amount of time in seconds to wait for a connection to be opened before throwing an error.</p>
</div>
</div>
<div class="form-group col-md-6">
<label class="control-label">Request Timeout</label>
<div>
<input type="number" required class="form-control" name="pterodactyl:guzzle:timeout" value="{{ old('pterodactyl:guzzle:timeout', config('pterodactyl.guzzle.timeout')) }}">
<p class="text-muted small">The amount of time in seconds to wait for a request to be completed before throwing an error.</p>
</div>
</div>
</div>
</div>
</div>
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Console</h3>
</div>
<div class="box-body">
<div class="row">
<div class="form-group col-md-6">
<label class="control-label">Message Count</label>
<div>
<input type="number" required class="form-control" name="pterodactyl:console:count" value="{{ old('pterodactyl:console:count', config('pterodactyl.console.count')) }}">
<p class="text-muted small">The number of messages to be pushed to the console per frequency tick.</p>
</div>
</div>
<div class="form-group col-md-6">
<label class="control-label">Frequency Tick</label>
<div>
<input type="number" required class="form-control" name="pterodactyl:console:frequency" value="{{ old('pterodactyl:console:frequency', config('pterodactyl.console.frequency')) }}">
<p class="text-muted small">The amount of time in milliseconds between each console message sending tick.</p>
</div>
</div>
</div>
</div>
</div>
<div class="box box-primary">
<div class="box-footer">
{{ csrf_field() }}
<button type="submit" name="_method" value="PATCH" class="btn btn-sm btn-primary pull-right">Save</button>
</div>
</div>
</form>
</div>
</div>
@endsection

View file

@ -0,0 +1,75 @@
@extends('layouts.admin')
@include('partials/admin.settings.nav', ['activeTab' => 'basic'])
@section('title')
Settings
@endsection
@section('content-header')
<h1>Panel Settings<small>Configure Pterodactyl to your liking.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li class="active">Settings</li>
</ol>
@endsection
@section('content')
@yield('settings::nav')
<div class="row">
<div class="col-xs-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Panel Settings</h3>
</div>
<form action="{{ route('admin.settings') }}" method="POST">
<div class="box-body">
<div class="row">
<div class="form-group col-md-4">
<label class="control-label">Company Name</label>
<div>
<input type="text" class="form-control" name="app:name" value="{{ old('app:name', config('app.name')) }}" />
<p class="text-muted"><small>This is the name that is used throughout the panel and in emails sent to clients.</small></p>
</div>
</div>
<div class="form-group col-md-4">
<label class="control-label">Require 2-Factor Authentication</label>
<div>
<div class="btn-group" data-toggle="buttons">
@php
$level = old('pterodactyl:auth:2fa_required', config('pterodactyl.auth.2fa_required'));
@endphp
<label class="btn btn-primary @if ($level == 0) active @endif">
<input type="radio" name="pterodactyl:auth:2fa_required" autocomplete="off" value="0" @if ($level == 0) checked @endif> Not Required
</label>
<label class="btn btn-primary @if ($level == 1) active @endif">
<input type="radio" name="pterodactyl:auth:2fa_required" autocomplete="off" value="1" @if ($level == 1) checked @endif> Admin Only
</label>
<label class="btn btn-primary @if ($level == 2) active @endif">
<input type="radio" name="pterodactyl:auth:2fa_required" autocomplete="off" value="2" @if ($level == 2) checked @endif> All Users
</label>
</div>
<p class="text-muted"><small>If enabled, any account falling into the selected grouping will be required to have 2-Factor authentication enabled to use the Panel.</small></p>
</div>
</div>
<div class="form-group col-md-4">
<label class="control-label">Default Language</label>
<div>
<select name="app:locale" class="form-control">
@foreach($languages as $key => $value)
<option value="{{ $key }}" @if(config('app.locale') === $key) selected @endif>{{ $value }}</option>
@endforeach
</select>
<p class="text-muted"><small>The default language to use when rendering UI components.</small></p>
</div>
</div>
</div>
</div>
<div class="box-footer">
{!! csrf_field() !!}
<button type="submit" name="_method" value="PATCH" class="btn btn-sm btn-primary pull-right">Save</button>
</div>
</form>
</div>
</div>
</div>
@endsection

View file

@ -0,0 +1,202 @@
@extends('layouts.admin')
@include('partials/admin.settings.nav', ['activeTab' => 'mail'])
@section('title')
Mail Settings
@endsection
@section('content-header')
<h1>Mail Settings<small>Configure how Pterodactyl should handle sending emails.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li class="active">Settings</li>
</ol>
@endsection
@section('content')
@yield('settings::nav')
<div class="row">
<div class="col-xs-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Email Settings</h3>
</div>
@if($disabled)
<div class="box-body">
<div class="row">
<div class="col-xs-12">
<div class="alert alert-info no-margin-bottom">
This interface is limited to instances using SMTP as the mail driver. Please either use <code>php artisan p:environment:mail</code> command to update your email settings, or set <code>MAIL_DRIVER=smtp</code> in your environment file.
</div>
</div>
</div>
</div>
@else
<form>
<div class="box-body">
<div class="row">
<div class="form-group col-md-6">
<label class="control-label">SMTP Host</label>
<div>
<input required type="text" class="form-control" name="mail:host" value="{{ old('mail:host', config('mail.host')) }}" />
<p class="text-muted small">Enter the SMTP server address that mail should be sent through.</p>
</div>
</div>
<div class="form-group col-md-2">
<label class="control-label">SMTP Port</label>
<div>
<input required type="number" class="form-control" name="mail:port" value="{{ old('mail:port', config('mail.port')) }}" />
<p class="text-muted small">Enter the SMTP server port that mail should be sent through.</p>
</div>
</div>
<div class="form-group col-md-4">
<label class="control-label">Encryption</label>
<div>
@php
$encryption = old('mail:encryption', config('mail.encryption'));
@endphp
<select name="mail:encryption" class="form-control">
<option value="" @if($encryption === '') selected @endif>None</option>
<option value="tls" @if($encryption === 'tls') selected @endif>Transport Layer Security (TLS)</option>
<option value="ssl" @if($encryption === 'ssl') selected @endif>Secure Sockets Layer (SSL)</option>
</select>
<p class="text-muted small">Select the type of encryption to use when sending mail.</p>
</div>
</div>
<div class="form-group col-md-6">
<label class="control-label">Username <span class="field-optional"></span></label>
<div>
<input type="text" class="form-control" name="mail:username" value="{{ old('mail:username', config('mail.username')) }}" />
<p class="text-muted small">The username to use when connecting to the SMTP server.</p>
</div>
</div>
<div class="form-group col-md-6">
<label class="control-label">Password <span class="field-optional"></span></label>
<div>
<input type="password" class="form-control" name="mail:password"/>
<p class="text-muted small">The password to use in conjunction with the SMTP username. Leave blank to continue using the existing password. To set the password to an empty value enter <code>!e</code> into the field.</p>
</div>
</div>
</div>
<div class="row">
<hr />
<div class="form-group col-md-6">
<label class="control-label">Mail From</label>
<div>
<input required type="email" class="form-control" name="mail:from:address" value="{{ old('mail:from:address', config('mail.from.address')) }}" />
<p class="text-muted small">Enter an email address that all outgoing emails will originate from.</p>
</div>
</div>
<div class="form-group col-md-6">
<label class="control-label">Mail From Name <span class="field-optional"></span></label>
<div>
<input type="text" class="form-control" name="mail:from:name" value="{{ old('mail:from:name', config('mail.from.name')) }}" />
<p class="text-muted small">The name that emails should appear to come from.</p>
</div>
</div>
</div>
</div>
<div class="box-footer">
{{ csrf_field() }}
<div class="pull-right">
<button type="button" id="testButton" class="btn btn-sm btn-success">Test</button>
<button type="button" id="saveButton" class="btn btn-sm btn-primary">Save</button>
</div>
</div>
</form>
@endif
</div>
</div>
</div>
@endsection
@section('footer-scripts')
@parent
<script>
function saveSettings() {
return $.ajax({
method: 'PATCH',
url: Router.route('admin.settings.mail'),
contentType: 'application/json',
data: JSON.stringify({
'mail:host': $('input[name="mail:host"]').val(),
'mail:port': $('input[name="mail:port"]').val(),
'mail:encryption': $('select[name="mail:encryption"]').val(),
'mail:username': $('input[name="mail:username"]').val(),
'mail:password': $('input[name="mail:password"]').val(),
'mail:from:address': $('input[name="mail:from:address"]').val(),
'mail:from:name': $('input[name="mail:from:name"]').val()
}),
headers: { 'X-CSRF-Token': $('input[name="_token"]').val() }
}).fail(function (jqXHR) {
showErrorDialog(jqXHR, 'save');
});
}
function testSettings() {
swal({
type: 'info',
title: 'Test Mail Settings',
text: 'Click "Test" to begin the test.',
showCancelButton: true,
confirmButtonText: 'Test',
closeOnConfirm: false,
showLoaderOnConfirm: true
}, function () {
$.ajax({
method: 'GET',
url: Router.route('admin.settings.mail.test'),
headers: { 'X-CSRF-Token': $('input[name="_token"]').val() }
}).fail(function (jqXHR) {
showErrorDialog(jqXHR, 'test');
}).done(function () {
swal({
title: 'Success',
text: 'The test message was sent successfully.',
type: 'success'
});
});
});
}
function saveAndTestSettings() {
saveSettings().done(testSettings);
}
function showErrorDialog(jqXHR, verb) {
console.error(jqXHR);
var errorText = '';
if (!jqXHR.responseJSON) {
errorText = jqXHR.responseText;
} else if (jqXHR.responseJSON.error) {
errorText = jqXHR.responseJSON.error;
} else if (jqXHR.responseJSON.errors) {
$.each(jqXHR.responseJSON.errors, function (i, v) {
if (v.detail) {
errorText += v.detail + ' ';
}
});
}
swal({
title: 'Whoops!',
text: 'An error occurred while attempting to ' + verb + ' mail settings: ' + errorText,
type: 'error'
});
}
$(document).ready(function () {
$('#testButton').on('click', saveAndTestSettings);
$('#saveButton').on('click', function () {
saveSettings().done(function () {
swal({
title: 'Success',
text: 'Mail settings have been updated successfully and the queue worker was restarted to apply these changes.',
type: 'success'
});
});
});
});
</script>
@endsection

View file

@ -0,0 +1,141 @@
@extends('layouts.admin')
@include('partials/admin.settings.nav', ['activeTab' => 'basic'])
@section('title')
Statistics Overview
@endsection
@section('content-header')
<h1>Statistics Overview<small>Monitor your panel usage.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li class="active">Statistics</li>
</ol>
@endsection
@section('content')
<div class="row">
<div class="col-xs-12 col-md-8">
<div class="box box-primary">
<div class="box-header with-border">
Servers
</div>
<div class="box-body">
<div class="col-xs-12 col-md-6">
<canvas id="servers_chart" width="100%" height="50"></canvas>
</div>
<div class="col-xs-12 col-md-6">
<canvas id="status_chart" width="100%" height="50"></canvas>
</div>
</div>
</div>
</div>
<div class="col-xs-12 col-md-4">
<div class="info-box bg-blue">
<span class="info-box-icon"><i class="fa fa-server"></i></span>
<div class="info-box-content number-info-box-content">
<span class="info-box-text">Servers</span>
<span class="info-box-number">{{ count($servers) }}</span>
</div>
</div>
<div class="info-box bg-blue">
<span class="info-box-icon"><i class="ion ion-ios-barcode-outline"></i></span>
<div class="info-box-content number-info-box-content">
<span class="info-box-text">Total used Memory (in MB)</span>
<span class="info-box-number">{{ $totalServerRam }}MB</span>
</div>
</div>
<div class="info-box bg-blue">
<span class="info-box-icon"><i class="ion ion-stats-bars"></i></span>
<div class="info-box-content number-info-box-content">
<span class="info-box-text">Total used Disk (in MB)</span>
<span class="info-box-number">{{ $totalServerDisk }}MB</span>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-md-8">
<div class="box box-primary">
<div class="box-header with-border">
Nodes
</div>
<div class="box-body">
<div class="col-xs-12 col-md-6">
<canvas id="ram_chart" width="100%" height="50"></canvas>
</div>
<div class="col-xs-12 col-md-6">
<canvas id="disk_chart" width="100%" height="50"></canvas>
</div>
</div>
</div>
</div>
<div class="col-xs-12 col-md-4">
<div class="info-box bg-blue">
<span class="info-box-icon"><i class="ion ion-ios-barcode-outline"></i></span>
<div class="info-box-content number-info-box-content">
<span class="info-box-text">Total RAM</span>
<span class="info-box-number">{{ $totalNodeRam }}MB</span>
</div>
</div>
<div class="info-box bg-blue">
<span class="info-box-icon"><i class="ion ion-stats-bars"></i></span>
<div class="info-box-content number-info-box-content">
<span class="info-box-text">Total Disk Space</span>
<span class="info-box-number">{{ $totalNodeDisk }}MB</span>
</div>
</div>
<div class="info-box bg-blue">
<span class="info-box-icon"><i class="fa fa-location-arrow"></i></span>
<div class="info-box-content number-info-box-content">
<span class="info-box-text">Total Allocations</span>
<span class="info-box-number">{{ $totalAllocations }}</span>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-md-3">
<div class="info-box bg-blue">
<span class="info-box-icon"><i class="fa fa-gamepad"></i></span>
<div class="info-box-content number-info-box-content">
<span class="info-box-text">Total Eggs</span>
<span class="info-box-number">{{ $eggsCount }}</span>
</div>
</div>
</div>
<div class="col-xs-12 col-md-3">
<div class="info-box bg-blue">
<span class="info-box-icon"><i class="fa fa-users"></i></span>
<div class="info-box-content number-info-box-content">
<span class="info-box-text">Total Users</span>
<span class="info-box-number">{{ $usersCount }}</span>
</div>
</div>
</div>
<div class="col-xs-12 col-md-3">
<div class="info-box bg-blue">
<span class="info-box-icon"><i class="fa fa-server"></i></span>
<div class="info-box-content number-info-box-content">
<span class="info-box-text">Total Nodes</span>
<span class="info-box-number">{{ count($nodes) }}</span>
</div>
</div>
</div>
<div class="col-xs-12 col-md-3">
<div class="info-box bg-blue">
<span class="info-box-icon"><i class="fa fa-database"></i></span>
<div class="info-box-content number-info-box-content">
<span class="info-box-text">Total Databases</span>
<span class="info-box-number">{{ $databasesCount }}</span>
</div>
</div>
</div>
</div>
@endsection
@section('footer-scripts')
@parent
{!! Theme::js('vendor/chartjs/chart.min.js') !!}
{!! Theme::js('js/admin/statistics.js') !!}
@endsection

View file

@ -0,0 +1,84 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
List Users
@endsection
@section('content-header')
<h1>Users<small>All registered users on the system.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li class="active">Users</li>
</ol>
@endsection
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">User List</h3>
<div class="box-tools">
<form action="{{ route('admin.users') }}" method="GET">
<div class="input-group input-group-sm">
<input type="text" name="query" class="form-control pull-right" style="width:30%;" value="{{ request()->input('query') }}" placeholder="Search">
<div class="input-group-btn">
<button type="submit" class="btn btn-default"><i class="fa fa-search"></i></button>
<a href="{{ route('admin.users.new') }}"><button type="button" class="btn btn-sm btn-primary" style="border-radius: 0 3px 3px 0;margin-left:-1px;">Create New</button></a>
</div>
</div>
</form>
</div>
</div>
<div class="box-body table-responsive no-padding">
<table class="table table-hover">
<thead>
<tr>
<th>ID</th>
<th>Email</th>
<th>Client Name</th>
<th>Username</th>
<th class="text-center">2FA</th>
<th class="text-center"><span data-toggle="tooltip" data-placement="top" title="Servers that this user is marked as the owner of.">Servers Owned</span></th>
<th class="text-center"><span data-toggle="tooltip" data-placement="top" title="Servers that this user can access because they are marked as a subuser.">Can Access</span></th>
<th></th>
</tr>
</thead>
<tbody>
@foreach ($users as $user)
<tr class="align-middle">
<td><code>{{ $user->id }}</code></td>
<td><a href="{{ route('admin.users.view', $user->id) }}">{{ $user->email }}</a> @if($user->root_admin)<i class="fa fa-star text-yellow"></i>@endif</td>
<td>{{ $user->name_last }}, {{ $user->name_first }}</td>
<td>{{ $user->username }}</td>
<td class="text-center">
@if($user->use_totp)
<i class="fa fa-lock text-green-500"></i>
@else
<i class="fa fa-unlock text-red"></i>
@endif
</td>
<td class="text-center">
<a href="{{ route('admin.servers', ['query' => $user->email]) }}">{{ $user->servers_count }}</a>
</td>
<td class="text-center">{{ $user->subuser_of_count }}</td>
<td class="text-center"><img src="https://www.gravatar.com/avatar/{{ md5(strtolower($user->email)) }}?s=100" style="height:20px;" class="img-circle" /></td>
</tr>
@endforeach
</tbody>
</table>
</div>
@if($users->hasPages())
<div class="box-footer with-border">
<div class="col-md-12 text-center">{!! $users->appends(['query' => Request::input('query')])->render() !!}</div>
</div>
@endif
</div>
</div>
</div>
@endsection

View file

@ -0,0 +1,133 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
Create User
@endsection
@section('content-header')
<h1>Create User<small>Add a new user to the system.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li><a href="{{ route('admin.users') }}">Users</a></li>
<li class="active">Create</li>
</ol>
@endsection
@section('content')
<div class="row">
<form method="post">
<div class="col-md-6">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Identity</h3>
</div>
<div class="box-body">
<div class="form-group">
<label for="email" class="control-label">Email</label>
<div>
<input type="text" autocomplete="off" name="email" value="{{ old('email') }}" class="form-control" />
</div>
</div>
<div class="form-group">
<label for="username" class="control-label">Username</label>
<div>
<input type="text" autocomplete="off" name="username" value="{{ old('username') }}" class="form-control" />
</div>
</div>
<div class="form-group">
<label for="name_first" class="control-label">Client First Name</label>
<div>
<input type="text" autocomplete="off" name="name_first" value="{{ old('name_first') }}" class="form-control" />
</div>
</div>
<div class="form-group">
<label for="name_last" class="control-label">Client Last Name</label>
<div>
<input type="text" autocomplete="off" name="name_last" value="{{ old('name_last') }}" class="form-control" />
</div>
</div>
<div class="form-group">
<label class="control-label">Default Language</label>
<div>
<select name="language" class="form-control">
@foreach($languages as $key => $value)
<option value="{{ $key }}" @if(config('app.locale') === $key) selected @endif>{{ $value }}</option>
@endforeach
</select>
<p class="text-muted"><small>The default language to use when rendering the Panel for this user.</small></p>
</div>
</div>
</div>
<div class="box-footer">
{!! csrf_field() !!}
<input type="submit" value="Create User" class="btn btn-success btn-sm">
</div>
</div>
</div>
<div class="col-md-6">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Permissions</h3>
</div>
<div class="box-body">
<div class="form-group col-md-12">
<label for="root_admin" class="control-label">Administrator</label>
<div>
<select name="root_admin" class="form-control">
<option value="0">@lang('strings.no')</option>
<option value="1">@lang('strings.yes')</option>
</select>
<p class="text-muted"><small>Setting this to 'Yes' gives a user full administrative access.</small></p>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Password</h3>
</div>
<div class="box-body">
<div class="alert alert-info">
<p>Providing a user password is optional. New user emails prompt users to create a password the first time they login. If a password is provided here you will need to find a different method of providing it to the user.</p>
</div>
<div id="gen_pass" class=" alert alert-success" style="display:none;margin-bottom: 10px;"></div>
<div class="form-group">
<label for="pass" class="control-label">Password</label>
<div>
<input type="password" name="password" class="form-control" />
</div>
</div>
</div>
</div>
</div>
</form>
</div>
@endsection
@section('footer-scripts')
@parent
<script>$("#gen_pass_bttn").click(function (event) {
event.preventDefault();
$.ajax({
type: "GET",
url: "/password-gen/12",
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
success: function(data) {
$("#gen_pass").html('<strong>Generated Password:</strong> ' + data).slideDown();
$('input[name="password"], input[name="password_confirmation"]').val(data);
return false;
}
});
return false;
});
</script>
@endsection

View file

@ -0,0 +1,172 @@
{{-- Pterodactyl - Panel --}}
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- This software is licensed under the terms of the MIT license. --}}
{{-- https://opensource.org/licenses/MIT --}}
@extends('layouts.admin')
@section('title')
Manager User: {{ $user->username }}
@endsection
@section('content-header')
<h1>{{ $user->name_first }} {{ $user->name_last}}<small>{{ $user->username }}</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li><a href="{{ route('admin.users') }}">Users</a></li>
<li class="active">{{ $user->username }}</li>
</ol>
@endsection
@section('content')
<div class="row">
<form action="{{ route('admin.users.view', $user->id) }}" method="post">
<div class="col-md-6">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Identity</h3>
</div>
<div class="box-body">
<div class="form-group">
<label for="email" class="control-label">Email</label>
<div>
<input readonly type="email" name="email" value="{{ $user->email }}" class="form-control form-autocomplete-stop">
</div>
</div>
<div class="form-group">
<label for="registered" class="control-label">Username</label>
<div>
<input readonly type="text" name="username" value="{{ $user->username }}" class="form-control form-autocomplete-stop">
</div>
</div>
<div class="form-group">
<label for="registered" class="control-label">Client First Name</label>
<div>
<input readonly type="text" name="name_first" value="{{ $user->name_first }}" class="form-control form-autocomplete-stop">
</div>
</div>
<div class="form-group">
<label for="registered" class="control-label">Client Last Name</label>
<div>
<input readonly type="text" name="name_last" value="{{ $user->name_last }}" class="form-control form-autocomplete-stop">
</div>
</div>
<div class="form-group">
<label class="control-label">Default Language</label>
<div>
<select name="language" class="form-control">
@foreach($languages as $key => $value)
<option value="{{ $key }}" @if($user->language === $key) selected @endif>{{ $value }}</option>
@endforeach
</select>
<p class="text-muted"><small>The default language to use when rendering the Panel for this user.</small></p>
</div>
</div>
</div>
<div class="box-footer">
{!! csrf_field() !!}
{!! method_field('PATCH') !!}
<input type="submit" value="Update User" class="btn btn-primary btn-sm">
</div>
</div>
</div>
<div class="col-md-6">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Password</h3>
</div>
<div class="box-body">
<div class="alert alert-success" style="display:none;margin-bottom:10px;" id="gen_pass"></div>
<div class="form-group no-margin-bottom">
<label for="password" class="control-label">Password <span class="field-optional"></span></label>
<div>
<input readonly type="password" id="password" name="password" class="form-control form-autocomplete-stop">
<p class="text-muted small">Leave blank to keep this user's password the same. User will not receive any notification if password is changed.</p>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Permissions</h3>
</div>
<div class="box-body">
<div class="form-group">
<label for="root_admin" class="control-label">Administrator</label>
<div>
<select name="root_admin" class="form-control">
<option value="0">@lang('strings.no')</option>
<option value="1" {{ $user->root_admin ? 'selected="selected"' : '' }}>@lang('strings.yes')</option>
</select>
<p class="text-muted"><small>Setting this to 'Yes' gives a user full administrative access.</small></p>
</div>
<div class="checkbox checkbox-primary">
<input type="checkbox" id="pIgnoreConnectionError" value="1" name="ignore_connection_error">
<label for="pIgnoreConnectionError"> Ignore exceptions raised while revoking keys.</label>
<p class="text-muted small">If checked, any errors thrown while revoking keys across nodes will be ignored. You should avoid this checkbox if possible as any non-revoked keys could continue to be active for up to 24 hours after this account is changed. If you are needing to revoke account permissions immediately and are facing node issues, you should check this box and then restart any nodes that failed to be updated to clear out any stored keys.</p>
</div>
</div>
</div>
</div>
</div>
</form>
{{--<div class="col-xs-12">--}}
{{--<div class="box">--}}
{{--<div class="box-header with-border">--}}
{{--<h3 class="box-title">Associated Servers</h3>--}}
{{--</div>--}}
{{--<div class="box-body table-responsive no-padding">--}}
{{--<table class="table table-hover">--}}
{{--<thead>--}}
{{--<tr>--}}
{{--<th style="width:2%;"></th>--}}
{{--<th>Identifier</th>--}}
{{--<th>Server Name</th>--}}
{{--<th>Access</th>--}}
{{--<th>Node</th>--}}
{{--<th style="width:10%;"></th>--}}
{{--</tr>--}}
{{--</thead>--}}
{{--<tbody>--}}
{{--@foreach($user->setAccessLevel('subuser')->access()->get() as $server)--}}
{{--<tr>--}}
{{--<td><a href="{{ route('server.index', $server->uuidShort) }}/"><i class="fa fa-tachometer"></i></a></td>--}}
{{--<td><code>{{ $server->uuidShort }}</code></td>--}}
{{--<td><a href="{{ route('admin.servers.view', $server->id) }}">{{ $server->name }}</a></td>--}}
{{--<td>--}}
{{--@if($server->owner_id === $user->id)--}}
{{--<span class="label bg-purple">Owner</span>--}}
{{--@else--}}
{{--<span class="label bg-blue">Subuser</span>--}}
{{--@endif--}}
{{--</td>--}}
{{--<td><a href="{{ route('admin.nodes.view', $server->node->id) }}">{{ $server->node->name }}</a></td>--}}
{{--<td class="centered">@if($server->suspended === 0)<span class="label muted muted-hover label-success">Active</span>@else<span class="label label-warning">Suspended</span>@endif</td>--}}
{{--</td>--}}
{{--@endforeach--}}
{{--</tbody>--}}
{{--</table>--}}
{{--</div>--}}
{{--</div>--}}
{{--</div>--}}
<div class="col-xs-12">
<div class="box box-danger">
<div class="box-header with-border">
<h3 class="box-title">Delete User</h3>
</div>
<div class="box-body">
<p class="no-margin">There must be no servers associated with this account in order for it to be deleted.</p>
</div>
<div class="box-footer">
<form action="{{ route('admin.users.view', $user->id) }}" method="POST">
{!! csrf_field() !!}
{!! method_field('DELETE') !!}
<input id="delete" type="submit" class="btn btn-sm btn-danger pull-right" {{ $user->servers->count() < 1 ?: 'disabled' }} value="Delete User" />
</form>
</div>
</div>
</div>
</div>
@endsection