Whats this? We can add new servers now?!

This commit is contained in:
Dane Everitt 2015-12-15 15:08:41 -05:00
parent 6289e7ae8d
commit 264431a271
10 changed files with 214 additions and 85 deletions

View file

@ -4,7 +4,9 @@ namespace Pterodactyl\Exceptions;
use Exception;
use DisplayException;
use Debugbar;
use DisplayValidationException;
use AccountNotFoundException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

View file

@ -2,15 +2,14 @@
namespace Pterodactyl\Http\Controllers\Admin;
use Alert;
use Debugbar;
use Pterodactyl\Models;
use Pterodactyl\Repositories\ServerRepository;
use Pterodactyl\Models\Server;
use Pterodactyl\Models\Node;
use Pterodactyl\Models\Location;
use Pterodactyl\Models\Allocation;
use Pterodactyl\Models\Service;
use Pterodactyl\Models\ServiceOptions;
use Pterodactyl\Models\ServiceVariables;
use Pterodactly\Exceptions\DisplayException;
use Pterodactly\Exceptions\DisplayValidationException;
use Pterodactyl\Http\Controllers\Controller;
use Illuminate\Http\Request;
@ -33,7 +32,7 @@ class ServersController extends Controller
public function getIndex(Request $request)
{
return view('admin.servers.index', [
'servers' => Server::select('servers.*', 'nodes.name as a_nodeName', 'users.email as a_ownerEmail')
'servers' => Models\Server::select('servers.*', 'nodes.name as a_nodeName', 'users.email as a_ownerEmail')
->join('nodes', 'servers.node', '=', 'nodes.id')
->join('users', 'servers.owner', '=', 'users.id')
->paginate(20),
@ -43,8 +42,8 @@ class ServersController extends Controller
public function getNew(Request $request)
{
return view('admin.servers.new', [
'locations' => Location::all(),
'services' => Service::all()
'locations' => Models\Location::all(),
'services' => Models\Service::all()
]);
}
@ -57,14 +56,26 @@ class ServersController extends Controller
{
try {
$server = new ServerRepository;
$resp = $server->create($request->all());
echo $resp . '<br />';
} catch (\Exception $e) {
Debugbar::addException($e);
}
return json_encode($request->all());
$server = new ServerRepository;
$response = $server->create($request->all());
return redirect()->route('admin.servers.view', [ 'id' => $response ]);
} catch (\Exception $e) {
if ($e instanceof \Pterodactyl\Exceptions\DisplayValidationException) {
return redirect()->route('admin.servers.new')->withErrors(json_decode($e->getMessage()))->withInput();
} else if ($e instanceof \Pterodactyl\Exceptions\DisplayException) {
Alert::danger($e->getMessage())->flash();
} else {
Debugbar::addException($e);
Alert::danger('An unhandled exception occured while attemping to add this server. Please try again.')->flash();
}
return redirect()->route('admin.servers.new')->withInput();
}
}
@ -83,7 +94,7 @@ class ServersController extends Controller
], 500);
}
return response()->json(Node::select('id', 'name', 'public')->where('location', $request->input('location'))->get());
return response()->json(Models\Node::select('id', 'name', 'public')->where('location', $request->input('location'))->get());
}
@ -102,7 +113,7 @@ class ServersController extends Controller
], 500);
}
$ips = Allocation::where('node', $request->input('node'))->whereNull('assigned_to')->get();
$ips = Models\Allocation::where('node', $request->input('node'))->whereNull('assigned_to')->get();
$listing = [];
foreach($ips as &$ip) {
@ -131,7 +142,7 @@ class ServersController extends Controller
], 500);
}
return response()->json(ServiceOptions::select('id', 'name', 'docker_image')->where('parent_service', $request->input('service'))->orderBy('name', 'asc')->get());
return response()->json(Models\ServiceOptions::select('id', 'name', 'docker_image')->where('parent_service', $request->input('service'))->orderBy('name', 'asc')->get());
}
@ -150,7 +161,7 @@ class ServersController extends Controller
], 500);
}
return response()->json(ServiceVariables::where('option_id', $request->input('option'))->get());
return response()->json(Models\ServiceVariables::where('option_id', $request->input('option'))->get());
}

View file

@ -24,6 +24,13 @@ class Server extends Model
*/
protected $hidden = ['daemonSecret'];
/**
* Fields that are not mass assignable.
*
* @var array
*/
protected $guarded = ['id', 'installed', 'created_at', 'updated_at'];
/**
* @var array
*/

View file

