Merge branch 'develop' into feature/service-changes
This commit is contained in:
commit
6bd9663f59
136 changed files with 2470 additions and 1737 deletions
|
@ -75,7 +75,7 @@ class UserController extends BaseController
|
|||
*/
|
||||
public function view(Request $request, $id)
|
||||
{
|
||||
$query = Models\User::where('id', $id);
|
||||
$query = Models\User::where((is_numeric($id) ? 'id' : 'email'), $id);
|
||||
|
||||
if (! is_null($request->input('fields'))) {
|
||||
foreach (explode(',', $request->input('fields')) as $field) {
|
||||
|
|
|
@ -27,6 +27,7 @@ namespace Pterodactyl\Http\Controllers\Admin;
|
|||
use DB;
|
||||
use Log;
|
||||
use Alert;
|
||||
use Carbon;
|
||||
use Validator;
|
||||
use Pterodactyl\Models;
|
||||
use Illuminate\Http\Request;
|
||||
|
@ -82,6 +83,7 @@ class NodesController extends Controller
|
|||
'_token',
|
||||
]));
|
||||
Alert::success('Successfully created new node. <strong>Before you can add any servers you need to first assign some IP addresses and ports.</strong>')->flash();
|
||||
Alert::info('<strong>To simplify the node setup you can generate a token on the configuration tab.</strong>')->flash();
|
||||
|
||||
return redirect()->route('admin.nodes.view', [
|
||||
'id' => $new,
|
||||
|
@ -276,4 +278,24 @@ class NodesController extends Controller
|
|||
'tab' => 'tab_delete',
|
||||
]);
|
||||
}
|
||||
|
||||
public function getConfigurationToken(Request $request, $id)
|
||||
{
|
||||
// Check if Node exists. Will lead to 404 if not.
|
||||
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();
|
||||
|
||||
$token_response = [
|
||||
'token' => $token->token,
|
||||
'expires_at' => $token->expires_at->toDateTimeString(),
|
||||
];
|
||||
|
||||
return response()->json($token_response, 200);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -232,7 +232,7 @@ class ServiceController extends Controller
|
|||
]));
|
||||
Alert::success('Successfully added new variable to this option.')->flash();
|
||||
|
||||
return redirect()->route('admin.services.option', [$service, $option])->withInput();
|
||||
return redirect()->route('admin.services.option', [$service, $option]);
|
||||
} catch (DisplayValidationException $ex) {
|
||||
return redirect()->route('admin.services.option.variable.new', [$service, $option])->withErrors(json_decode($ex->getMessage()))->withInput();
|
||||
} catch (DisplayException $ex) {
|
||||
|
|
|
@ -33,16 +33,14 @@ use Pterodactyl\Http\Controllers\Controller;
|
|||
class LanguageController extends Controller
|
||||
{
|
||||
protected $languages = [
|
||||
'de' => 'Danish',
|
||||
'de' => 'German',
|
||||
'en' => 'English',
|
||||
'es' => 'Spanish',
|
||||
'fr' => 'French',
|
||||
'it' => 'Italian',
|
||||
'pl' => 'Polish',
|
||||
'et' => 'Estonian',
|
||||
'nb' => 'Norwegian',
|
||||
'nl' => 'Dutch',
|
||||
'pt' => 'Portuguese',
|
||||
'ro' => 'Romanian',
|
||||
'ru' => 'Russian',
|
||||
'se' => 'Swedish',
|
||||
'zh' => 'Chinese',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
namespace Pterodactyl\Http\Controllers\Remote;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Pterodactyl\Models;
|
||||
use Illuminate\Http\Request;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
|
@ -107,4 +108,29 @@ class RemoteController extends Controller
|
|||
|
||||
return response('', 201);
|
||||
}
|
||||
|
||||
public function getConfiguration(Request $request, $tokenString)
|
||||
{
|
||||
// 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);
|
||||
} 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();
|
||||
|
||||
return response()->json(['error' => 'token_expired'], 403);
|
||||
}
|
||||
|
||||
// Delete the token, it's one-time use
|
||||
$token->delete();
|
||||
|
||||
// Manually as getConfigurationAsJson() returns it in correct format already
|
||||
return response($node->getConfigurationAsJson(), 200)
|
||||
->header('Content-Type', 'application/json');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,9 +28,9 @@ use DB;
|
|||
use Log;
|
||||
use Uuid;
|
||||
use Alert;
|
||||
use Javascript;
|
||||
use Pterodactyl\Models;
|
||||
use Illuminate\Http\Request;
|
||||
use InvalidArgumentException;
|
||||
use Pterodactyl\Exceptions\DisplayException;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Pterodactyl\Repositories\ServerRepository;
|
||||
|
@ -49,24 +49,6 @@ class ServerController extends Controller
|
|||
//
|
||||
}
|
||||
|
||||
public function getJavascript(Request $request, $uuid, $folder, $file)
|
||||
{
|
||||
$server = Models\Server::getByUUID($uuid);
|
||||
|
||||
$info = pathinfo($file);
|
||||
$routeFile = str_replace('/', '.', $info['dirname']) . '.' . $info['filename'];
|
||||
try {
|
||||
return response()->view('server.js.' . $folder . '.' . $routeFile, [
|
||||
'server' => $server,
|
||||
'node' => Models\Node::find($server->node),
|
||||
])->header('Content-Type', 'application/javascript');
|
||||
} catch (InvalidArgumentException $ex) {
|
||||
return abort(404);
|
||||
} catch (\Exception $ex) {
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders server index page for specified server.
|
||||
*
|
||||
|
@ -77,6 +59,13 @@ class ServerController extends Controller
|
|||
{
|
||||
$server = Models\Server::getByUUID($request->route()->server);
|
||||
|
||||
Javascript::put([
|
||||
'meta' => [
|
||||
'saveFile' => route('server.files.save', $server->uuidShort),
|
||||
'csrfToken' => csrf_token(),
|
||||
],
|
||||
]);
|
||||
|
||||
return view('server.index', [
|
||||
'server' => $server,
|
||||
'allocations' => Models\Allocation::where('assigned_to', $server->id)->orderBy('ip', 'asc')->orderBy('port', 'asc')->get(),
|
||||
|
@ -90,14 +79,34 @@ class ServerController extends Controller
|
|||
* @param Request $request
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function getFiles(Request $request)
|
||||
public function getFiles(Request $request, $uuid)
|
||||
{
|
||||
$server = Models\Server::getByUUID($request->route()->server);
|
||||
$server = Models\Server::getByUUID($uuid);
|
||||
$this->authorize('list-files', $server);
|
||||
|
||||
$node = Models\Node::find($server->node);
|
||||
|
||||
Javascript::put([
|
||||
'server' => collect($server->makeVisible('daemonSecret'))->only('uuid', 'uuidShort', 'daemonSecret'),
|
||||
'node' => collect($node)->only('fqdn', 'scheme', 'daemonListen'),
|
||||
'meta' => [
|
||||
'directoryList' => route('server.files.directory-list', $server->uuidShort),
|
||||
'csrftoken' => csrf_token(),
|
||||
],
|
||||
'permissions' => [
|
||||
'moveFiles' => $request->user()->can('move-files', $server),
|
||||
'copyFiles' => $request->user()->can('copy-files', $server),
|
||||
'compressFiles' => $request->user()->can('compress-files', $server),
|
||||
'decompressFiles' => $request->user()->can('decompress-files', $server),
|
||||
'createFiles' => $request->user()->can('create-files', $server),
|
||||
'downloadFiles' => $request->user()->can('download-files', $server),
|
||||
'deleteFiles' => $request->user()->can('delete-files', $server),
|
||||
],
|
||||
]);
|
||||
|
||||
return view('server.files.index', [
|
||||
'server' => $server,
|
||||
'node' => Models\Node::find($server->node),
|
||||
'node' => $node,
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -107,9 +116,9 @@ class ServerController extends Controller
|
|||
* @param Request $request
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function getAddFile(Request $request)
|
||||
public function getAddFile(Request $request, $uuid)
|
||||
{
|
||||
$server = Models\Server::getByUUID($request->route()->server);
|
||||
$server = Models\Server::getByUUID($uuid);
|
||||
$this->authorize('add-files', $server);
|
||||
|
||||
return view('server.files.add', [
|
||||
|
|
|
@ -286,6 +286,11 @@ class AdminRoutes
|
|||
'as' => 'admin.nodes.delete',
|
||||
'uses' => 'Admin\NodesController@deleteNode',
|
||||
]);
|
||||
|
||||
$router->get('/{id}/configurationtoken', [
|
||||
'as' => 'admin.nodes.configuration-token',
|
||||
'uses' => 'Admin\NodesController@getConfigurationToken',
|
||||
]);
|
||||
});
|
||||
|
||||
// Location Routes
|
||||
|
|
|
@ -46,6 +46,11 @@ class RemoteRoutes
|
|||
'as' => 'remote.event',
|
||||
'uses' => 'Remote\RemoteController@event',
|
||||
]);
|
||||
|
||||
$router->get('configuration/{token}', [
|
||||
'as' => 'remote.configuration',
|
||||
'uses' => 'Remote\RemoteController@getConfiguration',
|
||||
]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -166,15 +166,6 @@ class ServerRoutes
|
|||
'uses' => 'Server\AjaxController@postResetDatabasePassword',
|
||||
]);
|
||||
});
|
||||
|
||||
// Assorted AJAX Routes
|
||||
$router->group(['prefix' => 'js'], function ($server) use ($router) {
|
||||
// Returns Server Status
|
||||
$router->get('{folder}/{file}', [
|
||||
'as' => 'server.js',
|
||||
'uses' => 'Server\ServerController@getJavascript',
|
||||
])->where('file', '.*');
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,4 +117,61 @@ class Node extends Model
|
|||
|
||||
return self::$guzzle[$node];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configuration in JSON format.
|
||||
*
|
||||
* @param bool $pretty Wether to pretty print the JSON or not
|
||||
* @return string The configration in JSON format
|
||||
*/
|
||||
public function getConfigurationAsJson($pretty = false)
|
||||
{
|
||||
$config = [
|
||||
'web' => [
|
||||
'host' => '0.0.0.0',
|
||||
'listen' => $this->daemonListen,
|
||||
'ssl' => [
|
||||
'enabled' => $this->scheme === 'https',
|
||||
'certificate' => '/etc/letsencrypt/live/localhost/fullchain.pem',
|
||||
'key' => '/etc/letsencrypt/live/localhost/privkey.pem',
|
||||
],
|
||||
],
|
||||
'docker' => [
|
||||
'socket' => '/var/run/docker.sock',
|
||||
'autoupdate_images' => true,
|
||||
],
|
||||
'sftp' => [
|
||||
'path' => $this->daemonBase,
|
||||
'port' => $this->daemonSFTP,
|
||||
'container' => 'ptdl-sftp',
|
||||
],
|
||||
'query' => [
|
||||
'kill_on_fail' => true,
|
||||
'fail_limit' => 5,
|
||||
],
|
||||
'logger' => [
|
||||
'path' => 'logs/',
|
||||
'src' => false,
|
||||
'level' => 'info',
|
||||
'period' => '1d',
|
||||
'count' => 3,
|
||||
],
|
||||
'remote' => [
|
||||
'base' => config('app.url'),
|
||||
'download' => route('remote.download'),
|
||||
'installed' => route('remote.install'),
|
||||
],
|
||||
'uploads' => [
|
||||
'size_limit' => $this->upload_size,
|
||||
],
|
||||
'keys' => [$this->daemonSecret],
|
||||
];
|
||||
|
||||
$json_options = JSON_UNESCAPED_SLASHES;
|
||||
if ($pretty) {
|
||||
$json_options |= JSON_PRETTY_PRINT;
|
||||
}
|
||||
|
||||
return json_encode($config, $json_options);
|
||||
}
|
||||
}
|
||||
|
|
51
app/Models/NodeConfigurationToken.php
Normal file
51
app/Models/NodeConfigurationToken.php
Normal file
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
/**
|
||||
* Pterodactyl - Panel
|
||||
* Copyright (c) 2015 - 2016 Dane Everitt <dane@daneeveritt.com>.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace Pterodactyl\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class NodeConfigurationToken extends Model
|
||||
{
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'node_configuration_tokens';
|
||||
|
||||
/**
|
||||
* Fields that are not mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $guarded = ['id', 'created_at', 'updated_at'];
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dates = ['created_at', 'updated_at', 'expires_at'];
|
||||
}
|
|
@ -213,7 +213,13 @@ class NodeRepository
|
|||
throw new DisplayException('The mapping for ' . $port . ' is invalid and cannot be processed.');
|
||||
}
|
||||
if (preg_match('/^(\d{1,5})-(\d{1,5})$/', $port, $matches)) {
|
||||
foreach (range($matches[1], $matches[2]) as $assignPort) {
|
||||
$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' => $node->id,
|
||||
'ip' => $ip,
|
||||
|
@ -252,7 +258,6 @@ class NodeRepository
|
|||
}
|
||||
|
||||
DB::commit();
|
||||
// return true;
|
||||
} catch (\Exception $ex) {
|
||||
DB::rollBack();
|
||||
throw $ex;
|
||||
|
@ -277,6 +282,9 @@ class NodeRepository
|
|||
// Delete Allocations
|
||||
Models\Allocation::where('node', $node->id)->delete();
|
||||
|
||||
// Delete configure tokens
|
||||
Models\NodeConfigurationToken::where('node', $node->id)->delete();
|
||||
|
||||
// Delete Node
|
||||
$node->delete();
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ class ServerRepository
|
|||
// Validate Fields
|
||||
$validator = Validator::make($data, [
|
||||
'owner' => 'bail|required',
|
||||
'name' => 'required|regex:/^([\w -]{4,35})$/',
|
||||
'name' => 'required|regex:/^([\w .-]{1,200})$/',
|
||||
'memory' => 'required|numeric|min:0',
|
||||
'swap' => 'required|numeric|min:-1',
|
||||
'io' => 'required|numeric|min:10|max:1000',
|
||||
|
@ -179,7 +179,7 @@ class ServerRepository
|
|||
foreach ($variables as $variable) {
|
||||
|
||||
// Is the variable required?
|
||||
if (! $data['env_' . $variable->env_variable]) {
|
||||
if (! isset($data['env_' . $variable->env_variable])) {
|
||||
if ($variable->required === 1) {
|
||||
throw new DisplayException('A required service option variable field (env_' . $variable->env_variable . ') was missing from the request.');
|
||||
}
|
||||
|
@ -360,7 +360,7 @@ class ServerRepository
|
|||
// Validate Fields
|
||||
$validator = Validator::make($data, [
|
||||
'owner' => 'email|exists:users,email',
|
||||
'name' => 'regex:([\w -]{4,35})',
|
||||
'name' => 'regex:([\w .-]{1,200})',
|
||||
]);
|
||||
|
||||
// Run validator, throw catchable and displayable exception if it fails.
|
||||
|
|
|
@ -117,6 +117,7 @@ class SubuserRepository
|
|||
public function create($sid, array $data)
|
||||
{
|
||||
$server = Models\Server::findOrFail($sid);
|
||||
|
||||
$validator = Validator::make($data, [
|
||||
'permissions' => 'required|array',
|
||||
'email' => 'required|email',
|
||||
|
@ -140,6 +141,10 @@ class SubuserRepository
|
|||
} catch (\Exception $ex) {
|
||||
throw $ex;
|
||||
}
|
||||
} elseif ($server->owner === $user->id) {
|
||||
throw new DisplayException('You cannot add the owner of a server as a subuser.');
|
||||
} elseif (Models\Subuser::select('id')->where('user_id', $user->id)->where('server_id', $server->id)->first()) {
|
||||
throw new DisplayException('A subuser with that email already exists for this server.');
|
||||
}
|
||||
|
||||
$uuid = new UuidService;
|
||||
|
@ -159,6 +164,7 @@ class SubuserRepository
|
|||
if (! is_null($this->permissions[$permission])) {
|
||||
array_push($daemonPermissions, $this->permissions[$permission]);
|
||||
}
|
||||
|
||||
$model = new Models\Permission;
|
||||
$model->fill([
|
||||
'user_id' => $user->id,
|
||||
|
|
Reference in a new issue