1
1
mirror of https://github.com/pterodactyl/panel.git synced 2024-11-22 09:02:28 +01:00

Begin implementation of server admin view

Currently completed tabs: About, Details, Build Configuration
This commit is contained in:
Dane Everitt 2017-02-25 00:48:12 -05:00
parent bbf9fd12ae
commit fb21bf9282
No known key found for this signature in database
GPG Key ID: EEA66103B3D71F53
6 changed files with 464 additions and 147 deletions

View File

@ -149,71 +149,13 @@ class ServersController extends Controller
})->values();
}
/**
* Returns a JSON tree of all avaliable IPs and Ports on a given node.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function postNewServerGetIps(Request $request)
{
return Models\Allocation::select('id', 'ip')->where('node_id', $request->input('node'))->whereNull('server_id')->get()->unique('ip')->values()->all();
}
/**
* Returns a JSON tree of all avaliable options for a given service.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function postNewServerServiceOption(Request $request)
{
if (! $request->input('service')) {
return response()->json([
'error' => 'Missing service in request.',
], 500);
}
$service = Models\Service::select('executable', 'startup')->where('id', $request->input('service'))->first();
return response()->json(Models\ServiceOption::select('id', 'name', 'docker_image')->where('service_id', $request->input('service'))->orderBy('name', 'asc')->get());
}
/**
* Returns a JSON tree of all avaliable variables for a given service option.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function postNewServerOptionDetails(Request $request)
{
if (! $request->input('option')) {
return response()->json([
'error' => 'Missing option in request.',
], 500);
}
$option = Models\ServiceOption::with('variables')->with(['packs' => function ($query) {
$query->where('selectable', true);
}])->findOrFail($request->input('option'));
return response()->json([
'packs' => $option->packs,
'variables' => $option->variables,
'exec' => $option->display_executable,
'startup' => $option->display_startup,
]);
}
public function postUpdateServerDetails(Request $request, $id)
{
try {
$server = new ServerRepository;
$server->updateDetails($id, [
'owner' => $request->input('owner'),
'name' => $request->input('name'),
'reset_token' => ($request->input('reset_token', false) === 'on') ? true : false,
]);
$server->updateDetails($id, $request->intersect([
'owner_id', 'name', 'reset_token'
]));
Alert::success('Server details were successfully updated.')->flash();
} catch (DisplayValidationException $ex) {
@ -238,7 +180,7 @@ class ServersController extends Controller
{
try {
$server = new ServerRepository;
$server->updateContainer($id, ['image' => $request->input('docker_image')]);
$server->updateContainer($id, $request->intersect('docker_image'));
Alert::success('Successfully updated this server\'s docker image.')->flash();
} catch (DisplayValidationException $ex) {
return redirect()->route('admin.servers.view', [
@ -283,9 +225,9 @@ class ServersController extends Controller
{
try {
$server = new ServerRepository;
$server->changeBuild($id, $request->only([
'default', 'add_additional',
'remove_additional', 'memory',
$server->changeBuild($id, $request->intersect([
'allocation_id', 'add_allocations',
'remove_allocations', 'memory',
'swap', 'io', 'cpu',
]));
Alert::success('Server details were successfully updated.')->flash();

View File

@ -140,22 +140,6 @@ class AdminRoutes
'uses' => 'Admin\ServersController@postNewServerGetNodes',
]);
$router->post('/new/get-ips', [
'as' => 'admin.servers.new.get-ips',
'uses' => 'Admin\ServersController@postNewServerGetIps',
]);
$router->post('/new/service-options', [
'as' => 'admin.servers.new.service-options',
'uses' => 'Admin\ServersController@postNewServerServiceOption',
]);
$router->post('/new/option-details', [
'as' => 'admin.servers.new.option-details',
'uses' => 'Admin\ServersController@postNewServerOptionDetails',
]);
// End Assorted Page Helpers
// View Specific Server
$router->get('/view/{id}', [
'as' => 'admin.servers.view',
@ -170,6 +154,7 @@ class AdminRoutes
// Change Server Details
$router->post('/view/{id}/details', [
'as' => 'admin.servers.view.details',
'uses' => 'Admin\ServersController@postUpdateServerDetails',
]);

View File

@ -352,8 +352,9 @@ class ServerRepository
// Validate Fields
$validator = Validator::make($data, [
'owner' => 'email|exists:users,email',
'name' => 'regex:([\w .-]{1,200})',
'owner_id' => 'sometimes|required|numeric|exists:users,id',
'name' => 'sometimes|required|regex:([\w .-]{1,200})',
'reset_token' => 'sometimes|required|accepted'
]);
// Run validator, throw catchable and displayable exception if it fails.
@ -368,16 +369,15 @@ class ServerRepository
$server = Models\Server::with('user')->findOrFail($id);
// Update daemon secret if it was passed.
if ((isset($data['reset_token']) && $data['reset_token'] === true) || (isset($data['owner']) && $data['owner'] !== $server->user->email)) {
if (isset($data['reset_token']) || (isset($data['owner_id']) && $data['owner_id'] !== $server->user->id)) {
$oldDaemonKey = $server->daemonSecret;
$server->daemonSecret = $uuid->generate('servers', 'daemonSecret');
$resetDaemonKey = true;
}
// Update Server Owner if it was passed.
if (isset($data['owner']) && $data['owner'] !== $server->user->email) {
$newOwner = Models\User::select('id')->where('email', $data['owner'])->first();
$server->owner_id = $newOwner->id;
if (isset($data['owner_id']) && $data['owner_id'] !== $server->user->id) {
$server->owner_id = $data['owner_id'];
}
// Update Server Name if it was passed.
@ -431,7 +431,7 @@ class ServerRepository
public function updateContainer($id, array $data)
{
$validator = Validator::make($data, [
'image' => 'required|string',
'docker_image' => 'required|string',
]);
// Run validator, throw catchable and displayable exception if it fails.
@ -444,7 +444,7 @@ class ServerRepository
try {
$server = Models\Server::findOrFail($id);
$server->image = $data['image'];
$server->image = $data['docker_image'];
$server->save();
$server->node->guzzleClient([
@ -479,17 +479,14 @@ class ServerRepository
public function changeBuild($id, array $data)
{
$validator = Validator::make($data, [
'default' => [
'string',
'regex:/^(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5])):(\d{1,5})$/',
],
'add_additional' => 'nullable|array',
'remove_additional' => 'nullable|array',
'memory' => 'integer|min:0',
'swap' => 'integer|min:-1',
'io' => 'integer|min:10|max:1000',
'cpu' => 'integer|min:0',
'disk' => 'integer|min:0',
'allocation_id' => 'sometimes|required|exists:allocations,id',
'add_allocations' => 'sometimes|required|array',
'remove_allocations' => 'sometimes|required|array',
'memory' => 'sometimes|required|integer|min:0',
'swap' => 'sometimes|required|integer|min:-1',
'io' => 'sometimes|required|integer|min:10|max:1000',
'cpu' => 'sometimes|required|integer|min:0',
'disk' => 'sometimes|required|integer|min:0',
]);
// Run validator, throw catchable and displayable exception if it fails.
@ -503,43 +500,33 @@ class ServerRepository
try {
$server = Models\Server::with('allocation', 'allocations')->findOrFail($id);
$newBuild = [];
$newAllocations = [];
if (isset($data['default'])) {
list($ip, $port) = explode(':', $data['default']);
if ($ip !== $server->allocation->ip || (int) $port !== $server->allocation->port) {
$selection = $server->allocations->where('ip', $ip)->where('port', $port)->first();
if (isset($data['allocation_id'])) {
if ((int) $data['allocation_id'] !== $server->allocation_id) {
$selection = $server->allocations->where('id', $data['allocation_id'])->first();
if (! $selection) {
throw new DisplayException('The requested default connection (' . $ip . ':' . $port . ') is not allocated to this server.');
throw new DisplayException('The requested default connection is not allocated to this server.');
}
$server->allocation_id = $selection->id;
$newBuild['default'] = [
'ip' => $ip,
'port' => (int) $port,
];
$newBuild['default'] = ['ip' => $selection->ip, 'port' => $selection->port];
// Re-Run to keep updated for rest of function
$server->load('allocation');
}
}
$newPorts = false;
// Remove Assignments
if (isset($data['remove_additional'])) {
foreach ($data['remove_additional'] as $id => $combo) {
list($ip, $port) = explode(':', $combo);
// Invalid, not worth killing the whole thing, we'll just skip over it.
if (! filter_var($ip, FILTER_VALIDATE_IP) || ! preg_match('/^(\d{1,5})$/', $port)) {
break;
}
if (isset($data['remove_allocations'])) {
foreach ($data['remove_allocations'] as $allocation) {
// Can't remove the assigned IP/Port combo
if ($ip === $server->allocation->ip && (int) $port === (int) $server->allocation->port) {
break;
if ((int) $allocation === $server->allocation_id) {
continue;
}
$newPorts = true;
$server->allocations->where('ip', $ip)->where('port', $port)->update([
Models\Allocation::where('id', $allocation)->where('server_id', $server->id)->update([
'server_id' => null,
]);
}
@ -548,21 +535,15 @@ class ServerRepository
}
// Add Assignments
if (isset($data['add_additional'])) {
foreach ($data['add_additional'] as $id => $combo) {
list($ip, $port) = explode(':', $combo);
// Invalid, not worth killing the whole thing, we'll just skip over it.
if (! filter_var($ip, FILTER_VALIDATE_IP) || ! preg_match('/^(\d{1,5})$/', $port)) {
break;
}
// Don't allow double port assignments
if ($server->allocations->where('port', $port)->count() !== 0) {
break;
if (isset($data['add_allocations'])) {
foreach ($data['add_allocations'] as $allocation) {
$model = Models\Allocation::where('id', $allocation)->whereNull('server_id')->first();
if (! $model) {
continue;
}
$newPorts = true;
Models\Allocation::where('ip', $ip)->where('port', $port)->whereNull('server_id')->update([
$model->update([
'server_id' => $server->id,
]);
}
@ -570,18 +551,10 @@ class ServerRepository
$server->load('allocations');
}
// Loop All Assignments
$additionalAssignments = [];
foreach ($server->allocations as &$assignment) {
if (array_key_exists((string) $assignment->ip, $additionalAssignments)) {
array_push($additionalAssignments[(string) $assignment->ip], (int) $assignment->port);
} else {
$additionalAssignments[(string) $assignment->ip] = [(int) $assignment->port];
}
}
if ($newPorts === true) {
$newBuild['ports|overwrite'] = $additionalAssignments;
if ($newPorts) {
$newBuild['ports|overwrite'] = $server->allocations->groupBy('ip')->map(function ($item) {
return $item->pluck('port');
})->toArray();
}
// @TODO: verify that server can be set to this much memory without
@ -617,6 +590,7 @@ class ServerRepository
// This won't be committed unless the HTTP request succeedes anyways
$server->save();
dd($newBuild);
if (! empty($newBuild)) {
$server->node->guzzleClient([
'X-Access-Server' => $server->uuid,
@ -630,7 +604,7 @@ class ServerRepository
DB::commit();
return true;
return $server;
} catch (TransferException $ex) {
DB::rollBack();
throw new DisplayException('An error occured while attempting to update the configuration.', $ex);

View File

@ -198,3 +198,29 @@ span[aria-labelledby="select2-pUserId-container"] {
color: #777 !important;
background: transparent !important;
}
.tab-pane .box-footer {
margin: 0 -10px -10px;
}
.select2-container{ width: 100% !important; }
.nav-tabs-custom > .nav-tabs > li:hover {
border-top-color:#3c8dbc;
}
.nav-tabs-custom > .nav-tabs > li.active.tab-danger, .nav-tabs-custom > .nav-tabs > li.tab-danger:hover {
border-top-color: #c23321;
}
.nav-tabs-custom > .nav-tabs > li.active.tab-success, .nav-tabs-custom > .nav-tabs > li.tab-success:hover {
border-top-color: #00733e;
}
.nav-tabs-custom > .nav-tabs > li.active.tab-info, .nav-tabs-custom > .nav-tabs > li.tab-info:hover {
border-top-color: #0097bc;
}
.nav-tabs-custom > .nav-tabs > li.active.tab-warning, .nav-tabs-custom > .nav-tabs > li.tab-warning:hover {
border-top-color: #c87f0a;
}

View File

@ -0,0 +1,390 @@
{{-- Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com> --}}
{{-- Permission is hereby granted, free of charge, to any person obtaining a copy --}}
{{-- of this software and associated documentation files (the "Software"), to deal --}}
{{-- in the Software without restriction, including without limitation the rights --}}
{{-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell --}}
{{-- copies of the Software, and to permit persons to whom the Software is --}}
{{-- furnished to do so, subject to the following conditions: --}}
{{-- The above copyright notice and this permission notice shall be included in all --}}
{{-- copies or substantial portions of the Software. --}}
{{-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR --}}
{{-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, --}}
{{-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE --}}
{{-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER --}}
{{-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, --}}
{{-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE --}}
{{-- SOFTWARE. --}}
@extends('layouts.admin')
@section('title')
Manage Server: {{ $server->name }}
@endsection
@section('content-header')
<h1>{{ $server->name }}<small>{{ $server->uuid }}</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->username }}</li>
</ol>
@endsection
@section('content')
@if($server->suspended && ! $server->trashed())
<div class="alert alert-warning">
This server is suspended and has no user access. Processes cannot be started and files cannot be modified. All API access is disabled unless using a master token.
</div>
@elseif($server->trashed())
<div class="callout callout-danger">
This server is marked for deletion <strong>{{ Carbon::parse($server->deleted_at)->addMinutes(env('APP_DELETE_MINUTES', 10))->diffForHumans() }}</strong>. If you want to cancel this action simply click the button below.
<br /><br />
<form action="{{ route('admin.servers.post.queuedDeletion', $server->id) }}" method="POST">
<button class="btn btn-sm btn-default" name="cancel" value="1">Cancel Deletion</button>
<button class="btn btn-sm btn-danger pull-right" name="force_delete" value="1"><strong>Force</strong> Delete</button>
<button class="btn btn-sm btn-danger pull-right" name="delete" style="margin-right:10px;" value="1">Delete</button>
{!! csrf_field() !!}
</form>
</div>
@endif
@if(! $server->installed)
<div class="alert alert-warning">
This server is still running through the install process and is not avaliable for use just yet. This message will disappear once this process is completed.
</div>
@elseif($server->installed === 2)
<div class="alert alert-danger">
This server <strong>failed</strong> to install properly. You should delete it and try to create it again or check the daemon logs.
</div>
@endif
<div class="row">
<div class="col-xs-12">
<div class="nav-tabs-custom">
<ul class="nav nav-tabs">
<li class="active"><a href="#tab_about" data-toggle="tab">About</a></li>
@if($server->installed)
<li><a href="#tab_details" data-toggle="tab">Details</a></li>
<li><a href="#tab_build" data-toggle="tab">Build Configuration</a></li>
<li><a href="#tab_startup" data-toggle="tab">Startup</a></li>
<li><a href="#tab_database" data-toggle="tab">Database</a></li>
@endif
@if($server->installed !== 2)
<li><a href="#tab_manage" data-toggle="tab">Manage</a></li>
@endif
@if(! $server->trashed())
<li class="tab-danger"><a href="#tab_delete" data-toggle="tab">Delete</a></li>
@endif
</ul>
<div class="tab-content">
{{-- Start About --}}
<div class="tab-pane active" id="tab_about" style="margin: -10px -10px -30px;">
<table class="table table-hover">
<tr>
<td>UUID</td>
<td>{{ $server->uuid }}</td>
</tr>
<tr>
<td>Docker Container ID</td>
<td data-attr="container-id"><i class="fa fa-fw fa-refresh fa-spin"></i></td>
</tr>
<tr>
<td>Docker User ID</td>
<td data-attr="container-user"><i class="fa fa-fw fa-refresh fa-spin"></i></td>
</tr>
<tr>
<td>Owner</td>
<td><a href="{{ route('admin.users.view', $server->owner_id) }}">{{ $server->user->email }}</a></td>
</tr>
<tr>
<td>Location</td>
<td><a href="{{ route('admin.locations') }}">{{ $server->node->location->short }}</a></td>
</tr>
<tr>
<td>Node</td>
<td><a href="{{ route('admin.nodes.view', $server->node_id) }}">{{ $server->node->name }}</a></td>
</tr>
<tr>
<td>Service</td>
<td>{{ $server->option->service->name }} :: {{ $server->option->name }}</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><abbr title="Out of Memory">OOM</abbr> Killer</td>
<td>{!! ($server->oom_disabled === 0) ? '<span class="label label-success">Enabled</span>' : '<span class="label label-default">Disabled</span>' !!}</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>
<tr>
<td>Installed</td>
<td>{!! ($server->installed === 1) ? '<span class="label label-success">Yes</span>' : '<span class="label label-danger">No</span>' !!}</td>
</tr>
<tr>
<td>Suspended</td>
<td>{!! ($server->suspended === 1) ? '<span class="label label-warning">Suspended</span>' : '<span class="label label-success">No</span>' !!}</td>
</tr>
</table>
</div>
{{-- End About / Start Details --}}
@if($server->installed)
<div class="tab-pane" id="tab_details">
<div class="row">
<form class="col-sm-6" action="{{ route('admin.servers.view.details', $server->id) }}" method="POST" style="border-right: 1px solid #f4f4f4;">
<div class="form-group">
<label for="name" class="control-label">Server Name</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="pUserId" class="control-label">Server Owner</label>
<select name="owner_id" class="form-control" id="pUserId">
<option value="{{ $server->owner_id }}" selected>{{ $server->user->email }}</option>
</select>
{{-- <input type="text" name="owner" value="{{ old('owner', $server->user->email) }}" class="form-control" /> --}}
<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="name" class="control-label">Daemon Secret Token</label>
<input type="text" disabled value="{{ $server->daemonSecret }}" class="form-control" />
<p class="text-muted small">This token should not be shared with anyone as it has full control over this server.</p>
</div>
<div class="form-group">
<input type="checkbox" name="reset_token" id="pResetToken"/> <label for="pResetToken">Reset Daemon Token</label>
<p class="text-muted small">Resetting this token will cause any requests using the old token to fail.</p>
</div>
<div class="box-footer">
{!! csrf_field() !!}
<input type="submit" class="btn btn-sm btn-primary" value="Update Details" />
</div>
</form>
<form class="col-sm-6" action="{{ route('admin.servers.post.container', $server->id) }}" method="POST">
<div class="form-group">
<label for="name" class="control-label">Docker Container Image</label>
<input type="text" name="docker_image" value="{{ $server->image }}" class="form-control" />
<p class="text-muted small">The docker image to use for this server. The default image for this service and option combination is <code>{{ $server->option->docker_image }}</code>.</p>
</div>
<div class="box-footer">
{!! csrf_field() !!}
<input type="submit" class="btn btn-sm btn-primary" value="Update Docker Container" />
</div>
</form>
</div>
</div>
{{-- End Details / Start Build --}}
<div class="tab-pane" id="tab_build">
<form action="/admin/servers/view/{{ $server->id }}/build" method="POST">
<div class="row">
<div class="col-md-3 col-sm-6 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.</p>
</div>
<div class="col-md-3 col-sm-6 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="col-md-3 col-sm-6 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="col-md-3 col-sm-6 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>
<hr />
<div class="row">
<div class="col-md-4 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="col-md-4 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="col-md-4 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 }}" @if($server->allocation_id === $assignment->id)disabled @endif>{{ $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() !!}
<input type="submit" class="btn btn-sm btn-primary" value="Update Build Configuration" />
</div>
</form>
</div>
{{-- End Build / Start Startup --}}
<div class="tab-pane" id="tab_startup">
Startup
</div>
{{-- End Startup / Start Database --}}
<div class="tab-pane" id="tab_database">
Database
</div>
@endif
{{-- End Database / Start Manage --}}
@if($server->installed !== 2)
<div class="tab-pane" id="tab_manage">
Manage
</div>
@endif
{{-- End Manage / Start Delete --}}
@if(! $server->trashed())
<div class="tab-pane" id="tab_delete">
Delete
</div>
@endif
{{-- End Delete --}}
</div>
</div>
</div>
</div>
@endsection
@section('footer-scripts')
@parent
<script>
$('#pAddAllocations').select2();
$('#pRemoveAllocations').select2();
$('#pAllocation').select2();
$('#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>';
}
});
(function checkServerInfo() {
$.ajax({
type: 'GET',
headers: {
'X-Access-Token': '{{ $server->daemonSecret }}',
'X-Access-Server': '{{ $server->uuid }}'
},
url: '{{ $server->node->scheme }}://{{ $server->node->fqdn }}:{{ $server->node->daemonListen }}/server',
dataType: 'json',
timeout: 5000,
}).done(function (data) {
$('td[data-attr="container-id"]').html('<code>' + data.container.id + '</code>');
$('td[data-attr="container-user"]').html('<code>' + data.user + '</code>');
}).fail(function (jqXHR) {
$('td[data-attr="container-id"]').html('<code>error</code>');
$('td[data-attr="container-user"]').html('<code>error</code>');
console.error(jqXHR);
}).always(function () {
setTimeout(checkServerInfo, 60000);
})
})();
</script>
@endsection

View File

@ -67,7 +67,7 @@
<a href="#" data-action="control-sidebar" data-toggle="tooltip" data-placement="bottom" title="Quick Access"><i class="fa fa-fighter-jet" style="margin-top:4px;padding-bottom:2px;"></i></a>
</li>
<li>
<li><a href="{{ route('admin.index') }}" data-toggle="tooltip" data-placement="bottom" title="Exit Admin Control"><i class="fa fa-server" style="margin-top:4px;padding-bottom:2px;"></i></a></li>
<li><a href="{{ route('index') }}" data-toggle="tooltip" data-placement="bottom" title="Exit Admin Control"><i class="fa fa-server" style="margin-top:4px;padding-bottom:2px;"></i></a></li>
</li>
<li>
<li><a href="{{ route('auth.logout') }}" data-toggle="tooltip" data-placement="bottom" title="Logout"><i class="fa fa-power-off" style="margin-top:4px;padding-bottom:2px;"></i></a></li>