@ -14,4 +14,11 @@ class ServerVariables extends Model
*/
protected $table = 'server_variables';
/**
* Fields that are not mass assignable.
*
* @var array
*/
protected $guarded = ['id', 'created_at', 'updated_at'];
}

View file

@ -3,6 +3,7 @@
namespace Pterodactyl\Repositories;
use DB;
use Debugbar;
use Validator;
use Pterodactyl\Models;
@ -64,7 +65,7 @@ class ServerRepository
// Run validator, throw catchable and displayable exception if it fails.
// Exception includes a JSON result of failed validation rules.
if ($validator->fails()) {
throw new DisplayValidationException(json_encode($validator->errors()->all()));
throw new DisplayValidationException($validator->errors());
}
// Get the User ID; user exists since we passed the 'exists:users,email' part of the validation
@ -91,6 +92,7 @@ class ServerRepository
// Check those Variables
$variables = Models\ServiceVariables::where('option_id', $data['option'])->get();
$variableList = [];
if ($variables) {
foreach($variables as $variable) {
@ -100,7 +102,11 @@ class ServerRepository
throw new DisplayException('A required service option variable field (env_' . $variable->env_variable . ') was missing from the request.');
}
$data['env_' . $variable->env_variable] = $variable->default_value;
$variableList = array_merge($variableList, [[
'var_id' => $variable->id,
'var_val' => $variable->default_value
]]);
continue;
}
@ -109,13 +115,91 @@ class ServerRepository
throw new DisplayException('Failed to validate service option variable field (env_' . $variable->env_variable . ') aganist regex (' . $variable->regex . ').');
}
continue;
$variableList = array_merge($variableList, [[
'var_id' => $variable->id,
'var_val' => $data['env_' . $variable->env_variable]
]]);
continue;
}
}
return (new UuidService)->generateShort();
//return $this->generateSFTPUsername($data['name']);
// Check Overallocation
if (is_numeric($node->memory_overallocate) || is_numeric($node->disk_overallocate)) {
$totals = Models\Server::select(DB::raw('SUM(memory) as memory, SUM(disk) as disk'))->where('node', $node->id)->first();
// Check memory limits
if (is_numeric($node->memory_overallocate)) {
$newMemory = $totals->memory + $data['memory'];
$memoryLimit = ($node->memory * (1 + ($node->memory_overallocate / 100)));
if($newMemory > $memoryLimit) {
throw new DisplayException('The amount of memory allocated to this server would put the node over its allocation limits. This node is allowed ' . ($node->memory_overallocate + 100) . '% of its assigned ' . $node->memory . 'Mb of memory (' . $memoryLimit . 'Mb) of which ' . (($totals->memory / $node->memory) * 100) . '% (' . $totals->memory . 'Mb) is in use already. By allocating this server the node would be at ' . (($newMemory / $node->memory) * 100) . '% (' . $newMemory . 'Mb) usage.');
}
}
// Check Disk Limits
if (is_numeric($node->disk_overallocate)) {
$newDisk = $totals->disk + $data['disk'];
$diskLimit = ($node->disk * (1 + ($node->disk_overallocate / 100)));
if($newDisk > $diskLimit) {
throw new DisplayException('The amount of disk allocated to this server would put the node over its allocation limits. This node is allowed ' . ($node->disk_overallocate + 100) . '% of its assigned ' . $node->disk . 'Mb of disk (' . $diskLimit . 'Mb) of which ' . (($totals->disk / $node->disk) * 100) . '% (' . $totals->disk . 'Mb) is in use already. By allocating this server the node would be at ' . (($newDisk / $node->disk) * 100) . '% (' . $newDisk . 'Mb) usage.');
}
}
}
DB::beginTransaction();
$uuid = new UuidService;
// Add Server to the Database
$server = new Models\Server;
$server->fill([
'uuid' => $uuid->generate('servers', 'uuid'),
'uuidShort' => $uuid->generateShort(),
'node' => $data['node'],
'name' => $data['name'],
'active' => 1,
'owner' => $user->id,
'memory' => $data['memory'],
'disk' => $data['disk'],
'io' => $data['io'],
'cpu' => $data['cpu'],
'ip' => $data['ip'],
'port' => $data['port'],
'service' => $data['service'],
'option' => $data['option'],
'daemonSecret' => $uuid->generate('servers', 'daemonSecret'),
'username' => $this->generateSFTPUsername($data['name'])
]);
$server->save();
// Mark Allocation in Use
$allocation->assigned_to = $server->id;
$allocation->save();
// Add Variables
foreach($variableList as $item) {
Models\ServerVariables::create([
'server_id' => $server->id,
'variable_id' => $item['var_id'],
'variable_value' => $item['var_val']
]);
}
try {
// Add logic for communicating with Wings to make the server in here.
// We should add the server regardless of the Wings response, but
// handle the error and then allow the server to be re-deployed.
DB::commit();
return $server->id;
} catch (\Exception $e) {
DB::rollBack();
throw $e;
}
}

