diff --git a/app/Http/Controllers/Admin/LocationsController.php b/app/Http/Controllers/Admin/LocationsController.php index 9ef004bed..93a45fb66 100644 --- a/app/Http/Controllers/Admin/LocationsController.php +++ b/app/Http/Controllers/Admin/LocationsController.php @@ -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'); diff --git a/app/Http/Controllers/Admin/NodesController.php b/app/Http/Controllers/Admin/NodesController.php index 600ecb932..9cfac1fde 100644 --- a/app/Http/Controllers/Admin/NodesController.php +++ b/app/Http/Controllers/Admin/NodesController.php @@ -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. Before you can add any servers you need to first assign some IP addresses and ports.')->flash(); - Alert::info('To simplify the node setup you can generate a token on the configuration tab.')->flash(); + Alert::success('Successfully created new node that can be configured automatically on your remote machine by visiting the configuration tab. Before you can add any servers you need to first assign some IP addresses and ports.')->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(); diff --git a/app/Models/Allocation.php b/app/Models/Allocation.php index 10308ebdd..34b6baec5 100644 --- a/app/Models/Allocation.php +++ b/app/Models/Allocation.php @@ -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', ]; } diff --git a/app/Models/Node.php b/app/Models/Node.php index 0c4d8fb3c..2be35568e 100644 --- a/app/Models/Node.php +++ b/app/Models/Node.php @@ -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); + } } diff --git a/app/Models/Server.php b/app/Models/Server.php index db8ba6381..f7e8bdbed 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -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'); } /** diff --git a/app/Repositories/LocationRepository.php b/app/Repositories/LocationRepository.php index 79196ff35..e25446d31 100644 --- a/app/Repositories/LocationRepository.php +++ b/app/Repositories/LocationRepository.php @@ -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(); } diff --git a/app/Repositories/NodeRepository.php b/app/Repositories/NodeRepository.php index d55446196..b9ba82e4b 100644 --- a/app/Repositories/NodeRepository.php +++ b/app/Repositories/NodeRepository.php @@ -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' => [ diff --git a/database/migrations/2017_02_03_155554_RenameColumns.php b/database/migrations/2017_02_03_155554_RenameColumns.php new file mode 100644 index 000000000..519ccc826 --- /dev/null +++ b/database/migrations/2017_02_03_155554_RenameColumns.php @@ -0,0 +1,48 @@ +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'); + }); + } +} diff --git a/resources/views/admin/nodes/index.blade.php b/resources/views/admin/nodes/index.blade.php index 29546d4ea..de1687087 100644 --- a/resources/views/admin/nodes/index.blade.php +++ b/resources/views/admin/nodes/index.blade.php @@ -53,10 +53,10 @@
{{ $server->a_ownerEmail }}
{{ $server->user->email }}