diff --git a/.gitignore b/.gitignore index 0826a5ebe..90c2f050b 100644 --- a/.gitignore +++ b/.gitignore @@ -6,14 +6,17 @@ storage/framework/* composer.lock - -Homestead.yaml -Vagrantfile -Vagrantfile - -node_modules -yarn.lock node_modules _ide_helper_models.php _ide_helper.php + +sami.phar +/.sami + +# For local development with docker +# Remove if we ever put the Dockerfile in the repo +.dockerignore +Dockerfile +# for image related files +misc diff --git a/.sami.php b/.sami.php new file mode 100644 index 000000000..da4c4658f --- /dev/null +++ b/.sami.php @@ -0,0 +1,16 @@ +files() + ->name('*.php') + ->in($dir = __DIR__ . '/app'); + +return new Sami($iterator, array( + 'title' => 'Pterodactyl', + 'build_dir' => __DIR__ . '/.sami/build', + 'cache_dir' => __DIR__ . '/.sami/cache', + 'default_opened_level' => 2, +)); diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ab4382c4..960d838af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,80 @@ This file is a running track of new features and fixes to each version of the pa This project follows [Semantic Versioning](http://semver.org) guidelines. +## v0.6.0-beta.2.1 (Courageous Carniadactylus) +### Fixed +* `[beta.2]` — Suspended servers now show as suspended. +* `[beta.2]` — Corrected the information when a task has not run yet. +* `[beta.2]` — Fixes filemanager 404 when editing a file within a directory. +* `[beta.2]` — Fixes exception in tasks when deleting a server. +* `[beta.2]` — Fixes bug with Terarria and Voice servers reporting a `TypeError: Service is not a constructor` in the daemon due to a missing service configuration. +* `[beta.2]` — Fixes password reset form throwing a MethodNotAllowed error when accessed. +* `[beta.2]` — Fixes invalid password bug when attempting to change account email address. +* `[beta.2]` — New attempt at fixing the issues when rendering files in the browser file editor on certain browsers. +* `[beta.2]` — Fixes broken auto-deploy time checking causing no tokens to work. +* `[beta.2]` — Fixes display of subusers after creation. +* `[beta.2]` — Fixes bug throwing model not found exception when editing an existing subuser. + +### Changed +* Deleting a server safely now continues even if the daemon reports a `HTTP/404` missing server error (requires `Daemon@0.4.0-beta.2.1`) +* Changed behavior when modifying server allocation information. You can now remove the default allocation assuming you assing a new allocation at the same time. Reduces the number of steps to change the default allocation for a server. + +### Added +* Server listing and view in Admin CP now shows the SFTP username/Docker container name. +* Administrative server view includes link in navigation to go to server console/frontend management. + +## v0.6.0-beta.2 (Courageous Carniadactylus) +### Fixed +* `[beta.1]` — Fixes task management ststem not running correctly. +* `[beta.1]` — Fixes API endpoint for command sending missing the required class definition. +* `[beta.1]` — Fixes panel looking for an old compiled classfile that is no longer used. This was causing errors relating to `missing class DingoAPI` when trying to upgrade the panel. +* `[beta.1]` — Should fix render issues when trying to edit some files via the panel file editor. + +### Added +* Ability to launch the console in a new window as an individual unit. https://s3.kelp.in/IrTyE.png + +## v0.6.0-beta.1 (Courageous Carniadactylus) +### Fixed +* `[pre.7]` — Fixes bug with subuser checkbox display. +* `[pre.7]` — Fixes bug with injected JS that was causing `` to be ignored in templates. +* `[pre.7]` — Fixes exception thrown when trying to delete a node due to a misnamed model. +* `[pre.7]` — Fixes username vanishing on failed login attempts. +* `[pre.7]` — Terminal is now fixed to actually output all lines, rather than leaving one hanging in neverland until the browser is resized. + +### Added +* Login attempts and pasword reset requests are now protected by invisible ReCaptcha. This feature can be disabled with a `.env` variable. +* Server listing for individual users is now searchable on the front-end. +* Servers that a user is assocaited with as a subuser are now displayed in addition to owned servers when listing users in the Admin CP. + +### Changed +* Subuser permissions are now stored in `Permission::list()` to make views way cleaner and make adding to views significantly cleaner. +* `[pre.7]` — Sidebar for file manager now is a single link rather than a dropdown. +* Attempting to reset a password for an account that does not exist no longer returns an error, rather it displays a success message. Failed resets trigger a `Pterodactyl\Events\Auth\FailedPasswordReset` event that can be caught if needed to perform other actions. +* Servers are no longer queued for deletion due to the general hassle and extra logic required. +* Updated all panel components to run on Laravel v5.4 rather than 5.3 which is EOL. +* Routes are now handled in the `routes/` folder, and use a significantly cleaner syntax. Controller names and methods have been updated as well to be clearer as well as avoid conflicts with PHP reserved keywords. +* API has been completely overhauled to use new permissions system. **Any old API keys will immediately become invalid and fail to operate properly anymore. You will need to generate new keys.** +* Cleaned up dynamic database connection setting to use a single function call from the host model. +* `[pre.7]` — Corrected a config option for spigot servers to set a boolean value as boolean, and not as a string. + +## v0.6.0-pre.7 (Courageous Carniadactylus) +### Fixed +* `[pre.6]` — Addresses misconfigured console queue that was still sending data way to quickly thus causing the console to explode on some devices when large amounts of data were sent. +* `[pre.6]` — Fixes bug in allocation parsing for a node that prevented adding new allocations. +* `[pre.6]` — Fixes typo in migrations that wouldn't save custom regex for non-required variables. +* `[pre.6]` — Fixes auto-deploy checkbox on server creation causing validation error. + +## v0.6.0-pre.6 (Courageous Carniadactylus) +### Fixed +* `[pre.5]` — Console based server rebuild tool now actually rebuilds the servers with the correct information. +* `[pre.5]` — Fixes typo and wrong docker contaienr for certain applications. + +### Changed +* Removed all old theme JS and CSS folders to cleanup and avoid confusion in the future. + +### Added +* `[pre.5]` — Added foreign key to `pack_id` to ensure nothing eds up breaking there. + ## v0.6.0-pre.5 (Courageous Carniadactylus) ### Changed * New theme applied to Admin CP. Many graphical changes were made, some data was moved around and some display data changed. Too much was changed to feasibly log it all in here. Major breaking changes or notable new features will be logged. @@ -14,6 +88,7 @@ This project follows [Semantic Versioning](http://semver.org) guidelines. * Many structural changes to the database and `Pterodactyl\Models` classes that would flood this changelog if they were all included. All required migrations included to handle database changes. * `[pre.4]` — Service pack files are now stored in the database rather than on the host system to make updates easier. * Clarified details for database hosts to prevent users entering invalid account details, as well as renamed tables and columns relating to it to keep things clearer. +* Updated all code to be Laravel compliant when using `env()` and moved to using `config()` throughout non `config/*.php` files. ### Fixed * Fixes potential bug with invalid CIDR notation (ex: `192.168.1.1/z`) when adding allocations that could cause over 4 million records to be created at once. @@ -24,6 +99,7 @@ This project follows [Semantic Versioning](http://semver.org) guidelines. * Fixes bug where daemon was unable to register that certain games had fully booted and were ready to play on. * Fixes bug causing MySQL user accounts to be corrupted when resetting a password via the panel. * `[pre.4]` — Multiple clients refreshing the console no longer clears the console for all parties involved... sorry about that. +* `[pre.4]` — Fixes bug in environment setting script that would not remeber defaults and try to re-assign values. ### Added * Ability to assign multiple allocations at once when creating a new server. diff --git a/app/Console/Commands/AddNode.php b/app/Console/Commands/AddNode.php index 4b4468422..0aac540c0 100644 --- a/app/Console/Commands/AddNode.php +++ b/app/Console/Commands/AddNode.php @@ -83,7 +83,7 @@ class AddNode extends Command $selectedLocation = $this->option('location'); } - $this->data['location'] = $locations->where('short', $selectedLocation)->first()->id; + $this->data['location_id'] = $locations->where('short', $selectedLocation)->first()->id; if (is_null($this->option('fqdn'))) { $this->line('Please enter domain name (e.g node.example.com) to be used for connecting to the daemon. An IP address may only be used if you are not using SSL for this node.'); diff --git a/app/Console/Commands/ClearTasks.php b/app/Console/Commands/ClearTasks.php index 26f0da761..569caf028 100644 --- a/app/Console/Commands/ClearTasks.php +++ b/app/Console/Commands/ClearTasks.php @@ -64,7 +64,7 @@ class ClearTasks extends Command */ public function handle() { - $entries = Models\TaskLog::where('run_time', '<=', Carbon::now()->subHours(env('APP_CLEAR_TASKLOG', 720))->toAtomString())->get(); + $entries = Models\TaskLog::where('run_time', '<=', Carbon::now()->subHours(config('pterodactyl.tasks.clear_log'))->toAtomString())->get(); $this->info(sprintf('Preparing to delete %d old task log entries.', count($entries))); $bar = $this->output->createProgressBar(count($entries)); diff --git a/app/Console/Commands/RebuildServer.php b/app/Console/Commands/RebuildServer.php new file mode 100644 index 000000000..2177b112a --- /dev/null +++ b/app/Console/Commands/RebuildServer.php @@ -0,0 +1,146 @@ +. + * + * 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\Console\Commands; + +use Pterodactyl\Models\Node; +use Pterodactyl\Models\Server; +use Illuminate\Console\Command; + +class RebuildServer extends Command +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'pterodactyl:rebuild + {--all} + {--node= : Id of node to rebuild all servers on.} + {--server= : UUID of server to rebuild.}'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Rebuild docker containers for a server or multiple servers.'; + + /** + * Create a new command instance. + * + * @return void + */ + public function __construct() + { + parent::__construct(); + } + + /** + * Execute the console command. + * + * @return mixed + */ + public function handle() + { + if ($this->option('all')) { + $servers = Server::all(); + } elseif ($this->option('node')) { + $servers = Server::where('node_id', $this->option('node'))->get(); + } elseif ($this->option('server')) { + $servers = Server::where('id', $this->option('server'))->get(); + } else { + $this->error('You must pass a flag to determine which server(s) to rebuild.'); + + return; + } + + $servers->load('node', 'service', 'option.variables', 'pack'); + + $this->line('Beginning processing, do not exit this script.'); + $bar = $this->output->createProgressBar(count($servers)); + $results = collect([]); + foreach ($servers as $server) { + try { + $environment = $server->option->variables->map(function ($item, $key) use ($server) { + $display = $server->variables->where('variable_id', $item->id)->pluck('variable_value')->first(); + + return [ + 'variable' => $item->env_variable, + 'value' => (! is_null($display)) ? $display : $item->default_value, + ]; + }); + + $server->node->guzzleClient([ + 'X-Access-Server' => $server->uuid, + 'X-Access-Token' => $server->node->daemonSecret, + ])->request('PATCH', '/server', [ + 'json' => [ + 'build' => [ + 'image' => $server->image, + 'env|overwrite' => $environment->pluck('value', 'variable')->merge(['STARTUP' => $server->startup]), + ], + 'service' => [ + 'type' => $server->service->folder, + 'option' => $server->option->tag, + 'pack' => ! is_null($server->pack) ? $server->pack->uuid : null, + ], + ], + ]); + + $results = $results->merge([ + $server->uuid => [ + 'status' => 'info', + 'messages' => [ + '[✓] Processed rebuild request for ' . $server->uuid, + ], + ], + ]); + } catch (\Exception $ex) { + $results = $results->merge([ + $server->uuid => [ + 'status' => 'error', + 'messages' => [ + '[✗] Failed to process rebuild request for ' . $server->uuid, + $ex->getMessage(), + ], + ], + ]); + } + + $bar->advance(); + } + + $bar->finish(); + $console = $this; + + $this->line("\n"); + $results->each(function ($item, $key) use ($console) { + foreach ($item['messages'] as $line) { + $console->{$item['status']}($line); + } + }); + $this->line("\nCompleted rebuild command processing."); + } +} diff --git a/app/Console/Commands/RunTasks.php b/app/Console/Commands/RunTasks.php index 7807f8dbd..6c3ccc6c9 100644 --- a/app/Console/Commands/RunTasks.php +++ b/app/Console/Commands/RunTasks.php @@ -25,7 +25,7 @@ namespace Pterodactyl\Console\Commands; use Carbon; -use Pterodactyl\Models; +use Pterodactyl\Models\Task; use Illuminate\Console\Command; use Pterodactyl\Jobs\SendScheduledTask; use Illuminate\Foundation\Bus\DispatchesJobs; @@ -65,14 +65,14 @@ class RunTasks extends Command */ public function handle() { - $tasks = Models\Task::where('queued', 0)->where('active', 1)->where('next_run', '<=', Carbon::now()->toAtomString())->get(); + $tasks = Task::where('queued', false)->where('active', true)->where('next_run', '<=', Carbon::now()->toAtomString())->get(); $this->info(sprintf('Preparing to queue %d tasks.', count($tasks))); $bar = $this->output->createProgressBar(count($tasks)); foreach ($tasks as &$task) { $bar->advance(); - $this->dispatch((new SendScheduledTask(Models\Server::findOrFail($task->server), $task))->onQueue(env('QUEUE_LOW', 'low'))); + $this->dispatch((new SendScheduledTask($task))->onQueue(config('pterodactyl.queues.low'))); } $bar->finish(); diff --git a/app/Console/Commands/UpdateEmailSettings.php b/app/Console/Commands/UpdateEmailSettings.php index fabfa469d..679d94ee7 100644 --- a/app/Console/Commands/UpdateEmailSettings.php +++ b/app/Console/Commands/UpdateEmailSettings.php @@ -100,6 +100,7 @@ class UpdateEmailSettings extends Command 'Postmark Transactional Email Service', ], ]); + $variables['MAIL_DRIVER'] = is_null($this->option('driver')) ? $this->choice('Which email driver would you like to use?', [ 'smtp', 'mail', @@ -110,9 +111,9 @@ class UpdateEmailSettings extends Command switch ($variables['MAIL_DRIVER']) { case 'smtp': - $variables['MAIL_HOST'] = is_null($this->option('host')) ? $this->ask('SMTP Host (e.g smtp.google.com)') : $this->option('host'); - $variables['MAIL_PORT'] = is_null($this->option('port')) ? $this->anticipate('SMTP Host Port (e.g 587)', ['587']) : $this->option('port'); - $variables['MAIL_USERNAME'] = is_null($this->option('username')) ? $this->ask('SMTP Username') : $this->option('password'); + $variables['MAIL_HOST'] = is_null($this->option('host')) ? $this->ask('SMTP Host (e.g smtp.google.com)', config('mail.host')) : $this->option('host'); + $variables['MAIL_PORT'] = is_null($this->option('port')) ? $this->anticipate('SMTP Host Port (e.g 587)', ['587', config('mail.port')], config('mail.port')) : $this->option('port'); + $variables['MAIL_USERNAME'] = is_null($this->option('username')) ? $this->ask('SMTP Username', config('mail.username')) : $this->option('password'); $variables['MAIL_PASSWORD'] = is_null($this->option('password')) ? $this->secret('SMTP Password') : $this->option('password'); break; case 'mail': @@ -128,7 +129,7 @@ class UpdateEmailSettings extends Command $variables['MAIL_DRIVER'] = 'smtp'; $variables['MAIL_HOST'] = 'smtp.postmarkapp.com'; $variables['MAIL_PORT'] = 587; - $variables['MAIL_USERNAME'] = is_null($this->option('username')) ? $this->ask('Postmark API Token') : $this->option('username'); + $variables['MAIL_USERNAME'] = is_null($this->option('username')) ? $this->ask('Postmark API Token', config('mail.username')) : $this->option('username'); $variables['MAIL_PASSWORD'] = $variables['MAIL_USERNAME']; break; default: @@ -137,8 +138,9 @@ class UpdateEmailSettings extends Command break; } - $variables['MAIL_FROM'] = is_null($this->option('email')) ? $this->ask('Email address emails should originate from') : $this->option('email'); - $variables['MAIL_FROM_NAME'] = is_null($this->option('from-name')) ? $this->ask('Name emails should appear to be from') : $this->option('from-name'); + $variables['MAIL_FROM'] = is_null($this->option('email')) ? $this->ask('Email address emails should originate from', config('mail.from.address')) : $this->option('email'); + $variables['MAIL_FROM_NAME'] = is_null($this->option('from-name')) ? $this->ask('Name emails should appear to be from', config('mail.from.name')) : $this->option('from-name'); + $variables['MAIL_FROM_NAME'] = '"' . $variables['MAIL_FROM_NAME'] . '"'; $variables['MAIL_ENCRYPTION'] = 'tls'; $bar = $this->output->createProgressBar(count($variables)); diff --git a/app/Console/Commands/UpdateEnvironment.php b/app/Console/Commands/UpdateEnvironment.php index aa3dcfe7f..3dd26442f 100644 --- a/app/Console/Commands/UpdateEnvironment.php +++ b/app/Console/Commands/UpdateEnvironment.php @@ -41,6 +41,9 @@ class UpdateEnvironment extends Command {--dbuser=} {--dbpass=} {--url=} + {--driver=} + {--session-driver=} + {--queue-driver=} {--timezone=}'; /** @@ -77,39 +80,37 @@ class UpdateEnvironment extends Command $envContents = file_get_contents($file); $this->info('Simply leave blank and press enter to fields that you do not wish to update.'); - if (! env('SERVICE_AUTHOR', false)) { + if (is_null(config('pterodactyl.service.author', null))) { $this->info('No service author set, setting one now.'); - $variables['SERVICE_AUTHOR'] = env('SERVICE_AUTHOR', (string) Uuid::generate(4)); + $variables['SERVICE_AUTHOR'] = (string) Uuid::generate(4); } - if (! env('QUEUE_STANDARD', false) || ! env('QUEUE_DRIVER', false)) { - $this->info('Setting default queue settings.'); - $variables['QUEUE_DRIVER'] = env('QUEUE_DRIVER', 'database'); - $variables['QUEUE_HIGH'] = env('QUEUE_HIGH', 'high'); - $variables['QUEUE_STANDARD'] = env('QUEUE_STANDARD', 'standard'); - $variables['QUEUE_LOW'] = env('QUEUE_LOW', 'low'); + if(isset($variables['APP_THEME'])) { + if ($variables['APP_THEME'] === 'default') { + $variables['APP_THEME'] = 'pterodactyl'; + } } if (is_null($this->option('dbhost'))) { - $variables['DB_HOST'] = $this->anticipate('Database Host', ['localhost', '127.0.0.1', env('DB_HOST')], env('DB_HOST')); + $variables['DB_HOST'] = $this->anticipate('Database Host', ['localhost', '127.0.0.1', config('database.connections.mysql.host')], config('database.connections.mysql.host')); } else { $variables['DB_HOST'] = $this->option('dbhost'); } if (is_null($this->option('dbport'))) { - $variables['DB_PORT'] = $this->anticipate('Database Port', [3306, env('DB_PORT')], env('DB_PORT')); + $variables['DB_PORT'] = $this->anticipate('Database Port', [3306, config('database.connections.mysql.port')], config('database.connections.mysql.port')); } else { $variables['DB_PORT'] = $this->option('dbport'); } if (is_null($this->option('dbname'))) { - $variables['DB_DATABASE'] = $this->anticipate('Database Name', ['pterodactyl', 'homestead', ENV('DB_DATABASE')], env('DB_DATABASE')); + $variables['DB_DATABASE'] = $this->anticipate('Database Name', ['pterodactyl', 'homestead', config('database.connections.mysql.database')], config('database.connections.mysql.database')); } else { $variables['DB_DATABASE'] = $this->option('dbname'); } if (is_null($this->option('dbuser'))) { - $variables['DB_USERNAME'] = $this->anticipate('Database Username', [ENV('DB_DATABASE')], env('DB_USERNAME')); + $variables['DB_USERNAME'] = $this->anticipate('Database Username', [config('database.connections.mysql.username')], config('database.connections.mysql.username')); } else { $variables['DB_USERNAME'] = $this->option('dbuser'); } @@ -122,25 +123,68 @@ class UpdateEnvironment extends Command } if (is_null($this->option('url'))) { - $variables['APP_URL'] = $this->ask('Panel URL (include http(s)://)', env('APP_URL')); + $variables['APP_URL'] = $this->ask('Panel URL (include http(s)://)', config('app.url')); } else { $variables['APP_URL'] = $this->option('url'); } if (is_null($this->option('timezone'))) { $this->line('The timezone should match one of the supported timezones according to http://php.net/manual/en/timezones.php'); - $variables['APP_TIMEZONE'] = $this->anticipate('Panel Timezone', \DateTimeZone::listIdentifiers(\DateTimeZone::ALL), env('APP_TIMEZONE')); + $variables['APP_TIMEZONE'] = $this->anticipate('Panel Timezone', \DateTimeZone::listIdentifiers(\DateTimeZone::ALL), config('app.timezone')); } else { $variables['APP_TIMEZONE'] = $this->option('timezone'); } - $variables['APP_THEME'] = 'pterodactyl'; - $variables['CACHE_DRIVER'] = 'memcached'; - $variables['SESSION_DRIVER'] = 'database'; + if (is_null($this->option('driver'))) { + $options = [ + 'memcached' => 'Memcache', + 'redis' => 'Redis (recommended)', + 'apc' => 'APC', + 'array' => 'PHP Array', + ]; + $default = (in_array(config('cache.default', 'memcached'), $options)) ? config('cache.default', 'memcached') : 'memcached'; + + $this->line('If you chose redis as your cache driver backend, you *must* have a redis server configured already.'); + $variables['CACHE_DRIVER'] = $this->choice('Which cache driver backend would you like to use?', $options, $default); + } else { + $variables['CACHE_DRIVER'] = $this->option('driver'); + } + + if (is_null($this->option('session-driver'))) { + $options = [ + 'database' => 'MySQL (recommended)', + 'redis' => 'Redis', + 'file' => 'File', + 'cookie' => 'Cookie', + 'apc' => 'APC', + 'array' => 'PHP Array', + ]; + $default = (in_array(config('session.driver', 'database'), $options)) ? config('cache.default', 'database') : 'database'; + + $this->line('If you chose redis as your cache driver backend, you *must* have a redis server configured already.'); + $variables['SESSION_DRIVER'] = $this->choice('Which session driver backend would you like to use?', $options, $default); + } else { + $variables['SESSION_DRIVER'] = $this->option('session-driver'); + } + + if (is_null($this->option('queue-driver'))) { + $options = [ + 'database' => 'Database (recommended)', + 'redis' => 'Redis', + 'sqs' => 'Amazon SQS', + 'sync' => 'Sync', + 'null' => 'None', + ]; + $default = (in_array(config('queue.driver', 'database'), $options)) ? config('queue.driver', 'database') : 'database'; + + $this->line('If you chose redis as your queue driver backend, you *must* have a redis server configured already.'); + $variables['QUEUE_DRIVER'] = $this->choice('Which queue driver backend would you like to use?', $options, $default); + } else { + $variables['QUEUE_DRIVER'] = $this->option('queue-driver'); + } $bar = $this->output->createProgressBar(count($variables)); - $this->line('Writing new environment configuration to file.'); foreach ($variables as $key => $value) { $newValue = $key . '=' . $value; @@ -155,8 +199,7 @@ class UpdateEnvironment extends Command file_put_contents($file, $envContents); $bar->finish(); - $this->line('Updating evironment configuration cache file.'); $this->call('config:cache'); - echo "\n"; + $this->line("\n"); } } diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index d55c3525f..bba5bb66e 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -24,6 +24,7 @@ class Kernel extends ConsoleKernel \Pterodactyl\Console\Commands\CleanServiceBackup::class, \Pterodactyl\Console\Commands\AddNode::class, \Pterodactyl\Console\Commands\AddLocation::class, + \Pterodactyl\Console\Commands\RebuildServer::class, ]; /** diff --git a/app/Events/Auth/FailedCaptcha.php b/app/Events/Auth/FailedCaptcha.php new file mode 100644 index 000000000..ba741cf6d --- /dev/null +++ b/app/Events/Auth/FailedCaptcha.php @@ -0,0 +1,59 @@ +. +* +* 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\Events\Auth; + +use Illuminate\Queue\SerializesModels; + +class FailedCaptcha +{ + use SerializesModels; + + /** + * The IP that the request originated from. + * + * @var string + */ + public $ip; + + /** + * The domain that was used to try to verify the request with recaptcha api. + * + * @var string + */ + public $domain; + + /** + * Create a new event instance. + * + * @param string $ip + * @param string $domain + * @return void + */ + public function __construct($ip, $domain) + { + $this->ip = $ip; + $this->domain = $domain; + } +} diff --git a/app/Jobs/DeleteServer.php b/app/Events/Auth/FailedPasswordReset.php similarity index 68% rename from app/Jobs/DeleteServer.php rename to app/Events/Auth/FailedPasswordReset.php index 16d04b4ba..ec2de3b47 100644 --- a/app/Jobs/DeleteServer.php +++ b/app/Events/Auth/FailedPasswordReset.php @@ -22,42 +22,38 @@ * SOFTWARE. */ -namespace Pterodactyl\Jobs; +namespace Pterodactyl\Events\Auth; use Illuminate\Queue\SerializesModels; -use Illuminate\Queue\InteractsWithQueue; -use Illuminate\Contracts\Queue\ShouldQueue; -use Pterodactyl\Repositories\ServerRepository; -class DeleteServer extends Job implements ShouldQueue +class FailedPasswordReset { - use InteractsWithQueue, SerializesModels; + use SerializesModels; /** - * Id of server to be deleted. - * @var object - */ - protected $id; - - /** - * Create a new job instance. + * The IP that the request originated from. * - * @param int $server + * @var string + */ + public $ip; + + /** + * The email address that was used when the reset request failed. + * + * @var string + */ + public $email; + + /** + * Create a new event instance. + * + * @param string $ip + * @param string $email * @return void */ - public function __construct($id) + public function __construct($ip, $email) { - $this->id = $id; - } - - /** - * Execute the job. - * - * @return void - */ - public function handle() - { - $repo = new ServerRepository; - $repo->delete($this->id); + $this->ip = $ip; + $this->email = $email; } } diff --git a/app/Events/Server/Created.php b/app/Events/Server/Created.php index d5a308876..2591cd5af 100644 --- a/app/Events/Server/Created.php +++ b/app/Events/Server/Created.php @@ -31,11 +31,17 @@ class Created { use SerializesModels; + /** + * The Eloquent model of the server. + * + * @var \Pterodactyl\Models\Server + */ public $server; /** * Create a new event instance. * + * @param \Pterodactyl\Models\Server $server * @return void */ public function __construct(Server $server) diff --git a/app/Events/Server/Creating.php b/app/Events/Server/Creating.php index cbba63eb1..46e4898c1 100644 --- a/app/Events/Server/Creating.php +++ b/app/Events/Server/Creating.php @@ -31,11 +31,17 @@ class Creating { use SerializesModels; + /** + * The Eloquent model of the server. + * + * @var \Pterodactyl\Models\Server + */ public $server; /** * Create a new event instance. * + * @param \Pterodactyl\Models\Server $server * @return void */ public function __construct(Server $server) diff --git a/app/Events/Server/Deleted.php b/app/Events/Server/Deleted.php index b36dca12d..6f8709b85 100644 --- a/app/Events/Server/Deleted.php +++ b/app/Events/Server/Deleted.php @@ -31,11 +31,17 @@ class Deleted { use SerializesModels; + /** + * The Eloquent model of the server. + * + * @var \Pterodactyl\Models\Server + */ public $server; /** * Create a new event instance. * + * @param \Pterodactyl\Models\Server $server * @return void */ public function __construct(Server $server) diff --git a/app/Events/Server/Deleting.php b/app/Events/Server/Deleting.php index bb18e1ee9..3152ed96e 100644 --- a/app/Events/Server/Deleting.php +++ b/app/Events/Server/Deleting.php @@ -31,11 +31,17 @@ class Deleting { use SerializesModels; + /** + * The Eloquent model of the server. + * + * @var \Pterodactyl\Models\Server + */ public $server; /** * Create a new event instance. * + * @param \Pterodactyl\Models\Server $server * @return void */ public function __construct(Server $server) diff --git a/app/Events/Server/Saved.php b/app/Events/Server/Saved.php index 08700e3cb..d19659045 100644 --- a/app/Events/Server/Saved.php +++ b/app/Events/Server/Saved.php @@ -31,11 +31,17 @@ class Saved { use SerializesModels; + /** + * The Eloquent model of the server. + * + * @var \Pterodactyl\Models\Server + */ public $server; /** * Create a new event instance. * + * @param \Pterodactyl\Models\Server $server * @return void */ public function __construct(Server $server) diff --git a/app/Events/Server/Saving.php b/app/Events/Server/Saving.php index 2c6d3cb4e..1ae31da32 100644 --- a/app/Events/Server/Saving.php +++ b/app/Events/Server/Saving.php @@ -31,11 +31,17 @@ class Saving { use SerializesModels; + /** + * The Eloquent model of the server. + * + * @var \Pterodactyl\Models\Server + */ public $server; /** * Create a new event instance. * + * @param \Pterodactyl\Models\Server $server * @return void */ public function __construct(Server $server) diff --git a/app/Events/Server/Updated.php b/app/Events/Server/Updated.php index 0310e20c6..1284ea504 100644 --- a/app/Events/Server/Updated.php +++ b/app/Events/Server/Updated.php @@ -31,11 +31,17 @@ class Updated { use SerializesModels; + /** + * The Eloquent model of the server. + * + * @var \Pterodactyl\Models\Server + */ public $server; /** * Create a new event instance. * + * @param \Pterodactyl\Models\Server $server * @return void */ public function __construct(Server $server) diff --git a/app/Events/Server/Updating.php b/app/Events/Server/Updating.php index f333ede6b..b2fc255c8 100644 --- a/app/Events/Server/Updating.php +++ b/app/Events/Server/Updating.php @@ -31,11 +31,17 @@ class Updating { use SerializesModels; + /** + * The Eloquent model of the server. + * + * @var \Pterodactyl\Models\Server + */ public $server; /** * Create a new event instance. * + * @param \Pterodactyl\Models\Server $server * @return void */ public function __construct(Server $server) diff --git a/app/Events/Subuser/Created.php b/app/Events/Subuser/Created.php index 7b3be9597..9a2d28536 100644 --- a/app/Events/Subuser/Created.php +++ b/app/Events/Subuser/Created.php @@ -31,11 +31,17 @@ class Created { use SerializesModels; + /** + * The Eloquent model of the server. + * + * @var \Pterodactyl\Models\Subuser + */ public $subuser; /** * Create a new event instance. * + * @param \Pterodactyl\Models\Subuser $subuser * @return void */ public function __construct(Subuser $subuser) diff --git a/app/Events/Subuser/Creating.php b/app/Events/Subuser/Creating.php index 353497e24..5083c497c 100644 --- a/app/Events/Subuser/Creating.php +++ b/app/Events/Subuser/Creating.php @@ -31,11 +31,17 @@ class Creating { use SerializesModels; + /** + * The Eloquent model of the server. + * + * @var \Pterodactyl\Models\Subuser + */ public $subuser; /** * Create a new event instance. * + * @param \Pterodactyl\Models\Subuser $subuser * @return void */ public function __construct(Subuser $subuser) diff --git a/app/Events/Subuser/Deleted.php b/app/Events/Subuser/Deleted.php index 3ffe3afe8..1e419ab81 100644 --- a/app/Events/Subuser/Deleted.php +++ b/app/Events/Subuser/Deleted.php @@ -31,11 +31,17 @@ class Deleted { use SerializesModels; + /** + * The Eloquent model of the server. + * + * @var \Pterodactyl\Models\Subuser + */ public $subuser; /** * Create a new event instance. * + * @param \Pterodactyl\Models\Subuser $subuser * @return void */ public function __construct(Subuser $subuser) diff --git a/app/Events/Subuser/Deleting.php b/app/Events/Subuser/Deleting.php index da812019b..a06ebe2dc 100644 --- a/app/Events/Subuser/Deleting.php +++ b/app/Events/Subuser/Deleting.php @@ -31,11 +31,17 @@ class Deleting { use SerializesModels; + /** + * The Eloquent model of the server. + * + * @var \Pterodactyl\Models\Subuser + */ public $subuser; /** * Create a new event instance. * + * @param \Pterodactyl\Models\Subuser $subuser * @return void */ public function __construct(Subuser $subuser) diff --git a/app/Events/User/Created.php b/app/Events/User/Created.php index a00bdd450..4e1fd9e75 100644 --- a/app/Events/User/Created.php +++ b/app/Events/User/Created.php @@ -31,11 +31,17 @@ class Created { use SerializesModels; + /** + * The Eloquent model of the server. + * + * @var \Pterodactyl\Models\User + */ public $user; /** * Create a new event instance. * + * @param \Pterodactyl\Models\User $user * @return void */ public function __construct(User $user) diff --git a/app/Events/User/Creating.php b/app/Events/User/Creating.php index 39a50f0c8..cc544af9e 100644 --- a/app/Events/User/Creating.php +++ b/app/Events/User/Creating.php @@ -31,11 +31,17 @@ class Creating { use SerializesModels; + /** + * The Eloquent model of the server. + * + * @var \Pterodactyl\Models\User + */ public $user; /** * Create a new event instance. * + * @param \Pterodactyl\Models\User $user * @return void */ public function __construct(User $user) diff --git a/app/Events/User/Deleted.php b/app/Events/User/Deleted.php index 348c859c9..a3cc3cffe 100644 --- a/app/Events/User/Deleted.php +++ b/app/Events/User/Deleted.php @@ -31,11 +31,17 @@ class Deleted { use SerializesModels; + /** + * The Eloquent model of the server. + * + * @var \Pterodactyl\Models\User + */ public $user; /** * Create a new event instance. * + * @param \Pterodactyl\Models\User $user * @return void */ public function __construct(User $user) diff --git a/app/Events/User/Deleting.php b/app/Events/User/Deleting.php index 5bbd91366..ad9397e45 100644 --- a/app/Events/User/Deleting.php +++ b/app/Events/User/Deleting.php @@ -31,11 +31,17 @@ class Deleting { use SerializesModels; + /** + * The Eloquent model of the server. + * + * @var \Pterodactyl\Models\User + */ public $user; /** * Create a new event instance. * + * @param \Pterodactyl\Models\User $user * @return void */ public function __construct(User $user) diff --git a/app/Exceptions/AccountNotFoundException.php b/app/Exceptions/AccountNotFoundException.php index f8e36ed49..b35bd2fc0 100644 --- a/app/Exceptions/AccountNotFoundException.php +++ b/app/Exceptions/AccountNotFoundException.php @@ -26,4 +26,5 @@ namespace Pterodactyl\Exceptions; class AccountNotFoundException extends \Exception { + // } diff --git a/app/Exceptions/DisplayException.php b/app/Exceptions/DisplayException.php index c1b701f36..f3f8fd719 100644 --- a/app/Exceptions/DisplayException.php +++ b/app/Exceptions/DisplayException.php @@ -28,20 +28,19 @@ use Log; class DisplayException extends \Exception { - private $_logging = null; - + /** + * Exception constructor. + * + * @param string $message + * @param mixed $log + * @return void + */ public function __construct($message, $log = null) { - $this->_logging = $log; - if ($this->_logging !== null) { + if (! is_null($log)) { Log::error($log); } parent::__construct($message); } - - public function getLogging() - { - return $this->_logging; - } } diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 6aac7cc02..71d66cdc6 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -4,7 +4,6 @@ namespace Pterodactyl\Exceptions; use Log; use Exception; -use DisplayException; use Illuminate\Auth\AuthenticationException; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; @@ -29,7 +28,7 @@ class Handler extends ExceptionHandler * * This is a great spot to send exceptions to Sentry, Bugsnag, etc. * - * @param \Exception $e + * @param \Exception $exception * @return void */ public function report(Exception $exception) @@ -41,15 +40,25 @@ class Handler extends ExceptionHandler * Render an exception into an HTTP response. * * @param \Illuminate\Http\Request $request - * @param \Exception $e + * @param \Exception $exception * @return \Illuminate\Http\Response */ public function render($request, Exception $exception) { - if ($request->expectsJson()) { + if ($request->expectsJson() || $request->isJson() || $request->is(...config('pterodactyl.json_routes'))) { + $exception = $this->prepareException($exception); + + if (config('app.debug')) { + $report = [ + 'code' => (! $this->isHttpException($exception)) ?: $exception->getStatusCode(), + 'message' => class_basename($exception) . ' in ' . $exception->getFile() . ' on line ' . $exception->getLine(), + ]; + } + $response = response()->json([ - 'error' => ($exception instanceof DisplayException) ? $exception->getMessage() : 'An unhandled error occured while attempting to process this request.', - ], ($this->isHttpException($exception)) ? $exception->getStatusCode() : 500); + 'error' => (config('app.debug')) ? $exception->getMessage() : 'An unhandled exception was encountered with this request.', + 'exception' => ! isset($report) ?: $report, + ], ($this->isHttpException($exception)) ? $exception->getStatusCode() : 500, [], JSON_UNESCAPED_SLASHES); parent::report($exception); } @@ -60,7 +69,7 @@ class Handler extends ExceptionHandler /** * Convert an authentication exception into an unauthenticated response. * - * @param \Illuminate\Http\Request $request + * @param \Illuminate\Http\Request $request * @param \Illuminate\Auth\AuthenticationException $exception * @return \Illuminate\Http\Response */ @@ -70,6 +79,6 @@ class Handler extends ExceptionHandler return response()->json(['error' => 'Unauthenticated.'], 401); } - return redirect()->guest('/auth/login'); + return redirect()->guest(route('auth.login')); } } diff --git a/app/Extensions/PhraseAppTranslator.php b/app/Extensions/PhraseAppTranslator.php index 5a8d4a84e..f79cf2820 100644 --- a/app/Extensions/PhraseAppTranslator.php +++ b/app/Extensions/PhraseAppTranslator.php @@ -31,11 +31,11 @@ class PhraseAppTranslator extends LaravelTranslator /** * Get the translation for the given key. * - * @param string $key - * @param array $replace + * @param string $key + * @param array $replace * @param string|null $locale - * @param bool $fallback - * @return string|array|null + * @param bool $fallback + * @return string */ public function get($key, array $replace = [], $locale = null, $fallback = true) { diff --git a/app/Facades/Version.php b/app/Facades/Version.php index 2ce007360..e9475d2a6 100644 --- a/app/Facades/Version.php +++ b/app/Facades/Version.php @@ -28,6 +28,11 @@ use Illuminate\Support\Facades\Facade; class Version extends Facade { + /** + * Returns the facade accessor class. + * + * @return strig + */ protected static function getFacadeAccessor() { return '\Pterodactyl\Services\VersionService'; diff --git a/app/Http/Controllers/API/LocationController.php b/app/Http/Controllers/API/Admin/LocationController.php old mode 100755 new mode 100644 similarity index 65% rename from app/Http/Controllers/API/LocationController.php rename to app/Http/Controllers/API/Admin/LocationController.php index fcaf5a71c..41405e91e --- a/app/Http/Controllers/API/LocationController.php +++ b/app/Http/Controllers/API/Admin/LocationController.php @@ -22,38 +22,30 @@ * SOFTWARE. */ -namespace Pterodactyl\Http\Controllers\API; +namespace Pterodactyl\Http\Controllers\API\Admin; +use Fractal; use Illuminate\Http\Request; use Pterodactyl\Models\Location; +use Pterodactyl\Http\Controllers\Controller; +use Pterodactyl\Transformers\Admin\LocationTransformer; -/** - * @Resource("Servers") - */ -class LocationController extends BaseController +class LocationController extends Controller { - public function __construct() - { - // - } - /** - * List All Locations. + * Controller to handle returning all locations on the system. * - * Lists all locations currently on the system. - * - * @Get("/locations") - * @Versions({"v1"}) - * @Response(200) + * @param \Illuminate\Http\Request $request + * @return array */ - public function lists(Request $request) + public function index(Request $request) { - return Location::with('nodes')->get()->map(function ($item) { - $item->nodes->transform(function ($item) { - return collect($item)->only(['id', 'name', 'fqdn', 'scheme', 'daemonListen', 'daemonSFTP']); - }); + $this->authorize('location-list', $request->apiKey()); - return $item; - })->toArray(); + return Fractal::create() + ->collection(Location::all()) + ->transformWith(new LocationTransformer($request)) + ->withResourceName('location') + ->toArray(); } } diff --git a/app/Http/Controllers/API/Admin/NodeController.php b/app/Http/Controllers/API/Admin/NodeController.php new file mode 100644 index 000000000..d70e89969 --- /dev/null +++ b/app/Http/Controllers/API/Admin/NodeController.php @@ -0,0 +1,173 @@ +. + * + * 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\Http\Controllers\API\Admin; + +use Fractal; +use Illuminate\Http\Request; +use Pterodactyl\Models\Node; +use Pterodactyl\Exceptions\DisplayException; +use Pterodactyl\Http\Controllers\Controller; +use Pterodactyl\Repositories\NodeRepository; +use Pterodactyl\Transformers\Admin\NodeTransformer; +use Pterodactyl\Exceptions\DisplayValidationException; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; + +class NodeController extends Controller +{ + /** + * Controller to handle returning all nodes on the system. + * + * @param \Illuminate\Http\Request $request + * @return array + */ + public function index(Request $request) + { + $this->authorize('node-list', $request->apiKey()); + + $nodes = Node::paginate(config('pterodactyl.paginate.api.nodes')); + $fractal = Fractal::create()->collection($nodes) + ->transformWith(new NodeTransformer($request)) + ->withResourceName('user') + ->paginateWith(new IlluminatePaginatorAdapter($nodes)); + + if (config('pterodactyl.api.include_on_list') && $request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->toArray(); + } + + /** + * Display information about a single node on the system. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return array + */ + public function view(Request $request, $id) + { + $this->authorize('node-view', $request->apiKey()); + + $fractal = Fractal::create()->item(Node::findOrFail($id)); + if ($request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->transformWith(new NodeTransformer($request)) + ->withResourceName('node') + ->toArray(); + } + + /** + * Display information about a single node on the system. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\JsonResponse + */ + public function viewConfig(Request $request, $id) + { + $this->authorize('node-view-config', $request->apiKey()); + + $node = Node::findOrFail($id); + + return response()->json(json_decode($node->getConfigurationAsJson())); + } + + /** + * Create a new node on the system. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\JsonResponse|array + */ + public function store(Request $request) + { + $this->authorize('node-create', $request->apiKey()); + + $repo = new NodeRepository; + try { + $node = $repo->create(array_merge( + $request->only([ + 'public', 'disk_overallocate', 'memory_overallocate', + ]), + $request->intersect([ + 'name', 'location_id', 'fqdn', + 'scheme', 'memory', 'disk', + 'daemonBase', 'daemonSFTP', 'daemonListen', + ]) + )); + + $fractal = Fractal::create()->item($node)->transformWith(new NodeTransformer($request)); + if ($request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->withResourceName('node')->toArray(); + } catch (DisplayValidationException $ex) { + return response()->json([ + 'error' => json_decode($ex->getMessage()), + ], 400); + } catch (DisplayException $ex) { + return response()->json([ + 'error' => $ex->getMessage(), + ], 400); + } catch (\Exception $ex) { + Log::error($ex); + + return response()->json([ + 'error' => 'An unhandled exception occured while attemping to create this node. Please try again.', + ], 500); + } + } + + /** + * Delete a node from the system. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse + */ + public function delete(Request $request, $id) + { + $this->authorize('node-delete', $request->apiKey()); + + $repo = new NodeRepository; + try { + $repo->delete($id); + + return response('', 204); + } catch (DisplayException $ex) { + return response()->json([ + 'error' => $ex->getMessage(), + ], 400); + } catch (\Exception $ex) { + Log::error($ex); + + return response()->json([ + 'error' => 'An unhandled exception occured while attemping to delete this node. Please try again.', + ], 500); + } + } +} diff --git a/app/Http/Controllers/API/Admin/ServerController.php b/app/Http/Controllers/API/Admin/ServerController.php new file mode 100644 index 000000000..75bcdc107 --- /dev/null +++ b/app/Http/Controllers/API/Admin/ServerController.php @@ -0,0 +1,428 @@ +. + * + * 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\Http\Controllers\API\Admin; + +use Fractal; +use Illuminate\Http\Request; +use Pterodactyl\Models\Server; +use GuzzleHttp\Exception\TransferException; +use Pterodactyl\Exceptions\DisplayException; +use Pterodactyl\Http\Controllers\Controller; +use Pterodactyl\Repositories\ServerRepository; +use Pterodactyl\Transformers\Admin\ServerTransformer; +use Pterodactyl\Exceptions\DisplayValidationException; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; + +class ServerController extends Controller +{ + /** + * Controller to handle returning all servers on the system. + * + * @param \Illuminate\Http\Request $request + * @return array + */ + public function index(Request $request) + { + $this->authorize('server-list', $request->apiKey()); + + $servers = Server::paginate(config('pterodactyl.paginate.api.servers')); + $fractal = Fractal::create()->collection($servers) + ->transformWith(new ServerTransformer($request)) + ->withResourceName('user') + ->paginateWith(new IlluminatePaginatorAdapter($servers)); + + if (config('pterodactyl.api.include_on_list') && $request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->toArray(); + } + + /** + * Controller to handle returning information on a single server. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return array + */ + public function view(Request $request, $id) + { + $this->authorize('server-view', $request->apiKey()); + + $server = Server::findOrFail($id); + $fractal = Fractal::create()->item($server); + + if ($request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->transformWith(new ServerTransformer($request)) + ->withResourceName('server') + ->toArray(); + } + + /** + * Create a new server on the system. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\JsonResponse|array + */ + public function store(Request $request) + { + $this->authorize('server-create', $request->apiKey()); + + $repo = new ServerRepository; + try { + $server = $repo->create($request->all()); + + $fractal = Fractal::create()->item($server)->transformWith(new ServerTransformer($request)); + if ($request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->withResourceName('server')->toArray(); + } catch (DisplayValidationException $ex) { + return response()->json([ + 'error' => json_decode($ex->getMessage()), + ], 400); + } catch (DisplayException $ex) { + return response()->json([ + 'error' => $ex->getMessage(), + ], 400); + } catch (TransferException $ex) { + Log::warning($ex); + + return response()->json([ + 'error' => 'A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.', + ], 504); + } catch (\Exception $ex) { + Log::error($ex); + + return response()->json([ + 'error' => 'An unhandled exception occured while attemping to add this server. Please try again.', + ], 500); + } + } + + /** + * Delete a server from the system. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse + */ + public function delete(Request $request, $id) + { + $this->authorize('server-delete', $request->apiKey()); + + $repo = new ServerRepository; + try { + $repo->delete($id, $request->has('force_delete')); + + return response('', 204); + } catch (DisplayException $ex) { + return response()->json([ + 'error' => $ex->getMessage(), + ], 400); + } catch (TransferException $ex) { + Log::warning($ex); + + return response()->json([ + 'error' => 'A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.', + ], 504); + } catch (\Exception $ex) { + Log::error($ex); + + return response()->json([ + 'error' => 'An unhandled exception occured while attemping to add this server. Please try again.', + ], 500); + } + } + + /** + * Update the details for a server. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\JsonResponse|array + */ + public function details(Request $request, $id) + { + $this->authorize('server-edit-details', $request->apiKey()); + + $repo = new ServerRepository; + try { + $server = $repo->updateDetails($id, $request->intersect([ + 'owner_id', 'name', 'description', 'reset_token', + ])); + + $fractal = Fractal::create()->item($server)->transformWith(new ServerTransformer($request)); + if ($request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->withResourceName('server')->toArray(); + } catch (DisplayValidationException $ex) { + return response()->json([ + 'error' => json_decode($ex->getMessage()), + ], 400); + } catch (DisplayException $ex) { + return response()->json([ + 'error' => $ex->getMessage(), + ], 400); + } catch (\Exception $ex) { + Log::error($ex); + + return response()->json([ + 'error' => 'An unhandled exception occured while attemping to modify this server. Please try again.', + ], 500); + } + } + + /** + * Set the new docker container for a server. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\RedirectResponse|array + */ + public function container(Request $request, $id) + { + $this->authorize('server-edit-container', $request->apiKey()); + + $repo = new ServerRepository; + try { + $server = $repo->updateContainer($id, $request->intersect('docker_image')); + + $fractal = Fractal::create()->item($server)->transformWith(new ServerTransformer($request)); + if ($request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->withResourceName('server')->toArray(); + } catch (DisplayValidationException $ex) { + return response()->json([ + 'error' => json_decode($ex->getMessage()), + ], 400); + } catch (TransferException $ex) { + Log::warning($ex); + + return response()->json([ + 'error' => 'A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.', + ], 504); + } catch (\Exception $ex) { + Log::error($ex); + + return response()->json([ + 'error' => 'An unhandled exception occured while attemping to modify this server container. Please try again.', + ], 500); + } + } + + /** + * Toggles the install status for a server. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse + */ + public function install(Request $request, $id) + { + $this->authorize('server-install', $request->apiKey()); + + $repo = new ServerRepository; + try { + $repo->toggleInstall($id); + + return response('', 204); + } catch (DisplayException $ex) { + return response()->json([ + 'error' => $ex->getMessage(), + ], 400); + } catch (\Exception $ex) { + Log::error($ex); + + return response()->json([ + 'error' => 'An unhandled exception occured while attemping to toggle the install status for this server. Please try again.', + ], 500); + } + } + + /** + * Setup a server to have a container rebuild. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse + */ + public function rebuild(Request $request, $id) + { + $this->authorize('server-rebuild', $request->apiKey()); + $server = Server::with('node')->findOrFail($id); + + try { + $server->node->guzzleClient([ + 'X-Access-Server' => $server->uuid, + 'X-Access-Token' => $server->node->daemonSecret, + ])->request('POST', '/server/rebuild'); + + return response('', 204); + } catch (TransferException $ex) { + Log::warning($ex); + + return response()->json([ + 'error' => 'A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.', + ], 504); + } + } + + /** + * Manage the suspension status for a server. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse + */ + public function suspend(Request $request, $id) + { + $this->authorize('server-suspend', $request->apiKey()); + + $repo = new ServerRepository; + $action = $request->input('action'); + if (! in_array($action, ['suspend', 'unsuspend'])) { + return response()->json([ + 'error' => 'The action provided was invalid. Action should be one of: suspend, unsuspend.', + ], 400); + } + + try { + $repo->toggleAccess($id, ($action === 'unsuspend')); + + return response('', 204); + } catch (DisplayException $ex) { + return response()->json([ + 'error' => $ex->getMessage(), + ], 400); + } catch (TransferException $ex) { + Log::warning($ex); + + return response()->json([ + 'error' => 'A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.', + ], 504); + } catch (\Exception $ex) { + Log::error($ex); + + return response()->json([ + 'error' => 'An unhandled exception occured while attemping to ' . $action . ' this server. Please try again.', + ], 500); + } + } + + /** + * Update the build configuration for a server. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\JsonResponse|array + */ + public function build(Request $request, $id) + { + $this->authorize('server-edit-build', $request->apiKey()); + + $repo = new ServerRepository; + try { + $server = $repo->changeBuild($id, $request->intersect([ + 'allocation_id', 'add_allocations', 'remove_allocations', + 'memory', 'swap', 'io', 'cpu', + ])); + + $fractal = Fractal::create()->item($server)->transformWith(new ServerTransformer($request)); + if ($request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->withResourceName('server')->toArray(); + } catch (DisplayValidationException $ex) { + return response()->json([ + 'error' => json_decode($ex->getMessage()), + ], 400); + } catch (DisplayException $ex) { + return response()->json([ + 'error' => $ex->getMessage(), + ], 400); + } catch (TransferException $ex) { + Log::warning($ex); + + return response()->json([ + 'error' => 'A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.', + ], 504); + } catch (\Exception $ex) { + Log::error($ex); + + return response()->json([ + 'error' => 'An unhandled exception occured while attemping to modify the build settings for this server. Please try again.', + ], 500); + } + } + + /** + * Update the startup command as well as variables. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse + */ + public function startup(Request $request, $id) + { + $this->authorize('server-edit-startup', $request->apiKey()); + + $repo = new ServerRepository; + try { + $repo->updateStartup($id, $request->all(), true); + + return response('', 204); + } catch (DisplayValidationException $ex) { + return response()->json([ + 'error' => json_decode($ex->getMessage()), + ], 400); + } catch (DisplayException $ex) { + return response()->json([ + 'error' => $ex->getMessage(), + ], 400); + } catch (TransferException $ex) { + Log::warning($ex); + + return response()->json([ + 'error' => 'A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.', + ], 504); + } catch (\Exception $ex) { + Log::error($ex); + + return response()->json([ + 'error' => 'An unhandled exception occured while attemping to modify the startup settings for this server. Please try again.', + ], 500); + } + } +} diff --git a/app/Http/Controllers/API/Admin/ServiceController.php b/app/Http/Controllers/API/Admin/ServiceController.php new file mode 100644 index 000000000..fbe911f14 --- /dev/null +++ b/app/Http/Controllers/API/Admin/ServiceController.php @@ -0,0 +1,74 @@ +. + * + * 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\Http\Controllers\API\Admin; + +use Fractal; +use Illuminate\Http\Request; +use Pterodactyl\Models\Service; +use Pterodactyl\Http\Controllers\Controller; +use Pterodactyl\Transformers\Admin\ServiceTransformer; + +class ServiceController extends Controller +{ + /** + * Controller to handle returning all locations on the system. + * + * @param \Illuminate\Http\Request $request + * @return array + */ + public function index(Request $request) + { + $this->authorize('service-list', $request->apiKey()); + + return Fractal::create() + ->collection(Service::all()) + ->transformWith(new ServiceTransformer($request)) + ->withResourceName('service') + ->toArray(); + } + + /** + * Controller to handle returning information on a single server. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return array + */ + public function view(Request $request, $id) + { + $this->authorize('service-view', $request->apiKey()); + + $service = Service::findOrFail($id); + $fractal = Fractal::create()->item($service); + + if ($request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->transformWith(new ServiceTransformer($request)) + ->withResourceName('service') + ->toArray(); + } +} diff --git a/app/Http/Controllers/API/Admin/UserController.php b/app/Http/Controllers/API/Admin/UserController.php new file mode 100644 index 000000000..150ca4f87 --- /dev/null +++ b/app/Http/Controllers/API/Admin/UserController.php @@ -0,0 +1,184 @@ +. + * + * 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\Http\Controllers\API\Admin; + +use Fractal; +use Illuminate\Http\Request; +use Pterodactyl\Models\User; +use Pterodactyl\Exceptions\DisplayException; +use Pterodactyl\Http\Controllers\Controller; +use Pterodactyl\Repositories\UserRepository; +use Pterodactyl\Transformers\Admin\UserTransformer; +use Pterodactyl\Exceptions\DisplayValidationException; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; + +class UserController extends Controller +{ + /** + * Controller to handle returning all users on the system. + * + * @param \Illuminate\Http\Request $request + * @return array + */ + public function index(Request $request) + { + $this->authorize('user-list', $request->apiKey()); + + $users = User::paginate(config('pterodactyl.paginate.api.users')); + $fractal = Fractal::create()->collection($users) + ->transformWith(new UserTransformer($request)) + ->withResourceName('user') + ->paginateWith(new IlluminatePaginatorAdapter($users)); + + if (config('pterodactyl.api.include_on_list') && $request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->toArray(); + } + + /** + * Display information about a single user on the system. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return array + */ + public function view(Request $request, $id) + { + $this->authorize('user-view', $request->apiKey()); + + $fractal = Fractal::create()->item(User::findOrFail($id)); + if ($request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->transformWith(new UserTransformer($request)) + ->withResourceName('user') + ->toArray(); + } + + /** + * Create a new user on the system. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\JsonResponse|array + */ + public function store(Request $request) + { + $this->authorize('user-create', $request->apiKey()); + + $repo = new UserRepository; + try { + $user = $repo->create($request->only([ + 'custom_id', 'email', 'password', 'name_first', + 'name_last', 'username', 'root_admin', + ])); + + $fractal = Fractal::create()->item($user)->transformWith(new UserTransformer($request)); + if ($request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->withResourceName('user')->toArray(); + } catch (DisplayValidationException $ex) { + return response()->json([ + 'error' => json_decode($ex->getMessage()), + ], 400); + } catch (\Exception $ex) { + Log::error($ex); + + return response()->json([ + 'error' => 'An unhandled exception occured while attemping to create this user. Please try again.', + ], 500); + } + } + + /** + * Update a user. + * + * @param \Illuminate\Http\Request $request + * @param int $user + * @return \Illuminate\Http\RedirectResponse + */ + public function update(Request $request, $user) + { + $this->authorize('user-edit', $request->apiKey()); + + $repo = new UserRepository; + try { + $user = $repo->update($user, $request->intersect([ + 'email', 'password', 'name_first', + 'name_last', 'username', 'root_admin', + ])); + + $fractal = Fractal::create()->item($user)->transformWith(new UserTransformer($request)); + if ($request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->withResourceName('user')->toArray(); + } catch (DisplayValidationException $ex) { + return response()->json([ + 'error' => json_decode($ex->getMessage()), + ], 400); + } catch (\Exception $ex) { + Log::error($ex); + + return response()->json([ + 'error' => 'An unhandled exception occured while attemping to update this user. Please try again.', + ], 500); + } + } + + /** + * Delete a user from the system. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse + */ + public function delete(Request $request, $id) + { + $this->authorize('user-delete', $request->apiKey()); + + $repo = new UserRepository; + try { + $repo->delete($id); + + return response('', 204); + } catch (DisplayException $ex) { + return response()->json([ + 'error' => $ex->getMessage(), + ], 400); + } catch (\Exception $ex) { + Log::error($ex); + + return response()->json([ + 'error' => 'An unhandled exception occured while attemping to delete this user. Please try again.', + ], 500); + } + } +} diff --git a/app/Http/Controllers/API/NodeController.php b/app/Http/Controllers/API/NodeController.php deleted file mode 100755 index b0cfb452a..000000000 --- a/app/Http/Controllers/API/NodeController.php +++ /dev/null @@ -1,216 +0,0 @@ -. - * - * 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\Http\Controllers\API; - -use Log; -use Pterodactyl\Models; -use Illuminate\Http\Request; -use Dingo\Api\Exception\ResourceException; -use Pterodactyl\Exceptions\DisplayException; -use Pterodactyl\Repositories\NodeRepository; -use Pterodactyl\Exceptions\DisplayValidationException; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; -use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException; - -/** - * @Resource("Servers") - */ -class NodeController extends BaseController -{ - public function __construct() - { - // - } - - /** - * List All Nodes. - * - * Lists all nodes currently on the system. - * - * @Get("/nodes/{?page}") - * @Versions({"v1"}) - * @Parameters({ - * @Parameter("page", type="integer", description="The page of results to view.", default=1) - * }) - * @Response(200) - */ - public function lists(Request $request) - { - return Models\Node::all()->toArray(); - } - - /** - * Create a New Node. - * - * @Post("/nodes") - * @Versions({"v1"}) - * @Transaction({ - * @Request({ - * 'name' => 'My API Node', - * 'location' => 1, - * 'public' => 1, - * 'fqdn' => 'daemon.wuzzle.woo', - * 'scheme' => 'https', - * 'memory' => 10240, - * 'memory_overallocate' => 100, - * 'disk' => 204800, - * 'disk_overallocate' => -1, - * 'daemonBase' => '/srv/daemon-data', - * 'daemonSFTP' => 2022, - * 'daemonListen' => 8080 - * }, headers={"Authorization": "Bearer "}), - * @Response(200), - * @Response(422, body={ - * "message": "A validation error occured.", - * "errors": {}, - * "status_code": 422 - * }), - * @Response(503, body={ - * "message": "There was an error while attempting to add this node to the system.", - * "status_code": 503 - * }) - * }) - */ - public function create(Request $request) - { - try { - $repo = new NodeRepository; - $node = $repo->create($request->only([ - 'name', 'location_id', 'public', 'fqdn', - 'scheme', 'memory', 'memory_overallocate', - 'disk', 'disk_overallocate', 'daemonBase', - 'daemonSFTP', 'daemonListen', - ])); - - return ['id' => $repo->id]; - } catch (DisplayValidationException $ex) { - throw new ResourceException('A validation error occured.', json_decode($ex->getMessage(), true)); - } catch (DisplayException $ex) { - throw new ResourceException($ex->getMessage()); - } catch (\Exception $ex) { - Log::error($ex); - throw new BadRequestHttpException('There was an error while attempting to add this node to the system.'); - } - } - - /** - * List Specific Node. - * - * Lists specific fields about a server or all fields pertaining to that node. - * - * @Get("/nodes/{id}/{?fields}") - * @Versions({"v1"}) - * @Parameters({ - * @Parameter("id", type="integer", required=true, description="The ID of the node to get information on."), - * @Parameter("fields", type="string", required=false, description="A comma delimidated list of fields to include.") - * }) - * @Response(200) - */ - public function view(Request $request, $id, $fields = null) - { - $node = Models\Node::with('allocations')->where('id', $id)->first(); - if (! $node) { - throw new NotFoundHttpException('No node by that ID was found.'); - } - - $node->allocations->transform(function ($item) { - return collect($item)->only([ - 'id', 'ip', 'ip_alias', 'port', 'server_id', - ]); - }); - - if (! is_null($request->input('fields'))) { - $fields = explode(',', $request->input('fields')); - if (! empty($fields) && is_array($fields)) { - return collect($node)->only($fields); - } - } - - return $node; - } - - public function config(Request $request, $id) - { - $node = Models\Node::where('id', $id)->first(); - if (! $node) { - throw new NotFoundHttpException('No node by that ID was found.'); - } - - return $node->getConfigurationAsJson(); - } - - /** - * List all Node Allocations. - * - * Returns a listing of all allocations for every node. - * - * @Get("/nodes/allocations") - * @Versions({"v1"}) - * @Response(200) - */ - public function allocations(Request $request) - { - return Models\Allocation::all()->toArray(); - } - - /** - * List Node Allocation based on assigned to ID. - * - * Returns a listing of the allocation for the specified server id. - * - * @Get("/nodes/allocations/{id}") - * @Versions({"v1"}) - * @Response(200) - */ - public function allocationsView(Request $request, $id) - { - return Models\Allocation::where('server_id', $id)->get()->toArray(); - } - - /** - * Delete Node. - * - * @Delete("/nodes/{id}") - * @Versions({"v1"}) - * @Parameters({ - * @Parameter("id", type="integer", required=true, description="The ID of the node."), - * }) - * @Response(204) - */ - public function delete(Request $request, $id) - { - try { - $node = new NodeRepository; - $node->delete($id); - - return $this->response->noContent(); - } catch (DisplayException $ex) { - throw new ResourceException($ex->getMessage()); - } catch (\Exception $e) { - throw new ServiceUnavailableHttpException('An error occured while attempting to delete this node.'); - } - } -} diff --git a/app/Http/Controllers/API/ServerController.php b/app/Http/Controllers/API/ServerController.php deleted file mode 100755 index e6b2add2c..000000000 --- a/app/Http/Controllers/API/ServerController.php +++ /dev/null @@ -1,296 +0,0 @@ -. - * - * 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\Http\Controllers\API; - -use Log; -use Pterodactyl\Models; -use Illuminate\Http\Request; -use Dingo\Api\Exception\ResourceException; -use Pterodactyl\Exceptions\DisplayException; -use Pterodactyl\Repositories\ServerRepository; -use Pterodactyl\Exceptions\DisplayValidationException; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; -use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException; - -/** - * @Resource("Servers") - */ -class ServerController extends BaseController -{ - public function __construct() - { - // - } - - /** - * List All Servers. - * - * Lists all servers currently on the system. - * - * @Get("/servers/{?page}") - * @Versions({"v1"}) - * @Parameters({ - * @Parameter("page", type="integer", description="The page of results to view.", default=1) - * }) - * @Response(200) - */ - public function lists(Request $request) - { - return Models\Server::all()->toArray(); - } - - /** - * Create Server. - * - * @Post("/servers") - * @Versions({"v1"}) - * @Response(201) - */ - public function create(Request $request) - { - try { - $repo = new ServerRepository; - $server = $repo->create($request->all()); - - return ['id' => $server->id]; - } catch (DisplayValidationException $ex) { - throw new ResourceException('A validation error occured.', json_decode($ex->getMessage(), true)); - } catch (DisplayException $ex) { - throw new ResourceException($ex->getMessage()); - } catch (\Exception $ex) { - Log::error($ex); - throw new BadRequestHttpException('There was an error while attempting to add this server to the system.'); - } - } - - /** - * List Specific Server. - * - * Lists specific fields about a server or all fields pertaining to that server. - * - * @Get("/servers/{id}{?fields}") - * @Versions({"v1"}) - * @Parameters({ - * @Parameter("id", type="integer", required=true, description="The ID of the server to get information on."), - * @Parameter("fields", type="string", required=false, description="A comma delimidated list of fields to include.") - * }) - * @Response(200) - */ - public function view(Request $request, $id) - { - $server = Models\Server::with('node', 'allocations', 'pack')->where('id', $id)->first(); - if (! $server) { - throw new NotFoundHttpException('No server by that ID was found.'); - } - - if (! is_null($request->input('fields'))) { - $fields = explode(',', $request->input('fields')); - if (! empty($fields) && is_array($fields)) { - return collect($server)->only($fields); - } - } - - if ($request->input('daemon') === 'true') { - try { - $response = $server->node->guzzleClient([ - 'X-Access-Token' => $server->node->daemonSecret, - ])->request('GET', '/servers'); - - $server->daemon = json_decode($response->getBody())->{$server->uuid}; - } catch (\GuzzleHttp\Exception\TransferException $ex) { - // Couldn't hit the daemon, return what we have though. - $server->daemon = [ - 'error' => 'There was an error encountered while attempting to connect to the remote daemon.', - ]; - } - } - - $server->allocations->transform(function ($item) { - return collect($item)->except(['created_at', 'updated_at']); - }); - - return $server->toArray(); - } - - /** - * Update Server configuration. - * - * Updates display information on panel. - * - * @Patch("/servers/{id}/config") - * @Versions({"v1"}) - * @Transaction({ - * @Request({ - * "owner": "new@email.com", - * "name": "New Name", - * "reset_token": true - * }, headers={"Authorization": "Bearer "}), - * @Response(200, body={"name": "New Name"}), - * @Response(422) - * }) - * @Parameters({ - * @Parameter("id", type="integer", required=true, description="The ID of the server to modify.") - * }) - */ - public function config(Request $request, $id) - { - try { - $server = new ServerRepository; - $server->updateDetails($id, $request->only([ - 'owner', 'name', 'reset_token', - ])); - - return Models\Server::findOrFail($id); - } catch (DisplayValidationException $ex) { - throw new ResourceException('A validation error occured.', json_decode($ex->getMessage(), true)); - } catch (DisplayException $ex) { - throw new ResourceException($ex->getMessage()); - } catch (\Exception $ex) { - throw new ServiceUnavailableHttpException('Unable to update server on system due to an error.'); - } - } - - /** - * Update Server Build Configuration. - * - * Updates server build information on panel and on node. - * - * @Patch("/servers/{id}/build") - * @Versions({"v1"}) - * @Transaction({ - * @Request({ - * "default": "192.168.0.1:25565", - * "add_additional": [ - * "192.168.0.1:25566", - * "192.168.0.1:25567", - * "192.168.0.1:25568" - * ], - * "remove_additional": [], - * "memory": 1024, - * "swap": 0, - * "io": 500, - * "cpu": 0, - * "disk": 1024 - * }, headers={"Authorization": "Bearer "}), - * @Response(200, body={"name": "New Name"}), - * @Response(422) - * }) - * @Parameters({ - * @Parameter("id", type="integer", required=true, description="The ID of the server to modify.") - * }) - */ - public function build(Request $request, $id) - { - try { - $server = new ServerRepository; - $server->changeBuild($id, $request->only([ - 'default', 'add_additional', 'remove_additional', - 'memory', 'swap', 'io', 'cpu', 'disk', - ])); - - return Models\Server::findOrFail($id); - } catch (DisplayValidationException $ex) { - throw new ResourceException('A validation error occured.', json_decode($ex->getMessage(), true)); - } catch (DisplayException $ex) { - throw new ResourceException($ex->getMessage()); - } catch (\Exception $ex) { - throw new ServiceUnavailableHttpException('Unable to update server on system due to an error.'); - } - } - - /** - * Suspend Server. - * - * @Post("/servers/{id}/suspend") - * @Versions({"v1"}) - * @Parameters({ - * @Parameter("id", type="integer", required=true, description="The ID of the server."), - * }) - * @Response(204) - */ - public function suspend(Request $request, $id) - { - try { - $server = new ServerRepository; - $server->suspend($id); - - return $this->response->noContent(); - } catch (DisplayException $ex) { - throw new ResourceException($ex->getMessage()); - } catch (\Exception $ex) { - throw new ServiceUnavailableHttpException('An error occured while attempting to suspend this server instance.'); - } - } - - /** - * Unsuspend Server. - * - * @Post("/servers/{id}/unsuspend") - * @Versions({"v1"}) - * @Parameters({ - * @Parameter("id", type="integer", required=true, description="The ID of the server."), - * }) - * @Response(204) - */ - public function unsuspend(Request $request, $id) - { - try { - $server = new ServerRepository; - $server->unsuspend($id); - - return $this->response->noContent(); - } catch (DisplayException $ex) { - throw new ResourceException($ex->getMessage()); - } catch (\Exception $ex) { - throw new ServiceUnavailableHttpException('An error occured while attempting to unsuspend this server instance.'); - } - } - - /** - * Delete Server. - * - * @Delete("/servers/{id}/{force}") - * @Versions({"v1"}) - * @Parameters({ - * @Parameter("id", type="integer", required=true, description="The ID of the server."), - * @Parameter("force", type="string", required=false, description="Use 'force' if the server should be removed regardless of daemon response."), - * }) - * @Response(204) - */ - public function delete(Request $request, $id, $force = null) - { - try { - $server = new ServerRepository; - $server->deleteServer($id, $force); - - return $this->response->noContent(); - } catch (DisplayException $ex) { - throw new ResourceException($ex->getMessage()); - } catch (\Exception $e) { - throw new ServiceUnavailableHttpException('An error occured while attempting to delete this server.'); - } - } -} diff --git a/app/Http/Controllers/API/User/InfoController.php b/app/Http/Controllers/API/User/CoreController.php similarity index 64% rename from app/Http/Controllers/API/User/InfoController.php rename to app/Http/Controllers/API/User/CoreController.php index a22d1e114..5c1d07406 100644 --- a/app/Http/Controllers/API/User/InfoController.php +++ b/app/Http/Controllers/API/User/CoreController.php @@ -24,24 +24,28 @@ namespace Pterodactyl\Http\Controllers\API\User; +use Fractal; use Illuminate\Http\Request; -use Pterodactyl\Http\Controllers\API\BaseController; +use Pterodactyl\Http\Controllers\Controller; +use Pterodactyl\Transformers\User\OverviewTransformer; -class InfoController extends BaseController +class CoreController extends Controller { - public function me(Request $request) + /** + * Controller to handle base user request for all of their servers. + * + * @param \Illuminate\Http\Request $request + * @return array + */ + public function index(Request $request) { - return $request->user()->serverAccessCollection()->load('allocation', 'option')->map(function ($server) { - return [ - 'id' => $server->uuidShort, - 'uuid' => $server->uuid, - 'name' => $server->name, - 'node' => $server->node->name, - 'ip' => $server->allocation->alias, - 'port' => $server->allocation->port, - 'service' => $server->service->name, - 'option' => $server->option->name, - ]; - })->all(); + $this->authorize('user.server-list', $request->apiKey()); + + $servers = $request->user()->access('service', 'node', 'allocation', 'option')->get(); + + return Fractal::collection($servers) + ->transformWith(new OverviewTransformer) + ->withResourceName('server') + ->toArray(); } } diff --git a/app/Http/Controllers/API/User/ServerController.php b/app/Http/Controllers/API/User/ServerController.php index 465c7a345..904935186 100644 --- a/app/Http/Controllers/API/User/ServerController.php +++ b/app/Http/Controllers/API/User/ServerController.php @@ -24,78 +24,76 @@ namespace Pterodactyl\Http\Controllers\API\User; -use Log; -use Pterodactyl\Models; +use Fractal; use Illuminate\Http\Request; -use Pterodactyl\Http\Controllers\API\BaseController; +use Pterodactyl\Models\Server; +use Pterodactyl\Http\Controllers\Controller; +use Pterodactyl\Repositories\Daemon\PowerRepository; +use Pterodactyl\Transformers\User\ServerTransformer; +use Pterodactyl\Repositories\Daemon\CommandRepository; -class ServerController extends BaseController +class ServerController extends Controller { - public function info(Request $request, $uuid) + /** + * Controller to handle base request for individual server information. + * + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @return array + */ + public function index(Request $request, $uuid) { - $server = Models\Server::byUuid($uuid)->load('allocations'); + $this->authorize('user.server-view', $request->apiKey()); - try { - $response = $server->guzzleClient()->request('GET', '/server'); + $server = Server::byUuid($uuid); + $fractal = Fractal::create()->item($server); - $json = json_decode($response->getBody()); - $daemon = [ - 'status' => $json->status, - 'stats' => $json->proc, - ]; - } catch (\Exception $ex) { - $daemon = [ - 'error' => 'An error was encountered while trying to connect to the daemon to collect information. It might be offline.', - ]; - Log::error($ex); + if ($request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); } - return [ - 'uuidShort' => $server->uuidShort, - 'uuid' => $server->uuid, - 'name' => $server->name, - 'node' => $server->node->name, - 'limits' => [ - 'memory' => $server->memory, - 'swap' => $server->swap, - 'disk' => $server->disk, - 'io' => $server->io, - 'cpu' => $server->cpu, - 'oom_disabled' => (bool) $server->oom_disabled, - ], - 'allocations' => $server->allocations->map(function ($item) use ($server) { - return [ - 'ip' => $item->alias, - 'port' => $item->port, - 'default' => ($item->id === $server->allocation_id), - ]; - }), - 'sftp' => [ - 'username' => ($request->user()->can('view-sftp', $server)) ? $server->username : null, - ], - 'daemon' => [ - 'token' => $server->daemonSecret, - 'response' => $daemon, - ], - ]; + return $fractal->transformWith(new ServerTransformer) + ->withResourceName('server') + ->toArray(); } + /** + * Controller to handle request for server power toggle. + * + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @return \Illuminate\Http\Response + */ public function power(Request $request, $uuid) { - $server = Models\Server::byUuid($uuid); - Auth::user()->can('power-' . $request->input('action'), $server); + $this->authorize('user.server-power', $request->apiKey()); - $res = $server->guzzleClient()->request('PUT', '/server/power', [ - 'exceptions' => false, - 'json' => [ - 'action' => $request->input('action'), - ], - ]); + $server = Server::byUuid($uuid); + $request->user()->can('power-' . $request->input('action'), $server); - if ($res->getStatusCode() !== 204) { - return $this->response->error(json_decode($res->getBody())->error, $res->getStatusCode()); - } + $repo = new PowerRepository($server, $request->user()); + $repo->do($request->input('action')); - return $this->response->noContent(); + return response('', 204)->header('Content-Type', 'application/json'); + } + + /** + * Controller to handle base request for individual server information. + * + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @return \Illuminate\Http\Response + */ + public function command(Request $request, $uuid) + { + $this->authorize('user.server-command', $request->apiKey()); + + $server = Server::byUuid($uuid); + $request->user()->can('send-command', $server); + + $repo = new CommandRepository($server, $request->user()); + $repo->send($request->input('command')); + + return response('', 204)->header('Content-Type', 'application/json'); } } diff --git a/app/Http/Controllers/API/UserController.php b/app/Http/Controllers/API/UserController.php deleted file mode 100755 index 4121f64ef..000000000 --- a/app/Http/Controllers/API/UserController.php +++ /dev/null @@ -1,202 +0,0 @@ -. - * - * 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\Http\Controllers\API; - -use Pterodactyl\Models; -use Illuminate\Http\Request; -use Dingo\Api\Exception\ResourceException; -use Pterodactyl\Exceptions\DisplayException; -use Pterodactyl\Repositories\UserRepository; -use Pterodactyl\Exceptions\DisplayValidationException; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException; - -/** - * @Resource("Users") - */ -class UserController extends BaseController -{ - public function __construct() - { - } - - /** - * List All Users. - * - * Lists all users currently on the system. - * - * @Get("/users/{?page}") - * @Versions({"v1"}) - * @Parameters({ - * @Parameter("page", type="integer", description="The page of results to view.", default=1) - * }) - * @Response(200) - */ - public function lists(Request $request) - { - return Models\User::all()->toArray(); - } - - /** - * List Specific User. - * - * Lists specific fields about a user or all fields pertaining to that user. - * - * @Get("/users/{id}/{fields}") - * @Versions({"v1"}) - * @Parameters({ - * @Parameter("id", type="integer", required=true, description="The ID of the user to get information on."), - * @Parameter("fields", type="string", required=false, description="A comma delimidated list of fields to include.") - * }) - * @Response(200) - */ - public function view(Request $request, $id) - { - $user = Models\User::with('servers')->where((is_numeric($id) ? 'id' : 'email'), $id)->first(); - if (! $user->first()) { - throw new NotFoundHttpException('No user by that ID was found.'); - } - - $user->servers->transform(function ($item) { - return collect($item)->only([ - 'id', 'node_id', 'uuidShort', - 'uuid', 'name', 'suspended', - 'owner_id', - ]); - }); - - if (! is_null($request->input('fields'))) { - $fields = explode(',', $request->input('fields')); - if (! empty($fields) && is_array($fields)) { - return collect($user)->only($fields); - } - } - - return $user->toArray(); - } - - /** - * Create a New User. - * - * @Post("/users") - * @Versions({"v1"}) - * @Transaction({ - * @Request({ - * "email": "foo@example.com", - * "password": "foopassword", - * "admin": false, - * "custom_id": 123 - * }, headers={"Authorization": "Bearer "}), - * @Response(201), - * @Response(422) - * }) - */ - public function create(Request $request) - { - try { - $user = new UserRepository; - $create = $user->create($request->only([ - 'email', 'username', 'name_first', - 'name_last', 'password', - 'root_admin', 'custom_id', - ])); - $create = $user->create($request->input('email'), $request->input('password'), $request->input('admin'), $request->input('custom_id')); - - return ['id' => $create]; - } catch (DisplayValidationException $ex) { - throw new ResourceException('A validation error occured.', json_decode($ex->getMessage(), true)); - } catch (DisplayException $ex) { - throw new ResourceException($ex->getMessage()); - } catch (\Exception $ex) { - throw new ServiceUnavailableHttpException('Unable to create a user on the system due to an error.'); - } - } - - /** - * Update an Existing User. - * - * The data sent in the request will be used to update the existing user on the system. - * - * @Patch("/users/{id}") - * @Versions({"v1"}) - * @Transaction({ - * @Request({ - * "email": "new@email.com" - * }, headers={"Authorization": "Bearer "}), - * @Response(200, body={"email": "new@email.com"}), - * @Response(422) - * }) - * @Parameters({ - * @Parameter("id", type="integer", required=true, description="The ID of the user to modify.") - * }) - */ - public function update(Request $request, $id) - { - try { - $user = new UserRepository; - $user->update($id, $request->only([ - 'username', 'email', 'name_first', - 'name_last', 'password', - 'root_admin', 'language', - ])); - - return Models\User::findOrFail($id); - } catch (DisplayValidationException $ex) { - throw new ResourceException('A validation error occured.', json_decode($ex->getMessage(), true)); - } catch (DisplayException $ex) { - throw new ResourceException($ex->getMessage()); - } catch (\Exception $ex) { - throw new ServiceUnavailableHttpException('Unable to update a user on the system due to an error.'); - } - } - - /** - * Delete a User. - * - * @Delete("/users/{id}") - * @Versions({"v1"}) - * @Transaction({ - * @Request(headers={"Authorization": "Bearer "}), - * @Response(204), - * @Response(422) - * }) - * @Parameters({ - * @Parameter("id", type="integer", required=true, description="The ID of the user to delete.") - * }) - */ - public function delete(Request $request, $id) - { - try { - $user = new UserRepository; - $user->delete($id); - - return $this->response->noContent(); - } catch (DisplayException $ex) { - throw new ResourceException($ex->getMessage()); - } catch (\Exception $ex) { - throw new ServiceUnavailableHttpException('Unable to delete this user due to an error.'); - } - } -} diff --git a/app/Http/Controllers/Admin/BaseController.php b/app/Http/Controllers/Admin/BaseController.php index 0f54a8ee6..2d2fe469c 100644 --- a/app/Http/Controllers/Admin/BaseController.php +++ b/app/Http/Controllers/Admin/BaseController.php @@ -33,30 +33,38 @@ use Pterodactyl\Http\Controllers\Controller; class BaseController extends Controller { /** - * Controller Constructor. + * Return the admin index view. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\View\View */ - public function __construct() - { - // - } - public function getIndex(Request $request) { return view('admin.index'); } + /** + * Return the admin settings view. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\View\View + */ public function getSettings(Request $request) { return view('admin.settings'); } + /** + * Handle settings post request. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\RedirectResponse + */ public function postSettings(Request $request) { $validator = Validator::make($request->all(), [ 'company' => 'required|between:1,256', 'default_language' => 'required|alpha_dash|min:2|max:5', - 'email_from' => 'required|email', - 'email_sender_name' => 'required|between:1,256', ]); if ($validator->fails()) { @@ -65,8 +73,6 @@ class BaseController extends Controller Settings::set('company', $request->input('company')); Settings::set('default_language', $request->input('default_language')); - Settings::set('email_from', $request->input('email_from')); - Settings::set('email_sender_name', $request->input('email_sender_name')); Alert::success('Settings have been successfully updated.')->flash(); diff --git a/app/Http/Controllers/Admin/DatabaseController.php b/app/Http/Controllers/Admin/DatabaseController.php index e41575458..6b4e12a1d 100644 --- a/app/Http/Controllers/Admin/DatabaseController.php +++ b/app/Http/Controllers/Admin/DatabaseController.php @@ -40,7 +40,7 @@ class DatabaseController extends Controller /** * Display database host index. * - * @param Request $request + * @param \Illuminate\Http\Request $request * @return \Illuminate\View\View */ public function index(Request $request) @@ -54,8 +54,8 @@ class DatabaseController extends Controller /** * Display database host to user. * - * @param Request $request - * @param int $id + * @param \Illuminate\Http\Request $request + * @param int $id * @return \Illuminate\View\View */ public function view(Request $request, $id) @@ -69,8 +69,8 @@ class DatabaseController extends Controller /** * Handle post request to create database host. * - * @param Request $request - * @return \Illuminate\Response\RedirectResponse + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\RedirectResponse */ public function create(Request $request) { @@ -99,9 +99,9 @@ class DatabaseController extends Controller /** * Handle post request to update a database host. * - * @param Request $request - * @param int $id - * @return \Illuminate\Response\RedirectResponse + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\RedirectResponse */ public function update(Request $request, $id) { diff --git a/app/Http/Controllers/Admin/LocationController.php b/app/Http/Controllers/Admin/LocationController.php index 6ceae16e7..4e602f7ca 100644 --- a/app/Http/Controllers/Admin/LocationController.php +++ b/app/Http/Controllers/Admin/LocationController.php @@ -38,7 +38,7 @@ class LocationController extends Controller /** * Return the location overview page. * - * @param Request $request + * @param \Illuminate\Http\Request $request * @return \Illuminate\View\View */ public function index(Request $request) @@ -51,8 +51,8 @@ class LocationController extends Controller /** * Return the location view page. * - * @param Request $request - * @param int $id + * @param \Illuminate\Http\Request $request + * @param int $id * @return \Illuminate\View\View */ public function view(Request $request, $id) @@ -63,8 +63,8 @@ class LocationController extends Controller /** * Handle request to create new location. * - * @param Request $request - * @return \Illuminate\Response\RedirectResponse + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\RedirectResponse */ public function create(Request $request) { @@ -88,9 +88,9 @@ class LocationController extends Controller /** * Handle request to update or delete location. * - * @param Request $request - * @param int $id - * @return \Illuminate\Response\RedirectResponse + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\RedirectResponse */ public function update(Request $request, $id) { diff --git a/app/Http/Controllers/Admin/NodesController.php b/app/Http/Controllers/Admin/NodesController.php index cbf3ff6ce..1300b9643 100644 --- a/app/Http/Controllers/Admin/NodesController.php +++ b/app/Http/Controllers/Admin/NodesController.php @@ -40,7 +40,7 @@ class NodesController extends Controller /** * Displays the index page listing all nodes on the panel. * - * @param Request $request + * @param \Illuminate\Http\Request $request * @return \Illuminate\View\View */ public function index(Request $request) @@ -57,10 +57,10 @@ class NodesController extends Controller /** * Displays create new node page. * - * @param Request $request - * @return \Illuminate\View\View|\Illuminate\Response\RedirectResponse + * @param \Illuminate\Http\Request $request + * @return \Illuminate\View\View|\Illuminate\Http\RedirectResponse */ - public function new(Request $request) + public function create(Request $request) { $locations = Models\Location::all(); if (! $locations->count()) { @@ -75,10 +75,10 @@ class NodesController extends Controller /** * Post controller to create a new node on the system. * - * @param Request $request - * @return \Illuminate\Response\RedirectResponse + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\RedirectResponse */ - public function create(Request $request) + public function store(Request $request) { try { $repo = new NodeRepository; @@ -110,9 +110,8 @@ class NodesController extends Controller /** * Shows the index overview page for a specific node. * - * @param Request $request - * @param int $id The ID of the node to display information for. - * + * @param \Illuminate\Http\Request $request + * @param int $id * @return \Illuminate\View\View */ public function viewIndex(Request $request, $id) @@ -138,9 +137,8 @@ class NodesController extends Controller /** * Shows the settings page for a specific node. * - * @param Request $request - * @param int $id The ID of the node to display information for. - * + * @param \Illuminate\Http\Request $request + * @param int $id * @return \Illuminate\View\View */ public function viewSettings(Request $request, $id) @@ -154,9 +152,8 @@ class NodesController extends Controller /** * Shows the configuration page for a specific node. * - * @param Request $request - * @param int $id The ID of the node to display information for. - * + * @param \Illuminate\Http\Request $request + * @param int $id * @return \Illuminate\View\View */ public function viewConfiguration(Request $request, $id) @@ -169,9 +166,8 @@ class NodesController extends Controller /** * Shows the allocation page for a specific node. * - * @param Request $request - * @param int $id The ID of the node to display information for. - * + * @param \Illuminate\Http\Request $request + * @param int $id * @return \Illuminate\View\View */ public function viewAllocation(Request $request, $id) @@ -189,9 +185,8 @@ class NodesController extends Controller /** * Shows the server listing page for a specific node. * - * @param Request $request - * @param int $id The ID of the node to display information for. - * + * @param \Illuminate\Http\Request $request + * @param int $id * @return \Illuminate\View\View */ public function viewServers(Request $request, $id) @@ -209,8 +204,8 @@ class NodesController extends Controller /** * Updates settings for a node. * - * @param Request $request - * @param int $node + * @param \Illuminate\Http\Request $request + * @param int $id * @return \Illuminate\Http\RedirectResponse */ public function updateSettings(Request $request, $id) @@ -244,9 +239,9 @@ class NodesController extends Controller /** * Removes a single allocation from a node. * - * @param Request $request - * @param int $node - * @param int $allocation [description] + * @param \Illuminate\Http\Request $request + * @param int $node + * @param int $allocation * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse */ public function allocationRemoveSingle(Request $request, $node, $allocation) @@ -264,8 +259,8 @@ class NodesController extends Controller /** * Remove all allocations for a specific IP at once on a node. * - * @param Request $request - * @param int $node + * @param \Illuminate\Http\Request $request + * @param int $node * @return \Illuminate\Http\RedirectResponse */ public function allocationRemoveBlock(Request $request, $node) @@ -283,10 +278,9 @@ class NodesController extends Controller /** * Sets an alias for a specific allocation on a node. * - * @param Request $request - * @param int $node + * @param \Illuminate\Http\Request $request + * @param int $node * @return \Illuminate\Http\Response - * @throws \Exception */ public function allocationSetAlias(Request $request, $node) { @@ -308,8 +302,8 @@ class NodesController extends Controller /** * Creates new allocations on a node. * - * @param Request $request - * @param int $node + * @param \Illuminate\Http\Request $request + * @param int $node * @return \Illuminate\Http\RedirectResponse */ public function createAllocation(Request $request, $node) @@ -334,8 +328,8 @@ class NodesController extends Controller /** * Deletes a node from the system. * - * @param Request $request - * @param int $id + * @param \Illuminate\Http\Request $request + * @param int $id * @return \Illuminate\Http\RedirectResponse */ public function delete(Request $request, $id) @@ -360,8 +354,8 @@ class NodesController extends Controller /** * Returns the configuration token to auto-deploy a node. * - * @param Request $request - * @param int $id + * @param \Illuminate\Http\Request $request + * @param int $id * @return \Illuminate\Http\JsonResponse */ public function setToken(Request $request, $id) diff --git a/app/Http/Controllers/Admin/OptionController.php b/app/Http/Controllers/Admin/OptionController.php index 9300fcedb..9925e227a 100644 --- a/app/Http/Controllers/Admin/OptionController.php +++ b/app/Http/Controllers/Admin/OptionController.php @@ -41,10 +41,10 @@ class OptionController extends Controller /** * Handles request to view page for adding new option. * - * @param Request $request + * @param \Illuminate\Http\Request $request * @return \Illuminate\View\View */ - public function new(Request $request) + public function create(Request $request) { $services = Service::with('options')->get(); Javascript::put(['services' => $services->keyBy('id')]); @@ -55,10 +55,10 @@ class OptionController extends Controller /** * Handles POST request to create a new option. * - * @param Request $request + * @param \Illuminate\Http\Request $request * @return \Illuminate\Response\RedirectResponse */ - public function create(Request $request) + public function store(Request $request) { $repo = new OptionRepository; @@ -86,9 +86,9 @@ class OptionController extends Controller /** * Handles POST request to create a new option variable. * - * @param Request $request - * @param int $id The ID of the service option to assign this variable to. - * @return \Illuminate\Response\RedirectResponse + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\RedirectResponse */ public function createVariable(Request $request, $id) { @@ -116,8 +116,8 @@ class OptionController extends Controller /** * Display option overview page. * - * @param Request $request - * @param int $id + * @param \Illuminate\Http\Request $request + * @param int $id * @return \Illuminate\View\View */ public function viewConfiguration(Request $request, $id) @@ -128,8 +128,8 @@ class OptionController extends Controller /** * Display variable overview page for a service option. * - * @param Request $request - * @param int $id + * @param \Illuminate\Http\Request $request + * @param int $id * @return \Illuminate\View\View */ public function viewVariables(Request $request, $id) @@ -152,8 +152,9 @@ class OptionController extends Controller /** * Handles POST when editing a configration for a service option. * - * @param Request $request - * @return \Illuminate\Response\RedirectResponse + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\RedirectResponse */ public function editConfiguration(Request $request, $id) { @@ -188,10 +189,10 @@ class OptionController extends Controller /** * Handles POST when editing a configration for a service option. * - * @param Request $request - * @param int $option - * @param int $variable - * @return \Illuminate\Response\RedirectResponse + * @param \Illuminate\Http\Request $request + * @param int $option + * @param int $variable + * @return \Illuminate\Http\RedirectResponse */ public function editVariable(Request $request, $option, $variable) { diff --git a/app/Http/Controllers/Admin/PackController.php b/app/Http/Controllers/Admin/PackController.php index 46674ae02..d02273672 100644 --- a/app/Http/Controllers/Admin/PackController.php +++ b/app/Http/Controllers/Admin/PackController.php @@ -40,7 +40,7 @@ class PackController extends Controller /** * Display listing of all packs on the system. * - * @param Request $request + * @param \Illuminate\Http\Request $request * @return \Illuminate\View\View */ public function index(Request $request) @@ -57,10 +57,10 @@ class PackController extends Controller /** * Display new pack creation form. * - * @param Request $request + * @param \Illuminate\Http\Request $request * @return \Illuminate\View\View */ - public function new(Request $request) + public function create(Request $request) { return view('admin.packs.new', [ 'services' => Service::with('options')->get(), @@ -70,7 +70,7 @@ class PackController extends Controller /** * Display new pack creation modal for use with template upload. * - * @param Request $request + * @param \Illuminate\Http\Request $request * @return \Illuminate\View\View */ public function newTemplate(Request $request) @@ -83,10 +83,10 @@ class PackController extends Controller /** * Handle create pack request and route user to location. * - * @param Request $request + * @param \Illuminate\Http\Request $request * @return \Illuminate\View\View */ - public function create(Request $request) + public function store(Request $request) { $repo = new PackRepository; @@ -117,8 +117,8 @@ class PackController extends Controller /** * Display pack view template to user. * - * @param Request $request - * @param int $id + * @param \Illuminate\Http\Request $request + * @param int $id * @return \Illuminate\View\View */ public function view(Request $request, $id) @@ -132,9 +132,9 @@ class PackController extends Controller /** * Handle updating or deleting pack information. * - * @param Request $request - * @param int $id - * @return \Illuminate\Response\RedirectResponse + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\RedirectResponse */ public function update(Request $request, $id) { @@ -168,10 +168,10 @@ class PackController extends Controller /** * Creates an archive of the pack and downloads it to the browser. * - * @param Request $request - * @param int $id - * @param bool $files - * @return \Illuminate\Response\BinaryFileResponse + * @param \Illuminate\Http\Request $request + * @param int $id + * @param bool $files + * @return \Symfony\Component\HttpFoundation\BinaryFileResponse */ public function export(Request $request, $id, $files = false) { diff --git a/app/Http/Controllers/Admin/ServersController.php b/app/Http/Controllers/Admin/ServersController.php index 3de8d0c9f..1a8a8cfbc 100644 --- a/app/Http/Controllers/Admin/ServersController.php +++ b/app/Http/Controllers/Admin/ServersController.php @@ -41,14 +41,12 @@ class ServersController extends Controller /** * Display the index page with all servers currently on the system. * - * @param Request $request + * @param \Illuminate\Http\Request $request * @return \Illuminate\View\View */ public function index(Request $request) { - $servers = Models\Server::withTrashed()->with( - 'node', 'user', 'allocation' - ); + $servers = Models\Server::with('node', 'user', 'allocation'); if (! is_null($request->input('query'))) { $servers->search($request->input('query')); @@ -62,10 +60,10 @@ class ServersController extends Controller /** * Display create new server page. * - * @param Request $request + * @param \Illuminate\Http\Request $request * @return \Illuminate\View\View */ - public function new(Request $request) + public function create(Request $request) { $services = Models\Service::with('options.packs', 'options.variables')->get(); Javascript::put([ @@ -85,10 +83,10 @@ class ServersController extends Controller /** * Create server controller method. * - * @param Request $request + * @param \Illuminate\Http\Request $request * @return \Illuminate\Response\RedirectResponse */ - public function create(Request $request) + public function store(Request $request) { try { $repo = new ServerRepository; @@ -99,6 +97,9 @@ class ServersController extends Controller return redirect()->route('admin.servers.new')->withErrors(json_decode($ex->getMessage()))->withInput(); } catch (DisplayException $ex) { Alert::danger($ex->getMessage())->flash(); + } catch (TransferException $ex) { + Log::warning($ex); + 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(); } catch (\Exception $ex) { Log::error($ex); Alert::danger('An unhandled exception occured while attemping to add this server. Please try again.')->flash(); @@ -110,10 +111,10 @@ class ServersController extends Controller /** * Returns a tree of all avaliable nodes in a given location. * - * @param Request $request + * @param \Illuminate\Http\Request $request * @return array */ - public function newServerNodes(Request $request) + public function nodes(Request $request) { $nodes = Models\Node::with('allocations')->where('location_id', $request->input('location'))->get(); @@ -140,20 +141,20 @@ class ServersController extends Controller /** * Display the index when viewing a specific server. * - * @param Request $request - * @param int $id + * @param \Illuminate\Http\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)]); + return view('admin.servers.view.index', ['server' => Models\Server::findOrFail($id)]); } /** * Display the details page when viewing a specific server. * - * @param Request $request - * @param int $id + * @param \Illuminate\Http\Request $request + * @param int $id * @return \Illuminate\View\View */ public function viewDetails(Request $request, $id) @@ -166,8 +167,8 @@ class ServersController extends Controller /** * Display the build details page when viewing a specific server. * - * @param Request $request - * @param int $id + * @param \Illuminate\Http\Request $request + * @param int $id * @return \Illuminate\View\View */ public function viewBuild(Request $request, $id) @@ -184,8 +185,8 @@ class ServersController extends Controller /** * Display startup configuration page for a server. * - * @param Request $request - * @param int $id + * @param \Illuminate\Http\Request $request + * @param int $id * @return \Illuminate\View\View */ public function viewStartup(Request $request, $id) @@ -203,8 +204,8 @@ class ServersController extends Controller /** * Display the database management page for a specific server. * - * @param Request $request - * @param int $id + * @param \Illuminate\Http\Request $request + * @param int $id * @return \Illuminate\View\View */ public function viewDatabase(Request $request, $id) @@ -220,8 +221,8 @@ class ServersController extends Controller /** * Display the management page when viewing a specific server. * - * @param Request $request - * @param int $id + * @param \Illuminate\Http\Request $request + * @param int $id * @return \Illuminate\View\View */ public function viewManage(Request $request, $id) @@ -232,28 +233,28 @@ class ServersController extends Controller /** * Display the deletion page for a server. * - * @param Request $request - * @param int $id + * @param \Illuminate\Http\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)]); + return view('admin.servers.view.delete', ['server' => Models\Server::findOrFail($id)]); } /** * Update the details for a server. * - * @param Request $request - * @param int $id - * @return \Illuminate\Response\RedirectResponse + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\RedirectResponse */ public function setDetails(Request $request, $id) { $repo = new ServerRepository; try { $repo->updateDetails($id, $request->intersect([ - 'owner_id', 'name', 'reset_token', + 'owner_id', 'name', 'description', 'reset_token', ])); Alert::success('Server details were successfully updated.')->flash(); @@ -272,9 +273,9 @@ class ServersController extends Controller /** * Set the new docker container for a server. * - * @param Request $request - * @param int $id - * @return \Illuminate\Response\RedirectResponse + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\RedirectResponse */ public function setContainer(Request $request, $id) { @@ -286,8 +287,9 @@ class ServersController extends Controller Alert::success('Successfully updated this server\'s docker image.')->flash(); } catch (DisplayValidationException $ex) { return redirect()->route('admin.servers.view.details', $id)->withErrors(json_decode($ex->getMessage()))->withInput(); - } catch (DisplayException $ex) { - Alert::danger($ex->getMessage())->flash(); + } catch (TransferException $ex) { + Log::warning($ex); + Alert::danger('A TransferException occured while attempting to update the container image. Is the daemon online? This error has been logged.'); } catch (\Exception $ex) { Log::error($ex); Alert::danger('An unhandled exception occured while attemping to update this server\'s docker image. This error has been logged.')->flash(); @@ -299,9 +301,9 @@ class ServersController extends Controller /** * Toggles the install status for a server. * - * @param Request $request - * @param int $id - * @return \Illuminate\Response\RedirectResponse + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\RedirectResponse */ public function toggleInstall(Request $request, $id) { @@ -323,9 +325,9 @@ class ServersController extends Controller /** * Setup a server to have a container rebuild. * - * @param Request $request - * @param int $id - * @return \Illuminate\Response\RedirectResponse + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\RedirectResponse */ public function rebuildContainer(Request $request, $id) { @@ -349,9 +351,9 @@ class ServersController extends Controller /** * Manage the suspension status for a server. * - * @param Request $request - * @param int $id - * @return \Illuminate\Response\RedirectResponse + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\RedirectResponse */ public function manageSuspension(Request $request, $id) { @@ -365,11 +367,12 @@ class ServersController extends Controller } try { - $repo->$action($id); + $repo->toggleAccess($id, ($action === 'unsuspend')); Alert::success('Server has been ' . $action . 'ed.'); - } catch (DisplayException $ex) { - Alert::danger($ex->getMessage())->flash(); + } catch (TransferException $ex) { + Log::warning($ex); + 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(); } catch (\Exception $ex) { Log::error($ex); Alert::danger('An unhandled exception occured while attemping to ' . $action . ' this server. This error has been logged.')->flash(); @@ -381,9 +384,9 @@ class ServersController extends Controller /** * Update the build configuration for a server. * - * @param Request $request - * @param int $id - * @return \Illuminate\Response\RedirectResponse + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\RedirectResponse */ public function updateBuild(Request $request, $id) { @@ -400,6 +403,9 @@ class ServersController extends Controller return redirect()->route('admin.servers.view.build', $id)->withErrors(json_decode($ex->getMessage()))->withInput(); } catch (DisplayException $ex) { Alert::danger($ex->getMessage())->flash(); + } catch (TransferException $ex) { + Log::warning($ex); + 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(); } catch (\Exception $ex) { Log::error($ex); Alert::danger('An unhandled exception occured while attemping to add this server. This error has been logged.')->flash(); @@ -411,58 +417,16 @@ class ServersController extends Controller /** * Start the server deletion process. * - * @param Request $request - * @param int $id - * @return \Illuminate\Response\RedirectResponse + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\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 - * @param string $method - * @return \Illuminate\Response\RedirectResponse - */ - public function continueDeletion(Request $request, $id, $method = 'safe') - { - $repo = new ServerRepository; - - try { - $repo->delete($id, (isset($method) && $method === 'force')); + $repo->delete($id, $request->has('force_delete')); Alert::success('Server was successfully deleted from the system.')->flash(); return redirect()->route('admin.servers'); @@ -482,9 +446,9 @@ class ServersController extends Controller /** * Update the startup command as well as variables. * - * @param Request $request - * @param int $id - * @return \Illuminate\Response\RedirectResponse + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\RedirectResponse */ public function saveStartup(Request $request, $id) { @@ -511,9 +475,10 @@ class ServersController extends Controller /** * Creates a new database assigned to a specific server. - * @param Request $request - * @param int $id - * @return \Illuminate\Response\RedirectResponse + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\RedirectResponse */ public function newDatabase(Request $request, $id) { @@ -537,9 +502,10 @@ class ServersController extends Controller /** * Resets the database password for a specific database on this server. - * @param Request $request - * @param int $id - * @return \Illuminate\Response\RedirectResponse + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\RedirectResponse */ public function resetDatabasePassword(Request $request, $id) { @@ -559,9 +525,11 @@ class ServersController extends Controller /** * Deletes a database from a server. - * @param Request $request - * @param int $id - * @return \Illuminate\Response\RedirectResponse + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @param int $database + * @return \Illuminate\Http\RedirectResponse */ public function deleteDatabase(Request $request, $id, $database) { diff --git a/app/Http/Controllers/Admin/ServiceController.php b/app/Http/Controllers/Admin/ServiceController.php index bcf4d9008..beaac5340 100644 --- a/app/Http/Controllers/Admin/ServiceController.php +++ b/app/Http/Controllers/Admin/ServiceController.php @@ -38,7 +38,7 @@ class ServiceController extends Controller /** * Display service overview page. * - * @param Request $request + * @param \Illuminate\Http\Request $request * @return \Illuminate\View\View */ public function index(Request $request) @@ -51,10 +51,10 @@ class ServiceController extends Controller /** * Display create service page. * - * @param Request $request + * @param \Illuminate\Http\Request $request * @return \Illuminate\View\View */ - public function new(Request $request) + public function create(Request $request) { return view('admin.services.new'); } @@ -62,8 +62,8 @@ class ServiceController extends Controller /** * Return base view for a service. * - * @param Request $request - * @param int $id + * @param \Illuminate\Http\Request $request + * @param int $id * @return \Illuminate\View\View */ public function view(Request $request, $id) @@ -76,8 +76,8 @@ class ServiceController extends Controller /** * Return function editing view for a service. * - * @param Request $request - * @param int $id + * @param \Illuminate\Http\Request $request + * @param int $id * @return \Illuminate\View\View */ public function viewFunctions(Request $request, $id) @@ -88,10 +88,10 @@ class ServiceController extends Controller /** * Handle post action for new service. * - * @param Request $request - * @return \Illuminate\Response\RedirectResponse + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\RedirectResponse */ - public function create(Request $request) + public function store(Request $request) { $repo = new ServiceRepository; @@ -117,9 +117,9 @@ class ServiceController extends Controller /** * Edits configuration for a specific service. * - * @param Request $request - * @param int $id - * @return \Illuminate\Response\RedirectResponse + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\RedirectResponse */ public function edit(Request $request, $id) { diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index 0de02eca9..c9d7a4725 100644 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -37,17 +37,14 @@ use Pterodactyl\Exceptions\DisplayValidationException; class UserController extends Controller { /** - * Controller Constructor. + * Display user index page. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\View\View */ - public function __construct() + public function index(Request $request) { - // - } - - // @TODO: implement nicolaslopezj/searchable to clean up this disaster. - public function getIndex(Request $request) - { - $users = User::withCount('servers'); + $users = User::withCount('servers', 'subuserOf'); if (! is_null($request->input('query'))) { $users->search($request->input('query')); @@ -58,19 +55,39 @@ class UserController extends Controller ]); } - public function getNew(Request $request) + /** + * Display new user page. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\View\View + */ + public function create(Request $request) { return view('admin.users.new'); } - public function getView(Request $request, $id) + /** + * Display user view page. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\View\View + */ + public function view(Request $request, $id) { return view('admin.users.view', [ 'user' => User::with('servers.node')->findOrFail($id), ]); } - public function deleteUser(Request $request, $id) + /** + * Delete a user. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\RedirectResponse + */ + public function delete(Request $request, $id) { try { $repo = new UserRepository; @@ -88,7 +105,13 @@ class UserController extends Controller return redirect()->route('admin.users.view', $id); } - public function postNew(Request $request) + /** + * Create a user. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\RedirectResponse + */ + public function store(Request $request) { try { $user = new UserRepository; @@ -109,26 +132,39 @@ class UserController extends Controller } } - public function updateUser(Request $request, $user) + /** + * Update a user. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\RedirectResponse + */ + public function update(Request $request, $id) { try { $repo = new UserRepository; - $repo->update($user, $request->only([ + $user = $repo->update($id, $request->intersect([ 'email', 'password', 'name_first', 'name_last', 'username', 'root_admin', ])); Alert::success('User account was successfully updated.')->flash(); } catch (DisplayValidationException $ex) { - return redirect()->route('admin.users.view', $user)->withErrors(json_decode($ex->getMessage())); - } catch (\Exception $e) { - Log::error($e); + return redirect()->route('admin.users.view', $id)->withErrors(json_decode($ex->getMessage())); + } catch (\Exception $ex) { + Log::error($ex); Alert::danger('An error occured while attempting to update this user.')->flash(); } - return redirect()->route('admin.users.view', $user); + return redirect()->route('admin.users.view', $id); } - public function getJson(Request $request) + /** + * Get a JSON response of users on the system. + * + * @param \Illuminate\Http\Request $request + * @return \Pterodactyl\Models\User + */ + public function json(Request $request) { return User::select('id', 'email', 'username', 'name_first', 'name_last') ->search($request->input('q')) diff --git a/app/Http/Controllers/Auth/ForgotPasswordController.php b/app/Http/Controllers/Auth/ForgotPasswordController.php index 6d5f4bfa1..9dd80824a 100644 --- a/app/Http/Controllers/Auth/ForgotPasswordController.php +++ b/app/Http/Controllers/Auth/ForgotPasswordController.php @@ -1,8 +1,33 @@ . + * + * 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\Http\Controllers\Auth; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\Password; use Pterodactyl\Http\Controllers\Controller; +use Pterodactyl\Events\Auth\FailedPasswordReset; use Illuminate\Foundation\Auth\SendsPasswordResetEmails; class ForgotPasswordController extends Controller @@ -29,4 +54,21 @@ class ForgotPasswordController extends Controller { $this->middleware('guest'); } + + /** + * Get the response for a failed password reset link. + * + * @param \Illuminate\Http\Request + * @param string $response + * @return \Illuminate\Http\RedirectResponse + */ + protected function sendResetLinkFailedResponse(Request $request, $response) + { + // As noted in #358 we will return success even if it failed + // to avoid pointing out that an account does or does not + // exist on the system. + event(new FailedPasswordReset($request->ip(), $request->only('email'))); + + return $this->sendResetLinkResponse(Password::RESET_LINK_SENT); + } } diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index d4d8c89c3..23c012f73 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -28,9 +28,11 @@ namespace Pterodactyl\Http\Controllers\Auth; use Auth; use Alert; use Cache; +use Crypt; use Illuminate\Http\Request; use Pterodactyl\Models\User; use PragmaRX\Google2FA\Google2FA; +use Pterodactyl\Events\Auth\FailedLogin; use Pterodactyl\Http\Controllers\Controller; use Illuminate\Foundation\Auth\AuthenticatesUsers; @@ -79,95 +81,152 @@ class LoginController extends Controller $this->middleware('guest', ['except' => 'logout']); } + /** + * Get the failed login response instance. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\RedirectResponse + */ + protected function sendFailedLoginResponse(Request $request) + { + $this->incrementLoginAttempts($request); + + $errors = [$this->username() => trans('auth.failed')]; + + if ($request->expectsJson()) { + return response()->json($errors, 422); + } + + return redirect()->route('auth.login') + ->withInput($request->only($this->username(), 'remember')) + ->withErrors($errors); + } + /** * Handle a login request to the application. * * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response + * @return \Illuminate\Http\Response|\Illuminate\Response\RedirectResponse */ public function login(Request $request) { // Check wether the user identifier is an email address or a username - $isEmail = str_contains($request->input('user'), '@'); + $checkField = str_contains($request->input('user'), '@') ? 'email' : 'username'; - $this->validate($request, [ - 'user' => $isEmail ? 'required|email' : 'required|string', - 'password' => 'required', - ]); - - if ($lockedOut = $this->hasTooManyLoginAttempts($request)) { + if ($this->hasTooManyLoginAttempts($request)) { $this->fireLockoutEvent($request); return $this->sendLockoutResponse($request); } - // Is the user (email or username) & password valid? - if (! Auth::once([ - $isEmail ? 'email' : 'username' => $request->input('user'), - 'password' => $request->input('password'), - ], $request->has('remember'))) { - if (! $lockedOut) { - $this->incrementLoginAttempts($request); - } - + // Determine if the user even exists. + $user = User::where($checkField, $request->input($this->username()))->first(); + if (! $user) { return $this->sendFailedLoginResponse($request); } - // Verify TOTP Token was Valid - if (Auth::user()->use_totp) { - $verifyKey = str_random(64); - Cache::put($verifyKey, Auth::user()->id, 5); + // If user uses 2FA, redirect to that page. + if ($user->use_totp) { + $token = str_random(64); + Cache::put($token, [ + 'user_id' => $user->id, + 'credentials' => Crypt::encrypt(serialize([ + $checkField => $request->input($this->username()), + 'password' => $request->input('password'), + ])), + ], 5); - return redirect()->route('auth.totp')->with('authentication_token', $verifyKey); - } else { - Auth::login(Auth::user(), $request->has('remember')); + return redirect()->route('auth.totp')->with('authentication_token', $token); + } + $attempt = Auth::attempt([ + $checkField => $request->input($this->username()), + 'password' => $request->input('password'), + 'use_totp' => 0, + ], $request->has('remember')); + + if ($attempt) { return $this->sendLoginResponse($request); } + + // Login failed, send response. + return $this->sendFailedLoginResponse($request); } + /** + * Handle a TOTP implementation page. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View + */ public function totp(Request $request) { - $verifyKey = $request->session()->get('authentication_token'); + $token = $request->session()->get('authentication_token'); - if (is_null($verifyKey) || Auth::user()) { + if (is_null($token) || Auth::user()) { return redirect()->route('auth.login'); } return view('auth.totp', [ - 'verify_key' => $verifyKey, + 'verify_key' => $token, 'remember' => $request->has('remember'), ]); } + /** + * Handle a TOTP input. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\RedirectResponse + */ public function totpCheckpoint(Request $request) { $G2FA = new Google2FA(); if (is_null($request->input('verify_token'))) { - $this->incrementLoginAttempts($request); - Alert::danger(trans('auth.totp_failed'))->flash(); - - return redirect()->route('auth.login'); + return $this->sendFailedLoginResponse($request); } - $user = User::where('id', Cache::pull($request->input('verify_token')))->first(); - if (! $user) { - $this->incrementLoginAttempts($request); - Alert::danger(trans('auth.totp_failed'))->flash(); + $cache = Cache::pull($request->input('verify_token')); + $user = User::where('id', $cache['user_id'])->first(); - return redirect()->route('auth.login'); + if (! $user || ! $cache) { + $this->sendFailedLoginResponse($request); } - if (! is_null($request->input('2fa_token')) && $G2FA->verifyKey($user->totp_secret, $request->input('2fa_token'), 1)) { - Auth::login($user, $request->has('remember')); - - return redirect()->intended($this->redirectPath()); - } else { - $this->incrementLoginAttempts($request); - Alert::danger(trans('auth.2fa_failed'))->flash(); - - return redirect()->route('auth.login'); + if (is_null($request->input('2fa_token'))) { + return $this->sendFailedLoginResponse($request); } + + try { + $credentials = unserialize(Crypt::decrypt($cache['credentials'])); + } catch (\Illuminate\Contracts\Encryption\DecryptException $ex) { + return $this->sendFailedLoginResponse($request); + } + + if (! $G2FA->verifyKey($user->totp_secret, $request->input('2fa_token'), 2)) { + event(new \Illuminate\Auth\Events\Failed($user, $credentials)); + + return $this->sendFailedLoginResponse($request); + } + + $attempt = Auth::attempt($credentials, $request->has('remember')); + + if ($attempt) { + return $this->sendLoginResponse($request); + } + + // Login failed, send response. + return $this->sendFailedLoginResponse($request); + } + + /** + * Get the login username to be used by the controller. + * + * @return string + */ + public function username() + { + return 'user'; } } diff --git a/app/Http/Controllers/Auth/ResetPasswordController.php b/app/Http/Controllers/Auth/ResetPasswordController.php index fca008051..02138d04d 100644 --- a/app/Http/Controllers/Auth/ResetPasswordController.php +++ b/app/Http/Controllers/Auth/ResetPasswordController.php @@ -21,6 +21,11 @@ class ResetPasswordController extends Controller use ResetsPasswords; + /** + * The URL to redirect users to after password reset. + * + * @var string + */ public $redirectTo = '/'; /** @@ -33,6 +38,11 @@ class ResetPasswordController extends Controller $this->middleware('guest'); } + /** + * Return the rules used when validating password reset. + * + * @return array + */ protected function rules() { return [ diff --git a/app/Http/Controllers/Base/APIController.php b/app/Http/Controllers/Base/APIController.php index f4133bc7f..9c3816db3 100644 --- a/app/Http/Controllers/Base/APIController.php +++ b/app/Http/Controllers/Base/APIController.php @@ -27,8 +27,9 @@ namespace Pterodactyl\Http\Controllers\Base; use Log; use Alert; -use Pterodactyl\Models; use Illuminate\Http\Request; +use Pterodactyl\Models\APIKey; +use Pterodactyl\Models\APIPermission; use Pterodactyl\Repositories\APIRepository; use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Http\Controllers\Controller; @@ -36,25 +37,48 @@ use Pterodactyl\Exceptions\DisplayValidationException; class APIController extends Controller { + /** + * Display base API index page. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\View\View + */ public function index(Request $request) { return view('base.api.index', [ - 'keys' => Models\APIKey::where('user_id', $request->user()->id)->get(), + 'keys' => APIKey::where('user_id', $request->user()->id)->get(), ]); } + /** + * Display API key creation page. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\View\View + */ public function create(Request $request) { - return view('base.api.new'); + return view('base.api.new', [ + 'permissions' => [ + 'user' => collect(APIPermission::permissions())->pull('_user'), + 'admin' => collect(APIPermission::permissions())->except('_user')->toArray(), + ], + ]); } - public function save(Request $request) + /** + * Handle saving new API key. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\RedirectResponse + */ + public function store(Request $request) { try { $repo = new APIRepository($request->user()); $secret = $repo->create($request->intersect([ 'memo', 'allowed_ips', - 'adminPermissions', 'permissions', + 'admin_permissions', 'permissions', ])); Alert::success('An API Key-Pair has successfully been generated. The API secret for this public key is shown below and will not be shown again.

' . $secret . '')->flash(); @@ -71,6 +95,13 @@ class APIController extends Controller return redirect()->route('account.api.new')->withInput(); } + /** + * Handle revoking API key. + * + * @param \Illuminate\Http\Request $request + * @param string $key + * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\Response + */ public function revoke(Request $request, $key) { try { diff --git a/app/Http/Controllers/Base/AccountController.php b/app/Http/Controllers/Base/AccountController.php index 8db513bd5..10c33e380 100644 --- a/app/Http/Controllers/Base/AccountController.php +++ b/app/Http/Controllers/Base/AccountController.php @@ -38,8 +38,8 @@ class AccountController extends Controller /** * Display base account information page. * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Contracts\View\View + * @param \Illuminate\Http\Request $request + * @return \Illuminate\View\View */ public function index(Request $request) { @@ -48,8 +48,10 @@ class AccountController extends Controller /** * Update details for a users account. - * @param \Illuminate\Http\Request $request - * @return void + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\RedirectResponse + * @throws \Symfony\Component\HttpKernel\Exception\HttpException */ public function update(Request $request) { @@ -80,7 +82,7 @@ class AccountController extends Controller if ( in_array($request->input('do_action'), ['email', 'password']) - && ! password_verify($request->input('password'), $request->user()->password) + && ! password_verify($request->input('current_password'), $request->user()->password) ) { Alert::danger(trans('base.account.invalid_pass'))->flash(); diff --git a/app/Http/Controllers/Base/IndexController.php b/app/Http/Controllers/Base/IndexController.php index 5b6fab3a7..5c3d4f401 100644 --- a/app/Http/Controllers/Base/IndexController.php +++ b/app/Http/Controllers/Base/IndexController.php @@ -30,32 +30,32 @@ use Pterodactyl\Http\Controllers\Controller; class IndexController extends Controller { - /** - * Controller Constructor. - */ - public function __construct() - { - // - } - /** * Returns listing of user's servers. * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Contracts\View\View + * @param \Illuminate\Http\Request $request + * @return \Illuminate\View\View */ public function getIndex(Request $request) { + $servers = $request->user()->access()->with('user'); + + if (! is_null($request->input('query'))) { + $servers->search($request->input('query')); + } + return view('base.index', [ - 'servers' => $request->user()->serverAccessCollection(config('pterodactyl.paginate.frontend.servers')), + 'servers' => $servers->paginate(config('pterodactyl.paginate.frontend.servers')), ]); } /** * Generate a random string. * - * @param \Illuminate\Http\Request $request + * @param \Illuminate\Http\Request $request + * @param int $length * @return string + * @deprecated */ public function getPassword(Request $request, $length = 16) { diff --git a/app/Http/Controllers/Base/LanguageController.php b/app/Http/Controllers/Base/LanguageController.php index e4967ee5e..67d04f66c 100644 --- a/app/Http/Controllers/Base/LanguageController.php +++ b/app/Http/Controllers/Base/LanguageController.php @@ -32,6 +32,11 @@ use Pterodactyl\Http\Controllers\Controller; class LanguageController extends Controller { + /** + * A list of supported languages on the panel. + * + * @var array + */ protected $languages = [ 'de' => 'German', 'en' => 'English', @@ -44,13 +49,12 @@ class LanguageController extends Controller ]; /** - * Controller Constructor. + * Sets the language for a user. + * + * @param \Illuminate\Http\Request $request + * @param string $language + * @return \Illuminate\Http\RedirectResponse */ - public function __construct() - { - // - } - public function setLanguage(Request $request, $language) { if (array_key_exists($language, $this->languages)) { @@ -59,7 +63,7 @@ class LanguageController extends Controller $user->language = $language; $user->save(); } - Session::set('applocale', $language); + Session::put('applocale', $language); } return redirect()->back(); diff --git a/app/Http/Controllers/Base/SecurityController.php b/app/Http/Controllers/Base/SecurityController.php index 23b6f0d68..052a7a527 100644 --- a/app/Http/Controllers/Base/SecurityController.php +++ b/app/Http/Controllers/Base/SecurityController.php @@ -36,8 +36,8 @@ class SecurityController extends Controller /** * Returns Security Management Page. * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Contracts\View\View + * @param \Illuminate\Http\Request $request + * @return \Illuminate\View\View */ public function index(Request $request) { @@ -50,8 +50,8 @@ class SecurityController extends Controller * Generates TOTP Secret and returns popup data for user to verify * that they can generate a valid response. * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Contracts\View\View + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\JsonResponse */ public function generateTotp(Request $request) { @@ -73,7 +73,7 @@ class SecurityController extends Controller /** * Verifies that 2FA token recieved is valid and will work on the account. * - * @param \Illuminate\Http\Request $request + * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function setTotp(Request $request) @@ -95,8 +95,8 @@ class SecurityController extends Controller /** * Disables TOTP on an account. * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\RedirectResponse */ public function disableTotp(Request $request) { @@ -116,6 +116,13 @@ class SecurityController extends Controller return redirect()->route('account.security'); } + /** + * Revokes a user session. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\RedirectResponse + */ public function revoke(Request $request, $id) { Session::where('user_id', $request->user()->id)->findOrFail($id)->delete(); diff --git a/app/Http/Controllers/Daemon/PackController.php b/app/Http/Controllers/Daemon/PackController.php index 4d35f6d77..419ae7c9c 100644 --- a/app/Http/Controllers/Daemon/PackController.php +++ b/app/Http/Controllers/Daemon/PackController.php @@ -31,19 +31,12 @@ use Pterodactyl\Http\Controllers\Controller; class PackController extends Controller { - /** - * Controller Constructor. - */ - public function __construct() - { - // - } - /** * Pulls an install pack archive from the system. * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @return \Illuminate\Http\JsonResponse|\Symfony\Component\HttpFoundation\BinaryFileResponse */ public function pull(Request $request, $uuid) { @@ -63,8 +56,9 @@ class PackController extends Controller /** * Returns the hash information for a pack. * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @return \Illuminate\Http\JsonResponse */ public function hash(Request $request, $uuid) { @@ -87,9 +81,10 @@ class PackController extends Controller * Pulls an update pack archive from the system. * * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response + * @return void */ public function pullUpdate(Request $request) { + // } } diff --git a/app/Http/Controllers/Daemon/ServiceController.php b/app/Http/Controllers/Daemon/ServiceController.php index 99d69cf02..5762ecef2 100644 --- a/app/Http/Controllers/Daemon/ServiceController.php +++ b/app/Http/Controllers/Daemon/ServiceController.php @@ -36,7 +36,7 @@ class ServiceController extends Controller * as well as the associated files and the file hashes for * caching purposes. * - * @param \Illuminate\Http\Request $request + * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\JsonResponse */ public function list(Request $request) @@ -77,7 +77,7 @@ class ServiceController extends Controller * Returns a `main.json` file based on the configuration * of each service option. * - * @param int $id + * @param int $id * @return \Illuminate\Support\Collection */ protected function getConfiguration($id) diff --git a/app/Http/Controllers/Remote/RemoteController.php b/app/Http/Controllers/Remote/RemoteController.php index 93dd672e1..892a82842 100644 --- a/app/Http/Controllers/Remote/RemoteController.php +++ b/app/Http/Controllers/Remote/RemoteController.php @@ -32,13 +32,11 @@ use Pterodactyl\Http\Controllers\Controller; class RemoteController extends Controller { /** - * Controller Constructor. + * Handles download request from daemon. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\JsonResponse */ - public function __construct() - { - // No middleware for this route. - } - public function postDownload(Request $request) { $download = Models\Download::where('token', $request->input('token'))->first(); @@ -56,6 +54,12 @@ class RemoteController extends Controller ]); } + /** + * Handles install toggle request from daemon. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\JsonResponse + */ public function postInstall(Request $request) { $server = Models\Server::where('uuid', $request->input('server'))->with('node')->first(); @@ -82,6 +86,13 @@ class RemoteController extends Controller ], 200); } + /** + * Handles event from daemon. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\Response + * @deprecated + */ public function event(Request $request) { $server = Models\Server::where('uuid', $request->input('server'))->with('node')->first(); @@ -101,6 +112,13 @@ class RemoteController extends Controller return response('', 201); } + /** + * Handles configuration data request from daemon. + * + * @param \Illuminate\Http\Request $request + * @param string $token + * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\Response + */ public function getConfiguration(Request $request, $token) { // Try to query the token and the node from the database @@ -111,7 +129,7 @@ class RemoteController extends Controller } // Check if token is expired - if ($model->created_at->lt(Carbon::now())) { + if ($model->created_at->addMinutes(5)->lt(Carbon::now())) { $model->delete(); return response()->json(['error' => 'token_expired'], 403); diff --git a/app/Http/Controllers/Server/AjaxController.php b/app/Http/Controllers/Server/AjaxController.php index 4609ae270..9ffd13038 100644 --- a/app/Http/Controllers/Server/AjaxController.php +++ b/app/Http/Controllers/Server/AjaxController.php @@ -50,20 +50,12 @@ class AjaxController extends Controller */ protected $directory; - /** - * Controller Constructor. - */ - public function __construct() - { - // - } - /** * Returns true or false depending on the power status of the requested server. * - * @param \Illuminate\Http\Request $request - * @param string $uuid - * @return \Illuminate\Contracts\View\View + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @return \Illuminate\Http\JsonResponse */ public function getStatus(Request $request, $uuid) { @@ -96,9 +88,9 @@ class AjaxController extends Controller /** * Returns a listing of files in a given directory for a server. * - * @param \Illuminate\Http\Request $request - * @param string $uuid` - * @return \Illuminate\Contracts\View\View + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @return \Illuminate\View\View|\Illuminate\Http\Response */ public function postDirectoryList(Request $request, $uuid) { @@ -147,8 +139,8 @@ class AjaxController extends Controller /** * Handles a POST request to save a file. * - * @param Request $request - * @param string $uuid + * @param \Illuminate\Http\Request $request + * @param string $uuid * @return \Illuminate\Http\Response */ public function postSaveFile(Request $request, $uuid) @@ -172,10 +164,12 @@ class AjaxController extends Controller } /** - * [postSetPrimary description]. - * @param Request $request - * @param string $uuid - * @return \Illuminate\Http\Response + * Sets the primary allocation for a server. + * + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @return \Illuminate\Http\JsonResponse + * @deprecated */ public function postSetPrimary(Request $request, $uuid) { @@ -219,6 +213,14 @@ class AjaxController extends Controller } } + /** + * Resets a database password for a server. + * + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @return \Illuminate\Http\JsonResponse + * @deprecated + */ public function postResetDatabasePassword(Request $request, $uuid) { $server = Models\Server::byUuid($uuid); diff --git a/app/Http/Controllers/Server/ServerController.php b/app/Http/Controllers/Server/ServerController.php index 62cd3d304..4b6c9bcef 100644 --- a/app/Http/Controllers/Server/ServerController.php +++ b/app/Http/Controllers/Server/ServerController.php @@ -37,21 +37,12 @@ use Pterodactyl\Exceptions\DisplayValidationException; class ServerController extends Controller { - /** - * Controller Constructor. - * - * @return void - */ - public function __construct() - { - // - } - /** * Renders server index page for specified server. * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Contracts\View\View + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @return \Illuminate\View\View */ public function getIndex(Request $request, $uuid) { @@ -62,6 +53,10 @@ class ServerController extends Controller 'saveFile' => route('server.files.save', $server->uuidShort), 'csrfToken' => csrf_token(), ], + 'config' => [ + 'console_count' => config('pterodactyl.console.count'), + 'console_freq' => config('pterodactyl.console.frequency'), + ], ]); return view('server.index', [ @@ -70,11 +65,37 @@ class ServerController extends Controller ]); } + /** + * Renders server console as an individual item. + * + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @return \Illuminate\View\View + */ + public function getConsole(Request $request, $uuid) + { + \Debugbar::disable(); + $server = Models\Server::byUuid($uuid); + + $server->js([ + 'config' => [ + 'console_count' => config('pterodactyl.console.count'), + 'console_freq' => config('pterodactyl.console.frequency'), + ], + ]); + + return view('server.console', [ + 'server' => $server, + 'node' => $server->node, + ]); + } + /** * Renders file overview page. * - * @param Request $request - * @return \Illuminate\Contracts\View\View + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @return \Illuminate\View\View */ public function getFiles(Request $request, $uuid) { @@ -106,8 +127,9 @@ class ServerController extends Controller /** * Renders add file page. * - * @param Request $request - * @return \Illuminate\Contracts\View\View + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @return \Illuminate\View\View */ public function getAddFile(Request $request, $uuid) { @@ -126,10 +148,10 @@ class ServerController extends Controller /** * Renders edit file page for a given file. * - * @param Request $request - * @param string $uuid - * @param string $file - * @return \Illuminate\Contracts\View\View + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @param string $file + * @return \Illuminate\View\View */ public function getEditFile(Request $request, $uuid, $file) { @@ -169,10 +191,10 @@ class ServerController extends Controller /** * Handles downloading a file for the user. * - * @param Request $request - * @param string $uuid - * @param string $file - * @return \Illuminate\Contracts\View\View + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @param string $file + * @return \Illuminate\View\View */ public function getDownloadFile(Request $request, $uuid, $file) { @@ -190,6 +212,13 @@ class ServerController extends Controller return redirect($server->node->scheme . '://' . $server->node->fqdn . ':' . $server->node->daemonListen . '/server/file/download/' . $download->token); } + /** + * Returns the allocation overview for a server. + * + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @return \Illuminate\View\View + */ public function getAllocation(Request $request, $uuid) { $server = Models\Server::byUuid($uuid); @@ -205,6 +234,13 @@ class ServerController extends Controller ]); } + /** + * Returns the startup overview for a server. + * + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @return \Illuminate\View\View + */ public function getStartup(Request $request, $uuid) { $server = Models\Server::byUuid($uuid); @@ -235,6 +271,13 @@ class ServerController extends Controller ]); } + /** + * Returns the database overview for a server. + * + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @return \Illuminate\View\View + */ public function getDatabases(Request $request, $uuid) { $server = Models\Server::byUuid($uuid); @@ -250,6 +293,13 @@ class ServerController extends Controller ]); } + /** + * Returns the SFTP overview for a server. + * + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @return \Illuminate\View\View + */ public function getSFTP(Request $request, $uuid) { $server = Models\Server::byUuid($uuid); @@ -262,6 +312,13 @@ class ServerController extends Controller ]); } + /** + * Handles changing the SFTP password for a server. + * + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @return \Illuminate\Http\RedirectResponse + */ public function postSettingsSFTP(Request $request, $uuid) { $server = Models\Server::byUuid($uuid); @@ -283,6 +340,13 @@ class ServerController extends Controller return redirect()->route('server.settings.sftp', $uuid); } + /** + * Handles changing the startup settings for a server. + * + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @return \Illuminate\Http\RedirectResponse + */ public function postSettingsStartup(Request $request, $uuid) { $server = Models\Server::byUuid($uuid); diff --git a/app/Http/Controllers/Server/SubuserController.php b/app/Http/Controllers/Server/SubuserController.php index a3ae8e3ba..c0c15f29d 100644 --- a/app/Http/Controllers/Server/SubuserController.php +++ b/app/Http/Controllers/Server/SubuserController.php @@ -37,16 +37,13 @@ use Pterodactyl\Exceptions\DisplayValidationException; class SubuserController extends Controller { /** - * Controller Constructor. + * Displays the subuser overview index. * - * @return void + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @return \Illuminate\View\View */ - public function __construct() - { - // - } - - public function getIndex(Request $request, $uuid) + public function index(Request $request, $uuid) { $server = Models\Server::byUuid($uuid)->load('subusers.user'); $this->authorize('list-subusers', $server); @@ -60,7 +57,15 @@ class SubuserController extends Controller ]); } - public function getView(Request $request, $uuid, $id) + /** + * Displays the a single subuser overview. + * + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @param int $id + * @return \Illuminate\View\View + */ + public function view(Request $request, $uuid, $id) { $server = Models\Server::byUuid($uuid)->load('node'); $this->authorize('view-subuser', $server); @@ -74,13 +79,22 @@ class SubuserController extends Controller 'server' => $server, 'node' => $server->node, 'subuser' => $subuser, + 'permlist' => Models\Permission::list(), 'permissions' => $subuser->permissions->mapWithKeys(function ($item, $key) { return [$item->permission => true]; }), ]); } - public function postView(Request $request, $uuid, $id) + /** + * Handles editing a subuser. + * + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @param int $id + * @return \Illuminate\Http\RedirectResponse + */ + public function update(Request $request, $uuid, $id) { $server = Models\Server::byUuid($uuid); $this->authorize('edit-subuser', $server); @@ -118,7 +132,14 @@ class SubuserController extends Controller ]); } - public function getNew(Request $request, $uuid) + /** + * Display new subuser creation page. + * + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @return \Illuminate\View\View + */ + public function create(Request $request, $uuid) { $server = Models\Server::byUuid($uuid); $this->authorize('create-subuser', $server); @@ -126,11 +147,19 @@ class SubuserController extends Controller return view('server.users.new', [ 'server' => $server, + 'permissions' => Models\Permission::list(), 'node' => $server->node, ]); } - public function postNew(Request $request, $uuid) + /** + * Handles creating a new subuser. + * + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @return \Illuminate\Http\RedirectResponse + */ + public function store(Request $request, $uuid) { $server = Models\Server::byUuid($uuid); $this->authorize('create-subuser', $server); @@ -158,7 +187,15 @@ class SubuserController extends Controller return redirect()->route('server.subusers.new', $uuid)->withInput(); } - public function deleteSubuser(Request $request, $uuid, $id) + /** + * Handles deleting a subuser. + * + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @param int $id + * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\Response + */ + public function delete(Request $request, $uuid, $id) { $server = Models\Server::byUuid($uuid); $this->authorize('delete-subuser', $server); diff --git a/app/Http/Controllers/Server/TaskController.php b/app/Http/Controllers/Server/TaskController.php index 643e70a45..8eebaa21d 100644 --- a/app/Http/Controllers/Server/TaskController.php +++ b/app/Http/Controllers/Server/TaskController.php @@ -26,23 +26,25 @@ namespace Pterodactyl\Http\Controllers\Server; use Log; use Alert; -use Pterodactyl\Models; use Illuminate\Http\Request; -use Pterodactyl\Repositories; +use Pterodactyl\Models\Server; +use Pterodactyl\Repositories\TaskRepository; use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Http\Controllers\Controller; use Pterodactyl\Exceptions\DisplayValidationException; class TaskController extends Controller { - public function __constructor() + /** + * Display task index page. + * + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @return \Illuminate\View\View + */ + public function index(Request $request, $uuid) { - // - } - - public function getIndex(Request $request, $uuid) - { - $server = Models\Server::byUuid($uuid)->load('tasks'); + $server = Server::byUuid($uuid)->load('tasks'); $this->authorize('list-tasks', $server); $server->js(); @@ -57,9 +59,16 @@ class TaskController extends Controller ]); } - public function getNew(Request $request, $uuid) + /** + * Display new task page. + * + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @return \Illuminate\View\View + */ + public function create(Request $request, $uuid) { - $server = Models\Server::byUuid($uuid); + $server = Server::byUuid($uuid); $this->authorize('create-task', $server); $server->js(); @@ -69,14 +78,21 @@ class TaskController extends Controller ]); } - public function postNew(Request $request, $uuid) + /** + * Handle creation of new task. + * + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @return \Illuminate\Http\RedirectResponse + */ + public function store(Request $request, $uuid) { - $server = Models\Server::byUuid($uuid); + $server = Server::byUuid($uuid); $this->authorize('create-task', $server); + $repo = new TaskRepository; try { - $repo = new Repositories\TaskRepository; - $repo->create($server->id, $request->except([ + $repo->create($server->id, $request->user()->id, $request->except([ '_token', ])); @@ -93,9 +109,17 @@ class TaskController extends Controller return redirect()->route('server.tasks.new', $uuid); } - public function deleteTask(Request $request, $uuid, $id) + /** + * Handle deletion of a task. + * + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @param int $id + * @return \Illuminate\Http\JsonResponse + */ + public function delete(Request $request, $uuid, $id) { - $server = Models\Server::byUuid($uuid)->load('tasks'); + $server = Server::byUuid($uuid)->load('tasks'); $this->authorize('delete-task', $server); $task = $server->tasks->where('id', $id)->first(); @@ -105,8 +129,8 @@ class TaskController extends Controller ], 404); } + $repo = new TaskRepository; try { - $repo = new Repositories\TaskRepository; $repo->delete($id); return response()->json([], 204); @@ -119,9 +143,17 @@ class TaskController extends Controller } } - public function toggleTask(Request $request, $uuid, $id) + /** + * Toggle the status of a task. + * + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @param int $id + * @return \Illuminate\Http\JsonResponse + */ + public function toggle(Request $request, $uuid, $id) { - $server = Models\Server::byUuid($uuid)->load('tasks'); + $server = Server::byUuid($uuid)->load('tasks'); $this->authorize('toggle-task', $server); $task = $server->tasks->where('id', $id)->first(); @@ -131,8 +163,8 @@ class TaskController extends Controller ], 404); } + $repo = new TaskRepository; try { - $repo = new Repositories\TaskRepository; $resp = $repo->toggle($id); return response()->json([ diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 971835dac..a70895a3e 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -15,10 +15,11 @@ class Kernel extends HttpKernel \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class, \Pterodactyl\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, - \Illuminate\Session\Middleware\StartSession::class, - \Illuminate\View\Middleware\ShareErrorsFromSession::class, + \Pterodactyl\Http\Middleware\TrimStrings::class, - \Pterodactyl\Http\Middleware\LanguageMiddleware::class, + /* + * Custom middleware applied to all routes. + */ \Fideloper\Proxy\TrustProxies::class, ]; @@ -35,8 +36,10 @@ class Kernel extends HttpKernel \Illuminate\View\Middleware\ShareErrorsFromSession::class, \Pterodactyl\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, + \Pterodactyl\Http\Middleware\LanguageMiddleware::class, ], 'api' => [ + \Pterodactyl\Http\Middleware\HMACAuthorization::class, 'throttle:60,1', 'bindings', ], @@ -58,5 +61,6 @@ class Kernel extends HttpKernel 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'can' => \Illuminate\Auth\Middleware\Authorize::class, 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, + 'recaptcha' => \Pterodactyl\Http\Middleware\VerifyReCaptcha::class, ]; } diff --git a/app/Http/Middleware/APISecretToken.php b/app/Http/Middleware/APISecretToken.php deleted file mode 100755 index 25bf891ba..000000000 --- a/app/Http/Middleware/APISecretToken.php +++ /dev/null @@ -1,133 +0,0 @@ -. - * - * 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\Http\Middleware; - -use Auth; -use Crypt; -use Config; -use IPTools\IP; -use IPTools\Range; -use Dingo\Api\Routing\Route; -use Illuminate\Http\Request; -use Pterodactyl\Models\User; -use Pterodactyl\Models\APIKey; -use Pterodactyl\Models\APIPermission; -use Pterodactyl\Services\APILogService; -use Dingo\Api\Auth\Provider\Authorization; -use Symfony\Component\HttpKernel\Exception\HttpException; // 400 -use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; // 401 -use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; // 403 -use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException; //500 - -class APISecretToken extends Authorization -{ - protected $algo = 'sha256'; - - protected $permissionAllowed = false; - - protected $url = ''; - - public function __construct() - { - Config::set('session.driver', 'array'); - } - - public function getAuthorizationMethod() - { - return 'Authorization'; - } - - public function authenticate(Request $request, Route $route) - { - if (! $request->bearerToken() || empty($request->bearerToken())) { - APILogService::log($request, 'The authentication header was missing or malformed.'); - throw new UnauthorizedHttpException('The authentication header was missing or malformed.'); - } - - list($public, $hashed) = explode('.', $request->bearerToken()); - - $key = APIKey::where('public', $public)->first(); - if (! $key) { - APILogService::log($request, 'Invalid API Key.'); - throw new AccessDeniedHttpException('Invalid API Key.'); - } - - // Check for Resource Permissions - if (! empty($request->route()->getName())) { - if (! is_null($key->allowed_ips)) { - $inRange = false; - foreach (json_decode($key->allowed_ips) as $ip) { - if (Range::parse($ip)->contains(new IP($request->ip()))) { - $inRange = true; - break; - } - } - if (! $inRange) { - APILogService::log($request, 'This IP address <' . $request->ip() . '> does not have permission to use this API key.'); - throw new AccessDeniedHttpException('This IP address <' . $request->ip() . '> does not have permission to use this API key.'); - } - } - - $permission = APIPermission::where('key_id', $key->id)->where('permission', $request->route()->getName()); - - // Suport Wildcards - if (starts_with($request->route()->getName(), 'api.user')) { - $permission->orWhere('permission', 'api.user.*'); - } elseif (starts_with($request->route()->getName(), 'api.admin')) { - $permission->orWhere('permission', 'api.admin.*'); - } - - if (! $permission->first()) { - APILogService::log($request, 'You do not have permission to access this resource. This API Key requires the ' . $request->route()->getName() . ' permission node.'); - throw new AccessDeniedHttpException('You do not have permission to access this resource. This API Key requires the ' . $request->route()->getName() . ' permission node.'); - } - } - - try { - $decrypted = Crypt::decrypt($key->secret); - } catch (\Illuminate\Contracts\Encryption\DecryptException $ex) { - APILogService::log($request, 'There was an error while attempting to check your secret key.'); - throw new HttpException('There was an error while attempting to check your secret key.'); - } - - $this->url = urldecode($request->fullUrl()); - if ($this->_generateHMAC($request->getContent(), $decrypted) !== base64_decode($hashed)) { - APILogService::log($request, 'The hashed body was not valid. Potential modification of contents in route.'); - throw new BadRequestHttpException('The hashed body was not valid. Potential modification of contents in route.'); - } - - // Log the Route Access - APILogService::log($request, null, true); - - return Auth::loginUsingId($key->user_id); - } - - protected function _generateHMAC($body, $key) - { - $data = $this->url . $body; - - return hash_hmac($this->algo, $data, $key, true); - } -} diff --git a/app/Http/Middleware/AdminAuthenticate.php b/app/Http/Middleware/AdminAuthenticate.php index d533fc49d..175210929 100644 --- a/app/Http/Middleware/AdminAuthenticate.php +++ b/app/Http/Middleware/AdminAuthenticate.php @@ -32,14 +32,14 @@ class AdminAuthenticate /** * The Guard implementation. * - * @var Guard + * @var \Illuminate\Contracts\Auth\Guard */ protected $auth; /** * Create a new filter instance. * - * @param Guard $auth + * @param \Illuminate\Contracts\Auth\Guard $auth * @return void */ public function __construct(Guard $auth) @@ -51,7 +51,7 @@ class AdminAuthenticate * Handle an incoming request. * * @param \Illuminate\Http\Request $request - * @param \Closure $next + * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php index 216465e23..b6db005ef 100644 --- a/app/Http/Middleware/Authenticate.php +++ b/app/Http/Middleware/Authenticate.php @@ -10,14 +10,14 @@ class Authenticate /** * The Guard implementation. * - * @var Guard + * @var \Illuminate\Contracts\Auth\Guard */ protected $auth; /** * Create a new filter instance. * - * @param Guard $auth + * @param \Illuminate\Contracts\Auth\Guard $auth * @return void */ public function __construct(Guard $auth) @@ -29,7 +29,7 @@ class Authenticate * Handle an incoming request. * * @param \Illuminate\Http\Request $request - * @param \Closure $next + * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) diff --git a/app/Http/Middleware/CheckServer.php b/app/Http/Middleware/CheckServer.php index dba9395ac..4cfe08191 100644 --- a/app/Http/Middleware/CheckServer.php +++ b/app/Http/Middleware/CheckServer.php @@ -28,35 +28,102 @@ use Auth; use Closure; use Illuminate\Http\Request; use Pterodactyl\Models\Server; +use Illuminate\Auth\AuthenticationException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; class CheckServer { + /** + * The elquent model for the server. + * + * @var \Pterodactyl\Models\Server + */ + protected $server; + + /** + * The request object. + * + * @var \Illuminate\Http\Request + */ + protected $request; + /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request - * @param \Closure $next + * @param \Closure $next * @return mixed */ public function handle(Request $request, Closure $next) { if (! Auth::user()) { - return redirect()->guest('auth/login'); + throw new AuthenticationException(); } - $server = Server::byUuid($request->route()->server); - if (! $server) { + $this->request = $request; + $this->server = Server::byUuid($request->route()->server); + + if (! $this->exists()) { return response()->view('errors.404', [], 404); } - if ($server->suspended) { + if ($this->suspended()) { return response()->view('errors.suspended', [], 403); } - if (! $server->installed) { + if (! $this->installed()) { return response()->view('errors.installing', [], 403); } return $next($request); } + + /** + * Determine if the server was found on the system. + * + * @return bool + */ + protected function exists() + { + if (! $this->server) { + if ($this->request->expectsJson() || $this->request->is(...config('pterodactyl.json_routes'))) { + throw new NotFoundHttpException('The requested server was not found on the system.'); + } + } + + return (! $this->server) ? false : true; + } + + /** + * Determine if the server is suspended. + * + * @return bool + */ + protected function suspended() + { + if ($this->server->suspended) { + if ($this->request->expectsJson() || $this->request->is(...config('pterodactyl.json_routes'))) { + throw new AccessDeniedHttpException('Server is suspended.'); + } + } + + return $this->server->suspended; + } + + /** + * Determine if the server is installed. + * + * @return bool + */ + protected function installed() + { + if ($this->server->installed !== 1) { + if ($this->request->expectsJson() || $this->request->is(...config('pterodactyl.json_routes'))) { + throw new AccessDeniedHttpException('Server is completing install process.'); + } + } + + return $this->server->installed === 1; + } } diff --git a/app/Http/Middleware/DaemonAuthenticate.php b/app/Http/Middleware/DaemonAuthenticate.php index 73cb029d4..8b1813185 100644 --- a/app/Http/Middleware/DaemonAuthenticate.php +++ b/app/Http/Middleware/DaemonAuthenticate.php @@ -33,14 +33,14 @@ class DaemonAuthenticate /** * The Guard implementation. * - * @var Guard + * @var \Illuminate\Contracts\Auth\Guard */ protected $auth; /** * Create a new filter instance. * - * @param Guard $auth + * @param \Illuminate\Contracts\Auth\Guard $auth * @return void */ public function __construct(Guard $auth) @@ -52,7 +52,7 @@ class DaemonAuthenticate * Handle an incoming request. * * @param \Illuminate\Http\Request $request - * @param \Closure $next + * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) diff --git a/app/Http/Middleware/HMACAuthorization.php b/app/Http/Middleware/HMACAuthorization.php new file mode 100644 index 000000000..2c08c1449 --- /dev/null +++ b/app/Http/Middleware/HMACAuthorization.php @@ -0,0 +1,229 @@ +. + * + * 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\Http\Middleware; + +use Auth; +use Crypt; +use Config; +use Closure; +use Debugbar; +use IPTools\IP; +use IPTools\Range; +use Illuminate\Http\Request; +use Pterodactyl\Models\APIKey; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; // 400 +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; // 403 + +class HMACAuthorization +{ + /** + * The algorithm to use for handling HMAC encryption. + * + * @var string + */ + const HMAC_ALGORITHM = 'sha256'; + + /** + * Stored values from the Authorization header. + * + * @var array + */ + protected $token = []; + + /** + * The eloquent model for the API key. + * + * @var \Pterodactyl\Models\APIKey + */ + protected $key; + + /** + * The illuminate request object. + * + * @var \Illuminate\Http\Request + */ + private $request; + + /** + * Construct class instance. + * + * @return void + */ + public function __construct() + { + Debugbar::disable(); + Config::set('session.driver', 'array'); + } + + /** + * Handle an incoming request for the API. + * + * @param \Illuminate\Http\Request $request + * @param \Closure $next + * @return mixed + */ + public function handle(Request $request, Closure $next) + { + $this->request = $request; + + $this->checkBearer(); + $this->validateRequest(); + $this->validateIPAccess(); + $this->validateContents(); + + Auth::loginUsingId($this->key()->user_id); + + return $next($request); + } + + /** + * Checks that the Bearer token is provided and in a valid format. + * + * @return void + * + * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException + */ + protected function checkBearer() + { + if (empty($this->request()->bearerToken())) { + throw new BadRequestHttpException('Request was missing required Authorization header.'); + } + + $token = explode('.', $this->request()->bearerToken()); + if (count($token) !== 2) { + throw new BadRequestHttpException('The Authorization header passed was not in a validate public/private key format.'); + } + + $this->token = [ + 'public' => $token[0], + 'hash' => $token[1], + ]; + } + + /** + * Determine if the request contains a valid public API key + * as well as permissions for the resource. + * + * @return void + * + * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException + */ + protected function validateRequest() + { + $this->key = APIKey::where('public', $this->public())->first(); + if (! $this->key) { + throw new AccessDeniedHttpException('Unable to identify requester. Authorization token is invalid.'); + } + } + + /** + * Determine if the requesting IP address is allowed to use this API key. + * + * @return bool + * + * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException + */ + protected function validateIPAccess() + { + if (! is_null($this->key()->allowed_ips)) { + foreach ($this->key()->allowed_ips as $ip) { + if (Range::parse($ip)->contains(new IP($this->request()->ip()))) { + return true; + } + } + + throw new AccessDeniedHttpException('This IP address does not have permission to access the API using these credentials.'); + } + + return true; + } + + /** + * Determine if the HMAC sent in the request matches the one generated + * on the panel side. + * + * @return void + * + * @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException + */ + protected function validateContents() + { + if (base64_decode($this->hash()) !== $this->generateSignature()) { + throw new BadRequestHttpException('The HMAC for the request was invalid.'); + } + } + + /** + * Generate a HMAC from the request and known API secret key. + * + * @return string + */ + protected function generateSignature() + { + $content = urldecode($this->request()->fullUrl()) . $this->request()->getContent(); + + return hash_hmac(self::HMAC_ALGORITHM, $content, Crypt::decrypt($this->key()->secret), true); + } + + /** + * Return the public key passed in the Authorization header. + * + * @return string + */ + protected function public() + { + return $this->token['public']; + } + + /** + * Return the base64'd HMAC sent in the Authorization header. + * + * @return string + */ + protected function hash() + { + return $this->token['hash']; + } + + /** + * Return the API Key model. + * + * @return \Pterodactyl\Models\APIKey + */ + protected function key() + { + return $this->key; + } + + /** + * Return the Illuminate Request object. + * + * @return \Illuminate\Http\Request + */ + private function request() + { + return $this->request; + } +} diff --git a/app/Http/Middleware/LanguageMiddleware.php b/app/Http/Middleware/LanguageMiddleware.php index 58426063f..81b9a26cb 100644 --- a/app/Http/Middleware/LanguageMiddleware.php +++ b/app/Http/Middleware/LanguageMiddleware.php @@ -32,16 +32,11 @@ use Illuminate\Support\Facades\App; class LanguageMiddleware { - public function __construct() - { - // - } - /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request - * @param \Closure $next + * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) @@ -49,7 +44,7 @@ class LanguageMiddleware if (Session::has('applocale')) { App::setLocale(Session::get('applocale')); } elseif (Auth::check() && isset(Auth::user()->language)) { - Session::set('applocale', Auth::user()->language); + Session::put('applocale', Auth::user()->language); App::setLocale(Auth::user()->language); } else { App::setLocale(Settings::get('default_language', 'en')); diff --git a/app/Http/Middleware/RedirectIfAuthenticated.php b/app/Http/Middleware/RedirectIfAuthenticated.php index 1735e49fe..731a1767f 100644 --- a/app/Http/Middleware/RedirectIfAuthenticated.php +++ b/app/Http/Middleware/RedirectIfAuthenticated.php @@ -11,14 +11,14 @@ class RedirectIfAuthenticated * Handle an incoming request. * * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @param string|null $guard + * @param \Closure $next + * @param string|null $guard * @return mixed */ public function handle($request, Closure $next, $guard = null) { if (Auth::guard($guard)->check()) { - return redirect('/'); + return redirect(route('index')); } return $next($request); diff --git a/app/Http/Middleware/TrimStrings.php b/app/Http/Middleware/TrimStrings.php new file mode 100644 index 000000000..04f434b98 --- /dev/null +++ b/app/Http/Middleware/TrimStrings.php @@ -0,0 +1,18 @@ +has('g-recaptcha-response')) { + $client = new \GuzzleHttp\Client(); + $res = $client->post(config('recaptcha.domain'), [ + 'form_params' => [ + 'secret' => config('recaptcha.secret_key'), + 'response' => $request->input('g-recaptcha-response'), + ], + ]); + + if ($res->getStatusCode() === 200) { + $result = json_decode($res->getBody()); + + $verified = function ($result, $request) { + if (! config('recaptcha.verify_domain')) { + return false; + } + + $url = parse_url($request->url()); + + if (! array_key_exists('host', $url)) { + return false; + } + + return $result->hostname === $url['host']; + }; + + if ($result->success && (! config('recaptcha.verify_domain') || $verified($result, $request))) { + return $next($request); + } + } + } + + // Emit an event and return to the previous view with an error (only the captcha error will be shown!) + event(new FailedCaptcha($request->ip(), (! isset($result->hostname) ?: $result->hostname))); + + return back()->withErrors(['g-recaptcha-response' => trans('strings.captcha_invalid')])->withInput(); + } +} diff --git a/app/Http/Routes/APIRoutes.php b/app/Http/Routes/APIRoutes.php deleted file mode 100755 index a4fb84c60..000000000 --- a/app/Http/Routes/APIRoutes.php +++ /dev/null @@ -1,184 +0,0 @@ -. - * - * 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\Http\Routes; - -use Illuminate\Routing\Router; - -class APIRoutes -{ - public function map(Router $router) - { - $api = app('Dingo\Api\Routing\Router'); - $api->version('v1', ['prefix' => 'api/me', 'middleware' => 'api.auth'], function ($api) { - $api->get('/', [ - 'as' => 'api.user.me', - 'uses' => 'Pterodactyl\Http\Controllers\API\User\InfoController@me', - ]); - - $api->get('/server/{uuid}', [ - 'as' => 'api.user.server', - 'uses' => 'Pterodactyl\Http\Controllers\API\User\ServerController@info', - ]); - - $api->put('/server/{uuid}', [ - 'as' => 'api.user.server.power', - 'uses' => 'Pterodactyl\Http\Controllers\API\User\ServerController@power', - ]); - }); - - $api->version('v1', ['prefix' => 'api', 'middleware' => 'api.auth'], function ($api) { - - /* - * User Routes - */ - $api->get('users', [ - 'as' => 'api.admin.users.list', - 'uses' => 'Pterodactyl\Http\Controllers\API\UserController@lists', - ]); - - $api->post('users', [ - 'as' => 'api.admin.users.create', - 'uses' => 'Pterodactyl\Http\Controllers\API\UserController@create', - ]); - - $api->get('users/{id}', [ - 'as' => 'api.admin.users.view', - 'uses' => 'Pterodactyl\Http\Controllers\API\UserController@view', - ]); - - $api->patch('users/{id}', [ - 'as' => 'api.admin.users.update', - 'uses' => 'Pterodactyl\Http\Controllers\API\UserController@update', - ]); - - $api->delete('users/{id}', [ - 'as' => 'api.admin.users.delete', - 'uses' => 'Pterodactyl\Http\Controllers\API\UserController@delete', - ]); - - /* - * Server Routes - */ - $api->get('servers', [ - 'as' => 'api.admin.servers.list', - 'uses' => 'Pterodactyl\Http\Controllers\API\ServerController@lists', - ]); - - $api->post('servers', [ - 'as' => 'api.admin.servers.create', - 'uses' => 'Pterodactyl\Http\Controllers\API\ServerController@create', - ]); - - $api->get('servers/{id}', [ - 'as' => 'api.admin.servers.view', - 'uses' => 'Pterodactyl\Http\Controllers\API\ServerController@view', - ]); - - $api->patch('servers/{id}/config', [ - 'as' => 'api.admin.servers.config', - 'uses' => 'Pterodactyl\Http\Controllers\API\ServerController@config', - ]); - - $api->patch('servers/{id}/build', [ - 'as' => 'api.admin.servers.build', - 'uses' => 'Pterodactyl\Http\Controllers\API\ServerController@build', - ]); - - $api->post('servers/{id}/suspend', [ - 'as' => 'api.admin.servers.suspend', - 'uses' => 'Pterodactyl\Http\Controllers\API\ServerController@suspend', - ]); - - $api->post('servers/{id}/unsuspend', [ - 'as' => 'api.admin.servers.unsuspend', - 'uses' => 'Pterodactyl\Http\Controllers\API\ServerController@unsuspend', - ]); - - $api->delete('servers/{id}/{force?}', [ - 'as' => 'api.admin.servers.delete', - 'uses' => 'Pterodactyl\Http\Controllers\API\ServerController@delete', - ]); - - /* - * Node Routes - */ - $api->get('nodes', [ - 'as' => 'api.admin.nodes.list', - 'uses' => 'Pterodactyl\Http\Controllers\API\NodeController@lists', - ]); - - $api->post('nodes', [ - 'as' => 'api.admin.nodes.create', - 'uses' => 'Pterodactyl\Http\Controllers\API\NodeController@create', - ]); - - $api->get('nodes/allocations', [ - 'as' => 'api.admin.nodes.allocations', - 'uses' => 'Pterodactyl\Http\Controllers\API\NodeController@allocations', - ]); - - $api->get('nodes/allocations/{id}', [ - 'as' => 'api.admin.nodes.allocations', - 'uses' => 'Pterodactyl\Http\Controllers\API\NodeController@allocationsView', - ]); - - $api->get('nodes/{id}', [ - 'as' => 'api.admin.nodes.view', - 'uses' => 'Pterodactyl\Http\Controllers\API\NodeController@view', - ]); - - $api->get('nodes/{id}/config', [ - 'as' => 'api.admin.nodes.view', - 'uses' => 'Pterodactyl\Http\Controllers\API\NodeController@config', - ]); - - $api->delete('nodes/{id}', [ - 'as' => 'api.admin.nodes.delete', - 'uses' => 'Pterodactyl\Http\Controllers\API\NodeController@delete', - ]); - - /* - * Location Routes - */ - $api->get('locations', [ - 'as' => 'api.admin.locations.list', - 'uses' => 'Pterodactyl\Http\Controllers\API\LocationController@lists', - ]); - - /* - * Service Routes - */ - $api->get('services', [ - 'as' => 'api.admin.services.list', - 'uses' => 'Pterodactyl\Http\Controllers\API\ServiceController@lists', - ]); - - $api->get('services/{id}', [ - 'as' => 'api.admin.services.view', - 'uses' => 'Pterodactyl\Http\Controllers\API\ServiceController@view', - ]); - }); - } -} diff --git a/app/Http/Routes/AuthRoutes.php b/app/Http/Routes/AuthRoutes.php deleted file mode 100644 index 77968321c..000000000 --- a/app/Http/Routes/AuthRoutes.php +++ /dev/null @@ -1,94 +0,0 @@ - - * Some Modifications (c) 2015 Dylan Seidt . - * - * 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\Http\Routes; - -use Auth; -use Illuminate\Routing\Router; - -class AuthRoutes -{ - public function map(Router $router) - { - $router->group([ - 'prefix' => 'auth', - 'middleware' => [ - 'guest', - 'csrf', - ], - ], function () use ($router) { - - // Display Login Page - $router->get('login', [ - 'as' => 'auth.login', - 'uses' => 'Auth\LoginController@showLoginForm', - ]); - - // Handle Login - $router->post('login', [ - 'uses' => 'Auth\LoginController@login', - ]); - - $router->get('login/totp', [ - 'as' => 'auth.totp', - 'uses' => 'Auth\LoginController@totp', - ]); - - $router->post('login/totp', [ - 'uses' => 'Auth\LoginController@totpCheckpoint', - ]); - - // Show Password Reset Form - $router->get('password', [ - 'as' => 'auth.password', - 'uses' => 'Auth\ForgotPasswordController@showLinkRequestForm', - ]); - - // Handle Password Reset - $router->post('password', [ - 'uses' => 'Auth\ForgotPasswordController@sendResetLinkEmail', - ]); - - // Show Verification Checkpoint - $router->get('password/reset/{token}', [ - 'as' => 'auth.reset', - 'uses' => 'Auth\ResetPasswordController@showResetForm', - ]); - - // Handle Verification - $router->post('password/reset', [ - 'as' => 'auth.reset.post', - 'uses' => 'Auth\ResetPasswordController@reset', - ]); - }); - - // Not included above because we don't want the guest middleware - $router->get('auth/logout', [ - 'as' => 'auth.logout', - 'middleware' => 'auth', - 'uses' => 'Auth\LoginController@logout', - ]); - } -} diff --git a/app/Http/Routes/BaseRoutes.php b/app/Http/Routes/BaseRoutes.php deleted file mode 100644 index 40e86f8a7..000000000 --- a/app/Http/Routes/BaseRoutes.php +++ /dev/null @@ -1,125 +0,0 @@ - - * Some Modifications (c) 2015 Dylan Seidt . - * - * 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\Http\Routes; - -use Illuminate\Routing\Router; - -class BaseRoutes -{ - public function map(Router $router) - { - - // Index of Panel - $router->get('/', [ - 'as' => 'index', - 'middleware' => 'auth', - 'uses' => 'Base\IndexController@getIndex', - ]); - - // Handle Index. Redirect /index to / - $router->get('/index', function () { - return redirect()->route('index'); - }); - - // Password Generation - $router->get('/password-gen/{length}', [ - 'as' => 'password-gen', - 'middleware' => 'auth', - 'uses' => 'Base\IndexController@getPassword', - ]); - - // Account Routes - $router->group([ - 'prefix' => 'account', - 'middleware' => [ - 'auth', - 'csrf', - ], - ], function () use ($router) { - $router->get('/', [ - 'as' => 'account', - 'uses' => 'Base\AccountController@index', - ]); - $router->post('/', [ - 'uses' => 'Base\AccountController@update', - ]); - }); - - // API Management Routes - $router->group([ - 'prefix' => 'account/api', - 'middleware' => [ - 'auth', - 'csrf', - ], - ], function () use ($router) { - $router->get('/', [ - 'as' => 'account.api', - 'uses' => 'Base\APIController@index', - ]); - $router->get('/new', [ - 'as' => 'account.api.new', - 'uses' => 'Base\APIController@create', - ]); - $router->post('/new', [ - 'uses' => 'Base\APIController@save', - ]); - - $router->delete('/revoke/{key}', [ - 'as' => 'account.api.revoke', - 'uses' => 'Base\APIController@revoke', - ]); - }); - - // TOTP Routes - $router->group([ - 'prefix' => 'account/security', - 'middleware' => [ - 'auth', - 'csrf', - ], - ], function () use ($router) { - $router->get('/', [ - 'as' => 'account.security', - 'uses' => 'Base\SecurityController@index', - ]); - $router->get('/revoke/{id}', [ - 'as' => 'account.security.revoke', - 'uses' => 'Base\SecurityController@revoke', - ]); - $router->put('/totp', [ - 'as' => 'account.security.totp', - 'uses' => 'Base\SecurityController@generateTotp', - ]); - $router->post('/totp', [ - 'uses' => 'Base\SecurityController@setTotp', - ]); - $router->delete('/totp', [ - 'uses' => 'Base\SecurityController@disableTotp', - ]); - }); - } -} diff --git a/app/Http/Routes/RemoteRoutes.php b/app/Http/Routes/RemoteRoutes.php deleted file mode 100644 index 9ece1fb2e..000000000 --- a/app/Http/Routes/RemoteRoutes.php +++ /dev/null @@ -1,51 +0,0 @@ -. - * - * 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\Http\Routes; - -use Illuminate\Routing\Router; - -class RemoteRoutes -{ - public function map(Router $router) - { - $router->group(['prefix' => 'remote'], function () use ($router) { - // Handles Remote Download Authentication Requests - $router->post('download', [ - 'as' => 'remote.download', - 'uses' => 'Remote\RemoteController@postDownload', - ]); - - $router->post('install', [ - 'as' => 'remote.install', - 'uses' => 'Remote\RemoteController@postInstall', - ]); - - $router->get('configuration/{token}', [ - 'as' => 'remote.configuration', - 'uses' => 'Remote\RemoteController@getConfiguration', - ]); - }); - } -} diff --git a/app/Http/Routes/ServerRoutes.php b/app/Http/Routes/ServerRoutes.php deleted file mode 100644 index 36c95827b..000000000 --- a/app/Http/Routes/ServerRoutes.php +++ /dev/null @@ -1,186 +0,0 @@ -. - * - * 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\Http\Routes; - -use Illuminate\Routing\Router; - -class ServerRoutes -{ - public function map(Router $router) - { - // Returns Server Status - $router->get('/server/{server}/ajax/status', [ - 'middleware' => ['auth', 'csrf'], - 'as' => 'server.ajax.status', - 'uses' => 'Server\AjaxController@getStatus', - ]); - - $router->group([ - 'prefix' => 'server/{server}', - 'middleware' => [ - 'auth', - 'server', - 'csrf', - ], - ], function ($server) use ($router) { - - // Index View for Server - $router->get('/', [ - 'as' => 'server.index', - 'uses' => 'Server\ServerController@getIndex', - ]); - - $router->get('/settings/databases', [ - 'as' => 'server.settings.databases', - 'uses' => 'Server\ServerController@getDatabases', - ]); - - $router->get('/settings/sftp', [ - 'as' => 'server.settings.sftp', - 'uses' => 'Server\ServerController@getSFTP', - ]); - - $router->post('/settings/sftp', [ - 'uses' => 'Server\ServerController@postSettingsSFTP', - ]); - - $router->get('/settings/startup', [ - 'as' => 'server.settings.startup', - 'uses' => 'Server\ServerController@getStartup', - ]); - - $router->post('/settings/startup', [ - 'uses' => 'Server\ServerController@postSettingsStartup', - ]); - - $router->get('/settings/allocation', [ - 'as' => 'server.settings.allocation', - 'uses' => 'Server\ServerController@getAllocation', - ]); - - // File Manager Routes - $router->get('/files', [ - 'as' => 'server.files.index', - 'uses' => 'Server\ServerController@getFiles', - ]); - - $router->get('/files/edit/{file}', [ - 'as' => 'server.files.edit', - 'uses' => 'Server\ServerController@getEditFile', - ])->where('file', '.*'); - - $router->get('/files/download/{file}', [ - 'as' => 'server.files.download', - 'uses' => 'Server\ServerController@getDownloadFile', - ])->where('file', '.*'); - - $router->get('/files/add', [ - 'as' => 'server.files.add', - 'uses' => 'Server\ServerController@getAddFile', - ]); - - $router->post('files/directory-list', [ - 'as' => 'server.files.directory-list', - 'uses' => 'Server\AjaxController@postDirectoryList', - ]); - - $router->post('files/save', [ - 'as' => 'server.files.save', - 'uses' => 'Server\AjaxController@postSaveFile', - ]); - - // Sub-User Routes - $router->get('users', [ - 'as' => 'server.subusers', - 'uses' => 'Server\SubuserController@getIndex', - ]); - - $router->get('users/new', [ - 'as' => 'server.subusers.new', - 'uses' => 'Server\SubuserController@getNew', - ]); - - $router->post('users/new', [ - 'uses' => 'Server\SubuserController@postNew', - ]); - - $router->get('users/view/{id}', [ - 'as' => 'server.subusers.view', - 'uses' => 'Server\SubuserController@getView', - ]); - - $router->post('users/view/{id}', [ - 'uses' => 'Server\SubuserController@postView', - ]); - - $router->delete('users/delete/{id}', [ - 'as' => 'server.subusers.delete', - 'uses' => 'Server\SubuserController@deleteSubuser', - ]); - - $router->get('tasks/', [ - 'as' => 'server.tasks', - 'uses' => 'Server\TaskController@getIndex', - ]); - - $router->get('tasks/view/{id}', [ - 'as' => 'server.tasks.view', - 'uses' => 'Server\TaskController@getView', - ]); - - $router->get('tasks/new', [ - 'as' => 'server.tasks.new', - 'uses' => 'Server\TaskController@getNew', - ]); - - $router->post('tasks/new', [ - 'uses' => 'Server\TaskController@postNew', - ]); - - $router->delete('tasks/delete/{id}', [ - 'as' => 'server.tasks.delete', - 'uses' => 'Server\TaskController@deleteTask', - ]); - - $router->post('tasks/toggle/{id}', [ - 'as' => 'server.tasks.toggle', - 'uses' => 'Server\TaskController@toggleTask', - ]); - - // Assorted AJAX Routes - $router->group(['prefix' => 'ajax'], function ($server) use ($router) { - // Sets the Default Connection for the Server - $router->post('set-primary', [ - 'uses' => 'Server\AjaxController@postSetPrimary', - ]); - - $router->post('settings/reset-database-password', [ - 'as' => 'server.ajax.reset-database-password', - 'uses' => 'Server\AjaxController@postResetDatabasePassword', - ]); - }); - }); - } -} diff --git a/app/Jobs/SendScheduledTask.php b/app/Jobs/SendScheduledTask.php index dd7a56e27..167b8e250 100644 --- a/app/Jobs/SendScheduledTask.php +++ b/app/Jobs/SendScheduledTask.php @@ -26,7 +26,10 @@ namespace Pterodactyl\Jobs; use Cron; use Carbon; -use Pterodactyl\Models; +use Pterodactyl\Models\Task; +use Pterodactyl\Models\User; +use Pterodactyl\Models\Server; +use Pterodactyl\Models\TaskLog; use Illuminate\Queue\SerializesModels; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; @@ -37,8 +40,9 @@ class SendScheduledTask extends Job implements ShouldQueue { use InteractsWithQueue, SerializesModels; - protected $server; - + /** + * @var \Pterodactyl\Models\Task + */ protected $task; /** @@ -46,13 +50,12 @@ class SendScheduledTask extends Job implements ShouldQueue * * @return void */ - public function __construct(Models\Server $server, Models\Task $task) + public function __construct(Task $task) { - $this->server = $server; $this->task = $task; - $task->queued = 1; - $task->save(); + $this->task->queued = true; + $this->task->save(); } /** @@ -63,7 +66,7 @@ class SendScheduledTask extends Job implements ShouldQueue public function handle() { $time = Carbon::now(); - $log = new Models\TaskLog; + $log = new TaskLog; if ($this->attempts() >= 1) { // Just delete the job, we will attempt it again later anyways. @@ -72,12 +75,15 @@ class SendScheduledTask extends Job implements ShouldQueue try { if ($this->task->action === 'command') { - $repo = new CommandRepository($this->server); + $repo = new CommandRepository($this->task->server, $this->task->user); $response = $repo->send($this->task->data); - } elseif ($this->task->action === 'power') { - $repo = new PowerRepository($this->server); + } else if ($this->task->action === 'power') { + $repo = new PowerRepository($this->task->server, $this->task->user); $response = $repo->do($this->task->data); + } else { + throw new \Exception('Task type provided was not valid.'); } + $log->fill([ 'task_id' => $this->task->id, 'run_time' => $time, @@ -103,7 +109,7 @@ class SendScheduledTask extends Job implements ShouldQueue $this->task->fill([ 'last_run' => $time, 'next_run' => $cron->getNextRunDate(), - 'queued' => 0, + 'queued' => false, ]); $this->task->save(); $log->save(); diff --git a/app/Models/APIKey.php b/app/Models/APIKey.php index 68e481712..6ed73b7c2 100644 --- a/app/Models/APIKey.php +++ b/app/Models/APIKey.php @@ -28,6 +28,13 @@ use Illuminate\Database\Eloquent\Model; class APIKey extends Model { + /** + * Public key defined length used in verification methods. + * + * @var int + */ + const PUBLIC_KEY_LEN = 16; + /** * The table associated with the model. * @@ -42,6 +49,15 @@ class APIKey extends Model */ protected $hidden = ['secret']; + /** + * Cast values to correct type. + * + * @var array + */ + protected $casts = [ + 'allowed_ips' => 'json', + ]; + /** * Fields that are not mass assignable. * diff --git a/app/Models/APILog.php b/app/Models/APILog.php index 5b481d8ff..6d5f26fda 100644 --- a/app/Models/APILog.php +++ b/app/Models/APILog.php @@ -49,12 +49,12 @@ class APILog extends Model */ protected $guarded = ['id', 'created_at', 'updated_at']; - /** - * Cast values to correct type. - * - * @var array - */ - protected $casts = [ - 'authorized' => 'boolean', - ]; + /** + * Cast values to correct type. + * + * @var array + */ + protected $casts = [ + 'authorized' => 'boolean', + ]; } diff --git a/app/Models/APIPermission.php b/app/Models/APIPermission.php index 5bed095ff..bfe2fc908 100644 --- a/app/Models/APIPermission.php +++ b/app/Models/APIPermission.php @@ -42,14 +42,14 @@ class APIPermission extends Model */ protected $guarded = ['id']; - /** - * Cast values to correct type. - * - * @var array - */ - protected $casts = [ - 'key_id' => 'integer', - ]; + /** + * Cast values to correct type. + * + * @var array + */ + protected $casts = [ + 'key_id' => 'integer', + ]; /** * Disable timestamps for this table. @@ -57,4 +57,75 @@ class APIPermission extends Model * @var bool */ public $timestamps = false; + + /** + * List of permissions available for the API. + * + * @var array + */ + protected static $permissions = [ + // Items within this block are available to non-adminitrative users. + '_user' => [ + 'server' => [ + 'list', + 'view', + 'power', + 'command', + ], + ], + + // All other pemissions below are administrative actions. + 'server' => [ + 'list', + 'create', + 'view', + 'edit-details', + 'edit-container', + 'edit-build', + 'edit-startup', + 'suspend', + 'install', + 'rebuild', + 'delete', + ], + 'location' => [ + 'list', + ], + 'node' => [ + 'list', + 'view', + 'view-config', + 'create', + 'delete', + ], + 'user' => [ + 'list', + 'view', + 'create', + 'edit', + 'delete', + ], + 'service' => [ + 'list', + 'view', + ], + 'option' => [ + 'list', + 'view', + ], + 'pack' => [ + 'list', + 'view', + ], + ]; + + /** + * Return permissions for API. + * + * @return array + */ + public static function permissions() + { + return self::$permissions; + } } diff --git a/app/Models/Allocation.php b/app/Models/Allocation.php index cdcf79782..d37e52a0e 100644 --- a/app/Models/Allocation.php +++ b/app/Models/Allocation.php @@ -56,7 +56,7 @@ class Allocation extends Model /** * Accessor to automatically provide the IP alias if defined. * - * @param null|string $value + * @param null|string $value * @return string */ public function getAliasAttribute($value) @@ -67,7 +67,7 @@ class Allocation extends Model /** * Accessor to quickly determine if this allocation has an alias. * - * @param null|string $value + * @param null|string $value * @return bool */ public function getHasAliasAttribute($value) diff --git a/app/Models/Database.php b/app/Models/Database.php index 624b2c228..20ad3c1b0 100644 --- a/app/Models/Database.php +++ b/app/Models/Database.php @@ -51,33 +51,33 @@ class Database extends Model 'server_id', 'database_host_id', 'database', 'username', 'remote', ]; - /** - * Cast values to correct type. - * - * @var array - */ - protected $casts = [ - 'server_id' => 'integer', - 'database_host_id' => 'integer', - ]; + /** + * Cast values to correct type. + * + * @var array + */ + protected $casts = [ + 'server_id' => 'integer', + 'database_host_id' => 'integer', + ]; - /** - * Gets the host database server associated with a database. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo - */ - public function host() - { - return $this->belongsTo(DatabaseHost::class, 'database_host_id'); - } + /** + * Gets the host database server associated with a database. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function host() + { + return $this->belongsTo(DatabaseHost::class, 'database_host_id'); + } - /** - * Gets the server associated with a database. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo - */ - public function server() - { - return $this->belongsTo(Server::class); - } + /** + * Gets the server associated with a database. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function server() + { + return $this->belongsTo(Server::class); + } } diff --git a/app/Models/DatabaseHost.php b/app/Models/DatabaseHost.php index 0d5eaf30a..165a99c5a 100644 --- a/app/Models/DatabaseHost.php +++ b/app/Models/DatabaseHost.php @@ -24,6 +24,8 @@ namespace Pterodactyl\Models; +use Crypt; +use Config; use Illuminate\Database\Eloquent\Model; class DatabaseHost extends Model @@ -51,34 +53,54 @@ class DatabaseHost extends Model 'name', 'host', 'port', 'username', 'max_databases', 'node_id', ]; - /** - * Cast values to correct type. - * - * @var array - */ - protected $casts = [ - 'id' => 'integer', - 'max_databases' => 'integer', - 'node_id' => 'integer', - ]; + /** + * Cast values to correct type. + * + * @var array + */ + protected $casts = [ + 'id' => 'integer', + 'max_databases' => 'integer', + 'node_id' => 'integer', + ]; - /** - * Gets the node associated with a database host. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo - */ - public function node() - { - return $this->belongsTo(Node::class); - } + /** + * Sets the database connection name with the details of the host. + * + * @param string $connection + * @return void + */ + public function setDynamicConnection($connection = 'dynamic') + { + Config::set('database.connections.' . $connection, [ + 'driver' => 'mysql', + 'host' => $this->host, + 'port' => $this->port, + 'database' => 'mysql', + 'username' => $this->username, + 'password' => Crypt::decrypt($this->password), + 'charset' => 'utf8', + 'collation' => 'utf8_unicode_ci', + ]); + } - /** - * Gets the databases assocaited with this host. - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany - */ - public function databases() - { - return $this->hasMany(Database::class); - } + /** + * Gets the node associated with a database host. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function node() + { + return $this->belongsTo(Node::class); + } + + /** + * Gets the databases assocaited with this host. + * + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function databases() + { + return $this->hasMany(Database::class); + } } diff --git a/app/Models/Node.php b/app/Models/Node.php index f1a3159be..5b0464561 100644 --- a/app/Models/Node.php +++ b/app/Models/Node.php @@ -75,6 +75,11 @@ class Node extends Model 'daemonSFTP', 'daemonListen', ]; + /** + * Fields that are searchable. + * + * @var array + */ protected $searchable = [ 'columns' => [ 'nodes.name' => 10, @@ -90,7 +95,7 @@ class Node extends Model /** * Return an instance of the Guzzle client for this specific node. * - * @param array $headers + * @param array $headers * @return \GuzzleHttp\Client */ public function guzzleClient($headers = []) @@ -106,8 +111,8 @@ class Node extends Model /** * Returns the configuration in JSON format. * - * @param bool $pretty Wether to pretty print the JSON or not - * @return string The configration in JSON format + * @param bool $pretty + * @return string */ public function getConfigurationAsJson($pretty = false) { diff --git a/app/Models/Pack.php b/app/Models/Pack.php index 0a6310660..9cda19790 100644 --- a/app/Models/Pack.php +++ b/app/Models/Pack.php @@ -83,7 +83,7 @@ class Pack extends Model /** * Returns all of the archived files for a given pack. * - * @param bool $collection + * @param bool $collection * @return \Illuminate\Support\Collection|object */ public function files($collection = false) diff --git a/app/Models/Permission.php b/app/Models/Permission.php index 7dfd55f3f..a2bd58a7b 100644 --- a/app/Models/Permission.php +++ b/app/Models/Permission.php @@ -58,6 +58,80 @@ class Permission extends Model 'subuser_id' => 'integer', ]; + /** + * A list of all permissions available for a user. + * + * @var array + */ + protected static $permissions = [ + 'power' => [ + 'power-start' => 's:power:start', + 'power-stop' => 's:power:stop', + 'power-restart' => 's:power:restart', + 'power-kill' => 's:power:kill', + 'send-command' => 's:command', + ], + 'subuser' => [ + 'list-subusers' => null, + 'view-subuser' => null, + 'edit-subuser' => null, + 'create-subuser' => null, + 'delete-subuser' => null, + ], + 'server' => [ + 'set-connection' => null, + 'view-startup' => null, + 'edit-startup' => null, + ], + 'sftp' => [ + 'view-sftp' => null, + 'view-sftp-password' => null, + 'reset-sftp' => 's:set-password', + ], + 'file' => [ + 'list-files' => 's:files:get', + 'edit-files' => 's:files:read', + 'save-files' => 's:files:post', + 'move-files' => 's:files:move', + 'copy-files' => 's:files:copy', + 'compress-files' => 's:files:compress', + 'decompress-files' => 's:files:decompress', + 'create-files' => 's:files:create', + 'upload-files' => 's:files:upload', + 'delete-files' => 's:files:delete', + 'download-files' => null, + ], + 'task' => [ + 'list-tasks' => null, + 'view-task' => null, + 'toggle-task' => null, + 'queue-task' => null, + 'create-task' => null, + 'delete-task' => null, + ], + 'database' => [ + 'view-databases' => null, + 'reset-db-password' => null, + ], + ]; + + /** + * Return a collection of permissions available. + * + * @param array $single + * @return \Illuminate\Support\Collection|array + */ + public static function list($single = false) + { + if ($single) { + return collect(self::$permissions)->mapWithKeys(function ($item) { + return $item; + })->all(); + } + + return collect(self::$permissions); + } + /** * Find permission by permission node. * diff --git a/app/Models/Server.php b/app/Models/Server.php index c341aad70..729ae4f0d 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -27,15 +27,15 @@ namespace Pterodactyl\Models; use Auth; use Cache; use Carbon; +use Schema; 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, SearchableTrait, SoftDeletes; + use Notifiable, SearchableTrait; /** * The table associated with the model. @@ -115,8 +115,11 @@ class Server extends Model * DO NOT USE THIS TO MODIFY SERVER DETAILS OR SAVE THOSE DETAILS. * YOU WILL OVERWRITE THE SECRET KEY AND BREAK THINGS. * - * @param string $uuid The Short-UUID of the server to return an object about. - * @return \Illuminate\Database\Eloquent\Collection + * @param string $uuid + * @param array $with + * @param array $withCount + * @return \Pterodactyl\Models\Server + * @todo Remove $with and $withCount due to cache issues, they aren't used anyways. */ public static function byUuid($uuid, array $with = [], array $withCount = []) { @@ -147,30 +150,39 @@ class Server extends Model /** * Returns non-administrative headers for accessing a server on the daemon. * - * @param string $uuid + * @param Pterodactyl\Models\User|null $user * @return array */ - public function guzzleHeaders() + public function guzzleHeaders(User $user = null) { + // If no specific user is passed, see if we can find an active + // auth session to pull data from. + if (is_null($user) && Auth::check()) { + $user = Auth::user(); + } + return [ 'X-Access-Server' => $this->uuid, - 'X-Access-Token' => Auth::user()->daemonToken($this), + 'X-Access-Token' => ($user) ? $user->daemonToken($this) : $this->daemonSecret, ]; } /** * Return an instance of the Guzzle client for this specific server using defined access token. * + * @param Pterodactyl\Models\User|null $user * @return \GuzzleHttp\Client */ - public function guzzleClient() + public function guzzleClient(User $user = null) { - return $this->node->guzzleClient($this->guzzleHeaders()); + return $this->node->guzzleClient($this->guzzleHeaders($user)); } /** * Returns javascript object to be embedded on server view pages with relevant information. * + * @param array|null $additional + * @param array|null $overwrite * @return \Laracasts\Utilities\JavaScript\JavaScriptFacade */ public function js($additional = null, $overwrite = null) @@ -200,6 +212,16 @@ class Server extends Model return Javascript::put($response); } + /** + * Return the columns available for this table. + * + * @return array + */ + public function getTableColumns() + { + return Schema::getColumnListing($this->getTable()); + } + /** * Gets the user who owns the server. * @@ -298,7 +320,7 @@ class Server extends Model */ public function tasks() { - return $this->hasMany(Task::class, 'server', 'id'); + return $this->hasMany(Task::class); } /** @@ -310,4 +332,24 @@ class Server extends Model { return $this->hasMany(Database::class); } + + /** + * Gets all downloads associated with a server. + * + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function downloads() + { + return $this->hasMany(Download::class, 'server', 'id'); + } + + /** + * Gets the location of the server. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function location() + { + return $this->node->location(); + } } diff --git a/app/Models/ServiceOption.php b/app/Models/ServiceOption.php index fdafed020..159aaf328 100644 --- a/app/Models/ServiceOption.php +++ b/app/Models/ServiceOption.php @@ -42,6 +42,7 @@ class ServiceOption extends Model */ protected $guarded = ['id', 'created_at', 'updated_at']; +<<<<<<< HEAD /** * Cast values to correct type. * @@ -51,55 +52,65 @@ class ServiceOption extends Model 'service_id' => 'integer', 'script_is_privileged' => 'boolean', ]; +======= + /** + * Cast values to correct type. + * + * @var array + */ + protected $casts = [ + 'service_id' => 'integer', + ]; +>>>>>>> develop - /** - * Returns the display startup string for the option and will use the parent - * service one if the option does not have one defined. - * - * @return string - */ - public function getDisplayStartupAttribute($value) - { - return (is_null($this->startup)) ? $this->service->startup : $this->startup; - } + /** + * Returns the display startup string for the option and will use the parent + * service one if the option does not have one defined. + * + * @return string + */ + public function getDisplayStartupAttribute($value) + { + return (is_null($this->startup)) ? $this->service->startup : $this->startup; + } - /** - * Gets service associated with a service option. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo - */ - public function service() - { - return $this->belongsTo(Service::class); - } + /** + * Gets service associated with a service option. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function service() + { + return $this->belongsTo(Service::class); + } - /** - * Gets all servers associated with this service option. - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany - */ - public function servers() - { - return $this->hasMany(Server::class, 'option_id'); - } + /** + * Gets all servers associated with this service option. + * + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function servers() + { + return $this->hasMany(Server::class, 'option_id'); + } - /** - * Gets all variables associated with this service. - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany - */ - public function variables() - { - return $this->hasMany(ServiceVariable::class, 'option_id'); - } + /** + * Gets all variables associated with this service. + * + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function variables() + { + return $this->hasMany(ServiceVariable::class, 'option_id'); + } - /** - * Gets all packs associated with this service. - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany - */ - public function packs() - { - return $this->hasMany(Pack::class, 'option_id'); - } + /** + * Gets all packs associated with this service. + * + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function packs() + { + return $this->hasMany(Pack::class, 'option_id'); + } } diff --git a/app/Models/ServiceVariable.php b/app/Models/ServiceVariable.php index 3d4647583..ac3d47c69 100644 --- a/app/Models/ServiceVariable.php +++ b/app/Models/ServiceVariable.php @@ -57,7 +57,7 @@ class ServiceVariable extends Model * Returns the display executable for the option and will use the parent * service one if the option does not have one defined. * - * @return string + * @return bool */ public function getRequiredAttribute($value) { diff --git a/app/Models/Session.php b/app/Models/Session.php index 9af347bbe..4794d8e04 100644 --- a/app/Models/Session.php +++ b/app/Models/Session.php @@ -35,13 +35,13 @@ class Session extends Model */ protected $table = 'sessions'; - /** - * Cast values to correct type. - * - * @var array - */ - protected $casts = [ - 'id' => 'string', - 'user_id' => 'integer', - ]; + /** + * Cast values to correct type. + * + * @var array + */ + protected $casts = [ + 'id' => 'string', + 'user_id' => 'integer', + ]; } diff --git a/app/Models/Task.php b/app/Models/Task.php index 7f5e18182..5e44a8264 100644 --- a/app/Models/Task.php +++ b/app/Models/Task.php @@ -42,16 +42,18 @@ class Task extends Model */ protected $guarded = ['id', 'created_at', 'updated_at']; - /** - * Cast values to correct type. - * - * @var array - */ - protected $casts = [ - 'id' => 'integer', - 'server' => 'integer', - 'queued' => 'integer', - ]; + /** + * Cast values to correct type. + * + * @var array + */ + protected $casts = [ + 'id' => 'integer', + 'user_id' => 'integer', + 'server_id' => 'integer', + 'queued' => 'boolean', + 'active' => 'boolean', + ]; /** * The attributes that should be mutated to dates. @@ -59,4 +61,24 @@ class Task extends Model * @var array */ protected $dates = ['last_run', 'next_run', 'created_at', 'updated_at']; + + /** + * Gets the server associated with a task. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function server() + { + return $this->belongsTo(Server::class); + } + + /** + * Gets the user associated with a task. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function user() + { + return $this->belongsTo(User::class); + } } diff --git a/app/Models/TaskLog.php b/app/Models/TaskLog.php index 0ec947ae9..578c625c5 100644 --- a/app/Models/TaskLog.php +++ b/app/Models/TaskLog.php @@ -42,16 +42,16 @@ class TaskLog extends Model */ protected $guarded = ['id', 'created_at', 'updated_at']; - /** - * Cast values to correct type. - * - * @var array - */ - protected $casts = [ - 'id' => 'integer', - 'task_id' => 'integer', - 'run_status' => 'integer', - ]; + /** + * Cast values to correct type. + * + * @var array + */ + protected $casts = [ + 'id' => 'integer', + 'task_id' => 'integer', + 'run_status' => 'integer', + ]; /** * The attributes that should be mutated to dates. diff --git a/app/Models/User.php b/app/Models/User.php index 62ced4e72..95e39184a 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -70,16 +70,16 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac */ protected $fillable = ['username', 'email', 'name_first', 'name_last', 'password', 'language', 'use_totp', 'totp_secret', 'gravatar', 'root_admin']; - /** - * Cast values to correct type. - * - * @var array - */ - protected $casts = [ - 'root_admin' => 'integer', - 'use_totp' => 'integer', - 'gravatar' => 'integer', - ]; + /** + * Cast values to correct type. + * + * @var array + */ + protected $casts = [ + 'root_admin' => 'integer', + 'use_totp' => 'integer', + 'gravatar' => 'integer', + ]; /** * The attributes excluded from the model's JSON form. @@ -103,10 +103,12 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac ], ]; + protected $query; + /** * Enables or disables TOTP on an account if the token is valid. * - * @param int $token The token that we want to verify. + * @param int $token * @return bool */ public function toggleTotp($token) @@ -116,9 +118,8 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac } $this->use_totp = ! $this->use_totp; - $this->save(); - return true; + return $this->save(); } /** @@ -128,8 +129,8 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac * - at least one lowercase character * - at least one number. * - * @param string $password The raw password to set the account password to. - * @param string $regex The regex to use when validating the password. Defaults to '((?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,})'. + * @param string $password + * @param string $regex * @return void */ public function setPassword($password, $regex = '((?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,})') @@ -156,7 +157,7 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac /** * Return true or false depending on wether the user is root admin or not. * - * @return bool the user is root admin + * @return bool */ public function isRootAdmin() { @@ -165,7 +166,8 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac /** * Returns the user's daemon secret for a given server. - * @param Server $server \Pterodactyl\Models\Server + * + * @param \Pterodactyl\Models\Server $server * @return null|string */ public function daemonToken(Server $server) @@ -174,13 +176,9 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac return $server->daemonSecret; } - $subuser = Subuser::where('server_id', $server->id)->where('user_id', $this->id)->first(); + $subuser = $this->subuserOf->where('server_id', $server->id)->first(); - if (is_null($subuser)) { - return null; - } - - return $subuser->daemonSecret; + return ($subuser) ? $subuser->daemonSecret : null; } /** @@ -191,25 +189,31 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac */ public function serverAccessArray() { - $union = Subuser::select('server_id')->where('user_id', $this->id); - - return Server::select('id')->where('owner_id', $this->id)->union($union)->pluck('id')->all(); + return Server::select('id')->where('owner_id', $this->id)->union( + Subuser::select('server_id')->where('user_id', $this->id) + )->pluck('id')->all(); } /** * Returns an array of all servers a user is able to access. * Note: does not account for user admin status. * - * @return Collection + * @param array $load + * @return \Illuiminate\Database\Eloquent\Builder */ - public function serverAccessCollection($paginate = null, $load = ['service', 'node', 'allocation']) + public function access(...$load) { - $query = Server::with($load); + if (count($load) > 0 && is_null($load[0])) { + $query = Server::query(); + } else { + $query = Server::with(! empty($load) ? $load : ['service', 'node', 'allocation']); + } + if (! $this->isRootAdmin()) { $query->whereIn('id', $this->serverAccessArray()); } - return (is_numeric($paginate)) ? $query->paginate($paginate) : $query->get(); + return $query; } /** @@ -231,4 +235,14 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac { return $this->hasMany(Server::class, 'owner_id'); } + + /** + * Return all servers that user is listed as a subuser of directly. + * + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function subuserOf() + { + return $this->hasMany(Subuser::class); + } } diff --git a/app/Notifications/AccountCreated.php b/app/Notifications/AccountCreated.php index b7f86361c..f92a7a477 100644 --- a/app/Notifications/AccountCreated.php +++ b/app/Notifications/AccountCreated.php @@ -36,13 +36,14 @@ class AccountCreated extends Notification implements ShouldQueue /** * The password reset token to send. * - * @var string + * @var object */ public $user; /** * Create a new notification instance. * + * @param aray $user * @return void */ public function __construct(array $user) diff --git a/app/Notifications/AddedToServer.php b/app/Notifications/AddedToServer.php index 243584f13..415b39fb0 100644 --- a/app/Notifications/AddedToServer.php +++ b/app/Notifications/AddedToServer.php @@ -33,12 +33,15 @@ class AddedToServer extends Notification implements ShouldQueue { use Queueable; + /** + * @var object + */ public $server; /** * Create a new notification instance. * - * @param array $server + * @param array $server * @return void */ public function __construct(array $server) diff --git a/app/Notifications/RemovedFromServer.php b/app/Notifications/RemovedFromServer.php index d2a7599dc..d4831dbe4 100644 --- a/app/Notifications/RemovedFromServer.php +++ b/app/Notifications/RemovedFromServer.php @@ -33,12 +33,15 @@ class RemovedFromServer extends Notification implements ShouldQueue { use Queueable; + /** + * @var object + */ public $server; /** * Create a new notification instance. * - * @param array $server + * @param array $server * @return void */ public function __construct(array $server) diff --git a/app/Notifications/SendPasswordReset.php b/app/Notifications/SendPasswordReset.php index 0037fb7f3..367f863ed 100644 --- a/app/Notifications/SendPasswordReset.php +++ b/app/Notifications/SendPasswordReset.php @@ -43,6 +43,7 @@ class SendPasswordReset extends Notification implements ShouldQueue /** * Create a new notification instance. * + * @param string $token * @return void */ public function __construct($token) diff --git a/app/Notifications/ServerCreated.php b/app/Notifications/ServerCreated.php index a867e26c7..9f881729a 100644 --- a/app/Notifications/ServerCreated.php +++ b/app/Notifications/ServerCreated.php @@ -33,6 +33,9 @@ class ServerCreated extends Notification implements ShouldQueue { use Queueable; + /** + * @var object + */ public $server; /** diff --git a/app/Observers/ServerObserver.php b/app/Observers/ServerObserver.php index 92d1b8f9a..b7720ce52 100644 --- a/app/Observers/ServerObserver.php +++ b/app/Observers/ServerObserver.php @@ -25,11 +25,8 @@ namespace Pterodactyl\Observers; use Cache; -use Carbon; use Pterodactyl\Events; use Pterodactyl\Models\Server; -use Pterodactyl\Jobs\DeleteServer; -use Pterodactyl\Jobs\SuspendServer; use Pterodactyl\Notifications\ServerCreated; use Illuminate\Foundation\Bus\DispatchesJobs; @@ -40,7 +37,7 @@ class ServerObserver /** * Listen to the Server creating event. * - * @param Server $server The server model. + * @param \Pterodactyl\Models\Server $server * @return void */ public function creating(Server $server) @@ -51,7 +48,7 @@ class ServerObserver /** * Listen to the Server created event. * - * @param Server $server The server model. + * @param \Pterodactyl\Models\Server $server * @return void */ public function created(Server $server) @@ -72,37 +69,29 @@ class ServerObserver /** * Listen to the Server deleting event. * - * @param Server $server The server model. + * @param \Pterodactyl\Models\Server $server * @return void */ public function deleting(Server $server) { event(new Events\Server\Deleting($server)); - - $this->dispatch((new SuspendServer($server->id))->onQueue(env('QUEUE_HIGH', 'high'))); } /** * Listen to the Server deleted event. * - * @param Server $server The server model. + * @param \Pterodactyl\Models\Server $server * @return void */ public function deleted(Server $server) { event(new Events\Server\Deleted($server)); - - $this->dispatch( - (new DeleteServer($server->id)) - ->delay(Carbon::now()->addMinutes(env('APP_DELETE_MINUTES', 10))) - ->onQueue(env('QUEUE_STANDARD', 'standard')) - ); } /** * Listen to the Server saving event. * - * @param Server $server The server model. + * @param \Pterodactyl\Models\Server $server * @return void */ public function saving(Server $server) @@ -113,7 +102,7 @@ class ServerObserver /** * Listen to the Server saved event. * - * @param Server $server The server model. + * @param \Pterodactyl\Models\Server $server * @return void */ public function saved(Server $server) @@ -124,7 +113,7 @@ class ServerObserver /** * Listen to the Server updating event. * - * @param Server $server The server model. + * @param \Pterodactyl\Models\Server $server * @return void */ public function updating(Server $server) @@ -135,7 +124,7 @@ class ServerObserver /** * Listen to the Server saved event. * - * @param Server $server The server model. + * @param \Pterodactyl\Models\Server $server * @return void */ public function updated(Server $server) diff --git a/app/Observers/SubuserObserver.php b/app/Observers/SubuserObserver.php index b3d64d7d1..57eab2680 100644 --- a/app/Observers/SubuserObserver.php +++ b/app/Observers/SubuserObserver.php @@ -34,7 +34,7 @@ class SubuserObserver /** * Listen to the Subuser creating event. * - * @param Subuser $subuser The eloquent Subuser model. + * @param \Pterodactyl\Models\Subuser $subuser * @return void */ public function creating(Subuser $subuser) @@ -45,7 +45,7 @@ class SubuserObserver /** * Listen to the Subuser created event. * - * @param Subuser $subuser The eloquent Subuser model. + * @param \Pterodactyl\Models\Subuser $subuser * @return void */ public function created(Subuser $subuser) @@ -62,7 +62,7 @@ class SubuserObserver /** * Listen to the Subuser deleting event. * - * @param Subuser $subuser The eloquent Subuser model. + * @param \Pterodactyl\Models\Subuser $subuser * @return void */ public function deleting(Subuser $subuser) @@ -73,7 +73,7 @@ class SubuserObserver /** * Listen to the Subuser deleted event. * - * @param Subuser $subuser The eloquent Subuser model. + * @param \Pterodactyl\Models\Subuser $subuser * @return void */ public function deleted(Subuser $subuser) diff --git a/app/Observers/UserObserver.php b/app/Observers/UserObserver.php index 258174ffb..6caf8008d 100644 --- a/app/Observers/UserObserver.php +++ b/app/Observers/UserObserver.php @@ -34,7 +34,7 @@ class UserObserver /** * Listen to the User creating event. * - * @param User $user The eloquent User model. + * @param \Pterodactyl\Models\User $user * @return void */ public function creating(User $user) @@ -45,7 +45,7 @@ class UserObserver /** * Listen to the User created event. * - * @param User $user The eloquent User model. + * @param \Pterodactyl\Models\User $user * @return void */ public function created(User $user) @@ -63,7 +63,7 @@ class UserObserver /** * Listen to the User deleting event. * - * @param User $user The eloquent User model. + * @param \Pterodactyl\Models\User $user * @return void */ public function deleting(User $user) @@ -74,7 +74,7 @@ class UserObserver /** * Listen to the User deleted event. * - * @param User $user The eloquent User model. + * @param \Pterodactyl\Models\User $user * @return void */ public function deleted(User $user) diff --git a/app/Policies/APIKeyPolicy.php b/app/Policies/APIKeyPolicy.php new file mode 100644 index 000000000..95846b9e4 --- /dev/null +++ b/app/Policies/APIKeyPolicy.php @@ -0,0 +1,73 @@ +. + * + * 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\Policies; + +use Cache; +use Carbon; +use Pterodactyl\Models\User; +use Pterodactyl\Models\APIKey as Key; +use Pterodactyl\Models\APIPermission as Permission; + +class APIKeyPolicy +{ + /** + * Checks if the API key has permission to perform an action. + * + * @param \Pterodactyl\Models\User $user + * @param \Pterodactyl\Models\APIKey $key + * @param string $permission + * @return bool + */ + protected function checkPermission(User $user, Key $key, $permission) + { + // Non-administrative users cannot use administrative routes. + if (! starts_with($key, 'user.') && ! $user->isRootAdmin()) { + return false; + } + + // We don't tag this cache key with the user uuid because the key is already unique, + // and multiple users are not defiend for a single key. + $permissions = Cache::remember('APIKeyPolicy.' . $key->public, Carbon::now()->addSeconds(5), function () use ($key) { + return $key->permissions()->get()->transform(function ($item) { + return $item->permission; + })->values(); + }); + + return $permissions->search($permission, true) !== false; + } + + /** + * Determine if a user has permission to perform this action against the system. + * + * @param \Pterodactyl\Models\User $user + * @param string $permission + * @param \Pterodactyl\Models\APIKey $key + * @return bool + */ + public function before(User $user, $permission, Key $key) + { + return $this->checkPermission($user, $key, $permission); + } +} diff --git a/app/Policies/ServerPolicy.php b/app/Policies/ServerPolicy.php index 7079aaa19..56cd359e1 100644 --- a/app/Policies/ServerPolicy.php +++ b/app/Policies/ServerPolicy.php @@ -31,31 +31,17 @@ use Pterodactyl\Models\Server; class ServerPolicy { - /** - * Create a new policy instance. - * - * @return void - */ - public function __construct() - { - // - } - /** * Checks if the user has the given permission on/for the server. * * @param \Pterodactyl\Models\User $user * @param \Pterodactyl\Models\Server $server - * @param $permission + * @param string $permission * @return bool */ - private function checkPermission(User $user, Server $server, $permission) + protected function checkPermission(User $user, Server $server, $permission) { - if ($this->isOwner($user, $server)) { - return true; - } - - $permissions = Cache::remember('ServerPolicy.' . $user->uuid . $server->uuid, Carbon::now()->addSeconds(10), function () use ($user, $server) { + $permissions = Cache::remember('ServerPolicy.' . $user->uuid . $server->uuid, Carbon::now()->addSeconds(5), function () use ($user, $server) { return $user->permissions()->server($server)->get()->transform(function ($item) { return $item->permission; })->values(); @@ -65,485 +51,19 @@ class ServerPolicy } /** - * Determine if current user is the owner of a server. + * Runs before any of the functions are called. Used to determine if user is root admin, if so, ignore permissions. * * @param \Pterodactyl\Models\User $user + * @param string $ability * @param \Pterodactyl\Models\Server $server * @return bool */ - protected function isOwner(User $user, Server $server) + public function before(User $user, $ability, Server $server) { - return $server->owner_id === $user->id; - } - - /** - * Runs before any of the functions are called. Used to determine if user is root admin, if so, ignore permissions. - * - * @param \Pterodactyl\Models\User $user - * @param string $ability - * @return bool - */ - public function before(User $user, $ability) - { - if ($user->root_admin === 1) { + if ($user->isRootAdmin() || $server->owner_id === $user->id) { return true; } - } - /** - * Check if user has permission to control power for a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function power(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'power'); - } - - /** - * Check if user has permission to start a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function powerStart(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'power-start'); - } - - /** - * Check if user has permission to stop a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function powerStop(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'power-stop'); - } - - /** - * Check if user has permission to restart a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function powerRestart(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'power-restart'); - } - - /** - * Check if user has permission to kill a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function powerKill(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'power-kill'); - } - - /** - * Check if user has permission to run a command on a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function sendCommand(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'send-command'); - } - - /** - * Check if user has permission to list files on a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function listFiles(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'list-files'); - } - - /** - * Check if user has permission to edit files on a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function editFiles(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'edit-files'); - } - - /** - * Check if user has permission to save files on a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function saveFiles(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'save-files'); - } - - /** - * Check if user has permission to move and rename files and folders on a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function moveFiles(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'move-files'); - } - - /** - * Check if user has permission to copy folders and files on a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function copyFiles(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'copy-files'); - } - - /** - * Check if user has permission to compress files and folders on a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function compressFiles(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'compress-files'); - } - - /** - * Check if user has permission to decompress files on a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function decompressFiles(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'decompress-files'); - } - - /** - * Check if user has permission to add files to a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function createFiles(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'create-files'); - } - - /** - * Check if user has permission to upload files to a server. - * This permission relies on the user having the 'create-files' permission as well due to page authorization. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function uploadFiles(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'upload-files'); - } - - /** - * Check if user has permission to download files from a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function downloadFiles(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'download-files'); - } - - /** - * Check if user has permission to delete files from a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function deleteFiles(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'delete-files'); - } - - /** - * Check if user has permission to view subusers for the server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function listSubusers(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'list-subusers'); - } - - /** - * Check if user has permission to view specific subuser permissions. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function viewSubuser(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'view-subuser'); - } - - /** - * Check if user has permission to edit a subuser. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function editSubuser(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'edit-subuser'); - } - - /** - * Check if user has permission to delete a subuser. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function deleteSubuser(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'delete-subuser'); - } - - /** - * Check if user has permission to edit a subuser. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function createSubuser(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'create-subuser'); - } - - /** - * Check if user has permission to set the default connection for a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function setConnection(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'set-connection'); - } - - /** - * Check if user has permission to view the startup command used for a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function viewStartup(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'view-startup'); - } - - /** - * Check if user has permission to edit the startup command used for a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function editStartup(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'edit-startup'); - } - - /** - * Check if user has permission to view the SFTP information for a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function viewSftp(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'view-sftp'); - } - - /** - * Check if user has permission to reset the SFTP password for a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function resetSftp(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'reset-sftp'); - } - - /** - * Check if user has permission to view the SFTP password for a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function viewSftpPassword(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'view-sftp-password'); - } - - /** - * Check if user has permission to view databases for a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function viewDatabases(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'view-databases'); - } - - /** - * Check if user has permission to reset database passwords. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function resetDbPassword(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'reset-db-password'); - } - - /** - * Check if user has permission to view all tasks for a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function listTasks(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'list-tasks'); - } - - /** - * Check if user has permission to view a specific task for a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function viewTask(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'view-task'); - } - - /** - * Check if user has permission to view a toggle a task for a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function toggleTask(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'toggle-task'); - } - - /** - * Check if user has permission to queue a task for a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function queueTask(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'queue-task'); - } - - /** - * Check if user has permission to delete a specific task for a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function deleteTask(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'delete-task'); - } - - /** - * Check if user has permission to create a task for a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function createTask(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'create-task'); - } - - /** - * Check if user has permission to view server allocations. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function viewAllocation(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'view-allocation'); - } - - /** - * Check if user has permission to set the default allocation. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function setAllocation(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'set-allocation'); + return $this->checkPermission($user, $server, $ability); } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 5a9a0d925..dac07eb20 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -52,5 +52,9 @@ class AppServiceProvider extends ServiceProvider if ($this->app->environment() !== 'production') { $this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class); } + + if (config('pterodactyl.auth.notifications')) { + $this->app->register(\DaneEveritt\LoginNotifications\NotificationServiceProvider::class); + } } } diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 86c94d99f..e1401e844 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -2,7 +2,6 @@ namespace Pterodactyl\Providers; -use Illuminate\Contracts\Auth\Access\Gate as GateContract; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; class AuthServiceProvider extends ServiceProvider @@ -14,6 +13,7 @@ class AuthServiceProvider extends ServiceProvider */ protected $policies = [ 'Pterodactyl\Models\Server' => 'Pterodactyl\Policies\ServerPolicy', + 'Pterodactyl\Models\APIKey' => 'Pterodactyl\Policies\APIKeyPolicy', ]; /** @@ -22,10 +22,8 @@ class AuthServiceProvider extends ServiceProvider * @param \Illuminate\Contracts\Auth\Access\Gate $gate * @return void */ - public function boot(GateContract $gate) + public function boot() { - parent::registerPolicies($gate); - - // + $this->registerPolicies(); } } diff --git a/app/Providers/BroadcastServiceProvider.php b/app/Providers/BroadcastServiceProvider.php index 1dcf8d287..e61e610d5 100644 --- a/app/Providers/BroadcastServiceProvider.php +++ b/app/Providers/BroadcastServiceProvider.php @@ -1,6 +1,6 @@ remember('ApiKeyMacro.' . $parts[0], Carbon::now()->addMinutes(15), function () use ($parts) { + return APIKey::where('public', $parts[0])->first(); + }); + } + + return false; + }); + + Request::macro('apiKeyHasPermission', function ($permission) { + $key = Request::apiKey(); + if (! $key) { + return false; + } + + return Request::user()->can($permission, $key); + }); } } diff --git a/app/Providers/PhraseAppTranslationProvider.php b/app/Providers/PhraseAppTranslationProvider.php index e6ba42116..840234917 100644 --- a/app/Providers/PhraseAppTranslationProvider.php +++ b/app/Providers/PhraseAppTranslationProvider.php @@ -47,7 +47,7 @@ class PhraseAppTranslationProvider extends TranslationServiceProvider // configuration so we can easily get both of these values from there. $locale = $app['config']['app.locale']; - if ($app['config']['app.phrase_in_context']) { + if ($app['config']['pterodactyl.lang.in_context']) { $trans = new PhraseAppTranslator($loader, $locale); } else { $trans = new IlluminateTranslator($loader, $locale); diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index f25c59471..d129489af 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -19,13 +19,10 @@ class RouteServiceProvider extends ServiceProvider /** * Define your route model bindings, pattern filters, etc. * - * @param \Illuminate\Routing\Router $router * @return void */ public function boot() { - // - parent::boot(); } @@ -36,10 +33,36 @@ class RouteServiceProvider extends ServiceProvider */ public function map() { - Route::group(['namespace' => $this->namespace], function ($router) { - foreach (glob(app_path('Http//Routes') . '/*.php') as $file) { - $this->app->make('Pterodactyl\\Http\\Routes\\' . basename($file, '.php'))->map($router); - } - }); + Route::middleware(['api'])->prefix('/api/user') + ->namespace($this->namespace . '\API\User') + ->group(base_path('routes/api.php')); + + Route::middleware(['api'])->prefix('/api/admin') + ->namespace($this->namespace . '\API\Admin') + ->group(base_path('routes/api-admin.php')); + + Route::middleware(['web', 'auth', 'csrf']) + ->namespace($this->namespace . '\Base') + ->group(base_path('routes/base.php')); + + Route::middleware(['web', 'auth', 'admin', 'csrf'])->prefix('/admin') + ->namespace($this->namespace . '\Admin') + ->group(base_path('routes/admin.php')); + + Route::middleware(['web', 'csrf'])->prefix('/auth') + ->namespace($this->namespace . '\Auth') + ->group(base_path('routes/auth.php')); + + Route::middleware(['web', 'auth', 'server', 'csrf'])->prefix('/server/{server}') + ->namespace($this->namespace . '\Server') + ->group(base_path('routes/server.php')); + + Route::middleware(['web'])->prefix('/remote') + ->namespace($this->namespace . '\Remote') + ->group(base_path('routes/remote.php')); + + Route::middleware(['web', 'daemon'])->prefix('/daemon') + ->namespace($this->namespace . '\Daemon') + ->group(base_path('routes/daemon.php')); } } diff --git a/app/Repositories/APIRepository.php b/app/Repositories/APIRepository.php index dc856e188..10af25155 100644 --- a/app/Repositories/APIRepository.php +++ b/app/Repositories/APIRepository.php @@ -29,99 +29,58 @@ use Auth; use Crypt; use Validator; use IPTools\Network; -use Pterodactyl\Models; +use Pterodactyl\Models\User; +use Pterodactyl\Models\APIKey as Key; use Pterodactyl\Exceptions\DisplayException; +use Pterodactyl\Models\APIPermission as Permission; use Pterodactyl\Exceptions\DisplayValidationException; class APIRepository { - /** - * Valid API permissions. - * @var array - */ - protected $permissions = [ - 'admin' => [ - '*', - - // User Management Routes - 'users.list', - 'users.create', - 'users.view', - 'users.update', - 'users.delete', - - // Server Manaement Routes - 'servers.list', - 'servers.create', - 'servers.view', - 'servers.config', - 'servers.build', - 'servers.suspend', - 'servers.unsuspend', - 'servers.delete', - - // Node Management Routes - 'nodes.list', - 'nodes.view', - 'nodes.create', - 'nodes.allocations', - 'nodes.delete', - - // Service Routes - 'services.list', - 'services.view', - - // Location Routes - 'locations.list', - - ], - 'user' => [ - '*', - - // Informational - 'me', - - // Server Control - 'server', - 'server.power', - ], - ]; - /** * Holder for listing of allowed IPs when creating a new key. + * * @var array */ protected $allowed = []; + /** + * The eloquent model for a user. + * + * @var \Pterodactyl\Models\User + */ protected $user; /** - * Constructor. + * Constructor for API Repository. + * + * @param null|\Pterodactyl\Models\User $user + * @return void */ - public function __construct(Models\User $user = null) + public function __construct(User $user = null) { $this->user = is_null($user) ? Auth::user() : $user; if (is_null($this->user)) { - throw new \Exception('Cannot access API Repository without passing a user to constructor.'); + throw new \Exception('Unable to initialize user for API repository instance.'); } } /** * Create a New API Keypair on the system. * - * @param array $data An array with a permissions and allowed_ips key. + * @param array $data + * @return string * - * @throws Pterodactyl\Exceptions\DisplayException if there was an error that can be safely displayed to end-users. - * @throws Pterodactyl\Exceptions\DisplayValidationException if there was a validation error. - * - * @return string Returns the generated secret token. + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\DisplayValidationException */ public function create(array $data) { $validator = Validator::make($data, [ 'memo' => 'string|max:500', + 'allowed_ips' => 'sometimes|string', 'permissions' => 'sometimes|required|array', - 'adminPermissions' => 'sometimes|required|array', + 'admin_permissions' => 'sometimes|required|array', ]); $validator->after(function ($validator) use ($data) { @@ -147,8 +106,7 @@ class APIRepository DB::beginTransaction(); try { $secretKey = str_random(16) . '.' . str_random(7) . '.' . str_random(7); - $key = new Models\APIKey; - $key->fill([ + $key = Key::create([ 'user_id' => $this->user->id, 'public' => str_random(16), 'secret' => Crypt::encrypt($secretKey), @@ -156,44 +114,61 @@ class APIRepository 'memo' => $data['memo'], 'expires_at' => null, ]); - $key->save(); $totalPermissions = 0; + $pNodes = Permission::permissions(); + if (isset($data['permissions'])) { - foreach ($data['permissions'] as $permNode) { - if (! strpos($permNode, ':')) { + foreach ($data['permissions'] as $permission) { + $parts = explode('-', $permission); + + if (count($parts) !== 2) { continue; } - list($toss, $permission) = explode(':', $permNode); - if (in_array($permission, $this->permissions['user'])) { - $totalPermissions++; - $model = new Models\APIPermission; - $model->fill([ - 'key_id' => $key->id, - 'permission' => 'api.user.' . $permission, - ]); - $model->save(); + list($block, $search) = $parts; + + if (! array_key_exists($block, $pNodes['_user'])) { + continue; } + + if (! in_array($search, $pNodes['_user'][$block])) { + continue; + } + + $totalPermissions++; + Permission::create([ + 'key_id' => $key->id, + 'permission' => 'user.' . $permission, + ]); } } - if ($this->user->isRootAdmin() && isset($data['adminPermissions'])) { - foreach ($data['adminPermissions'] as $permNode) { - if (! strpos($permNode, ':')) { + if ($this->user->isRootAdmin() && isset($data['admin_permissions'])) { + unset($pNodes['_user']); + + foreach ($data['admin_permissions'] as $permission) { + $parts = explode('-', $permission); + + if (count($parts) !== 2) { continue; } - list($toss, $permission) = explode(':', $permNode); - if (in_array($permission, $this->permissions['admin'])) { - $totalPermissions++; - $model = new Models\APIPermission; - $model->fill([ - 'key_id' => $key->id, - 'permission' => 'api.admin.' . $permission, - ]); - $model->save(); + list($block, $search) = $parts; + + if (! array_key_exists($block, $pNodes)) { + continue; } + + if (! in_array($search, $pNodes[$block])) { + continue; + } + + $totalPermissions++; + Permission::create([ + 'key_id' => $key->id, + 'permission' => $permission, + ]); } } @@ -213,28 +188,20 @@ class APIRepository /** * Revokes an API key and associated permissions. * - * @param string $key The public key. - * - * @throws Illuminate\Database\Eloquent\ModelNotFoundException - * + * @param string $key * @return void + * + * @throws \Illuminate\Database\Eloquent\ModelNotFoundException */ public function revoke($key) { - DB::beginTransaction(); - - try { - $model = Models\APIKey::with('permissions')->where('public', $key)->where('user_id', $this->user->id)->firstOrFail(); + DB::transaction(function () use ($key) { + $model = Key::with('permissions')->where('public', $key)->where('user_id', $this->user->id)->firstOrFail(); foreach ($model->permissions as &$permission) { $permission->delete(); } $model->delete(); - - DB::commit(); - } catch (\Exception $ex) { - DB::rollBack(); - throw $ex; - } + }); } } diff --git a/app/Repositories/Daemon/CommandRepository.php b/app/Repositories/Daemon/CommandRepository.php index 76ac93178..beb9e8530 100644 --- a/app/Repositories/Daemon/CommandRepository.php +++ b/app/Repositories/Daemon/CommandRepository.php @@ -24,44 +24,67 @@ namespace Pterodactyl\Repositories\Daemon; -use Pterodactyl\Models; -use GuzzleHttp\Exception\RequestException; +use Pterodactyl\Models\User; +use Pterodactyl\Models\Server; +use GuzzleHttp\Exception\ConnectException; use Pterodactyl\Exceptions\DisplayException; class CommandRepository { + /** + * The Eloquent Model associated with the requested server. + * + * @var \Pterodactyl\Models\Server + */ protected $server; - public function __construct($server) + /** + * The Eloquent Model associated with the user to run the request as. + * + * @var \Pterodactyl\Models\User|null + */ + protected $user; + + /** + * Constuctor for repository. + * + * @param \Pterodactyl\Models\Server $server + * @param \Pterodactyl\Models\User|null $user + * @return void + */ + public function __construct(Server $server, User $user = null) { - $this->server = ($server instanceof Models\Server) ? $server : Models\Server::findOrFail($server); + $this->server = $server; + $this->user = $user; } /** - * [send description]. - * @param string $command - * @return bool - * @throws DisplayException - * @throws RequestException + * Sends a command to the daemon. + * + * @param string $command + * @return string + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \GuzzleHttp\Exception\RequestException */ public function send($command) { // We don't use the user's specific daemon secret here since we // are assuming that a call to this function has been validated. - // Additionally not all calls to this will be from a logged in user. - // (e.g. task queue or API) try { - $response = $this->server->node->guzzleClient([ - 'X-Access-Token' => $this->server->daemonSecret, - 'X-Access-Server' => $this->server->uuid, - ])->request('POST', '/server/command', ['json' => ['command' => $command]]); + $response = $this->server->guzzleClient($this->user)->request('POST', '/server/command', [ + 'http_errors' => false, + 'json' => [ + 'command' => $command, + ], + ]); if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) { - throw new DisplayException('Command sending responded with a non-200 error code.'); + throw new DisplayException('Command sending responded with a non-200 error code (HTTP/' . $response->getStatusCode() . ').'); } return $response->getBody(); - } catch (\Exception $ex) { + } catch (ConnectException $ex) { throw $ex; } } diff --git a/app/Repositories/Daemon/FileRepository.php b/app/Repositories/Daemon/FileRepository.php index 5fc2245fb..cc415793a 100644 --- a/app/Repositories/Daemon/FileRepository.php +++ b/app/Repositories/Daemon/FileRepository.php @@ -35,14 +35,15 @@ class FileRepository /** * The Eloquent Model associated with the requested server. * - * @var \Illuminate\Database\Eloquent\Model + * @var \Pterodactyl\Models\Server */ protected $server; /** * Constructor. * - * @param string $server The server Short UUID + * @param string $uuid + * @return void */ public function __construct($uuid) { @@ -52,8 +53,11 @@ class FileRepository /** * Get the contents of a requested file for the server. * - * @param string $file + * @param string $file * @return array + * + * @throws \GuzzleHttp\Exception\RequestException + * @throws \Pterodactyl\Exceptions\DisplayException */ public function returnFileContents($file) { @@ -95,9 +99,12 @@ class FileRepository /** * Save the contents of a requested file on the daemon. * - * @param string $file - * @param string $content + * @param string $file + * @param string $content * @return bool + * + * @throws \GuzzleHttp\Exception\RequestException + * @throws \Pterodactyl\Exceptions\DisplayException */ public function saveFileContents($file, $content) { @@ -125,8 +132,11 @@ class FileRepository /** * Returns a listing of all files and folders within a specified directory on the daemon. * - * @param string $directory + * @param string $directory * @return object + * + * @throws \GuzzleHttp\Exception\RequestException + * @throws \Pterodactyl\Exceptions\DisplayException */ public function returnDirectoryListing($directory) { diff --git a/app/Repositories/Daemon/PowerRepository.php b/app/Repositories/Daemon/PowerRepository.php index b31c0cda0..925379096 100644 --- a/app/Repositories/Daemon/PowerRepository.php +++ b/app/Repositories/Daemon/PowerRepository.php @@ -24,55 +24,104 @@ namespace Pterodactyl\Repositories\Daemon; -use Pterodactyl\Models; +use Pterodactyl\Models\User; +use Pterodactyl\Models\Server; +use GuzzleHttp\Exception\ConnectException; use Pterodactyl\Exceptions\DisplayException; class PowerRepository { + /** + * The Eloquent Model associated with the requested server. + * + * @var \Pterodactyl\Models\Server + */ protected $server; - public function __construct($server) + /** + * The Eloquent Model associated with the user to run the request as. + * + * @var \Pterodactyl\Models\User|null + */ + protected $user; + + /** + * Constuctor for repository. + * + * @param \Pterodactyl\Models\Server $server + * @param \Pterodactyl\Models\User|null $user + * @return void + */ + public function __construct(Server $server, User $user = null) { - $this->server = ($server instanceof Models\Server) ? $server : Models\Server::findOrFail($server); + $this->server = $server; + $this->user = $user; } + /** + * Sends a power option to the daemon. + * + * @param string $action + * @return string + * + * @throws \GuzzleHttp\Exception\RequestException + * @throws \Pterodactyl\Exceptions\DisplayException + */ public function do($action) { - // We don't use the user's specific daemon secret here since we - // are assuming that a call to this function has been validated. - // Additionally not all calls to this will be from a logged in user. - // (e.g. task queue or API) try { - $response = $this->server->node->guzzleClient([ - 'X-Access-Token' => $this->server->daemonSecret, - 'X-Access-Server' => $this->server->uuid, - ])->request('PUT', '/server/power', ['json' => ['action' => $action]]); + $response = $this->server->guzzleClient($this->user)->request('PUT', '/server/power', [ + 'http_errors' => false, + 'json' => [ + 'action' => $action, + ], + ]); if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) { - throw new DisplayException('Power status responded with a non-200 error code.'); + throw new DisplayException('Power toggle endpoint responded with a non-200 error code (HTTP/' . $response->getStatusCode() . ').'); } return $response->getBody(); - } catch (\Exception $ex) { + } catch (ConnectException $ex) { throw $ex; } } + /** + * Starts a server. + * + * @return void + */ public function start() { $this->do('start'); } + /** + * Stops a server. + * + * @return void + */ public function stop() { $this->do('stop'); } + /** + * Restarts a server. + * + * @return void + */ public function restart() { $this->do('restart'); } + /** + * Kills a server. + * + * @return void + */ public function kill() { $this->do('kill'); diff --git a/app/Repositories/DatabaseRepository.php b/app/Repositories/DatabaseRepository.php index 346d33376..27a096ddf 100644 --- a/app/Repositories/DatabaseRepository.php +++ b/app/Repositories/DatabaseRepository.php @@ -39,8 +39,8 @@ class DatabaseRepository /** * Adds a new database to a specified database host server. * - * @param int $id - * @param array $data + * @param int $id + * @param array $data * @return \Pterodactyl\Models\Database * * @throws \Pterodactyl\Exceptions\DisplayException @@ -84,18 +84,9 @@ class DatabaseRepository throw $ex; } - Config::set('database.connections.dynamic', [ - 'driver' => 'mysql', - 'host' => $host->host, - 'port' => $host->port, - 'database' => 'mysql', - 'username' => $host->username, - 'password' => Crypt::decrypt($host->password), - 'charset' => 'utf8', - 'collation' => 'utf8_unicode_ci', - ]); - try { + $host->setDynamicConnection(); + DB::connection('dynamic')->statement(sprintf('CREATE DATABASE IF NOT EXISTS `%s`', $database->database)); DB::connection('dynamic')->statement(sprintf( 'CREATE USER `%s`@`%s` IDENTIFIED BY \'%s\'', @@ -128,8 +119,8 @@ class DatabaseRepository /** * Updates the password for a given database. * - * @param int $id - * @param string $password + * @param int $id + * @param string $password * @return void * * @todo Fix logic behind resetting passwords. @@ -137,21 +128,11 @@ class DatabaseRepository public function password($id, $password) { $database = Database::with('host')->findOrFail($id); + $database->host->setDynamicConnection(); DB::transaction(function () use ($database, $password) { $database->password = Crypt::encrypt($password); - Config::set('database.connections.dynamic', [ - 'driver' => 'mysql', - 'host' => $database->host->host, - 'port' => $database->host->port, - 'database' => 'mysql', - 'username' => $database->host->username, - 'password' => Crypt::decrypt($database->host->password), - 'charset' => 'utf8', - 'collation' => 'utf8_unicode_ci', - ]); - // We have to do the whole delete user, create user thing rather than // SET PASSWORD ... because MariaDB and PHP statements ends up inserting // a corrupted password. A way around this is strtoupper(sha1(sha1($password, true))) @@ -174,25 +155,15 @@ class DatabaseRepository /** * Drops a database from the associated database host. * - * @param int $id + * @param int $id * @return void */ public function drop($id) { $database = Database::with('host')->findOrFail($id); + $database->host->setDynamicConnection(); DB::transaction(function () use ($database) { - Config::set('database.connections.dynamic', [ - 'driver' => 'mysql', - 'host' => $database->host->host, - 'port' => $database->host->port, - 'database' => 'mysql', - 'username' => $database->host->username, - 'password' => Crypt::decrypt($database->host->password), - 'charset' => 'utf8', - 'collation' => 'utf8_unicode_ci', - ]); - DB::connection('dynamic')->statement(sprintf('DROP DATABASE IF EXISTS `%s`', $database->database)); DB::connection('dynamic')->statement(sprintf('DROP USER IF EXISTS `%s`@`%s`', $database->username, $database->remote)); DB::connection('dynamic')->statement('FLUSH PRIVILEGES'); @@ -204,7 +175,7 @@ class DatabaseRepository /** * Deletes a database host from the system if it has no associated databases. * - * @param int $server + * @param int $id * @return void * * @throws \Pterodactyl\Exceptions\DisplayException @@ -223,7 +194,7 @@ class DatabaseRepository /** * Adds a new Database Host to the system. * - * @param array $data + * @param array $data * @return \Pterodactyl\Models\DatabaseHost * * @throws \Pterodactyl\Exceptions\DisplayValidationException @@ -248,20 +219,6 @@ class DatabaseRepository } return DB::transaction(function () use ($data) { - Config::set('database.connections.dynamic', [ - 'driver' => 'mysql', - 'host' => $data['host'], - 'port' => $data['port'], - 'database' => 'mysql', - 'username' => $data['username'], - 'password' => $data['password'], - 'charset' => 'utf8', - 'collation' => 'utf8_unicode_ci', - ]); - - // Allows us to check that we can connect to things. - DB::connection('dynamic')->select('SELECT 1 FROM dual'); - $host = new DatabaseHost; $host->password = Crypt::encrypt($data['password']); @@ -274,6 +231,10 @@ class DatabaseRepository 'node_id' => (isset($data['node_id'])) ? $data['node_id'] : null, ])->save(); + // Allows us to check that we can connect to things. + $host->setDynamicConnection(); + DB::connection('dynamic')->select('SELECT 1 FROM dual'); + return $host; }); } @@ -281,8 +242,8 @@ class DatabaseRepository /** * Updates a Database Host on the system. * - * @param int $id - * @param array $data + * @param int $id + * @param array $data * @return \Pterodactyl\Models\DatabaseHost * * @throws \Pterodactyl\Exceptions\DisplayValidationException @@ -315,18 +276,7 @@ class DatabaseRepository $host->fill($data)->save(); // Check that we can still connect with these details. - Config::set('database.connections.dynamic', [ - 'driver' => 'mysql', - 'host' => $host->host, - 'port' => $host->port, - 'database' => 'mysql', - 'username' => $host->username, - 'password' => Crypt::decrypt($host->password), - 'charset' => 'utf8', - 'collation' => 'utf8_unicode_ci', - ]); - - // Allows us to check that we can connect to things. + $host->setDynamicConnection(); DB::connection('dynamic')->select('SELECT 1 FROM dual'); return $host; diff --git a/app/Repositories/HelperRepository.php b/app/Repositories/HelperRepository.php index 1e3248173..204480ec2 100644 --- a/app/Repositories/HelperRepository.php +++ b/app/Repositories/HelperRepository.php @@ -28,6 +28,7 @@ class HelperRepository { /** * Listing of editable files in the control panel. + * * @var array */ protected static $editable = [ @@ -44,17 +45,13 @@ class HelperRepository 'inode/x-empty', ]; - public function __construct() - { - // - } - /** * Converts from bytes to the largest possible size that is still readable. * - * @param int $bytes - * @param int $decimals + * @param int $bytes + * @param int $decimals * @return string + * @deprecated */ public static function bytesToHuman($bytes, $decimals = 2) { @@ -64,6 +61,11 @@ class HelperRepository return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . ' ' . $sz[$factor]; } + /** + * Returns array of editable files. + * + * @return array + */ public static function editableFiles() { return self::$editable; diff --git a/app/Repositories/LocationRepository.php b/app/Repositories/LocationRepository.php index d3efb5d25..5f08cfc17 100644 --- a/app/Repositories/LocationRepository.php +++ b/app/Repositories/LocationRepository.php @@ -35,8 +35,9 @@ class LocationRepository * Creates a new location on the system. * * @param array $data - * @throws \Pterodactyl\Exceptions\DisplayValidationException * @return \Pterodactyl\Models\Location + * + * @throws \Pterodactyl\Exceptions\DisplayValidationException */ public function create(array $data) { @@ -58,11 +59,11 @@ class LocationRepository /** * Modifies a location. * - * @param int $id - * @param array $data + * @param int $id + * @param array $data * @return \Pterodactyl\Models\Location * - * @throws Pterodactyl\Exceptions\DisplayValidationException + * @throws \Pterodactyl\Exceptions\DisplayValidationException */ public function update($id, array $data) { @@ -85,10 +86,10 @@ class LocationRepository /** * Deletes a location from the system. * - * @param int $id + * @param int $id * @return void * - * @throws Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\DisplayException */ public function delete($id) { diff --git a/app/Repositories/NodeRepository.php b/app/Repositories/NodeRepository.php index 0c2c455c1..0b361c3d5 100644 --- a/app/Repositories/NodeRepository.php +++ b/app/Repositories/NodeRepository.php @@ -34,11 +34,15 @@ use Pterodactyl\Exceptions\DisplayValidationException; class NodeRepository { - public function __construct() - { - // - } - + /** + * Creates a new node on the system. + * + * @param array $data + * @return \Pterodactyl\Models\Node + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\DisplayValidationException + */ public function create(array $data) { // Validate Fields @@ -84,6 +88,16 @@ class NodeRepository return Models\Node::create($data); } + /** + * Updates a node on the system. + * + * @param int $id + * @param array $data + * @return \Pterodactyl\Models\Node + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\DisplayValidationException + */ public function update($id, array $data) { $node = Models\Node::findOrFail($id); @@ -179,8 +193,13 @@ class NodeRepository /** * Adds allocations to a provided node. - * @param int $id - * @param array $data + * + * @param int $id + * @param array $data + * @return void + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\DisplayValidationException */ public function addAllocations($id, array $data) { @@ -203,7 +222,7 @@ class NodeRepository } } - DB::transaction(function () use ($parsed, $node, $data) { + DB::transaction(function () use ($data, $node) { foreach (Network::parse(gethostbyname($data['allocation_ip'])) as $ip) { foreach ($data['allocation_ports'] as $port) { // Determine if this is a valid single port, or a valid port range. @@ -243,6 +262,14 @@ class NodeRepository }); } + /** + * Deletes a node on the system. + * + * @param int $id + * @return void + * + * @throws \Pterodactyl\Exceptions\DisplayException + */ public function delete($id) { $node = Models\Node::withCount('servers')->findOrFail($id); @@ -250,13 +277,9 @@ class NodeRepository throw new DisplayException('You cannot delete a node with servers currently attached to it.'); } - DB::beginTransaction(); - - try { + DB::transaction(function () use ($node) { // Unlink Database Servers - Models\DatabaseServer::where('linked_node', $node->id)->update([ - 'linked_node' => null, - ]); + Models\DatabaseHost::where('node_id', $node->id)->update(['node_id' => null]); // Delete Allocations Models\Allocation::where('node_id', $node->id)->delete(); @@ -266,11 +289,6 @@ class NodeRepository // Delete Node $node->delete(); - - DB::commit(); - } catch (\Exception $ex) { - DB::rollback(); - throw $ex; - } + }); } } diff --git a/app/Repositories/OptionRepository.php b/app/Repositories/OptionRepository.php index 8169c9d60..cc502a75f 100644 --- a/app/Repositories/OptionRepository.php +++ b/app/Repositories/OptionRepository.php @@ -73,7 +73,7 @@ class OptionRepository /** * Deletes a service option from the system. * - * @param int $id + * @param int $id * @return void * * @throws \Pterodactyl\Exceptions\DisplayException diff --git a/app/Repositories/PackRepository.php b/app/Repositories/PackRepository.php index fdc5fbd5d..0a8854465 100644 --- a/app/Repositories/PackRepository.php +++ b/app/Repositories/PackRepository.php @@ -38,7 +38,7 @@ class PackRepository /** * Creates a new pack on the system. * - * @param array $data + * @param array $data * @return \Pterodactyl\Models\Pack * * @throws \Pterodactyl\Exceptions\DisplayException @@ -101,7 +101,7 @@ class PackRepository /** * Creates a new pack on the system given a template file. * - * @param array $data + * @param array $data * @return \Pterodactyl\Models\Pack * * @throws \Pterodactyl\Exceptions\DisplayException @@ -173,8 +173,8 @@ class PackRepository /** * Updates a pack on the system. * - * @param int $id - * @param array $data + * @param int $id + * @param array $data * @return \Pterodactyl\Models\Pack * * @throws \Pterodactyl\Exceptions\DisplayException @@ -225,7 +225,7 @@ class PackRepository */ public function delete($id) { - $pack = Models\Pack::withCount('servers')->findOrFail($id); + $pack = Pack::withCount('servers')->findOrFail($id); if ($pack->servers_count > 0) { throw new DisplayException('Cannot delete a pack from the system if servers are assocaited with it.'); diff --git a/app/Repositories/ServerRepository.php b/app/Repositories/ServerRepository.php index 223659fcd..674afa670 100644 --- a/app/Repositories/ServerRepository.php +++ b/app/Repositories/ServerRepository.php @@ -25,11 +25,11 @@ namespace Pterodactyl\Repositories; use DB; -use Log; use Crypt; use Validator; use Pterodactyl\Models; use Pterodactyl\Services\UuidService; +use GuzzleHttp\Exception\ClientException; use GuzzleHttp\Exception\TransferException; use Pterodactyl\Services\DeploymentService; use Pterodactyl\Exceptions\DisplayException; @@ -37,21 +37,21 @@ use Pterodactyl\Exceptions\DisplayValidationException; class ServerRepository { + /** + * An array of daemon permission to assign to this server. + * + * @var array + */ protected $daemonPermissions = [ 's:*', ]; - public function __construct() - { - // - } - /** * Generates a SFTP username for a server given a server name. * format: mumble_67c7a4b0. * - * @param string $name - * @param string $identifier + * @param string $name + * @param null|string $identifier * @return string */ protected function generateSFTPUsername($name, $identifier = null) @@ -75,8 +75,12 @@ class ServerRepository /** * Adds a new server to the system. - * @param array $data An array of data descriptors for creating the server. These should align to the columns in the database. - * @return int + * + * @param array $data + * @return \Pterodactyl\Models\Server + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\DisplayValidationException */ public function create(array $data) { @@ -85,6 +89,7 @@ class ServerRepository $validator = Validator::make($data, [ 'user_id' => 'required|exists:users,id', 'name' => 'required|regex:/^([\w .-]{1,200})$/', + 'description' => 'sometimes|nullable|string', 'memory' => 'required|numeric|min:0', 'swap' => 'required|numeric|min:-1', 'io' => 'required|numeric|min:10|max:1000', @@ -96,7 +101,7 @@ class ServerRepository 'pack_id' => 'sometimes|nullable|numeric|min:0', 'custom_container' => 'string', 'startup' => 'string', - 'auto_deploy' => 'sometimes|boolean', + 'auto_deploy' => 'sometimes|required|accepted', 'custom_id' => 'sometimes|required|numeric|unique:servers,id', ]); @@ -243,6 +248,7 @@ class ServerRepository 'uuidShort' => $genShortUuid, 'node_id' => $node->id, 'name' => $data['name'], + 'description' => $data['description'], 'suspended' => 0, 'owner_id' => $user->id, 'memory' => $data['memory'], @@ -325,15 +331,13 @@ class ServerRepository (string) $server->daemonSecret => $this->daemonPermissions, ], 'rebuild' => false, + 'start_on_completion' => isset($data['start_on_completion']), ], ]); DB::commit(); return $server; - } catch (TransferException $ex) { - DB::rollBack(); - throw new DisplayException('There was an error while attempting to connect to the daemon to add this server.', $ex); } catch (\Exception $ex) { DB::rollBack(); throw $ex; @@ -341,10 +345,14 @@ class ServerRepository } /** - * [updateDetails description]. - * @param int $id - * @param array $data - * @return bool + * Update the details for a server. + * + * @param int $id + * @param array $data + * @return \Pterodactyl\Models\Server + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\DisplayValidationException */ public function updateDetails($id, array $data) { @@ -355,6 +363,7 @@ class ServerRepository $validator = Validator::make($data, [ 'owner_id' => 'sometimes|required|integer|exists:users,id', 'name' => 'sometimes|required|regex:([\w .-]{1,200})', + 'description' => 'sometimes|required|string', 'reset_token' => 'sometimes|required|accepted', ]); @@ -376,24 +385,12 @@ class ServerRepository $resetDaemonKey = true; } - // Update Server Owner if it was passed. - 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. - if (isset($data['name'])) { - $server->name = $data['name']; - } - // Save our changes - $server->save(); + $server->fill($data)->save(); // Do we need to update? If not, return successful. if (! $resetDaemonKey) { - DB::commit(); - - return true; + return DB::commit(); } $res = $server->node->guzzleClient([ @@ -412,22 +409,24 @@ class ServerRepository if ($res->getStatusCode() === 204) { DB::commit(); - return true; + return $server; } else { throw new DisplayException('Daemon returned a a non HTTP/204 error code. HTTP/' + $res->getStatusCode()); } } catch (\Exception $ex) { DB::rollBack(); - Log::error($ex); - throw new DisplayException('An error occured while attempting to update this server\'s information.'); + throw $ex; } } /** - * [updateContainer description]. - * @param int $id - * @param array $data - * @return bool + * Update the container for a server. + * + * @param int $id + * @param array $data + * @return \Pterodactyl\Models\Server + * + * @throws \Pterodactyl\Exceptions\DisplayValidationException */ public function updateContainer($id, array $data) { @@ -461,10 +460,7 @@ class ServerRepository DB::commit(); - return true; - } catch (TransferException $ex) { - DB::rollBack(); - throw new DisplayException('A TransferException occured while attempting to update the container image. Is the daemon online? This error has been logged.', $ex); + return $server; } catch (\Exception $ex) { DB::rollBack(); throw $ex; @@ -472,10 +468,14 @@ class ServerRepository } /** - * [changeBuild description]. - * @param int $id - * @param array $data - * @return bool + * Update the build details for a server. + * + * @param int $id + * @param array $data + * @return \Pterodactyl\Models\Server + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\DisplayValidationException */ public function changeBuild($id, array $data) { @@ -518,23 +518,7 @@ class ServerRepository } $newPorts = false; - // Remove Assignments - if (isset($data['remove_allocations'])) { - foreach ($data['remove_allocations'] as $allocation) { - // Can't remove the assigned IP/Port combo - if ((int) $allocation === $server->allocation_id) { - continue; - } - - $newPorts = true; - Models\Allocation::where('id', $allocation)->where('server_id', $server->id)->update([ - 'server_id' => null, - ]); - } - - $server->load('allocations'); - } - + $firstNewAllocation = null; // Add Assignments if (isset($data['add_allocations'])) { foreach ($data['add_allocations'] as $allocation) { @@ -544,6 +528,7 @@ class ServerRepository } $newPorts = true; + $firstNewAllocation = (is_null($firstNewAllocation)) ? $model->id : $firstNewAllocation; $model->update([ 'server_id' => $server->id, ]); @@ -552,6 +537,29 @@ class ServerRepository $server->load('allocations'); } + // Remove Assignments + if (isset($data['remove_allocations'])) { + foreach ($data['remove_allocations'] as $allocation) { + // Can't remove the assigned IP/Port combo + if ((int) $allocation === $server->allocation_id) { + // No New Allocation + if (is_null($firstNewAllocation)) { + continue; + } + + // New Allocation, set as the default. + $server->allocation_id = $firstNewAllocation; + } + + $newPorts = true; + Models\Allocation::where('id', $allocation)->where('server_id', $server->id)->update([ + 'server_id' => null, + ]); + } + + $server->load('allocations'); + } + if ($newPorts) { $newBuild['ports|overwrite'] = $server->allocations->groupBy('ip')->map(function ($item) { return $item->pluck('port'); @@ -605,15 +613,24 @@ class ServerRepository DB::commit(); return $server; - } catch (TransferException $ex) { - DB::rollBack(); - 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; } } + /** + * Update the startup details for a server. + * + * @param int $id + * @param array $data + * @param bool $admin + * @return void + * + * @throws \GuzzleHttp\Exception\RequestException + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\DisplayValidationException + */ public function updateStartup($id, array $data, $admin = false) { $server = Models\Server::with('variables', 'option.variables')->findOrFail($id); @@ -691,34 +708,18 @@ class ServerRepository }); } - public function queueDeletion($id, $force = false) - { - $server = Models\Server::findOrFail($id); - DB::beginTransaction(); - - try { - if ($force) { - $server->installed = 3; - $server->save(); - } - $server->delete(); - - return DB::commit(); - } catch (\Exception $ex) { - DB::rollBack(); - throw $ex; - } - } - + /** + * Delete a server from the system permanetly. + * + * @param int $id + * @param bool $force + * @return void + * + * @throws \Pterodactyl\Exceptions\DisplayException + */ public function delete($id, $force = false) { - $server = Models\Server::withTrashed()->with('node', 'allocations', 'variables')->findOrFail($id); - - // Handle server being restored previously or - // an accidental queue. - if (! $server->trashed()) { - return; - } + $server = Models\Server::with('node', 'allocations', 'variables')->findOrFail($id); // Due to MySQL lockouts if the daemon response fails, we need to // delete the server from the daemon first. If it succeedes and then @@ -730,8 +731,18 @@ class ServerRepository 'X-Access-Token' => $server->node->daemonSecret, 'X-Access-Server' => $server->uuid, ])->request('DELETE', '/servers'); + } catch (ClientException $ex) { + // Exception is thrown on 4XX HTTP errors, so catch and determine + // if we should continue, or if there is a permissions error. + // + // Daemon throws a 404 if the server doesn't exist, if that is returned + // continue with deletion, even if not a force deletion. + $response = $ex->getResponse(); + if ($ex->getResponse()->getStatusCode() !== 404 && ! $force) { + throw new DisplayException($ex->getMessage()); + } } catch (TransferException $ex) { - if ($server->installed !== 3 && ! $force) { + if (! $force) { throw new DisplayException($ex->getMessage()); } } catch (\Exception $ex) { @@ -744,45 +755,40 @@ class ServerRepository $item->save(); }); - $server->variables->each(function ($item) { - $item->delete(); + $server->variables->each->delete(); + + $server->load('subusers.permissions'); + $server->subusers->each(function ($subuser) { + $subuser->permissions->each(function ($permission) { + $perm->delete(); + }); + $subuser->delete(); }); - foreach (Models\Subuser::with('permissions')->where('server_id', $server->id)->get() as &$subuser) { - foreach ($subuser->permissions as &$permission) { - $permission->delete(); - } - $subuser->delete(); - } - - // Remove Downloads - Models\Download::where('server', $server->uuid)->delete(); - - // Clear Tasks - Models\Task::where('server', $server->id)->delete(); + $server->downloads->each->delete(); + $server->tasks->each->delete(); // 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); - } + $server->databases->each(function ($item) { + $repository->drop($item->id); + }); // Fully delete the server. - $server->forceDelete(); + $server->delete(); }); } - public function cancelDeletion($id) - { - $server = Models\Server::withTrashed()->findOrFail($id); - $server->restore(); - - $server->installed = 1; - $server->save(); - } - + /** + * Toggle the install status of a serve. + * + * @param int $id + * @return bool + * + * @throws \Pterodactyl\Exceptions\DisplayException + */ public function toggleInstall($id) { $server = Models\Server::findOrFail($id); @@ -795,77 +801,43 @@ class ServerRepository } /** - * Suspends a server instance making it unable to be booted or used by a user. - * @param int $id - * @return bool + * Suspends or unsuspends a server. + * + * @param int $id + * @param bool $unsuspend + * @return void */ - public function suspend($id, $deleted = false) - { - $server = Models\Server::withTrashed()->with('node')->findOrFail($id); - - DB::beginTransaction(); - - try { - - // Already suspended, no need to make more requests. - if ($server->suspended) { - return true; - } - - $server->suspended = 1; - $server->save(); - - $server->node->guzzleClient([ - 'X-Access-Token' => $server->node->daemonSecret, - 'X-Access-Server' => $server->uuid, - ])->request('POST', '/server/suspend'); - - return DB::commit(); - } catch (TransferException $ex) { - DB::rollBack(); - throw new DisplayException('An error occured while attempting to contact the remote daemon to suspend this server.', $ex); - } catch (\Exception $ex) { - DB::rollBack(); - throw $ex; - } - } - - /** - * Unsuspends a server instance. - * @param int $id - * @return bool - */ - public function unsuspend($id) + public function toggleAccess($id, $unsuspend = true) { $server = Models\Server::with('node')->findOrFail($id); - DB::beginTransaction(); - - try { - - // Already unsuspended, no need to make more requests. - if ($server->suspended === 0) { + DB::transaction(function () use ($server, $unsuspend) { + if ( + (! $unsuspend && $server->suspended) || + ($unsuspend && ! $server->suspended) + ) { return true; } - $server->suspended = 0; + $server->suspended = ! $unsuspend; $server->save(); $server->node->guzzleClient([ 'X-Access-Token' => $server->node->daemonSecret, 'X-Access-Server' => $server->uuid, - ])->request('POST', '/server/unsuspend'); - - return DB::commit(); - } catch (TransferException $ex) { - DB::rollBack(); - throw new DisplayException('An error occured while attempting to contact the remote daemon to un-suspend this server.', $ex); - } catch (\Exception $ex) { - DB::rollBack(); - throw $ex; - } + ])->request('POST', ($unsuspend) ? '/server/unsuspend' : '/server/suspend'); + }); } + /** + * Updates the SFTP password for a server. + * + * @param int $id + * @param string $password + * @return void + * + * @throws \Pterodactyl\Exceptions\DisplayValidationException + */ public function updateSFTPPassword($id, $password) { $server = Models\Server::with('node')->findOrFail($id); @@ -878,10 +850,8 @@ class ServerRepository throw new DisplayValidationException(json_encode($validator->errors())); } - DB::beginTransaction(); - $server->sftp_password = Crypt::encrypt($password); - - try { + DB::transaction(function () use ($password, $server) { + $server->sftp_password = Crypt::encrypt($password); $server->save(); $server->node->guzzleClient([ @@ -890,16 +860,6 @@ class ServerRepository ])->request('POST', '/server/password', [ 'json' => ['password' => $password], ]); - - DB::commit(); - - return true; - } catch (TransferException $ex) { - DB::rollBack(); - throw new DisplayException('There was an error while attmping to contact the remote service to change the password.', $ex); - } catch (\Exception $ex) { - DB::rollBack(); - throw $ex; - } + }); } } diff --git a/app/Repositories/ServiceRepository.php b/app/Repositories/ServiceRepository.php index 126feb647..a0d1716cc 100644 --- a/app/Repositories/ServiceRepository.php +++ b/app/Repositories/ServiceRepository.php @@ -37,6 +37,8 @@ class ServiceRepository * * @param array $data * @return \Pterodactyl\Models\Service + * + * @throws \Pterodactyl\Exceptions\DisplayValidationException */ public function create(array $data) { @@ -80,6 +82,8 @@ class ServiceRepository * @param int $id * @param array $data * @return \Pterodactyl\Models\Service + * + * @throws \Pterodactyl\Exceptions\DisplayValidationException */ public function update($id, array $data) { @@ -109,6 +113,8 @@ class ServiceRepository * * @param int $id * @return void + * + * @throws \Pterodactyl\Exceptions\DisplayException */ public function delete($id) { diff --git a/app/Repositories/SubuserRepository.php b/app/Repositories/SubuserRepository.php index a6e0123ce..a2cd23d99 100644 --- a/app/Repositories/SubuserRepository.php +++ b/app/Repositories/SubuserRepository.php @@ -26,8 +26,12 @@ namespace Pterodactyl\Repositories; use DB; use Validator; -use Pterodactyl\Models; +use Pterodactyl\Models\User; +use Pterodactyl\Models\Server; +use Pterodactyl\Models\Subuser; +use Pterodactyl\Models\Permission; use Pterodactyl\Services\UuidService; +use GuzzleHttp\Exception\TransferException; use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Exceptions\DisplayValidationException; @@ -37,6 +41,7 @@ class SubuserRepository * Core permissions required for every subuser on the daemon. * Without this we cannot connect the websocket or get basic * information about the server. + * * @var array */ protected $coreDaemonPermissions = [ @@ -44,77 +49,19 @@ class SubuserRepository 's:console', ]; - /** - * Allowed permissions and their related daemon permission. - * @var array - */ - protected $permissions = [ - // Power Permissions - 'power-start' => 's:power:start', - 'power-stop' => 's:power:stop', - 'power-restart' => 's:power:restart', - 'power-kill' => 's:power:kill', - - // Commands - 'send-command' => 's:command', - - // File Manager - 'list-files' => 's:files:get', - 'edit-files' => 's:files:read', - 'save-files' => 's:files:post', - 'create-files' => 's:files:create', - 'download-files' => null, - 'upload-files' => 's:files:upload', - 'delete-files' => 's:files:delete', - 'move-files' => 's:files:move', - 'copy-files' => 's:files:copy', - 'compress-files' => 's:files:compress', - 'decompress-files' => 's:files:decompress', - - // Subusers - 'list-subusers' => null, - 'view-subuser' => null, - 'edit-subuser' => null, - 'create-subuser' => null, - 'delete-subuser' => null, - - // Tasks - 'list-tasks' => null, - 'view-task' => null, - 'toggle-task' => null, - 'delete-task' => null, - 'create-task' => null, - 'queue-task' => null, - - // Management - 'set-connection' => null, - 'view-startup' => null, - 'edit-startup' => null, - 'view-sftp' => null, - 'reset-sftp' => 's:set-password', - 'view-sftp-password' => null, - - // Databases - 'view-databases' => null, - 'reset-db-password' => null, - ]; - - public function __construct() - { - // - } - /** * Creates a new subuser on the server. - * @param int $id The ID of the server to add this subuser to. + * + * @param int $sid * @param array $data - * @throws DisplayValidationException - * @throws DisplayException * @return \Pterodactyl\Models\Subuser + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\DisplayValidationException */ public function create($sid, array $data) { - $server = Models\Server::with('node')->findOrFail($sid); + $server = Server::with('node')->findOrFail($sid); $validator = Validator::make($data, [ 'permissions' => 'required|array', @@ -129,7 +76,7 @@ class SubuserRepository try { // Determine if this user exists or if we need to make them an account. - $user = Models\User::where('email', $data['email'])->first(); + $user = User::where('email', $data['email'])->first(); if (! $user) { try { $repo = new UserRepository; @@ -145,26 +92,28 @@ class SubuserRepository } } elseif ($server->owner_id === $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()) { + } elseif (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; - $subuser = Models\Subuser::create([ + $subuser = Subuser::create([ 'user_id' => $user->id, 'server_id' => $server->id, 'daemonSecret' => (string) $uuid->generate('servers', 'uuid'), ]); + $perms = Permission::list(true); $daemonPermissions = $this->coreDaemonPermissions; + foreach ($data['permissions'] as $permission) { - if (array_key_exists($permission, $this->permissions)) { + if (array_key_exists($permission, $perms)) { // Build the daemon permissions array for sending. - if (! is_null($this->permissions[$permission])) { - array_push($daemonPermissions, $this->permissions[$permission]); + if (! is_null($perms[$permission])) { + array_push($daemonPermissions, $perms[$permission]); } - Models\Permission::create([ + Permission::create([ 'subuser_id' => $subuser->id, 'permission' => $permission, ]); @@ -189,7 +138,7 @@ class SubuserRepository DB::commit(); return $subuser; - } catch (\GuzzleHttp\Exception\TransferException $ex) { + } catch (TransferException $ex) { DB::rollBack(); throw new DisplayException('There was an error attempting to connect to the daemon to add this user.', $ex); } catch (\Exception $ex) { @@ -202,15 +151,15 @@ class SubuserRepository /** * Revokes a users permissions on a server. - * @param int $id The ID of the subuser row in MySQL. - * @param array $data - * @throws DisplayValidationException - * @throws DisplayException + * + * @param int $id * @return void + * + * @throws \Pterodactyl\Exceptions\DisplayException */ public function delete($id) { - $subuser = Models\Subuser::with('server.node')->findOrFail($id); + $subuser = Subuser::with('server.node')->findOrFail($id); $server = $subuser->server; DB::beginTransaction(); @@ -232,26 +181,24 @@ class SubuserRepository } $subuser->delete(); DB::commit(); - - return true; - } catch (\GuzzleHttp\Exception\TransferException $ex) { + } catch (TransferException $ex) { DB::rollBack(); throw new DisplayException('There was an error attempting to connect to the daemon to delete this subuser.', $ex); } catch (\Exception $ex) { DB::rollBack(); throw $ex; } - - return false; } /** * Updates permissions for a given subuser. - * @param int $id The ID of the subuser row in MySQL. (Not the user ID) + * + * @param int $id * @param array $data - * @throws DisplayValidationException - * @throws DisplayException * @return void + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\DisplayValidationException */ public function update($id, array $data) { @@ -265,7 +212,7 @@ class SubuserRepository throw new DisplayValidationException(json_encode($validator->all())); } - $subuser = Models\Subuser::with('server.node')->findOrFail($id); + $subuser = Subuser::with('server.node')->findOrFail($id); $server = $subuser->server; DB::beginTransaction(); @@ -275,14 +222,16 @@ class SubuserRepository $permission->delete(); } + $perms = Permission::list(true); $daemonPermissions = $this->coreDaemonPermissions; + foreach ($data['permissions'] as $permission) { - if (array_key_exists($permission, $this->permissions)) { + if (array_key_exists($permission, $perms)) { // Build the daemon permissions array for sending. - if (! is_null($this->permissions[$permission])) { - array_push($daemonPermissions, $this->permissions[$permission]); + if (! is_null($perms[$permission])) { + array_push($daemonPermissions, $perms[$permission]); } - Models\Permission::create([ + Permission::create([ 'subuser_id' => $subuser->id, 'permission' => $permission, ]); @@ -304,16 +253,12 @@ class SubuserRepository ]); DB::commit(); - - return true; - } catch (\GuzzleHttp\Exception\TransferException $ex) { + } catch (TransferException $ex) { DB::rollBack(); throw new DisplayException('There was an error attempting to connect to the daemon to update permissions.', $ex); } catch (\Exception $ex) { DB::rollBack(); throw $ex; } - - return false; } } diff --git a/app/Repositories/TaskRepository.php b/app/Repositories/TaskRepository.php index c42a00482..790078c22 100644 --- a/app/Repositories/TaskRepository.php +++ b/app/Repositories/TaskRepository.php @@ -24,14 +24,22 @@ namespace Pterodactyl\Repositories; +use DB; use Cron; use Validator; -use Pterodactyl\Models; +use Pterodactyl\Models\Task; +use Pterodactyl\Models\User; +use Pterodactyl\Models\Server; use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Exceptions\DisplayValidationException; class TaskRepository { + /** + * The default values to use for new tasks. + * + * @var array + */ protected $defaults = [ 'year' => '*', 'day_of_week' => '*', @@ -41,66 +49,60 @@ class TaskRepository 'minute' => '*/30', ]; + /** + * Task action types. + * + * @var array + */ protected $actions = [ 'command', 'power', ]; - public function __construct() - { - // - } - /** * Deletes a given task. - * @param int $id * + * @param int $id * @return bool */ public function delete($id) { - $task = Models\Task::findOrFail($id); - try { - $task->delete(); - - return true; - } catch (\Exception $ex) { - throw $ex; - } + $task = Task::findOrFail($id); + $task->delete(); } /** * Toggles a task active or inactive. - * @param int $id * - * @return int + * @param int $id + * @return bool */ public function toggle($id) { - $task = Models\Task::findOrFail($id); - try { - $task->active = ($task->active === 1) ? 0 : 1; - $task->queued = 0; - $task->save(); + $task = Task::findOrFail($id); - return $task->active; - } catch (\Exception $ex) { - throw $ex; - } + $task->active = ! $task->active; + $task->queued = false; + $task->save(); + + return $task->active; } /** * Create a new scheduled task for a given server. - * @param int $id - * @param array $data * - * @throws DisplayException - * @throws DisplayValidationException - * @return void + * @param int $server + * @param int $user + * @param array $data + * @return \Pterodactyl\Models\Task + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\DisplayValidationException */ - public function create($id, $data) + public function create($server, $user, $data) { - $server = Models\Server::findOrFail($id); + $server = Server::findOrFail($server); + $user = User::findOrFail($user); $validator = Validator::make($data, [ 'action' => 'string|required', @@ -142,9 +144,9 @@ class TaskRepository throw $ex; } - $task = new Models\Task; - $task->fill([ - 'server' => $server->id, + return Task::create([ + 'user_id' => $user->id, + 'server_id' => $server->id, 'active' => 1, 'action' => $data['action'], 'data' => $data['data'], @@ -158,7 +160,5 @@ class TaskRepository 'last_run' => null, 'next_run' => $buildCron->getNextRunDate(), ]); - - return $task->save(); } } diff --git a/app/Repositories/UserRepository.php b/app/Repositories/UserRepository.php index 714718cbd..b2ddaeadd 100644 --- a/app/Repositories/UserRepository.php +++ b/app/Repositories/UserRepository.php @@ -38,19 +38,13 @@ use Pterodactyl\Exceptions\DisplayValidationException; class UserRepository { - public function __construct() - { - // - } - /** * Creates a user on the panel. Returns the created user's ID. * - * @param string $email - * @param string|null $password An unhashed version of the user's password. - * @param bool $admin Boolean value if user should be an admin or not. - * @param int $token A custom user ID. - * @return bool|int + * @param array $data + * @return \Pterodactyl\Models\User + * + * @throws \Pterodactyl\Exceptions\DisplayValidationException */ public function create(array $data) { @@ -118,9 +112,11 @@ class UserRepository /** * Updates a user on the panel. * - * @param int $id - * @param array $data An array of columns and their associated values to update for the user. - * @return bool + * @param int $id + * @param array $data + * @return \Pterodactyl\Models\User + * + * @throws \Pterodactyl\Exceptions\DisplayValidationException */ public function update($id, array $data) { @@ -151,16 +147,19 @@ class UserRepository unset($data['password']); } - $user->fill($data); + $user->fill($data)->save(); - return $user->save(); + return $user; } /** - * Deletes a user on the panel, returns the number of records deleted. + * Deletes a user on the panel. * * @param int $id - * @return int + * @return void + * @todo Move user self-deletion checking to the controller, rather than the repository. + * + * @throws \Pterodactyl\Exceptions\DisplayException */ public function delete($id) { @@ -168,8 +167,7 @@ class UserRepository throw new DisplayException('Cannot delete a user with active servers attached to thier account.'); } - // @TODO: this should probably be checked outside of this method because we won't always have Auth::user() - if (! is_null(Auth::user()) && Auth::user()->id === $id) { + if (! is_null(Auth::user()) && (int) Auth::user()->id === (int) $id) { throw new DisplayException('Cannot delete your own account.'); } @@ -186,8 +184,6 @@ class UserRepository Models\User::destroy($id); DB::commit(); - - return true; } catch (\Exception $ex) { DB::rollBack(); throw $ex; diff --git a/app/Repositories/VariableRepository.php b/app/Repositories/VariableRepository.php index ee6885280..4584d2bba 100644 --- a/app/Repositories/VariableRepository.php +++ b/app/Repositories/VariableRepository.php @@ -91,7 +91,7 @@ class VariableRepository * Deletes a specified option variable as well as all server * variables currently assigned. * - * @param int $id + * @param int $id * @return void */ public function delete($id) diff --git a/app/Services/APILogService.php b/app/Services/APILogService.php index f43693aca..c8e5c098c 100644 --- a/app/Services/APILogService.php +++ b/app/Services/APILogService.php @@ -30,11 +30,14 @@ use Pterodactyl\Models\APILog; class APILogService { - public function __constructor() - { - // - } - + /** + * Log an API Request. + * + * @param \Illuminate\Http\Request $request + * @param null|string $error + * @param bool $authorized + * @return void + */ public static function log(Request $request, $error = null, $authorized = false) { if ($request->bearerToken() && ! empty($request->bearerToken())) { diff --git a/app/Services/DeploymentService.php b/app/Services/DeploymentService.php index d4d28e9a9..b0befe8c0 100644 --- a/app/Services/DeploymentService.php +++ b/app/Services/DeploymentService.php @@ -30,20 +30,13 @@ use Pterodactyl\Exceptions\DisplayException; class DeploymentService { - public function __constructor() - { - // - } - /** * Return a random location model. DO NOT USE. - * @return \Pterodactyl\Models\Node * - * @TODO Actually make this smarter. If we're selecting a random location + * @return \Pterodactyl\Models\Node + * @todo Actually make this smarter. If we're selecting a random location * but then it has no nodes we should probably continue attempting all locations * until we hit one. - * - * Currently you should just pick a location and go from there. */ public static function randomLocation() { @@ -52,8 +45,8 @@ class DeploymentService /** * Return a model instance of a random node. - * @param int $location - * @param array $not + * @param int $location + * @param array $not * @return \Pterodactyl\Models\Node * * @throws \Pterodactyl\Exceptions\DisplayException @@ -76,9 +69,9 @@ class DeploymentService /** * Selects a random node ensuring it does not put the node * over allocation limits. - * @param int $memory - * @param int $disk - * @param int $location + * @param int $memory + * @param int $disk + * @param null|int $location * @return \Pterodactyl\Models\Node * * @throws \Pterodactyl\Exceptions\DisplayException @@ -102,7 +95,7 @@ class DeploymentService /** * Returns a random allocation for a node. - * @param int $node + * @param int $node * @return \Models\Pterodactyl\Allocation */ public static function randomAllocation($node) @@ -118,8 +111,8 @@ class DeploymentService /** * Checks that a node's allocation limits will not be passed with the given information. * @param \Pterodactyl\Models\Node $node - * @param int $memory - * @param int $disk + * @param int $memory + * @param int $disk * @return bool Returns true if this information would not put the node over it's limit. */ protected static function checkNodeAllocation(Models\Node $node, $memory, $disk) diff --git a/app/Services/FileService.php b/app/Services/FileService.php deleted file mode 100644 index e69de29bb..000000000 diff --git a/app/Services/UuidService.php b/app/Services/UuidService.php index 6f81f1465..2b043731a 100644 --- a/app/Services/UuidService.php +++ b/app/Services/UuidService.php @@ -29,21 +29,13 @@ use Uuid; class UuidService { - /** - * Constructor. - */ - public function __construct() - { - // - } - /** * Generate a unique UUID validating against specified table and column. * Defaults to `users.uuid`. * - * @param string $table - * @param string $field - * @param int $type The type of UUID to generate. + * @param string $table + * @param string $field + * @param int $type * @return string */ public function generate($table = 'users', $field = 'uuid', $type = 4) @@ -62,8 +54,9 @@ class UuidService /** * Generates a ShortUUID code which is 8 characters long and is used for identifying servers in the system. * - * @param string $table - * @param string $field + * @param string $table + * @param string $field + * @param null|string $attachedUuid * @return string */ public function generateShort($table = 'servers', $field = 'uuidShort', $attachedUuid = null) diff --git a/app/Services/VersionService.php b/app/Services/VersionService.php index dea0bb16d..e3eaf8ade 100644 --- a/app/Services/VersionService.php +++ b/app/Services/VersionService.php @@ -29,18 +29,25 @@ use GuzzleHttp\Client; class VersionService { + /** + * The cached CDN response. + * + * @var object + */ protected static $versions; /** - * Constructor. + * Version constructor. + * + * @return void */ public function __construct() { - self::$versions = Cache::remember('versions', env('VERSION_CACHE_TIME', 60), function () { + self::$versions = Cache::remember('versions', config('pterodactyl.cdn.cache'), function () { $client = new Client(); try { - $response = $client->request('GET', env('VERSION_CHECK_URL', 'https://cdn.pterodactyl.io/releases/latest.json')); + $response = $client->request('GET', config('pterodactyl.cdn.url')); if ($response->getStatusCode() === 200) { return json_decode($response->getBody()); @@ -52,32 +59,57 @@ class VersionService return (object) [ 'panel' => 'error', 'daemon' => 'error', - 'discord' => 'https://pterodactyl.io', + 'discord' => 'https://pterodactyl.io/discord', ]; } }); } + /** + * Return current panel version from CDN. + * + * @return string + */ public static function getPanel() { return self::$versions->panel; } + /** + * Return current daemon version from CDN. + * + * @return string + */ public static function getDaemon() { return self::$versions->daemon; } + /** + * Return Discord link from CDN. + * + * @return string + */ public static function getDiscord() { return self::$versions->discord; } + /** + * Return current panel version. + * + * @return null|string + */ public function getCurrentPanel() { return config('app.version'); } + /** + * Determine if panel is latest version. + * + * @return bool + */ public static function isLatestPanel() { if (config('app.version') === 'canary') { @@ -87,6 +119,11 @@ class VersionService return version_compare(config('app.version'), self::$versions->panel) >= 0; } + /** + * Determine if daemon is latest version. + * + * @return bool + */ public static function isLatestDaemon($daemon) { if ($daemon === '0.0.0-canary') { diff --git a/app/Transformers/Admin/AllocationTransformer.php b/app/Transformers/Admin/AllocationTransformer.php new file mode 100644 index 000000000..e7bd15c36 --- /dev/null +++ b/app/Transformers/Admin/AllocationTransformer.php @@ -0,0 +1,98 @@ +. + * + * 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\Transformers\Admin; + +use Illuminate\Http\Request; +use Pterodactyl\Models\Allocation; +use League\Fractal\TransformerAbstract; + +class AllocationTransformer extends TransformerAbstract +{ + /** + * The filter to be applied to this transformer. + * + * @var bool|string + */ + protected $filter; + + /** + * The Illuminate Request object if provided. + * + * @var \Illuminate\Http\Request|bool + */ + protected $request; + + /** + * Setup request object for transformer. + * + * @param \Illuminate\Http\Request|bool $request + * @param bool $filter + * @return void + */ + public function __construct($request = false, $filter = false) + { + if (! $request instanceof Request && $request !== false) { + throw new DisplayException('Request passed to constructor must be of type Request or false.'); + } + + $this->request = $request; + $this->filter = $filter; + } + + /** + * Return a generic transformed allocation array. + * + * @return array + */ + public function transform(Allocation $allocation) + { + return $this->transformWithFilter($allocation); + } + + /** + * Determine which transformer filter to apply. + * + * @return array + */ + protected function transformWithFilter(Allocation $allocation) + { + if ($this->filter === 'server') { + return $this->transformForServer($allocation); + } + + return $allocation->toArray(); + } + + /** + * Transform the allocation to only return information not duplicated + * in the server response (discard node_id and server_id). + * + * @return array + */ + protected function transformForServer(Allocation $allocation) + { + return collect($allocation)->only('id', 'ip', 'ip_alias', 'port')->toArray(); + } +} diff --git a/app/Transformers/Admin/LocationTransformer.php b/app/Transformers/Admin/LocationTransformer.php new file mode 100644 index 000000000..f3fd95885 --- /dev/null +++ b/app/Transformers/Admin/LocationTransformer.php @@ -0,0 +1,102 @@ +. + * + * 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\Transformers\Admin; + +use Illuminate\Http\Request; +use Pterodactyl\Models\Location; +use League\Fractal\TransformerAbstract; + +class LocationTransformer extends TransformerAbstract +{ + /** + * List of resources that can be included. + * + * @var array + */ + protected $availableIncludes = [ + 'nodes', + 'servers', + ]; + + /** + * The Illuminate Request object if provided. + * + * @var \Illuminate\Http\Request|bool + */ + protected $request; + + /** + * Setup request object for transformer. + * + * @param \Illuminate\Http\Request|bool $request + * @return void + */ + public function __construct($request = false) + { + if (! $request instanceof Request && $request !== false) { + throw new DisplayException('Request passed to constructor must be of type Request or false.'); + } + + $this->request = $request; + } + + /** + * Return a generic transformed pack array. + * + * @return array + */ + public function transform(Location $location) + { + return $location->toArray(); + } + + /** + * Return the nodes associated with this location. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeServers(Location $location) + { + if ($this->request && ! $this->request->apiKeyHasPermission('server-list')) { + return; + } + + return $this->collection($location->servers, new ServerTransformer($this->request), 'server'); + } + + /** + * Return the nodes associated with this location. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeNodes(Location $location) + { + if ($this->request && ! $this->request->apiKeyHasPermission('node-list')) { + return; + } + + return $this->collection($location->nodes, new NodeTransformer($this->request), 'node'); + } +} diff --git a/app/Transformers/Admin/NodeTransformer.php b/app/Transformers/Admin/NodeTransformer.php new file mode 100644 index 000000000..d18b64f23 --- /dev/null +++ b/app/Transformers/Admin/NodeTransformer.php @@ -0,0 +1,117 @@ +. + * + * 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\Transformers\Admin; + +use Illuminate\Http\Request; +use Pterodactyl\Models\Node; +use League\Fractal\TransformerAbstract; + +class NodeTransformer extends TransformerAbstract +{ + /** + * List of resources that can be included. + * + * @var array + */ + protected $availableIncludes = [ + 'allocations', + 'location', + 'servers', + ]; + + /** + * The Illuminate Request object if provided. + * + * @var \Illuminate\Http\Request|bool + */ + protected $request; + + /** + * Setup request object for transformer. + * + * @param \Illuminate\Http\Request|bool $request + * @return void + */ + public function __construct($request = false) + { + if (! $request instanceof Request && $request !== false) { + throw new DisplayException('Request passed to constructor must be of type Request or false.'); + } + + $this->request = $request; + } + + /** + * Return a generic transformed pack array. + * + * @return array + */ + public function transform(Node $node) + { + return $node->toArray(); + } + + /** + * Return the nodes associated with this location. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeAllocations(Node $node) + { + if ($this->request && ! $this->request->apiKeyHasPermission('node-view')) { + return; + } + + return $this->collection($node->allocations, new AllocationTransformer($this->request), 'allocation'); + } + + /** + * Return the nodes associated with this location. + * + * @return \Leauge\Fractal\Resource\Item + */ + public function includeLocation(Node $node) + { + if ($this->request && ! $this->request->apiKeyHasPermission('location-list')) { + return; + } + + return $this->item($node->location, new LocationTransformer($this->request), 'location'); + } + + /** + * Return the nodes associated with this location. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeServers(Node $node) + { + if ($this->request && ! $this->request->apiKeyHasPermission('server-list')) { + return; + } + + return $this->collection($node->servers, new ServerTransformer($this->request), 'server'); + } +} diff --git a/app/Transformers/Admin/OptionTransformer.php b/app/Transformers/Admin/OptionTransformer.php new file mode 100644 index 000000000..5b86b53d6 --- /dev/null +++ b/app/Transformers/Admin/OptionTransformer.php @@ -0,0 +1,132 @@ +. + * + * 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\Transformers\Admin; + +use Illuminate\Http\Request; +use Pterodactyl\Models\ServiceOption; +use League\Fractal\TransformerAbstract; + +class OptionTransformer extends TransformerAbstract +{ + /** + * List of resources that can be included. + * + * @var array + */ + protected $availableIncludes = [ + 'service', + 'packs', + 'servers', + 'variables', + ]; + + /** + * The Illuminate Request object if provided. + * + * @var \Illuminate\Http\Request|bool + */ + protected $request; + + /** + * Setup request object for transformer. + * + * @param \Illuminate\Http\Request|bool $request + * @return void + */ + public function __construct($request = false) + { + if (! $request instanceof Request && $request !== false) { + throw new DisplayException('Request passed to constructor must be of type Request or false.'); + } + + $this->request = $request; + } + + /** + * Return a generic transformed service option array. + * + * @return array + */ + public function transform(ServiceOption $option) + { + return $option->toArray(); + } + + /** + * Return the parent service for this service option. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeService(ServiceOption $option) + { + if ($this->request && ! $this->request->apiKeyHasPermission('service-view')) { + return; + } + + return $this->item($option->service, new ServiceTransformer($this->request), 'service'); + } + + /** + * Return the packs associated with this service option. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includePacks(ServiceOption $option) + { + if ($this->request && ! $this->request->apiKeyHasPermission('pack-list')) { + return; + } + + return $this->collection($option->packs, new PackTransformer($this->request), 'pack'); + } + + /** + * Return the servers associated with this service option. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeServers(ServiceOption $option) + { + if ($this->request && ! $this->request->apiKeyHasPermission('server-list')) { + return; + } + + return $this->collection($option->servers, new ServerTransformer($this->request), 'server'); + } + + /** + * Return the variables for this service option. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeVariables(ServiceOption $option) + { + if ($this->request && ! $this->request->apiKeyHasPermission('option-view')) { + return; + } + + return $this->collection($option->variables, new ServiceVariableTransformer($this->request), 'variable'); + } +} diff --git a/app/Transformers/Admin/PackTransformer.php b/app/Transformers/Admin/PackTransformer.php new file mode 100644 index 000000000..8d059faaf --- /dev/null +++ b/app/Transformers/Admin/PackTransformer.php @@ -0,0 +1,106 @@ +. + * + * 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\Transformers\Admin; + +use Illuminate\Http\Request; +use Pterodactyl\Models\Pack; +use League\Fractal\TransformerAbstract; + +class PackTransformer extends TransformerAbstract +{ + /** + * List of resources that can be included. + * + * @var array + */ + protected $availableIncludes = [ + 'option', + 'servers', + ]; + + /** + * The Illuminate Request object if provided. + * + * @var \Illuminate\Http\Request|bool + */ + protected $request; + + /** + * Setup request object for transformer. + * + * @param \Illuminate\Http\Request|bool $request + * @return void + */ + public function __construct($request = false) + { + if (! $request instanceof Request && $request !== false) { + throw new DisplayException('Request passed to constructor must be of type Request or false.'); + } + + $this->request = $request; + } + + /** + * Return a generic transformed pack array. + * + * @return array + */ + public function transform($pack) + { + if (! $pack instanceof Pack) { + return ['id' => null]; + } + + return $pack->toArray(); + } + + /** + * Return the packs associated with this service. + * + * @return \Leauge\Fractal\Resource\Item + */ + public function includeOption(Pack $pack) + { + if ($this->request && ! $this->request->apiKeyHasPermission('option-view')) { + return; + } + + return $this->item($pack->option, new OptionTransformer($this->request), 'option'); + } + + /** + * Return the packs associated with this service. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeServers(Pack $pack) + { + if ($this->request && ! $this->request->apiKeyHasPermission('server-list')) { + return; + } + + return $this->collection($pack->servers, new ServerTransformer($this->request), 'server'); + } +} diff --git a/app/Transformers/Admin/ServerTransformer.php b/app/Transformers/Admin/ServerTransformer.php new file mode 100644 index 000000000..4d94b7e10 --- /dev/null +++ b/app/Transformers/Admin/ServerTransformer.php @@ -0,0 +1,207 @@ +. + * + * 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\Transformers\Admin; + +use Illuminate\Http\Request; +use Pterodactyl\Models\Server; +use League\Fractal\TransformerAbstract; + +class ServerTransformer extends TransformerAbstract +{ + /** + * List of resources that can be included. + * + * @var array + */ + protected $availableIncludes = [ + 'allocations', + 'user', + 'subusers', + 'pack', + 'service', + 'option', + 'variables', + 'location', + 'node', + ]; + + /** + * The Illuminate Request object if provided. + * + * @var \Illuminate\Http\Request|bool + */ + protected $request; + + /** + * Setup request object for transformer. + * + * @param \Illuminate\Http\Request|bool $request + * @return void + */ + public function __construct($request = false) + { + if (! $request instanceof Request && $request !== false) { + throw new DisplayException('Request passed to constructor must be of type Request or false.'); + } + + $this->request = $request; + } + + /** + * Return a generic transformed server array. + * + * @return array + */ + public function transform(Server $server) + { + return collect($server->toArray())->only($server->getTableColumns())->toArray(); + } + + /** + * Return a generic array of allocations for this server. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeAllocations(Server $server) + { + if ($this->request && ! $this->request->apiKeyHasPermission('server-view')) { + return; + } + + return $this->collection($server->allocations, new AllocationTransformer($this->request, 'server'), 'allocation'); + } + + /** + * Return a generic array of data about subusers for this server. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeSubusers(Server $server) + { + if ($this->request && ! $this->request->apiKeyHasPermission('server-view')) { + return; + } + + return $this->collection($server->subusers, new SubuserTransformer($this->request), 'subuser'); + } + + /** + * Return a generic array of data about subusers for this server. + * + * @return \Leauge\Fractal\Resource\Item + */ + public function includeUser(Server $server) + { + if ($this->request && ! $this->request->apiKeyHasPermission('user-view')) { + return; + } + + return $this->item($server->user, new UserTransformer($this->request), 'user'); + } + + /** + * Return a generic array with pack information for this server. + * + * @return \Leauge\Fractal\Resource\Item + */ + public function includePack(Server $server) + { + if ($this->request && ! $this->request->apiKeyHasPermission('pack-view')) { + return; + } + + return $this->item($server->pack, new PackTransformer($this->request), 'pack'); + } + + /** + * Return a generic array with service information for this server. + * + * @return \Leauge\Fractal\Resource\Item + */ + public function includeService(Server $server) + { + if ($this->request && ! $this->request->apiKeyHasPermission('service-view')) { + return; + } + + return $this->item($server->service, new ServiceTransformer($this->request), 'service'); + } + + /** + * Return a generic array with service option information for this server. + * + * @return \Leauge\Fractal\Resource\Item + */ + public function includeOption(Server $server) + { + if ($this->request && ! $this->request->apiKeyHasPermission('option-view')) { + return; + } + + return $this->item($server->option, new OptionTransformer($this->request), 'option'); + } + + /** + * Return a generic array of data about subusers for this server. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeVariables(Server $server) + { + if ($this->request && ! $this->request->apiKeyHasPermission('server-view')) { + return; + } + + return $this->collection($server->variables, new ServerVariableTransformer($this->request), 'server_variable'); + } + + /** + * Return a generic array with pack information for this server. + * + * @return \Leauge\Fractal\Resource\Item + */ + public function includeLocation(Server $server) + { + if ($this->request && ! $this->request->apiKeyHasPermission('location-list')) { + return; + } + + return $this->item($server->location, new LocationTransformer($this->request), 'location'); + } + + /** + * Return a generic array with pack information for this server. + * + * @return \Leauge\Fractal\Resource\Item|void + */ + public function includeNode(Server $server) + { + if ($this->request && ! $this->request->apiKeyHasPermission('node-view')) { + return; + } + + return $this->item($server->node, new NodeTransformer($this->request), 'node'); + } +} diff --git a/app/Transformers/Admin/ServerVariableTransformer.php b/app/Transformers/Admin/ServerVariableTransformer.php new file mode 100644 index 000000000..3211e0295 --- /dev/null +++ b/app/Transformers/Admin/ServerVariableTransformer.php @@ -0,0 +1,85 @@ +. + * + * 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\Transformers\Admin; + +use Illuminate\Http\Request; +use Pterodactyl\Models\ServerVariable; +use League\Fractal\TransformerAbstract; + +class ServerVariableTransformer extends TransformerAbstract +{ + /** + * List of resources that can be included. + * + * @var array + */ + protected $availableIncludes = ['parent']; + + /** + * The Illuminate Request object if provided. + * + * @var \Illuminate\Http\Request|bool + */ + protected $request; + + /** + * Setup request object for transformer. + * + * @param \Illuminate\Http\Request|bool $request + * @return void + */ + public function __construct($request = false) + { + if (! $request instanceof Request && $request !== false) { + throw new DisplayException('Request passed to constructor must be of type Request or false.'); + } + + $this->request = $request; + } + + /** + * Return a generic transformed server variable array. + * + * @return array + */ + public function transform(ServerVariable $variable) + { + return $variable->toArray(); + } + + /** + * Return the parent service variable data. + * + * @return \Leauge\Fractal\Resource\Item + */ + public function includeParent(ServerVariable $variable) + { + if ($this->request && ! $this->request->apiKeyHasPermission('option-view')) { + return; + } + + return $this->item($variable->variable, new ServiceVariableTransformer($this->request), 'variable'); + } +} diff --git a/app/Transformers/Admin/ServiceTransformer.php b/app/Transformers/Admin/ServiceTransformer.php new file mode 100644 index 000000000..2df1fc8cc --- /dev/null +++ b/app/Transformers/Admin/ServiceTransformer.php @@ -0,0 +1,117 @@ +. + * + * 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\Transformers\Admin; + +use Illuminate\Http\Request; +use Pterodactyl\Models\Service; +use League\Fractal\TransformerAbstract; + +class ServiceTransformer extends TransformerAbstract +{ + /** + * List of resources that can be included. + * + * @var array + */ + protected $availableIncludes = [ + 'options', + 'servers', + 'packs', + ]; + + /** + * The Illuminate Request object if provided. + * + * @var \Illuminate\Http\Request|bool + */ + protected $request; + + /** + * Setup request object for transformer. + * + * @param \Illuminate\Http\Request|bool $request + * @return void + */ + public function __construct($request = false) + { + if (! $request instanceof Request && $request !== false) { + throw new DisplayException('Request passed to constructor must be of type Request or false.'); + } + + $this->request = $request; + } + + /** + * Return a generic transformed service array. + * + * @return array + */ + public function transform(Service $service) + { + return $service->toArray(); + } + + /** + * Return the the service options. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeOptions(Service $service) + { + if ($this->request && ! $this->request->apiKeyHasPermission('option-list')) { + return; + } + + return $this->collection($service->options, new OptionTransformer($this->request), 'option'); + } + + /** + * Return the servers associated with this service. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeServers(Service $service) + { + if ($this->request && ! $this->request->apiKeyHasPermission('server-list')) { + return; + } + + return $this->collection($service->servers, new ServerTransformer($this->request), 'server'); + } + + /** + * Return the packs associated with this service. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includePacks(Service $service) + { + if ($this->request && ! $this->request->apiKeyHasPermission('pack-list')) { + return; + } + + return $this->collection($service->packs, new PackTransformer($this->request), 'pack'); + } +} diff --git a/app/Transformers/Admin/ServiceVariableTransformer.php b/app/Transformers/Admin/ServiceVariableTransformer.php new file mode 100644 index 000000000..aa10428d9 --- /dev/null +++ b/app/Transformers/Admin/ServiceVariableTransformer.php @@ -0,0 +1,85 @@ +. + * + * 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\Transformers\Admin; + +use Illuminate\Http\Request; +use League\Fractal\TransformerAbstract; +use Pterodactyl\Models\ServiceVariable; + +class ServiceVariableTransformer extends TransformerAbstract +{ + /** + * List of resources that can be included. + * + * @var array + */ + protected $availableIncludes = ['variables']; + + /** + * The Illuminate Request object if provided. + * + * @var \Illuminate\Http\Request|bool + */ + protected $request; + + /** + * Setup request object for transformer. + * + * @param \Illuminate\Http\Request|bool $request + * @return void + */ + public function __construct($request = false) + { + if (! $request instanceof Request && $request !== false) { + throw new DisplayException('Request passed to constructor must be of type Request or false.'); + } + + $this->request = $request; + } + + /** + * Return a generic transformed server variable array. + * + * @return array + */ + public function transform(ServiceVariable $variable) + { + return $variable->toArray(); + } + + /** + * Return the server variables associated with this variable. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeVariables(ServiceVariable $variable) + { + if ($this->request && ! $this->request->apiKeyHasPermission('server-view')) { + return; + } + + return $this->collection($variable->serverVariable, new ServerVariableTransformer($this->request), 'server_variable'); + } +} diff --git a/app/Transformers/Admin/SubuserTransformer.php b/app/Transformers/Admin/SubuserTransformer.php new file mode 100644 index 000000000..129da7ad3 --- /dev/null +++ b/app/Transformers/Admin/SubuserTransformer.php @@ -0,0 +1,77 @@ +. + * + * 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\Transformers\Admin; + +use Illuminate\Http\Request; +use Pterodactyl\Models\Subuser; +use Pterodactyl\Models\Permission; +use League\Fractal\TransformerAbstract; + +class SubuserTransformer extends TransformerAbstract +{ + /** + * The Illuminate Request object if provided. + * + * @var \Illuminate\Http\Request|bool + */ + protected $request; + + /** + * Setup request object for transformer. + * + * @param \Illuminate\Http\Request|bool $request + * @return void + */ + public function __construct($request = false) + { + if (! $request instanceof Request && $request !== false) { + throw new DisplayException('Request passed to constructor must be of type Request or false.'); + } + + $this->request = $request; + } + + /** + * Return a generic transformed subuser array. + * + * @return array + */ + public function transform(Subuser $subuser) + { + if ($this->request && ! $this->request->apiKeyHasPermission('server-view')) { + return; + } + + return [ + 'id' => $subuser->id, + 'username' => $subuser->user->username, + 'email' => $subuser->user->email, + '2fa' => (bool) $subuser->user->use_totp, + 'permissions' => $subuser->permissions->pluck('permission'), + 'created_at' => $subuser->created_at, + 'updated_at' => $subuser->updated_at, + ]; + } +} diff --git a/app/Transformers/Admin/UserTransformer.php b/app/Transformers/Admin/UserTransformer.php new file mode 100644 index 000000000..0d26961b9 --- /dev/null +++ b/app/Transformers/Admin/UserTransformer.php @@ -0,0 +1,102 @@ +. + * + * 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\Transformers\Admin; + +use Illuminate\Http\Request; +use Pterodactyl\Models\User; +use League\Fractal\TransformerAbstract; + +class UserTransformer extends TransformerAbstract +{ + /** + * List of resources that can be included. + * + * @var array + */ + protected $availableIncludes = [ + 'access', + 'servers', + ]; + + /** + * The Illuminate Request object if provided. + * + * @var \Illuminate\Http\Request|bool + */ + protected $request; + + /** + * Setup request object for transformer. + * + * @param \Illuminate\Http\Request|bool $request + * @return void + */ + public function __construct($request = false) + { + if (! $request instanceof Request && $request !== false) { + throw new DisplayException('Request passed to constructor must be of type Request or false.'); + } + + $this->request = $request; + } + + /** + * Return a generic transformed subuser array. + * + * @return array + */ + public function transform(User $user) + { + return $user->toArray(); + } + + /** + * Return the servers associated with this user. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeServers(User $user) + { + if ($this->request && ! $this->request->apiKeyHasPermission('server-list')) { + return; + } + + return $this->collection($user->servers, new ServerTransformer($this->request), 'server'); + } + + /** + * Return the servers that this user can access. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeAccess(User $user) + { + if ($this->request && ! $this->request->apiKeyHasPermission('server-list')) { + return; + } + + return $this->collection($user->access()->get(), new ServerTransformer($this->request), 'server'); + } +} diff --git a/app/Jobs/SuspendServer.php b/app/Transformers/User/AllocationTransformer.php similarity index 61% rename from app/Jobs/SuspendServer.php rename to app/Transformers/User/AllocationTransformer.php index e4920f263..0fd0be453 100644 --- a/app/Jobs/SuspendServer.php +++ b/app/Transformers/User/AllocationTransformer.php @@ -22,42 +22,43 @@ * SOFTWARE. */ -namespace Pterodactyl\Jobs; +namespace Pterodactyl\Transformers\User; -use Illuminate\Queue\SerializesModels; -use Illuminate\Queue\InteractsWithQueue; -use Illuminate\Contracts\Queue\ShouldQueue; -use Pterodactyl\Repositories\ServerRepository; +use Pterodactyl\Models\Server; +use Pterodactyl\Models\Allocation; +use League\Fractal\TransformerAbstract; -class SuspendServer extends Job implements ShouldQueue +class AllocationTransformer extends TransformerAbstract { - use InteractsWithQueue, SerializesModels; - /** - * ID of associated server model. - * @var object - */ - protected $id; - - /** - * Create a new job instance. + * Server eloquent model. + * + * @return \Pterodactyl\Models\Server + */ + protected $server; + + /** + * Setup allocation transformer with access to server data. * - * @param int $id * @return void */ - public function __construct($id) + public function __construct(Server $server) { - $this->id = $id; + $this->server = $server; } /** - * Execute the job. + * Return a generic transformed allocation array. * - * @return void + * @return array */ - public function handle() + public function transform(Allocation $allocation) { - $repo = new ServerRepository; - $repo->suspend($this->id, true); + return [ + 'id' => $allocation->id, + 'ip' => $allocation->alias, + 'port' => $allocation->port, + 'default' => ($allocation->id === $this->server->allocation_id), + ]; } } diff --git a/app/Transformers/ServerTransformer.php b/app/Transformers/User/OverviewTransformer.php old mode 100755 new mode 100644 similarity index 72% rename from app/Transformers/ServerTransformer.php rename to app/Transformers/User/OverviewTransformer.php index 12707cd04..c8e1db9ed --- a/app/Transformers/ServerTransformer.php +++ b/app/Transformers/User/OverviewTransformer.php @@ -22,20 +22,29 @@ * SOFTWARE. */ -namespace Pterodactyl\Transformers; +namespace Pterodactyl\Transformers\User; use Pterodactyl\Models\Server; use League\Fractal\TransformerAbstract; -class ServerTransformer extends TransformerAbstract +class OverviewTransformer extends TransformerAbstract { /** - * Turn this item object into a generic array. + * Return a generic transformed server array. * * @return array */ public function transform(Server $server) { - return $server; + return [ + 'id' => $server->uuidShort, + 'uuid' => $server->uuid, + 'name' => $server->name, + 'node' => $server->node->name, + 'ip' => $server->allocation->alias, + 'port' => $server->allocation->port, + 'service' => $server->service->name, + 'option' => $server->option->name, + ]; } } diff --git a/app/Transformers/User/ServerTransformer.php b/app/Transformers/User/ServerTransformer.php new file mode 100644 index 000000000..4e5aacb56 --- /dev/null +++ b/app/Transformers/User/ServerTransformer.php @@ -0,0 +1,100 @@ +. + * + * 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\Transformers\User; + +use Pterodactyl\Models\Server; +use League\Fractal\TransformerAbstract; + +class ServerTransformer extends TransformerAbstract +{ + /** + * List of resources that can be included. + * + * @var array + */ + protected $availableIncludes = [ + 'allocations', + 'subusers', + 'stats', + ]; + + /** + * Return a generic transformed server array. + * + * @return array + */ + public function transform(Server $server) + { + return [ + 'id' => $server->uuidShort, + 'uuid' => $server->uuid, + 'name' => $server->name, + 'description' => $server->description, + 'node' => $server->node->name, + 'limits' => [ + 'memory' => $server->memory, + 'swap' => $server->swap, + 'disk' => $server->disk, + 'io' => $server->io, + 'cpu' => $server->cpu, + 'oom_disabled' => (bool) $server->oom_disabled, + ], + ]; + } + + /** + * Return a generic array of allocations for this server. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeAllocations(Server $server) + { + $allocations = $server->allocations; + + return $this->collection($allocations, new AllocationTransformer($server), 'allocation'); + } + + /** + * Return a generic array of subusers for this server. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeSubusers(Server $server) + { + $server->load('subusers.permissions', 'subusers.user'); + + return $this->collection($server->subusers, new SubuserTransformer, 'subuser'); + } + + /** + * Return a generic array of allocations for this server. + * + * @return \Leauge\Fractal\Resource\Item + */ + public function includeStats(Server $server) + { + return $this->item($server->guzzleClient(), new StatsTransformer, 'stat'); + } +} diff --git a/app/Transformers/UserTransformer.php b/app/Transformers/User/StatsTransformer.php old mode 100755 new mode 100644 similarity index 54% rename from app/Transformers/UserTransformer.php rename to app/Transformers/User/StatsTransformer.php index c36baaa69..6b08ea8a5 --- a/app/Transformers/UserTransformer.php +++ b/app/Transformers/User/StatsTransformer.php @@ -22,20 +22,42 @@ * SOFTWARE. */ -namespace Pterodactyl\Transformers; +namespace Pterodactyl\Transformers\User; -use Pterodactyl\Models\User; +use GuzzleHttp\Client; use League\Fractal\TransformerAbstract; +use GuzzleHttp\Exception\ConnectException; -class UserTransformer extends TransformerAbstract +class StatsTransformer extends TransformerAbstract { /** - * Turn this item object into a generic array. + * Return a generic transformed subuser array. * * @return array */ - public function transform(User $user) + public function transform(Client $client) { - return $user; + try { + $res = $client->request('GET', '/server', ['http_errors' => false]); + + if ($res->getStatusCode() !== 200) { + return [ + 'error' => 'Error: HttpResponseException. Recieved non-200 HTTP status code from daemon: ' . $res->statusCode(), + ]; + } + + $json = json_decode($res->getBody()); + + return [ + 'id' => 1, + 'status' => $json->status, + 'resources' => $json->proc, + ]; + } catch (ConnectException $ex) { + return [ + 'error' => 'Error: ConnectException. Unable to contact the daemon to request server status.', + 'exception' => (config('app.debug')) ? $ex->getMessage() : null, + ]; + } } } diff --git a/app/Transformers/AllocationTransformer.php b/app/Transformers/User/SubuserTransformer.php similarity index 70% rename from app/Transformers/AllocationTransformer.php rename to app/Transformers/User/SubuserTransformer.php index 8ca06a9c8..e1a122f1f 100644 --- a/app/Transformers/AllocationTransformer.php +++ b/app/Transformers/User/SubuserTransformer.php @@ -22,20 +22,27 @@ * SOFTWARE. */ -namespace Pterodactyl\Transformers; +namespace Pterodactyl\Transformers\User; -use Pterodactyl\Models\Allocation; +use Pterodactyl\Models\Subuser; +use Pterodactyl\Models\Permission; use League\Fractal\TransformerAbstract; -class AllocationTransformer extends TransformerAbstract +class SubuserTransformer extends TransformerAbstract { /** - * Turn this item object into a generic array. + * Return a generic transformed subuser array. * * @return array */ - public function transform(Allocation $allocation) + public function transform(Subuser $subuser) { - return array_except($allocation, ['created_at', 'updated_at']); + return [ + 'id' => $subuser->id, + 'username' => $subuser->user->username, + 'email' => $subuser->user->email, + '2fa' => (bool) $subuser->user->use_totp, + 'permissions' => $subuser->permissions->pluck('permission'), + ]; } } diff --git a/bootstrap/autoload.php b/bootstrap/autoload.php index 0f7519510..94adc9977 100644 --- a/bootstrap/autoload.php +++ b/bootstrap/autoload.php @@ -14,21 +14,4 @@ define('LARAVEL_START', microtime(true)); | */ -require __DIR__ . '/../vendor/autoload.php'; - -/* -|-------------------------------------------------------------------------- -| Include The Compiled Class File -|-------------------------------------------------------------------------- -| -| To dramatically increase your application's performance, you may use a -| compiled class file which contains all of the classes commonly used -| by a request. The Artisan "optimize" is used to create this file. -| -*/ - -$compiledPath = __DIR__ . '/cache/compiled.php'; - -if (file_exists($compiledPath)) { - require $compiledPath; -} +require __DIR__.'/../vendor/autoload.php'; diff --git a/composer.json b/composer.json index a58fc7f82..a58a52202 100644 --- a/composer.json +++ b/composer.json @@ -11,34 +11,33 @@ } ], "require": { - "php": ">=5.6.4", - "laravel/framework": "5.3.21", - "barryvdh/laravel-debugbar": "2.2.3", - "doctrine/dbal": "2.5.5", - "guzzlehttp/guzzle": "6.2.2", - "pragmarx/google2fa": "1.0.1", - "webpatser/laravel-uuid": "2.0.1", - "prologue/alerts": "0.4.0", - "s1lentium/iptools": "1.1.0", + "php": ">=7.0.0", + "aws/aws-sdk-php": "3.25.1", + "barryvdh/laravel-debugbar": "2.3.2", + "daneeveritt/login-notifications": "1.0.0", + "doctrine/dbal": "2.5.12", "edvinaskrucas/settings": "2.0.0", - "igaster/laravel-theme": "1.1.3", - "nesbot/carbon": "1.21.0", - "mtdowling/cron-expression": "1.1.0", - "dingo/api": "1.0.0-beta6", - "aws/aws-sdk-php": "3.19.20", - "predis/predis": "1.1.1", - "fideloper/proxy": "3.2.0", + "fideloper/proxy": "3.3.0", + "guzzlehttp/guzzle": "6.2.3", + "igaster/laravel-theme": "1.14.0", "laracasts/utilities": "2.1.0", - "lord/laroute": "2.3.0", - "nicolaslopezj/searchable": "1.9.5" + "laravel/framework": "5.4.18", + "laravel/tinker": "1.0.0", + "lord/laroute": "2.4.4", + "mtdowling/cron-expression": "1.2.0", + "nesbot/carbon": "1.22.1", + "nicolaslopezj/searchable": "1.9.5", + "pragmarx/google2fa": "1.0.1", + "predis/predis": "1.1.1", + "prologue/alerts": "0.4.1", + "s1lentium/iptools": "1.1.0", + "spatie/laravel-fractal": "3.5.0", + "webpatser/laravel-uuid": "2.0.1" }, "require-dev": { "fzaninotto/faker": "~1.4", "mockery/mockery": "0.9.*", - "phpunit/phpunit": "~5.0", - "symfony/css-selector": "3.1.*", - "symfony/dom-crawler": "3.1.*", - "laravel/homestead": "3.0.*", + "phpunit/phpunit": "~5.7", "barryvdh/laravel-ide-helper": "^2.3" }, "autoload": { @@ -50,16 +49,18 @@ } }, "autoload-dev": { - "classmap": [ - "tests/TestCase.php" - ] + "psr-4": { + "Tests\\": "tests/" + } }, "scripts": { - "post-root-package-install": [ - "php -r \"file_exists('.env') || copy('.env.example', '.env');\"" + "pre-install-cmd": [ + "php -r \"!file_exists('bootstrap/cache/services.php') || @unlink('bootstrap/cache/services.php');\"", + "php -r \"!file_exists('bootstrap/cache/compiled.php') || @unlink('bootstrap/cache/compiled.php');\"" ], - "post-create-project-cmd": [ - "php artisan key:generate" + "pre-update-cmd": [ + "php -r \"!file_exists('bootstrap/cache/services.php') || @unlink('bootstrap/cache/services.php');\"", + "php -r \"!file_exists('bootstrap/cache/compiled.php') || @unlink('bootstrap/cache/compiled.php');\"" ], "post-install-cmd": [ "Illuminate\\Foundation\\ComposerScripts::postInstall", @@ -70,14 +71,12 @@ "Illuminate\\Foundation\\ComposerScripts::postUpdate", "php artisan optimize", "php artisan config:cache" - ], - "setup": [ - "composer install --ansi --no-dev", - "php -r \"file_exists('.env') || copy('.env.example', '.env');\"", - "php artisan key:generate" ] }, + "prefer-stable": true, "config": { - "preferred-install": "dist" + "preferred-install": "dist", + "sort-packages": true, + "optimize-autoloader": true } } diff --git a/composer.lock b/composer.lock index 8975e85a0..581eb6477 100644 --- a/composer.lock +++ b/composer.lock @@ -4,27 +4,27 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "7c01d337ebab3d47ae3479360c63e68f", - "content-hash": "c2addd888e0a4fd26518ea7c691f6f25", + "hash": "df3cc5312acd808f26f5a6117de4506a", + "content-hash": "071f2fce48f27388231da47a740ceb2a", "packages": [ { "name": "aws/aws-sdk-php", - "version": "3.19.20", + "version": "3.25.1", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "36a05623d5f7f4a001cd3396ce0f000262c75062" + "reference": "cd362e4dda55d6c3466e60070dad6527f6938d5d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/36a05623d5f7f4a001cd3396ce0f000262c75062", - "reference": "36a05623d5f7f4a001cd3396ce0f000262c75062", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/cd362e4dda55d6c3466e60070dad6527f6938d5d", + "reference": "cd362e4dda55d6c3466e60070dad6527f6938d5d", "shasum": "" }, "require": { "guzzlehttp/guzzle": "^5.3.1|^6.2.1", "guzzlehttp/promises": "~1.0", - "guzzlehttp/psr7": "~1.3.1", + "guzzlehttp/psr7": "^1.4.1", "mtdowling/jmespath.php": "~2.2", "php": ">=5.5" }, @@ -85,32 +85,32 @@ "s3", "sdk" ], - "time": "2016-10-25 19:23:39" + "time": "2017-03-31 19:42:09" }, { "name": "barryvdh/laravel-debugbar", - "version": "V2.2.3", + "version": "v2.3.2", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-debugbar.git", - "reference": "ecd1ce5c4a827e2f6a8fb41bcf67713beb1c1cbd" + "reference": "24e4f0261e352d3fd86d0447791b56ae49398674" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/ecd1ce5c4a827e2f6a8fb41bcf67713beb1c1cbd", - "reference": "ecd1ce5c4a827e2f6a8fb41bcf67713beb1c1cbd", + "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/24e4f0261e352d3fd86d0447791b56ae49398674", + "reference": "24e4f0261e352d3fd86d0447791b56ae49398674", "shasum": "" }, "require": { - "illuminate/support": "5.1.*|5.2.*|5.3.*", - "maximebf/debugbar": "~1.11.0|~1.12.0", + "illuminate/support": "5.1.*|5.2.*|5.3.*|5.4.*", + "maximebf/debugbar": "~1.13.0", "php": ">=5.5.9", "symfony/finder": "~2.7|~3.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.2-dev" + "dev-master": "2.3-dev" } }, "autoload": { @@ -139,7 +139,7 @@ "profiler", "webprofiler" ], - "time": "2016-07-29 15:00:36" + "time": "2017-01-19 08:19:49" }, { "name": "christian-riesen/base32", @@ -196,35 +196,28 @@ "time": "2016-05-05 11:49:03" }, { - "name": "classpreloader/classpreloader", - "version": "3.1.0", + "name": "daneeveritt/login-notifications", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/ClassPreloader/ClassPreloader.git", - "reference": "bc7206aa892b5a33f4680421b69b191efd32b096" + "url": "https://github.com/DaneEveritt/login-notifications.git", + "reference": "a12e9b25e9a5e42d3f25c16579ba6dc2b8aba910" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ClassPreloader/ClassPreloader/zipball/bc7206aa892b5a33f4680421b69b191efd32b096", - "reference": "bc7206aa892b5a33f4680421b69b191efd32b096", + "url": "https://api.github.com/repos/DaneEveritt/login-notifications/zipball/a12e9b25e9a5e42d3f25c16579ba6dc2b8aba910", + "reference": "a12e9b25e9a5e42d3f25c16579ba6dc2b8aba910", "shasum": "" }, "require": { - "nikic/php-parser": "^1.0|^2.0|^3.0", - "php": ">=5.5.9" - }, - "require-dev": { - "phpunit/phpunit": "^4.8|^5.0" + "laravel/framework": "~5.3.0|~5.4.0", + "nesbot/carbon": "1.22.*", + "php": "^5.6|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - }, "autoload": { "psr-4": { - "ClassPreloader\\": "src/" + "DaneEveritt\\LoginNotifications\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -233,152 +226,19 @@ ], "authors": [ { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com" - }, - { - "name": "Graham Campbell", - "email": "graham@alt-three.com" + "name": "Dane Everitt", + "email": "dane@daneeveritt.com" } ], - "description": "Helps class loading performance by generating a single PHP file containing all of the autoloaded files for a specific use case", + "description": "Login notifications for Laravel", "keywords": [ - "autoload", - "class", - "preload" - ], - "time": "2016-09-16 12:50:15" - }, - { - "name": "dingo/api", - "version": "v1.0.0-beta6", - "source": { - "type": "git", - "url": "https://github.com/dingo/api.git", - "reference": "cac67ab05da38c3de6d48c7146b700a13ed54cd0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dingo/api/zipball/cac67ab05da38c3de6d48c7146b700a13ed54cd0", - "reference": "cac67ab05da38c3de6d48c7146b700a13ed54cd0", - "shasum": "" - }, - "require": { - "dingo/blueprint": "0.2.*", - "doctrine/annotations": "1.2.*", - "illuminate/routing": "5.1.* || 5.2.* || 5.3.*", - "illuminate/support": "5.1.* || 5.2.* || 5.3.*", - "league/fractal": ">=0.12.0", - "php": "^5.5.9 || ^7.0" - }, - "require-dev": { - "illuminate/auth": "5.1.* || 5.2.* || 5.3.*", - "illuminate/cache": "5.1.* || 5.2.* || 5.3.*", - "illuminate/console": "5.1.* || 5.2.* || 5.3.*", - "illuminate/database": "5.1.* || 5.2.* || 5.3.*", - "illuminate/events": "5.1.* || 5.2.* || 5.3.*", - "illuminate/filesystem": "5.1.* || 5.2.* || 5.3.*", - "illuminate/log": "5.1.* || 5.2.* || 5.3.*", - "illuminate/pagination": "5.1.* || 5.2.* || 5.3.*", - "laravel/lumen-framework": "5.1.* || 5.2.*", - "lucadegasperi/oauth2-server-laravel": "5.0.*", - "mockery/mockery": "~0.9", - "phpunit/phpunit": "^4.8 || ^5.0", - "squizlabs/php_codesniffer": "~2.0", - "tymon/jwt-auth": "1.0.*" - }, - "suggest": { - "lucadegasperi/oauth2-server-laravel": "Protect your API with OAuth 2.0.", - "tymon/jwt-auth": "Protect your API with JSON Web Tokens." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-4": { - "Dingo\\Api\\": "src/" - }, - "files": [ - "src/helpers.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jason Lewis", - "email": "jason.lewis1991@gmail.com" - } - ], - "description": "A RESTful API package for the Laravel and Lumen frameworks.", - "keywords": [ - "api", - "dingo", + "email", + "events", "laravel", - "restful" + "login", + "notifications" ], - "time": "2016-08-30 03:57:04" - }, - { - "name": "dingo/blueprint", - "version": "0.2.2", - "source": { - "type": "git", - "url": "https://github.com/dingo/blueprint.git", - "reference": "690bdf0f76b4428fd52835b9d778fb4551333867" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dingo/blueprint/zipball/690bdf0f76b4428fd52835b9d778fb4551333867", - "reference": "690bdf0f76b4428fd52835b9d778fb4551333867", - "shasum": "" - }, - "require": { - "doctrine/annotations": "~1.2", - "illuminate/filesystem": "5.1.* || 5.2.* || 5.3.* || 5.4.*", - "illuminate/support": "5.1.* || 5.2.* || 5.3.* || 5.4.*", - "php": ">=5.5.9", - "phpdocumentor/reflection-docblock": "3.1.*" - }, - "require-dev": { - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.2-dev" - } - }, - "autoload": { - "psr-4": { - "Dingo\\Blueprint\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jason Lewis", - "email": "jason.lewis1991@gmail.com" - } - ], - "description": "API Blueprint documentation generator.", - "keywords": [ - "api", - "blueprint", - "dingo", - "docs", - "laravel" - ], - "time": "2017-02-11 17:28:57" + "time": "2017-04-14 20:57:26" }, { "name": "dnoegel/php-xdg-base-dir", @@ -415,35 +275,35 @@ }, { "name": "doctrine/annotations", - "version": "v1.2.7", + "version": "v1.4.0", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535" + "reference": "54cacc9b81758b14e3ce750f205a393d52339e97" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/f25c8aab83e0c3e976fd7d19875f198ccf2f7535", - "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/54cacc9b81758b14e3ce750f205a393d52339e97", + "reference": "54cacc9b81758b14e3ce750f205a393d52339e97", "shasum": "" }, "require": { "doctrine/lexer": "1.*", - "php": ">=5.3.2" + "php": "^5.6 || ^7.0" }, "require-dev": { "doctrine/cache": "1.*", - "phpunit/phpunit": "4.*" + "phpunit/phpunit": "^5.7" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3.x-dev" + "dev-master": "1.4.x-dev" } }, "autoload": { - "psr-0": { - "Doctrine\\Common\\Annotations\\": "lib/" + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" } }, "notification-url": "https://packagist.org/downloads/", @@ -479,7 +339,7 @@ "docblock", "parser" ], - "time": "2015-08-31 12:32:49" + "time": "2017-02-24 16:22:25" }, { "name": "doctrine/cache", @@ -620,16 +480,16 @@ }, { "name": "doctrine/common", - "version": "v2.6.2", + "version": "v2.7.2", "source": { "type": "git", "url": "https://github.com/doctrine/common.git", - "reference": "7bce00698899aa2c06fe7365c76e4d78ddb15fa3" + "reference": "930297026c8009a567ac051fd545bf6124150347" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/common/zipball/7bce00698899aa2c06fe7365c76e4d78ddb15fa3", - "reference": "7bce00698899aa2c06fe7365c76e4d78ddb15fa3", + "url": "https://api.github.com/repos/doctrine/common/zipball/930297026c8009a567ac051fd545bf6124150347", + "reference": "930297026c8009a567ac051fd545bf6124150347", "shasum": "" }, "require": { @@ -638,10 +498,10 @@ "doctrine/collections": "1.*", "doctrine/inflector": "1.*", "doctrine/lexer": "1.*", - "php": "~5.5|~7.0" + "php": "~5.6|~7.0" }, "require-dev": { - "phpunit/phpunit": "~4.8|~5.0" + "phpunit/phpunit": "^5.4.6" }, "type": "library", "extra": { @@ -689,24 +549,24 @@ "persistence", "spl" ], - "time": "2016-11-30 16:50:46" + "time": "2017-01-13 14:02:13" }, { "name": "doctrine/dbal", - "version": "v2.5.5", + "version": "v2.5.12", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "9f8c05cd5225a320d56d4bfdb4772f10d045a0c9" + "reference": "7b9e911f9d8b30d43b96853dab26898c710d8f44" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/9f8c05cd5225a320d56d4bfdb4772f10d045a0c9", - "reference": "9f8c05cd5225a320d56d4bfdb4772f10d045a0c9", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/7b9e911f9d8b30d43b96853dab26898c710d8f44", + "reference": "7b9e911f9d8b30d43b96853dab26898c710d8f44", "shasum": "" }, "require": { - "doctrine/common": ">=2.4,<2.7-dev", + "doctrine/common": ">=2.4,<2.8-dev", "php": ">=5.3.2" }, "require-dev": { @@ -760,7 +620,7 @@ "persistence", "queryobject" ], - "time": "2016-09-09 19:13:33" + "time": "2017-02-08 12:53:47" }, { "name": "doctrine/inflector", @@ -935,17 +795,59 @@ "time": "2016-01-19 13:50:39" }, { - "name": "fideloper/proxy", - "version": "3.2.0", + "name": "erusev/parsedown", + "version": "1.6.2", "source": { "type": "git", - "url": "https://github.com/fideloper/TrustedProxy.git", - "reference": "b270bfa52915e55cfb67faa0f16863a479c36121" + "url": "https://github.com/erusev/parsedown.git", + "reference": "1bf24f7334fe16c88bf9d467863309ceaf285b01" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fideloper/TrustedProxy/zipball/b270bfa52915e55cfb67faa0f16863a479c36121", - "reference": "b270bfa52915e55cfb67faa0f16863a479c36121", + "url": "https://api.github.com/repos/erusev/parsedown/zipball/1bf24f7334fe16c88bf9d467863309ceaf285b01", + "reference": "1bf24f7334fe16c88bf9d467863309ceaf285b01", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Parsedown": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Emanuil Rusev", + "email": "hello@erusev.com", + "homepage": "http://erusev.com" + } + ], + "description": "Parser for Markdown.", + "homepage": "http://parsedown.org", + "keywords": [ + "markdown", + "parser" + ], + "time": "2017-03-29 16:04:15" + }, + { + "name": "fideloper/proxy", + "version": "3.3.0", + "source": { + "type": "git", + "url": "https://github.com/fideloper/TrustedProxy.git", + "reference": "4ac60dbd4bcd6636bf231ea0fd87c40ece4bdce0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fideloper/TrustedProxy/zipball/4ac60dbd4bcd6636bf231ea0fd87c40ece4bdce0", + "reference": "4ac60dbd4bcd6636bf231ea0fd87c40ece4bdce0", "shasum": "" }, "require": { @@ -959,7 +861,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "3.3-dev" } }, "autoload": { @@ -983,25 +885,25 @@ "proxy", "trusted proxy" ], - "time": "2016-12-20 14:23:22" + "time": "2017-03-23 23:17:29" }, { "name": "guzzlehttp/guzzle", - "version": "6.2.2", + "version": "6.2.3", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "ebf29dee597f02f09f4d5bbecc68230ea9b08f60" + "reference": "8d6c6cc55186db87b7dc5009827429ba4e9dc006" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/ebf29dee597f02f09f4d5bbecc68230ea9b08f60", - "reference": "ebf29dee597f02f09f4d5bbecc68230ea9b08f60", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/8d6c6cc55186db87b7dc5009827429ba4e9dc006", + "reference": "8d6c6cc55186db87b7dc5009827429ba4e9dc006", "shasum": "" }, "require": { "guzzlehttp/promises": "^1.0", - "guzzlehttp/psr7": "^1.3.1", + "guzzlehttp/psr7": "^1.4", "php": ">=5.5" }, "require-dev": { @@ -1045,7 +947,7 @@ "rest", "web service" ], - "time": "2016-10-08 15:01:37" + "time": "2017-02-28 22:50:30" }, { "name": "guzzlehttp/promises", @@ -1100,16 +1002,16 @@ }, { "name": "guzzlehttp/psr7", - "version": "1.3.1", + "version": "1.4.2", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "5c6447c9df362e8f8093bda8f5d8873fe5c7f65b" + "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/5c6447c9df362e8f8093bda8f5d8873fe5c7f65b", - "reference": "5c6447c9df362e8f8093bda8f5d8873fe5c7f65b", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c", + "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c", "shasum": "" }, "require": { @@ -1145,29 +1047,36 @@ "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Schultze", + "homepage": "https://github.com/Tobion" } ], - "description": "PSR-7 message implementation", + "description": "PSR-7 message implementation that also provides common utility methods", "keywords": [ "http", "message", + "request", + "response", "stream", - "uri" + "uri", + "url" ], - "time": "2016-06-24 23:00:38" + "time": "2017-03-20 17:10:46" }, { "name": "igaster/laravel-theme", - "version": "v1.1.3", + "version": "v1.14", "source": { "type": "git", "url": "https://github.com/igaster/laravel-theme.git", - "reference": "d1b504b80e45a9fb40dffa1ae3d34ea364691da1" + "reference": "2f15e330c6fb3fec3a8ebbe424f4f22933ce3afb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/igaster/laravel-theme/zipball/d1b504b80e45a9fb40dffa1ae3d34ea364691da1", - "reference": "d1b504b80e45a9fb40dffa1ae3d34ea364691da1", + "url": "https://api.github.com/repos/igaster/laravel-theme/zipball/2f15e330c6fb3fec3a8ebbe424f4f22933ce3afb", + "reference": "2f15e330c6fb3fec3a8ebbe424f4f22933ce3afb", "shasum": "" }, "require": { @@ -1210,7 +1119,7 @@ "themes", "views" ], - "time": "2016-06-04 07:19:01" + "time": "2017-03-30 11:50:54" }, { "name": "jakub-onderka/php-console-color", @@ -1299,64 +1208,6 @@ ], "time": "2015-04-20 18:58:01" }, - { - "name": "jeremeamia/SuperClosure", - "version": "2.3.0", - "source": { - "type": "git", - "url": "https://github.com/jeremeamia/super_closure.git", - "reference": "443c3df3207f176a1b41576ee2a66968a507b3db" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/jeremeamia/super_closure/zipball/443c3df3207f176a1b41576ee2a66968a507b3db", - "reference": "443c3df3207f176a1b41576ee2a66968a507b3db", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^1.2|^2.0|^3.0", - "php": ">=5.4", - "symfony/polyfill-php56": "^1.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0|^5.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.3-dev" - } - }, - "autoload": { - "psr-4": { - "SuperClosure\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jeremy Lindblom", - "email": "jeremeamia@gmail.com", - "homepage": "https://github.com/jeremeamia", - "role": "Developer" - } - ], - "description": "Serialize Closure objects, including their context and binding", - "homepage": "https://github.com/jeremeamia/super_closure", - "keywords": [ - "closure", - "function", - "lambda", - "parser", - "serializable", - "serialize", - "tokenizer" - ], - "time": "2016-12-07 09:37:55" - }, { "name": "laracasts/utilities", "version": "2.1", @@ -1403,42 +1254,40 @@ }, { "name": "laravel/framework", - "version": "v5.3.21", + "version": "v5.4.18", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "140ad823863d5cc6f4580f1cdf9b18b9a6a457f4" + "reference": "d15ded55b5e690199393ea93a57edcbae20d1ffc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/140ad823863d5cc6f4580f1cdf9b18b9a6a457f4", - "reference": "140ad823863d5cc6f4580f1cdf9b18b9a6a457f4", + "url": "https://api.github.com/repos/laravel/framework/zipball/d15ded55b5e690199393ea93a57edcbae20d1ffc", + "reference": "d15ded55b5e690199393ea93a57edcbae20d1ffc", "shasum": "" }, "require": { - "classpreloader/classpreloader": "~3.0", "doctrine/inflector": "~1.0", + "erusev/parsedown": "~1.6", "ext-mbstring": "*", "ext-openssl": "*", - "jeremeamia/superclosure": "~2.2", "league/flysystem": "~1.0", "monolog/monolog": "~1.11", "mtdowling/cron-expression": "~1.0", "nesbot/carbon": "~1.20", "paragonie/random_compat": "~1.4|~2.0", "php": ">=5.6.4", - "psy/psysh": "0.7.*", "ramsey/uuid": "~3.0", - "swiftmailer/swiftmailer": "~5.1", - "symfony/console": "3.1.*", - "symfony/debug": "3.1.*", - "symfony/finder": "3.1.*", - "symfony/http-foundation": "3.1.*", - "symfony/http-kernel": "3.1.*", - "symfony/process": "3.1.*", - "symfony/routing": "3.1.*", - "symfony/translation": "3.1.*", - "symfony/var-dumper": "3.1.*", + "swiftmailer/swiftmailer": "~5.4", + "symfony/console": "~3.2", + "symfony/debug": "~3.2", + "symfony/finder": "~3.2", + "symfony/http-foundation": "~3.2", + "symfony/http-kernel": "~3.2", + "symfony/process": "~3.2", + "symfony/routing": "~3.2", + "symfony/var-dumper": "~3.2", + "tijsverkoyen/css-to-inline-styles": "~2.2", "vlucas/phpdotenv": "~2.2" }, "replace": { @@ -1475,31 +1324,34 @@ }, "require-dev": { "aws/aws-sdk-php": "~3.0", + "doctrine/dbal": "~2.5", "mockery/mockery": "~0.9.4", "pda/pheanstalk": "~3.0", - "phpunit/phpunit": "~5.4", + "phpunit/phpunit": "~5.7", "predis/predis": "~1.0", - "symfony/css-selector": "3.1.*", - "symfony/dom-crawler": "3.1.*" + "symfony/css-selector": "~3.2", + "symfony/dom-crawler": "~3.2" }, "suggest": { "aws/aws-sdk-php": "Required to use the SQS queue driver and SES mail driver (~3.0).", - "doctrine/dbal": "Required to rename columns and drop SQLite columns (~2.4).", + "doctrine/dbal": "Required to rename columns and drop SQLite columns (~2.5).", "fzaninotto/faker": "Required to use the eloquent factory builder (~1.4).", - "guzzlehttp/guzzle": "Required to use the Mailgun and Mandrill mail drivers and the ping methods on schedules (~5.3|~6.0).", + "guzzlehttp/guzzle": "Required to use the Mailgun and Mandrill mail drivers and the ping methods on schedules (~6.0).", + "laravel/tinker": "Required to use the tinker console command (~1.0).", "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (~1.0).", "league/flysystem-rackspace": "Required to use the Flysystem Rackspace driver (~1.0).", + "nexmo/client": "Required to use the Nexmo transport (~1.0).", "pda/pheanstalk": "Required to use the beanstalk queue driver (~3.0).", "predis/predis": "Required to use the redis cache and queue drivers (~1.0).", "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (~2.0).", - "symfony/css-selector": "Required to use some of the crawler integration testing tools (3.1.*).", - "symfony/dom-crawler": "Required to use most of the crawler integration testing tools (3.1.*).", - "symfony/psr-http-message-bridge": "Required to use psr7 bridging features (0.2.*)." + "symfony/css-selector": "Required to use some of the crawler integration testing tools (~3.2).", + "symfony/dom-crawler": "Required to use most of the crawler integration testing tools (~3.2).", + "symfony/psr-http-message-bridge": "Required to psr7 bridging features (0.2.*)." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.3-dev" + "dev-master": "5.4-dev" } }, "autoload": { @@ -1527,20 +1379,78 @@ "framework", "laravel" ], - "time": "2016-10-26 13:27:05" + "time": "2017-04-10 19:27:41" }, { - "name": "league/flysystem", - "version": "1.0.35", + "name": "laravel/tinker", + "version": "v1.0.0", "source": { "type": "git", - "url": "https://github.com/thephpleague/flysystem.git", - "reference": "dda7f3ab94158a002d9846a97dc18ebfb7acc062" + "url": "https://github.com/laravel/tinker.git", + "reference": "3d5b675b55b24ccbf86395964042dbe061d5a965" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/dda7f3ab94158a002d9846a97dc18ebfb7acc062", - "reference": "dda7f3ab94158a002d9846a97dc18ebfb7acc062", + "url": "https://api.github.com/repos/laravel/tinker/zipball/3d5b675b55b24ccbf86395964042dbe061d5a965", + "reference": "3d5b675b55b24ccbf86395964042dbe061d5a965", + "shasum": "" + }, + "require": { + "illuminate/console": "~5.1", + "illuminate/contracts": "~5.1", + "illuminate/support": "~5.1", + "php": ">=5.5.9", + "psy/psysh": "0.7.*|0.8.*", + "symfony/var-dumper": "~3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0|~5.0" + }, + "suggest": { + "illuminate/database": "The Illuminate Database package (~5.1)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Tinker\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Powerful REPL for the Laravel framework.", + "keywords": [ + "REPL", + "Tinker", + "laravel", + "psysh" + ], + "time": "2016-12-30 18:13:17" + }, + { + "name": "league/flysystem", + "version": "1.0.37", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "78b5cc4feb61a882302df4fbaf63b7662e5e4ccd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/78b5cc4feb61a882302df4fbaf63b7662e5e4ccd", + "reference": "78b5cc4feb61a882302df4fbaf63b7662e5e4ccd", "shasum": "" }, "require": { @@ -1610,7 +1520,7 @@ "sftp", "storage" ], - "time": "2017-02-09 11:33:58" + "time": "2017-03-22 15:43:14" }, { "name": "league/fractal", @@ -1677,24 +1587,24 @@ }, { "name": "lord/laroute", - "version": "v2.3.0", + "version": "v2.4.4", "source": { "type": "git", "url": "https://github.com/aaronlord/laroute.git", - "reference": "97a3812af4bcc4cad87c52c142f407c270e20f09" + "reference": "2adee9daa5491f1ad7b953fc01df36ebc7294c3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aaronlord/laroute/zipball/97a3812af4bcc4cad87c52c142f407c270e20f09", - "reference": "97a3812af4bcc4cad87c52c142f407c270e20f09", + "url": "https://api.github.com/repos/aaronlord/laroute/zipball/2adee9daa5491f1ad7b953fc01df36ebc7294c3e", + "reference": "2adee9daa5491f1ad7b953fc01df36ebc7294c3e", "shasum": "" }, "require": { - "illuminate/config": "5.0.*|5.1.*|5.2.*|5.3.*", - "illuminate/console": "5.0.*|5.1.*|5.2.*|5.3.*", - "illuminate/filesystem": "5.0.*|5.1.*|5.2.*|5.3.*", - "illuminate/routing": "5.0.*|5.1.*|5.2.*|5.3.*", - "illuminate/support": "5.0.*|5.1.*|5.2.*|5.3.*", + "illuminate/config": "5.0.*|5.1.*|5.2.*|5.3.*|5.4.*", + "illuminate/console": "5.0.*|5.1.*|5.2.*|5.3.*|5.4.*", + "illuminate/filesystem": "5.0.*|5.1.*|5.2.*|5.3.*|5.4.*", + "illuminate/routing": "5.0.*|5.1.*|5.2.*|5.3.*|5.4.*", + "illuminate/support": "5.0.*|5.1.*|5.2.*|5.3.*|5.4.*", "php": ">=5.4.0" }, "require-dev": { @@ -1724,20 +1634,20 @@ "routes", "routing" ], - "time": "2016-08-12 13:38:39" + "time": "2017-02-08 11:05:52" }, { "name": "maximebf/debugbar", - "version": "v1.12.0", + "version": "1.13.1", "source": { "type": "git", "url": "https://github.com/maximebf/php-debugbar.git", - "reference": "e634fbd32cd6bc3fa0e8c972b52d4bf49bab3988" + "reference": "afee79a236348e39a44cb837106b7c5b4897ac2a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/e634fbd32cd6bc3fa0e8c972b52d4bf49bab3988", - "reference": "e634fbd32cd6bc3fa0e8c972b52d4bf49bab3988", + "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/afee79a236348e39a44cb837106b7c5b4897ac2a", + "reference": "afee79a236348e39a44cb837106b7c5b4897ac2a", "shasum": "" }, "require": { @@ -1756,7 +1666,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.12-dev" + "dev-master": "1.13-dev" } }, "autoload": { @@ -1785,20 +1695,20 @@ "debug", "debugbar" ], - "time": "2016-05-15 13:11:34" + "time": "2017-01-05 08:46:19" }, { "name": "monolog/monolog", - "version": "1.22.0", + "version": "1.22.1", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "bad29cb8d18ab0315e6c477751418a82c850d558" + "reference": "1e044bc4b34e91743943479f1be7a1d5eb93add0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/bad29cb8d18ab0315e6c477751418a82c850d558", - "reference": "bad29cb8d18ab0315e6c477751418a82c850d558", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/1e044bc4b34e91743943479f1be7a1d5eb93add0", + "reference": "1e044bc4b34e91743943479f1be7a1d5eb93add0", "shasum": "" }, "require": { @@ -1863,20 +1773,20 @@ "logging", "psr-3" ], - "time": "2016-11-26 00:15:39" + "time": "2017-03-13 07:08:03" }, { "name": "mtdowling/cron-expression", - "version": "v1.1.0", + "version": "v1.2.0", "source": { "type": "git", "url": "https://github.com/mtdowling/cron-expression.git", - "reference": "c9ee7886f5a12902b225a1a12f36bb45f9ab89e5" + "reference": "9504fa9ea681b586028adaaa0877db4aecf32bad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mtdowling/cron-expression/zipball/c9ee7886f5a12902b225a1a12f36bb45f9ab89e5", - "reference": "c9ee7886f5a12902b225a1a12f36bb45f9ab89e5", + "url": "https://api.github.com/repos/mtdowling/cron-expression/zipball/9504fa9ea681b586028adaaa0877db4aecf32bad", + "reference": "9504fa9ea681b586028adaaa0877db4aecf32bad", "shasum": "" }, "require": { @@ -1887,8 +1797,8 @@ }, "type": "library", "autoload": { - "psr-0": { - "Cron": "src/" + "psr-4": { + "Cron\\": "src/Cron/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1907,7 +1817,7 @@ "cron", "schedule" ], - "time": "2016-01-26 21:23:30" + "time": "2017-01-23 04:29:33" }, { "name": "mtdowling/jmespath.php", @@ -1966,26 +1876,32 @@ }, { "name": "nesbot/carbon", - "version": "1.21.0", + "version": "1.22.1", "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "7b08ec6f75791e130012f206e3f7b0e76e18e3d7" + "reference": "7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/7b08ec6f75791e130012f206e3f7b0e76e18e3d7", - "reference": "7b08ec6f75791e130012f206e3f7b0e76e18e3d7", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc", + "reference": "7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc", "shasum": "" }, "require": { "php": ">=5.3.0", - "symfony/translation": "~2.6|~3.0" + "symfony/translation": "~2.6 || ~3.0" }, "require-dev": { - "phpunit/phpunit": "~4.0|~5.0" + "friendsofphp/php-cs-fixer": "~2", + "phpunit/phpunit": "~4.0 || ~5.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.23-dev" + } + }, "autoload": { "psr-4": { "Carbon\\": "src/Carbon/" @@ -2009,7 +1925,7 @@ "datetime", "time" ], - "time": "2015-11-04 20:07:17" + "time": "2017-01-16 07:55:07" }, { "name": "nicolaslopezj/searchable", @@ -2059,24 +1975,24 @@ }, { "name": "nikic/php-parser", - "version": "v2.1.1", + "version": "v3.0.5", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "4dd659edadffdc2143e4753df655d866dbfeedf0" + "reference": "2b9e2f71b722f7c53918ab0c25f7646c2013f17d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4dd659edadffdc2143e4753df655d866dbfeedf0", - "reference": "4dd659edadffdc2143e4753df655d866dbfeedf0", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/2b9e2f71b722f7c53918ab0c25f7646c2013f17d", + "reference": "2b9e2f71b722f7c53918ab0c25f7646c2013f17d", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": ">=5.4" + "php": ">=5.5" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "phpunit/phpunit": "~4.0|~5.0" }, "bin": [ "bin/php-parse" @@ -2084,7 +2000,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -2106,20 +2022,20 @@ "parser", "php" ], - "time": "2016-09-16 12:04:44" + "time": "2017-03-05 18:23:57" }, { "name": "paragonie/random_compat", - "version": "v2.0.4", + "version": "v2.0.10", "source": { "type": "git", "url": "https://github.com/paragonie/random_compat.git", - "reference": "a9b97968bcde1c4de2a5ec6cbd06a0f6c919b46e" + "reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/a9b97968bcde1c4de2a5ec6cbd06a0f6c919b46e", - "reference": "a9b97968bcde1c4de2a5ec6cbd06a0f6c919b46e", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/634bae8e911eefa89c1abfbf1b66da679ac8f54d", + "reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d", "shasum": "" }, "require": { @@ -2154,153 +2070,7 @@ "pseudorandom", "random" ], - "time": "2016-11-07 23:38:38" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "1.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c", - "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c", - "shasum": "" - }, - "require": { - "php": ">=5.5" - }, - "require-dev": { - "phpunit/phpunit": "^4.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "time": "2015-12-27 11:43:31" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "3.1.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/8331b5efe816ae05461b7ca1e721c01b46bafb3e", - "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e", - "shasum": "" - }, - "require": { - "php": ">=5.5", - "phpdocumentor/reflection-common": "^1.0@dev", - "phpdocumentor/type-resolver": "^0.2.0", - "webmozart/assert": "^1.0" - }, - "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^4.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2016-09-30 07:12:33" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "0.2.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", - "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", - "shasum": "" - }, - "require": { - "php": ">=5.5", - "phpdocumentor/reflection-common": "^1.0" - }, - "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^5.2||^4.8.24" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "time": "2016-11-25 06:54:22" + "time": "2017-03-13 16:27:32" }, { "name": "pragmarx/google2fa", @@ -2415,16 +2185,16 @@ }, { "name": "prologue/alerts", - "version": "0.4.0", + "version": "0.4.1", "source": { "type": "git", - "url": "git@github.com:driesvints/Alerts.git", - "reference": "f4ea1e784070f43ddd12dab732552809e54ff7d5" + "url": "https://github.com/prologuephp/alerts.git", + "reference": "2a7184a9f39ab6f6dde85dbe0b9c783241bf52ea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/driesvints/Alerts/zipball/f4ea1e784070f43ddd12dab732552809e54ff7d5", - "reference": "f4ea1e784070f43ddd12dab732552809e54ff7d5", + "url": "https://api.github.com/repos/prologuephp/alerts/zipball/2a7184a9f39ab6f6dde85dbe0b9c783241bf52ea", + "reference": "2a7184a9f39ab6f6dde85dbe0b9c783241bf52ea", "shasum": "" }, "require": { @@ -2461,7 +2231,7 @@ "laravel", "messages" ], - "time": "2015-04-13 17:27:18" + "time": "2017-01-24 13:22:25" }, { "name": "psr/http-message", @@ -2562,37 +2332,38 @@ }, { "name": "psy/psysh", - "version": "v0.7.2", + "version": "v0.8.3", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "e64e10b20f8d229cac76399e1f3edddb57a0f280" + "reference": "1dd4bbbc64d71e7ec075ffe82b42d9e096dc8d5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/e64e10b20f8d229cac76399e1f3edddb57a0f280", - "reference": "e64e10b20f8d229cac76399e1f3edddb57a0f280", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/1dd4bbbc64d71e7ec075ffe82b42d9e096dc8d5e", + "reference": "1dd4bbbc64d71e7ec075ffe82b42d9e096dc8d5e", "shasum": "" }, "require": { "dnoegel/php-xdg-base-dir": "0.1", "jakub-onderka/php-console-highlighter": "0.3.*", - "nikic/php-parser": "^1.2.1|~2.0", + "nikic/php-parser": "~1.3|~2.0|~3.0", "php": ">=5.3.9", "symfony/console": "~2.3.10|^2.4.2|~3.0", "symfony/var-dumper": "~2.7|~3.0" }, "require-dev": { - "fabpot/php-cs-fixer": "~1.5", - "phpunit/phpunit": "~3.7|~4.0|~5.0", - "squizlabs/php_codesniffer": "~2.0", + "friendsofphp/php-cs-fixer": "~1.11", + "hoa/console": "~3.16|~1.14", + "phpunit/phpunit": "~4.4|~5.0", "symfony/finder": "~2.1|~3.0" }, "suggest": { "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", "ext-pdo-sqlite": "The doc command requires SQLite to work.", "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well.", - "ext-readline": "Enables support for arrow-key history navigation, and showing and manipulating command history." + "ext-readline": "Enables support for arrow-key history navigation, and showing and manipulating command history.", + "hoa/console": "A pure PHP readline implementation. You'll want this if your PHP install doesn't already support readline or libedit." }, "bin": [ "bin/psysh" @@ -2600,7 +2371,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-develop": "0.8.x-dev" + "dev-develop": "0.9.x-dev" } }, "autoload": { @@ -2630,34 +2401,34 @@ "interactive", "shell" ], - "time": "2016-03-09 05:03:14" + "time": "2017-03-19 21:40:44" }, { "name": "ramsey/uuid", - "version": "3.5.2", + "version": "3.6.1", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "5677cfe02397dd6b58c861870dfaa5d9007d3954" + "reference": "4ae32dd9ab8860a4bbd750ad269cba7f06f7934e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/5677cfe02397dd6b58c861870dfaa5d9007d3954", - "reference": "5677cfe02397dd6b58c861870dfaa5d9007d3954", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/4ae32dd9ab8860a4bbd750ad269cba7f06f7934e", + "reference": "4ae32dd9ab8860a4bbd750ad269cba7f06f7934e", "shasum": "" }, "require": { "paragonie/random_compat": "^1.0|^2.0", - "php": ">=5.4" + "php": "^5.4 || ^7.0" }, "replace": { "rhumsaa/uuid": "self.version" }, "require-dev": { "apigen/apigen": "^4.1", - "codeception/aspect-mock": "1.0.0", + "codeception/aspect-mock": "^1.0 | ^2.0", "doctrine/annotations": "~1.2.0", - "goaop/framework": "1.0.0-alpha.2", + "goaop/framework": "1.0.0-alpha.2 | ^1.0 | ^2.1", "ircmaxell/random-lib": "^1.1", "jakub-onderka/php-parallel-lint": "^0.9.0", "mockery/mockery": "^0.9.4", @@ -2712,7 +2483,7 @@ "identifier", "uuid" ], - "time": "2016-11-22 19:21:44" + "time": "2017-03-26 20:37:53" }, { "name": "s1lentium/iptools", @@ -2765,17 +2536,126 @@ "time": "2016-08-21 15:57:09" }, { - "name": "swiftmailer/swiftmailer", - "version": "v5.4.5", + "name": "spatie/fractalistic", + "version": "1.0.2", "source": { "type": "git", - "url": "https://github.com/swiftmailer/swiftmailer.git", - "reference": "cd142238a339459b10da3d8234220963f392540c" + "url": "https://github.com/spatie/fractalistic.git", + "reference": "59a2e7dfbd49e58b7de7d5621c1e7365786809f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/cd142238a339459b10da3d8234220963f392540c", - "reference": "cd142238a339459b10da3d8234220963f392540c", + "url": "https://api.github.com/repos/spatie/fractalistic/zipball/59a2e7dfbd49e58b7de7d5621c1e7365786809f9", + "reference": "59a2e7dfbd49e58b7de7d5621c1e7365786809f9", + "shasum": "" + }, + "require": { + "league/fractal": "^0.14.0|^0.15.0", + "php": "^5.6|^7.0" + }, + "require-dev": { + "illuminate/pagination": "^5.3", + "phpunit/phpunit": "^5.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\Fractalistic\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "A developer friendly wrapper around Fractal", + "homepage": "https://github.com/spatie/fractalistic", + "keywords": [ + "api", + "fractal", + "fractalistic", + "spatie", + "transform" + ], + "time": "2017-01-19 20:51:03" + }, + { + "name": "spatie/laravel-fractal", + "version": "3.5.0", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-fractal.git", + "reference": "16689b0ce340bc139ba767f0bf2904e4eab2b254" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-fractal/zipball/16689b0ce340bc139ba767f0bf2904e4eab2b254", + "reference": "16689b0ce340bc139ba767f0bf2904e4eab2b254", + "shasum": "" + }, + "require": { + "illuminate/contracts": "~5.1.0|~5.2.0|~5.3.0|~5.4.0", + "illuminate/support": "~5.1.0|~5.2.0|~5.3.0|~5.4.0", + "php": "^5.6|^7.0", + "spatie/fractalistic": "^1.0" + }, + "require-dev": { + "orchestra/testbench": "~3.2.0|3.3.0|~3.4.0", + "phpunit/phpunit": "^5.7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\Fractal\\": "src" + }, + "files": [ + "src/helpers.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "A Fractal service provider for Laravel and Lumen", + "homepage": "https://github.com/spatie/laravel-fractal", + "keywords": [ + "api", + "fractal", + "laravel", + "laravel-fractal", + "lumen", + "spatie", + "transform" + ], + "time": "2017-02-21 23:17:02" + }, + { + "name": "swiftmailer/swiftmailer", + "version": "v5.4.6", + "source": { + "type": "git", + "url": "https://github.com/swiftmailer/swiftmailer.git", + "reference": "81fdccfaf8bdc5d5d7a1ef6bb3a61bbb1a6c4a3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/81fdccfaf8bdc5d5d7a1ef6bb3a61bbb1a6c4a3e", + "reference": "81fdccfaf8bdc5d5d7a1ef6bb3a61bbb1a6c4a3e", "shasum": "" }, "require": { @@ -2816,20 +2696,20 @@ "mail", "mailer" ], - "time": "2016-12-29 10:02:40" + "time": "2017-02-13 07:52:53" }, { "name": "symfony/console", - "version": "v3.1.10", + "version": "v3.2.7", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "047f16485d68c083bd5d9b73ff16f9cb9c1a9f52" + "reference": "c30243cc51f726812be3551316b109a2f5deaf8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/047f16485d68c083bd5d9b73ff16f9cb9c1a9f52", - "reference": "047f16485d68c083bd5d9b73ff16f9cb9c1a9f52", + "url": "https://api.github.com/repos/symfony/console/zipball/c30243cc51f726812be3551316b109a2f5deaf8d", + "reference": "c30243cc51f726812be3551316b109a2f5deaf8d", "shasum": "" }, "require": { @@ -2840,17 +2720,19 @@ "require-dev": { "psr/log": "~1.0", "symfony/event-dispatcher": "~2.8|~3.0", + "symfony/filesystem": "~2.8|~3.0", "symfony/process": "~2.8|~3.0" }, "suggest": { "psr/log": "For using the console logger", "symfony/event-dispatcher": "", + "symfony/filesystem": "", "symfony/process": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "3.2-dev" } }, "autoload": { @@ -2877,20 +2759,73 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2017-01-08 20:43:43" + "time": "2017-04-04 14:33:42" }, { - "name": "symfony/debug", - "version": "v3.1.10", + "name": "symfony/css-selector", + "version": "v3.2.7", "source": { "type": "git", - "url": "https://github.com/symfony/debug.git", - "reference": "c6661361626b3cf5cf2089df98b3b5006a197e85" + "url": "https://github.com/symfony/css-selector.git", + "reference": "a48f13dc83c168f1253a5d2a5a4fb46c36244c4c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/c6661361626b3cf5cf2089df98b3b5006a197e85", - "reference": "c6661361626b3cf5cf2089df98b3b5006a197e85", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/a48f13dc83c168f1253a5d2a5a4fb46c36244c4c", + "reference": "a48f13dc83c168f1253a5d2a5a4fb46c36244c4c", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony CssSelector Component", + "homepage": "https://symfony.com", + "time": "2017-02-21 09:12:04" + }, + { + "name": "symfony/debug", + "version": "v3.2.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/debug.git", + "reference": "56f613406446a4a0a031475cfd0a01751de22659" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/debug/zipball/56f613406446a4a0a031475cfd0a01751de22659", + "reference": "56f613406446a4a0a031475cfd0a01751de22659", "shasum": "" }, "require": { @@ -2907,7 +2842,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "3.2-dev" } }, "autoload": { @@ -2934,20 +2869,20 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2017-01-28 00:04:57" + "time": "2017-03-28 21:38:24" }, { "name": "symfony/event-dispatcher", - "version": "v3.2.3", + "version": "v3.2.7", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "9137eb3a3328e413212826d63eeeb0217836e2b6" + "reference": "154bb1ef7b0e42ccc792bd53edbce18ed73440ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9137eb3a3328e413212826d63eeeb0217836e2b6", - "reference": "9137eb3a3328e413212826d63eeeb0217836e2b6", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/154bb1ef7b0e42ccc792bd53edbce18ed73440ca", + "reference": "154bb1ef7b0e42ccc792bd53edbce18ed73440ca", "shasum": "" }, "require": { @@ -2994,20 +2929,20 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2017-01-02 20:32:22" + "time": "2017-04-04 07:26:27" }, { "name": "symfony/finder", - "version": "v3.1.10", + "version": "v3.2.7", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "59687a255d1562f2c17b012418273862083d85f7" + "reference": "b20900ce5ea164cd9314af52725b0bb5a758217a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/59687a255d1562f2c17b012418273862083d85f7", - "reference": "59687a255d1562f2c17b012418273862083d85f7", + "url": "https://api.github.com/repos/symfony/finder/zipball/b20900ce5ea164cd9314af52725b0bb5a758217a", + "reference": "b20900ce5ea164cd9314af52725b0bb5a758217a", "shasum": "" }, "require": { @@ -3016,7 +2951,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "3.2-dev" } }, "autoload": { @@ -3043,20 +2978,20 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2017-01-02 20:31:54" + "time": "2017-03-20 09:32:19" }, { "name": "symfony/http-foundation", - "version": "v3.1.10", + "version": "v3.2.7", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "cef0ad49a2e90455cfc649522025b5a2929648c0" + "reference": "cb0b6418f588952c9290b3df4ca650f1b7ab570a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/cef0ad49a2e90455cfc649522025b5a2929648c0", - "reference": "cef0ad49a2e90455cfc649522025b5a2929648c0", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/cb0b6418f588952c9290b3df4ca650f1b7ab570a", + "reference": "cb0b6418f588952c9290b3df4ca650f1b7ab570a", "shasum": "" }, "require": { @@ -3069,7 +3004,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "3.2-dev" } }, "autoload": { @@ -3096,20 +3031,20 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2017-01-08 20:43:43" + "time": "2017-04-04 15:30:56" }, { "name": "symfony/http-kernel", - "version": "v3.1.10", + "version": "v3.2.7", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "c830387dec1b48c100473d10a6a356c3c3ae2a13" + "reference": "8285ab5faf1306b1a5ebcf287fe91c231a6de88e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/c830387dec1b48c100473d10a6a356c3c3ae2a13", - "reference": "c830387dec1b48c100473d10a6a356c3c3ae2a13", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/8285ab5faf1306b1a5ebcf287fe91c231a6de88e", + "reference": "8285ab5faf1306b1a5ebcf287fe91c231a6de88e", "shasum": "" }, "require": { @@ -3137,7 +3072,7 @@ "symfony/stopwatch": "~2.8|~3.0", "symfony/templating": "~2.8|~3.0", "symfony/translation": "~2.8|~3.0", - "symfony/var-dumper": "~2.8|~3.0" + "symfony/var-dumper": "~3.2" }, "suggest": { "symfony/browser-kit": "", @@ -3151,7 +3086,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "3.2-dev" } }, "autoload": { @@ -3178,7 +3113,7 @@ ], "description": "Symfony HttpKernel Component", "homepage": "https://symfony.com", - "time": "2017-01-28 02:53:17" + "time": "2017-04-05 12:52:03" }, { "name": "symfony/polyfill-mbstring", @@ -3349,16 +3284,16 @@ }, { "name": "symfony/process", - "version": "v3.1.10", + "version": "v3.2.7", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "2605753c5f8c531623d24d002825ebb1d6a22248" + "reference": "57fdaa55827ae14d617550ebe71a820f0a5e2282" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/2605753c5f8c531623d24d002825ebb1d6a22248", - "reference": "2605753c5f8c531623d24d002825ebb1d6a22248", + "url": "https://api.github.com/repos/symfony/process/zipball/57fdaa55827ae14d617550ebe71a820f0a5e2282", + "reference": "57fdaa55827ae14d617550ebe71a820f0a5e2282", "shasum": "" }, "require": { @@ -3367,7 +3302,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "3.2-dev" } }, "autoload": { @@ -3394,20 +3329,20 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2017-01-21 17:13:55" + "time": "2017-03-27 18:07:02" }, { "name": "symfony/routing", - "version": "v3.1.10", + "version": "v3.2.7", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "f25581d4eb0a82962c291917f826166f0dcd8a9a" + "reference": "d6605f9a5767bc5bc4895e1c762ba93964608aee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/f25581d4eb0a82962c291917f826166f0dcd8a9a", - "reference": "f25581d4eb0a82962c291917f826166f0dcd8a9a", + "url": "https://api.github.com/repos/symfony/routing/zipball/d6605f9a5767bc5bc4895e1c762ba93964608aee", + "reference": "d6605f9a5767bc5bc4895e1c762ba93964608aee", "shasum": "" }, "require": { @@ -3436,7 +3371,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "3.2-dev" } }, "autoload": { @@ -3469,20 +3404,20 @@ "uri", "url" ], - "time": "2017-01-28 00:04:57" + "time": "2017-03-02 15:58:09" }, { "name": "symfony/translation", - "version": "v3.1.10", + "version": "v3.2.7", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "d5a20fab5f63f44c233c69b3041c3cb1d4945e45" + "reference": "c740eee70783d2af4d3d6b70d5146f209e6b4d13" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/d5a20fab5f63f44c233c69b3041c3cb1d4945e45", - "reference": "d5a20fab5f63f44c233c69b3041c3cb1d4945e45", + "url": "https://api.github.com/repos/symfony/translation/zipball/c740eee70783d2af4d3d6b70d5146f209e6b4d13", + "reference": "c740eee70783d2af4d3d6b70d5146f209e6b4d13", "shasum": "" }, "require": { @@ -3495,7 +3430,7 @@ "require-dev": { "psr/log": "~1.0", "symfony/config": "~2.8|~3.0", - "symfony/intl": "~2.8|~3.0", + "symfony/intl": "^2.8.18|^3.2.5", "symfony/yaml": "~2.8|~3.0" }, "suggest": { @@ -3506,7 +3441,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "3.2-dev" } }, "autoload": { @@ -3533,26 +3468,29 @@ ], "description": "Symfony Translation Component", "homepage": "https://symfony.com", - "time": "2017-01-21 17:01:39" + "time": "2017-03-21 21:44:32" }, { "name": "symfony/var-dumper", - "version": "v3.1.10", + "version": "v3.2.7", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "16df11647e5b992d687cb4eeeb9a882d5f5c26b9" + "reference": "81dce20f69a8b40427e1f4e6462178df87cafc03" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/16df11647e5b992d687cb4eeeb9a882d5f5c26b9", - "reference": "16df11647e5b992d687cb4eeeb9a882d5f5c26b9", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/81dce20f69a8b40427e1f4e6462178df87cafc03", + "reference": "81dce20f69a8b40427e1f4e6462178df87cafc03", "shasum": "" }, "require": { "php": ">=5.5.9", "symfony/polyfill-mbstring": "~1.0" }, + "conflict": { + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0" + }, "require-dev": { "twig/twig": "~1.20|~2.0" }, @@ -3562,7 +3500,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "3.2-dev" } }, "autoload": { @@ -3596,7 +3534,54 @@ "debug", "dump" ], - "time": "2017-01-24 13:02:38" + "time": "2017-03-12 16:07:05" + }, + { + "name": "tijsverkoyen/css-to-inline-styles", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", + "reference": "ab03919dfd85a74ae0372f8baf9f3c7d5c03b04b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/ab03919dfd85a74ae0372f8baf9f3c7d5c03b04b", + "reference": "ab03919dfd85a74ae0372f8baf9f3c7d5c03b04b", + "shasum": "" + }, + "require": { + "php": "^5.5 || ^7", + "symfony/css-selector": "^2.7|~3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8|5.1.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "TijsVerkoyen\\CssToInlineStyles\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Tijs Verkoyen", + "email": "css_to_inline_styles@verkoyen.eu", + "role": "Developer" + } + ], + "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.", + "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", + "time": "2016-09-20 12:50:39" }, { "name": "vlucas/phpdotenv", @@ -3648,56 +3633,6 @@ ], "time": "2016-09-01 10:05:43" }, - { - "name": "webmozart/assert", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f", - "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.6", - "sebastian/version": "^1.0.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "time": "2016-11-23 20:04:58" - }, { "name": "webpatser/laravel-uuid", "version": "2.0.1", @@ -4009,64 +3944,18 @@ ], "time": "2015-05-11 14:41:42" }, - { - "name": "laravel/homestead", - "version": "v3.0.2", - "source": { - "type": "git", - "url": "https://github.com/laravel/homestead.git", - "reference": "705449c3dbedbded4bd4f3ed725303c69253cad4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laravel/homestead/zipball/705449c3dbedbded4bd4f3ed725303c69253cad4", - "reference": "705449c3dbedbded4bd4f3ed725303c69253cad4", - "shasum": "" - }, - "require": { - "php": ">=5.5.9", - "symfony/console": "~2.3|~3.0", - "symfony/process": "~2.3|~3.0" - }, - "bin": [ - "homestead" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "psr-4": { - "Laravel\\Homestead\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylorotwell@gmail.com" - } - ], - "description": "A virtual machine for web artisans.", - "time": "2016-02-16 22:31:00" - }, { "name": "mockery/mockery", - "version": "0.9.8", + "version": "0.9.9", "source": { "type": "git", - "url": "https://github.com/padraic/mockery.git", - "reference": "1e5e2ffdc4d71d7358ed58a6fdd30a4a0c506855" + "url": "https://github.com/mockery/mockery.git", + "reference": "6fdb61243844dc924071d3404bb23994ea0b6856" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/padraic/mockery/zipball/1e5e2ffdc4d71d7358ed58a6fdd30a4a0c506855", - "reference": "1e5e2ffdc4d71d7358ed58a6fdd30a4a0c506855", + "url": "https://api.github.com/repos/mockery/mockery/zipball/6fdb61243844dc924071d3404bb23994ea0b6856", + "reference": "6fdb61243844dc924071d3404bb23994ea0b6856", "shasum": "" }, "require": { @@ -4118,20 +4007,20 @@ "test double", "testing" ], - "time": "2017-02-09 13:29:38" + "time": "2017-02-28 12:52:32" }, { "name": "myclabs/deep-copy", - "version": "1.6.0", + "version": "1.6.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "5a5a9fc8025a08d8919be87d6884d5a92520cefe" + "reference": "8e6e04167378abf1ddb4d3522d8755c5fd90d102" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/5a5a9fc8025a08d8919be87d6884d5a92520cefe", - "reference": "5a5a9fc8025a08d8919be87d6884d5a92520cefe", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/8e6e04167378abf1ddb4d3522d8755c5fd90d102", + "reference": "8e6e04167378abf1ddb4d3522d8755c5fd90d102", "shasum": "" }, "require": { @@ -4160,31 +4049,177 @@ "object", "object graph" ], - "time": "2017-01-26 22:05:40" + "time": "2017-04-12 18:52:22" }, { - "name": "phpspec/prophecy", - "version": "v1.6.2", + "name": "phpdocumentor/reflection-common", + "version": "1.0", "source": { "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "6c52c2722f8460122f96f86346600e1077ce22cb" + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/6c52c2722f8460122f96f86346600e1077ce22cb", - "reference": "6c52c2722f8460122f96f86346600e1077ce22cb", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c", + "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^4.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "time": "2015-12-27 11:43:31" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/8331b5efe816ae05461b7ca1e721c01b46bafb3e", + "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e", + "shasum": "" + }, + "require": { + "php": ">=5.5", + "phpdocumentor/reflection-common": "^1.0@dev", + "phpdocumentor/type-resolver": "^0.2.0", + "webmozart/assert": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^4.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "time": "2016-09-30 07:12:33" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "0.2.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", + "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", + "shasum": "" + }, + "require": { + "php": ">=5.5", + "phpdocumentor/reflection-common": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^5.2||^4.8.24" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "time": "2016-11-25 06:54:22" + }, + { + "name": "phpspec/prophecy", + "version": "v1.7.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "93d39f1f7f9326d746203c7c056f300f7f126073" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/93d39f1f7f9326d746203c7c056f300f7f126073", + "reference": "93d39f1f7f9326d746203c7c056f300f7f126073", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", - "sebastian/comparator": "^1.1", - "sebastian/recursion-context": "^1.0|^2.0" + "sebastian/comparator": "^1.1|^2.0", + "sebastian/recursion-context": "^1.0|^2.0|^3.0" }, "require-dev": { - "phpspec/phpspec": "^2.0", + "phpspec/phpspec": "^2.5|^3.2", "phpunit/phpunit": "^4.8 || ^5.6.5" }, "type": "library", @@ -4223,39 +4258,39 @@ "spy", "stub" ], - "time": "2016-11-21 14:58:47" + "time": "2017-03-02 20:05:34" }, { "name": "phpunit/php-code-coverage", - "version": "4.0.5", + "version": "4.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "c19cfc7cbb0e9338d8c469c7eedecc2a428b0971" + "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c19cfc7cbb0e9338d8c469c7eedecc2a428b0971", - "reference": "c19cfc7cbb0e9338d8c469c7eedecc2a428b0971", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ef7b2f56815df854e66ceaee8ebe9393ae36a40d", + "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d", "shasum": "" }, "require": { + "ext-dom": "*", + "ext-xmlwriter": "*", "php": "^5.6 || ^7.0", - "phpunit/php-file-iterator": "~1.3", - "phpunit/php-text-template": "~1.2", - "phpunit/php-token-stream": "^1.4.2", - "sebastian/code-unit-reverse-lookup": "~1.0", + "phpunit/php-file-iterator": "^1.3", + "phpunit/php-text-template": "^1.2", + "phpunit/php-token-stream": "^1.4.2 || ^2.0", + "sebastian/code-unit-reverse-lookup": "^1.0", "sebastian/environment": "^1.3.2 || ^2.0", - "sebastian/version": "~1.0|~2.0" + "sebastian/version": "^1.0 || ^2.0" }, "require-dev": { - "ext-xdebug": ">=2.1.4", - "phpunit/phpunit": "^5.4" + "ext-xdebug": "^2.1.4", + "phpunit/phpunit": "^5.7" }, "suggest": { - "ext-dom": "*", - "ext-xdebug": ">=2.4.0", - "ext-xmlwriter": "*" + "ext-xdebug": "^2.5.1" }, "type": "library", "extra": { @@ -4286,7 +4321,7 @@ "testing", "xunit" ], - "time": "2017-01-20 15:06:43" + "time": "2017-04-02 07:44:40" }, { "name": "phpunit/php-file-iterator", @@ -4378,25 +4413,30 @@ }, { "name": "phpunit/php-timer", - "version": "1.0.8", + "version": "1.0.9", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260" + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/38e9124049cf1a164f1e4537caf19c99bf1eb260", - "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^5.3.3 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "~4|~5" + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, "autoload": { "classmap": [ "src/" @@ -4418,20 +4458,20 @@ "keywords": [ "timer" ], - "time": "2016-05-12 18:03:57" + "time": "2017-02-26 11:10:40" }, { "name": "phpunit/php-token-stream", - "version": "1.4.9", + "version": "1.4.11", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "3b402f65a4cc90abf6e1104e388b896ce209631b" + "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3b402f65a4cc90abf6e1104e388b896ce209631b", - "reference": "3b402f65a4cc90abf6e1104e388b896ce209631b", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e03f8f67534427a787e21a385a67ec3ca6978ea7", + "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7", "shasum": "" }, "require": { @@ -4467,20 +4507,20 @@ "keywords": [ "tokenizer" ], - "time": "2016-11-15 14:06:22" + "time": "2017-02-27 10:12:30" }, { "name": "phpunit/phpunit", - "version": "5.7.13", + "version": "5.7.19", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "60ebeed87a35ea46fd7f7d8029df2d6f013ebb34" + "reference": "69c4f49ff376af2692bad9cebd883d17ebaa98a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/60ebeed87a35ea46fd7f7d8029df2d6f013ebb34", - "reference": "60ebeed87a35ea46fd7f7d8029df2d6f013ebb34", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/69c4f49ff376af2692bad9cebd883d17ebaa98a1", + "reference": "69c4f49ff376af2692bad9cebd883d17ebaa98a1", "shasum": "" }, "require": { @@ -4504,7 +4544,7 @@ "sebastian/global-state": "^1.1", "sebastian/object-enumerator": "~2.0", "sebastian/resource-operations": "~1.0", - "sebastian/version": "~1.0|~2.0", + "sebastian/version": "~1.0.3|~2.0", "symfony/yaml": "~2.1|~3.0" }, "conflict": { @@ -4549,7 +4589,7 @@ "testing", "xunit" ], - "time": "2017-02-10 09:05:10" + "time": "2017-04-03 02:22:27" }, { "name": "phpunit/phpunit-mock-objects", @@ -4612,23 +4652,23 @@ }, { "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe" + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/c36f5e7cfce482fde5bf8d10d41a53591e0198fe", - "reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", "shasum": "" }, "require": { - "php": ">=5.6" + "php": "^5.6 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "~5" + "phpunit/phpunit": "^5.7 || ^6.0" }, "type": "library", "extra": { @@ -4653,7 +4693,7 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2016-02-13 06:45:14" + "time": "2017-03-04 06:30:41" }, { "name": "sebastian/comparator", @@ -4941,16 +4981,16 @@ }, { "name": "sebastian/object-enumerator", - "version": "2.0.0", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35" + "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35", - "reference": "96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1311872ac850040a79c3c058bea3e22d0f09cbb7", + "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7", "shasum": "" }, "require": { @@ -4983,7 +5023,7 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2016-11-19 07:35:10" + "time": "2017-02-18 15:18:39" }, { "name": "sebastian/recursion-context", @@ -5125,7 +5165,7 @@ }, { "name": "symfony/class-loader", - "version": "v3.2.6", + "version": "v3.2.7", "source": { "type": "git", "url": "https://github.com/symfony/class-loader.git", @@ -5179,127 +5219,18 @@ "homepage": "https://symfony.com", "time": "2017-02-18 17:28:00" }, - { - "name": "symfony/css-selector", - "version": "v3.1.10", - "source": { - "type": "git", - "url": "https://github.com/symfony/css-selector.git", - "reference": "722a87478a72d95dc2a3bcf41dc9c2d13fd4cb2d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/722a87478a72d95dc2a3bcf41dc9c2d13fd4cb2d", - "reference": "722a87478a72d95dc2a3bcf41dc9c2d13fd4cb2d", - "shasum": "" - }, - "require": { - "php": ">=5.5.9" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\CssSelector\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jean-François Simon", - "email": "jeanfrancois.simon@sensiolabs.com" - }, - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony CssSelector Component", - "homepage": "https://symfony.com", - "time": "2017-01-02 20:31:54" - }, - { - "name": "symfony/dom-crawler", - "version": "v3.1.10", - "source": { - "type": "git", - "url": "https://github.com/symfony/dom-crawler.git", - "reference": "7eede2a901a19928494194f7d1815a77b9a473a0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/7eede2a901a19928494194f7d1815a77b9a473a0", - "reference": "7eede2a901a19928494194f7d1815a77b9a473a0", - "shasum": "" - }, - "require": { - "php": ">=5.5.9", - "symfony/polyfill-mbstring": "~1.0" - }, - "require-dev": { - "symfony/css-selector": "~2.8|~3.0" - }, - "suggest": { - "symfony/css-selector": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\DomCrawler\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony DomCrawler Component", - "homepage": "https://symfony.com", - "time": "2017-01-21 17:13:55" - }, { "name": "symfony/yaml", - "version": "v3.2.3", + "version": "v3.2.7", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "e1718c6bf57e1efbb8793ada951584b2ab27775b" + "reference": "62b4cdb99d52cb1ff253c465eb1532a80cebb621" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/e1718c6bf57e1efbb8793ada951584b2ab27775b", - "reference": "e1718c6bf57e1efbb8793ada951584b2ab27775b", + "url": "https://api.github.com/repos/symfony/yaml/zipball/62b4cdb99d52cb1ff253c465eb1532a80cebb621", + "reference": "62b4cdb99d52cb1ff253c465eb1532a80cebb621", "shasum": "" }, "require": { @@ -5341,18 +5272,66 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2017-01-21 17:06:35" + "time": "2017-03-20 09:45:15" + }, + { + "name": "webmozart/assert", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/webmozart/assert.git", + "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f", + "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.6", + "sebastian/version": "^1.0.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "time": "2016-11-23 20:04:58" } ], "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "dingo/api": 10 - }, - "prefer-stable": false, + "stability-flags": [], + "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": ">=5.6.4" + "php": ">=7.0.0" }, "platform-dev": [] } diff --git a/config/api.php b/config/api.php deleted file mode 100644 index 2a847e7ac..000000000 --- a/config/api.php +++ /dev/null @@ -1,220 +0,0 @@ - env('API_STANDARDS_TREE', 'x'), - - /* - |-------------------------------------------------------------------------- - | API Subtype - |-------------------------------------------------------------------------- - | - | Your subtype will follow the standards tree you use when used in the - | "Accept" header to negotiate the content type and version. - | - | For example: Accept: application/x.SUBTYPE.v1+json - | - */ - - 'subtype' => env('API_SUBTYPE', ''), - - /* - |-------------------------------------------------------------------------- - | Default API Version - |-------------------------------------------------------------------------- - | - | This is the default version when strict mode is disabled and your API - | is accessed via a web browser. It's also used as the default version - | when generating your APIs documentation. - | - */ - - 'version' => env('API_VERSION', 'v1'), - - /* - |-------------------------------------------------------------------------- - | Default API Prefix - |-------------------------------------------------------------------------- - | - | A default prefix to use for your API routes so you don't have to - | specify it for each group. - | - */ - - 'prefix' => env('API_PREFIX', 'api'), - - /* - |-------------------------------------------------------------------------- - | Default API Domain - |-------------------------------------------------------------------------- - | - | A default domain to use for your API routes so you don't have to - | specify it for each group. - | - */ - - 'domain' => env('API_DOMAIN', null), - - /* - |-------------------------------------------------------------------------- - | Name - |-------------------------------------------------------------------------- - | - | When documenting your API using the API Blueprint syntax you can - | configure a default name to avoid having to manually specify - | one when using the command. - | - */ - - 'name' => env('API_NAME', null), - - /* - |-------------------------------------------------------------------------- - | Conditional Requests - |-------------------------------------------------------------------------- - | - | Globally enable conditional requests so that an ETag header is added to - | any successful response. Subsequent requests will perform a check and - | will return a 304 Not Modified. This can also be enabled or disabled - | on certain groups or routes. - | - */ - - 'conditionalRequest' => env('API_CONDITIONAL_REQUEST', true), - - /* - |-------------------------------------------------------------------------- - | Strict Mode - |-------------------------------------------------------------------------- - | - | Enabling strict mode will require clients to send a valid Accept header - | with every request. This also voids the default API version, meaning - | your API will not be browsable via a web browser. - | - */ - - 'strict' => env('API_STRICT', false), - - /* - |-------------------------------------------------------------------------- - | Debug Mode - |-------------------------------------------------------------------------- - | - | Enabling debug mode will result in error responses caused by thrown - | exceptions to have a "debug" key that will be populated with - | more detailed information on the exception. - | - */ - - 'debug' => env('API_DEBUG', false), - - /* - |-------------------------------------------------------------------------- - | Generic Error Format - |-------------------------------------------------------------------------- - | - | When some HTTP exceptions are not caught and dealt with the API will - | generate a generic error response in the format provided. Any - | keys that aren't replaced with corresponding values will be - | removed from the final response. - | - */ - - 'errorFormat' => [ - 'message' => ':message', - 'errors' => ':errors', - 'code' => ':code', - 'status_code' => ':status_code', - 'debug' => ':debug', - ], - - /* - |-------------------------------------------------------------------------- - | API Middleware - |-------------------------------------------------------------------------- - | - | Middleware that will be applied globally to all API requests. - | - */ - - 'middleware' => [ - - ], - - /* - |-------------------------------------------------------------------------- - | Authentication Providers - |-------------------------------------------------------------------------- - | - | The authentication providers that should be used when attempting to - | authenticate an incoming API request. - | - */ - - 'auth' => [ - 'custom' => 'Pterodactyl\Http\Middleware\APISecretToken', - ], - - /* - |-------------------------------------------------------------------------- - | Throttling / Rate Limiting - |-------------------------------------------------------------------------- - | - | Consumers of your API can be limited to the amount of requests they can - | make. You can create your own throttles or simply change the default - | throttles. - | - */ - - 'throttling' => [ - - ], - - /* - |-------------------------------------------------------------------------- - | Response Transformer - |-------------------------------------------------------------------------- - | - | Responses can be transformed so that they are easier to format. By - | default a Fractal transformer will be used to transform any - | responses prior to formatting. You can easily replace - | this with your own transformer. - | - */ - - 'transformer' => env('API_TRANSFORMER', Dingo\Api\Transformer\Adapter\Fractal::class), - - /* - |-------------------------------------------------------------------------- - | Response Formats - |-------------------------------------------------------------------------- - | - | Responses can be returned in multiple formats by registering different - | response formatters. You can also customize an existing response - | formatter. - | - */ - - 'defaultFormat' => env('API_DEFAULT_FORMAT', 'json'), - - 'formats' => [ - 'json' => Dingo\Api\Http\Response\Format\Json::class, - ], - -]; diff --git a/config/app.php b/config/app.php index 4721bb3d2..3c3fb772d 100644 --- a/config/app.php +++ b/config/app.php @@ -6,7 +6,16 @@ return [ 'version' => env('APP_VERSION', 'canary'), - 'phrase_in_context' => env('PHRASE_IN_CONTEXT', false), + /* + |-------------------------------------------------------------------------- + | Application Name + |-------------------------------------------------------------------------- + | + | This value is the name of your application. This value is used when the + | framework needs to place the application's name in a notification or + | any other location as required by the application or its packages. + */ + 'name' => 'Pterodactyl', /* |-------------------------------------------------------------------------- @@ -101,7 +110,9 @@ return [ | */ - 'log' => 'daily', + 'log' => env('APP_LOG', 'daily'), + + 'log_level' => env('APP_LOG_LEVEL', 'debug'), /* |-------------------------------------------------------------------------- @@ -116,8 +127,6 @@ return [ 'providers' => [ - Dingo\Api\Provider\LaravelServiceProvider::class, - /* * Laravel Framework Service Providers... */ @@ -143,6 +152,11 @@ return [ Illuminate\View\ViewServiceProvider::class, Illuminate\Notifications\NotificationServiceProvider::class, + /* + * Package Service Providers... + */ + Laravel\Tinker\TinkerServiceProvider::class, + /* * Application Service Providers... */ @@ -164,6 +178,7 @@ return [ Fideloper\Proxy\TrustedProxyServiceProvider::class, Laracasts\Utilities\JavaScript\JavaScriptServiceProvider::class, Lord\Laroute\LarouteServiceProvider::class, + Spatie\Fractal\FractalServiceProvider::class, ], @@ -194,11 +209,10 @@ return [ 'Crypt' => Illuminate\Support\Facades\Crypt::class, 'DB' => Illuminate\Support\Facades\DB::class, 'Debugbar' => Barryvdh\Debugbar\Facade::class, - 'Dingo' => Dingo\Api\Facade\API::class, - 'DingoRoute'=> Dingo\Api\Facade\Route::class, 'Eloquent' => Illuminate\Database\Eloquent\Model::class, 'Event' => Illuminate\Support\Facades\Event::class, 'File' => Illuminate\Support\Facades\File::class, + 'Fractal' => Spatie\Fractal\FractalFacade::class, 'Gate' => Illuminate\Support\Facades\Gate::class, 'Google2FA' => PragmaRX\Google2FA\Vendor\Laravel\Facade::class, 'Hash' => Illuminate\Support\Facades\Hash::class, diff --git a/config/auth.php b/config/auth.php index c8c53e14e..566926483 100644 --- a/config/auth.php +++ b/config/auth.php @@ -69,11 +69,6 @@ return [ 'driver' => 'eloquent', 'model' => Pterodactyl\Models\User::class, ], - - // 'users' => [ - // 'driver' => 'database', - // 'table' => 'users', - // ], ], /* diff --git a/config/broadcasting.php b/config/broadcasting.php index 36f9b3c14..85e045124 100644 --- a/config/broadcasting.php +++ b/config/broadcasting.php @@ -11,9 +11,10 @@ return [ | framework when an event needs to be broadcast. You may set this to | any of the connections defined in the "connections" array below. | + | Supported: "pusher", "redis", "log", "null" + | */ - - 'default' => env('BROADCAST_DRIVER', 'pusher'), + 'default' => env('BROADCAST_DRIVER', 'null'), /* |-------------------------------------------------------------------------- @@ -44,6 +45,10 @@ return [ 'driver' => 'log', ], + 'null' => [ + 'driver' => 'null', + ], + ], ]; diff --git a/config/cache.php b/config/cache.php index cb4bbd25d..7bd9dd70e 100644 --- a/config/cache.php +++ b/config/cache.php @@ -13,7 +13,7 @@ return [ | */ - 'default' => env('CACHE_DRIVER', 'memcached'), + 'default' => env('CACHE_DRIVER', 'file'), /* |-------------------------------------------------------------------------- @@ -38,21 +38,28 @@ return [ 'database' => [ 'driver' => 'database', - 'table' => 'cache', + 'table' => 'cache', 'connection' => null, ], - 'file' => [ 'driver' => 'file', - 'path' => storage_path('framework/cache'), + 'path' => storage_path('framework/cache/data'), ], 'memcached' => [ - 'driver' => 'memcached', + 'driver' => 'memcached', + 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'), + 'sasl' => [ + env('MEMCACHED_USERNAME'), + env('MEMCACHED_PASSWORD'), + ], + 'options' => [ + // Memcached::OPT_CONNECT_TIMEOUT => 2000, + ], 'servers' => [ [ - 'host' => env('MEMCACHE_DRIVER_HOST', '127.0.0.1'), - 'port' => env('MEMCACHE_DRIVER_PORT', 11211), + 'host' => env('MEMCACHED_HOST', '127.0.0.1'), + 'port' => env('MEMCACHED_PORT', 11211), 'weight' => 100, ], ], diff --git a/config/database.php b/config/database.php index 420cd6184..58324a0b5 100644 --- a/config/database.php +++ b/config/database.php @@ -2,19 +2,6 @@ return [ - /* - |-------------------------------------------------------------------------- - | PDO Fetch Style - |-------------------------------------------------------------------------- - | - | By default, database results will be returned as instances of the PHP - | stdClass object; however, you may desire to retrieve records in an - | array format for simplicity. Here you can tweak the fetch style. - | - */ - - 'fetch' => PDO::FETCH_CLASS, - /* |-------------------------------------------------------------------------- | Default Database Connection Name @@ -45,13 +32,6 @@ return [ */ 'connections' => [ - - 'sqlite' => [ - 'driver' => 'sqlite', - 'database' => storage_path('database.sqlite'), - 'prefix' => '', - ], - 'mysql' => [ 'driver' => 'mysql', 'host' => env('DB_HOST', 'localhost'), @@ -64,28 +44,6 @@ return [ 'prefix' => '', 'strict' => false, ], - - 'pgsql' => [ - 'driver' => 'pgsql', - 'host' => env('DB_HOST', 'localhost'), - 'database' => env('DB_DATABASE', 'forge'), - 'username' => env('DB_USERNAME', 'forge'), - 'password' => env('DB_PASSWORD', ''), - 'charset' => 'utf8', - 'prefix' => '', - 'schema' => 'public', - ], - - 'sqlsrv' => [ - 'driver' => 'sqlsrv', - 'host' => env('DB_HOST', 'localhost'), - 'database' => env('DB_DATABASE', 'forge'), - 'username' => env('DB_USERNAME', 'forge'), - 'password' => env('DB_PASSWORD', ''), - 'charset' => 'utf8', - 'prefix' => '', - ], - ], /* @@ -113,15 +71,13 @@ return [ */ 'redis' => [ - - 'cluster' => false, - + 'client' => 'predis', 'default' => [ - 'host' => '127.0.0.1', - 'port' => 6379, + 'host' => env('REDIS_HOST', 'localhost'), + 'password' => env('REDIS_PASSWORD', null), + 'port' => env('REDIS_PORT', 6379), 'database' => 0, ], - ], ]; diff --git a/config/debugbar.php b/config/debugbar.php index 4d9d8c45e..05e78c34c 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -28,7 +28,7 @@ return [ */ 'storage' => [ 'enabled' => true, - 'driver' => 'file', // redis, file, pdo + 'driver' => env('DEBUGBAR_DRIVER', 'file'), // redis, file, pdo 'path' => storage_path() . '/debugbar', // For file driver 'connection' => null, // Leave null for default connection (Redis/PDO) ], @@ -125,7 +125,7 @@ return [ 'enabled' => false, 'types' => ['SELECT', 'INSERT', 'UPDATE', 'DELETE'], // array('SELECT', 'INSERT', 'UPDATE', 'DELETE'); for MySQL 5.6.3+ ], - 'hints' => true, // Show hints for common mistakes + 'hints' => false, // Show hints for common mistakes ], 'mail' => [ 'full_log' => false, diff --git a/config/filesystems.php b/config/filesystems.php index 3fffcf0a2..e726fda0c 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -39,45 +39,29 @@ return [ | may even configure multiple disks of the same driver. Defaults have | been setup for each driver as an example of the required options. | + | Supported Drivers: "local", "ftp", "s3", "rackspace" + | */ - 'disks' => [ 'local' => [ 'driver' => 'local', - 'root' => storage_path('app'), + 'root' => storage_path('app'), ], - 'ftp' => [ - 'driver' => 'ftp', - 'host' => 'ftp.example.com', - 'username' => 'your-username', - 'password' => 'your-password', - - // Optional FTP Settings... - // 'port' => 21, - // 'root' => '', - // 'passive' => true, - // 'ssl' => true, - // 'timeout' => 30, + 'public' => [ + 'driver' => 'local', + 'root' => storage_path('app/public'), + 'url' => env('APP_URL') . '/storage', + 'visibility' => 'public', ], 's3' => [ 'driver' => 's3', - 'key' => 'your-key', - 'secret' => 'your-secret', - 'region' => 'your-region', - 'bucket' => 'your-bucket', - ], - - 'rackspace' => [ - 'driver' => 'rackspace', - 'username' => 'your-username', - 'key' => 'your-key', - 'container' => 'your-container', - 'endpoint' => 'https://identity.api.rackspacecloud.com/v2.0/', - 'region' => 'IAD', - 'url_type' => 'publicURL', + 'key' => env('AWS_KEY'), + 'secret' => env('AWS_SECRET'), + 'region' => env('AWS_REGION'), + 'bucket' => env('AWS_BUCKET'), ], ], diff --git a/config/javascript.php b/config/javascript.php index 1c2ef82dc..1aeb222e0 100644 --- a/config/javascript.php +++ b/config/javascript.php @@ -14,8 +14,7 @@ return [ | */ 'bind_js_vars_to_this_view' => [ - 'layouts.master', - 'layouts.admin', + 'layouts.scripts', ], /* diff --git a/config/laravel-fractal.php b/config/laravel-fractal.php new file mode 100644 index 000000000..32ced203e --- /dev/null +++ b/config/laravel-fractal.php @@ -0,0 +1,18 @@ + League\Fractal\Serializer\JsonApiSerializer::class, + +]; diff --git a/config/mail.php b/config/mail.php index 6b07f59ff..146e1b11c 100644 --- a/config/mail.php +++ b/config/mail.php @@ -11,7 +11,8 @@ return [ | sending of e-mail. You may specify which one you're using throughout | your application here. By default, Laravel is setup for SMTP mail. | - | Supported: "smtp", "mail", "sendmail", "mailgun", "mandrill", "ses", "log" + | Supported: "smtp", "sendmail", "mailgun", "mandrill", "ses", + | "sparkpost", "log", "array" | */ @@ -124,4 +125,22 @@ return [ 'pretend' => false, + /* + |-------------------------------------------------------------------------- + | Markdown Mail Settings + |-------------------------------------------------------------------------- + | + | If you are using Markdown based email rendering, you may configure your + | theme and component paths here, allowing you to customize the design + | of the emails. Or, you may simply stick with the Laravel defaults! + | + */ + 'markdown' => [ + 'theme' => 'default', + + 'paths' => [ + resource_path('views/vendor/mail'), + ], + ], + ]; diff --git a/config/pterodactyl.php b/config/pterodactyl.php index 1b946c6a3..bd10183c6 100644 --- a/config/pterodactyl.php +++ b/config/pterodactyl.php @@ -16,6 +16,17 @@ return [ 'author' => env('SERVICE_AUTHOR'), ], + /* + |-------------------------------------------------------------------------- + | Authentication + |-------------------------------------------------------------------------- + | + | Should login success and failure events trigger an email to the user? + */ + 'auth' => [ + 'notifications' => env('LOGIN_NOTIFICATIONS', false), + ], + /* |-------------------------------------------------------------------------- | Pagination @@ -26,8 +37,24 @@ return [ */ 'paginate' => [ 'frontend' => [ - 'servers' => 15, + 'servers' => env('APP_PAGINATE_FRONT_SERVERS', 15), ], + 'api' => [ + 'nodes' => env('APP_PAGINATE_API_NODES', 25), + 'servers' => env('APP_PAGINATE_API_SERVERS', 25), + 'users' => env('APP_PAGINATE_API_USERS', 25), + ], + ], + + /* + |-------------------------------------------------------------------------- + | API Options + |-------------------------------------------------------------------------- + | + | Configuration options for the API. + */ + 'api' => [ + 'include_on_list' => env('API_INCLUDE_ON_LIST', false), ], /* @@ -42,4 +69,79 @@ return [ 'connect_timeout' => env('GUZZLE_CONNECT_TIMEOUT', 3), ], + /* + |-------------------------------------------------------------------------- + | Queue Names + |-------------------------------------------------------------------------- + | + | Configure the names of queues to be used in the database. + */ + 'queues' => [ + 'low' => env('QUEUE_LOW', 'low'), + 'standard' => env('QUEUE_STANDARD', 'standard'), + 'high' => env('QUEUE_HIGH', 'high'), + ], + + /* + |-------------------------------------------------------------------------- + | Console Configuration + |-------------------------------------------------------------------------- + | + | Configure the speed at which data is rendered to the console. + */ + 'console' => [ + 'count' => env('CONSOLE_PUSH_COUNT', 10), + 'frequency' => env('CONSOLE_PUSH_FREQ', 200), + ], + + /* + |-------------------------------------------------------------------------- + | Task Timers + |-------------------------------------------------------------------------- + | + | The amount of time in minutes before performing certain actions on the system. + */ + 'tasks' => [ + 'clear_log' => env('PTERODACTYL_CLEAR_TASKLOG', 720), + 'delete_server' => env('PTERODACTYL_DELETE_MINUTES', 10), + ], + + /* + |-------------------------------------------------------------------------- + | CDN + |-------------------------------------------------------------------------- + | + | Information for the panel to use when contacting the CDN to confirm + | if panel is up to date. + */ + 'cdn' => [ + 'cache' => 60, + 'url' => 'https://cdn.pterodactyl.io/releases/latest.json', + ], + + /* + |-------------------------------------------------------------------------- + | Language Editor + |-------------------------------------------------------------------------- + | + | Set `PHRASE_IN_CONTEXT` to true to enable the PhaseApp in-context editor + | on this site which allows you to translate the panel, from the panel. + */ + 'lang' => [ + 'in_context' => env('PHRASE_IN_CONTEXT', false), + ], + + /* + |-------------------------------------------------------------------------- + | JSON Response Routes + |-------------------------------------------------------------------------- + | + | You should not edit this block. These routes are ajax based routes that + | expect content to be returned in JSON format. + */ + 'json_routes' => [ + 'api/*', + 'daemon/*', + 'remote/*', + ], ]; diff --git a/config/queue.php b/config/queue.php index 0a2181f77..a3d726f4a 100644 --- a/config/queue.php +++ b/config/queue.php @@ -7,12 +7,11 @@ return [ | Default Queue Driver |-------------------------------------------------------------------------- | - | The Laravel queue API supports a variety of back-ends via an unified + | Laravel's queue API supports an assortment of back-ends via a single | API, giving you convenient access to each back-end using the same | syntax for each one. Here you may set the default queue driver. | - | Supported: "null", "sync", "database", "beanstalkd", - | "sqs", "iron", "redis" + | Supported: "sync", "database", "beanstalkd", "sqs", "redis", "null" | */ @@ -39,7 +38,7 @@ return [ 'driver' => 'database', 'table' => 'jobs', 'queue' => env('QUEUE_STANDARD', 'standard'), - 'retry_after' => 60, + 'retry_after' => 90, ], 'sqs' => [ @@ -55,7 +54,7 @@ return [ 'driver' => 'redis', 'connection' => 'default', 'queue' => env('QUEUE_STANDARD', 'standard'), - 'retry_after' => 60, + 'retry_after' => 90, ], ], @@ -72,7 +71,8 @@ return [ */ 'failed' => [ - 'database' => 'mysql', 'table' => 'failed_jobs', + 'database' => 'mysql', + 'table' => 'failed_jobs', ], ]; diff --git a/config/recaptcha.php b/config/recaptcha.php new file mode 100644 index 000000000..646c9931a --- /dev/null +++ b/config/recaptcha.php @@ -0,0 +1,31 @@ + env('RECAPTCHA_ENABLED', true), + + /* + * API endpoint for recaptcha checks. You should not edit this. + */ + 'domain' => 'https://www.google.com/recaptcha/api/siteverify', + + /* + * Use a custom secret key, we use our public one by default + */ + 'secret_key' => env('RECAPTCHA_SECRET_KEY', '6LekAxoUAAAAAPW-PxNWaCLH76WkClMLSa2jImwD'), + + /* + * Use a custom website key, we use our public one by default + */ + 'website_key' => env('RECAPTCHA_WEBSITE_KEY', '6LekAxoUAAAAADjWZJ4ufcDRZBBiH9vfHawqRbup'), + + /* + * Domain verification is enabled by default and compares the domain used when solving the captcha + * as public keys can't have domain verification on google's side enabled (obviously). + */ + 'verify_domain' => true, + +]; diff --git a/config/services.php b/config/services.php index 0121c0b24..6ea80674c 100644 --- a/config/services.php +++ b/config/services.php @@ -29,10 +29,8 @@ return [ 'region' => 'us-east-1', ], - 'stripe' => [ - 'model' => Pterodactyl\Models\User::class, - 'key' => env('STRIPE_KEY'), - 'secret' => env('STRIPE_SECRET'), + 'sparkpost' => [ + 'secret' => env('SPARKPOST_SECRET'), ], ]; diff --git a/config/session.php b/config/session.php index f1b004214..246fc9347 100644 --- a/config/session.php +++ b/config/session.php @@ -16,7 +16,7 @@ return [ | */ - 'driver' => env('SESSION_DRIVER', 'file'), + 'driver' => env('SESSION_DRIVER', 'database'), /* |-------------------------------------------------------------------------- @@ -85,6 +85,19 @@ return [ 'table' => 'sessions', + /* + |-------------------------------------------------------------------------- + | Session Cache Store + |-------------------------------------------------------------------------- + | + | When using the "apc" or "memcached" session drivers, you may specify a + | cache store that should be used for these sessions. This value must + | correspond with one of the application's configured cache stores. + | + */ + + 'store' => null, + /* |-------------------------------------------------------------------------- | Session Sweeping Lottery @@ -135,7 +148,7 @@ return [ | */ - 'domain' => null, + 'domain' => env('SESSION_DOMAIN', null), /* |-------------------------------------------------------------------------- @@ -148,6 +161,19 @@ return [ | */ - 'secure' => false, + 'secure' => env('SESSION_SECURE_COOKIE', false), + + /* + |-------------------------------------------------------------------------- + | HTTP Access Only + |-------------------------------------------------------------------------- + | + | Setting this value to true will prevent JavaScript from accessing the + | value of the cookie and the cookie will only be accessible through + | the HTTP protocol. You are free to modify this option if needed. + | + */ + + 'http_only' => true, ]; diff --git a/config/themes.php b/config/themes.php index 03a0b6155..f90a29f68 100644 --- a/config/themes.php +++ b/config/themes.php @@ -4,14 +4,9 @@ return [ 'enabled' => true, 'themes_path' => realpath(base_path('resources/themes')), 'asset_not_found' => 'LOG_ERROR', - 'active' => ENV('APP_THEME', 'default'), + 'active' => env('APP_THEME', 'pterodactyl'), 'themes' => [ - 'default' => [ - 'extends' => null, - 'views-path' => 'default', - 'asset-path' => 'themes/default', - ], 'pterodactyl' => [ 'extends' => null, 'views-path' => 'pterodactyl', diff --git a/config/view.php b/config/view.php index e193ab61d..2acfd9cc9 100644 --- a/config/view.php +++ b/config/view.php @@ -14,7 +14,7 @@ return [ */ 'paths' => [ - realpath(base_path('resources/views')), + resource_path('views'), ], /* diff --git a/database/migrations/2016_01_23_195641_add_allocations_table.php b/database/migrations/2016_01_23_195641_add_allocations_table.php index 2edec4485..7384b7de2 100644 --- a/database/migrations/2016_01_23_195641_add_allocations_table.php +++ b/database/migrations/2016_01_23_195641_add_allocations_table.php @@ -29,6 +29,6 @@ class AddAllocationsTable extends Migration */ public function down() { - Schema::dropIfExsits('allocations'); + Schema::dropIfExists('allocations'); } } diff --git a/database/migrations/2016_01_23_200421_create_failed_jobs_table.php b/database/migrations/2016_01_23_200421_create_failed_jobs_table.php index c1ba41b48..63eaf53a6 100644 --- a/database/migrations/2016_01_23_200421_create_failed_jobs_table.php +++ b/database/migrations/2016_01_23_200421_create_failed_jobs_table.php @@ -28,6 +28,6 @@ class CreateFailedJobsTable extends Migration */ public function down() { - Schema::drop('failed_jobs'); + Schema::dropIfExists('failed_jobs'); } } diff --git a/database/migrations/2016_01_23_200440_create_jobs_table.php b/database/migrations/2016_01_23_200440_create_jobs_table.php index 81b2d29f2..01d3a9e65 100644 --- a/database/migrations/2016_01_23_200440_create_jobs_table.php +++ b/database/migrations/2016_01_23_200440_create_jobs_table.php @@ -32,6 +32,6 @@ class CreateJobsTable extends Migration */ public function down() { - Schema::drop('jobs'); + Schema::dropIfExists('jobs'); } } diff --git a/database/migrations/2016_01_23_201649_add_server_variables.php b/database/migrations/2016_01_23_201649_add_server_variables.php index bfbb4b99d..229b33c60 100644 --- a/database/migrations/2016_01_23_201649_add_server_variables.php +++ b/database/migrations/2016_01_23_201649_add_server_variables.php @@ -28,8 +28,6 @@ class AddServerVariables extends Migration */ public function down() { - Schema::table('server_variables', function (Blueprint $table) { - // - }); + Schema::dropIfExists('server_variables'); } } diff --git a/database/migrations/2016_01_23_202544_add_service_options.php b/database/migrations/2016_01_23_202544_add_service_options.php index 1a05caec8..172f1eb7b 100644 --- a/database/migrations/2016_01_23_202544_add_service_options.php +++ b/database/migrations/2016_01_23_202544_add_service_options.php @@ -30,6 +30,6 @@ class AddServiceOptions extends Migration */ public function down() { - Schema::dropIfExsits('service_options'); + Schema::dropIfExists('service_options'); } } diff --git a/database/migrations/2016_10_23_203857_add_foreign_server_variables.php b/database/migrations/2016_10_23_203857_add_foreign_server_variables.php index c232176c7..af78a161c 100644 --- a/database/migrations/2016_10_23_203857_add_foreign_server_variables.php +++ b/database/migrations/2016_10_23_203857_add_foreign_server_variables.php @@ -32,14 +32,11 @@ class AddForeignServerVariables extends Migration public function down() { Schema::table('server_variables', function (Blueprint $table) { - $table->dropForeign('server_variables_server_id_foreign'); - $table->dropForeign('server_variables_variable_id_foreign'); - - $table->dropIndex('server_variables_server_id_foreign'); - $table->dropIndex('server_variables_variable_id_foreign'); + $table->dropForeign(['server_id']); + $table->dropForeign(['variable_id']); }); - DB::statement('ALTER TABLE allocations + DB::statement('ALTER TABLE server_variables MODIFY COLUMN server_id MEDIUMINT(8) UNSIGNED NULL, MODIFY COLUMN variable_id MEDIUMINT(8) UNSIGNED NOT NULL '); diff --git a/database/migrations/2016_10_23_204610_add_foreign_tasks.php b/database/migrations/2016_10_23_204610_add_foreign_tasks.php index 77b736201..3821caffc 100644 --- a/database/migrations/2016_10_23_204610_add_foreign_tasks.php +++ b/database/migrations/2016_10_23_204610_add_foreign_tasks.php @@ -26,11 +26,7 @@ class AddForeignTasks extends Migration public function down() { Schema::table('tasks', function (Blueprint $table) { - $table->dropForeign('tasks_server_foreign'); - $table->dropForeign('tasks_server_foreign'); - - $table->dropIndex('tasks_server_foreign'); - $table->dropIndex('tasks_server_foreign'); + $table->dropForeign(['server']); }); } } diff --git a/database/migrations/2016_11_27_142519_add_pack_column.php b/database/migrations/2016_11_27_142519_add_pack_column.php index f2c2f0964..4e3507c39 100644 --- a/database/migrations/2016_11_27_142519_add_pack_column.php +++ b/database/migrations/2016_11_27_142519_add_pack_column.php @@ -28,8 +28,7 @@ class AddPackColumn extends Migration public function down() { Schema::table('servers', function (Blueprint $table) { - $table->dropForeign('servers_pack_foreign'); - $table->dropIndex('servers_pack_foreign'); + $table->dropForeign(['pack']); $table->dropColumn('pack'); }); } diff --git a/database/migrations/2017_02_02_175548_UpdateColumnNames.php b/database/migrations/2017_02_02_175548_UpdateColumnNames.php index 800762417..233780bfd 100644 --- a/database/migrations/2017_02_02_175548_UpdateColumnNames.php +++ b/database/migrations/2017_02_02_175548_UpdateColumnNames.php @@ -40,6 +40,9 @@ class UpdateColumnNames extends Migration $table->foreign('allocation_id')->references('id')->on('allocations'); $table->foreign('service_id')->references('id')->on('services'); $table->foreign('option_id')->references('id')->on('service_options'); + + // Pack ID was forgotten until multiple releases later, therefore it is + // contained in '2017_03_18_204953_AddForeignKeyToPacks' }); } @@ -51,19 +54,11 @@ class UpdateColumnNames extends Migration public function down() { Schema::table('servers', function (Blueprint $table) { - $table->dropForeign('servers_node_id_foreign'); - $table->dropForeign('servers_owner_id_foreign'); - $table->dropForeign('servers_allocation_id_foreign'); - $table->dropForeign('servers_service_id_foreign'); - $table->dropForeign('servers_option_id_foreign'); - $table->dropForeign('servers_pack_id_foreign'); - - $table->dropIndex('servers_node_id_foreign'); - $table->dropIndex('servers_owner_id_foreign'); - $table->dropIndex('servers_allocation_id_foreign'); - $table->dropIndex('servers_service_id_foreign'); - $table->dropIndex('servers_option_id_foreign'); - $table->dropIndex('servers_pack_id_foreign'); + $table->dropForeign(['node_id']); + $table->dropForeign(['owner_id']); + $table->dropForeign(['allocation_id']); + $table->dropForeign(['service_id']); + $table->dropForeign(['option_id']); $table->renameColumn('node_id', 'node'); $table->renameColumn('owner_id', 'owner'); @@ -77,6 +72,7 @@ class UpdateColumnNames extends Migration $table->foreign('allocation')->references('id')->on('allocations'); $table->foreign('service')->references('id')->on('services'); $table->foreign('option')->references('id')->on('service_options'); + $table->foreign('pack')->references('id')->on('service_packs'); }); } } diff --git a/database/migrations/2017_03_05_212803_DeleteServiceExecutableOption.php b/database/migrations/2017_03_05_212803_DeleteServiceExecutableOption.php index 5a1df2afb..e4af2511a 100644 --- a/database/migrations/2017_03_05_212803_DeleteServiceExecutableOption.php +++ b/database/migrations/2017_03_05_212803_DeleteServiceExecutableOption.php @@ -1,5 +1,6 @@ renameColumn('file', 'folder'); - $table->text('description')->nullable()->change(); - $table->text('startup')->nullable()->change(); - }); - // Attempt to fix any startup commands for servers - // that we possibly can. + // that we possibly can. Also set new containers. foreach (ServiceOption::with('servers')->get() as $option) { $option->servers->each(function ($s) use ($option) { $prepend = $option->display_executable; @@ -29,12 +24,27 @@ class DeleteServiceExecutableOption extends Migration $prepend = ($prepend === 'TerrariaServer.exe') ? 'mono TerrariaServer.exe' : $prepend; $s->startup = $prepend . ' ' . $s->startup; + + $container = $s->container; + if (starts_with($container, 'quay.io/pterodactyl/minecraft')) { + $s->container = 'quay.io/pterodactyl/core:java'; + } elseif (starts_with($container, 'quay.io/pterodactyl/srcds')) { + $s->container = 'quay.io/pterodactyl/core:source'; + } elseif (starts_with($container, 'quay.io/pterodactyl/voice')) { + $s->container = 'quay.io/pterodactyl/core:glibc'; + } elseif (starts_with($container, 'quay.io/pterodactyl/terraria')) { + $s->container = 'quay.io/pterodactyl/core:mono'; + } + $s->save(); }); } Schema::table('services', function (Blueprint $table) { + $table->renameColumn('file', 'folder'); $table->dropColumn('executable'); + $table->text('description')->nullable()->change(); + $table->text('startup')->nullable()->change(); }); }); } diff --git a/database/migrations/2017_03_10_162934_AddNewServiceOptionsColumns.php b/database/migrations/2017_03_10_162934_AddNewServiceOptionsColumns.php index f2c50cdd7..351327d3c 100644 --- a/database/migrations/2017_03_10_162934_AddNewServiceOptionsColumns.php +++ b/database/migrations/2017_03_10_162934_AddNewServiceOptionsColumns.php @@ -34,7 +34,7 @@ class AddNewServiceOptionsColumns extends Migration public function down() { Schema::table('service_options', function (Blueprint $table) { - $table->dropForeign('config_from'); + $table->dropForeign(['config_from']); $table->dropColumn('config_from'); $table->dropColumn('config_stop'); diff --git a/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php b/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php index 9f935fff8..ee247ee96 100644 --- a/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php +++ b/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php @@ -49,6 +49,7 @@ class MigrateToNewServiceSystem extends Migration } elseif ($item->tag === 'srcds' && $item->name === 'Custom Source Engine Game') { $item->tag = 'source'; } + $item->save(); }); }); } diff --git a/database/migrations/2017_03_11_215455_ChangeServiceVariablesValidationRules.php b/database/migrations/2017_03_11_215455_ChangeServiceVariablesValidationRules.php index 225a1edc0..617c349fa 100644 --- a/database/migrations/2017_03_11_215455_ChangeServiceVariablesValidationRules.php +++ b/database/migrations/2017_03_11_215455_ChangeServiceVariablesValidationRules.php @@ -20,7 +20,7 @@ class ChangeServiceVariablesValidationRules extends Migration DB::transaction(function () { foreach (ServiceVariable::all() as $variable) { - $variable->rules = ($variable->required) ? 'required|regex:' . $variable->rules : 'regex:' . $variable->regex; + $variable->rules = ($variable->required) ? 'required|regex:' . $variable->rules : 'regex:' . $variable->rules; $variable->save(); } }); diff --git a/database/migrations/2017_03_18_204953_AddForeignKeyToPacks.php b/database/migrations/2017_03_18_204953_AddForeignKeyToPacks.php new file mode 100644 index 000000000..0271616a0 --- /dev/null +++ b/database/migrations/2017_03_18_204953_AddForeignKeyToPacks.php @@ -0,0 +1,32 @@ +foreign('pack_id')->references('id')->on('packs'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('servers', function (Blueprint $table) { + $table->dropForeign(['pack_id']); + }); + } +} diff --git a/database/migrations/2017_03_31_221948_AddServerDescriptionColumn.php b/database/migrations/2017_03_31_221948_AddServerDescriptionColumn.php new file mode 100644 index 000000000..1e5ce0273 --- /dev/null +++ b/database/migrations/2017_03_31_221948_AddServerDescriptionColumn.php @@ -0,0 +1,32 @@ +text('description')->after('name'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('servers', function (Blueprint $table) { + $table->dropColumn('description'); + }); + } +} diff --git a/database/migrations/2017_04_02_163232_DropDeletedAtColumnFromServers.php b/database/migrations/2017_04_02_163232_DropDeletedAtColumnFromServers.php new file mode 100644 index 000000000..ccd318654 --- /dev/null +++ b/database/migrations/2017_04_02_163232_DropDeletedAtColumnFromServers.php @@ -0,0 +1,32 @@ +dropColumn('deleted_at'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('servers', function (Blueprint $table) { + $table->timestamp('deleted_at')->nullable(); + }); + } +} diff --git a/database/migrations/2017_04_15_125021_UpgradeTaskSystem.php b/database/migrations/2017_04_15_125021_UpgradeTaskSystem.php new file mode 100644 index 000000000..16e4d4e9d --- /dev/null +++ b/database/migrations/2017_04_15_125021_UpgradeTaskSystem.php @@ -0,0 +1,52 @@ +dropForeign(['server']); + + $table->renameColumn('server', 'server_id'); + $table->unsignedInteger('user_id')->nullable()->after('id'); + + $table->foreign('server_id')->references('id')->on('servers'); + $table->foreign('user_id')->references('id')->on('users'); + }); + + DB::transaction(function () { + foreach(Task::all() as $task) { + $task->user_id = $task->server->owner_id; + $task->save(); + } + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('tasks', function (Blueprint $table) { + $table->dropForeign(['server_id']); + $table->dropForeign(['user_id']); + + $table->renameColumn('server_id', 'server'); + $table->dropColumn('user_id'); + + $table->foreign('server')->references('id')->on('servers'); + }); + } +} diff --git a/database/seeds/MinecraftServiceTableSeeder.php b/database/seeds/MinecraftServiceTableSeeder.php index d1e7b844a..9086c3fbb 100644 --- a/database/seeds/MinecraftServiceTableSeeder.php +++ b/database/seeds/MinecraftServiceTableSeeder.php @@ -116,7 +116,7 @@ EOF; ], [ 'name' => 'Vanilla Minecraft', 'description' => 'Minecraft is a game about placing blocks and going on adventures. Explore randomly generated worlds and build amazing things from the simplest of homes to the grandest of castles. Play in Creative Mode with unlimited resources or mine deep in Survival Mode, crafting weapons and armor to fend off dangerous mobs. Do all this alone or with friends.', - 'docker_image' => 'quay.io/pterodactyl/minecraft', + 'docker_image' => 'quay.io/pterodactyl/core:java', 'config_startup' => '{"done": ")! For help, type ", "userInteraction": [ "Go to eula.txt for more info."]}', 'config_logs' => '{"custom": false, "location": "logs/latest.log"}', 'config_files' => '{"server.properties":{"parser": "properties", "find":{"server-ip": "0.0.0.0", "enable-query": "true", "server-port": "{{server.build.default.port}}", "query.port": "{{server.build.default.port}}"}}}', @@ -131,9 +131,9 @@ EOF; ], [ 'name' => 'Spigot', 'description' => 'Spigot is the most widely-used modded Minecraft server software in the world. It powers many of the top Minecraft server networks around to ensure they can cope with their huge player base and ensure the satisfaction of their players. Spigot works by reducing and eliminating many causes of lag, as well as adding in handy features and settings that help make your job of server administration easier.', - 'docker_image' => 'quay.io/pterodactyl/minecraft:spigot', + 'docker_image' => 'quay.io/pterodactyl/core:java-glibc', 'config_startup' => null, - 'config_files' => '{"spigot.yml":{"parser": "yaml", "find":{"settings.restart-on-crash": "false"}}}', + 'config_files' => '{"spigot.yml":{"parser": "yaml", "find":{"settings.restart-on-crash": false}}}', 'config_logs' => null, 'config_stop' => null, 'config_from' => $this->option['vanilla']->id, @@ -146,7 +146,7 @@ EOF; ], [ 'name' => 'Sponge (SpongeVanilla)', 'description' => 'SpongeVanilla is the SpongeAPI implementation for Vanilla Minecraft.', - 'docker_image' => 'quay.io/pterodactyl/minecraft:sponge', + 'docker_image' => 'quay.io/pterodactyl/core:java-glibc', 'config_startup' => '{"userInteraction": [ "You need to agree to the EULA"]}', 'config_files' => null, 'config_logs' => null, @@ -161,7 +161,7 @@ EOF; ], [ 'name' => 'Bungeecord', 'description' => 'For a long time, Minecraft server owners have had a dream that encompasses a free, easy, and reliable way to connect multiple Minecraft servers together. BungeeCord is the answer to said dream. Whether you are a small server wishing to string multiple game-modes together, or the owner of the ShotBow Network, BungeeCord is the ideal solution for you. With the help of BungeeCord, you will be able to unlock your community\'s full potential.', - 'docker_image' => 'quay.io/pterodactyl/minecraft:bungeecord', + 'docker_image' => 'quay.io/pterodactyl/core:java', 'config_startup' => '{"done": "Listening on ", "userInteraction": [ "Listening on /0.0.0.0:25577"]}', 'config_files' => '{"config.yml":{"parser": "yaml", "find":{"listeners[0].query_enabled": true, "listeners[0].query_port": "{{server.build.default.port}}", "listeners[0].host": "0.0.0.0:{{server.build.default.port}}", "servers.*.address":{"127.0.0.1": "{{config.docker.interface}}", "localhost": "{{config.docker.interface}}"}}}}', 'config_logs' => '{"custom": false, "location": "proxy.log.0"}', diff --git a/database/seeds/SourceServiceTableSeeder.php b/database/seeds/SourceServiceTableSeeder.php index 226387c1c..46f061365 100644 --- a/database/seeds/SourceServiceTableSeeder.php +++ b/database/seeds/SourceServiceTableSeeder.php @@ -75,7 +75,7 @@ class SourceServiceTableSeeder extends Seeder ], [ 'name' => 'Custom Source Engine Game', 'description' => 'This option allows modifying the startup arguments and other details to run a custo SRCDS based game on the panel.', - 'docker_image' => 'quay.io/pterodactyl/srcds', + 'docker_image' => 'quay.io/pterodactyl/core:source', 'config_startup' => '{"done": "gameserver Steam ID", "userInteraction": []}', 'config_files' => '{}', 'config_logs' => '{"custom": true, "location": "logs/latest.log"}', @@ -90,7 +90,7 @@ class SourceServiceTableSeeder extends Seeder ], [ 'name' => 'Insurgency', 'description' => 'Take to the streets for intense close quarters combat, where a team\'s survival depends upon securing crucial strongholds and destroying enemy supply in this multiplayer and cooperative Source Engine based experience.', - 'docker_image' => 'quay.io/pterodactyl/srcds', + 'docker_image' => 'quay.io/pterodactyl/core:source', 'config_startup' => null, 'config_files' => null, 'config_logs' => null, @@ -105,7 +105,7 @@ class SourceServiceTableSeeder extends Seeder ], [ 'name' => 'Team Fortress 2', 'description' => 'Team Fortress 2 is a team-based first-person shooter multiplayer video game developed and published by Valve Corporation. It is the sequel to the 1996 mod Team Fortress for Quake and its 1999 remake.', - 'docker_image' => 'quay.io/pterodactyl/srcds', + 'docker_image' => 'quay.io/pterodactyl/core:source', 'config_startup' => null, 'config_files' => null, 'config_logs' => null, @@ -120,7 +120,7 @@ class SourceServiceTableSeeder extends Seeder ], [ 'name' => 'Ark: Survival Evolved', 'description' => 'As a man or woman stranded, naked, freezing, and starving on the unforgiving shores of a mysterious island called ARK, use your skill and cunning to kill or tame and ride the plethora of leviathan dinosaurs and other primeval creatures roaming the land. Hunt, harvest resources, craft items, grow crops, research technologies, and build shelters to withstand the elements and store valuables, all while teaming up with (or preying upon) hundreds of other players to survive, dominate... and escape! — Gamepedia: ARK', - 'docker_image' => 'quay.io/pterodactyl/srcds:ark', + 'docker_image' => 'quay.io/pterodactyl/core:source', 'config_startup' => '{"done": "Setting breakpad minidump AppID"}', 'config_files' => null, 'config_logs' => null, diff --git a/database/seeds/TerrariaServiceTableSeeder.php b/database/seeds/TerrariaServiceTableSeeder.php index 036e42e0e..ec4e0d52f 100644 --- a/database/seeds/TerrariaServiceTableSeeder.php +++ b/database/seeds/TerrariaServiceTableSeeder.php @@ -63,6 +63,7 @@ class TerrariaServiceTableSeeder extends Seeder 'name' => 'Terraria', 'description' => 'Terraria is a land of adventure! A land of mystery! A land that\'s yours to shape, defend, and enjoy. Your options in Terraria are limitless. Are you an action gamer with an itchy trigger finger? A master builder? A collector? An explorer? There\'s something for everyone.', 'startup' => 'mono TerrariaServer.exe -port {{SERVER_PORT}} -autocreate 2 -worldname World', + 'index_file' => Service::defaultIndexFile(), ]); } @@ -74,7 +75,7 @@ class TerrariaServiceTableSeeder extends Seeder ], [ 'name' => 'Terraria Server (TShock)', 'description' => 'TShock is a server modification for Terraria, written in C#, and based upon the Terraria Server API. It uses JSON for configuration management, and offers several features not present in the Terraria Server normally.', - 'docker_image' => 'quay.io/pterodactyl/terraria:tshock', + 'docker_image' => 'quay.io/pterodactyl/core:mono', 'config_startup' => '{"userInteraction": [ "You need to agree to the EULA"]}', 'config_startup' => '{"done": "Type \'help\' for a list of commands", "userInteraction": []}', 'config_files' => '{"tshock/config.json":{"parser": "json", "find":{"ServerPort": "{{server.build.default.port}}", "MaxSlots": "{{server.build.env.MAX_SLOTS}}"}}}', diff --git a/database/seeds/VoiceServiceTableSeeder.php b/database/seeds/VoiceServiceTableSeeder.php index 2e1c26a8b..52c121463 100644 --- a/database/seeds/VoiceServiceTableSeeder.php +++ b/database/seeds/VoiceServiceTableSeeder.php @@ -63,6 +63,7 @@ class VoiceServiceTableSeeder extends Seeder 'name' => 'Voice Servers', 'description' => 'Voice servers such as Mumble and Teamspeak 3.', 'startup' => '', + 'index_file' => Service::defaultIndexFile(), ]); } @@ -74,7 +75,7 @@ class VoiceServiceTableSeeder extends Seeder ], [ 'name' => 'Mumble Server', 'description' => 'Mumble is an open source, low-latency, high quality voice chat software primarily intended for use while gaming.', - 'docker_image' => 'quay.io/pterodactyl/voice:mumble', + 'docker_image' => 'quay.io/pterodactyl/core:glibc', 'config_startup' => '{"done": "Server listening on", "userInteraction": [ "Generating new server certificate"]}', 'config_files' => '{"murmur.ini":{"parser": "ini", "find":{"logfile": "murmur.log", "port": "{{server.build.default.port}}", "host": "0.0.0.0", "users": "{{server.build.env.MAX_USERS}}"}}}', 'config_logs' => '{"custom": true, "location": "logs/murmur.log"}', @@ -89,7 +90,7 @@ class VoiceServiceTableSeeder extends Seeder ], [ 'name' => 'Teamspeak3 Server', 'description' => 'VoIP software designed with security in mind, featuring crystal clear voice quality, endless customization options, and scalabilty up to thousands of simultaneous users.', - 'docker_image' => 'quay.io/pterodactyl/voice:ts3', + 'docker_image' => 'quay.io/pterodactyl/core:glibc', 'config_startup' => '{"done": "listening on 0.0.0.0:", "userInteraction": []}', 'config_files' => '{"ts3server.ini":{"parser": "ini", "find":{"default_voice_port": "{{server.build.default.port}}", "voice_ip": "0.0.0.0", "query_port": "{{server.build.default.port}}", "query_ip": "0.0.0.0"}}}', 'config_logs' => '{"custom": true, "location": "logs/ts3.log"}', diff --git a/public/css/animate.css b/public/css/animate.css deleted file mode 100755 index 441e618ab..000000000 --- a/public/css/animate.css +++ /dev/null @@ -1,645 +0,0 @@ -@charset "UTF-8"; - -/*! -Animate.css - http://daneden.me/animate -Licensed under the MIT license - http://opensource.org/licenses/MIT - -Copyright (c) 2015 Daniel Eden -*/ - -.animated{-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-fill-mode:both;animation-fill-mode:both} -.animated.infinite{-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite} -.animated.hinge{-webkit-animation-duration:2s;animation-duration:2s} -.animated.bounceIn,.animated.bounceOut,.animated.flipOutX,.animated.flipOutY{-webkit-animation-duration:.75s;animation-duration:.75s} -@-webkit-keyframes bounce{0%,100%,20%,53%,80%{-webkit-transition-timing-function:cubic-bezier(.215,.61,.355,1);transition-timing-function:cubic-bezier(.215,.61,.355,1);-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)} -40%,43%{-webkit-transition-timing-function:cubic-bezier(.755,.050,.855,.060);transition-timing-function:cubic-bezier(.755,.050,.855,.060);-webkit-transform:translate3d(0,-30px,0);transform:translate3d(0,-30px,0)} -70%{-webkit-transition-timing-function:cubic-bezier(.755,.050,.855,.060);transition-timing-function:cubic-bezier(.755,.050,.855,.060);-webkit-transform:translate3d(0,-15px,0);transform:translate3d(0,-15px,0)} -90%{-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0)} -} -@keyframes bounce{0%,100%,20%,53%,80%{-webkit-transition-timing-function:cubic-bezier(.215,.61,.355,1);transition-timing-function:cubic-bezier(.215,.61,.355,1);-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)} -40%,43%{-webkit-transition-timing-function:cubic-bezier(.755,.050,.855,.060);transition-timing-function:cubic-bezier(.755,.050,.855,.060);-webkit-transform:translate3d(0,-30px,0);transform:translate3d(0,-30px,0)} -70%{-webkit-transition-timing-function:cubic-bezier(.755,.050,.855,.060);transition-timing-function:cubic-bezier(.755,.050,.855,.060);-webkit-transform:translate3d(0,-15px,0);transform:translate3d(0,-15px,0)} -90%{-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0)} -} -.bounce{-webkit-animation-name:bounce;animation-name:bounce;-webkit-transform-origin:center bottom;transform-origin:center bottom} -@-webkit-keyframes flash{0%,100%,50%{opacity:1} -25%,75%{opacity:0} -} -@keyframes flash{0%,100%,50%{opacity:1} -25%,75%{opacity:0} -} -.flash{-webkit-animation-name:flash;animation-name:flash} -@-webkit-keyframes pulse{0%,100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)} -50%{-webkit-transform:scale3d(1.05,1.05,1.05);transform:scale3d(1.05,1.05,1.05)} -} -@keyframes pulse{0%,100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)} -50%{-webkit-transform:scale3d(1.05,1.05,1.05);transform:scale3d(1.05,1.05,1.05)} -} -.pulse{-webkit-animation-name:pulse;animation-name:pulse} -@-webkit-keyframes rubberBand{0%,100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)} -30%{-webkit-transform:scale3d(1.25,.75,1);transform:scale3d(1.25,.75,1)} -40%{-webkit-transform:scale3d(.75,1.25,1);transform:scale3d(.75,1.25,1)} -50%{-webkit-transform:scale3d(1.15,.85,1);transform:scale3d(1.15,.85,1)} -65%{-webkit-transform:scale3d(.95,1.05,1);transform:scale3d(.95,1.05,1)} -75%{-webkit-transform:scale3d(1.05,.95,1);transform:scale3d(1.05,.95,1)} -} -@keyframes rubberBand{0%,100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)} -30%{-webkit-transform:scale3d(1.25,.75,1);transform:scale3d(1.25,.75,1)} -40%{-webkit-transform:scale3d(.75,1.25,1);transform:scale3d(.75,1.25,1)} -50%{-webkit-transform:scale3d(1.15,.85,1);transform:scale3d(1.15,.85,1)} -65%{-webkit-transform:scale3d(.95,1.05,1);transform:scale3d(.95,1.05,1)} -75%{-webkit-transform:scale3d(1.05,.95,1);transform:scale3d(1.05,.95,1)} -} -.rubberBand{-webkit-animation-name:rubberBand;animation-name:rubberBand} -@-webkit-keyframes shake{0%,100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)} -10%,30%,50%,70%,90%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)} -20%,40%,60%,80%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)} -} -@keyframes shake{0%,100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)} -10%,30%,50%,70%,90%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)} -20%,40%,60%,80%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)} -} -.shake{-webkit-animation-name:shake;animation-name:shake} -@-webkit-keyframes swing{20%{-webkit-transform:rotate3d(0,0,1,15deg);transform:rotate3d(0,0,1,15deg)} -40%{-webkit-transform:rotate3d(0,0,1,-10deg);transform:rotate3d(0,0,1,-10deg)} -60%{-webkit-transform:rotate3d(0,0,1,5deg);transform:rotate3d(0,0,1,5deg)} -80%{-webkit-transform:rotate3d(0,0,1,-5deg);transform:rotate3d(0,0,1,-5deg)} -100%{-webkit-transform:rotate3d(0,0,1,0deg);transform:rotate3d(0,0,1,0deg)} -} -@keyframes swing{20%{-webkit-transform:rotate3d(0,0,1,15deg);transform:rotate3d(0,0,1,15deg)} -40%{-webkit-transform:rotate3d(0,0,1,-10deg);transform:rotate3d(0,0,1,-10deg)} -60%{-webkit-transform:rotate3d(0,0,1,5deg);transform:rotate3d(0,0,1,5deg)} -80%{-webkit-transform:rotate3d(0,0,1,-5deg);transform:rotate3d(0,0,1,-5deg)} -100%{-webkit-transform:rotate3d(0,0,1,0deg);transform:rotate3d(0,0,1,0deg)} -} -.swing{-webkit-transform-origin:top center;transform-origin:top center;-webkit-animation-name:swing;animation-name:swing} -@-webkit-keyframes tada{0%,100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)} -10%,20%{-webkit-transform:scale3d(.9,.9,.9)rotate3d(0,0,1,-3deg);transform:scale3d(.9,.9,.9)rotate3d(0,0,1,-3deg)} -30%,50%,70%,90%{-webkit-transform:scale3d(1.1,1.1,1.1)rotate3d(0,0,1,3deg);transform:scale3d(1.1,1.1,1.1)rotate3d(0,0,1,3deg)} -40%,60%,80%{-webkit-transform:scale3d(1.1,1.1,1.1)rotate3d(0,0,1,-3deg);transform:scale3d(1.1,1.1,1.1)rotate3d(0,0,1,-3deg)} -} -@keyframes tada{0%,100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)} -10%,20%{-webkit-transform:scale3d(.9,.9,.9)rotate3d(0,0,1,-3deg);transform:scale3d(.9,.9,.9)rotate3d(0,0,1,-3deg)} -30%,50%,70%,90%{-webkit-transform:scale3d(1.1,1.1,1.1)rotate3d(0,0,1,3deg);transform:scale3d(1.1,1.1,1.1)rotate3d(0,0,1,3deg)} -40%,60%,80%{-webkit-transform:scale3d(1.1,1.1,1.1)rotate3d(0,0,1,-3deg);transform:scale3d(1.1,1.1,1.1)rotate3d(0,0,1,-3deg)} -} -.tada{-webkit-animation-name:tada;animation-name:tada} -@-webkit-keyframes wobble{0%,100%{-webkit-transform:none;transform:none} -15%{-webkit-transform:translate3d(-25%,0,0)rotate3d(0,0,1,-5deg);transform:translate3d(-25%,0,0)rotate3d(0,0,1,-5deg)} -30%{-webkit-transform:translate3d(20%,0,0)rotate3d(0,0,1,3deg);transform:translate3d(20%,0,0)rotate3d(0,0,1,3deg)} -45%{-webkit-transform:translate3d(-15%,0,0)rotate3d(0,0,1,-3deg);transform:translate3d(-15%,0,0)rotate3d(0,0,1,-3deg)} -60%{-webkit-transform:translate3d(10%,0,0)rotate3d(0,0,1,2deg);transform:translate3d(10%,0,0)rotate3d(0,0,1,2deg)} -75%{-webkit-transform:translate3d(-5%,0,0)rotate3d(0,0,1,-1deg);transform:translate3d(-5%,0,0)rotate3d(0,0,1,-1deg)} -} -@keyframes wobble{0%,100%{-webkit-transform:none;transform:none} -15%{-webkit-transform:translate3d(-25%,0,0)rotate3d(0,0,1,-5deg);transform:translate3d(-25%,0,0)rotate3d(0,0,1,-5deg)} -30%{-webkit-transform:translate3d(20%,0,0)rotate3d(0,0,1,3deg);transform:translate3d(20%,0,0)rotate3d(0,0,1,3deg)} -45%{-webkit-transform:translate3d(-15%,0,0)rotate3d(0,0,1,-3deg);transform:translate3d(-15%,0,0)rotate3d(0,0,1,-3deg)} -60%{-webkit-transform:translate3d(10%,0,0)rotate3d(0,0,1,2deg);transform:translate3d(10%,0,0)rotate3d(0,0,1,2deg)} -75%{-webkit-transform:translate3d(-5%,0,0)rotate3d(0,0,1,-1deg);transform:translate3d(-5%,0,0)rotate3d(0,0,1,-1deg)} -} -.wobble{-webkit-animation-name:wobble;animation-name:wobble} -@-webkit-keyframes bounceIn{0%,100%,20%,40%,60%,80%{-webkit-transition-timing-function:cubic-bezier(.215,.61,.355,1);transition-timing-function:cubic-bezier(.215,.61,.355,1)} -0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)} -20%{-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)} -40%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)} -60%{opacity:1;-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)} -80%{-webkit-transform:scale3d(.97,.97,.97);transform:scale3d(.97,.97,.97)} -100%{opacity:1;-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)} -} -@keyframes bounceIn{0%,100%,20%,40%,60%,80%{-webkit-transition-timing-function:cubic-bezier(.215,.61,.355,1);transition-timing-function:cubic-bezier(.215,.61,.355,1)} -0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)} -20%{-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)} -40%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)} -60%{opacity:1;-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)} -80%{-webkit-transform:scale3d(.97,.97,.97);transform:scale3d(.97,.97,.97)} -100%{opacity:1;-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)} -} -.bounceIn{-webkit-animation-name:bounceIn;animation-name:bounceIn} -@-webkit-keyframes bounceInDown{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(.215,.61,.355,1);transition-timing-function:cubic-bezier(.215,.61,.355,1)} -0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0);transform:translate3d(0,-3000px,0)} -60%{opacity:1;-webkit-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)} -75%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)} -90%{-webkit-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)} -100%{-webkit-transform:none;transform:none} -} -@keyframes bounceInDown{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(.215,.61,.355,1);transition-timing-function:cubic-bezier(.215,.61,.355,1)} -0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0);transform:translate3d(0,-3000px,0)} -60%{opacity:1;-webkit-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)} -75%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)} -90%{-webkit-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)} -100%{-webkit-transform:none;transform:none} -} -.bounceInDown{-webkit-animation-name:bounceInDown;animation-name:bounceInDown} -@-webkit-keyframes bounceInLeft{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(.215,.61,.355,1);transition-timing-function:cubic-bezier(.215,.61,.355,1)} -0%{opacity:0;-webkit-transform:translate3d(-3000px,0,0);transform:translate3d(-3000px,0,0)} -60%{opacity:1;-webkit-transform:translate3d(25px,0,0);transform:translate3d(25px,0,0)} -75%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)} -90%{-webkit-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)} -100%{-webkit-transform:none;transform:none} -} -@keyframes bounceInLeft{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(.215,.61,.355,1);transition-timing-function:cubic-bezier(.215,.61,.355,1)} -0%{opacity:0;-webkit-transform:translate3d(-3000px,0,0);transform:translate3d(-3000px,0,0)} -60%{opacity:1;-webkit-transform:translate3d(25px,0,0);transform:translate3d(25px,0,0)} -75%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)} -90%{-webkit-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)} -100%{-webkit-transform:none;transform:none} -} -.bounceInLeft{-webkit-animation-name:bounceInLeft;animation-name:bounceInLeft} -@-webkit-keyframes bounceInRight{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(.215,.61,.355,1);transition-timing-function:cubic-bezier(.215,.61,.355,1)} -0%{opacity:0;-webkit-transform:translate3d(3000px,0,0);transform:translate3d(3000px,0,0)} -60%{opacity:1;-webkit-transform:translate3d(-25px,0,0);transform:translate3d(-25px,0,0)} -75%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)} -90%{-webkit-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)} -100%{-webkit-transform:none;transform:none} -} -@keyframes bounceInRight{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(.215,.61,.355,1);transition-timing-function:cubic-bezier(.215,.61,.355,1)} -0%{opacity:0;-webkit-transform:translate3d(3000px,0,0);transform:translate3d(3000px,0,0)} -60%{opacity:1;-webkit-transform:translate3d(-25px,0,0);transform:translate3d(-25px,0,0)} -75%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)} -90%{-webkit-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)} -100%{-webkit-transform:none;transform:none} -} -.bounceInRight{-webkit-animation-name:bounceInRight;animation-name:bounceInRight} -@-webkit-keyframes bounceInUp{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(.215,.61,.355,1);transition-timing-function:cubic-bezier(.215,.61,.355,1)} -0%{opacity:0;-webkit-transform:translate3d(0,3000px,0);transform:translate3d(0,3000px,0)} -60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)} -75%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)} -90%{-webkit-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)} -100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)} -} -@keyframes bounceInUp{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(.215,.61,.355,1);transition-timing-function:cubic-bezier(.215,.61,.355,1)} -0%{opacity:0;-webkit-transform:translate3d(0,3000px,0);transform:translate3d(0,3000px,0)} -60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)} -75%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)} -90%{-webkit-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)} -100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)} -} -.bounceInUp{-webkit-animation-name:bounceInUp;animation-name:bounceInUp} -@-webkit-keyframes bounceOut{20%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)} -50%,55%{opacity:1;-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)} -100%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)} -} -@keyframes bounceOut{20%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)} -50%,55%{opacity:1;-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)} -100%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)} -} -.bounceOut{-webkit-animation-name:bounceOut;animation-name:bounceOut} -@-webkit-keyframes bounceOutDown{20%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)} -40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)} -100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)} -} -@keyframes bounceOutDown{20%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)} -40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)} -100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)} -} -.bounceOutDown{-webkit-animation-name:bounceOutDown;animation-name:bounceOutDown} -@-webkit-keyframes bounceOutLeft{20%{opacity:1;-webkit-transform:translate3d(20px,0,0);transform:translate3d(20px,0,0)} -100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)} -} -@keyframes bounceOutLeft{20%{opacity:1;-webkit-transform:translate3d(20px,0,0);transform:translate3d(20px,0,0)} -100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)} -} -.bounceOutLeft{-webkit-animation-name:bounceOutLeft;animation-name:bounceOutLeft} -@-webkit-keyframes bounceOutRight{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0);transform:translate3d(-20px,0,0)} -100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)} -} -@keyframes bounceOutRight{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0);transform:translate3d(-20px,0,0)} -100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)} -} -.bounceOutRight{-webkit-animation-name:bounceOutRight;animation-name:bounceOutRight} -@-webkit-keyframes bounceOutUp{20%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)} -40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0);transform:translate3d(0,20px,0)} -100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)} -} -@keyframes bounceOutUp{20%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)} -40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0);transform:translate3d(0,20px,0)} -100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)} -} -.bounceOutUp{-webkit-animation-name:bounceOutUp;animation-name:bounceOutUp} -@-webkit-keyframes fadeIn{0%{opacity:0} -100%{opacity:1} -} -@keyframes fadeIn{0%{opacity:0} -100%{opacity:1} -} -.fadeIn{-webkit-animation-name:fadeIn;animation-name:fadeIn} -@-webkit-keyframes fadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)} -100%{opacity:1;-webkit-transform:none;transform:none} -} -@keyframes fadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)} -100%{opacity:1;-webkit-transform:none;transform:none} -} -.fadeInDown{-webkit-animation-name:fadeInDown;animation-name:fadeInDown} -@-webkit-keyframes fadeInDownBig{0%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)} -100%{opacity:1;-webkit-transform:none;transform:none} -} -@keyframes fadeInDownBig{0%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)} -100%{opacity:1;-webkit-transform:none;transform:none} -} -.fadeInDownBig{-webkit-animation-name:fadeInDownBig;animation-name:fadeInDownBig} -@-webkit-keyframes fadeInLeft{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)} -100%{opacity:1;-webkit-transform:none;transform:none} -} -@keyframes fadeInLeft{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)} -100%{opacity:1;-webkit-transform:none;transform:none} -} -.fadeInLeft{-webkit-animation-name:fadeInLeft;animation-name:fadeInLeft} -@-webkit-keyframes fadeInLeftBig{0%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)} -100%{opacity:1;-webkit-transform:none;transform:none} -} -@keyframes fadeInLeftBig{0%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)} -100%{opacity:1;-webkit-transform:none;transform:none} -} -.fadeInLeftBig{-webkit-animation-name:fadeInLeftBig;animation-name:fadeInLeftBig} -@-webkit-keyframes fadeInRight{0%{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)} -100%{opacity:1;-webkit-transform:none;transform:none} -} -@keyframes fadeInRight{0%{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)} -100%{opacity:1;-webkit-transform:none;transform:none} -} -.fadeInRight{-webkit-animation-name:fadeInRight;animation-name:fadeInRight} -@-webkit-keyframes fadeInRightBig{0%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)} -100%{opacity:1;-webkit-transform:none;transform:none} -} -@keyframes fadeInRightBig{0%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)} -100%{opacity:1;-webkit-transform:none;transform:none} -} -.fadeInRightBig{-webkit-animation-name:fadeInRightBig;animation-name:fadeInRightBig} -@-webkit-keyframes fadeInUp{0%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)} -100%{opacity:1;-webkit-transform:none;transform:none} -} -@keyframes fadeInUp{0%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)} -100%{opacity:1;-webkit-transform:none;transform:none} -} -.fadeInUp{-webkit-animation-name:fadeInUp;animation-name:fadeInUp} -@-webkit-keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)} -100%{opacity:1;-webkit-transform:none;transform:none} -} -@keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)} -100%{opacity:1;-webkit-transform:none;transform:none} -} -.fadeInUpBig{-webkit-animation-name:fadeInUpBig;animation-name:fadeInUpBig} -@-webkit-keyframes fadeOut{0%{opacity:1} -100%{opacity:0} -} -@keyframes fadeOut{0%{opacity:1} -100%{opacity:0} -} -.fadeOut{-webkit-animation-name:fadeOut;animation-name:fadeOut} -@-webkit-keyframes fadeOutDown{0%{opacity:1} -100%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)} -} -@keyframes fadeOutDown{0%{opacity:1} -100%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)} -} -.fadeOutDown{-webkit-animation-name:fadeOutDown;animation-name:fadeOutDown} -@-webkit-keyframes fadeOutDownBig{0%{opacity:1} -100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)} -} -@keyframes fadeOutDownBig{0%{opacity:1} -100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)} -} -.fadeOutDownBig{-webkit-animation-name:fadeOutDownBig;animation-name:fadeOutDownBig} -@-webkit-keyframes fadeOutLeft{0%{opacity:1} -100%{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)} -} -@keyframes fadeOutLeft{0%{opacity:1} -100%{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)} -} -.fadeOutLeft{-webkit-animation-name:fadeOutLeft;animation-name:fadeOutLeft} -@-webkit-keyframes fadeOutLeftBig{0%{opacity:1} -100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)} -} -@keyframes fadeOutLeftBig{0%{opacity:1} -100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)} -} -.fadeOutLeftBig{-webkit-animation-name:fadeOutLeftBig;animation-name:fadeOutLeftBig} -@-webkit-keyframes fadeOutRight{0%{opacity:1} -100%{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)} -} -@keyframes fadeOutRight{0%{opacity:1} -100%{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)} -} -.fadeOutRight{-webkit-animation-name:fadeOutRight;animation-name:fadeOutRight} -@-webkit-keyframes fadeOutRightBig{0%{opacity:1} -100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)} -} -@keyframes fadeOutRightBig{0%{opacity:1} -100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)} -} -.fadeOutRightBig{-webkit-animation-name:fadeOutRightBig;animation-name:fadeOutRightBig} -@-webkit-keyframes fadeOutUp{0%{opacity:1} -100%{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)} -} -@keyframes fadeOutUp{0%{opacity:1} -100%{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)} -} -.fadeOutUp{-webkit-animation-name:fadeOutUp;animation-name:fadeOutUp} -@-webkit-keyframes fadeOutUpBig{0%{opacity:1} -100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)} -} -@keyframes fadeOutUpBig{0%{opacity:1} -100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)} -} -.fadeOutUpBig{-webkit-animation-name:fadeOutUpBig;animation-name:fadeOutUpBig} -@-webkit-keyframes flip{0%{-webkit-transform:perspective(400px)rotate3d(0,1,0,-360deg);transform:perspective(400px)rotate3d(0,1,0,-360deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out} -40%{-webkit-transform:perspective(400px)translate3d(0,0,150px)rotate3d(0,1,0,-190deg);transform:perspective(400px)translate3d(0,0,150px)rotate3d(0,1,0,-190deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out} -50%{-webkit-transform:perspective(400px)translate3d(0,0,150px)rotate3d(0,1,0,-170deg);transform:perspective(400px)translate3d(0,0,150px)rotate3d(0,1,0,-170deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in} -80%{-webkit-transform:perspective(400px)scale3d(.95,.95,.95);transform:perspective(400px)scale3d(.95,.95,.95);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in} -100%{-webkit-transform:perspective(400px);transform:perspective(400px);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in} -} -@keyframes flip{0%{-webkit-transform:perspective(400px)rotate3d(0,1,0,-360deg);transform:perspective(400px)rotate3d(0,1,0,-360deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out} -40%{-webkit-transform:perspective(400px)translate3d(0,0,150px)rotate3d(0,1,0,-190deg);transform:perspective(400px)translate3d(0,0,150px)rotate3d(0,1,0,-190deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out} -50%{-webkit-transform:perspective(400px)translate3d(0,0,150px)rotate3d(0,1,0,-170deg);transform:perspective(400px)translate3d(0,0,150px)rotate3d(0,1,0,-170deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in} -80%{-webkit-transform:perspective(400px)scale3d(.95,.95,.95);transform:perspective(400px)scale3d(.95,.95,.95);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in} -100%{-webkit-transform:perspective(400px);transform:perspective(400px);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in} -} -.animated.flip{-webkit-backface-visibility:visible;backface-visibility:visible;-webkit-animation-name:flip;animation-name:flip} -@-webkit-keyframes flipInX{0%{-webkit-transform:perspective(400px)rotate3d(1,0,0,90deg);transform:perspective(400px)rotate3d(1,0,0,90deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in;opacity:0} -40%{-webkit-transform:perspective(400px)rotate3d(1,0,0,-20deg);transform:perspective(400px)rotate3d(1,0,0,-20deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in} -60%{-webkit-transform:perspective(400px)rotate3d(1,0,0,10deg);transform:perspective(400px)rotate3d(1,0,0,10deg);opacity:1} -80%{-webkit-transform:perspective(400px)rotate3d(1,0,0,-5deg);transform:perspective(400px)rotate3d(1,0,0,-5deg)} -100%{-webkit-transform:perspective(400px);transform:perspective(400px)} -} -@keyframes flipInX{0%{-webkit-transform:perspective(400px)rotate3d(1,0,0,90deg);transform:perspective(400px)rotate3d(1,0,0,90deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in;opacity:0} -40%{-webkit-transform:perspective(400px)rotate3d(1,0,0,-20deg);transform:perspective(400px)rotate3d(1,0,0,-20deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in} -60%{-webkit-transform:perspective(400px)rotate3d(1,0,0,10deg);transform:perspective(400px)rotate3d(1,0,0,10deg);opacity:1} -80%{-webkit-transform:perspective(400px)rotate3d(1,0,0,-5deg);transform:perspective(400px)rotate3d(1,0,0,-5deg)} -100%{-webkit-transform:perspective(400px);transform:perspective(400px)} -} -.flipInX{backface-visibility:visible!important;-webkit-animation-name:flipInX;animation-name:flipInX} -.flipInX,.flipInY{-webkit-backface-visibility:visible!important} -@-webkit-keyframes flipInY{0%{-webkit-transform:perspective(400px)rotate3d(0,1,0,90deg);transform:perspective(400px)rotate3d(0,1,0,90deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in;opacity:0} -40%{-webkit-transform:perspective(400px)rotate3d(0,1,0,-20deg);transform:perspective(400px)rotate3d(0,1,0,-20deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in} -60%{-webkit-transform:perspective(400px)rotate3d(0,1,0,10deg);transform:perspective(400px)rotate3d(0,1,0,10deg);opacity:1} -80%{-webkit-transform:perspective(400px)rotate3d(0,1,0,-5deg);transform:perspective(400px)rotate3d(0,1,0,-5deg)} -100%{-webkit-transform:perspective(400px);transform:perspective(400px)} -} -@keyframes flipInY{0%{-webkit-transform:perspective(400px)rotate3d(0,1,0,90deg);transform:perspective(400px)rotate3d(0,1,0,90deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in;opacity:0} -40%{-webkit-transform:perspective(400px)rotate3d(0,1,0,-20deg);transform:perspective(400px)rotate3d(0,1,0,-20deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in} -60%{-webkit-transform:perspective(400px)rotate3d(0,1,0,10deg);transform:perspective(400px)rotate3d(0,1,0,10deg);opacity:1} -80%{-webkit-transform:perspective(400px)rotate3d(0,1,0,-5deg);transform:perspective(400px)rotate3d(0,1,0,-5deg)} -100%{-webkit-transform:perspective(400px);transform:perspective(400px)} -} -.flipInY{backface-visibility:visible!important;-webkit-animation-name:flipInY;animation-name:flipInY} -@-webkit-keyframes flipOutX{0%{-webkit-transform:perspective(400px);transform:perspective(400px)} -30%{-webkit-transform:perspective(400px)rotate3d(1,0,0,-20deg);transform:perspective(400px)rotate3d(1,0,0,-20deg);opacity:1} -100%{-webkit-transform:perspective(400px)rotate3d(1,0,0,90deg);transform:perspective(400px)rotate3d(1,0,0,90deg);opacity:0} -} -@keyframes flipOutX{0%{-webkit-transform:perspective(400px);transform:perspective(400px)} -30%{-webkit-transform:perspective(400px)rotate3d(1,0,0,-20deg);transform:perspective(400px)rotate3d(1,0,0,-20deg);opacity:1} -100%{-webkit-transform:perspective(400px)rotate3d(1,0,0,90deg);transform:perspective(400px)rotate3d(1,0,0,90deg);opacity:0} -} -.flipOutX{-webkit-animation-name:flipOutX;animation-name:flipOutX;backface-visibility:visible!important} -.flipOutX,.flipOutY{-webkit-backface-visibility:visible!important} -@-webkit-keyframes flipOutY{0%{-webkit-transform:perspective(400px);transform:perspective(400px)} -30%{-webkit-transform:perspective(400px)rotate3d(0,1,0,-15deg);transform:perspective(400px)rotate3d(0,1,0,-15deg);opacity:1} -100%{-webkit-transform:perspective(400px)rotate3d(0,1,0,90deg);transform:perspective(400px)rotate3d(0,1,0,90deg);opacity:0} -} -@keyframes flipOutY{0%{-webkit-transform:perspective(400px);transform:perspective(400px)} -30%{-webkit-transform:perspective(400px)rotate3d(0,1,0,-15deg);transform:perspective(400px)rotate3d(0,1,0,-15deg);opacity:1} -100%{-webkit-transform:perspective(400px)rotate3d(0,1,0,90deg);transform:perspective(400px)rotate3d(0,1,0,90deg);opacity:0} -} -.flipOutY{backface-visibility:visible!important;-webkit-animation-name:flipOutY;animation-name:flipOutY} -@-webkit-keyframes lightSpeedIn{0%{-webkit-transform:translate3d(100%,0,0)skewX(-30deg);transform:translate3d(100%,0,0)skewX(-30deg);opacity:0} -60%{-webkit-transform:skewX(20deg);transform:skewX(20deg);opacity:1} -80%{-webkit-transform:skewX(-5deg);transform:skewX(-5deg);opacity:1} -100%{-webkit-transform:none;transform:none;opacity:1} -} -@keyframes lightSpeedIn{0%{-webkit-transform:translate3d(100%,0,0)skewX(-30deg);transform:translate3d(100%,0,0)skewX(-30deg);opacity:0} -60%{-webkit-transform:skewX(20deg);transform:skewX(20deg);opacity:1} -80%{-webkit-transform:skewX(-5deg);transform:skewX(-5deg);opacity:1} -100%{-webkit-transform:none;transform:none;opacity:1} -} -.lightSpeedIn{-webkit-animation-name:lightSpeedIn;animation-name:lightSpeedIn;-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out} -@-webkit-keyframes lightSpeedOut{0%{opacity:1} -100%{-webkit-transform:translate3d(100%,0,0)skewX(30deg);transform:translate3d(100%,0,0)skewX(30deg);opacity:0} -} -@keyframes lightSpeedOut{0%{opacity:1} -100%{-webkit-transform:translate3d(100%,0,0)skewX(30deg);transform:translate3d(100%,0,0)skewX(30deg);opacity:0} -} -.lightSpeedOut{-webkit-animation-name:lightSpeedOut;animation-name:lightSpeedOut;-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in} -@-webkit-keyframes rotateIn{0%{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:rotate3d(0,0,1,-200deg);transform:rotate3d(0,0,1,-200deg);opacity:0} -100%{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:none;transform:none;opacity:1} -} -@keyframes rotateIn{0%{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:rotate3d(0,0,1,-200deg);transform:rotate3d(0,0,1,-200deg);opacity:0} -100%{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:none;transform:none;opacity:1} -} -.rotateIn{-webkit-animation-name:rotateIn;animation-name:rotateIn} -@-webkit-keyframes rotateInDownLeft{0%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0} -100%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:none;transform:none;opacity:1} -} -@keyframes rotateInDownLeft{0%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0} -100%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:none;transform:none;opacity:1} -} -.rotateInDownLeft{-webkit-animation-name:rotateInDownLeft;animation-name:rotateInDownLeft} -@-webkit-keyframes rotateInDownRight{0%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0} -100%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:none;transform:none;opacity:1} -} -@keyframes rotateInDownRight{0%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0} -100%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:none;transform:none;opacity:1} -} -.rotateInDownRight{-webkit-animation-name:rotateInDownRight;animation-name:rotateInDownRight} -@-webkit-keyframes rotateInUpLeft{0%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0} -100%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:none;transform:none;opacity:1} -} -@keyframes rotateInUpLeft{0%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0} -100%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:none;transform:none;opacity:1} -} -.rotateInUpLeft{-webkit-animation-name:rotateInUpLeft;animation-name:rotateInUpLeft} -@-webkit-keyframes rotateInUpRight{0%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,-90deg);transform:rotate3d(0,0,1,-90deg);opacity:0} -100%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:none;transform:none;opacity:1} -} -@keyframes rotateInUpRight{0%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,-90deg);transform:rotate3d(0,0,1,-90deg);opacity:0} -100%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:none;transform:none;opacity:1} -} -.rotateInUpRight{-webkit-animation-name:rotateInUpRight;animation-name:rotateInUpRight} -@-webkit-keyframes rotateOut{0%{-webkit-transform-origin:center;transform-origin:center;opacity:1} -100%{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:rotate3d(0,0,1,200deg);transform:rotate3d(0,0,1,200deg);opacity:0} -} -@keyframes rotateOut{0%{-webkit-transform-origin:center;transform-origin:center;opacity:1} -100%{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:rotate3d(0,0,1,200deg);transform:rotate3d(0,0,1,200deg);opacity:0} -} -.rotateOut{-webkit-animation-name:rotateOut;animation-name:rotateOut} -@-webkit-keyframes rotateOutDownLeft{0%{-webkit-transform-origin:left bottom;transform-origin:left bottom;opacity:1} -100%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0} -} -@keyframes rotateOutDownLeft{0%{-webkit-transform-origin:left bottom;transform-origin:left bottom;opacity:1} -100%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0} -} -.rotateOutDownLeft{-webkit-animation-name:rotateOutDownLeft;animation-name:rotateOutDownLeft} -@-webkit-keyframes rotateOutDownRight{0%{-webkit-transform-origin:right bottom;transform-origin:right bottom;opacity:1} -100%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0} -} -@keyframes rotateOutDownRight{0%{-webkit-transform-origin:right bottom;transform-origin:right bottom;opacity:1} -100%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0} -} -.rotateOutDownRight{-webkit-animation-name:rotateOutDownRight;animation-name:rotateOutDownRight} -@-webkit-keyframes rotateOutUpLeft{0%{-webkit-transform-origin:left bottom;transform-origin:left bottom;opacity:1} -100%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0} -} -@keyframes rotateOutUpLeft{0%{-webkit-transform-origin:left bottom;transform-origin:left bottom;opacity:1} -100%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0} -} -.rotateOutUpLeft{-webkit-animation-name:rotateOutUpLeft;animation-name:rotateOutUpLeft} -@-webkit-keyframes rotateOutUpRight{0%{-webkit-transform-origin:right bottom;transform-origin:right bottom;opacity:1} -100%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,90deg);transform:rotate3d(0,0,1,90deg);opacity:0} -} -@keyframes rotateOutUpRight{0%{-webkit-transform-origin:right bottom;transform-origin:right bottom;opacity:1} -100%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,90deg);transform:rotate3d(0,0,1,90deg);opacity:0} -} -.rotateOutUpRight{-webkit-animation-name:rotateOutUpRight;animation-name:rotateOutUpRight} -@-webkit-keyframes hinge{0%{-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out} -20%,60%{-webkit-transform:rotate3d(0,0,1,80deg);transform:rotate3d(0,0,1,80deg);-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out} -40%,80%{-webkit-transform:rotate3d(0,0,1,60deg);transform:rotate3d(0,0,1,60deg);-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;opacity:1} -100%{-webkit-transform:translate3d(0,700px,0);transform:translate3d(0,700px,0);opacity:0} -} -@keyframes hinge{0%{-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out} -20%,60%{-webkit-transform:rotate3d(0,0,1,80deg);transform:rotate3d(0,0,1,80deg);-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out} -40%,80%{-webkit-transform:rotate3d(0,0,1,60deg);transform:rotate3d(0,0,1,60deg);-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;opacity:1} -100%{-webkit-transform:translate3d(0,700px,0);transform:translate3d(0,700px,0);opacity:0} -} -.hinge{-webkit-animation-name:hinge;animation-name:hinge} -@-webkit-keyframes rollIn{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0)rotate3d(0,0,1,-120deg);transform:translate3d(-100%,0,0)rotate3d(0,0,1,-120deg)} -100%{opacity:1;-webkit-transform:none;transform:none} -} -@keyframes rollIn{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0)rotate3d(0,0,1,-120deg);transform:translate3d(-100%,0,0)rotate3d(0,0,1,-120deg)} -100%{opacity:1;-webkit-transform:none;transform:none} -} -.rollIn{-webkit-animation-name:rollIn;animation-name:rollIn} -@-webkit-keyframes rollOut{0%{opacity:1} -100%{opacity:0;-webkit-transform:translate3d(100%,0,0)rotate3d(0,0,1,120deg);transform:translate3d(100%,0,0)rotate3d(0,0,1,120deg)} -} -@keyframes rollOut{0%{opacity:1} -100%{opacity:0;-webkit-transform:translate3d(100%,0,0)rotate3d(0,0,1,120deg);transform:translate3d(100%,0,0)rotate3d(0,0,1,120deg)} -} -.rollOut{-webkit-animation-name:rollOut;animation-name:rollOut} -@-webkit-keyframes zoomIn{0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)} -50%{opacity:1} -} -@keyframes zoomIn{0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)} -50%{opacity:1} -} -.zoomIn{-webkit-animation-name:zoomIn;animation-name:zoomIn} -@-webkit-keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1)translate3d(0,-1000px,0);transform:scale3d(.1,.1,.1)translate3d(0,-1000px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)} -60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(0,60px,0);transform:scale3d(.475,.475,.475)translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)} -} -@keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1)translate3d(0,-1000px,0);transform:scale3d(.1,.1,.1)translate3d(0,-1000px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)} -60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(0,60px,0);transform:scale3d(.475,.475,.475)translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)} -} -.zoomInDown{-webkit-animation-name:zoomInDown;animation-name:zoomInDown} -@-webkit-keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1)translate3d(-1000px,0,0);transform:scale3d(.1,.1,.1)translate3d(-1000px,0,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)} -60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(10px,0,0);transform:scale3d(.475,.475,.475)translate3d(10px,0,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)} -} -@keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1)translate3d(-1000px,0,0);transform:scale3d(.1,.1,.1)translate3d(-1000px,0,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)} -60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(10px,0,0);transform:scale3d(.475,.475,.475)translate3d(10px,0,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)} -} -.zoomInLeft{-webkit-animation-name:zoomInLeft;animation-name:zoomInLeft} -@-webkit-keyframes zoomInRight{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1)translate3d(1000px,0,0);transform:scale3d(.1,.1,.1)translate3d(1000px,0,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)} -60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(-10px,0,0);transform:scale3d(.475,.475,.475)translate3d(-10px,0,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)} -} -@keyframes zoomInRight{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1)translate3d(1000px,0,0);transform:scale3d(.1,.1,.1)translate3d(1000px,0,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)} -60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(-10px,0,0);transform:scale3d(.475,.475,.475)translate3d(-10px,0,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)} -} -.zoomInRight{-webkit-animation-name:zoomInRight;animation-name:zoomInRight} -@-webkit-keyframes zoomInUp{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1)translate3d(0,1000px,0);transform:scale3d(.1,.1,.1)translate3d(0,1000px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)} -60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(0,-60px,0);transform:scale3d(.475,.475,.475)translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)} -} -@keyframes zoomInUp{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1)translate3d(0,1000px,0);transform:scale3d(.1,.1,.1)translate3d(0,1000px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)} -60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(0,-60px,0);transform:scale3d(.475,.475,.475)translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)} -} -.zoomInUp{-webkit-animation-name:zoomInUp;animation-name:zoomInUp} -@-webkit-keyframes zoomOut{0%{opacity:1} -50%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)} -100%{opacity:0} -} -@keyframes zoomOut{0%{opacity:1} -50%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)} -100%{opacity:0} -} -.zoomOut{-webkit-animation-name:zoomOut;animation-name:zoomOut} -@-webkit-keyframes zoomOutDown{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(0,-60px,0);transform:scale3d(.475,.475,.475)translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)} -100%{opacity:0;-webkit-transform:scale3d(.1,.1,.1)translate3d(0,2000px,0);transform:scale3d(.1,.1,.1)translate3d(0,2000px,0);-webkit-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)} -} -@keyframes zoomOutDown{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(0,-60px,0);transform:scale3d(.475,.475,.475)translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)} -100%{opacity:0;-webkit-transform:scale3d(.1,.1,.1)translate3d(0,2000px,0);transform:scale3d(.1,.1,.1)translate3d(0,2000px,0);-webkit-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)} -} -.zoomOutDown{-webkit-animation-name:zoomOutDown;animation-name:zoomOutDown} -@-webkit-keyframes zoomOutLeft{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(42px,0,0);transform:scale3d(.475,.475,.475)translate3d(42px,0,0)} -100%{opacity:0;-webkit-transform:scale(.1)translate3d(-2000px,0,0);transform:scale(.1)translate3d(-2000px,0,0);-webkit-transform-origin:left center;transform-origin:left center} -} -@keyframes zoomOutLeft{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(42px,0,0);transform:scale3d(.475,.475,.475)translate3d(42px,0,0)} -100%{opacity:0;-webkit-transform:scale(.1)translate3d(-2000px,0,0);transform:scale(.1)translate3d(-2000px,0,0);-webkit-transform-origin:left center;transform-origin:left center} -} -.zoomOutLeft{-webkit-animation-name:zoomOutLeft;animation-name:zoomOutLeft} -@-webkit-keyframes zoomOutRight{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(-42px,0,0);transform:scale3d(.475,.475,.475)translate3d(-42px,0,0)} -100%{opacity:0;-webkit-transform:scale(.1)translate3d(2000px,0,0);transform:scale(.1)translate3d(2000px,0,0);-webkit-transform-origin:right center;transform-origin:right center} -} -@keyframes zoomOutRight{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(-42px,0,0);transform:scale3d(.475,.475,.475)translate3d(-42px,0,0)} -100%{opacity:0;-webkit-transform:scale(.1)translate3d(2000px,0,0);transform:scale(.1)translate3d(2000px,0,0);-webkit-transform-origin:right center;transform-origin:right center} -} -.zoomOutRight{-webkit-animation-name:zoomOutRight;animation-name:zoomOutRight} -@-webkit-keyframes zoomOutUp{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(0,60px,0);transform:scale3d(.475,.475,.475)translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)} -100%{opacity:0;-webkit-transform:scale3d(.1,.1,.1)translate3d(0,-2000px,0);transform:scale3d(.1,.1,.1)translate3d(0,-2000px,0);-webkit-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)} -} -@keyframes zoomOutUp{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(0,60px,0);transform:scale3d(.475,.475,.475)translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)} -100%{opacity:0;-webkit-transform:scale3d(.1,.1,.1)translate3d(0,-2000px,0);transform:scale3d(.1,.1,.1)translate3d(0,-2000px,0);-webkit-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)} -} -.zoomOutUp{-webkit-animation-name:zoomOutUp;animation-name:zoomOutUp} -@-webkit-keyframes slideInDown{0%{-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0);visibility:visible} -100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)} -} -@keyframes slideInDown{0%{-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0);visibility:visible} -100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)} -} -.slideInDown{-webkit-animation-name:slideInDown;animation-name:slideInDown} -@-webkit-keyframes slideInLeft{0%{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0);visibility:visible} -100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)} -} -@keyframes slideInLeft{0%{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0);visibility:visible} -100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)} -} -.slideInLeft{-webkit-animation-name:slideInLeft;animation-name:slideInLeft} -@-webkit-keyframes slideInRight{0%{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0);visibility:visible} -100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)} -} -@keyframes slideInRight{0%{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0);visibility:visible} -100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)} -} -.slideInRight{-webkit-animation-name:slideInRight;animation-name:slideInRight} -@-webkit-keyframes slideInUp{0%{-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0);visibility:visible} -100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)} -} -@keyframes slideInUp{0%{-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0);visibility:visible} -100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)} -} -.slideInUp{-webkit-animation-name:slideInUp;animation-name:slideInUp} -@-webkit-keyframes slideOutDown{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)} -100%{visibility:hidden;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)} -} -@keyframes slideOutDown{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)} -100%{visibility:hidden;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)} -} -.slideOutDown{-webkit-animation-name:slideOutDown;animation-name:slideOutDown} -@-webkit-keyframes slideOutLeft{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)} -100%{visibility:hidden;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)} -} -@keyframes slideOutLeft{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)} -100%{visibility:hidden;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)} -} -.slideOutLeft{-webkit-animation-name:slideOutLeft;animation-name:slideOutLeft} -@-webkit-keyframes slideOutRight{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)} -100%{visibility:hidden;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)} -} -@keyframes slideOutRight{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)} -100%{visibility:hidden;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)} -} -.slideOutRight{-webkit-animation-name:slideOutRight;animation-name:slideOutRight} -@-webkit-keyframes slideOutUp{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)} -100%{visibility:hidden;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)} -} -@keyframes slideOutUp{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)} -100%{visibility:hidden;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)} -} -.slideOutUp{-webkit-animation-name:slideOutUp;animation-name:slideOutUp} \ No newline at end of file diff --git a/public/css/jquery.terminal.css b/public/css/jquery.terminal.css deleted file mode 100755 index c838aa0d3..000000000 --- a/public/css/jquery.terminal.css +++ /dev/null @@ -1,253 +0,0 @@ -/*! - * __ _____ ________ __ - * / // _ /__ __ _____ ___ __ _/__ ___/__ ___ ______ __ __ __ ___ / / - * __ / // // // // // _ // _// // / / // _ // _// // // \/ // _ \/ / - * / / // // // // // ___// / / // / / // ___// / / / / // // /\ // // / /__ - * \___//____ \\___//____//_/ _\_ / /_//____//_/ /_/ /_//_//_/ /_/ \__\_\___/ - * \/ /____/ version 0.11.6 - * http://terminal.jcubic.pl - * - * This file is part of jQuery Terminal. - * - * Copyright (c) 2011-2016 Jakub Jankiewicz - * Released under the MIT license - * - * Date: Thu, 15 Sep 2016 20:19:20 +0000 - */ -.terminal .terminal-output .format, .cmd .format, -.cmd .prompt, .cmd .prompt div, .terminal .terminal-output div div{ - display: inline-block; -} -.terminal h1, .terminal h2, .terminal h3, .terminal h4, .terminal h5, .terminal h6, .terminal pre, .cmd { - margin: 0; -} -.terminal h1, .terminal h2, .terminal h3, .terminal h4, .terminal h5, .terminal h6 { - line-height: 1.2em; -} -/* -.cmd .mask { - width: 10px; - height: 11px; - background: black; - z-index: 100; -} -*/ -.cmd .clipboard { - position: absolute; - left: -16px; - top: 0; - width: 10px; - height: 16px; - /* this seems to work after all on Android */ - /*left: -99999px; - clip: rect(1px,1px,1px,1px); - /* on desktop textarea appear when paste */ - /* - opacity: 0.01; - filter: alpha(opacity = 0.01); - filter: progid:DXImageTransform.Microsoft.Alpha(opacity=0.01); - */ - background: transparent; - border: none; - color: transparent; - outline: none; - padding: 0; - resize: none; - z-index: 0; - overflow: hidden; -} -.terminal .error { - color: #f00; -} -.terminal { - padding: 10px; - position: relative; - /*overflow: hidden;*/ - overflow: auto; -} -.cmd { - padding: 0; - height: 1.3em; - position: relative; - /*margin-top: 3px; */ -} -.terminal .inverted, .cmd .inverted, .cmd .cursor.blink { - background-color: #aaa; - color: #000; -} -.cmd .cursor.blink { - -webkit-animation: terminal-blink 1s infinite steps(1, start); - -moz-animation: terminal-blink 1s infinite steps(1, start); - -ms-animation: terminal-blink 1s infinite steps(1, start); - animation: terminal-blink 1s infinite steps(1, start); -} -@-webkit-keyframes terminal-blink { - 0%, 100% { - background-color: #000; - color: #aaa; - } - 50% { - background-color: #bbb; - color: #000; - } -} - -@-ms-keyframes terminal-blink { - 0%, 100% { - background-color: #000; - color: #aaa; - } - 50% { - background-color: #bbb; - color: #000; - } -} - -@-moz-keyframes terminal-blink { - 0%, 100% { - background-color: #000; - color: #aaa; - } - 50% { - background-color: #bbb; - color: #000; - } -} -@keyframes terminal-blink { - 0%, 100% { - background-color: #000; - color: #aaa; - } - 50% { - background-color: #bbb; /* not #aaa because it's seems there is Google Chrome bug */ - color: #000; - } -} - -.terminal .terminal-output div div, .cmd .prompt { - display: block; - line-height: 14px; - height: auto; -} -.cmd .prompt { - float: left; -} -.terminal, .cmd { - font-family: monospace; - /*font-family: FreeMono, monospace; this don't work on Android */ - color: #aaa; - background-color: #000; - font-size: 12px; - line-height: 14px; -} -.terminal-output > div { - /*padding-top: 3px;*/ - min-height: 14px; -} -.terminal-output > div > div * { - word-wrap: break-word; /* when echo html */ -} -.terminal .terminal-output div span { - display: inline-block; -} -.cmd span { - float: left; - /*display: inline-block; */ -} -/* fix double style of selecting text in terminal */ -.terminal-output span, .terminal-output a, .cmd div, .cmd span, .terminal td, -.terminal pre, .terminal h1, .terminal h2, .terminal h3, .terminal h4, -.terminal h5, .terminal h6 { - -webkit-touch-callout: initial; - -webkit-user-select: initial; - -khtml-user-select: initial; - -moz-user-select: initial; - -ms-user-select: initial; - user-select: initial; -} -.terminal, .terminal-output, .terminal-output div { - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} -/* firefox hack */ -@-moz-document url-prefix() { - .terminal, .terminal-output, .terminal-output div { - -webkit-touch-callout: initial; - -webkit-user-select: initial; - -khtml-user-select: initial; - -moz-user-select: initial; - -ms-user-select: initial; - user-select: initial; - } -} -.terminal table { - border-collapse: collapse; -} -.terminal td { - border: 1px solid #aaa; -} -.terminal h1::-moz-selection, -.terminal h2::-moz-selection, -.terminal h3::-moz-selection, -.terminal h4::-moz-selection, -.terminal h5::-moz-selection, -.terminal h6::-moz-selection, -.terminal pre::-moz-selection, -.terminal td::-moz-selection, -.terminal .terminal-output div div::-moz-selection, -.terminal .terminal-output div span::-moz-selection, -.terminal .terminal-output div div a::-moz-selection, -.cmd div::-moz-selection, -.cmd > span::-moz-selection, -.cmd .prompt span::-moz-selection { - background-color: #aaa; - color: #000; -} -/* this don't work in Chrome -.terminal tr td::-moz-selection { - border-color: #000; -} -.terminal tr td::selection { - border-color: #000; -} -*/ -.terminal h1::selection, -.terminal h2::selection, -.terminal h3::selection, -.terminal h4::selection, -.terminal h5::selection, -.terminal h6::selection, -.terminal pre::selection, -.terminal td::selection, -.terminal .terminal-output div div::selection, -.terminal .terminal-output div div a::selection, -.terminal .terminal-output div span::selection, -.cmd div::selection, -.cmd > span::selection, -.cmd .prompt span::selection { - background-color: #aaa; - color: #000; -} -.terminal .terminal-output div.error, .terminal .terminal-output div.error div { - color: red; -} -.tilda { - position: fixed; - top: 0; - left: 0; - width: 100%; - z-index: 1100; -} -.clear { - clear: both; -} -.terminal a { - color: #0F60FF; -} -.terminal a:hover { - color: red; -} diff --git a/public/css/metricsgraphics.css b/public/css/metricsgraphics.css deleted file mode 100755 index b523ab4ad..000000000 --- a/public/css/metricsgraphics.css +++ /dev/null @@ -1,369 +0,0 @@ - /*v 2.8*/ -.mg-active-datapoint { - fill: black; - font-size: 0.9rem; - font-weight: 400; - opacity: 0.8; -} - -.mg-area1-color { - fill: #0000ff; -} - -.mg-area2-color { - fill: #05b378; -} - -.mg-area3-color { - fill: #db4437; -} - -.mg-area4-color { - fill: #f8b128; -} - -.mg-area5-color { - fill: #5c5c5c; -} - -.mg-barplot rect.mg-bar { - shape-rendering: auto; - fill: #b6b6fc; -} - -.mg-barplot rect.mg-bar.active { - fill: #9e9efc; -} - -.mg-barplot .mg-bar-prediction { - fill: #5b5b5b; -} - -.mg-barplot .mg-bar-baseline { - stroke: #5b5b5b; - stroke-width: 2; -} - -.mg-baselines line { - opacity: 1; - shape-rendering: auto; - stroke: #b3b2b2; - stroke-width: 1px; -} - -.mg-baselines text { - fill: black; - font-size: 0.9rem; - opacity: 0.6; - stroke: none; -} - -.mg-baselines-small text { - font-size: 0.6rem; -} - -.mg-header { - cursor: default; - font-size: 1.2rem; -} - -.mg-header .mg-chart-description { - fill: #ccc; - font-family: FontAwesome; - font-size: 1.2rem; -} - -.mg-points circle { - opacity: 0.65; -} - -.mg-popover { - font-size: 0.95rem; -} - -.mg-popover-content { - cursor: auto; - line-height: 17px; -} - -.mg-data-table { - margin-top: 30px; -} - -.mg-data-table thead tr th { - border-bottom: 1px solid darkgray; - cursor: default; - font-size: 1.1rem; - font-weight: normal; - padding: 5px 5px 8px 5px; - text-align: right; -} - -.mg-data-table thead tr th .fa { - color: #ccc; - padding-left: 4px; -} - -.mg-data-table thead tr th .popover { - font-size: 1rem; - font-weight: normal; -} - -.mg-data-table .secondary-title { - color: darkgray; -} - -.mg-data-table tbody tr td { - margin: 2px; - padding: 5px; - vertical-align: top; -} - -.mg-data-table tbody tr td.table-text { - opacity: 0.8; - padding-left: 30px; -} - -.mg-y-axis line.mg-extended-y-ticks { - opacity: 0.4; -} - -.mg-x-axis line.mg-extended-x-ticks { - opacity: 0.4; -} - -.mg-histogram .axis path, -.mg-histogram .axis line { - fill: none; - opacity: 0.7; - shape-rendering: auto; - stroke: #ccc; -} - -.mg-histogram .mg-bar rect { - fill: #b6b6fc; - shape-rendering: auto; -} - -.mg-histogram .mg-bar rect.active { - fill: #9e9efc; -} - -.mg-least-squares-line { - stroke: red; - stroke-width: 1px; -} - -.mg-lowess-line { - fill: none; - stroke: red; -} - -.mg-line1-color { - stroke: #4040e8; -} - -.mg-hover-line1-color { - fill: #4040e8; -} - -.mg-line2-color { - stroke: #05b378; -} - -.mg-hover-line2-color { - fill: #05b378; -} - -.mg-line3-color { - stroke: #db4437; -} - -.mg-hover-line3-color { - fill: #db4437; -} - -.mg-line4-color { - stroke: #f8b128; -} - -.mg-hover-line4-color { - fill: #f8b128; -} - -.mg-line5-color { - stroke: #5c5c5c; -} - -.mg-hover-line5-color { - fill: #5c5c5c; -} - -.mg-line-legend text { - font-size: 0.9rem; - font-weight: 300; - stroke: none; -} - -.mg-line1-legend-color { - color: #4040e8; - fill: #4040e8; -} - -.mg-line2-legend-color { - color: #05b378; - fill: #05b378; -} - -.mg-line3-legend-color { - color: #db4437; - fill: #db4437; -} - -.mg-line4-legend-color { - color: #f8b128; - fill: #f8b128; -} - -.mg-line5-legend-color { - color: #5c5c5c; - fill: #5c5c5c; -} - -.mg-main-area-solid svg .mg-main-area { - fill: #ccccff; - opacity: 1; -} - -.mg-markers line { - opacity: 1; - shape-rendering: auto; - stroke: #b3b2b2; - stroke-width: 1px; -} - -.mg-markers text { - fill: black; - font-size: 0.8rem; - opacity: 0.6; -} - -.mg-missing-text { - opacity: 0.9; -} - -.mg-missing-background { - stroke: blue; - fill: none; - stroke-dasharray: 10,5; - stroke-opacity: 0.05; - stroke-width: 2; -} - -.mg-missing .mg-main-line { - opacity: 0.1; -} - -.mg-missing .mg-main-area { - opacity: 0.03; -} - -path.mg-main-area { - opacity: 0.2; - stroke: none; -} - -path.mg-confidence-band { - fill: #ccc; - opacity: 0.4; - stroke: none; -} - -path.mg-main-line { - fill: none; - opacity: 0.8; - stroke-width: 1.1px; -} - -.mg-points circle { - fill-opacity: 0.4; - stroke-opacity: 1; -} - -circle.mg-points-mono { - fill: #0000ff; - stroke: #0000ff; -} - -/* a selected point in a scatterplot */ -.mg-points circle.selected { - fill-opacity: 1; - stroke-opacity: 1; -} - -.mg-voronoi path { - fill: none; - pointer-events: all; - stroke: none; - stroke-opacity: 0.1; -} - -.mg-x-rug-mono, -.mg-y-rug-mono { - stroke: black; -} - -.mg-x-axis line, -.mg-y-axis line { - opacity: 1; - shape-rendering: auto; - stroke: #b3b2b2; - stroke-width: 1px; -} - -.mg-x-axis text, -.mg-y-axis text, -.mg-histogram .axis text { - fill: black; - font-size: 0.9rem; - opacity: 0.6; -} - -.mg-x-axis .label, -.mg-y-axis .label, -.mg-axis .label { - font-size: 0.8rem; - text-transform: uppercase; - font-weight: 400; -} - -.mg-x-axis-small text, -.mg-y-axis-small text, -.mg-active-datapoint-small { - font-size: 0.6rem; -} - -.mg-x-axis-small .label, -.mg-y-axis-small .label { - font-size: 0.65rem; -} - -.mg-european-hours { -} - -.mg-year-marker text { - fill: black; - font-size: 0.7rem; - opacity: 0.6; -} - -.mg-year-marker line { - opacity: 1; - shape-rendering: auto; - stroke: #b3b2b2; - stroke-width: 1px; -} - -.mg-year-marker-small text { - font-size: 0.6rem; -} diff --git a/public/css/vendor/fontawesome/animation.min.css b/public/css/vendor/fontawesome/animation.min.css deleted file mode 100644 index 8d4256393..000000000 --- a/public/css/vendor/fontawesome/animation.min.css +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * font-awesome-animation - v0.0.9 - * https://github.com/l-lin/font-awesome-animation - * License: MIT - */ -@-webkit-keyframes wrench{0%{-webkit-transform:rotate(-12deg);transform:rotate(-12deg)}8%{-webkit-transform:rotate(12deg);transform:rotate(12deg)}10%{-webkit-transform:rotate(24deg);transform:rotate(24deg)}18%,20%{-webkit-transform:rotate(-24deg);transform:rotate(-24deg)}28%,30%{-webkit-transform:rotate(24deg);transform:rotate(24deg)}38%,40%{-webkit-transform:rotate(-24deg);transform:rotate(-24deg)}48%,50%{-webkit-transform:rotate(24deg);transform:rotate(24deg)}58%,60%{-webkit-transform:rotate(-24deg);transform:rotate(-24deg)}68%{-webkit-transform:rotate(24deg);transform:rotate(24deg)}100%,75%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@keyframes wrench{0%{-webkit-transform:rotate(-12deg);-ms-transform:rotate(-12deg);transform:rotate(-12deg)}8%{-webkit-transform:rotate(12deg);-ms-transform:rotate(12deg);transform:rotate(12deg)}10%{-webkit-transform:rotate(24deg);-ms-transform:rotate(24deg);transform:rotate(24deg)}18%,20%{-webkit-transform:rotate(-24deg);-ms-transform:rotate(-24deg);transform:rotate(-24deg)}28%,30%{-webkit-transform:rotate(24deg);-ms-transform:rotate(24deg);transform:rotate(24deg)}38%,40%{-webkit-transform:rotate(-24deg);-ms-transform:rotate(-24deg);transform:rotate(-24deg)}48%,50%{-webkit-transform:rotate(24deg);-ms-transform:rotate(24deg);transform:rotate(24deg)}58%,60%{-webkit-transform:rotate(-24deg);-ms-transform:rotate(-24deg);transform:rotate(-24deg)}68%{-webkit-transform:rotate(24deg);-ms-transform:rotate(24deg);transform:rotate(24deg)}100%,75%{-webkit-transform:rotate(0deg);-ms-transform:rotate(0deg);transform:rotate(0deg)}}.faa-parent.animated-hover:hover>.faa-wrench,.faa-wrench.animated,.faa-wrench.animated-hover:hover{-webkit-animation:wrench 2.5s ease infinite;animation:wrench 2.5s ease infinite;transform-origin-x:90%;transform-origin-y:35%;transform-origin-z:initial}.faa-parent.animated-hover:hover>.faa-wrench.faa-fast,.faa-wrench.animated-hover.faa-fast:hover,.faa-wrench.animated.faa-fast{-webkit-animation:wrench 1.2s ease infinite;animation:wrench 1.2s ease infinite}.faa-parent.animated-hover:hover>.faa-wrench.faa-slow,.faa-wrench.animated-hover.faa-slow:hover,.faa-wrench.animated.faa-slow{-webkit-animation:wrench 3.7s ease infinite;animation:wrench 3.7s ease infinite}@-webkit-keyframes ring{0%{-webkit-transform:rotate(-15deg);transform:rotate(-15deg)}2%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}4%{-webkit-transform:rotate(-18deg);transform:rotate(-18deg)}6%{-webkit-transform:rotate(18deg);transform:rotate(18deg)}8%{-webkit-transform:rotate(-22deg);transform:rotate(-22deg)}10%{-webkit-transform:rotate(22deg);transform:rotate(22deg)}12%{-webkit-transform:rotate(-18deg);transform:rotate(-18deg)}14%{-webkit-transform:rotate(18deg);transform:rotate(18deg)}16%{-webkit-transform:rotate(-12deg);transform:rotate(-12deg)}18%{-webkit-transform:rotate(12deg);transform:rotate(12deg)}100%,20%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@keyframes ring{0%{-webkit-transform:rotate(-15deg);-ms-transform:rotate(-15deg);transform:rotate(-15deg)}2%{-webkit-transform:rotate(15deg);-ms-transform:rotate(15deg);transform:rotate(15deg)}4%{-webkit-transform:rotate(-18deg);-ms-transform:rotate(-18deg);transform:rotate(-18deg)}6%{-webkit-transform:rotate(18deg);-ms-transform:rotate(18deg);transform:rotate(18deg)}8%{-webkit-transform:rotate(-22deg);-ms-transform:rotate(-22deg);transform:rotate(-22deg)}10%{-webkit-transform:rotate(22deg);-ms-transform:rotate(22deg);transform:rotate(22deg)}12%{-webkit-transform:rotate(-18deg);-ms-transform:rotate(-18deg);transform:rotate(-18deg)}14%{-webkit-transform:rotate(18deg);-ms-transform:rotate(18deg);transform:rotate(18deg)}16%{-webkit-transform:rotate(-12deg);-ms-transform:rotate(-12deg);transform:rotate(-12deg)}18%{-webkit-transform:rotate(12deg);-ms-transform:rotate(12deg);transform:rotate(12deg)}100%,20%{-webkit-transform:rotate(0deg);-ms-transform:rotate(0deg);transform:rotate(0deg)}}.faa-parent.animated-hover:hover>.faa-ring,.faa-ring.animated,.faa-ring.animated-hover:hover{-webkit-animation:ring 2s ease infinite;animation:ring 2s ease infinite;transform-origin-x:50%;transform-origin-y:0;transform-origin-z:initial}.faa-parent.animated-hover:hover>.faa-ring.faa-fast,.faa-ring.animated-hover.faa-fast:hover,.faa-ring.animated.faa-fast{-webkit-animation:ring 1s ease infinite;animation:ring 1s ease infinite}.faa-parent.animated-hover:hover>.faa-ring.faa-slow,.faa-ring.animated-hover.faa-slow:hover,.faa-ring.animated.faa-slow{-webkit-animation:ring 3s ease infinite;animation:ring 3s ease infinite}@-webkit-keyframes vertical{0%{-webkit-transform:translate(0,-3px);transform:translate(0,-3px)}4%{-webkit-transform:translate(0,3px);transform:translate(0,3px)}8%{-webkit-transform:translate(0,-3px);transform:translate(0,-3px)}12%{-webkit-transform:translate(0,3px);transform:translate(0,3px)}16%{-webkit-transform:translate(0,-3px);transform:translate(0,-3px)}20%{-webkit-transform:translate(0,3px);transform:translate(0,3px)}100%,22%{-webkit-transform:translate(0,0);transform:translate(0,0)}}@keyframes vertical{0%{-webkit-transform:translate(0,-3px);-ms-transform:translate(0,-3px);transform:translate(0,-3px)}4%{-webkit-transform:translate(0,3px);-ms-transform:translate(0,3px);transform:translate(0,3px)}8%{-webkit-transform:translate(0,-3px);-ms-transform:translate(0,-3px);transform:translate(0,-3px)}12%{-webkit-transform:translate(0,3px);-ms-transform:translate(0,3px);transform:translate(0,3px)}16%{-webkit-transform:translate(0,-3px);-ms-transform:translate(0,-3px);transform:translate(0,-3px)}20%{-webkit-transform:translate(0,3px);-ms-transform:translate(0,3px);transform:translate(0,3px)}100%,22%{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}}.faa-parent.animated-hover:hover>.faa-vertical,.faa-vertical.animated,.faa-vertical.animated-hover:hover{-webkit-animation:vertical 2s ease infinite;animation:vertical 2s ease infinite}.faa-parent.animated-hover:hover>.faa-vertical.faa-fast,.faa-vertical.animated-hover.faa-fast:hover,.faa-vertical.animated.faa-fast{-webkit-animation:vertical 1s ease infinite;animation:vertical 1s ease infinite}.faa-parent.animated-hover:hover>.faa-vertical.faa-slow,.faa-vertical.animated-hover.faa-slow:hover,.faa-vertical.animated.faa-slow{-webkit-animation:vertical 4s ease infinite;animation:vertical 4s ease infinite}@-webkit-keyframes horizontal{0%{-webkit-transform:translate(0,0);transform:translate(0,0)}6%{-webkit-transform:translate(5px,0);transform:translate(5px,0)}12%{-webkit-transform:translate(0,0);transform:translate(0,0)}18%{-webkit-transform:translate(5px,0);transform:translate(5px,0)}24%{-webkit-transform:translate(0,0);transform:translate(0,0)}30%{-webkit-transform:translate(5px,0);transform:translate(5px,0)}100%,36%{-webkit-transform:translate(0,0);transform:translate(0,0)}}@keyframes horizontal{0%{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}6%{-webkit-transform:translate(5px,0);-ms-transform:translate(5px,0);transform:translate(5px,0)}12%{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}18%{-webkit-transform:translate(5px,0);-ms-transform:translate(5px,0);transform:translate(5px,0)}24%{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}30%{-webkit-transform:translate(5px,0);-ms-transform:translate(5px,0);transform:translate(5px,0)}100%,36%{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}}.faa-horizontal.animated,.faa-horizontal.animated-hover:hover,.faa-parent.animated-hover:hover>.faa-horizontal{-webkit-animation:horizontal 2s ease infinite;animation:horizontal 2s ease infinite}.faa-horizontal.animated-hover.faa-fast:hover,.faa-horizontal.animated.faa-fast,.faa-parent.animated-hover:hover>.faa-horizontal.faa-fast{-webkit-animation:horizontal 1s ease infinite;animation:horizontal 1s ease infinite}.faa-horizontal.animated-hover.faa-slow:hover,.faa-horizontal.animated.faa-slow,.faa-parent.animated-hover:hover>.faa-horizontal.faa-slow{-webkit-animation:horizontal 3s ease infinite;animation:horizontal 3s ease infinite}@-webkit-keyframes flash{0%,100%,50%{opacity:1}25%,75%{opacity:0}}@keyframes flash{0%,100%,50%{opacity:1}25%,75%{opacity:0}}.faa-flash.animated,.faa-flash.animated-hover:hover,.faa-parent.animated-hover:hover>.faa-flash{-webkit-animation:flash 2s ease infinite;animation:flash 2s ease infinite}.faa-flash.animated-hover.faa-fast:hover,.faa-flash.animated.faa-fast,.faa-parent.animated-hover:hover>.faa-flash.faa-fast{-webkit-animation:flash 1s ease infinite;animation:flash 1s ease infinite}.faa-flash.animated-hover.faa-slow:hover,.faa-flash.animated.faa-slow,.faa-parent.animated-hover:hover>.faa-flash.faa-slow{-webkit-animation:flash 3s ease infinite;animation:flash 3s ease infinite}@-webkit-keyframes bounce{0%,10%,100%,20%,50%,80%{-webkit-transform:translateY(0);transform:translateY(0)}40%,60%{-webkit-transform:translateY(-15px);transform:translateY(-15px)}}@keyframes bounce{0%,10%,100%,20%,50%,80%{-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}40%,60%{-webkit-transform:translateY(-15px);-ms-transform:translateY(-15px);transform:translateY(-15px)}}.faa-bounce.animated,.faa-bounce.animated-hover:hover,.faa-parent.animated-hover:hover>.faa-bounce{-webkit-animation:bounce 2s ease infinite;animation:bounce 2s ease infinite}.faa-bounce.animated-hover.faa-fast:hover,.faa-bounce.animated.faa-fast,.faa-parent.animated-hover:hover>.faa-bounce.faa-fast{-webkit-animation:bounce 1s ease infinite;animation:bounce 1s ease infinite}.faa-bounce.animated-hover.faa-slow:hover,.faa-bounce.animated.faa-slow,.faa-parent.animated-hover:hover>.faa-bounce.faa-slow{-webkit-animation:bounce 3s ease infinite;animation:bounce 3s ease infinite}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes spin{0%{-webkit-transform:rotate(0deg);-ms-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);-ms-transform:rotate(359deg);transform:rotate(359deg)}}.faa-parent.animated-hover:hover>.faa-spin,.faa-spin.animated,.faa-spin.animated-hover:hover{-webkit-animation:spin 1.5s linear infinite;animation:spin 1.5s linear infinite}.faa-parent.animated-hover:hover>.faa-spin.faa-fast,.faa-spin.animated-hover.faa-fast:hover,.faa-spin.animated.faa-fast{-webkit-animation:spin .7s linear infinite;animation:spin .7s linear infinite}.faa-parent.animated-hover:hover>.faa-spin.faa-slow,.faa-spin.animated-hover.faa-slow:hover,.faa-spin.animated.faa-slow{-webkit-animation:spin 2.2s linear infinite;animation:spin 2.2s linear infinite}@-webkit-keyframes float{0%{-webkit-transform:translateY(0);transform:translateY(0)}50%{-webkit-transform:translateY(-6px);transform:translateY(-6px)}100%{-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes float{0%{-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}50%{-webkit-transform:translateY(-6px);-ms-transform:translateY(-6px);transform:translateY(-6px)}100%{-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}.faa-float.animated,.faa-float.animated-hover:hover,.faa-parent.animated-hover:hover>.faa-float{-webkit-animation:float 2s linear infinite;animation:float 2s linear infinite}.faa-float.animated-hover.faa-fast:hover,.faa-float.animated.faa-fast,.faa-parent.animated-hover:hover>.faa-float.faa-fast{-webkit-animation:float 1s linear infinite;animation:float 1s linear infinite}.faa-float.animated-hover.faa-slow:hover,.faa-float.animated.faa-slow,.faa-parent.animated-hover:hover>.faa-float.faa-slow{-webkit-animation:float 3s linear infinite;animation:float 3s linear infinite}@-webkit-keyframes pulse{0%{-webkit-transform:scale(1.1);transform:scale(1.1)}50%{-webkit-transform:scale(0.8);transform:scale(0.8)}100%{-webkit-transform:scale(1.1);transform:scale(1.1)}}@keyframes pulse{0%{-webkit-transform:scale(1.1);-ms-transform:scale(1.1);transform:scale(1.1)}50%{-webkit-transform:scale(0.8);-ms-transform:scale(0.8);transform:scale(0.8)}100%{-webkit-transform:scale(1.1);-ms-transform:scale(1.1);transform:scale(1.1)}}.faa-parent.animated-hover:hover>.faa-pulse,.faa-pulse.animated,.faa-pulse.animated-hover:hover{-webkit-animation:pulse 2s linear infinite;animation:pulse 2s linear infinite}.faa-parent.animated-hover:hover>.faa-pulse.faa-fast,.faa-pulse.animated-hover.faa-fast:hover,.faa-pulse.animated.faa-fast{-webkit-animation:pulse 1s linear infinite;animation:pulse 1s linear infinite}.faa-parent.animated-hover:hover>.faa-pulse.faa-slow,.faa-pulse.animated-hover.faa-slow:hover,.faa-pulse.animated.faa-slow{-webkit-animation:pulse 3s linear infinite;animation:pulse 3s linear infinite}.faa-parent.animated-hover:hover>.faa-shake,.faa-shake.animated,.faa-shake.animated-hover:hover{-webkit-animation:wrench 2.5s ease infinite;animation:wrench 2.5s ease infinite}.faa-parent.animated-hover:hover>.faa-shake.faa-fast,.faa-shake.animated-hover.faa-fast:hover,.faa-shake.animated.faa-fast{-webkit-animation:wrench 1.2s ease infinite;animation:wrench 1.2s ease infinite}.faa-parent.animated-hover:hover>.faa-shake.faa-slow,.faa-shake.animated-hover.faa-slow:hover,.faa-shake.animated.faa-slow{-webkit-animation:wrench 3.7s ease infinite;animation:wrench 3.7s ease infinite}@-webkit-keyframes tada{0%{-webkit-transform:scale(1);transform:scale(1)}10%,20%{-webkit-transform:scale(.9) rotate(-8deg);transform:scale(.9) rotate(-8deg)}30%,50%,70%{-webkit-transform:scale(1.3) rotate(8deg);transform:scale(1.3) rotate(8deg)}40%,60%{-webkit-transform:scale(1.3) rotate(-8deg);transform:scale(1.3) rotate(-8deg)}100%,80%{-webkit-transform:scale(1) rotate(0);transform:scale(1) rotate(0)}}@keyframes tada{0%{-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}10%,20%{-webkit-transform:scale(.9) rotate(-8deg);-ms-transform:scale(.9) rotate(-8deg);transform:scale(.9) rotate(-8deg)}30%,50%,70%{-webkit-transform:scale(1.3) rotate(8deg);-ms-transform:scale(1.3) rotate(8deg);transform:scale(1.3) rotate(8deg)}40%,60%{-webkit-transform:scale(1.3) rotate(-8deg);-ms-transform:scale(1.3) rotate(-8deg);transform:scale(1.3) rotate(-8deg)}100%,80%{-webkit-transform:scale(1) rotate(0);-ms-transform:scale(1) rotate(0);transform:scale(1) rotate(0)}}.faa-parent.animated-hover:hover>.faa-tada,.faa-tada.animated,.faa-tada.animated-hover:hover{-webkit-animation:tada 2s linear infinite;animation:tada 2s linear infinite}.faa-parent.animated-hover:hover>.faa-tada.faa-fast,.faa-tada.animated-hover.faa-fast:hover,.faa-tada.animated.faa-fast{-webkit-animation:tada 1s linear infinite;animation:tada 1s linear infinite}.faa-parent.animated-hover:hover>.faa-tada.faa-slow,.faa-tada.animated-hover.faa-slow:hover,.faa-tada.animated.faa-slow{-webkit-animation:tada 3s linear infinite;animation:tada 3s linear infinite}@-webkit-keyframes passing{0%{-webkit-transform:translateX(-50%);transform:translateX(-50%);opacity:0}50%{-webkit-transform:translateX(0%);transform:translateX(0%);opacity:1}100%{-webkit-transform:translateX(50%);transform:translateX(50%);opacity:0}}@keyframes passing{0%{-webkit-transform:translateX(-50%);-ms-transform:translateX(-50%);transform:translateX(-50%);opacity:0}50%{-webkit-transform:translateX(0%);-ms-transform:translateX(0%);transform:translateX(0%);opacity:1}100%{-webkit-transform:translateX(50%);-ms-transform:translateX(50%);transform:translateX(50%);opacity:0}}.faa-parent.animated-hover:hover>.faa-passing,.faa-passing.animated,.faa-passing.animated-hover:hover{-webkit-animation:passing 2s linear infinite;animation:passing 2s linear infinite}.faa-parent.animated-hover:hover>.faa-passing.faa-fast,.faa-passing.animated-hover.faa-fast:hover,.faa-passing.animated.faa-fast{-webkit-animation:passing 1s linear infinite;animation:passing 1s linear infinite}.faa-parent.animated-hover:hover>.faa-passing.faa-slow,.faa-passing.animated-hover.faa-slow:hover,.faa-passing.animated.faa-slow{-webkit-animation:passing 3s linear infinite;animation:passing 3s linear infinite}@-webkit-keyframes passing-reverse{0%{-webkit-transform:translateX(50%);transform:translateX(50%);opacity:0}50%{-webkit-transform:translateX(0%);transform:translateX(0%);opacity:1}100%{-webkit-transform:translateX(-50%);transform:translateX(-50%);opacity:0}}@keyframes passing-reverse{0%{-webkit-transform:translateX(50%);-ms-transform:translateX(50%);transform:translateX(50%);opacity:0}50%{-webkit-transform:translateX(0%);-ms-transform:translateX(0%);transform:translateX(0%);opacity:1}100%{-webkit-transform:translateX(-50%);-ms-transform:translateX(-50%);transform:translateX(-50%);opacity:0}}.faa-parent.animated-hover:hover>.faa-passing-reverse,.faa-passing-reverse.animated,.faa-passing-reverse.animated-hover:hover{-webkit-animation:passing-reverse 2s linear infinite;animation:passing-reverse 2s linear infinite}.faa-parent.animated-hover:hover>.faa-passing-reverse.faa-fast,.faa-passing-reverse.animated-hover.faa-fast:hover,.faa-passing-reverse.animated.faa-fast{-webkit-animation:passing-reverse 1s linear infinite;animation:passing-reverse 1s linear infinite}.faa-parent.animated-hover:hover>.faa-passing-reverse.faa-slow,.faa-passing-reverse.animated-hover.faa-slow:hover,.faa-passing-reverse.animated.faa-slow{-webkit-animation:passing-reverse 3s linear infinite;animation:passing-reverse 3s linear infinite}@-webkit-keyframes burst{0%{opacity:.6}50%{-webkit-transform:scale(1.8);transform:scale(1.8);opacity:0}100%{opacity:0}}@keyframes burst{0%{opacity:.6}50%{-webkit-transform:scale(1.8);-ms-transform:scale(1.8);transform:scale(1.8);opacity:0}100%{opacity:0}}.faa-burst.animated,.faa-burst.animated-hover:hover,.faa-parent.animated-hover:hover>.faa-burst{-webkit-animation:burst 2s infinite linear;animation:burst 2s infinite linear}.faa-burst.animated-hover.faa-fast:hover,.faa-burst.animated.faa-fast,.faa-parent.animated-hover:hover>.faa-burst.faa-fast{-webkit-animation:burst 1s infinite linear;animation:burst 1s infinite linear}.faa-burst.animated-hover.faa-slow:hover,.faa-burst.animated.faa-slow,.faa-parent.animated-hover:hover>.faa-burst.faa-slow{-webkit-animation:burst 3s infinite linear;animation:burst 3s infinite linear} diff --git a/public/css/vendor/fontawesome/font-awesome.min.css b/public/css/vendor/fontawesome/font-awesome.min.css deleted file mode 100755 index acce2ef23..000000000 --- a/public/css/vendor/fontawesome/font-awesome.min.css +++ /dev/null @@ -1,4 +0,0 @@ -/*! - * Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:'FontAwesome';src:url('fonts/fontawesome-webfont.eot?v=4.5.0');src:url('fonts/fontawesome-webfont.eot?#iefix&v=4.5.0') format('embedded-opentype'),url('fonts/fontawesome-webfont.woff2?v=4.5.0') format('woff2'),url('fonts/fontawesome-webfont.woff?v=4.5.0') format('woff'),url('fonts/fontawesome-webfont.ttf?v=4.5.0') format('truetype'),url('fonts/fontawesome-webfont.svg?v=4.5.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"} diff --git a/public/css/vendor/fontawesome/fonts/FontAwesome.otf b/public/css/vendor/fontawesome/fonts/FontAwesome.otf deleted file mode 100755 index 3ed7f8b48..000000000 Binary files a/public/css/vendor/fontawesome/fonts/FontAwesome.otf and /dev/null differ diff --git a/public/css/vendor/fontawesome/fonts/fontawesome-webfont.eot b/public/css/vendor/fontawesome/fonts/fontawesome-webfont.eot deleted file mode 100755 index 9b6afaedc..000000000 Binary files a/public/css/vendor/fontawesome/fonts/fontawesome-webfont.eot and /dev/null differ diff --git a/public/css/vendor/fontawesome/fonts/fontawesome-webfont.svg b/public/css/vendor/fontawesome/fonts/fontawesome-webfont.svg deleted file mode 100755 index d05688e9e..000000000 --- a/public/css/vendor/fontawesome/fonts/fontawesome-webfont.svg +++ /dev/null @@ -1,655 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/public/css/vendor/fontawesome/fonts/fontawesome-webfont.ttf b/public/css/vendor/fontawesome/fonts/fontawesome-webfont.ttf deleted file mode 100755 index 26dea7951..000000000 Binary files a/public/css/vendor/fontawesome/fonts/fontawesome-webfont.ttf and /dev/null differ diff --git a/public/css/vendor/fontawesome/fonts/fontawesome-webfont.woff b/public/css/vendor/fontawesome/fonts/fontawesome-webfont.woff deleted file mode 100755 index dc35ce3c2..000000000 Binary files a/public/css/vendor/fontawesome/fonts/fontawesome-webfont.woff and /dev/null differ diff --git a/public/css/vendor/fontawesome/fonts/fontawesome-webfont.woff2 b/public/css/vendor/fontawesome/fonts/fontawesome-webfont.woff2 deleted file mode 100755 index 500e51725..000000000 Binary files a/public/css/vendor/fontawesome/fonts/fontawesome-webfont.woff2 and /dev/null differ diff --git a/public/favicons/android-chrome-192x192.png b/public/favicons/android-chrome-192x192.png new file mode 100644 index 000000000..aed30b263 Binary files /dev/null and b/public/favicons/android-chrome-192x192.png differ diff --git a/public/favicons/android-chrome-512x512.png b/public/favicons/android-chrome-512x512.png new file mode 100644 index 000000000..5ca796ca7 Binary files /dev/null and b/public/favicons/android-chrome-512x512.png differ diff --git a/public/favicons/apple-touch-icon.png b/public/favicons/apple-touch-icon.png new file mode 100644 index 000000000..5ec6cfcc2 Binary files /dev/null and b/public/favicons/apple-touch-icon.png differ diff --git a/public/favicons/browserconfig.xml b/public/favicons/browserconfig.xml new file mode 100644 index 000000000..e3cb776ee --- /dev/null +++ b/public/favicons/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #165ed4 + + + diff --git a/public/favicons/favicon-16x16.png b/public/favicons/favicon-16x16.png new file mode 100644 index 000000000..d568bd20a Binary files /dev/null and b/public/favicons/favicon-16x16.png differ diff --git a/public/favicons/favicon-32x32.png b/public/favicons/favicon-32x32.png new file mode 100644 index 000000000..edfd13405 Binary files /dev/null and b/public/favicons/favicon-32x32.png differ diff --git a/public/favicons/favicon.ico b/public/favicons/favicon.ico new file mode 100644 index 000000000..99e5bffe2 Binary files /dev/null and b/public/favicons/favicon.ico differ diff --git a/public/favicons/manifest.json b/public/favicons/manifest.json new file mode 100644 index 000000000..796d1ff0c --- /dev/null +++ b/public/favicons/manifest.json @@ -0,0 +1,18 @@ +{ + "name": "", + "icons": [ + { + "src": "favicons/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "favicons/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} \ No newline at end of file diff --git a/public/favicons/mstile-150x150.png b/public/favicons/mstile-150x150.png new file mode 100644 index 000000000..9a8fa880b Binary files /dev/null and b/public/favicons/mstile-150x150.png differ diff --git a/public/favicons/safari-pinned-tab.svg b/public/favicons/safari-pinned-tab.svg new file mode 100644 index 000000000..fd23e1d96 --- /dev/null +++ b/public/favicons/safari-pinned-tab.svg @@ -0,0 +1,475 @@ + + + + +Created by potrace 1.11, written by Peter Selinger 2001-2013 + + + + + diff --git a/public/js/admin.min.js b/public/js/admin.min.js deleted file mode 100755 index b012c35d0..000000000 --- a/public/js/admin.min.js +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Pterodactyl - Panel - * Copyright (c) 2015 - 2017 Dane Everitt - * - * 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. - */ -function randomKey(length) { - var text = ''; - var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - - for( var i=0; i < length; i++ ) { - text += possible.charAt(Math.floor(Math.random() * possible.length)); - } - - return text; -} -function escapeRegExp(str) { - return str.replace(/^\/|\/$/g, ''); -} -$(document).ready(function () { - $.urlParam=function(name){var results=new RegExp("[\\?&]"+name+"=([^&#]*)").exec(decodeURIComponent(window.location.href));if(results==null){return null}else{return results[1]||0}};function getPageName(url){var index=url.lastIndexOf("/")+1;var filenameWithExtension=url.substr(index);var filename=filenameWithExtension.split(".")[0];return filename} - function centerModal(element) { - var modal = (element.length > 0) ? element : $('.modal:visible'); - var clone = modal.clone().css('display', 'block').appendTo('body'); - var top = Math.round((clone.height() - clone.find('.modal-content').height()) / 3); - clone.remove(); - modal.find('div.modal-content').css('margin-top', top); - } - $('body').on('show.bs.modal', '.modal', function() { - centerModal($(this)); - }); - $(window).on('resize', centerModal); - - // Idea code for multiplicators submitted by @Taronyuu on Github - // https://github.com/Pterodactyl/Panel/issues/154#issuecomment-257116078 - $('input[data-multiplicator="true"]').on('change', function () { - var value = $(this).val(); - if (!/^\d+$/.test(value)) { - var multiplicator = value.replace(/[0-9]/g, '').toLowerCase(); - value = value.replace(/\D/g, ''); - - if (multiplicator === 't') { - value = value * (1024 * 1024); - } - - if (multiplicator === 'g') { - value = value * 1024; - } - } - - $(this).val(value); - }); -}); diff --git a/public/js/filemanager.min.js b/public/js/filemanager.min.js deleted file mode 100644 index 64a74e92d..000000000 --- a/public/js/filemanager.min.js +++ /dev/null @@ -1,5 +0,0 @@ -'use strict';var _createClass=function(){function defineProperties(target,props){for(var i=0;i\n \n ';nameBlock.html(attachEditor);var inputField=nameBlock.find('input');var inputLoader=nameBlock.find('.input-loader');inputField.focus();inputField.on('blur keydown',function(e){if(e.type==='keydown'&&e.which===27||e.type==='blur'||e.type==='keydown'&&e.which===13&¤tName===inputField.val()){if(!_.isEmpty(currentLink)){nameBlock.html(currentLink)}else{nameBlock.html(currentName)}inputField.remove();ContextMenu.unbind().run();return}if(e.type==='keydown'&&e.which!==13)return;inputLoader.show();var currentPath=decodeURIComponent(nameBlock.data('path'));$.ajax({type:'POST',headers:{'X-Access-Token':Pterodactyl.server.daemonSecret,'X-Access-Server':Pterodactyl.server.uuid},contentType:'application/json; charset=utf-8',url:Pterodactyl.node.scheme+'://'+Pterodactyl.node.fqdn+':'+Pterodactyl.node.daemonListen+'/server/file/rename',timeout:10000,data:JSON.stringify({from:''+currentPath+currentName,to:''+currentPath+inputField.val()})}).done(function(data){nameBlock.attr('data-name',inputField.val());if(!_.isEmpty(currentLink)){var newLink=currentLink.attr('href');if(nameBlock.parent().data('type')!=='folder'){newLink=newLink.substr(0,newLink.lastIndexOf('/'))+'/'+inputField.val()}currentLink.attr('href',newLink);nameBlock.html(currentLink.html(inputField.val()))}else{nameBlock.html(inputField.val())}inputField.remove()}).fail(function(jqXHR){console.error(jqXHR);var error='An error occured while trying to process this request.';if(typeof jqXHR.responseJSON!=='undefined'&&typeof jqXHR.responseJSON.error!=='undefined'){error=jqXHR.responseJSON.error}nameBlock.addClass('has-error').delay(2000).queue(function(){nameBlock.removeClass('has-error').dequeue()});inputField.popover({animation:true,placement:'top',content:error,title:'Save Error'}).popover('show')}).always(function(){inputLoader.remove();ContextMenu.unbind().run()})})}},{key:'copy',value:function copy(){var nameBlock=$(this.element).find('td[data-identifier="name"]');var currentName=decodeURIComponent(nameBlock.attr('data-name'));var currentPath=decodeURIComponent(nameBlock.data('path'));swal({type:'input',title:'Copy File',text:'Please enter the new path for the copied file below.',showCancelButton:true,showConfirmButton:true,closeOnConfirm:false,showLoaderOnConfirm:true,inputValue:''+currentPath+currentName},function(val){$.ajax({type:'POST',headers:{'X-Access-Token':Pterodactyl.server.daemonSecret,'X-Access-Server':Pterodactyl.server.uuid},contentType:'application/json; charset=utf-8',url:Pterodactyl.node.scheme+'://'+Pterodactyl.node.fqdn+':'+Pterodactyl.node.daemonListen+'/server/file/copy',timeout:10000,data:JSON.stringify({from:''+currentPath+currentName,to:''+val})}).done(function(data){swal({type:'success',title:'',text:'File successfully copied.'});Files.list()}).fail(function(jqXHR){console.error(jqXHR);var error='An error occured while trying to process this request.';if(typeof jqXHR.responseJSON!=='undefined'&&typeof jqXHR.responseJSON.error!=='undefined'){error=jqXHR.responseJSON.error}swal({type:'error',title:'',text:error})})})}},{key:'download',value:function download(){var nameBlock=$(this.element).find('td[data-identifier="name"]');var fileName=decodeURIComponent(nameBlock.attr('data-name'));var filePath=decodeURIComponent(nameBlock.data('path'));window.location='/server/'+Pterodactyl.server.uuidShort+'/files/download/'+filePath+fileName}},{key:'delete',value:function _delete(){var nameBlock=$(this.element).find('td[data-identifier="name"]');var delPath=decodeURIComponent(nameBlock.data('path'));var delName=decodeURIComponent(nameBlock.data('name'));swal({type:'warning',title:'',text:'Are you sure you want to delete '+delName+'? There is no reversing this action.',html:true,showCancelButton:true,showConfirmButton:true,closeOnConfirm:false,showLoaderOnConfirm:true},function(){$.ajax({type:'DELETE',url:Pterodactyl.node.scheme+'://'+Pterodactyl.node.fqdn+':'+Pterodactyl.node.daemonListen+'/server/file/f/'+delPath+delName,headers:{'X-Access-Token':Pterodactyl.server.daemonSecret,'X-Access-Server':Pterodactyl.server.uuid}}).done(function(data){nameBlock.parent().addClass('warning').delay(200).fadeOut();swal({type:'success',title:'File Deleted'})}).fail(function(jqXHR){console.error(jqXHR);swal({type:'error',title:'Whoops!',html:true,text:'An error occured while attempting to delete this file. Please try again.'})})})}},{key:'decompress',value:function decompress(){var nameBlock=$(this.element).find('td[data-identifier="name"]');var compPath=decodeURIComponent(nameBlock.data('path'));var compName=decodeURIComponent(nameBlock.data('name'));swal({title:' Decompressing...',text:'This might take a few seconds to complete.',html:true,allowOutsideClick:false,allowEscapeKey:false,showConfirmButton:false});$.ajax({type:'POST',url:Pterodactyl.node.scheme+'://'+Pterodactyl.node.fqdn+':'+Pterodactyl.node.daemonListen+'/server/file/decompress',headers:{'X-Access-Token':Pterodactyl.server.daemonSecret,'X-Access-Server':Pterodactyl.server.uuid},contentType:'application/json; charset=utf-8',data:JSON.stringify({files:''+compPath+compName})}).done(function(data){swal.close();Files.list(compPath)}).fail(function(jqXHR){console.error(jqXHR);var error='An error occured while trying to process this request.';if(typeof jqXHR.responseJSON!=='undefined'&&typeof jqXHR.responseJSON.error!=='undefined'){error=jqXHR.responseJSON.error}swal({type:'error',title:'Whoops!',html:true,text:error})})}},{key:'compress',value:function compress(){var nameBlock=$(this.element).find('td[data-identifier="name"]');var compPath=decodeURIComponent(nameBlock.data('path'));var compName=decodeURIComponent(nameBlock.data('name'));$.ajax({type:'POST',url:Pterodactyl.node.scheme+'://'+Pterodactyl.node.fqdn+':'+Pterodactyl.node.daemonListen+'/server/file/compress',headers:{'X-Access-Token':Pterodactyl.server.daemonSecret,'X-Access-Server':Pterodactyl.server.uuid},contentType:'application/json; charset=utf-8',data:JSON.stringify({files:''+compPath+compName,to:compPath.toString()})}).done(function(data){Files.list(compPath,function(err){if(err)return;var fileListing=$('#file_listing').find('[data-name="'+data.saved_as+'"]').parent();fileListing.addClass('success pulsate').delay(3000).queue(function(){fileListing.removeClass('success pulsate').dequeue()})})}).fail(function(jqXHR){console.error(jqXHR);var error='An error occured while trying to process this request.';if(typeof jqXHR.responseJSON!=='undefined'&&typeof jqXHR.responseJSON.error!=='undefined'){error=jqXHR.responseJSON.error}swal({type:'error',title:'Whoops!',html:true,text:error})})}}]);return ActionsClass}(); -'use strict';var _createClass=function(){function defineProperties(target,props){for(var i=0;i New File
  • New Folder
  • '}if(Pterodactyl.permissions.downloadFiles||Pterodactyl.permissions.deleteFiles){buildMenu+='
  • '}if(Pterodactyl.permissions.downloadFiles){buildMenu+=''}if(Pterodactyl.permissions.deleteFiles){buildMenu+='
  • Delete
  • '}buildMenu+='';return buildMenu}},{key:'rightClick',value:function rightClick(){var _this=this;$('[data-action="toggleMenu"]').on('mousedown',function(){event.preventDefault();_this.showMenu(event)});$('#file_listing > tbody td').on('contextmenu',function(event){_this.showMenu(event)})}},{key:'showMenu',value:function showMenu(event){var _this2=this;var parent=$(event.target).closest('tr');var menu=$(this.makeMenu(parent));if(parent.data('type')==='disabled')return;event.preventDefault();$(menu).appendTo('body');$(menu).data('invokedOn',$(event.target)).show().css({position:'absolute',left:event.pageX-150,top:event.pageY});this.activeLine=parent;this.activeLine.addClass('active');var Actions=new ActionsClass(parent,menu);if(Pterodactyl.permissions.moveFiles){$(menu).find('li[data-action="move"]').unbind().on('click',function(e){e.preventDefault();Actions.move()});$(menu).find('li[data-action="rename"]').unbind().on('click',function(e){e.preventDefault();Actions.rename()})}if(Pterodactyl.permissions.copyFiles){$(menu).find('li[data-action="copy"]').unbind().on('click',function(e){e.preventDefault();Actions.copy()})}if(Pterodactyl.permissions.compressFiles){if(parent.data('type')==='folder'){$(menu).find('li[data-action="compress"]').removeClass('hidden')}$(menu).find('li[data-action="compress"]').unbind().on('click',function(e){e.preventDefault();Actions.compress()})}if(Pterodactyl.permissions.decompressFiles){if(_.without(['application/zip','application/gzip','application/x-gzip'],parent.data('mime')).length<3){$(menu).find('li[data-action="decompress"]').removeClass('hidden')}$(menu).find('li[data-action="decompress"]').unbind().on('click',function(e){e.preventDefault();Actions.decompress()})}if(Pterodactyl.permissions.createFiles){$(menu).find('li[data-action="folder"]').unbind().on('click',function(e){e.preventDefault();Actions.folder()})}if(Pterodactyl.permissions.downloadFiles){if(parent.data('type')==='file'){$(menu).find('li[data-action="download"]').removeClass('hidden')}$(menu).find('li[data-action="download"]').unbind().on('click',function(e){e.preventDefault();Actions.download()})}if(Pterodactyl.permissions.deleteFiles){$(menu).find('li[data-action="delete"]').unbind().on('click',function(e){e.preventDefault();Actions.delete()})}$(window).on('click',function(){$(menu).remove();if(!_.isNull(_this2.activeLine))_this2.activeLine.removeClass('active')})}},{key:'directoryClick',value:function directoryClick(){$('a[data-action="directory-view"]').on('click',function(event){event.preventDefault();var path=$(this).parent().data('path')||'';var name=$(this).parent().data('name')||'';window.location.hash=encodeURIComponent(path+name);Files.list()})}}]);return ContextMenuClass}();window.ContextMenu=new ContextMenuClass; -'use strict';var _createClass=function(){function defineProperties(target,props){for(var i=0;i\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\nclass ActionsClass {\n constructor(element, menu) {\n this.element = element;\n this.menu = menu;\n }\n\n destroy() {\n this.element = undefined;\n }\n\n folder() {\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\n const currentName = decodeURIComponent(nameBlock.attr('data-name'));\n const currentPath = decodeURIComponent(nameBlock.data('path'));\n\n let inputValue = `${currentPath}${currentName}/`;\n if ($(this.element).data('type') === 'file') {\n inputValue = currentPath;\n }\n swal({\n type: 'input',\n title: 'Create Folder',\n text: 'Please enter the path and folder name below.',\n showCancelButton: true,\n showConfirmButton: true,\n closeOnConfirm: false,\n showLoaderOnConfirm: true,\n inputValue: inputValue\n }, (val) => {\n $.ajax({\n type: 'POST',\n headers: {\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\n 'X-Access-Server': Pterodactyl.server.uuid,\n },\n contentType: 'application/json; charset=utf-8',\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/server/file/folder`,\n timeout: 10000,\n data: JSON.stringify({\n path: val,\n }),\n }).done(data => {\n swal.close();\n Files.list();\n }).fail(jqXHR => {\n console.error(jqXHR);\n var error = 'An error occured while trying to process this request.';\n if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {\n error = jqXHR.responseJSON.error;\n }\n swal({\n type: 'error',\n title: '',\n text: error,\n });\n });\n });\n }\n\n move() {\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\n const currentName = decodeURIComponent(nameBlock.attr('data-name'));\n const currentPath = decodeURIComponent(nameBlock.data('path'));\n\n swal({\n type: 'input',\n title: 'Move File',\n text: 'Please enter the new path for the file below.',\n showCancelButton: true,\n showConfirmButton: true,\n closeOnConfirm: false,\n showLoaderOnConfirm: true,\n inputValue: `${currentPath}${currentName}`,\n }, (val) => {\n $.ajax({\n type: 'POST',\n headers: {\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\n 'X-Access-Server': Pterodactyl.server.uuid,\n },\n contentType: 'application/json; charset=utf-8',\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/server/file/move`,\n timeout: 10000,\n data: JSON.stringify({\n from: `${currentPath}${currentName}`,\n to: `${val}`,\n }),\n }).done(data => {\n nameBlock.parent().addClass('warning').delay(200).fadeOut();\n swal.close();\n }).fail(jqXHR => {\n console.error(jqXHR);\n var error = 'An error occured while trying to process this request.';\n if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {\n error = jqXHR.responseJSON.error;\n }\n swal({\n type: 'error',\n title: '',\n text: error,\n });\n });\n });\n\n }\n\n rename() {\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\n const currentLink = nameBlock.find('a');\n const currentName = decodeURIComponent(nameBlock.attr('data-name'));\n const attachEditor = `\n \n \n `;\n\n nameBlock.html(attachEditor);\n const inputField = nameBlock.find('input');\n const inputLoader = nameBlock.find('.input-loader');\n\n inputField.focus();\n inputField.on('blur keydown', e => {\n // Save Field\n if (\n (e.type === 'keydown' && e.which === 27)\n || e.type === 'blur'\n || (e.type === 'keydown' && e.which === 13 && currentName === inputField.val())\n ) {\n if (!_.isEmpty(currentLink)) {\n nameBlock.html(currentLink);\n } else {\n nameBlock.html(currentName);\n }\n inputField.remove();\n ContextMenu.unbind().run();\n return;\n }\n\n if (e.type === 'keydown' && e.which !== 13) return;\n\n inputLoader.show();\n const currentPath = decodeURIComponent(nameBlock.data('path'));\n\n $.ajax({\n type: 'POST',\n headers: {\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\n 'X-Access-Server': Pterodactyl.server.uuid,\n },\n contentType: 'application/json; charset=utf-8',\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/server/file/rename`,\n timeout: 10000,\n data: JSON.stringify({\n from: `${currentPath}${currentName}`,\n to: `${currentPath}${inputField.val()}`,\n }),\n }).done(data => {\n nameBlock.attr('data-name', inputField.val());\n if (!_.isEmpty(currentLink)) {\n let newLink = currentLink.attr('href');\n if (nameBlock.parent().data('type') !== 'folder') {\n newLink = newLink.substr(0, newLink.lastIndexOf('/')) + '/' + inputField.val();\n }\n currentLink.attr('href', newLink);\n nameBlock.html(\n currentLink.html(inputField.val())\n );\n } else {\n nameBlock.html(inputField.val());\n }\n inputField.remove();\n }).fail(jqXHR => {\n console.error(jqXHR);\n var error = 'An error occured while trying to process this request.';\n if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {\n error = jqXHR.responseJSON.error;\n }\n nameBlock.addClass('has-error').delay(2000).queue(() => {\n nameBlock.removeClass('has-error').dequeue();\n });\n inputField.popover({\n animation: true,\n placement: 'top',\n content: error,\n title: 'Save Error'\n }).popover('show');\n }).always(() => {\n inputLoader.remove();\n ContextMenu.unbind().run();\n });\n });\n }\n\n copy() {\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\n const currentName = decodeURIComponent(nameBlock.attr('data-name'));\n const currentPath = decodeURIComponent(nameBlock.data('path'));\n\n swal({\n type: 'input',\n title: 'Copy File',\n text: 'Please enter the new path for the copied file below.',\n showCancelButton: true,\n showConfirmButton: true,\n closeOnConfirm: false,\n showLoaderOnConfirm: true,\n inputValue: `${currentPath}${currentName}`,\n }, (val) => {\n $.ajax({\n type: 'POST',\n headers: {\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\n 'X-Access-Server': Pterodactyl.server.uuid,\n },\n contentType: 'application/json; charset=utf-8',\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/server/file/copy`,\n timeout: 10000,\n data: JSON.stringify({\n from: `${currentPath}${currentName}`,\n to: `${val}`,\n }),\n }).done(data => {\n swal({\n type: 'success',\n title: '',\n text: 'File successfully copied.'\n });\n Files.list();\n }).fail(jqXHR => {\n console.error(jqXHR);\n var error = 'An error occured while trying to process this request.';\n if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {\n error = jqXHR.responseJSON.error;\n }\n swal({\n type: 'error',\n title: '',\n text: error,\n });\n });\n });\n }\n\n download() {\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\n const fileName = decodeURIComponent(nameBlock.attr('data-name'));\n const filePath = decodeURIComponent(nameBlock.data('path'));\n\n window.location = `/server/${Pterodactyl.server.uuidShort}/files/download/${filePath}${fileName}`;\n }\n\n delete() {\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\n const delPath = decodeURIComponent(nameBlock.data('path'));\n const delName = decodeURIComponent(nameBlock.data('name'));\n\n swal({\n type: 'warning',\n title: '',\n text: 'Are you sure you want to delete ' + delName + '? There is no reversing this action.',\n html: true,\n showCancelButton: true,\n showConfirmButton: true,\n closeOnConfirm: false,\n showLoaderOnConfirm: true\n }, () => {\n $.ajax({\n type: 'DELETE',\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/server/file/f/${delPath}${delName}`,\n headers: {\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\n 'X-Access-Server': Pterodactyl.server.uuid,\n }\n }).done(data => {\n nameBlock.parent().addClass('warning').delay(200).fadeOut();\n swal({\n type: 'success',\n title: 'File Deleted'\n });\n }).fail(jqXHR => {\n console.error(jqXHR);\n swal({\n type: 'error',\n title: 'Whoops!',\n html: true,\n text: 'An error occured while attempting to delete this file. Please try again.',\n });\n });\n });\n }\n\n decompress() {\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\n const compPath = decodeURIComponent(nameBlock.data('path'));\n const compName = decodeURIComponent(nameBlock.data('name'));\n\n swal({\n title: ' Decompressing...',\n text: 'This might take a few seconds to complete.',\n html: true,\n allowOutsideClick: false,\n allowEscapeKey: false,\n showConfirmButton: false,\n });\n\n $.ajax({\n type: 'POST',\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/server/file/decompress`,\n headers: {\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\n 'X-Access-Server': Pterodactyl.server.uuid,\n },\n contentType: 'application/json; charset=utf-8',\n data: JSON.stringify({\n files: `${compPath}${compName}`\n })\n }).done(data => {\n swal.close();\n Files.list(compPath);\n }).fail(jqXHR => {\n console.error(jqXHR);\n var error = 'An error occured while trying to process this request.';\n if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {\n error = jqXHR.responseJSON.error;\n }\n swal({\n type: 'error',\n title: 'Whoops!',\n html: true,\n text: error\n });\n });\n }\n\n compress() {\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\n const compPath = decodeURIComponent(nameBlock.data('path'));\n const compName = decodeURIComponent(nameBlock.data('name'));\n\n $.ajax({\n type: 'POST',\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/server/file/compress`,\n headers: {\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\n 'X-Access-Server': Pterodactyl.server.uuid,\n },\n contentType: 'application/json; charset=utf-8',\n data: JSON.stringify({\n files: `${compPath}${compName}`,\n to: compPath.toString()\n })\n }).done(data => {\n Files.list(compPath, err => {\n if (err) return;\n const fileListing = $('#file_listing').find(`[data-name=\"${data.saved_as}\"]`).parent();\n fileListing.addClass('success pulsate').delay(3000).queue(() => {\n fileListing.removeClass('success pulsate').dequeue();\n });\n });\n }).fail(jqXHR => {\n console.error(jqXHR);\n var error = 'An error occured while trying to process this request.';\n if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {\n error = jqXHR.responseJSON.error;\n }\n swal({\n type: 'error',\n title: 'Whoops!',\n html: true,\n text: error\n });\n });\n }\n}\n","\"use strict\";\n\n// Copyright (c) 2015 - 2017 Dane Everitt \n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\nclass ContextMenuClass {\n constructor() {\n this.activeLine = null;\n }\n\n run() {\n this.directoryClick();\n this.rightClick();\n }\n\n makeMenu(parent) {\n $(document).find('#fileOptionMenu').remove();\n if (!_.isNull(this.activeLine)) this.activeLine.removeClass('active');\n\n let newFilePath = $('#headerTableRow').attr('data-currentDir');\n if (parent.data('type') === 'folder') {\n const nameBlock = parent.find('td[data-identifier=\"name\"]');\n const currentName = decodeURIComponent(nameBlock.attr('data-name'));\n const currentPath = decodeURIComponent(nameBlock.data('path'));\n newFilePath = `${currentPath}${currentName}`;\n }\n\n let buildMenu = '
      ';\n\n if (Pterodactyl.permissions.moveFiles) {\n buildMenu += '
    • Rename
    • \\\n
    • Move
    • ';\n }\n\n if (Pterodactyl.permissions.copyFiles) {\n buildMenu += '
    • Copy
    • ';\n }\n\n if (Pterodactyl.permissions.compressFiles) {\n buildMenu += '
    • Compress
    • ';\n }\n\n if (Pterodactyl.permissions.decompressFiles) {\n buildMenu += '
    • Decompress
    • ';\n }\n\n if (Pterodactyl.permissions.createFiles) {\n buildMenu += '
    • \\\n
    • New File
    • \\\n
    • New Folder
    • ';\n }\n\n if (Pterodactyl.permissions.downloadFiles || Pterodactyl.permissions.deleteFiles) {\n buildMenu += '
    • ';\n }\n\n if (Pterodactyl.permissions.downloadFiles) {\n buildMenu += '
    • Download
    • ';\n }\n\n if (Pterodactyl.permissions.deleteFiles) {\n buildMenu += '
    • Delete
    • ';\n }\n\n buildMenu += '
    ';\n return buildMenu;\n }\n\n rightClick() {\n $('[data-action=\"toggleMenu\"]').on('mousedown', () => {\n event.preventDefault();\n this.showMenu(event);\n });\n $('#file_listing > tbody td').on('contextmenu', event => {\n this.showMenu(event);\n });\n }\n\n showMenu(event) {\n const parent = $(event.target).closest('tr');\n const menu = $(this.makeMenu(parent));\n\n if (parent.data('type') === 'disabled') return;\n event.preventDefault();\n\n $(menu).appendTo('body');\n $(menu).data('invokedOn', $(event.target)).show().css({\n position: 'absolute',\n left: event.pageX - 150,\n top: event.pageY,\n });\n\n this.activeLine = parent;\n this.activeLine.addClass('active');\n\n // Handle Events\n const Actions = new ActionsClass(parent, menu);\n if (Pterodactyl.permissions.moveFiles) {\n $(menu).find('li[data-action=\"move\"]').unbind().on('click', e => {\n e.preventDefault();\n Actions.move();\n });\n $(menu).find('li[data-action=\"rename\"]').unbind().on('click', e => {\n e.preventDefault();\n Actions.rename();\n });\n }\n\n if (Pterodactyl.permissions.copyFiles) {\n $(menu).find('li[data-action=\"copy\"]').unbind().on('click', e => {\n e.preventDefault();\n Actions.copy();\n });\n }\n\n if (Pterodactyl.permissions.compressFiles) {\n if (parent.data('type') === 'folder') {\n $(menu).find('li[data-action=\"compress\"]').removeClass('hidden');\n }\n $(menu).find('li[data-action=\"compress\"]').unbind().on('click', e => {\n e.preventDefault();\n Actions.compress();\n });\n }\n\n if (Pterodactyl.permissions.decompressFiles) {\n if (_.without(['application/zip', 'application/gzip', 'application/x-gzip'], parent.data('mime')).length < 3) {\n $(menu).find('li[data-action=\"decompress\"]').removeClass('hidden');\n }\n $(menu).find('li[data-action=\"decompress\"]').unbind().on('click', e => {\n e.preventDefault();\n Actions.decompress();\n });\n }\n\n if (Pterodactyl.permissions.createFiles) {\n $(menu).find('li[data-action=\"folder\"]').unbind().on('click', e => {\n e.preventDefault();\n Actions.folder();\n });\n }\n\n if (Pterodactyl.permissions.downloadFiles) {\n if (parent.data('type') === 'file') {\n $(menu).find('li[data-action=\"download\"]').removeClass('hidden');\n }\n $(menu).find('li[data-action=\"download\"]').unbind().on('click', e => {\n e.preventDefault();\n Actions.download();\n });\n }\n\n if (Pterodactyl.permissions.deleteFiles) {\n $(menu).find('li[data-action=\"delete\"]').unbind().on('click', e => {\n e.preventDefault();\n Actions.delete();\n });\n }\n\n $(window).on('click', () => {\n $(menu).remove();\n if(!_.isNull(this.activeLine)) this.activeLine.removeClass('active');\n });\n }\n\n directoryClick() {\n $('a[data-action=\"directory-view\"]').on('click', function (event) {\n event.preventDefault();\n\n const path = $(this).parent().data('path') || '';\n const name = $(this).parent().data('name') || '';\n\n window.location.hash = encodeURIComponent(path + name);\n Files.list();\n });\n }\n}\n\nwindow.ContextMenu = new ContextMenuClass;\n","\"use strict\";\n\n// Copyright (c) 2015 - 2017 Dane Everitt \n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\nclass FileManager {\n constructor() {\n this.list(this.decodeHash());\n }\n\n list(path, next) {\n if (_.isUndefined(path)) {\n path = this.decodeHash();\n }\n\n this.loader(true);\n $.ajax({\n type: 'POST',\n url: Pterodactyl.meta.directoryList,\n headers: {\n 'X-CSRF-Token': Pterodactyl.meta.csrftoken,\n },\n data: {\n directory: path,\n },\n }).done(data => {\n this.loader(false);\n $('#load_files').slideUp().html(data).slideDown(100, () => {\n ContextMenu.run();\n this.reloadFilesButton();\n if (_.isFunction(next)) {\n return next();\n }\n });\n $('#internal_alert').slideUp();\n }).fail(jqXHR => {\n this.loader(false);\n if (_.isFunction(next)) {\n return next(new Error('Failed to load file listing.'));\n }\n swal({\n type: 'error',\n title: 'File Error',\n text: 'An error occured while attempting to process this request. Please try again.',\n });\n console.log(jqXHR);\n });\n }\n\n loader(show) {\n if ($('#load_files').height() < 5) return;\n\n if (show === true){\n var height = $('#load_files').height();\n var width = $('.ajax_loading_box').width();\n var center_height = (height / 2) - 30;\n var center_width = (width / 2) - 30;\n\n $('#position_me').css({\n 'top': center_height,\n 'left': center_width,\n 'font-size': '60px'\n });\n\n $('.ajax_loading_box').css('height', (height + 5)).show();\n } else {\n $('.ajax_loading_box').hide();\n }\n }\n\n reloadFilesButton() {\n $('i[data-action=\"reload-files\"]').unbind().on('click', () => {\n $('i[data-action=\"reload-files\"]').addClass('fa-spin');\n this.list();\n });\n }\n\n decodeHash() {\n return decodeURIComponent(window.location.hash.substring(1));\n }\n\n}\n\nwindow.Files = new FileManager;\n"]} \ No newline at end of file diff --git a/public/js/files/actions.js b/public/js/files/actions.js deleted file mode 100644 index f286f4c1f..000000000 --- a/public/js/files/actions.js +++ /dev/null @@ -1,394 +0,0 @@ -"use strict"; - -// Copyright (c) 2015 - 2017 Dane Everitt -// -// 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. -class ActionsClass { - constructor(element, menu) { - this.element = element; - this.menu = menu; - } - - destroy() { - this.element = undefined; - } - - folder() { - const nameBlock = $(this.element).find('td[data-identifier="name"]'); - const currentName = decodeURIComponent(nameBlock.attr('data-name')); - const currentPath = decodeURIComponent(nameBlock.data('path')); - - let inputValue = `${currentPath}${currentName}/`; - if ($(this.element).data('type') === 'file') { - inputValue = currentPath; - } - swal({ - type: 'input', - title: 'Create Folder', - text: 'Please enter the path and folder name below.', - showCancelButton: true, - showConfirmButton: true, - closeOnConfirm: false, - showLoaderOnConfirm: true, - inputValue: inputValue - }, (val) => { - $.ajax({ - type: 'POST', - headers: { - 'X-Access-Token': Pterodactyl.server.daemonSecret, - 'X-Access-Server': Pterodactyl.server.uuid, - }, - contentType: 'application/json; charset=utf-8', - url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/server/file/folder`, - timeout: 10000, - data: JSON.stringify({ - path: val, - }), - }).done(data => { - swal.close(); - Files.list(); - }).fail(jqXHR => { - console.error(jqXHR); - var error = 'An error occured while trying to process this request.'; - if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') { - error = jqXHR.responseJSON.error; - } - swal({ - type: 'error', - title: '', - text: error, - }); - }); - }); - } - - move() { - const nameBlock = $(this.element).find('td[data-identifier="name"]'); - const currentName = decodeURIComponent(nameBlock.attr('data-name')); - const currentPath = decodeURIComponent(nameBlock.data('path')); - - swal({ - type: 'input', - title: 'Move File', - text: 'Please enter the new path for the file below.', - showCancelButton: true, - showConfirmButton: true, - closeOnConfirm: false, - showLoaderOnConfirm: true, - inputValue: `${currentPath}${currentName}`, - }, (val) => { - $.ajax({ - type: 'POST', - headers: { - 'X-Access-Token': Pterodactyl.server.daemonSecret, - 'X-Access-Server': Pterodactyl.server.uuid, - }, - contentType: 'application/json; charset=utf-8', - url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/server/file/move`, - timeout: 10000, - data: JSON.stringify({ - from: `${currentPath}${currentName}`, - to: `${val}`, - }), - }).done(data => { - nameBlock.parent().addClass('warning').delay(200).fadeOut(); - swal.close(); - }).fail(jqXHR => { - console.error(jqXHR); - var error = 'An error occured while trying to process this request.'; - if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') { - error = jqXHR.responseJSON.error; - } - swal({ - type: 'error', - title: '', - text: error, - }); - }); - }); - - } - - rename() { - const nameBlock = $(this.element).find('td[data-identifier="name"]'); - const currentLink = nameBlock.find('a'); - const currentName = decodeURIComponent(nameBlock.attr('data-name')); - const attachEditor = ` - - - `; - - nameBlock.html(attachEditor); - const inputField = nameBlock.find('input'); - const inputLoader = nameBlock.find('.input-loader'); - - inputField.focus(); - inputField.on('blur keydown', e => { - // Save Field - if ( - (e.type === 'keydown' && e.which === 27) - || e.type === 'blur' - || (e.type === 'keydown' && e.which === 13 && currentName === inputField.val()) - ) { - if (!_.isEmpty(currentLink)) { - nameBlock.html(currentLink); - } else { - nameBlock.html(currentName); - } - inputField.remove(); - ContextMenu.unbind().run(); - return; - } - - if (e.type === 'keydown' && e.which !== 13) return; - - inputLoader.show(); - const currentPath = decodeURIComponent(nameBlock.data('path')); - - $.ajax({ - type: 'POST', - headers: { - 'X-Access-Token': Pterodactyl.server.daemonSecret, - 'X-Access-Server': Pterodactyl.server.uuid, - }, - contentType: 'application/json; charset=utf-8', - url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/server/file/rename`, - timeout: 10000, - data: JSON.stringify({ - from: `${currentPath}${currentName}`, - to: `${currentPath}${inputField.val()}`, - }), - }).done(data => { - nameBlock.attr('data-name', inputField.val()); - if (!_.isEmpty(currentLink)) { - let newLink = currentLink.attr('href'); - if (nameBlock.parent().data('type') !== 'folder') { - newLink = newLink.substr(0, newLink.lastIndexOf('/')) + '/' + inputField.val(); - } - currentLink.attr('href', newLink); - nameBlock.html( - currentLink.html(inputField.val()) - ); - } else { - nameBlock.html(inputField.val()); - } - inputField.remove(); - }).fail(jqXHR => { - console.error(jqXHR); - var error = 'An error occured while trying to process this request.'; - if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') { - error = jqXHR.responseJSON.error; - } - nameBlock.addClass('has-error').delay(2000).queue(() => { - nameBlock.removeClass('has-error').dequeue(); - }); - inputField.popover({ - animation: true, - placement: 'top', - content: error, - title: 'Save Error' - }).popover('show'); - }).always(() => { - inputLoader.remove(); - ContextMenu.unbind().run(); - }); - }); - } - - copy() { - const nameBlock = $(this.element).find('td[data-identifier="name"]'); - const currentName = decodeURIComponent(nameBlock.attr('data-name')); - const currentPath = decodeURIComponent(nameBlock.data('path')); - - swal({ - type: 'input', - title: 'Copy File', - text: 'Please enter the new path for the copied file below.', - showCancelButton: true, - showConfirmButton: true, - closeOnConfirm: false, - showLoaderOnConfirm: true, - inputValue: `${currentPath}${currentName}`, - }, (val) => { - $.ajax({ - type: 'POST', - headers: { - 'X-Access-Token': Pterodactyl.server.daemonSecret, - 'X-Access-Server': Pterodactyl.server.uuid, - }, - contentType: 'application/json; charset=utf-8', - url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/server/file/copy`, - timeout: 10000, - data: JSON.stringify({ - from: `${currentPath}${currentName}`, - to: `${val}`, - }), - }).done(data => { - swal({ - type: 'success', - title: '', - text: 'File successfully copied.' - }); - Files.list(); - }).fail(jqXHR => { - console.error(jqXHR); - var error = 'An error occured while trying to process this request.'; - if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') { - error = jqXHR.responseJSON.error; - } - swal({ - type: 'error', - title: '', - text: error, - }); - }); - }); - } - - download() { - const nameBlock = $(this.element).find('td[data-identifier="name"]'); - const fileName = decodeURIComponent(nameBlock.attr('data-name')); - const filePath = decodeURIComponent(nameBlock.data('path')); - - window.location = `/server/${Pterodactyl.server.uuidShort}/files/download/${filePath}${fileName}`; - } - - delete() { - const nameBlock = $(this.element).find('td[data-identifier="name"]'); - const delPath = decodeURIComponent(nameBlock.data('path')); - const delName = decodeURIComponent(nameBlock.data('name')); - - swal({ - type: 'warning', - title: '', - text: 'Are you sure you want to delete ' + delName + '? There is no reversing this action.', - html: true, - showCancelButton: true, - showConfirmButton: true, - closeOnConfirm: false, - showLoaderOnConfirm: true - }, () => { - $.ajax({ - type: 'DELETE', - url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/server/file/f/${delPath}${delName}`, - headers: { - 'X-Access-Token': Pterodactyl.server.daemonSecret, - 'X-Access-Server': Pterodactyl.server.uuid, - } - }).done(data => { - nameBlock.parent().addClass('warning').delay(200).fadeOut(); - swal({ - type: 'success', - title: 'File Deleted' - }); - }).fail(jqXHR => { - console.error(jqXHR); - swal({ - type: 'error', - title: 'Whoops!', - html: true, - text: 'An error occured while attempting to delete this file. Please try again.', - }); - }); - }); - } - - decompress() { - const nameBlock = $(this.element).find('td[data-identifier="name"]'); - const compPath = decodeURIComponent(nameBlock.data('path')); - const compName = decodeURIComponent(nameBlock.data('name')); - - swal({ - title: ' Decompressing...', - text: 'This might take a few seconds to complete.', - html: true, - allowOutsideClick: false, - allowEscapeKey: false, - showConfirmButton: false, - }); - - $.ajax({ - type: 'POST', - url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/server/file/decompress`, - headers: { - 'X-Access-Token': Pterodactyl.server.daemonSecret, - 'X-Access-Server': Pterodactyl.server.uuid, - }, - contentType: 'application/json; charset=utf-8', - data: JSON.stringify({ - files: `${compPath}${compName}` - }) - }).done(data => { - swal.close(); - Files.list(compPath); - }).fail(jqXHR => { - console.error(jqXHR); - var error = 'An error occured while trying to process this request.'; - if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') { - error = jqXHR.responseJSON.error; - } - swal({ - type: 'error', - title: 'Whoops!', - html: true, - text: error - }); - }); - } - - compress() { - const nameBlock = $(this.element).find('td[data-identifier="name"]'); - const compPath = decodeURIComponent(nameBlock.data('path')); - const compName = decodeURIComponent(nameBlock.data('name')); - - $.ajax({ - type: 'POST', - url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/server/file/compress`, - headers: { - 'X-Access-Token': Pterodactyl.server.daemonSecret, - 'X-Access-Server': Pterodactyl.server.uuid, - }, - contentType: 'application/json; charset=utf-8', - data: JSON.stringify({ - files: `${compPath}${compName}`, - to: compPath.toString() - }) - }).done(data => { - Files.list(compPath, err => { - if (err) return; - const fileListing = $('#file_listing').find(`[data-name="${data.saved_as}"]`).parent(); - fileListing.addClass('success pulsate').delay(3000).queue(() => { - fileListing.removeClass('success pulsate').dequeue(); - }); - }); - }).fail(jqXHR => { - console.error(jqXHR); - var error = 'An error occured while trying to process this request.'; - if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') { - error = jqXHR.responseJSON.error; - } - swal({ - type: 'error', - title: 'Whoops!', - html: true, - text: error - }); - }); - } -} diff --git a/public/js/files/contextmenu.js b/public/js/files/contextmenu.js deleted file mode 100644 index 7c91ac004..000000000 --- a/public/js/files/contextmenu.js +++ /dev/null @@ -1,195 +0,0 @@ -"use strict"; - -// Copyright (c) 2015 - 2017 Dane Everitt -// -// 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. -class ContextMenuClass { - constructor() { - this.activeLine = null; - } - - run() { - this.directoryClick(); - this.rightClick(); - } - - makeMenu(parent) { - $(document).find('#fileOptionMenu').remove(); - if (!_.isNull(this.activeLine)) this.activeLine.removeClass('active'); - - let newFilePath = $('#headerTableRow').attr('data-currentDir'); - if (parent.data('type') === 'folder') { - const nameBlock = parent.find('td[data-identifier="name"]'); - const currentName = decodeURIComponent(nameBlock.attr('data-name')); - const currentPath = decodeURIComponent(nameBlock.data('path')); - newFilePath = `${currentPath}${currentName}`; - } - - let buildMenu = ''; - return buildMenu; - } - - rightClick() { - $('[data-action="toggleMenu"]').on('mousedown', () => { - event.preventDefault(); - this.showMenu(event); - }); - $('#file_listing > tbody td').on('contextmenu', event => { - this.showMenu(event); - }); - } - - showMenu(event) { - const parent = $(event.target).closest('tr'); - const menu = $(this.makeMenu(parent)); - - if (parent.data('type') === 'disabled') return; - event.preventDefault(); - - $(menu).appendTo('body'); - $(menu).data('invokedOn', $(event.target)).show().css({ - position: 'absolute', - left: event.pageX - 150, - top: event.pageY, - }); - - this.activeLine = parent; - this.activeLine.addClass('active'); - - // Handle Events - const Actions = new ActionsClass(parent, menu); - if (Pterodactyl.permissions.moveFiles) { - $(menu).find('li[data-action="move"]').unbind().on('click', e => { - e.preventDefault(); - Actions.move(); - }); - $(menu).find('li[data-action="rename"]').unbind().on('click', e => { - e.preventDefault(); - Actions.rename(); - }); - } - - if (Pterodactyl.permissions.copyFiles) { - $(menu).find('li[data-action="copy"]').unbind().on('click', e => { - e.preventDefault(); - Actions.copy(); - }); - } - - if (Pterodactyl.permissions.compressFiles) { - if (parent.data('type') === 'folder') { - $(menu).find('li[data-action="compress"]').removeClass('hidden'); - } - $(menu).find('li[data-action="compress"]').unbind().on('click', e => { - e.preventDefault(); - Actions.compress(); - }); - } - - if (Pterodactyl.permissions.decompressFiles) { - if (_.without(['application/zip', 'application/gzip', 'application/x-gzip'], parent.data('mime')).length < 3) { - $(menu).find('li[data-action="decompress"]').removeClass('hidden'); - } - $(menu).find('li[data-action="decompress"]').unbind().on('click', e => { - e.preventDefault(); - Actions.decompress(); - }); - } - - if (Pterodactyl.permissions.createFiles) { - $(menu).find('li[data-action="folder"]').unbind().on('click', e => { - e.preventDefault(); - Actions.folder(); - }); - } - - if (Pterodactyl.permissions.downloadFiles) { - if (parent.data('type') === 'file') { - $(menu).find('li[data-action="download"]').removeClass('hidden'); - } - $(menu).find('li[data-action="download"]').unbind().on('click', e => { - e.preventDefault(); - Actions.download(); - }); - } - - if (Pterodactyl.permissions.deleteFiles) { - $(menu).find('li[data-action="delete"]').unbind().on('click', e => { - e.preventDefault(); - Actions.delete(); - }); - } - - $(window).on('click', () => { - $(menu).remove(); - if(!_.isNull(this.activeLine)) this.activeLine.removeClass('active'); - }); - } - - directoryClick() { - $('a[data-action="directory-view"]').on('click', function (event) { - event.preventDefault(); - - const path = $(this).parent().data('path') || ''; - const name = $(this).parent().data('name') || ''; - - window.location.hash = encodeURIComponent(path + name); - Files.list(); - }); - } -} - -window.ContextMenu = new ContextMenuClass; diff --git a/public/js/files/index.js b/public/js/files/index.js deleted file mode 100644 index f833ab2af..000000000 --- a/public/js/files/index.js +++ /dev/null @@ -1,100 +0,0 @@ -"use strict"; - -// Copyright (c) 2015 - 2017 Dane Everitt -// -// 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. -class FileManager { - constructor() { - this.list(this.decodeHash()); - } - - list(path, next) { - if (_.isUndefined(path)) { - path = this.decodeHash(); - } - - this.loader(true); - $.ajax({ - type: 'POST', - url: Pterodactyl.meta.directoryList, - headers: { - 'X-CSRF-Token': Pterodactyl.meta.csrftoken, - }, - data: { - directory: path, - }, - }).done(data => { - this.loader(false); - $('#load_files').slideUp().html(data).slideDown(100, () => { - ContextMenu.run(); - this.reloadFilesButton(); - if (_.isFunction(next)) { - return next(); - } - }); - $('#internal_alert').slideUp(); - }).fail(jqXHR => { - this.loader(false); - if (_.isFunction(next)) { - return next(new Error('Failed to load file listing.')); - } - swal({ - type: 'error', - title: 'File Error', - text: 'An error occured while attempting to process this request. Please try again.', - }); - console.log(jqXHR); - }); - } - - loader(show) { - if ($('#load_files').height() < 5) return; - - if (show === true){ - var height = $('#load_files').height(); - var width = $('.ajax_loading_box').width(); - var center_height = (height / 2) - 30; - var center_width = (width / 2) - 30; - - $('#position_me').css({ - 'top': center_height, - 'left': center_width, - 'font-size': '60px' - }); - - $('.ajax_loading_box').css('height', (height + 5)).show(); - } else { - $('.ajax_loading_box').hide(); - } - } - - reloadFilesButton() { - $('i[data-action="reload-files"]').unbind().on('click', () => { - $('i[data-action="reload-files"]').addClass('fa-spin'); - this.list(); - }); - } - - decodeHash() { - return decodeURIComponent(window.location.hash.substring(1)); - } - -} - -window.Files = new FileManager; diff --git a/public/js/jquery.terminal-0.11.23.min.js b/public/js/jquery.terminal-0.11.23.min.js deleted file mode 100644 index 0fd1cd1fd..000000000 --- a/public/js/jquery.terminal-0.11.23.min.js +++ /dev/null @@ -1,38 +0,0 @@ -/**@license - * __ _____ ________ __ - * / // _ /__ __ _____ ___ __ _/__ ___/__ ___ ______ __ __ __ ___ / / - * __ / // // // // // _ // _// // / / // _ // _// // // \/ // _ \/ / - * / / // // // // // ___// / / // / / // ___// / / / / // // /\ // // / /__ - * \___//____ \\___//____//_/ _\_ / /_//____//_/ /_/ /_//_//_/ /_/ \__\_\___/ - * \/ /____/ version 0.11.23 - * - * This file is part of jQuery Terminal. http://terminal.jcubic.pl - * - * Copyright (c) 2010-2016 Jakub Jankiewicz - * Released under the MIT license - * - * Contains: - * - * Storage plugin Distributed under the MIT License - * Copyright (c) 2010 Dave Schindler - * - * jQuery Timers licenced with the WTFPL - * - * - * Cross-Browser Split 1.1.1 - * Copyright 2007-2012 Steven Levithan - * Available under the MIT License - * - * jQuery Caret - * Copyright (c) 2009, Gideon Sireling - * 3 clause BSD License - * - * sprintf.js - * Copyright (c) 2007-2013 Alexandru Marasteanu - * licensed under 3 clause BSD license - * - * Date: Sat, 10 Dec 2016 10:56:53 +0000 - */ -(function(e){var n=function(){if(!n.cache.hasOwnProperty(arguments[0])){n.cache[arguments[0]]=n.parse(arguments[0])}return n.format.call(null,n.cache[arguments[0]],arguments)};n.format=function(e,t){var o=1,a=e.length,s="",l,f=[],c,u,h,p,m,g;for(c=0;c>>0;break;case"x":l=l.toString(16);break;case"X":l=l.toString(16).toUpperCase();break}l=/[def]/.test(h[8])&&h[3]&&l>=0?"+"+l:l;m=h[4]?h[4]=="0"?"0":h[4].charAt(1):" ";g=h[6]-String(l).length;p=h[6]?i(m,g):"";f.push(h[5]?l+p:p+l)}}return f.join("")};n.cache={};n.parse=function(e){var n=e,t=[],r=[],i=0;while(n){if((t=/^[^\x25]+/.exec(n))!==null){r.push(t[0])}else if((t=/^\x25{2}/.exec(n))!==null){r.push("%")}else if((t=/^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(n))!==null){if(t[2]){i|=1;var o=[],a=t[2],s=[];if((s=/^([a-z_][a-z_\d]*)/i.exec(a))!==null){o.push(s[1]);while((a=a.substring(s[0].length))!==""){if((s=/^\.([a-z_][a-z_\d]*)/i.exec(a))!==null){o.push(s[1])}else if((s=/^\[(\d+)\]/.exec(a))!==null){o.push(s[1])}else{throw"[sprintf] huh?"}}}else{throw"[sprintf] huh?"}t[2]=o}else{i|=2}if(i===3){throw"[sprintf] mixing positional and named placeholders is not (yet) supported"}r.push(t)}else{throw"[sprintf] huh?"}n=n.substring(t[0].length)}return r};var t=function(e,t,r){r=t.slice(0);r.splice(0,0,e);return n.apply(null,r)};function r(e){return Object.prototype.toString.call(e).slice(8,-1).toLowerCase()}function i(e,n){for(var t=[];n>0;t[--n]=e){}return t.join("")}e.sprintf=n;e.vsprintf=t})(typeof global!="undefined"?global:window);(function(e,n){"use strict";e.omap=function(n,t){var r={};e.each(n,function(e,i){r[e]=t.call(n,e,i)});return r};var t={clone_object:function(n){var t={};if(typeof n=="object"){if(e.isArray(n)){return this.clone_array(n)}else if(n===null){return n}else{for(var r in n){if(e.isArray(n[r])){t[r]=this.clone_array(n[r])}else if(typeof n[r]=="object"){t[r]=this.clone_object(n[r])}else{t[r]=n[r]}}}}return t},clone_array:function(n){if(!e.isFunction(Array.prototype.map)){throw new Error("You'r browser don't support ES5 array map "+"use es5-shim")}return n.slice(0).map(function(e){if(typeof e=="object"){return this.clone_object(e)}else{return e}}.bind(this))}};var r=function(e){return t.clone_object(e)};var i=function(){var e="test",n=window.localStorage;try{n.setItem(e,"1");n.removeItem(e);return true}catch(t){return false}};var o=i();function a(e,n){var t;if(typeof e==="string"&&typeof n==="string"){localStorage[e]=n;return true}else if(typeof e==="object"&&typeof n==="undefined"){for(t in e){if(e.hasOwnProperty(t)){localStorage[t]=e[t]}}return true}return false}function s(e,n){var t,r,i;t=new Date;t.setTime(t.getTime()+31536e6);r="; expires="+t.toGMTString();if(typeof e==="string"&&typeof n==="string"){document.cookie=e+"="+n+r+"; path=/";return true}else if(typeof e==="object"&&typeof n==="undefined"){for(i in e){if(e.hasOwnProperty(i)){document.cookie=i+"="+e[i]+r+"; path=/"}}return true}return false}function l(e){return localStorage[e]}function f(e){var n,t,r,i;n=e+"=";t=document.cookie.split(";");for(r=0;ri&&i!==0||r.call(e,a)===false){h.timer.remove(e,t,r)}s.inProgress=false};s.$timerID=r.$timerID;if(!e.$timers[t][r.$timerID]){e.$timers[t][r.$timerID]=window.setInterval(s,n)}if(!this.global[t]){this.global[t]=[]}this.global[t].push(e)},remove:function(e,n,t){var r=e.$timers,i;if(r){if(!n){for(var o in r){if(r.hasOwnProperty(o)){this.remove(e,o,t)}}}else if(r[n]){if(t){if(t.$timerID){window.clearInterval(r[n][t.$timerID]);delete r[n][t.$timerID]}}else{for(var a in r[n]){if(r[n].hasOwnProperty(a)){window.clearInterval(r[n][a]);delete r[n][a]}}}for(i in r[n]){if(r[n].hasOwnProperty(i)){break}}if(!i){i=null;delete r[n]}}for(i in r){if(r.hasOwnProperty(i)){break}}if(!i){e.$timers=null}}}}});if(/(msie) ([\w.]+)/.exec(navigator.userAgent.toLowerCase())){h(window).one("unload",function(){var e=h.timer.global;for(var n in e){if(e.hasOwnProperty(n)){var t=e[n],r=t.length;while(--r){h.timer.remove(t[r],n)}}}})}(function(e){if(!String.prototype.split.toString().match(/\[native/)){return}var n=String.prototype.split,t=/()??/.exec("")[1]===e,r;r=function(r,i,o){if(Object.prototype.toString.call(i)!=="[object RegExp]"){return n.call(r,i,o)}var a=[],s=(i.ignoreCase?"i":"")+(i.multiline?"m":"")+(i.extended?"x":"")+(i.sticky?"y":""),l=0,f,c,u,h;i=new RegExp(i.source,s+"g");r+="";if(!t){f=new RegExp("^"+i.source+"$(?!\\s)",s)}o=o===e?-1>>>0:o>>>0;while(c=i.exec(r)){u=c.index+c[0].length;if(u>l){a.push(r.slice(l,c.index));if(!t&&c.length>1){c[0].replace(f,function(){for(var n=1;n1&&c.index=o){break}}if(i.lastIndex===c.index){i.lastIndex++}}if(l===r.length){if(h||!i.test("")){a.push("")}}else{a.push(r.slice(l))}return a.length>o?a.slice(0,o):a};String.prototype.split=function(e,n){return r(this,e,n)};return r})();e.fn.caret=function(e){var n=this[0];var t=n.contentEditable==="true";if(arguments.length==0){if(window.getSelection){if(t){n.focus();var r=window.getSelection().getRangeAt(0),i=r.cloneRange();i.selectNodeContents(n);i.setEnd(r.endContainer,r.endOffset);return i.toString().length}return n.selectionStart}if(document.selection){n.focus();if(t){var r=document.selection.createRange(),i=document.body.createTextRange();i.moveToElementText(n);i.setEndPoint("EndToEnd",r);return i.text.length}var e=0,o=n.createTextRange(),i=document.selection.createRange().duplicate(),a=i.getBookmark();o.moveToBookmark(a);while(o.moveStart("character",-1)!==0)e++;return e}return 0}if(e==-1)e=this[t?"text":"val"]().length;if(window.getSelection){if(t){n.focus();window.getSelection().collapse(n.firstChild,e)}else n.setSelectionRange(e,e)}else if(document.body.createTextRange){var o=document.body.createTextRange();o.moveToElementText(n);o.moveStart("character",e);o.collapse(true);o.select()}if(!t)n.focus();return e};function p(e,n){var t=[];var r=e.length;if(rt.length){if(n){break}e=0;n=true}}return t[e]}},append:function(e){t.push(e)}})}function g(n){var t=n instanceof Array?n:n?[n]:[];e.extend(this,{data:function(){return t},map:function(n){return e.map(t,n)},size:function(){return t.length},pop:function(){if(t.length===0){return null}else{var e=t[t.length-1];t=t.slice(0,t.length-1);return e}},push:function(e){t=t.concat([e]);return e},top:function(){return t.length>0?t[t.length-1]:null},clone:function(){return new g(t.slice(0))}})}function d(n,t,r){var i=true;var o="";if(typeof n==="string"&&n!==""){o=n+"_"}o+="commands";var a;if(r){a=[]}else{a=e.Storage.get(o);a=a?e.parseJSON(a):[]}var s=a.length-1;e.extend(this,{append:function(n){if(i){if(a[a.length-1]!==n){a.push(n);if(t&&a.length>t){a=a.slice(-t)}s=a.length-1;if(!r){e.Storage.set(o,JSON.stringify(a))}}}},data:function(){return a},reset:function(){s=a.length-1},last:function(){return a[a.length-1]},end:function(){return s===a.length-1},position:function(){return s},current:function(){return a[s]},next:function(){if(s0){--s}if(e!==-1){return a[s]}},clear:function(){a=[];this.purge()},enabled:function(){return i},enable:function(){i=true},purge:function(){if(!r){e.Storage.remove(o)}},disable:function(){i=false}})}var v=function(){var e=document.createElement("div");e.setAttribute("onpaste","return;");return typeof e.onpaste=="function"}();var y=true;e.fn.cmd=function(t){var r=this;var i=r.data("cmd");if(i){return i}r.addClass("cmd");r.append(''+' ');var o=e("${6}\nsnippet tfoot\n \n ${1}\n \nsnippet th\n ${1}\nsnippet th.\n ${2}\nsnippet th#\n ${2}\nsnippet th+\n ${1}\n th+${2}\nsnippet thead\n \n ${1}\n \nsnippet time\n