View file

@ -29,7 +29,7 @@ class UserRepository
$user = new User;
$uuid = new UuidService;
$user->uuid = $uuid->table('users')->generate();
$user->uuid = $uuid->generate('users', 'uuid');
$user->username = $username;
$user->email = $email;

View file

@ -8,16 +8,6 @@ use Uuid;
class UuidService
{
/**
* @var string
*/
protected $table = 'users';
/**
* @var string
*/
protected $field = 'uuid';
/**
* Constructor
*/
@ -26,45 +16,23 @@ class UuidService
//
}
/**
* Set the table that we need to be checking in the database.
*
* @param string $table
* @return void
*/
public function table($table)
{
$this->table = $table;
return $this;
}
/**
* Set the field in the given table that we want to check for a unique UUID.
*
* @param string $field
* @return void
*/
public function field($field)
{
$this->field = $field;
return $this;
}
/**
* Generate a unique UUID validating against specified table and column.
* Defaults to `users.uuid`
*
* @param string $table
* @param string $field
* @param integer $type The type of UUID to generate.
* @return string
*/
public function generate($type = 4)
public function generate($table = 'users', $field = 'uuid', $type = 4)
{
$return = false;
do {
$uuid = LaravelUUID::generate($type);
if (!DB::table($this->table)->where($this->field, $uuid)->exists()) {
$uuid = Uuid::generate($type);
if (!DB::table($table)->where($field, $uuid)->exists()) {
$return = $uuid;
}
@ -81,13 +49,15 @@ class UuidService
* @param string $field
* @return string
*/
public function generateShort($table = 'servers', $field = 'uuidShort')
public function generateShort($table = 'servers', $field = 'uuidShort', $attachedUuid = null)
{
$return = false;
do {
$short = substr(Uuid::generate(4), 0, 8);
$short = (is_null($attachedUuid)) ? substr(Uuid::generate(4), 0, 8) : substr($attachedUuid, 0, 8);
$attachedUuid = null;
if (!DB::table($table)->where($field, $short)->exists()) {
$return = $short;
}

View file

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class RemoveDaemonTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::dropIfExists('daemon');
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::create('daemon', function (Blueprint $table) {
$table->increments('id')->unsigned();
$table->mediumInteger('server')->unsigned();
$table->string('parameter');
$table->text('value');
$table->tinyInteger('editable')->unsigned()->default(0);
$table->tinyInteger('visible')->unsigned()->default(0);
$table->text('regex')->nullable();
$table->timestamps();
});
}
}

View file

@ -12,6 +12,8 @@ return [
|
*/
'validation_error' => 'An error occured while validating the data you submitted:',
'failed' => 'These credentials do not match our records.',
'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
'view_as_admin' => 'You are viewing this server listing as an admin. As such, all servers installed on the system are displayed. Any servers that you are set as the owner of are marked with a blue dot to the left of their name.',

View file

@ -11,11 +11,21 @@
<li><a href="/admin/servers">Servers</a></li>
<li class="active">Create New Server</li>
</ul>
@if (count($errors) > 0)
<div class="alert alert-danger">
<strong>{{ trans('strings.whoops') }}!</strong> {{ trans('base.validation_error') }}<br><br>
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
@foreach (Alert::getMessages() as $type => $messages)
@foreach ($messages as $message)
<div class="alert alert-{{ $type }} alert-dismissable" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
{{ $message }}
{!! $message !!}
</div>
@endforeach
@endforeach
@ -26,14 +36,14 @@
<div class="form-group col-md-6">
<label for="name" class="control-label">Server Name</label>
<div>
<input type="text" autocomplete="off" name="name" class="form-control" />
<input type="text" autocomplete="off" name="name" class="form-control" value="{{ old('name') }}" />
<p class="text-muted"><small><em>Character limits: <code>a-zA-Z0-9_-</code> and <code>[Space]</code> (max 35 characters)</em></small></p>
</div>
</div>
<div class="form-group col-md-6">
<label for="owner" class="control-label">Owner Email</label>
<div>
<input type="text" autocomplete="off" name="owner" class="form-control" />
<input type="text" autocomplete="off" name="owner" class="form-control" value="{{ old('owner') }}" />
</div>
</div>
</div>
@ -46,7 +56,7 @@
<label for="location" class="control-label">Server Location</label>
<div>
<select name="location" id="getLocation" class="form-control">
<option></option>
<option disabled selected> -- Select a Location</option>
@foreach($locations as $location)
<option value="{{ $location->id }}">{{ $location->long }} ({{ $location->short }})</option>
@endforeach
@ -58,7 +68,7 @@
<label for="node" class="control-label">Server Node</label>
<div>
<select name="node" id="getNode" class="form-control">
<option></option>
<option disabled selected> -- Select a Node</option>
</select>
<p class="text-muted"><small>The node which this server will be deployed to.</small></p>
</div>
@ -69,7 +79,7 @@
<label for="ip" class="control-label">Server IP</label>
<div>
<select name="ip" id="getIP" class="form-control">
<option></option>
<option disabled selected> -- Select an IP</option>
</select>
<p class="text-muted"><small>Select the main IP that this server will be listening on. You can assign additional open IPs and ports below.</small></p>
</div>
@ -89,28 +99,28 @@
<div class="form-group col-md-3 col-xs-6">
<label for="memory" class="control-label">Memory</label>
<div class="input-group">
<input type="text" name="memory" class="form-control" />
<input type="text" name="memory" class="form-control" value="{{ old('memory') }}"/>
<span class="input-group-addon">MB</span>
</div>
</div>
<div class="form-group col-md-3 col-xs-6">
<label for="disk" class="control-label">Disk Space</label>
<div class="input-group">
<input type="text" name="disk" class="form-control" />
<input type="text" name="disk" class="form-control" value="{{ old('disk') }}"/>
<span class="input-group-addon">MB</span>
</div>
</div>
<div class="form-group col-md-3 col-xs-6">
<label for="cpu" class="control-label">CPU Limit</label>
<div class="input-group">
<input type="text" name="cpu" value="0" class="form-control" />
<input type="text" name="cpu" value="0" class="form-control" value="{{ old('cpu') }}"/>
<span class="input-group-addon">%</span>
</div>
</div>
<div class="form-group col-md-3 col-xs-6">
<label for="io" class="control-label">Block I/O</label>
<div class="input-group">
<input type="text" name="io" value="500" class="form-control" />
<input type="text" name="io" value="500" class="form-control" value="{{ old('io') }}"/>
<span class="input-group-addon">I/O</span>
</div>
</div>
@ -130,7 +140,7 @@
<label for="service" class="control-label">Service Type</label>
<div>
<select name="service" id="getService" class="form-control">
<option></option>
<option disabled selected> -- Select a Service</option>
@foreach($services as $service)
<option value="{{ $service->id }}">{{ $service->name }}</option>
@endforeach
@ -142,7 +152,7 @@
<label for="option" class="control-label">Service Option</label>
<div>
<select name="option" id="getOption" class="form-control">
<option></option>
<option disabled selected> -- Select a Service Option</option>
</select>
<p class="text-muted"><small>Select the type of service that this server will be running.</small></p>
</div>
@ -157,9 +167,9 @@
<label for="use_custom_image" class="control-label">Use Custom Docker Image</label>
<div class="input-group">
<span class="input-group-addon">
<input type="checkbox" name="use_custom_image" />
<input @if(old('name') === 'use_custom_image')checked="checked"@endif type="checkbox" name="use_custom_image"/>
</span>
<input type="text" class="form-control" name="custom_image_name" disabled />
<input type="text" class="form-control" name="custom_image_name" value="{{ old('custom_image_name') }}" disabled />
</div>
<p class="text-muted"><small>If you would like to use a custom docker image for this server please enter it here. Most users can ignore this option.</small></p>
</div>
@ -210,8 +220,8 @@ $(document).ready(function () {
currentNode = null;
// Hide Existing, and Reset contents
$('#getNode').html('<option></option>').parent().parent().addClass('hidden');
$('#getIP').html('<option></option>').parent().parent().addClass('hidden');
$('#getNode').html('<option disabled selected> -- Select a Node</option>').parent().parent().addClass('hidden');
$('#getIP').html('<option disabled selected> -- Select an IP</option>').parent().parent().addClass('hidden');
$('#getPort').html('').parent().parent().addClass('hidden');
handleLoader('#load_settings', true);
@ -249,7 +259,7 @@ $(document).ready(function () {
currentNode = $('#getNode').val();
// Hide Existing, and Reset contents
$('#getIP').html('<option></option>').parent().parent().addClass('hidden');
$('#getIP').html('<option disabled selected> -- Select an IP</option>').parent().parent().addClass('hidden');
$('#getPort').html('').parent().parent().addClass('hidden');
handleLoader('#load_settings', true);