' . $request->input('ip') . '
.')->flash();
}
- Alert::success('Deleted all unallocated ports for ' . $request->input('ip') . '
.')->flash();
- return redirect()->route('admin.nodes.view', [
- 'id' => $node,
- 'tab' => 'tab_allocation',
- ]);
+ return redirect()->route('admin.nodes.view.allocation', $node);
}
- public function setAlias(Request $request, $node)
+ /**
+ * Sets an alias for a specific allocation on a node.
+ *
+ * @param Request $request
+ * @param int $node
+ * @return \Illuminate\Http\Response
+ * @throws \Exception
+ */
+ public function allocationSetAlias(Request $request, $node)
{
- if (! $request->input('allocation')) {
+ if (! $request->input('allocation_id')) {
return response('Missing required parameters.', 422);
}
try {
- $update = Models\Allocation::findOrFail($request->input('allocation'));
+ $update = Models\Allocation::findOrFail($request->input('allocation_id'));
$update->ip_alias = (empty($request->input('alias'))) ? null : $request->input('alias');
$update->save();
@@ -197,100 +296,74 @@ class NodesController extends Controller
}
}
- public function getAllocationsJson(Request $request, $id)
+ /**
+ * Creates new allocations on a node.
+ *
+ * @param Request $request
+ * @param int $node
+ * @return \Illuminate\Http\RedirectResponse
+ */
+ public function createAllocation(Request $request, $node)
{
- $allocations = Models\Allocation::select('ip')->where('node_id', $id)->groupBy('ip')->get();
-
- return response()->json($allocations);
- }
-
- public function postAllocations(Request $request, $id)
- {
- $validator = Validator::make($request->all(), [
- 'allocate_ip.*' => 'required|string',
- 'allocate_port.*' => 'required',
- ]);
-
- if ($validator->fails()) {
- return redirect()->route('admin.nodes.view', [
- 'id' => $id,
- 'tab' => 'tab_allocation',
- ])->withErrors($validator->errors())->withInput();
- }
-
- $processedData = [];
- foreach ($request->input('allocate_ip') as $ip) {
- if (! array_key_exists($ip, $processedData)) {
- $processedData[$ip] = [];
- }
- }
-
- foreach ($request->input('allocate_port') as $portid => $ports) {
- if (array_key_exists($portid, $request->input('allocate_ip'))) {
- $json = json_decode($ports);
- if (json_last_error() === 0 && ! empty($json)) {
- foreach ($json as &$parsed) {
- array_push($processedData[$request->input('allocate_ip')[$portid]], $parsed->value);
- }
- }
- }
- }
+ $repo = new NodeRepository;
try {
- $node = new NodeRepository;
- $node->addAllocations($id, $processedData);
- Alert::success('Successfully added new allocations to this node.')->flash();
- } catch (DisplayException $e) {
- Alert::danger($e->getMessage())->flash();
- } catch (\Exception $e) {
- Log::error($e);
- Alert::danger('An unhandled exception occured while attempting to add allocations this node. Please try again.')->flash();
- } finally {
- return redirect()->route('admin.nodes.view', [
- 'id' => $id,
- 'tab' => 'tab_allocation',
- ]);
+ $repo->addAllocations($node, $request->intersect(['allocation_ip', 'allocation_alias', 'allocation_ports']));
+ Alert::success('Successfully added new allocations!')->flash();
+ } catch (DisplayValidationException $ex) {
+ return redirect()->route('admin.nodes.view.allocation', $node)->withErrors(json_decode($ex->getMessage()))->withInput();
+ } catch (DisplayException $ex) {
+ Alert::danger($ex->getMessage())->flash();
+ } catch (\Exception $ex) {
+ Log::error($ex);
+ Alert::danger('An unhandled exception occured while attempting to add allocations this node. This error has been logged.')->flash();
}
+
+ return redirect()->route('admin.nodes.view.allocation', $node);
}
- public function deleteNode(Request $request, $id)
+ /**
+ * Deletes a node from the system.
+ *
+ * @param Request $request
+ * @param int $id
+ * @return \Illuminate\Http\RedirectResponse
+ */
+ public function delete(Request $request, $id)
{
+ $repo = new NodeRepository;
+
try {
- $repo = new NodeRepository;
$repo->delete($id);
Alert::success('Successfully deleted the requested node from the panel.')->flash();
return redirect()->route('admin.nodes');
- } catch (DisplayException $e) {
- Alert::danger($e->getMessage())->flash();
- } catch (\Exception $e) {
- Log::error($e);
+ } catch (DisplayException $ex) {
+ Alert::danger($ex->getMessage())->flash();
+ } catch (\Exception $ex) {
+ Log::error($ex);
Alert::danger('An unhandled exception occured while attempting to delete this node. Please try again.')->flash();
}
- return redirect()->route('admin.nodes.view', [
- 'id' => $id,
- 'tab' => 'tab_delete',
- ]);
+ return redirect()->route('admin.nodes.view', $id);
}
- public function getConfigurationToken(Request $request, $id)
+ /**
+ * Returns the configuration token to auto-deploy a node.
+ *
+ * @param Request $request
+ * @param int $id
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function setToken(Request $request, $id)
{
- // Check if Node exists. Will lead to 404 if not.
- Models\Node::findOrFail($id);
+ $node = Models\Node::findOrFail($id);
- // Create a token
- $token = new Models\NodeConfigurationToken();
- $token->node = $id;
- $token->token = str_random(32);
- $token->expires_at = Carbon::now()->addMinutes(5); // Expire in 5 Minutes
- $token->save();
+ $t = Models\NodeConfigurationToken::create([
+ 'node_id' => $id,
+ 'token' => str_random(32),
+ ]);
- $token_response = [
- 'token' => $token->token,
- 'expires_at' => $token->expires_at->toDateTimeString(),
- ];
-
- return response()->json($token_response, 200);
+ return response()->json(['token' => $t->token]);
}
}
diff --git a/app/Http/Controllers/Admin/ServersController.php b/app/Http/Controllers/Admin/ServersController.php
index 017dc1fcc..6b1808516 100644
--- a/app/Http/Controllers/Admin/ServersController.php
+++ b/app/Http/Controllers/Admin/ServersController.php
@@ -26,8 +26,10 @@ namespace Pterodactyl\Http\Controllers\Admin;
use Log;
use Alert;
+use Javascript;
use Pterodactyl\Models;
use Illuminate\Http\Request;
+use GuzzleHttp\Exception\TransferException;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Repositories\ServerRepository;
@@ -37,419 +39,540 @@ use Pterodactyl\Exceptions\DisplayValidationException;
class ServersController extends Controller
{
/**
- * Controller Constructor.
+ * Display the index page with all servers currently on the system.
+ *
+ * @param Request $request
+ * @return \Illuminate\View\View
*/
- public function __construct()
+ public function index(Request $request)
{
- //
- }
+ $servers = Models\Server::withTrashed()->with(
+ 'node', 'user', 'allocation'
+ );
+
+ if (! is_null($request->input('query'))) {
+ $servers->search($request->input('query'));
+ }
- public function getIndex(Request $request)
- {
return view('admin.servers.index', [
- 'servers' => Models\Server::withTrashed()->with('node', 'user')->paginate(25),
+ 'servers' => $servers->paginate(25),
]);
}
- public function getNew(Request $request)
+ /**
+ * Display create new server page.
+ *
+ * @param Request $request
+ * @return \Illuminate\View\View
+ */
+ public function new(Request $request)
{
+ $services = Models\Service::with('options.packs', 'options.variables')->get();
+ Javascript::put([
+ 'services' => $services->map(function ($item) {
+ return array_merge($item->toArray(), [
+ 'options' => $item->options->keyBy('id')->toArray(),
+ ]);
+ })->keyBy('id'),
+ ]);
+
return view('admin.servers.new', [
'locations' => Models\Location::all(),
- 'services' => Models\Service::all(),
+ 'services' => $services,
]);
}
- public function getView(Request $request, $id)
+ /**
+ * Create server controller method.
+ *
+ * @param Request $request
+ * @return \Illuminate\Response\RedirectResponse
+ */
+ public function create(Request $request)
{
- $server = Models\Server::withTrashed()->with(
- 'user', 'option.variables', 'variables',
- 'node.allocations', 'databases.host'
- )->findOrFail($id);
+ try {
+ $repo = new ServerRepository;
+ $server = $repo->create($request->except('_token'));
+ return redirect()->route('admin.servers.view', $server->id);
+ } catch (DisplayValidationException $ex) {
+ return redirect()->route('admin.servers.new')->withErrors(json_decode($ex->getMessage()))->withInput();
+ } catch (DisplayException $ex) {
+ Alert::danger($ex->getMessage())->flash();
+ } catch (\Exception $ex) {
+ Log::error($ex);
+ Alert::danger('An unhandled exception occured while attemping to add this server. Please try again.')->flash();
+ }
+
+ return redirect()->route('admin.servers.new')->withInput();
+ }
+
+ /**
+ * Returns a tree of all avaliable nodes in a given location.
+ *
+ * @param Request $request
+ * @return array
+ */
+ public function newServerNodes(Request $request)
+ {
+ $nodes = Models\Node::with('allocations')->where('location_id', $request->input('location'))->get();
+
+ return $nodes->map(function ($item) {
+ $filtered = $item->allocations->where('server_id', null)->map(function ($map) {
+ return collect($map)->only(['id', 'ip', 'port']);
+ });
+
+ $item->ports = $filtered->map(function ($map) use ($item) {
+ return [
+ 'id' => $map['id'],
+ 'text' => $map['ip'] . ':' . $map['port'],
+ ];
+ })->values();
+
+ return [
+ 'id' => $item->id,
+ 'text' => $item->name,
+ 'allocations' => $item->ports,
+ ];
+ })->values();
+ }
+
+ /**
+ * Display the index when viewing a specific server.
+ *
+ * @param Request $request
+ * @param int $id
+ * @return \Illuminate\View\View
+ */
+ public function viewIndex(Request $request, $id)
+ {
+ return view('admin.servers.view.index', ['server' => Models\Server::withTrashed()->findOrFail($id)]);
+ }
+
+ /**
+ * Display the details page when viewing a specific server.
+ *
+ * @param Request $request
+ * @param int $id
+ * @return \Illuminate\View\View
+ */
+ public function viewDetails(Request $request, $id)
+ {
+ $server = Models\Server::where('installed', 1)->findOrFail($id);
+
+ return view('admin.servers.view.details', ['server' => $server]);
+ }
+
+ /**
+ * Display the build details page when viewing a specific server.
+ *
+ * @param Request $request
+ * @param int $id
+ * @return \Illuminate\View\View
+ */
+ public function viewBuild(Request $request, $id)
+ {
+ $server = Models\Server::where('installed', 1)->with('node.allocations')->findOrFail($id);
+
+ return view('admin.servers.view.build', [
+ 'server' => $server,
+ 'assigned' => $server->node->allocations->where('server_id', $server->id)->sortBy('port')->sortBy('ip'),
+ 'unassigned' => $server->node->allocations->where('server_id', null)->sortBy('port')->sortBy('ip'),
+ ]);
+ }
+
+ /**
+ * Display startup configuration page for a server.
+ *
+ * @param Request $request
+ * @param int $id
+ * @return \Illuminate\View\View
+ */
+ public function viewStartup(Request $request, $id)
+ {
+ $server = Models\Server::where('installed', 1)->with('option.variables', 'variables')->findOrFail($id);
$server->option->variables->transform(function ($item, $key) use ($server) {
$item->server_value = $server->variables->where('variable_id', $item->id)->pluck('variable_value')->first();
return $item;
});
- return view('admin.servers.view', [
+ return view('admin.servers.view.startup', ['server' => $server]);
+ }
+
+ /**
+ * Display the database management page for a specific server.
+ *
+ * @param Request $request
+ * @param int $id
+ * @return \Illuminate\View\View
+ */
+ public function viewDatabase(Request $request, $id)
+ {
+ $server = Models\Server::where('installed', 1)->with('databases.host')->findOrFail($id);
+
+ return view('admin.servers.view.database', [
+ 'hosts' => Models\DatabaseServer::all(),
'server' => $server,
- 'assigned' => $server->node->allocations->where('server_id', $server->id)->sortBy('port')->sortBy('ip'),
- 'unassigned' => $server->node->allocations->where('server_id', null)->sortBy('port')->sortBy('ip'),
- 'db_servers' => Models\DatabaseServer::all(),
]);
}
- public function postNewServer(Request $request)
+ /**
+ * Display the management page when viewing a specific server.
+ *
+ * @param Request $request
+ * @param int $id
+ * @return \Illuminate\View\View
+ */
+ public function viewManage(Request $request, $id)
{
+ return view('admin.servers.view.manage', ['server' => Models\Server::findOrFail($id)]);
+ }
+
+ /**
+ * Display the deletion page for a server.
+ *
+ * @param Request $request
+ * @param int $id
+ * @return \Illuminate\View\View
+ */
+ public function viewDelete(Request $request, $id)
+ {
+ return view('admin.servers.view.delete', ['server' => Models\Server::withTrashed()->findOrFail($id)]);
+ }
+
+ /**
+ * Update the details for a server.
+ *
+ * @param Request $request
+ * @param int $id
+ * @return \Illuminate\Response\RedirectResponse
+ */
+ public function setDetails(Request $request, $id)
+ {
+ $repo = new ServerRepository;
try {
- $server = new ServerRepository;
- $response = $server->create($request->except('_token'));
-
- return redirect()->route('admin.servers.view', ['id' => $response->id]);
- } catch (DisplayValidationException $ex) {
- return redirect()->route('admin.servers.new')->withErrors(json_decode($ex->getMessage()))->withInput();
- } catch (DisplayException $ex) {
- Alert::danger($ex->getMessage())->flash();
-
- return redirect()->route('admin.servers.new')->withInput();
- } catch (\Exception $ex) {
- Log::error($ex);
- Alert::danger('An unhandled exception occured while attemping to add this server. Please try again.')->flash();
-
- return redirect()->route('admin.servers.new')->withInput();
- }
- }
-
- /**
- * Returns a JSON tree of all avaliable nodes in a given location.
- *
- * @param \Illuminate\Http\Request $request
- * @return \Illuminate\Contracts\View\View
- */
- public function postNewServerGetNodes(Request $request)
- {
- if (! $request->input('location')) {
- return response()->json([
- 'error' => 'Missing location in request.',
- ], 500);
- }
-
- return response()->json(Models\Node::select('id', 'name', 'public')->where('location_id', $request->input('location'))->get());
- }
-
- /**
- * 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)
- {
- if (! $request->input('node')) {
- return response()->json([
- 'error' => 'Missing node in request.',
- ], 500);
- }
-
- $ips = Models\Allocation::where('node_id', $request->input('node'))->whereNull('server_id')->get();
- $listing = [];
-
- foreach ($ips as &$ip) {
- if (array_key_exists($ip->ip, $listing)) {
- $listing[$ip->ip] = array_merge($listing[$ip->ip], [$ip->port]);
- } else {
- $listing[$ip->ip] = [$ip->port];
- }
- }
-
- return response()->json($listing);
- }
-
- /**
- * 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,
- ]);
+ $repo->updateDetails($id, $request->intersect([
+ 'owner_id', 'name', 'reset_token',
+ ]));
Alert::success('Server details were successfully updated.')->flash();
} catch (DisplayValidationException $ex) {
- return redirect()->route('admin.servers.view', [
- 'id' => $id,
- 'tab' => 'tab_details',
- ])->withErrors(json_decode($ex->getMessage()))->withInput();
+ return redirect()->route('admin.servers.view.details', $id)->withErrors(json_decode($ex->getMessage()))->withInput();
} catch (DisplayException $ex) {
Alert::danger($ex->getMessage())->flash();
} catch (\Exception $ex) {
Log::error($ex);
- Alert::danger('An unhandled exception occured while attemping to update this server. Please try again.')->flash();
+ Alert::danger('An unhandled exception occured while attemping to update this server. This error has been logged.')->flash();
}
- return redirect()->route('admin.servers.view', [
- 'id' => $id,
- 'tab' => 'tab_details',
- ])->withInput();
+ return redirect()->route('admin.servers.view.details', $id)->withInput();
}
- public function postUpdateContainerDetails(Request $request, $id)
+ /**
+ * Set the new docker container for a server.
+ *
+ * @param Request $request
+ * @param int $id
+ * @return \Illuminate\Response\RedirectResponse
+ */
+ public function setContainer(Request $request, $id)
{
+ $repo = new ServerRepository;
+
try {
- $server = new ServerRepository;
- $server->updateContainer($id, ['image' => $request->input('docker_image')]);
+ $repo->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', [
- 'id' => $id,
- 'tab' => 'tab_details',
- ])->withErrors(json_decode($ex->getMessage()))->withInput();
+ return redirect()->route('admin.servers.view.details', $id)->withErrors(json_decode($ex->getMessage()))->withInput();
} catch (DisplayException $ex) {
Alert::danger($ex->getMessage())->flash();
} catch (\Exception $ex) {
Log::error($ex);
- Alert::danger('An unhandled exception occured while attemping to update this server\'s docker image. Please try again.')->flash();
+ Alert::danger('An unhandled exception occured while attemping to update this server\'s docker image. This error has been logged.')->flash();
}
- return redirect()->route('admin.servers.view', [
- 'id' => $id,
- 'tab' => 'tab_details',
- ]);
+ return redirect()->route('admin.servers.view.details', $id);
}
- public function postUpdateServerToggleBuild(Request $request, $id)
+ /**
+ * Toggles the install status for a server.
+ *
+ * @param Request $request
+ * @param int $id
+ * @return \Illuminate\Response\RedirectResponse
+ */
+ public function toggleInstall(Request $request, $id)
+ {
+ $repo = new ServerRepository;
+ try {
+ $repo->toggleInstall($id);
+
+ Alert::success('Server install status was successfully toggled.')->flash();
+ } catch (DisplayException $ex) {
+ Alert::danger($ex->getMessage())->flash();
+ } catch (\Exception $ex) {
+ Log::error($ex);
+ Alert::danger('An unhandled exception occured while attemping to toggle this servers status. This error has been logged.')->flash();
+ }
+
+ return redirect()->route('admin.servers.view.manage', $id);
+ }
+
+ /**
+ * Setup a server to have a container rebuild.
+ *
+ * @param Request $request
+ * @param int $id
+ * @return \Illuminate\Response\RedirectResponse
+ */
+ public function rebuildContainer(Request $request, $id)
{
$server = Models\Server::with('node')->findOrFail($id);
try {
- $res = $server->node->guzzleClient([
+ $server->node->guzzleClient([
'X-Access-Server' => $server->uuid,
'X-Access-Token' => $server->node->daemonSecret,
])->request('POST', '/server/rebuild');
+
Alert::success('A rebuild has been queued successfully. It will run the next time this server is booted.')->flash();
- } catch (\GuzzleHttp\Exception\TransferException $ex) {
+ } catch (TransferException $ex) {
Log::warning($ex);
- Alert::danger('An error occured while attempting to toggle a rebuild.')->flash();
+ Alert::danger('A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.')->flash();
}
- return redirect()->route('admin.servers.view', [
- 'id' => $id,
- 'tab' => 'tab_manage',
- ]);
+ return redirect()->route('admin.servers.view.manage', $id);
}
- public function postUpdateServerUpdateBuild(Request $request, $id)
+ /**
+ * Manage the suspension status for a server.
+ *
+ * @param Request $request
+ * @param int $id
+ * @return \Illuminate\Response\RedirectResponse
+ */
+ public function manageSuspension(Request $request, $id)
{
+ $repo = new ServerRepository;
+ $action = $request->input('action');
+
+ if (! in_array($action, ['suspend', 'unsuspend'])) {
+ Alert::danger('Invalid action was passed to function.')->flash();
+
+ return redirect()->route('admin.servers.view.manage', $id);
+ }
+
try {
- $server = new ServerRepository;
- $server->changeBuild($id, $request->only([
- 'default', 'add_additional',
- 'remove_additional', 'memory',
- 'swap', 'io', 'cpu',
- ]));
- Alert::success('Server details were successfully updated.')->flash();
- } catch (DisplayValidationException $ex) {
- return redirect()->route('admin.servers.view', [
- 'id' => $id,
- 'tab' => 'tab_build',
- ])->withErrors(json_decode($ex->getMessage()))->withInput();
+ $repo->$action($id);
+
+ Alert::success('Server has been ' . $action . 'ed.');
} catch (DisplayException $ex) {
Alert::danger($ex->getMessage())->flash();
-
- return redirect()->route('admin.servers.view', [
- 'id' => $id,
- 'tab' => 'tab_build',
- ]);
} catch (\Exception $ex) {
Log::error($ex);
- Alert::danger('An unhandled exception occured while attemping to add this server. Please try again.')->flash();
+ Alert::danger('An unhandled exception occured while attemping to ' . $action . ' this server. This error has been logged.')->flash();
}
- return redirect()->route('admin.servers.view', [
- 'id' => $id,
- 'tab' => 'tab_build',
- ]);
+ return redirect()->route('admin.servers.view.manage', $id);
}
- public function deleteServer(Request $request, $id, $force = null)
+ /**
+ * Update the build configuration for a server.
+ *
+ * @param Request $request
+ * @param int $id
+ * @return \Illuminate\Response\RedirectResponse
+ */
+ public function updateBuild(Request $request, $id)
{
+ $repo = new ServerRepository;
+
try {
- $server = new ServerRepository;
- $server->deleteServer($id, $force);
+ $repo->changeBuild($id, $request->intersect([
+ 'allocation_id', 'add_allocations', 'remove_allocations',
+ 'memory', 'swap', 'io', 'cpu',
+ ]));
+
+ Alert::success('Server details were successfully updated.')->flash();
+ } catch (DisplayValidationException $ex) {
+ return redirect()->route('admin.servers.view.build', $id)->withErrors(json_decode($ex->getMessage()))->withInput();
+ } catch (DisplayException $ex) {
+ Alert::danger($ex->getMessage())->flash();
+ } catch (\Exception $ex) {
+ Log::error($ex);
+ Alert::danger('An unhandled exception occured while attemping to add this server. This error has been logged.')->flash();
+ }
+
+ return redirect()->route('admin.servers.view.build', $id);
+ }
+
+ /**
+ * Start the server deletion process.
+ *
+ * @param Request $request
+ * @param int $id
+ * @return \Illuminate\Response\RedirectResponse
+ */
+ public function delete(Request $request, $id)
+ {
+ $repo = new ServerRepository;
+
+ try {
+ $repo->queueDeletion($id, ($request->input('is_force') > 0));
Alert::success('Server has been marked for deletion on the system.')->flash();
+ } catch (DisplayException $ex) {
+ Alert::danger($ex->getMessage())->flash();
+ } catch (\Exception $ex) {
+ Log::error($ex);
+ Alert::danger('An unhandled exception occured while attemping to delete this server. This error has been logged.')->flash();
+ }
+
+ return redirect()->route('admin.servers.view.delete', $id);
+ }
+
+ /**
+ * Cancels a pending server deletion request.
+ *
+ * @param Request $request
+ * @param int $id
+ * @return \Illuminate\Response\RedirectResponse
+ */
+ public function cancelDeletion(Request $request, $id)
+ {
+ $repo = new ServerRepository;
+
+ $repo->cancelDeletion($id);
+ Alert::success('Server deletion has been cancelled. This server will remain suspended until you unsuspend it.')->flash();
+
+ return redirect()->route('admin.servers.view.delete', $id);
+ }
+
+ /**
+ * Skips the queue and continues the server deletion process.
+ *
+ * @param Request $request
+ * @param int $id
+ * @return \Illuminate\Response\RedirectResponse
+ */
+ public function continueDeletion(Request $request, $id, $method)
+ {
+ $repo = new ServerRepository;
+
+ try {
+ $repo->delete($id, (isset($method) && $method === 'force'));
+ Alert::success('Server was successfully deleted from the system.')->flash();
return redirect()->route('admin.servers');
} catch (DisplayException $ex) {
Alert::danger($ex->getMessage())->flash();
+ } catch (TransferException $ex) {
+ Log::warning($ex);
+ Alert::danger('A TransferException occurred while attempting to delete this server from the daemon, please ensure it is running. This error has been logged.')->flash();
} catch (\Exception $ex) {
Log::error($ex);
- Alert::danger('An unhandled exception occured while attemping to delete this server. Please try again.')->flash();
+ Alert::danger('An unhandled exception occured while attemping to delete this server. This error has been logged.')->flash();
}
- return redirect()->route('admin.servers.view', [
- 'id' => $id,
- 'tab' => 'tab_delete',
- ]);
+ return redirect()->route('admin.servers.view.delete', $id);
}
- public function postToggleInstall(Request $request, $id)
+ /**
+ * Update the startup command as well as variables.
+ *
+ * @param Request $request
+ * @param int $id
+ * @return \Illuminate\Response\RedirectResponse
+ */
+ public function saveStartup(Request $request, $id)
{
+ $repo = new ServerRepository;
+
try {
- $server = new ServerRepository;
- $server->toggleInstall($id);
- Alert::success('Server status was successfully toggled.')->flash();
+ $repo->updateStartup($id, $request->except('_token'), true);
+
+ Alert::success('Startup variables were successfully modified and assigned for this server.')->flash();
} catch (DisplayException $ex) {
Alert::danger($ex->getMessage())->flash();
+ } catch (TransferException $ex) {
+ Log::warning($ex);
+ Alert::danger('A TransferException occurred while attempting to update the startup for this server, please ensure the daemon is running. This error has been logged.')->flash();
} catch (\Exception $ex) {
Log::error($ex);
- Alert::danger('An unhandled exception occured while attemping to toggle this servers status.')->flash();
- } finally {
- return redirect()->route('admin.servers.view', [
- 'id' => $id,
- 'tab' => 'tab_manage',
- ]);
+ Alert::danger('An unhandled exception occured while attemping to update startup variables for this server. This error has been logged.')->flash();
}
+
+ return redirect()->route('admin.servers.view.startup', $id);
}
- public function postUpdateServerStartup(Request $request, $id)
+ /**
+ * Creates a new database assigned to a specific server.
+ * @param Request $request
+ * @param int $id
+ * @return \Illuminate\Response\RedirectResponse
+ */
+ public function newDatabase(Request $request, $id)
{
- try {
- $server = new ServerRepository;
- $server->updateStartup($id, $request->except([
- '_token',
- ]), true);
- Alert::success('Server startup variables were successfully updated.')->flash();
- } catch (\Pterodactyl\Exceptions\DisplayException $e) {
- Alert::danger($e->getMessage())->flash();
- } catch (\Exception $e) {
- Log::error($e);
- Alert::danger('An unhandled exception occured while attemping to update startup variables for this server. Please try again.')->flash();
- } finally {
- return redirect()->route('admin.servers.view', [
- 'id' => $id,
- 'tab' => 'tab_startup',
- ])->withInput();
- }
- }
+ $repo = new DatabaseRepository;
- public function postDatabase(Request $request, $id)
- {
try {
- $repo = new DatabaseRepository;
- $repo->create($id, $request->only([
- 'db_server', 'database', 'remote',
- ]));
- Alert::success('Added new database to this server.')->flash();
+ $repo->create($id, $request->only(['host', 'database', 'connection']));
+
+ Alert::success('A new database was assigned to this server successfully.')->flash();
} catch (DisplayValidationException $ex) {
- return redirect()->route('admin.servers.view', [
- 'id' => $id,
- 'tab' => 'tab_database',
- ])->withInput()->withErrors(json_decode($ex->getMessage()))->withInput();
- } catch (\Exception $ex) {
- Log::error($ex);
- Alert::danger('An exception occured while attempting to add a new database for this server.')->flash();
- }
-
- return redirect()->route('admin.servers.view', [
- 'id' => $id,
- 'tab' => 'tab_database',
- ])->withInput();
- }
-
- public function postSuspendServer(Request $request, $id)
- {
- try {
- $repo = new ServerRepository;
- $repo->suspend($id);
- Alert::success('Server has been suspended on the system. All running processes have been stopped and will not be startable until it is un-suspended.');
- } catch (DisplayException $e) {
- Alert::danger($e->getMessage())->flash();
- } catch (\Exception $e) {
- Log::error($e);
- Alert::danger('An unhandled exception occured while attemping to suspend this server. Please try again.')->flash();
- } finally {
- return redirect()->route('admin.servers.view', [
- 'id' => $id,
- 'tab' => 'tab_manage',
- ]);
- }
- }
-
- public function postUnsuspendServer(Request $request, $id)
- {
- try {
- $repo = new ServerRepository;
- $repo->unsuspend($id);
- Alert::success('Server has been unsuspended on the system. Access has been re-enabled.');
- } catch (DisplayException $e) {
- Alert::danger($e->getMessage())->flash();
- } catch (\Exception $e) {
- Log::error($e);
- Alert::danger('An unhandled exception occured while attemping to unsuspend this server. Please try again.')->flash();
- } finally {
- return redirect()->route('admin.servers.view', [
- 'id' => $id,
- 'tab' => 'tab_manage',
- ]);
- }
- }
-
- public function postQueuedDeletionHandler(Request $request, $id)
- {
- try {
- $repo = new ServerRepository;
- if (! is_null($request->input('cancel'))) {
- $repo->cancelDeletion($id);
- Alert::success('Server deletion has been cancelled. This server will remain suspended until you unsuspend it.')->flash();
-
- return redirect()->route('admin.servers.view', $id);
- } elseif (! is_null($request->input('delete'))) {
- $repo->deleteNow($id);
- Alert::success('Server was successfully deleted from the system.')->flash();
-
- return redirect()->route('admin.servers');
- } elseif (! is_null($request->input('force_delete'))) {
- $repo->deleteNow($id, true);
- Alert::success('Server was successfully force deleted from the system.')->flash();
-
- return redirect()->route('admin.servers');
- }
+ return redirect()->route('admin.servers.view.database', $id)->withInput()->withErrors(json_decode($ex->getMessage()))->withInput();
} catch (DisplayException $ex) {
Alert::danger($ex->getMessage())->flash();
-
- return redirect()->route('admin.servers.view', $id);
} catch (\Exception $ex) {
Log::error($ex);
- Alert::danger('An unhandled error occured while attempting to perform this action.')->flash();
+ Alert::danger('An exception occured while attempting to add a new database for this server. This error has been logged.')->flash();
+ }
- return redirect()->route('admin.servers.view', $id);
+ return redirect()->route('admin.servers.view.database', $id)->withInput();
+ }
+
+ /**
+ * Resets the database password for a specific database on this server.
+ * @param Request $request
+ * @param int $id
+ * @return \Illuminate\Response\RedirectResponse
+ */
+ public function resetDatabasePassword(Request $request, $id)
+ {
+ $database = Models\Database::where('server_id', $id)->findOrFail($request->input('database'));
+ $repo = new DatabaseRepository;
+
+ try {
+ $repo->password($database->id, str_random(20));
+
+ return response('', 204);
+ } catch (\Exception $ex) {
+ Log::error($ex);
+
+ return response()->json(['error' => 'A unhandled exception occurred while attempting to reset this password. This error has been logged.'], 503);
+ }
+ }
+
+ /**
+ * Deletes a database from a server.
+ * @param Request $request
+ * @param int $id
+ * @return \Illuminate\Response\RedirectResponse
+ */
+ public function deleteDatabase(Request $request, $id, $database)
+ {
+ $database = Models\Database::where('server_id', $id)->findOrFail($database);
+ $repo = new DatabaseRepository;
+
+ try {
+ $repo->drop($database->id);
+
+ return response('', 204);
+ } catch (\Exception $ex) {
+ Log::error($ex);
+
+ return response()->json(['error' => 'A unhandled exception occurred while attempting to drop this database. This error has been logged.'], 503);
}
}
}
diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php
index 8f90795c2..0de02eca9 100644
--- a/app/Http/Controllers/Admin/UserController.php
+++ b/app/Http/Controllers/Admin/UserController.php
@@ -47,8 +47,14 @@ class UserController extends Controller
// @TODO: implement nicolaslopezj/searchable to clean up this disaster.
public function getIndex(Request $request)
{
+ $users = User::withCount('servers');
+
+ if (! is_null($request->input('query'))) {
+ $users->search($request->input('query'));
+ }
+
return view('admin.users.index', [
- 'users' => User::paginate(25),
+ 'users' => $users->paginate(25),
]);
}
@@ -124,6 +130,12 @@ class UserController extends Controller
public function getJson(Request $request)
{
- return User::select('email')->get()->pluck('email');
+ return User::select('id', 'email', 'username', 'name_first', 'name_last')
+ ->search($request->input('q'))
+ ->get()->transform(function ($item) {
+ $item->md5 = md5(strtolower($item->email));
+
+ return $item;
+ });
}
}
diff --git a/app/Http/Controllers/Remote/RemoteController.php b/app/Http/Controllers/Remote/RemoteController.php
index 23ae805b6..2e8b782a1 100644
--- a/app/Http/Controllers/Remote/RemoteController.php
+++ b/app/Http/Controllers/Remote/RemoteController.php
@@ -105,27 +105,26 @@ class RemoteController extends Controller
return response('', 201);
}
- public function getConfiguration(Request $request, $tokenString)
+ public function getConfiguration(Request $request, $token)
{
// Try to query the token and the node from the database
try {
- $token = Models\NodeConfigurationToken::where('token', $tokenString)->firstOrFail();
- $node = Models\Node::findOrFail($token->node);
+ $model = Models\NodeConfigurationToken::with('node')->where('token', $token)->firstOrFail();
} catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) {
return response()->json(['error' => 'token_invalid'], 403);
}
// Check if token is expired
- if ($token->expires_at->lt(Carbon::now())) {
- $token->delete();
+ if ($model->created_at->lt(Carbon::now())) {
+ $model->delete();
return response()->json(['error' => 'token_expired'], 403);
}
// Delete the token, it's one-time use
- $token->delete();
+ $model->delete();
// Manually as getConfigurationAsJson() returns it in correct format already
- return response($node->getConfigurationAsJson())->header('Content-Type', 'text/json');
+ return response($model->node->getConfigurationAsJson())->header('Content-Type', 'text/json');
}
}
diff --git a/app/Http/Controllers/Server/AjaxController.php b/app/Http/Controllers/Server/AjaxController.php
index 8f3736da7..4609ae270 100644
--- a/app/Http/Controllers/Server/AjaxController.php
+++ b/app/Http/Controllers/Server/AjaxController.php
@@ -224,17 +224,16 @@ class AjaxController extends Controller
$server = Models\Server::byUuid($uuid);
$this->authorize('reset-db-password', $server);
- $database = Models\Database::where('id', $request->input('database'))->where('server_id', $server->id)->firstOrFail();
+ $database = Models\Database::where('server_id', $server->id)->findOrFail($request->input('database'));
+ $repo = new Repositories\DatabaseRepository;
+
try {
- $repo = new Repositories\DatabaseRepository;
- $password = str_random(16);
- $repo->modifyPassword($request->input('database'), $password);
+ $password = str_random(20);
+ $repo->password($database->id, $password);
return response($password);
- } catch (\Pterodactyl\Exceptions\DisplayException $ex) {
- return response()->json([
- 'error' => $ex->getMessage(),
- ], 503);
+ } catch (DisplayException $ex) {
+ return response()->json(['error' => $ex->getMessage()], 503);
} catch (\Exception $ex) {
Log::error($ex);
diff --git a/app/Http/Controllers/Server/ServerController.php b/app/Http/Controllers/Server/ServerController.php
index 07dee5439..7be93ff61 100644
--- a/app/Http/Controllers/Server/ServerController.php
+++ b/app/Http/Controllers/Server/ServerController.php
@@ -309,9 +309,7 @@ class ServerController extends Controller
try {
$repo = new ServerRepository;
- $repo->updateStartup($server->id, $request->except([
- '_token',
- ]));
+ $repo->updateStartup($server->id, $request->except('_token'));
Alert::success('Server startup variables were successfully updated.')->flash();
} catch (DisplayException $ex) {
Alert::danger($ex->getMessage())->flash();
diff --git a/app/Http/Middleware/AdminAuthenticate.php b/app/Http/Middleware/AdminAuthenticate.php
index c0d74f262..d533fc49d 100644
--- a/app/Http/Middleware/AdminAuthenticate.php
+++ b/app/Http/Middleware/AdminAuthenticate.php
@@ -24,7 +24,6 @@
namespace Pterodactyl\Http\Middleware;
-use Theme;
use Closure;
use Illuminate\Contracts\Auth\Guard;
@@ -69,9 +68,6 @@ class AdminAuthenticate
return abort(403);
}
- // @TODO: eventually update admin themes
- Theme::set('default');
-
return $next($request);
}
}
diff --git a/app/Http/Routes/AdminRoutes.php b/app/Http/Routes/AdminRoutes.php
index 127055daa..c25cb91e3 100644
--- a/app/Http/Routes/AdminRoutes.php
+++ b/app/Http/Routes/AdminRoutes.php
@@ -121,99 +121,118 @@ class AdminRoutes
// View All Servers
$router->get('/', [
'as' => 'admin.servers',
- 'uses' => 'Admin\ServersController@getIndex', ]);
+ 'uses' => 'Admin\ServersController@index',
+ ]);
// View Create Server Page
$router->get('/new', [
'as' => 'admin.servers.new',
- 'uses' => 'Admin\ServersController@getNew',
+ 'uses' => 'Admin\ServersController@new',
]);
// Handle POST Request for Creating Server
$router->post('/new', [
- 'uses' => 'Admin\ServersController@postNewServer',
+ 'uses' => 'Admin\ServersController@create',
]);
// Assorted Page Helpers
- $router->post('/new/get-nodes', [
- 'uses' => 'Admin\ServersController@postNewServerGetNodes',
+ $router->post('/new/nodes', [
+ 'as' => 'admin.servers.new.nodes',
+ 'uses' => 'Admin\ServersController@newServerNodes',
]);
- $router->post('/new/get-ips', [
- 'uses' => 'Admin\ServersController@postNewServerGetIps',
- ]);
-
- $router->post('/new/service-options', [
- 'uses' => 'Admin\ServersController@postNewServerServiceOption',
- ]);
-
- $router->post('/new/option-details', [
- 'uses' => 'Admin\ServersController@postNewServerOptionDetails',
- ]);
- // End Assorted Page Helpers
-
- // View Specific Server
$router->get('/view/{id}', [
'as' => 'admin.servers.view',
- 'uses' => 'Admin\ServersController@getView',
+ 'uses' => 'Admin\ServersController@viewIndex',
]);
- // Database Stuffs
- $router->post('/view/{id}/database', [
- 'as' => 'admin.servers.database',
- 'uses' => 'Admin\ServersController@postDatabase',
+ $router->get('/view/{id}/details', [
+ 'as' => 'admin.servers.view.details',
+ 'uses' => 'Admin\ServersController@viewDetails',
]);
- // Change Server Details
$router->post('/view/{id}/details', [
- 'uses' => 'Admin\ServersController@postUpdateServerDetails',
+ 'uses' => 'Admin\ServersController@setDetails',
]);
- // Change Server Details
- $router->post('/view/{id}/container', [
- 'as' => 'admin.servers.post.container',
- 'uses' => 'Admin\ServersController@postUpdateContainerDetails',
+ $router->post('/view/{id}/details/container', [
+ 'as' => 'admin.servers.view.details.container',
+ 'uses' => 'Admin\ServersController@setContainer',
]);
- // Change Server Details
- $router->post('/view/{id}/startup', [
- 'as' => 'admin.servers.post.startup',
- 'uses' => 'Admin\ServersController@postUpdateServerStartup',
+ $router->get('/view/{id}/build', [
+ 'as' => 'admin.servers.view.build',
+ 'uses' => 'Admin\ServersController@viewBuild',
]);
- // Rebuild Server
- $router->post('/view/{id}/rebuild', [
- 'uses' => 'Admin\ServersController@postUpdateServerToggleBuild',
- ]);
-
- // Change Build Details
$router->post('/view/{id}/build', [
- 'uses' => 'Admin\ServersController@postUpdateServerUpdateBuild',
+ 'uses' => 'Admin\ServersController@updateBuild',
]);
- // Suspend Server
- $router->post('/view/{id}/suspend', [
- 'uses' => 'Admin\ServersController@postSuspendServer',
+ $router->get('/view/{id}/startup', [
+ 'as' => 'admin.servers.view.startup',
+ 'uses' => 'Admin\ServersController@viewStartup',
]);
- // Unsuspend Server
- $router->post('/view/{id}/unsuspend', [
- 'uses' => 'Admin\ServersController@postUnsuspendServer',
+ $router->post('/view/{id}/startup', [
+ 'uses' => 'Admin\ServersController@saveStartup',
]);
- // Change Install Status
- $router->post('/view/{id}/installed', [
- 'uses' => 'Admin\ServersController@postToggleInstall',
+ $router->get('/view/{id}/database', [
+ 'as' => 'admin.servers.view.database',
+ 'uses' => 'Admin\ServersController@viewDatabase',
]);
- // Delete [force delete]
- $router->delete('/view/{id}/{force?}', [
- 'uses' => 'Admin\ServersController@deleteServer',
+ $router->post('/view/{id}/database', [
+ 'uses' => 'Admin\ServersController@newDatabase',
]);
- $router->post('/view/{id}/queuedDeletion', [
- 'uses' => 'Admin\ServersController@postQueuedDeletionHandler',
- 'as' => 'admin.servers.post.queuedDeletion',
+ $router->patch('/view/{id}/database', [
+ 'uses' => 'Admin\ServersController@resetDatabasePassword',
+ ]);
+
+ $router->delete('/view/{id}/database/{database}/delete', [
+ 'as' => 'admin.servers.view.database.delete',
+ 'uses' => 'Admin\ServersController@deleteDatabase',
+ ]);
+
+ $router->get('/view/{id}/manage', [
+ 'as' => 'admin.servers.view.manage',
+ 'uses' => 'Admin\ServersController@viewManage',
+ ]);
+
+ $router->post('/view/{id}/manage/toggle', [
+ 'as' => 'admin.servers.view.manage.toggle',
+ 'uses' => 'Admin\ServersController@toggleInstall',
+ ]);
+
+ $router->post('/view/{id}/manage/rebuild', [
+ 'as' => 'admin.servers.view.manage.rebuild',
+ 'uses' => 'Admin\ServersController@rebuildContainer',
+ ]);
+
+ $router->post('/view/{id}/manage/suspension', [
+ 'as' => 'admin.servers.view.manage.suspension',
+ 'uses' => 'Admin\ServersController@manageSuspension',
+ ]);
+
+ $router->get('/view/{id}/delete', [
+ 'as' => 'admin.servers.view.delete',
+ 'uses' => 'Admin\ServersController@viewDelete',
+ ]);
+
+ $router->post('/view/{id}/delete', [
+ 'uses' => 'Admin\ServersController@delete',
+ ]);
+
+ $router->post('/view/{id}/delete/continue/{force?}', [
+ 'as' => 'admin.servers.view.delete.continue',
+ 'uses' => 'Admin\ServersController@continueDeletion',
+ ]);
+
+ $router->post('/view/{id}/delete/cancel', [
+ 'as' => 'admin.servers.view.delete.cancel',
+ 'uses' => 'Admin\ServersController@cancelDeletion',
]);
});
@@ -230,66 +249,75 @@ class AdminRoutes
// View All Nodes
$router->get('/', [
'as' => 'admin.nodes',
- 'uses' => 'Admin\NodesController@getIndex',
+ 'uses' => 'Admin\NodesController@index',
]);
// Add New Node
$router->get('/new', [
'as' => 'admin.nodes.new',
- 'uses' => 'Admin\NodesController@getNew',
+ 'uses' => 'Admin\NodesController@new',
]);
$router->post('/new', [
- 'uses' => 'Admin\NodesController@postNew',
+ 'uses' => 'Admin\NodesController@create',
]);
- // View Node
$router->get('/view/{id}', [
'as' => 'admin.nodes.view',
- 'uses' => 'Admin\NodesController@getView',
+ 'uses' => 'Admin\NodesController@viewIndex',
]);
- $router->post('/view/{id}', [
- 'uses' => 'Admin\NodesController@postView',
+ $router->get('/view/{id}/settings', [
+ 'as' => 'admin.nodes.view.settings',
+ 'uses' => 'Admin\NodesController@viewSettings',
]);
- $router->delete('/view/{id}/deallocate/single/{allocation}', [
- 'uses' => 'Admin\NodesController@deallocateSingle',
+ $router->post('/view/{id}/settings', [
+ 'uses' => 'Admin\NodesController@updateSettings',
]);
- $router->post('/view/{id}/deallocate/block', [
- 'uses' => 'Admin\NodesController@deallocateBlock',
+ $router->get('/view/{id}/configuration', [
+ 'as' => 'admin.nodes.view.configuration',
+ 'uses' => 'Admin\NodesController@viewConfiguration',
]);
- $router->post('/view/{id}/alias', [
- 'as' => 'admin.nodes.alias',
- 'uses' => 'Admin\NodesController@setAlias',
+ $router->get('/view/{id}/allocation', [
+ 'as' => 'admin.nodes.view.allocation',
+ 'uses' => 'Admin\NodesController@viewAllocation',
]);
- $router->get('/view/{id}/allocations.json', [
- 'as' => 'admin.nodes.view.allocations',
- 'uses' => 'Admin\NodesController@getAllocationsJson',
+ $router->post('/view/{id}/allocation', [
+ 'uses' => 'Admin\NodesController@createAllocation',
]);
- $router->post('/view/{id}/allocations', [
- 'as' => 'admin.nodes.post.allocations',
- 'uses' => 'Admin\NodesController@postAllocations',
+ $router->get('/view/{id}/servers', [
+ 'as' => 'admin.nodes.view.servers',
+ 'uses' => 'Admin\NodesController@viewServers',
]);
- // View Deploy
- $router->get('/view/{id}/deploy', [
- 'as' => 'admin.nodes.deply',
- 'uses' => 'Admin\NodesController@getScript',
+ $router->delete('/view/{id}/delete', [
+ 'as' => 'admin.nodes.view.delete',
+ 'uses' => 'Admin\NodesController@delete',
]);
- $router->delete('/view/{id}', [
- 'as' => 'admin.nodes.delete',
- 'uses' => 'Admin\NodesController@deleteNode',
+ $router->delete('/view/{id}/allocation/remove/{allocation}', [
+ 'as' => 'admin.nodes.view.allocation.removeSingle',
+ 'uses' => 'Admin\NodesController@allocationRemoveSingle',
]);
- $router->get('/{id}/configurationtoken', [
- 'as' => 'admin.nodes.configuration-token',
- 'uses' => 'Admin\NodesController@getConfigurationToken',
+ $router->post('/view/{id}/allocation/remove', [
+ 'as' => 'admin.nodes.view.allocation.removeBlock',
+ 'uses' => 'Admin\NodesController@allocationRemoveBlock',
+ ]);
+
+ $router->post('/view/{id}/allocation/alias', [
+ 'as' => 'admin.nodes.view.allocation.setAlias',
+ 'uses' => 'Admin\NodesController@allocationSetAlias',
+ ]);
+
+ $router->get('/view/{id}/settings/token', [
+ 'as' => 'admin.nodes.view.configuration.token',
+ 'uses' => 'Admin\NodesController@setToken',
]);
});
diff --git a/app/Jobs/DeleteServer.php b/app/Jobs/DeleteServer.php
index a42637ff2..16d04b4ba 100644
--- a/app/Jobs/DeleteServer.php
+++ b/app/Jobs/DeleteServer.php
@@ -58,6 +58,6 @@ class DeleteServer extends Job implements ShouldQueue
public function handle()
{
$repo = new ServerRepository;
- $repo->deleteNow($this->id);
+ $repo->delete($this->id);
}
}
diff --git a/app/Models/Node.php b/app/Models/Node.php
index 520df2a5e..928633095 100644
--- a/app/Models/Node.php
+++ b/app/Models/Node.php
@@ -27,10 +27,11 @@ namespace Pterodactyl\Models;
use GuzzleHttp\Client;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notifiable;
+use Nicolaslopezj\Searchable\SearchableTrait;
class Node extends Model
{
- use Notifiable;
+ use Notifiable, SearchableTrait;
/**
* The table associated with the model.
@@ -74,6 +75,18 @@ class Node extends Model
'daemonSFTP', 'daemonListen',
];
+ protected $searchable = [
+ 'columns' => [
+ 'nodes.name' => 10,
+ 'nodes.fqdn' => 8,
+ 'locations.short' => 4,
+ 'locations.long' => 4,
+ ],
+ 'joins' => [
+ 'locations' => ['locations.id', 'nodes.location_id'],
+ ],
+ ];
+
/**
* Return an instance of the Guzzle client for this specific node.
*
@@ -117,10 +130,6 @@ class Node extends Model
'port' => $this->daemonSFTP,
'container' => 'ptdl-sftp',
],
- 'query' => [
- 'kill_on_fail' => true,
- 'fail_limit' => 5,
- ],
'logger' => [
'path' => 'logs/',
'src' => false,
diff --git a/app/Models/NodeConfigurationToken.php b/app/Models/NodeConfigurationToken.php
index dd029ec78..b09e096bd 100644
--- a/app/Models/NodeConfigurationToken.php
+++ b/app/Models/NodeConfigurationToken.php
@@ -48,4 +48,14 @@ class NodeConfigurationToken extends Model
* @var array
*/
protected $dates = ['created_at', 'updated_at', 'expires_at'];
+
+ /**
+ * Gets the node associated with a configuration token.
+ *
+ * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
+ */
+ public function node()
+ {
+ return $this->belongsTo(Node::class);
+ }
}
diff --git a/app/Models/Server.php b/app/Models/Server.php
index 7c69c763f..524708cde 100644
--- a/app/Models/Server.php
+++ b/app/Models/Server.php
@@ -26,14 +26,16 @@ namespace Pterodactyl\Models;
use Auth;
use Cache;
+use Carbon;
use Javascript;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\SoftDeletes;
+use Nicolaslopezj\Searchable\SearchableTrait;
class Server extends Model
{
- use Notifiable, SoftDeletes;
+ use Notifiable, SearchableTrait, SoftDeletes;
/**
* The table associated with the model.
@@ -85,6 +87,22 @@ class Server extends Model
'installed' => 'integer',
];
+ protected $searchable = [
+ 'columns' => [
+ 'servers.name' => 10,
+ 'servers.username' => 10,
+ 'servers.uuidShort' => 9,
+ 'servers.uuid' => 8,
+ 'users.email' => 6,
+ 'users.username' => 6,
+ 'nodes.name' => 2,
+ ],
+ 'joins' => [
+ 'users' => ['users.id', 'servers.owner_id'],
+ 'nodes' => ['nodes.id', 'servers.node_id'],
+ ],
+ ];
+
/**
* Returns a single server specified by UUID.
* DO NOT USE THIS TO MODIFY SERVER DETAILS OR SAVE THOSE DETAILS.
@@ -95,8 +113,12 @@ class Server extends Model
*/
public static function byUuid($uuid)
{
+ if (! Auth::check()) {
+ throw new \Exception('You must call Server:byUuid as an authenticated user.');
+ }
+
// Results are cached because we call this functions a few times on page load.
- $result = Cache::remember('Server.byUuid.' . $uuid . Auth::user()->uuid, 60, function () use ($uuid) {
+ $result = Cache::tags(['Model:Server', 'Model:Server:byUuid:' . $uuid])->remember('Model:Server:byUuid:' . $uuid . Auth::user()->uuid, Carbon::now()->addMinutes(15), function () use ($uuid) {
$query = self::with('service', 'node')->where(function ($q) use ($uuid) {
$q->where('uuidShort', $uuid)->orWhere('uuid', $uuid);
});
diff --git a/app/Models/User.php b/app/Models/User.php
index 131192264..b660e304c 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -30,6 +30,7 @@ use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notifiable;
use Pterodactyl\Exceptions\DisplayException;
+use Nicolaslopezj\Searchable\SearchableTrait;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Foundation\Auth\Access\Authorizable;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
@@ -39,7 +40,7 @@ use Pterodactyl\Notifications\SendPasswordReset as ResetPasswordNotification;
class User extends Model implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract
{
- use Authenticatable, Authorizable, CanResetPassword, Notifiable;
+ use Authenticatable, Authorizable, CanResetPassword, Notifiable, SearchableTrait;
/**
* The rules for user passwords.
@@ -87,6 +88,16 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
*/
protected $hidden = ['password', 'remember_token', 'totp_secret'];
+ protected $searchable = [
+ 'columns' => [
+ 'email' => 10,
+ 'username' => 9,
+ 'name_first' => 6,
+ 'name_last' => 6,
+ 'uuid' => 1,
+ ],
+ ];
+
/**
* Enables or disables TOTP on an account if the token is valid.
*
diff --git a/app/Observers/ServerObserver.php b/app/Observers/ServerObserver.php
index 5d919a559..92d1b8f9a 100644
--- a/app/Observers/ServerObserver.php
+++ b/app/Observers/ServerObserver.php
@@ -24,7 +24,6 @@
namespace Pterodactyl\Observers;
-use Auth;
use Cache;
use Carbon;
use Pterodactyl\Events;
@@ -41,8 +40,8 @@ class ServerObserver
/**
* Listen to the Server creating event.
*
- * @param Server $server [description]
- * @return [type] [description]
+ * @param Server $server The server model.
+ * @return void
*/
public function creating(Server $server)
{
@@ -52,8 +51,8 @@ class ServerObserver
/**
* Listen to the Server created event.
*
- * @param Server $server [description]
- * @return [type] [description]
+ * @param Server $server The server model.
+ * @return void
*/
public function created(Server $server)
{
@@ -73,8 +72,8 @@ class ServerObserver
/**
* Listen to the Server deleting event.
*
- * @param Server $server [description]
- * @return [type] [description]
+ * @param Server $server The server model.
+ * @return void
*/
public function deleting(Server $server)
{
@@ -86,8 +85,8 @@ class ServerObserver
/**
* Listen to the Server deleted event.
*
- * @param Server $server [description]
- * @return [type] [description]
+ * @param Server $server The server model.
+ * @return void
*/
public function deleted(Server $server)
{
@@ -103,8 +102,8 @@ class ServerObserver
/**
* Listen to the Server saving event.
*
- * @param Server $server [description]
- * @return [type] [description]
+ * @param Server $server The server model.
+ * @return void
*/
public function saving(Server $server)
{
@@ -114,8 +113,8 @@ class ServerObserver
/**
* Listen to the Server saved event.
*
- * @param Server $server [description]
- * @return [type] [description]
+ * @param Server $server The server model.
+ * @return void
*/
public function saved(Server $server)
{
@@ -123,10 +122,10 @@ class ServerObserver
}
/**
- * Listen to the Server saving event.
+ * Listen to the Server updating event.
*
- * @param Server $server [description]
- * @return [type] [description]
+ * @param Server $server The server model.
+ * @return void
*/
public function updating(Server $server)
{
@@ -136,14 +135,20 @@ class ServerObserver
/**
* Listen to the Server saved event.
*
- * @param Server $server [description]
- * @return [type] [description]
+ * @param Server $server The server model.
+ * @return void
*/
public function updated(Server $server)
{
- // Clear Caches
- Cache::forget('Server.byUuid.' . $server->uuid . Auth::user()->uuid);
- Cache::forget('Server.byUuid.' . $server->uuidShort . Auth::user()->uuid);
+ /*
+ * The cached byUuid model calls are tagged with Model:Server:byUuid:' . $port . '
is invalid and cannot be processed.');
}
- }
- foreach ($parsedIP as $ip) {
- foreach ($ports as $port) {
- if (! is_int($port) && ! preg_match('/^(\d{1,5})-(\d{1,5})$/', $port)) {
- throw new DisplayException('The mapping for ' . $port . ' is invalid and cannot be processed.');
+
+ if (preg_match('/^(\d{1,5})-(\d{1,5})$/', $port, $matches)) {
+ $block = range($matches[1], $matches[2]);
+
+ if (count($block) > 1000) {
+ throw new DisplayException('Adding more than 1000 ports at once is not supported. Please use a smaller port range.');
}
- if (preg_match('/^(\d{1,5})-(\d{1,5})$/', $port, $matches)) {
- $portBlock = range($matches[1], $matches[2]);
- if (count($portBlock) > 2000) {
- throw new DisplayException('Adding more than 2000 ports at once is not currently supported. Please consider using a smaller port range.');
- }
-
- foreach ($portBlock as $assignPort) {
- $alloc = Models\Allocation::firstOrNew([
- 'node_id' => $node->id,
- 'ip' => $ip,
- 'port' => $assignPort,
- ]);
- if (! $alloc->exists) {
- $alloc->fill([
- 'node_id' => $node->id,
- 'ip' => $ip,
- 'port' => $assignPort,
- 'ip_alias' => $setAlias,
- 'server_id' => null,
- ]);
- $alloc->save();
- }
- }
- } else {
- $alloc = Models\Allocation::firstOrNew([
+ foreach ($block as $unit) {
+ // Insert into Database
+ Models\Allocation::firstOrCreate([
'node_id' => $node->id,
'ip' => $ip,
- 'port' => $port,
+ 'port' => $unit,
+ 'ip_alias' => isset($data['allocation_alias']) ? $data['allocation_alias'] : null,
+ 'server_id' => null,
]);
- if (! $alloc->exists) {
- $alloc->fill([
- 'node_id' => $node->id,
- 'ip' => $ip,
- 'port' => $port,
- 'ip_alias' => $setAlias,
- 'server_id' => null,
- ]);
- $alloc->save();
- }
}
+ } else {
+ // Insert into Database
+ Models\Allocation::firstOrCreate([
+ 'node_id' => $node->id,
+ 'ip' => $ip,
+ 'port' => $port,
+ 'ip_alias' => isset($data['allocation_alias']) ? $data['allocation_alias'] : null,
+ 'server_id' => null,
+ ]);
}
}
}
-
- DB::commit();
- } catch (\Exception $ex) {
- DB::rollBack();
- throw $ex;
- }
+ });
}
public function delete($id)
diff --git a/app/Repositories/ServerRepository.php b/app/Repositories/ServerRepository.php
index 4b5ed1f15..0e4e0f099 100644
--- a/app/Repositories/ServerRepository.php
+++ b/app/Repositories/ServerRepository.php
@@ -83,7 +83,7 @@ class ServerRepository
// Validate Fields
$validator = Validator::make($data, [
- 'owner' => 'bail|required',
+ 'user_id' => 'required|exists:users,id',
'name' => 'required|regex:/^([\w .-]{1,200})$/',
'memory' => 'required|numeric|min:0',
'swap' => 'required|numeric|min:-1',
@@ -95,25 +95,20 @@ class ServerRepository
'location_id' => 'required|numeric|min:1|exists:locations,id',
'pack_id' => 'sometimes|nullable|numeric|min:0',
'startup' => 'string',
- 'custom_image_name' => 'required_if:use_custom_image,on',
'auto_deploy' => 'sometimes|boolean',
'custom_id' => 'sometimes|required|numeric|unique:servers,id',
]);
- $validator->sometimes('node_id', 'bail|required|numeric|min:1|exists:nodes,id', function ($input) {
+ $validator->sometimes('node_id', 'required|numeric|min:1|exists:nodes,id', function ($input) {
return ! ($input->auto_deploy);
});
- $validator->sometimes('ip', 'required|ip', function ($input) {
- return ! $input->auto_deploy && ! $input->allocation;
+ $validator->sometimes('allocation_id', 'required|numeric|exists:allocations,id', function ($input) {
+ return ! ($input->auto_deploy);
});
- $validator->sometimes('port', 'required|numeric|min:1|max:65535', function ($input) {
- return ! $input->auto_deploy && ! $input->allocation;
- });
-
- $validator->sometimes('allocation_id', 'numeric|exists:allocations,id', function ($input) {
- return ! ($input->auto_deploy || ($input->port && $input->ip));
+ $validator->sometimes('allocation_additional.*', 'sometimes|required|numeric|exists:allocations,id', function ($input) {
+ return ! ($input->auto_deploy);
});
// Run validator, throw catchable and displayable exception if it fails.
@@ -122,16 +117,13 @@ class ServerRepository
throw new DisplayValidationException($validator->errors());
}
- $user = Models\User::select('id', 'email')->where((is_int($data['owner'])) ? 'id' : 'email', $data['owner'])->first();
- if (! $user) {
- throw new DisplayException('The user id or email passed to the function was not found on the system.');
- }
+ $user = Models\User::findOrFail($data['user_id']);
$autoDeployed = false;
- if (isset($data['auto_deploy']) && in_array($data['auto_deploy'], [true, 1, '1'])) {
+ if (isset($data['auto_deploy']) && $data['auto_deploy']) {
// This is an auto-deployment situation
// Ignore any other passed node data
- unset($data['node_id'], $data['ip'], $data['port'], $data['allocation_id']);
+ unset($data['node_id'], $data['allocation_id']);
$autoDeployed = true;
$node = DeploymentService::smartRandomNode($data['memory'], $data['disk'], $data['location_id']);
@@ -143,17 +135,12 @@ class ServerRepository
// Verify IP & Port are a.) free and b.) assigned to the node.
// We know the node exists because of 'exists:nodes,id' in the validation
if (! $autoDeployed) {
- if (! isset($data['allocation_id'])) {
- $model = Models\Allocation::where('ip', $data['ip'])->where('port', $data['port']);
- } else {
- $model = Models\Allocation::where('id', $data['allocation_id']);
- }
- $allocation = $model->where('node_id', $data['node_id'])->whereNull('server_id')->first();
+ $allocation = Models\Allocation::where('id', $data['allocation_id'])->where('node_id', $data['node_id'])->whereNull('server_id')->first();
}
// Something failed in the query, either that combo doesn't exist, or it is in use.
if (! $allocation) {
- throw new DisplayException('The selected IP/Port combination or Allocation ID is either already in use, or unavaliable for this node.');
+ throw new DisplayException('The selected Allocation ID is either already in use, or unavaliable for this node.');
}
// Validate those Service Option Variables
@@ -166,11 +153,9 @@ class ServerRepository
}
// Validate the Pack
- if ($data['pack_id'] == 0) {
+ if (! isset($data['pack_id']) || (int) $data['pack_id'] < 1) {
$data['pack_id'] = null;
- }
-
- if (! is_null($data['pack_id'])) {
+ } else {
$pack = Models\ServicePack::where('id', $data['pack_id'])->where('option_id', $data['option_id'])->first();
if (! $pack) {
throw new DisplayException('The requested service pack does not seem to exist for this combination.');
@@ -188,7 +173,7 @@ class ServerRepository
// Is the variable required?
if (! isset($data['env_' . $variable->env_variable])) {
- if ($variable->required === 1) {
+ if ($variable->required) {
throw new DisplayException('A required service option variable field (env_' . $variable->env_variable . ') was missing from the request.');
}
$variableList[] = [
@@ -271,7 +256,7 @@ class ServerRepository
'pack_id' => $data['pack_id'],
'startup' => $data['startup'],
'daemonSecret' => $uuid->generate('servers', 'daemonSecret'),
- 'image' => (isset($data['custom_image_name'])) ? $data['custom_image_name'] : $option->docker_image,
+ 'image' => (isset($data['custom_container'])) ? $data['custom_container'] : $option->docker_image,
'username' => $this->generateSFTPUsername($data['name'], $genShortUuid),
'sftp_password' => Crypt::encrypt('not set'),
]);
@@ -281,6 +266,19 @@ class ServerRepository
$allocation->server_id = $server->id;
$allocation->save();
+ // Add Additional Allocations
+ if (isset($data['allocation_additional']) && is_array($data['allocation_additional'])) {
+ foreach ($data['allocation_additional'] as $allocation) {
+ $model = Models\Allocation::where('id', $allocation)->where('node_id', $data['node_id'])->whereNull('server_id')->first();
+ if (! $model) {
+ continue;
+ }
+
+ $model->server_id = $server->id;
+ $model->save();
+ }
+ }
+
// Add Variables
$environmentVariables = [
'STARTUP' => $data['startup'],
@@ -296,25 +294,26 @@ class ServerRepository
]);
}
+ $server->load('allocation', 'allocations');
$node->guzzleClient(['X-Access-Token' => $node->daemonSecret])->request('POST', '/servers', [
'json' => [
'uuid' => (string) $server->uuid,
'user' => $server->username,
'build' => [
'default' => [
- 'ip' => $allocation->ip,
- 'port' => (int) $allocation->port,
- ],
- 'ports' => [
- (string) $allocation->ip => [(int) $allocation->port],
+ 'ip' => $server->allocation->ip,
+ 'port' => $server->allocation->port,
],
+ 'ports' => $server->allocations->groupBy('ip')->map(function ($item) {
+ return $item->pluck('port');
+ })->toArray(),
'env' => $environmentVariables,
'memory' => (int) $server->memory,
'swap' => (int) $server->swap,
'io' => (int) $server->io,
'cpu' => (int) $server->cpu,
'disk' => (int) $server->disk,
- 'image' => (isset($data['custom_image_name'])) ? $data['custom_image_name'] : $option->docker_image,
+ 'image' => (isset($data['custom_container'])) ? $data['custom_container'] : $option->docker_image,
],
'service' => [
'type' => $service->file,
@@ -353,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|integer|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.
@@ -369,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']) && (int) $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']) && (int) $data['owner_id'] !== $server->user->id) {
+ $server->owner_id = $data['owner_id'];
}
// Update Server Name if it was passed.
@@ -432,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.
@@ -445,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([
@@ -464,7 +463,7 @@ class ServerRepository
return true;
} catch (TransferException $ex) {
DB::rollBack();
- throw new DisplayException('An error occured while attempting to update the container image.', $ex);
+ throw new DisplayException('A TransferException occured while attempting to update the container image. Is the daemon online? This error has been logged.', $ex);
} catch (\Exception $ex) {
DB::rollBack();
throw $ex;
@@ -480,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.
@@ -504,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,
]);
}
@@ -549,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,
]);
}
@@ -571,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
@@ -631,10 +603,10 @@ 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);
+ throw new DisplayException('A TransferException occured while attempting to update the server configuration, check that the daemon is online. This error has been logged.', $ex);
} catch (\Exception $ex) {
DB::rollBack();
throw $ex;
@@ -645,87 +617,63 @@ class ServerRepository
{
$server = Models\Server::with('variables', 'option.variables')->findOrFail($id);
- DB::beginTransaction();
-
- try {
- // Check the startup
+ DB::transaction(function () use ($admin, $data, $server) {
if (isset($data['startup']) && $admin) {
$server->startup = $data['startup'];
$server->save();
}
- // Check those Variables
- $server->option->variables->transform(function ($item, $key) use ($server) {
- $displayValue = $server->variables->where('variable_id', $item->id)->pluck('variable_value')->first();
- $item->server_value = (! is_null($displayValue)) ? $displayValue : $item->default_value;
-
- return $item;
- });
-
- $variableList = [];
if ($server->option->variables) {
foreach ($server->option->variables as &$variable) {
- // Move on if the new data wasn't even sent
- if (! isset($data[$variable->env_variable])) {
- $variableList[] = [
- 'id' => $variable->id,
- 'env' => $variable->env_variable,
- 'val' => $variable->server_value,
- ];
+ $set = isset($data['env_' . $variable->id]);
+
+ // Variable is required but was not passed into the function.
+ if ($variable->required && ! $set) {
+ throw new DisplayException('A required variable (' . $variable->env_variable . ') was not passed in the request.');
+ }
+
+ // If user is not an admin and are trying to edit a non-editable field
+ // or an invisible field just silently skip the variable.
+ if (! $admin && (! $variable->user_editable || ! $variable->user_viewable)) {
continue;
}
- // Update Empty but skip validation
- if (empty($data[$variable->env_variable])) {
- $variableList[] = [
- 'id' => $variable->id,
- 'env' => $variable->env_variable,
- 'val' => null,
- ];
- continue;
- }
-
- // Is the variable required?
- // @TODO: is this even logical to perform this check?
- if (isset($data[$variable->env_variable]) && empty($data[$variable->env_variable])) {
- if ($variable->required) {
- throw new DisplayException('A required service option variable field (' . $variable->env_variable . ') was included in this request but was left blank.');
+ // Confirm value is valid when compared aganist regex.
+ // @TODO: switch to Laravel validation rules.
+ if ($set && ! is_null($variable->regex)) {
+ if (! preg_match($variable->regex, $data['env_' . $variable->id])) {
+ throw new DisplayException('The value passed for a variable (' . $variable->env_variable . ') could not be matched aganist the regex for that field (' . $variable->regex . ').');
}
}
- // Variable hidden and/or not user editable
- if ((! $variable->user_viewable || ! $variable->user_editable) && ! $admin) {
- throw new DisplayException('A service option variable field (' . $variable->env_variable . ') does not exist or you do not have permission to edit it.');
+ $svar = Models\ServerVariable::firstOrNew([
+ 'server_id' => $server->id,
+ 'variable_id' => $variable->id,
+ ]);
+
+ // Set the value; if one was not passed set it to the default value
+ if ($set) {
+ $svar->variable_value = $data['env_' . $variable->id];
+
+ // Not passed, check if this record exists if so keep value, otherwise set default
+ } else {
+ $svar->variable_value = ($svar->exists) ? $svar->variable_value : $variable->default_value;
}
- // Check aganist Regex Pattern
- if (! is_null($variable->regex) && ! preg_match($variable->regex, $data[$variable->env_variable])) {
- throw new DisplayException('Failed to validate service option variable field (' . $variable->env_variable . ') aganist regex (' . $variable->regex . ').');
- }
-
- $variableList[] = [
- 'id' => $variable->id,
- 'env' => $variable->env_variable,
- 'val' => $data[$variable->env_variable],
- ];
+ $svar->save();
}
}
- // Add Variables
- $environmentVariables = [
- 'STARTUP' => $server->startup,
- ];
- foreach ($variableList as $item) {
- $environmentVariables[$item['env']] = $item['val'];
+ // Reload Variables
+ $server->load('variables');
+ $environment = $server->option->variables->map(function ($item, $key) use ($server) {
+ $display = $server->variables->where('variable_id', $item->id)->pluck('variable_value')->first();
- // Update model or make a new record if it doesn't exist.
- $model = Models\ServerVariable::firstOrNew([
- 'variable_id' => $item['id'],
- 'server_id' => $server->id,
- ]);
- $model->variable_value = $item['val'];
- $model->save();
- }
+ return [
+ 'variable' => $item->env_variable,
+ 'value' => (! is_null($display)) ? $display : $item->default_value,
+ ];
+ });
$server->node->guzzleClient([
'X-Access-Server' => $server->uuid,
@@ -733,30 +681,20 @@ class ServerRepository
])->request('PATCH', '/server', [
'json' => [
'build' => [
- 'env|overwrite' => $environmentVariables,
+ 'env|overwrite' => $environment->pluck('value', 'variable')->merge(['STARTUP' => $server->startup]),
],
],
]);
-
- DB::commit();
-
- return true;
- } catch (TransferException $ex) {
- DB::rollBack();
- throw new DisplayException('An error occured while attempting to update the server configuration.', $ex);
- } catch (\Exception $ex) {
- DB::rollBack();
- throw $ex;
- }
+ });
}
- public function deleteServer($id, $force)
+ public function queueDeletion($id, $force = false)
{
$server = Models\Server::findOrFail($id);
DB::beginTransaction();
try {
- if ($force === 'force' || $force) {
+ if ($force) {
$server->installed = 3;
$server->save();
}
@@ -770,7 +708,7 @@ class ServerRepository
}
}
- public function deleteNow($id, $force = false)
+ public function delete($id, $force = false)
{
$server = Models\Server::withTrashed()->with('node')->findOrFail($id);
@@ -807,10 +745,12 @@ class ServerRepository
// Delete Databases
// This is the one un-recoverable point where
// transactions will not save us.
- $repository = new DatabaseRepository;
- foreach (Models\Database::select('id')->where('server_id', $server->id)->get() as &$database) {
- $repository->drop($database->id);
- }
+ //
+ // @TODO: move to post-deletion event as a queued task!
+ // $repository = new DatabaseRepository;
+ // foreach (Models\Database::select('id')->where('server_id', $server->id)->get() as &$database) {
+ // $repository->drop($database->id);
+ // }
$server->node->guzzleClient([
'X-Access-Token' => $server->node->daemonSecret,
@@ -846,8 +786,8 @@ class ServerRepository
public function toggleInstall($id)
{
$server = Models\Server::findOrFail($id);
- if ($server->installed === 2) {
- throw new DisplayException('This server was marked as having a failed install, you cannot override this.');
+ if ($server->installed > 1) {
+ throw new DisplayException('This server was marked as having a failed install or being deleted, you cannot override this.');
}
$server->installed = ! $server->installed;
diff --git a/composer.json b/composer.json
index 40bee8bcb..a95d82df1 100644
--- a/composer.json
+++ b/composer.json
@@ -29,7 +29,8 @@
"predis/predis": "1.1.1",
"fideloper/proxy": "3.2.0",
"laracasts/utilities": "2.1.0",
- "lord/laroute": "2.3.0"
+ "lord/laroute": "2.3.0",
+ "nicolaslopezj/searchable": "1.9.5"
},
"require-dev": {
"fzaninotto/faker": "~1.4",
diff --git a/composer.lock b/composer.lock
index 22e5c4e3b..535af7aa2 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,8 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "hash": "49714983a18ad2bba4759ccde45314c5",
- "content-hash": "af8cd5b69f96dd17c1e02afc1ba8e467",
+ "hash": "6a9656aff0fb3809d27a2a093a810197",
+ "content-hash": "8affaad10f155172b5079a72015b8bc5",
"packages": [
{
"name": "aws/aws-sdk-php",
@@ -2011,6 +2011,52 @@
],
"time": "2015-11-04 20:07:17"
},
+ {
+ "name": "nicolaslopezj/searchable",
+ "version": "1.9.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nicolaslopezj/searchable.git",
+ "reference": "1351b1b21ae9be9e0f49f375f56488df839723d4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nicolaslopezj/searchable/zipball/1351b1b21ae9be9e0f49f375f56488df839723d4",
+ "reference": "1351b1b21ae9be9e0f49f375f56488df839723d4",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "illuminate/database": "4.2.x|~5.0",
+ "php": ">=5.4.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Nicolaslopezj\\Searchable\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Lopez",
+ "email": "nicolaslopezj@me.com"
+ }
+ ],
+ "description": "Eloquent model search trait.",
+ "keywords": [
+ "database",
+ "eloquent",
+ "laravel",
+ "model",
+ "search",
+ "searchable"
+ ],
+ "time": "2016-12-16 21:23:34"
+ },
{
"name": "nikic/php-parser",
"version": "v2.1.1",
diff --git a/config/javascript.php b/config/javascript.php
index 2a5cda584..1c2ef82dc 100644
--- a/config/javascript.php
+++ b/config/javascript.php
@@ -15,6 +15,7 @@ return [
*/
'bind_js_vars_to_this_view' => [
'layouts.master',
+ 'layouts.admin',
],
/*
diff --git a/database/migrations/2017_03_03_224254_UpdateNodeConfigTokensColumns.php b/database/migrations/2017_03_03_224254_UpdateNodeConfigTokensColumns.php
new file mode 100644
index 000000000..5931518d6
--- /dev/null
+++ b/database/migrations/2017_03_03_224254_UpdateNodeConfigTokensColumns.php
@@ -0,0 +1,40 @@
+dropForeign(['node']);
+ $table->dropColumn('expires_at');
+ $table->renameColumn('node', 'node_id');
+
+ $table->foreign('node_id')->references('id')->on('nodes');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table('node_configuration_tokens', function (Blueprint $table) {
+ $table->dropForeign(['node_id']);
+ $table->renameColumn('node_id', 'node');
+ $table->timestamp('expires_at')->after('token');
+
+ $table->foreign('node')->references('id')->on('nodes');
+ });
+ }
+}
diff --git a/public/js/laroute.js b/public/js/laroute.js
index 633b139f9..b4f553912 100644
--- a/public/js/laroute.js
+++ b/public/js/laroute.js
@@ -6,7 +6,7 @@
absolute: false,
rootUrl: 'http://pterodactyl.app',
- routes : [{"host":null,"methods":["GET","HEAD"],"uri":"admin","name":"admin.index","action":"Pterodactyl\Http\Controllers\Admin\BaseController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/settings","name":"admin.settings","action":"Pterodactyl\Http\Controllers\Admin\BaseController@getSettings"},{"host":null,"methods":["POST"],"uri":"admin\/settings","name":null,"action":"Pterodactyl\Http\Controllers\Admin\BaseController@postSettings"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users","name":"admin.users","action":"Pterodactyl\Http\Controllers\Admin\UserController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/accounts.json","name":"admin.users.json","action":"Pterodactyl\Http\Controllers\Admin\UserController@getJson"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/view\/{id}","name":"admin.users.view","action":"Pterodactyl\Http\Controllers\Admin\UserController@getView"},{"host":null,"methods":["POST"],"uri":"admin\/users\/view\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@updateUser"},{"host":null,"methods":["DELETE"],"uri":"admin\/users\/view\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@deleteUser"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/new","name":"admin.users.new","action":"Pterodactyl\Http\Controllers\Admin\UserController@getNew"},{"host":null,"methods":["POST"],"uri":"admin\/users\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@postNew"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers","name":"admin.servers","action":"Pterodactyl\Http\Controllers\Admin\ServersController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/new","name":"admin.servers.new","action":"Pterodactyl\Http\Controllers\Admin\ServersController@getNew"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@postNewServer"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/new\/get-nodes","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@postNewServerGetNodes"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/new\/get-ips","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@postNewServerGetIps"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/new\/service-options","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@postNewServerServiceOption"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/new\/option-details","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@postNewServerOptionDetails"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{id}","name":"admin.servers.view","action":"Pterodactyl\Http\Controllers\Admin\ServersController@getView"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/database","name":"admin.servers.database","action":"Pterodactyl\Http\Controllers\Admin\ServersController@postDatabase"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/details","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@postUpdateServerDetails"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/container","name":"admin.servers.post.container","action":"Pterodactyl\Http\Controllers\Admin\ServersController@postUpdateContainerDetails"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/startup","name":"admin.servers.post.startup","action":"Pterodactyl\Http\Controllers\Admin\ServersController@postUpdateServerStartup"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/rebuild","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@postUpdateServerToggleBuild"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/build","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@postUpdateServerUpdateBuild"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/suspend","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@postSuspendServer"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/unsuspend","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@postUnsuspendServer"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/installed","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@postToggleInstall"},{"host":null,"methods":["DELETE"],"uri":"admin\/servers\/view\/{id}\/{force?}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@deleteServer"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/queuedDeletion","name":"admin.servers.post.queuedDeletion","action":"Pterodactyl\Http\Controllers\Admin\ServersController@postQueuedDeletionHandler"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes","name":"admin.nodes","action":"Pterodactyl\Http\Controllers\Admin\NodesController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/new","name":"admin.nodes.new","action":"Pterodactyl\Http\Controllers\Admin\NodesController@getNew"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@postNew"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{id}","name":"admin.nodes.view","action":"Pterodactyl\Http\Controllers\Admin\NodesController@getView"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@postView"},{"host":null,"methods":["DELETE"],"uri":"admin\/nodes\/view\/{id}\/deallocate\/single\/{allocation}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@deallocateSingle"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{id}\/deallocate\/block","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@deallocateBlock"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{id}\/alias","name":"admin.nodes.alias","action":"Pterodactyl\Http\Controllers\Admin\NodesController@setAlias"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{id}\/allocations.json","name":"admin.nodes.view.allocations","action":"Pterodactyl\Http\Controllers\Admin\NodesController@getAllocationsJson"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{id}\/allocations","name":"admin.nodes.post.allocations","action":"Pterodactyl\Http\Controllers\Admin\NodesController@postAllocations"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{id}\/deploy","name":"admin.nodes.deply","action":"Pterodactyl\Http\Controllers\Admin\NodesController@getScript"},{"host":null,"methods":["DELETE"],"uri":"admin\/nodes\/view\/{id}","name":"admin.nodes.delete","action":"Pterodactyl\Http\Controllers\Admin\NodesController@deleteNode"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/{id}\/configurationtoken","name":"admin.nodes.configuration-token","action":"Pterodactyl\Http\Controllers\Admin\NodesController@getConfigurationToken"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/locations","name":"admin.locations","action":"Pterodactyl\Http\Controllers\Admin\LocationsController@getIndex"},{"host":null,"methods":["DELETE"],"uri":"admin\/locations\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\LocationsController@deleteLocation"},{"host":null,"methods":["PATCH"],"uri":"admin\/locations\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\LocationsController@patchLocation"},{"host":null,"methods":["POST"],"uri":"admin\/locations","name":null,"action":"Pterodactyl\Http\Controllers\Admin\LocationsController@postLocation"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/databases","name":"admin.databases","action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/databases\/new","name":"admin.databases.new","action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@getNew"},{"host":null,"methods":["POST"],"uri":"admin\/databases\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@postNew"},{"host":null,"methods":["DELETE"],"uri":"admin\/databases\/delete\/{id}","name":"admin.databases.delete","action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@deleteDatabase"},{"host":null,"methods":["DELETE"],"uri":"admin\/databases\/delete-server\/{id}","name":"admin.databases.delete-server","action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@deleteServer"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services","name":"admin.services","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/new","name":"admin.services.new","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@getNew"},{"host":null,"methods":["POST"],"uri":"admin\/services\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@postNew"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/service\/{id}","name":"admin.services.service","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@getService"},{"host":null,"methods":["POST"],"uri":"admin\/services\/service\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@postService"},{"host":null,"methods":["DELETE"],"uri":"admin\/services\/service\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@deleteService"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/service\/{id}\/configuration","name":"admin.services.service.config","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@getConfiguration"},{"host":null,"methods":["POST"],"uri":"admin\/services\/service\/{id}\/configuration","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@postConfiguration"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/service\/{service}\/option\/new","name":"admin.services.option.new","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@newOption"},{"host":null,"methods":["POST"],"uri":"admin\/services\/service\/{service}\/option\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@postNewOption"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/service\/{service}\/option\/{option}","name":"admin.services.option","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@getOption"},{"host":null,"methods":["POST"],"uri":"admin\/services\/service\/{service}\/option\/{option}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@postOption"},{"host":null,"methods":["DELETE"],"uri":"admin\/services\/service\/{service}\/option\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@deleteOption"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/service\/{service}\/option\/{option}\/variable\/new","name":"admin.services.option.variable.new","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@getNewVariable"},{"host":null,"methods":["POST"],"uri":"admin\/services\/service\/{service}\/option\/{option}\/variable\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@postNewVariable"},{"host":null,"methods":["POST"],"uri":"admin\/services\/service\/{service}\/option\/{option}\/variable\/{variable}","name":"admin.services.option.variable","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@postOptionVariable"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/service\/{service}\/option\/{option}\/variable\/{variable}\/delete","name":"admin.services.option.variable.delete","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@deleteVariable"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/packs\/new\/{option?}","name":"admin.services.packs.new","action":"Pterodactyl\Http\Controllers\Admin\PackController@new"},{"host":null,"methods":["POST"],"uri":"admin\/services\/packs\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\PackController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/packs\/upload\/{option?}","name":"admin.services.packs.uploadForm","action":"Pterodactyl\Http\Controllers\Admin\PackController@uploadForm"},{"host":null,"methods":["POST"],"uri":"admin\/services\/packs\/upload","name":null,"action":"Pterodactyl\Http\Controllers\Admin\PackController@postUpload"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/packs","name":"admin.services.packs","action":"Pterodactyl\Http\Controllers\Admin\PackController@listAll"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/packs\/for\/option\/{option}","name":"admin.services.packs.option","action":"Pterodactyl\Http\Controllers\Admin\PackController@listByOption"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/packs\/for\/service\/{service}","name":"admin.services.packs.service","action":"Pterodactyl\Http\Controllers\Admin\PackController@listByService"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/packs\/edit\/{pack}","name":"admin.services.packs.edit","action":"Pterodactyl\Http\Controllers\Admin\PackController@edit"},{"host":null,"methods":["POST"],"uri":"admin\/services\/packs\/edit\/{pack}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\PackController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/packs\/edit\/{pack}\/export\/{archive?}","name":"admin.services.packs.export","action":"Pterodactyl\Http\Controllers\Admin\PackController@export"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/login","name":"auth.login","action":"Pterodactyl\Http\Controllers\Auth\LoginController@showLoginForm"},{"host":null,"methods":["POST"],"uri":"auth\/login","name":null,"action":"Pterodactyl\Http\Controllers\Auth\LoginController@login"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/login\/totp","name":"auth.totp","action":"Pterodactyl\Http\Controllers\Auth\LoginController@totp"},{"host":null,"methods":["POST"],"uri":"auth\/login\/totp","name":null,"action":"Pterodactyl\Http\Controllers\Auth\LoginController@totpCheckpoint"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/password","name":"auth.password","action":"Pterodactyl\Http\Controllers\Auth\ForgotPasswordController@showLinkRequestForm"},{"host":null,"methods":["POST"],"uri":"auth\/password","name":null,"action":"Pterodactyl\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/password\/reset\/{token}","name":"auth.reset","action":"Pterodactyl\Http\Controllers\Auth\ResetPasswordController@showResetForm"},{"host":null,"methods":["POST"],"uri":"auth\/password\/reset","name":"auth.reset.post","action":"Pterodactyl\Http\Controllers\Auth\ResetPasswordController@reset"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/logout","name":"auth.logout","action":"Pterodactyl\Http\Controllers\Auth\LoginController@logout"},{"host":null,"methods":["GET","HEAD"],"uri":"\/","name":"index","action":"Pterodactyl\Http\Controllers\Base\IndexController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"index","name":null,"action":"Closure"},{"host":null,"methods":["GET","HEAD"],"uri":"password-gen\/{length}","name":"password-gen","action":"Pterodactyl\Http\Controllers\Base\IndexController@getPassword"},{"host":null,"methods":["GET","HEAD"],"uri":"account","name":"account","action":"Pterodactyl\Http\Controllers\Base\AccountController@index"},{"host":null,"methods":["POST"],"uri":"account","name":null,"action":"Pterodactyl\Http\Controllers\Base\AccountController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/api","name":"account.api","action":"Pterodactyl\Http\Controllers\Base\APIController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/api\/new","name":"account.api.new","action":"Pterodactyl\Http\Controllers\Base\APIController@create"},{"host":null,"methods":["POST"],"uri":"account\/api\/new","name":null,"action":"Pterodactyl\Http\Controllers\Base\APIController@save"},{"host":null,"methods":["DELETE"],"uri":"account\/api\/revoke\/{key}","name":"account.api.revoke","action":"Pterodactyl\Http\Controllers\Base\APIController@revoke"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/security","name":"account.security","action":"Pterodactyl\Http\Controllers\Base\SecurityController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/security\/revoke\/{id}","name":"account.security.revoke","action":"Pterodactyl\Http\Controllers\Base\SecurityController@revoke"},{"host":null,"methods":["PUT"],"uri":"account\/security\/totp","name":"account.security.totp","action":"Pterodactyl\Http\Controllers\Base\SecurityController@generateTotp"},{"host":null,"methods":["POST"],"uri":"account\/security\/totp","name":null,"action":"Pterodactyl\Http\Controllers\Base\SecurityController@setTotp"},{"host":null,"methods":["DELETE"],"uri":"account\/security\/totp","name":null,"action":"Pterodactyl\Http\Controllers\Base\SecurityController@disableTotp"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/services","name":"daemon.services","action":"Pterodactyl\Http\Controllers\Daemon\ServiceController@list"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/services\/pull\/{service}\/{file}","name":"remote.install","action":"Pterodactyl\Http\Controllers\Daemon\ServiceController@pull"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/packs\/pull\/{uuid}","name":"daemon.pack.pull","action":"Pterodactyl\Http\Controllers\Daemon\PackController@pull"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/packs\/pull\/{uuid}\/hash","name":"daemon.pack.hash","action":"Pterodactyl\Http\Controllers\Daemon\PackController@hash"},{"host":null,"methods":["GET","HEAD"],"uri":"language\/{lang}","name":"langauge.set","action":"Pterodactyl\Http\Controllers\Base\LanguageController@setLanguage"},{"host":null,"methods":["POST"],"uri":"remote\/download","name":"remote.download","action":"Pterodactyl\Http\Controllers\Remote\RemoteController@postDownload"},{"host":null,"methods":["POST"],"uri":"remote\/install","name":"remote.install","action":"Pterodactyl\Http\Controllers\Remote\RemoteController@postInstall"},{"host":null,"methods":["GET","HEAD"],"uri":"remote\/configuration\/{token}","name":"remote.configuration","action":"Pterodactyl\Http\Controllers\Remote\RemoteController@getConfiguration"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}","name":"server.index","action":"Pterodactyl\Http\Controllers\Server\ServerController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/databases","name":"server.settings.databases","action":"Pterodactyl\Http\Controllers\Server\ServerController@getDatabases"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/sftp","name":"server.settings.sftp","action":"Pterodactyl\Http\Controllers\Server\ServerController@getSFTP"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/settings\/sftp","name":null,"action":"Pterodactyl\Http\Controllers\Server\ServerController@postSettingsSFTP"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/startup","name":"server.settings.startup","action":"Pterodactyl\Http\Controllers\Server\ServerController@getStartup"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/settings\/startup","name":null,"action":"Pterodactyl\Http\Controllers\Server\ServerController@postSettingsStartup"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/allocation","name":"server.settings.allocation","action":"Pterodactyl\Http\Controllers\Server\ServerController@getAllocation"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files","name":"server.files.index","action":"Pterodactyl\Http\Controllers\Server\ServerController@getFiles"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files\/edit\/{file}","name":"server.files.edit","action":"Pterodactyl\Http\Controllers\Server\ServerController@getEditFile"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files\/download\/{file}","name":"server.files.download","action":"Pterodactyl\Http\Controllers\Server\ServerController@getDownloadFile"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files\/add","name":"server.files.add","action":"Pterodactyl\Http\Controllers\Server\ServerController@getAddFile"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/files\/directory-list","name":"server.files.directory-list","action":"Pterodactyl\Http\Controllers\Server\AjaxController@postDirectoryList"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/files\/save","name":"server.files.save","action":"Pterodactyl\Http\Controllers\Server\AjaxController@postSaveFile"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/users","name":"server.subusers","action":"Pterodactyl\Http\Controllers\Server\SubuserController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/users\/new","name":"server.subusers.new","action":"Pterodactyl\Http\Controllers\Server\SubuserController@getNew"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/users\/new","name":null,"action":"Pterodactyl\Http\Controllers\Server\SubuserController@postNew"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/users\/view\/{id}","name":"server.subusers.view","action":"Pterodactyl\Http\Controllers\Server\SubuserController@getView"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/users\/view\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Server\SubuserController@postView"},{"host":null,"methods":["DELETE"],"uri":"server\/{server}\/users\/delete\/{id}","name":"server.subusers.delete","action":"Pterodactyl\Http\Controllers\Server\SubuserController@deleteSubuser"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/tasks","name":"server.tasks","action":"Pterodactyl\Http\Controllers\Server\TaskController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/tasks\/view\/{id}","name":"server.tasks.view","action":"Pterodactyl\Http\Controllers\Server\TaskController@getView"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/tasks\/new","name":"server.tasks.new","action":"Pterodactyl\Http\Controllers\Server\TaskController@getNew"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/tasks\/new","name":null,"action":"Pterodactyl\Http\Controllers\Server\TaskController@postNew"},{"host":null,"methods":["DELETE"],"uri":"server\/{server}\/tasks\/delete\/{id}","name":"server.tasks.delete","action":"Pterodactyl\Http\Controllers\Server\TaskController@deleteTask"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/tasks\/toggle\/{id}","name":"server.tasks.toggle","action":"Pterodactyl\Http\Controllers\Server\TaskController@toggleTask"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/ajax\/status","name":"server.ajax.status","action":"Pterodactyl\Http\Controllers\Server\AjaxController@getStatus"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/ajax\/set-primary","name":null,"action":"Pterodactyl\Http\Controllers\Server\AjaxController@postSetPrimary"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/ajax\/settings\/reset-database-password","name":"server.ajax.reset-database-password","action":"Pterodactyl\Http\Controllers\Server\AjaxController@postResetDatabasePassword"},{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/open","name":"debugbar.openhandler","action":"Barryvdh\Debugbar\Controllers\OpenHandlerController@handle"},{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/clockwork\/{id}","name":"debugbar.clockwork","action":"Barryvdh\Debugbar\Controllers\OpenHandlerController@clockwork"},{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/assets\/stylesheets","name":"debugbar.assets.css","action":"Barryvdh\Debugbar\Controllers\AssetController@css"},{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/assets\/javascript","name":"debugbar.assets.js","action":"Barryvdh\Debugbar\Controllers\AssetController@js"}],
+ routes : [{"host":null,"methods":["GET","HEAD"],"uri":"admin","name":"admin.index","action":"Pterodactyl\Http\Controllers\Admin\BaseController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/settings","name":"admin.settings","action":"Pterodactyl\Http\Controllers\Admin\BaseController@getSettings"},{"host":null,"methods":["POST"],"uri":"admin\/settings","name":null,"action":"Pterodactyl\Http\Controllers\Admin\BaseController@postSettings"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users","name":"admin.users","action":"Pterodactyl\Http\Controllers\Admin\UserController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/accounts.json","name":"admin.users.json","action":"Pterodactyl\Http\Controllers\Admin\UserController@getJson"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/view\/{id}","name":"admin.users.view","action":"Pterodactyl\Http\Controllers\Admin\UserController@getView"},{"host":null,"methods":["POST"],"uri":"admin\/users\/view\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@updateUser"},{"host":null,"methods":["DELETE"],"uri":"admin\/users\/view\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@deleteUser"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/new","name":"admin.users.new","action":"Pterodactyl\Http\Controllers\Admin\UserController@getNew"},{"host":null,"methods":["POST"],"uri":"admin\/users\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@postNew"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers","name":"admin.servers","action":"Pterodactyl\Http\Controllers\Admin\ServersController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/new","name":"admin.servers.new","action":"Pterodactyl\Http\Controllers\Admin\ServersController@new"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@create"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/new\/nodes","name":"admin.servers.new.nodes","action":"Pterodactyl\Http\Controllers\Admin\ServersController@newServerNodes"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{id}","name":"admin.servers.view","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{id}\/details","name":"admin.servers.view.details","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewDetails"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/details","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@setDetails"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/details\/container","name":"admin.servers.view.details.container","action":"Pterodactyl\Http\Controllers\Admin\ServersController@setContainer"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{id}\/build","name":"admin.servers.view.build","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewBuild"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/build","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@updateBuild"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{id}\/startup","name":"admin.servers.view.startup","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewStartup"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/startup","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@saveStartup"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{id}\/database","name":"admin.servers.view.database","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewDatabase"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/database","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@newDatabase"},{"host":null,"methods":["PATCH"],"uri":"admin\/servers\/view\/{id}\/database","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@resetDatabasePassword"},{"host":null,"methods":["DELETE"],"uri":"admin\/servers\/view\/{id}\/database\/{database}\/delete","name":"admin.servers.view.database.delete","action":"Pterodactyl\Http\Controllers\Admin\ServersController@deleteDatabase"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{id}\/manage","name":"admin.servers.view.manage","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewManage"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/manage\/toggle","name":"admin.servers.view.manage.toggle","action":"Pterodactyl\Http\Controllers\Admin\ServersController@toggleInstall"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/manage\/rebuild","name":"admin.servers.view.manage.rebuild","action":"Pterodactyl\Http\Controllers\Admin\ServersController@rebuildContainer"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/manage\/suspension","name":"admin.servers.view.manage.suspension","action":"Pterodactyl\Http\Controllers\Admin\ServersController@manageSuspension"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{id}\/delete","name":"admin.servers.view.delete","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewDelete"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/delete","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@delete"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/delete\/continue\/{force?}","name":"admin.servers.view.delete.continue","action":"Pterodactyl\Http\Controllers\Admin\ServersController@continueDeletion"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/delete\/cancel","name":"admin.servers.view.delete.cancel","action":"Pterodactyl\Http\Controllers\Admin\ServersController@cancelDeletion"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes","name":"admin.nodes","action":"Pterodactyl\Http\Controllers\Admin\NodesController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/new","name":"admin.nodes.new","action":"Pterodactyl\Http\Controllers\Admin\NodesController@new"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{id}","name":"admin.nodes.view","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{id}\/settings","name":"admin.nodes.view.settings","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewSettings"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{id}\/settings","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@updateSettings"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{id}\/configuration","name":"admin.nodes.view.configuration","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewConfiguration"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{id}\/allocation","name":"admin.nodes.view.allocation","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewAllocation"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{id}\/allocation","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@createAllocation"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{id}\/servers","name":"admin.nodes.view.servers","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewServers"},{"host":null,"methods":["DELETE"],"uri":"admin\/nodes\/view\/{id}\/delete","name":"admin.nodes.view.delete","action":"Pterodactyl\Http\Controllers\Admin\NodesController@delete"},{"host":null,"methods":["DELETE"],"uri":"admin\/nodes\/view\/{id}\/allocation\/remove\/{allocation}","name":"admin.nodes.view.allocation.removeSingle","action":"Pterodactyl\Http\Controllers\Admin\NodesController@allocationRemoveSingle"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{id}\/allocation\/remove","name":"admin.nodes.view.allocation.removeBlock","action":"Pterodactyl\Http\Controllers\Admin\NodesController@allocationRemoveBlock"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{id}\/allocation\/alias","name":"admin.nodes.view.allocation.setAlias","action":"Pterodactyl\Http\Controllers\Admin\NodesController@allocationSetAlias"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{id}\/settings\/token","name":"admin.nodes.view.configuration.token","action":"Pterodactyl\Http\Controllers\Admin\NodesController@setToken"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/locations","name":"admin.locations","action":"Pterodactyl\Http\Controllers\Admin\LocationsController@getIndex"},{"host":null,"methods":["DELETE"],"uri":"admin\/locations\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\LocationsController@deleteLocation"},{"host":null,"methods":["PATCH"],"uri":"admin\/locations\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\LocationsController@patchLocation"},{"host":null,"methods":["POST"],"uri":"admin\/locations","name":null,"action":"Pterodactyl\Http\Controllers\Admin\LocationsController@postLocation"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/databases","name":"admin.databases","action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/databases\/new","name":"admin.databases.new","action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@getNew"},{"host":null,"methods":["POST"],"uri":"admin\/databases\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@postNew"},{"host":null,"methods":["DELETE"],"uri":"admin\/databases\/delete\/{id}","name":"admin.databases.delete","action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@deleteDatabase"},{"host":null,"methods":["DELETE"],"uri":"admin\/databases\/delete-server\/{id}","name":"admin.databases.delete-server","action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@deleteServer"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services","name":"admin.services","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/new","name":"admin.services.new","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@getNew"},{"host":null,"methods":["POST"],"uri":"admin\/services\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@postNew"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/service\/{id}","name":"admin.services.service","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@getService"},{"host":null,"methods":["POST"],"uri":"admin\/services\/service\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@postService"},{"host":null,"methods":["DELETE"],"uri":"admin\/services\/service\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@deleteService"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/service\/{id}\/configuration","name":"admin.services.service.config","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@getConfiguration"},{"host":null,"methods":["POST"],"uri":"admin\/services\/service\/{id}\/configuration","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@postConfiguration"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/service\/{service}\/option\/new","name":"admin.services.option.new","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@newOption"},{"host":null,"methods":["POST"],"uri":"admin\/services\/service\/{service}\/option\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@postNewOption"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/service\/{service}\/option\/{option}","name":"admin.services.option","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@getOption"},{"host":null,"methods":["POST"],"uri":"admin\/services\/service\/{service}\/option\/{option}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@postOption"},{"host":null,"methods":["DELETE"],"uri":"admin\/services\/service\/{service}\/option\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@deleteOption"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/service\/{service}\/option\/{option}\/variable\/new","name":"admin.services.option.variable.new","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@getNewVariable"},{"host":null,"methods":["POST"],"uri":"admin\/services\/service\/{service}\/option\/{option}\/variable\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@postNewVariable"},{"host":null,"methods":["POST"],"uri":"admin\/services\/service\/{service}\/option\/{option}\/variable\/{variable}","name":"admin.services.option.variable","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@postOptionVariable"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/service\/{service}\/option\/{option}\/variable\/{variable}\/delete","name":"admin.services.option.variable.delete","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@deleteVariable"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/packs\/new\/{option?}","name":"admin.services.packs.new","action":"Pterodactyl\Http\Controllers\Admin\PackController@new"},{"host":null,"methods":["POST"],"uri":"admin\/services\/packs\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\PackController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/packs\/upload\/{option?}","name":"admin.services.packs.uploadForm","action":"Pterodactyl\Http\Controllers\Admin\PackController@uploadForm"},{"host":null,"methods":["POST"],"uri":"admin\/services\/packs\/upload","name":null,"action":"Pterodactyl\Http\Controllers\Admin\PackController@postUpload"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/packs","name":"admin.services.packs","action":"Pterodactyl\Http\Controllers\Admin\PackController@listAll"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/packs\/for\/option\/{option}","name":"admin.services.packs.option","action":"Pterodactyl\Http\Controllers\Admin\PackController@listByOption"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/packs\/for\/service\/{service}","name":"admin.services.packs.service","action":"Pterodactyl\Http\Controllers\Admin\PackController@listByService"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/packs\/edit\/{pack}","name":"admin.services.packs.edit","action":"Pterodactyl\Http\Controllers\Admin\PackController@edit"},{"host":null,"methods":["POST"],"uri":"admin\/services\/packs\/edit\/{pack}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\PackController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/packs\/edit\/{pack}\/export\/{archive?}","name":"admin.services.packs.export","action":"Pterodactyl\Http\Controllers\Admin\PackController@export"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/login","name":"auth.login","action":"Pterodactyl\Http\Controllers\Auth\LoginController@showLoginForm"},{"host":null,"methods":["POST"],"uri":"auth\/login","name":null,"action":"Pterodactyl\Http\Controllers\Auth\LoginController@login"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/login\/totp","name":"auth.totp","action":"Pterodactyl\Http\Controllers\Auth\LoginController@totp"},{"host":null,"methods":["POST"],"uri":"auth\/login\/totp","name":null,"action":"Pterodactyl\Http\Controllers\Auth\LoginController@totpCheckpoint"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/password","name":"auth.password","action":"Pterodactyl\Http\Controllers\Auth\ForgotPasswordController@showLinkRequestForm"},{"host":null,"methods":["POST"],"uri":"auth\/password","name":null,"action":"Pterodactyl\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/password\/reset\/{token}","name":"auth.reset","action":"Pterodactyl\Http\Controllers\Auth\ResetPasswordController@showResetForm"},{"host":null,"methods":["POST"],"uri":"auth\/password\/reset","name":"auth.reset.post","action":"Pterodactyl\Http\Controllers\Auth\ResetPasswordController@reset"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/logout","name":"auth.logout","action":"Pterodactyl\Http\Controllers\Auth\LoginController@logout"},{"host":null,"methods":["GET","HEAD"],"uri":"\/","name":"index","action":"Pterodactyl\Http\Controllers\Base\IndexController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"index","name":null,"action":"Closure"},{"host":null,"methods":["GET","HEAD"],"uri":"password-gen\/{length}","name":"password-gen","action":"Pterodactyl\Http\Controllers\Base\IndexController@getPassword"},{"host":null,"methods":["GET","HEAD"],"uri":"account","name":"account","action":"Pterodactyl\Http\Controllers\Base\AccountController@index"},{"host":null,"methods":["POST"],"uri":"account","name":null,"action":"Pterodactyl\Http\Controllers\Base\AccountController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/api","name":"account.api","action":"Pterodactyl\Http\Controllers\Base\APIController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/api\/new","name":"account.api.new","action":"Pterodactyl\Http\Controllers\Base\APIController@create"},{"host":null,"methods":["POST"],"uri":"account\/api\/new","name":null,"action":"Pterodactyl\Http\Controllers\Base\APIController@save"},{"host":null,"methods":["DELETE"],"uri":"account\/api\/revoke\/{key}","name":"account.api.revoke","action":"Pterodactyl\Http\Controllers\Base\APIController@revoke"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/security","name":"account.security","action":"Pterodactyl\Http\Controllers\Base\SecurityController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/security\/revoke\/{id}","name":"account.security.revoke","action":"Pterodactyl\Http\Controllers\Base\SecurityController@revoke"},{"host":null,"methods":["PUT"],"uri":"account\/security\/totp","name":"account.security.totp","action":"Pterodactyl\Http\Controllers\Base\SecurityController@generateTotp"},{"host":null,"methods":["POST"],"uri":"account\/security\/totp","name":null,"action":"Pterodactyl\Http\Controllers\Base\SecurityController@setTotp"},{"host":null,"methods":["DELETE"],"uri":"account\/security\/totp","name":null,"action":"Pterodactyl\Http\Controllers\Base\SecurityController@disableTotp"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/services","name":"daemon.services","action":"Pterodactyl\Http\Controllers\Daemon\ServiceController@list"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/services\/pull\/{service}\/{file}","name":"remote.install","action":"Pterodactyl\Http\Controllers\Daemon\ServiceController@pull"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/packs\/pull\/{uuid}","name":"daemon.pack.pull","action":"Pterodactyl\Http\Controllers\Daemon\PackController@pull"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/packs\/pull\/{uuid}\/hash","name":"daemon.pack.hash","action":"Pterodactyl\Http\Controllers\Daemon\PackController@hash"},{"host":null,"methods":["GET","HEAD"],"uri":"language\/{lang}","name":"langauge.set","action":"Pterodactyl\Http\Controllers\Base\LanguageController@setLanguage"},{"host":null,"methods":["POST"],"uri":"remote\/download","name":"remote.download","action":"Pterodactyl\Http\Controllers\Remote\RemoteController@postDownload"},{"host":null,"methods":["POST"],"uri":"remote\/install","name":"remote.install","action":"Pterodactyl\Http\Controllers\Remote\RemoteController@postInstall"},{"host":null,"methods":["GET","HEAD"],"uri":"remote\/configuration\/{token}","name":"remote.configuration","action":"Pterodactyl\Http\Controllers\Remote\RemoteController@getConfiguration"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/ajax\/status","name":"server.ajax.status","action":"Pterodactyl\Http\Controllers\Server\AjaxController@getStatus"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}","name":"server.index","action":"Pterodactyl\Http\Controllers\Server\ServerController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/databases","name":"server.settings.databases","action":"Pterodactyl\Http\Controllers\Server\ServerController@getDatabases"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/sftp","name":"server.settings.sftp","action":"Pterodactyl\Http\Controllers\Server\ServerController@getSFTP"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/settings\/sftp","name":null,"action":"Pterodactyl\Http\Controllers\Server\ServerController@postSettingsSFTP"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/startup","name":"server.settings.startup","action":"Pterodactyl\Http\Controllers\Server\ServerController@getStartup"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/settings\/startup","name":null,"action":"Pterodactyl\Http\Controllers\Server\ServerController@postSettingsStartup"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/allocation","name":"server.settings.allocation","action":"Pterodactyl\Http\Controllers\Server\ServerController@getAllocation"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files","name":"server.files.index","action":"Pterodactyl\Http\Controllers\Server\ServerController@getFiles"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files\/edit\/{file}","name":"server.files.edit","action":"Pterodactyl\Http\Controllers\Server\ServerController@getEditFile"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files\/download\/{file}","name":"server.files.download","action":"Pterodactyl\Http\Controllers\Server\ServerController@getDownloadFile"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files\/add","name":"server.files.add","action":"Pterodactyl\Http\Controllers\Server\ServerController@getAddFile"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/files\/directory-list","name":"server.files.directory-list","action":"Pterodactyl\Http\Controllers\Server\AjaxController@postDirectoryList"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/files\/save","name":"server.files.save","action":"Pterodactyl\Http\Controllers\Server\AjaxController@postSaveFile"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/users","name":"server.subusers","action":"Pterodactyl\Http\Controllers\Server\SubuserController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/users\/new","name":"server.subusers.new","action":"Pterodactyl\Http\Controllers\Server\SubuserController@getNew"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/users\/new","name":null,"action":"Pterodactyl\Http\Controllers\Server\SubuserController@postNew"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/users\/view\/{id}","name":"server.subusers.view","action":"Pterodactyl\Http\Controllers\Server\SubuserController@getView"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/users\/view\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Server\SubuserController@postView"},{"host":null,"methods":["DELETE"],"uri":"server\/{server}\/users\/delete\/{id}","name":"server.subusers.delete","action":"Pterodactyl\Http\Controllers\Server\SubuserController@deleteSubuser"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/tasks","name":"server.tasks","action":"Pterodactyl\Http\Controllers\Server\TaskController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/tasks\/view\/{id}","name":"server.tasks.view","action":"Pterodactyl\Http\Controllers\Server\TaskController@getView"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/tasks\/new","name":"server.tasks.new","action":"Pterodactyl\Http\Controllers\Server\TaskController@getNew"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/tasks\/new","name":null,"action":"Pterodactyl\Http\Controllers\Server\TaskController@postNew"},{"host":null,"methods":["DELETE"],"uri":"server\/{server}\/tasks\/delete\/{id}","name":"server.tasks.delete","action":"Pterodactyl\Http\Controllers\Server\TaskController@deleteTask"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/tasks\/toggle\/{id}","name":"server.tasks.toggle","action":"Pterodactyl\Http\Controllers\Server\TaskController@toggleTask"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/ajax\/set-primary","name":null,"action":"Pterodactyl\Http\Controllers\Server\AjaxController@postSetPrimary"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/ajax\/settings\/reset-database-password","name":"server.ajax.reset-database-password","action":"Pterodactyl\Http\Controllers\Server\AjaxController@postResetDatabasePassword"},{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/open","name":"debugbar.openhandler","action":"Barryvdh\Debugbar\Controllers\OpenHandlerController@handle"},{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/clockwork\/{id}","name":"debugbar.clockwork","action":"Barryvdh\Debugbar\Controllers\OpenHandlerController@clockwork"},{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/assets\/stylesheets","name":"debugbar.assets.css","action":"Barryvdh\Debugbar\Controllers\AssetController@css"},{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/assets\/javascript","name":"debugbar.assets.js","action":"Barryvdh\Debugbar\Controllers\AssetController@js"}],
prefix: '',
route : function (name, parameters, route) {
diff --git a/public/themes/pterodactyl/css/pterodactyl.css b/public/themes/pterodactyl/css/pterodactyl.css
index e5fefe52f..ca34e46a5 100644
--- a/public/themes/pterodactyl/css/pterodactyl.css
+++ b/public/themes/pterodactyl/css/pterodactyl.css
@@ -65,7 +65,7 @@ code {
font-size: 14px !important;
}
-.middle {
+.middle, .align-middle {
vertical-align: middle !important;
}
@@ -114,7 +114,7 @@ td.has-progress {
.input-loader {
display: none;
position:relative;
- top: -23px;
+ top: -25px;
float: right;
right: 5px;
color: #cccccc;
@@ -133,6 +133,119 @@ td.has-progress {
margin: 0 !important;
}
+li.select2-results__option--highlighted[aria-selected="false"] > .user-block > .username > a {
+ color: #fff;
+}
+
+li.select2-results__option--highlighted[aria-selected="false"] > .user-block > .description {
+ color: #eee;
+}
+
+.select2-selection.select2-selection--multiple {
+ min-height: 36px !important;
+}
+
+.select2-search--inline .select2-search__field:focus {
+ outline: none;
+ border: 0 !important;
+}
+
+.img-bordered-xs {
+ border: 1px solid #d2d6de;
+ padding: 1px;
+}
+
+span[aria-labelledby="select2-pUserId-container"] {
+ padding-left: 2px !important;
+}
+
+.callout-slim a {
+ color: #555 !important;
+}
+
+.callout.callout-info.callout-slim {
+ border: 1px solid #0097bc !important;
+ border-left: 5px solid #0097bc !important;
+ border-right: 5px solid #0097bc !important;
+ color: #777 !important;
+ background: transparent !important;
+}
+
+.callout.callout-danger.callout-slim {
+ border: 1px solid #c23321 !important;
+ border-left: 5px solid #c23321 !important;
+ border-right: 5px solid #c23321 !important;
+ color: #777 !important;
+ background: transparent !important;
+}
+
+.callout.callout-warning.callout-slim {
+ border: 1px solid #c87f0a !important;
+ border-left: 5px solid #c87f0a !important;
+ border-right: 5px solid #c87f0a !important;
+ color: #777 !important;
+ background: transparent !important;
+}
+
+.callout.callout-success.callout-slim {
+ border: 1px solid #00733e !important;
+ border-left: 5px solid #00733e !important;
+ border-right: 5px solid #00733e !important;
+ color: #777 !important;
+ background: transparent !important;
+}
+
+.callout.callout-default.callout-slim {
+ border: 1px solid #eee !important;
+ border-left: 5px solid #eee !important;
+ border-right: 5px solid #eee !important;
+ 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;
+}
+
+.nav-tabs-custom.nav-tabs-floating > .nav-tabs {
+ border-bottom: 0px !important;
+}
+
+.nav-tabs-custom.nav-tabs-floating > .nav-tabs > li {
+ margin-bottom: 0px !important;
+}
+
+.nav-tabs-custom.nav-tabs-floating > .nav-tabs > li:first-child.active,
+.nav-tabs-custom.nav-tabs-floating > .nav-tabs > li:first-child:hover {
+ border-radius: 3px 0 0 0;
+}
+
+.nav-tabs-custom.nav-tabs-floating > .nav-tabs > li:first-child.active > a {
+ border-radius: 0 0 0 3px;
+}
+
.position-relative {
position: relative;
}
diff --git a/public/themes/pterodactyl/js/admin/functions.js b/public/themes/pterodactyl/js/admin/functions.js
new file mode 100644
index 000000000..26115cd46
--- /dev/null
+++ b/public/themes/pterodactyl/js/admin/functions.js
@@ -0,0 +1,23 @@
+// Copyright (c) 2015 - 2017 Dane Everitt ' + item.description + '
\
+ Access in Startup: @{{' + item.env_variable + '}}
\
+ Validation Regex: ' + item.regex + '
{{ Version::getCurrentPanel() }}
. Your panel is up-to-date!
+ @else
+ Your panel is not up-to-date! The latest version is {{ Version::getPanel() }}
and you are currently running version {{ Version::getCurrentPanel() }}
.
+ @endif
+ + | Name | +Location | +Memory | +Disk | +Servers | +SSL | +Public | +
---|---|---|---|---|---|---|---|
+ | {{ $node->name }} | +{{ $node->location->short }} | +{{ $node->memory }} MB | +{{ $node->disk }} MB | +{{ $node->servers_count }} | ++ | + |
IP Address | +IP Alias | +Port | +Assigned To | ++ |
---|---|---|---|---|
{{ $allocation->ip }} | ++ + + | +{{ $allocation->port }} | ++ @if(! is_null($allocation->server)) + {{ $allocation->server->name }} + @endif + | ++ @if(is_null($allocation->server_id)) + + @else + + @endif + | +
{{ $node->getConfigurationAsJson(true) }}+
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. Tokens are only valid for 5 minutes.
+Daemon Version | + (Latest: {{ Version::getDaemon() }} ) |
+
System Information | ++ |
Total CPU Cores | ++ |
Deleting a node is a irreversable action and will immediately remove this node from the panel. There must be no servers associated with this node in order to continue.
+ID | +Server Name | +Owner | +Service | +Memory | +Disk | +CPU | +Status | +
---|---|---|---|---|---|---|---|
{{ $server->uuidShort }} |
+ {{ $server->name }} | +{{ $server->user->username }} | +{{ $server->service->name }} ({{ $server->option->name }}) | +NaN / {{ $server->memory === 0 ? '∞' : $server->memory }} MB | +{{ $server->disk }} MB | +NaN % | +NaN | +
ID | +Server Name | +Owner | +Node | +Connection | ++ |
---|---|---|---|---|---|
{{ $server->uuidShort }} |
+ {{ $server->name }} | +{{ $server->user->username }} | +{{ $server->node->name }} | +
+ {{ $server->allocation->alias }}:{{ $server->allocation->port }}
+ |
+ + @if($server->suspended && ! $server->trashed()) + Suspended + @elseif($server->trashed()) + Pending Deletion + @elseif(! $server->installed) + Installing + @else + Active + @endif + | +
Database | +Username | +Connections From | +Host | ++ |
---|---|---|---|---|
{{ $database->database }} | +{{ $database->username }} | +{{ $database->remote }} | +{{ $database->host->host }}:{{ $database->host->port }} |
+ + + + | +
This server is currently marked for deletion by the system {{ Carbon::parse($server->deleted_at)->addMinutes(env('APP_DELETE_MINUTES', 10))->diffForHumans() }}.
+Deleting a server is an irreversible action. All server data (including files and users) will be removed from the system.
+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.
+Deleting a server is an irreversible action. All server data (including files and users) will be removed from the system.
+This action will attempt to delete the server from both the panel and daemon. The the daemon does not respond, or reports an error the deletion will continue.
+Deleting a server is an irreversible action. All server data (including files and users) will be removed from the system. This method may leave dangling files on your daemon if it reports an error.
+UUID | +{{ $server->uuid }} | +
Docker Container ID | ++ |
Docker User ID | ++ |
Service | +{{ $server->option->service->name }} :: {{ $server->option->name }} | +
Name | +{{ $server->name }} | +
Memory | +{{ $server->memory }}MB / {{ $server->swap }}MB |
+
OOM Killer | +{!! ($server->oom_disabled === 0) ? 'Enabled' : 'Disabled' !!} | +
Disk Space | +{{ $server->disk }}MB |
+
Block IO Weight | +{{ $server->io }} |
+
CPU Limit | +{{ $server->cpu }}% |
+
Default Connection | +{{ $server->allocation->ip }}:{{ $server->allocation->port }} |
+
Connection Alias | +
+ @if($server->allocation->alias !== $server->allocation->ip)
+ {{ $server->allocation->alias }}:{{ $server->allocation->port }}
+ @else
+ No Alias Assigned
+ @endif
+ |
+
If you need to change the install status from uninstalled to installed, or vice versa, you may do so with the button below.
+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.
+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.
+This will unsuspend the server and restore normal user access.
+ID + | Email + | Client Name | +Username | +Servers | ++ |
---|---|---|---|---|---|
{{ $user->id }} |
+ {{ $user->email }} | +{{ $user->name_last }}, {{ $user->name_first }} | +{{ $user->username }} | +{{ $user->servers_count }} | ++ |
Registered {{ (new Carbon($user->created_at))->toRfc1123String() }}
-