New models for node and location admin pages.

This commit is contained in:
Dane Everitt 2017-02-03 16:50:28 -05:00
parent 96d3aa767f
commit 09d23deed6
No known key found for this signature in database
GPG Key ID: EEA66103B3D71F53
10 changed files with 152 additions and 78 deletions

View File

@ -45,8 +45,8 @@ class LocationsController extends Controller
return view('admin.locations.index', [
'locations' => Models\Location::select(
'locations.*',
DB::raw('(SELECT COUNT(*) FROM nodes WHERE nodes.location = locations.id) as a_nodeCount'),
DB::raw('(SELECT COUNT(*) FROM servers WHERE servers.node_id IN (SELECT nodes.id FROM nodes WHERE nodes.location = locations.id)) as a_serverCount')
DB::raw('(SELECT COUNT(*) FROM nodes WHERE nodes.location_id = locations.id) as a_nodeCount'),
DB::raw('(SELECT COUNT(*) FROM servers WHERE servers.node_id IN (SELECT nodes.id FROM nodes WHERE nodes.location_id = locations.id)) as a_serverCount')
)->paginate(20),
]);
}
@ -55,8 +55,8 @@ class LocationsController extends Controller
{
$model = Models\Location::select(
'locations.id',
DB::raw('(SELECT COUNT(*) FROM nodes WHERE nodes.location = locations.id) as a_nodeCount'),
DB::raw('(SELECT COUNT(*) FROM servers WHERE servers.node_id IN (SELECT nodes.id FROM nodes WHERE nodes.location = locations.id)) as a_serverCount')
DB::raw('(SELECT COUNT(*) FROM nodes WHERE nodes.location_id = locations.id) as a_nodeCount'),
DB::raw('(SELECT COUNT(*) FROM servers WHERE servers.node_id IN (SELECT nodes.id FROM nodes WHERE nodes.location_id = locations.id)) as a_serverCount')
)->where('id', $id)->first();
if (! $model) {
@ -80,12 +80,12 @@ class LocationsController extends Controller
{
try {
$location = new LocationRepository;
$location->edit($id, $request->all());
$location->edit($id, $request->only(['long', 'short']));
return response('', 204);
} catch (DisplayValidationException $ex) {
return response()->json([
'error' => 'There was a validation error while processing this request. Location descriptions must be between 1 and 255 characters, and the location code must be between 1 and 10 characters with no spaces or special characters.',
'error' => 'There was a validation error while processing this request. Location descriptions must be between 1 and 255 characters, and the location code must be between 1 and 20 characters with no spaces or special characters.',
], 422);
} catch (\Exception $ex) {
// This gets caught and processed into JSON anyways.
@ -97,9 +97,7 @@ class LocationsController extends Controller
{
try {
$location = new LocationRepository;
$id = $location->create($request->except([
'_token',
]));
$id = $location->create($request->only(['long', 'short']));
Alert::success('New location successfully added.')->flash();
return redirect()->route('admin.locations');

View File

@ -48,17 +48,15 @@ class NodesController extends Controller
public function getScript(Request $request, $id)
{
return response()->view('admin.nodes.remote.deploy', ['node' => Models\Node::findOrFail($id)])->header('Content-Type', 'text/plain');
return response()->view('admin.nodes.remote.deploy', [
'node' => Models\Node::findOrFail($id),
])->header('Content-Type', 'text/plain');
}
public function getIndex(Request $request)
{
return view('admin.nodes.index', [
'nodes' => Models\Node::select(
'nodes.*',
'locations.long as a_locationName',
DB::raw('(SELECT COUNT(*) FROM servers WHERE servers.node_id = nodes.id) as a_serverCount')
)->join('locations', 'nodes.location', '=', 'locations.id')->paginate(20),
'nodes' => Models\Node::with('location')->withCount('servers')->paginate(20),
]);
}
@ -78,15 +76,25 @@ class NodesController extends Controller
public function postNew(Request $request)
{
try {
$node = new NodeRepository;
$new = $node->create($request->except([
'_token',
$repo = new NodeRepository;
$node = $repo->create($request->only([
'name',
'location',
'public',
'fqdn',
'scheme',
'memory',
'memory_overallocate',
'disk',
'disk_overallocate',
'daemonBase',
'daemonSFTP',
'daemonListen',
]));
Alert::success('Successfully created new node. <strong>Before you can add any servers you need to first assign some IP addresses and ports.</strong>')->flash();
Alert::info('<strong>To simplify the node setup you can generate a token on the configuration tab.</strong>')->flash();
Alert::success('Successfully created new node that can be configured automatically on your remote machine by visiting the configuration tab. <strong>Before you can add any servers you need to first assign some IP addresses and ports.</strong>')->flash();
return redirect()->route('admin.nodes.view', [
'id' => $new,
'id' => $node->id,
'tab' => 'tab_allocation',
]);
} catch (DisplayValidationException $e) {
@ -103,26 +111,18 @@ class NodesController extends Controller
public function getView(Request $request, $id)
{
$node = Models\Node::findOrFail($id);
$node = Models\Node::with(
'servers.user',
'servers.service',
'servers.allocations',
'location'
)->findOrFail($id);
$node->setRelation('allocations', $node->allocations()->paginate(40));
return view('admin.nodes.view', [
'node' => $node,
'servers' => Models\Server::select('servers.*', 'users.email as a_ownerEmail', 'services.name as a_serviceName')
->join('users', 'users.id', '=', 'servers.owner_id')
->join('services', 'services.id', '=', 'servers.service_id')
->where('node_id', $id)->paginate(10, ['*'], 'servers'),
'stats' => Models\Server::select(DB::raw('SUM(memory) as memory, SUM(disk) as disk'))->where('node_id', $node->id)->first(),
'locations' => Models\Location::all(),
'allocations' => Models\Allocation::select('allocations.*', 'servers.name as assigned_to_name')
->where('allocations.node', $node->id)
->leftJoin('servers', 'servers.id', '=', 'allocations.assigned_to')
->orderBy('allocations.ip', 'asc')
->orderBy('allocations.port', 'asc')
->paginate(20, ['*'], 'allocations'),
'allocation_ips' => Models\Allocation::select('id', 'ip')
->where('node', $node->id)
->groupBy('ip')
->get(),
]);
}
@ -130,8 +130,21 @@ class NodesController extends Controller
{
try {
$node = new NodeRepository;
$node->update($id, $request->except([
'_token',
$node->update($id, $request->only([
'name',
'location',
'public',
'fqdn',
'scheme',
'memory',
'memory_overallocate',
'disk',
'disk_overallocate',
'upload_size',
'daemonBase',
'daemonSFTP',
'daemonListen',
'reset_secret',
]));
Alert::success('Successfully update this node\'s information. If you changed any daemon settings you will need to restart it now.')->flash();

View File

@ -48,8 +48,8 @@ class Allocation extends Model
* @var array
*/
protected $casts = [
'node' => 'integer',
'node_id' => 'integer',
'port' => 'integer',
'assigned_to' => 'integer',
'server_id' => 'integer',
];
}

View File

@ -226,4 +226,24 @@ class Node extends Model
{
return $this->hasOne(Location::class, 'id', 'location_id');
}
/**
* Gets the servers associated with a node.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function servers()
{
return $this->hasMany(Server::class);
}
/**
* Gets the allocations associated with a node.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function allocations()
{
return $this->hasMany(Allocation::class);
}
}

View File

@ -121,7 +121,7 @@ class Server extends Model
'services.name as a_serviceName',
'service_options.name as a_serviceOptionName'
)->join('nodes', 'servers.node_id', '=', 'nodes.id')
->join('locations', 'nodes.location', '=', 'locations.id')
->join('locations', 'nodes.location_id', '=', 'locations.id')
->join('services', 'servers.service_id', '=', 'services.id')
->join('service_options', 'servers.option_id', '=', 'service_options.id')
->join('allocations', 'servers.allocation_id', '=', 'allocations.id');
@ -218,6 +218,16 @@ class Server extends Model
return Javascript::put($response);
}
/**
* Gets the user who owns the server.
*
* @return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function user()
{
return $this->hasOne(User::class, 'id', 'owner_id');
}
/**
* Gets all allocations associated with this server.
*
@ -225,7 +235,7 @@ class Server extends Model
*/
public function allocations()
{
return $this->hasMany(Allocation::class, 'assigned_to');
return $this->hasMany(Allocation::class, 'server_id');
}
/**

View File

@ -44,7 +44,7 @@ class LocationRepository
public function create(array $data)
{
$validator = Validator::make($data, [
'short' => 'required|regex:/^[a-z0-9_.-]{1,10}$/i|unique:locations,short',
'short' => 'required|regex:/^[\w.-]{1,20}$/i|unique:locations,short',
'long' => 'required|string|min:1|max:255',
]);
@ -73,9 +73,11 @@ class LocationRepository
*/
public function edit($id, array $data)
{
$location = Models\Location::findOrFail($id);
$validator = Validator::make($data, [
'short' => 'regex:/^[a-z0-9_.-]{1,10}$/i',
'long' => 'string|min:1|max:255',
'short' => 'required|regex:/^[\w.-]{1,20}$/i|unique:locations,short,' . $location->id,
'long' => 'required|string|min:1|max:255',
]);
// Run validator, throw catchable and displayable exception if it fails.
@ -84,15 +86,7 @@ class LocationRepository
throw new DisplayValidationException($validator->errors());
}
$location = Models\Location::findOrFail($id);
if (isset($data['short'])) {
$location->short = $data['short'];
}
if (isset($data['long'])) {
$location->long = $data['long'];
}
$location->fill($data);
return $location->save();
}

View File

@ -65,7 +65,7 @@ class NodeRepository
// Verify the FQDN if using SSL
if (filter_var($data['fqdn'], FILTER_VALIDATE_IP) && $data['scheme'] === 'https') {
throw new DisplayException('A fully qualified domain name is required to use secure comunication on this node.');
throw new DisplayException('A fully qualified domain name is required to use a secure comunication method on this node.');
}
// Verify FQDN is resolvable, or if not using SSL that the IP is valid.
@ -86,7 +86,7 @@ class NodeRepository
$node->fill($data);
$node->save();
return $node->id;
return $node;
}
public function update($id, array $data)
@ -152,18 +152,12 @@ class NodeRepository
$oldDaemonKey = $node->daemonSecret;
$node->update($data);
try {
$client = Models\Node::guzzleRequest($node->id);
$client->request('PATCH', '/config', [
'headers' => [
'X-Access-Token' => $oldDaemonKey,
],
$node->guzzleClient(['X-Access-Token' => $oldDaemonKey])->request('PATCH', '/config', [
'json' => [
'web' => [
'listen' => $node->daemonListen,
'ssl' => [
'enabled' => ($node->scheme === 'https'),
'certificate' => '/etc/letsencrypt/live/' . $node->fqdn . '/fullchain.pem',
'key' => '/etc/letsencrypt/live/' . $node->fqdn . '/privkey.pem',
],
],
'sftp' => [

View File

@ -0,0 +1,48 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class RenameColumns extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('allocations', function (Blueprint $table) {
$table->dropForeign('allocations_node_foreign');
$table->dropForeign('allocations_assigned_to_foreign');
$table->dropIndex('allocations_node_foreign');
$table->dropIndex('allocations_assigned_to_foreign');
$table->renameColumn('node', 'node_id');
$table->renameColumn('assigned_to', 'server_id');
$table->foreign('node_id')->references('id')->on('nodes');
$table->foreign('server_id')->references('id')->on('servers');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('allocations', function (Blueprint $table) {
$table->dropForeign('allocations_node_id_foreign');
$table->dropForeign('allocations_server_id_foreign');
$table->dropIndex('allocations_node_id_foreign');
$table->dropIndex('allocations_server_id_foreign');
$table->renameColumn('node_id', 'node');
$table->renameColumn('server_id', 'assigned_to');
$table->foreign('node')->references('id')->on('nodes');
$table->foreign('assigned_to')->references('id')->on('servers');
});
}
}

View File

@ -53,10 +53,10 @@
<tr>
<td class="text-center text-muted left-icon" data-action="ping" data-secret="{{ $node->daemonSecret }}" data-location="{{ $node->scheme }}://{{ $node->fqdn }}:{{ $node->daemonListen }}"><i class="fa fa-fw fa-refresh fa-spin"></i></td>
<td><a href="/admin/nodes/view/{{ $node->id }}">{{ $node->name }}</td>
<td>{{ $node->a_locationName }}</td>
<td>{{ $node->location->short }}</td>
<td class="hidden-xs">{{ $node->memory }} MB</td>
<td class="hidden-xs">{{ $node->disk }} MB</td>
<td class="text-center hidden-xs">{{ $node->a_serverCount }}</td>
<td class="text-center hidden-xs">{{ $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 hidden-xs"><i class="fa fa-{{ ($node->public === 1) ? 'eye' : 'eye-slash' }}"></i></td>
</tr>

View File

@ -60,7 +60,7 @@
<li><a href="#tab_configuration" data-toggle="tab">Configuration</a></li>
<li><a href="#tab_allocation" data-toggle="tab">Allocation</a></li>
<li><a href="#tab_servers" data-toggle="tab">Servers</a></li>
@if(count($servers) === 0)<li><a href="#tab_delete" data-toggle="tab">Delete</a></li>@endif
@if(count($node->servers) === 0)<li><a href="#tab_delete" data-toggle="tab">Delete</a></li>@endif
</ul>
<div class="tab-content">
<div class="tab-pane active" id="tab_about">
@ -83,7 +83,7 @@
</tr>
<tr>
<td>Total Servers</td>
<td>{{ count($servers) }}</td>
<td>{{ count($node->servers) }}</td>
</tr>
<tr>
<td>Memory Allocated</td>
@ -309,7 +309,7 @@
<div class="input-group-btn">
<button type="button" class="btn btn-sm btn-primary dropdown-toggle" data-toggle="dropdown"><span class="caret"></span></button>
<ul class="dropdown-menu dropdown-menu-right">
@foreach($allocation_ips as $allocation)
@foreach($node->allocations->unique('ip')->values()->all() as $allocation)
<li data-action="alloc_dropdown_val" data-value="{{ $allocation->ip }}"><a href="#">{{ $allocation->ip }}</a></li>
@endforeach
</ul>
@ -355,7 +355,7 @@
<td></td>
</thead>
<tbody>
@foreach($allocations as $allocation)
@foreach($node->allocations as $allocation)
<tr>
<td class="col-sm-3 align-middle">{{ $allocation->ip }}</td>
<td class="col-sm-3 align-middle">
@ -376,7 +376,7 @@
</tbody>
</table>
<div class="col-md-12 text-center">
{{ $allocations->appends(['tab' => 'tab_allocation'])->links() }}
{{ $node->allocations->appends(['tab' => 'tab_allocation'])->render() }}
</div>
</div>
</div>
@ -402,11 +402,11 @@
</tr>
</thead>
<tbody>
@foreach($servers as $server)
@foreach($node->servers as $server)
<tr data-server="{{ $server->uuid }}">
<td><a href="/admin/servers/view/{{ $server->id }}">{{ $server->name }}</a></td>
<td><a href="/admin/users/view/{{ $server->owner_id }}"><code>{{ $server->a_ownerEmail }}</a></a></td>
<td>{{ $server->a_serviceName }}</td>
<td><a href="/admin/users/view/{{ $server->owner_id }}"><code>{{ $server->user->email }}</a></a></td>
<td>{{ $server->service->name }}</td>
<td class="text-center"><span data-action="memory">--</span> / {{ $server->memory === 0 ? '&infin;' : $server->memory }} MB</td>
<td class="text-center">{{ $server->disk }} MB</td>
<td class="text-center"><span data-action="cpu" data-cpumax="{{ $server->cpu }}">--</span> %</td>
@ -415,13 +415,10 @@
@endforeach
</tbody>
</table>
<div class="row">
<div class="col-md-12 text-center">{!! $servers->appends(['tab' => 'tab_servers'])->render() !!}</div>
</div>
</div>
</div>
</div>
@if(count($servers) === 0)
@if(count($node->servers) === 0)
<div class="tab-pane" id="tab_delete">
<div class="panel panel-default">
<div class="panel-heading"></div>
@ -459,7 +456,7 @@
<div class="row">
<div class="col-md-12">
<select class="form-control" name="ip">
@foreach($allocation_ips as $allocation)
@foreach($node->allocations->unique('ip')->values()->all() as $allocation)
<option value="{{ $allocation->ip }}">{{ $allocation->ip }}</option>
@endforeach
</select>