diff --git a/.dev/vagrant/.env.vagrant b/.dev/vagrant/.env.vagrant deleted file mode 100644 index 2427ec04e..000000000 --- a/.dev/vagrant/.env.vagrant +++ /dev/null @@ -1,39 +0,0 @@ -APP_ENV=develop -APP_DEBUG=true -APP_KEY=SomeRandomString3232RandomString -APP_THEME=pterodactyl -APP_TIMEZONE=UTC -APP_CLEAR_TASKLOG=720 -APP_DELETE_MINUTES=10 -APP_URL=http://192.168.50.2/ - -DB_HOST=localhost -DB_PORT=3306 -DB_DATABASE=panel -DB_USERNAME=pterodactyl -DB_PASSWORD=pterodactyl - -CACHE_DRIVER=memcached -MEMCACHED_HOST=127.0.0.1 -SESSION_DRIVER=database - -MAIL_DRIVER=smtp -MAIL_HOST=127.0.0.1 -MAIL_PORT=1025 -MAIL_USERNAME= -MAIL_PASSWORD= -MAIL_ENCRYPTION= -MAIL_FROM=support@pterodactyl.io - -API_PREFIX=api -API_VERSION=v1 -API_DEBUG=true - -QUEUE_DRIVER=database -QUEUE_HIGH=high -QUEUE_STANDARD=standard -QUEUE_LOW=low - -SQS_KEY=aws-public -SQS_SECRET=aws-secret -SQS_QUEUE_PREFIX=aws-queue-prefix diff --git a/.dev/vagrant/mailhog.service b/.dev/vagrant/mailhog.service deleted file mode 100644 index 01334183d..000000000 --- a/.dev/vagrant/mailhog.service +++ /dev/null @@ -1,13 +0,0 @@ -[Unit] -Description=Mailhog - -[Service] -# On some systems the user and group might be different. -# Some systems use `apache` as the user and group. -User=www-data -Group=www-data -Restart=on-failure -ExecStart=/usr/bin/mailhog - -[Install] -WantedBy=multi-user.target diff --git a/.dev/vagrant/mariadb.cnf b/.dev/vagrant/mariadb.cnf deleted file mode 100644 index 48b31ed8b..000000000 --- a/.dev/vagrant/mariadb.cnf +++ /dev/null @@ -1,189 +0,0 @@ -# MariaDB database server configuration file. -# -# You can copy this file to one of: -# - "/etc/mysql/my.cnf" to set global options, -# - "~/.my.cnf" to set user-specific options. -# -# One can use all long options that the program supports. -# Run program with --help to get a list of available options and with -# --print-defaults to see which it would actually understand and use. -# -# For explanations see -# http://dev.mysql.com/doc/mysql/en/server-system-variables.html - -# This will be passed to all mysql clients -# It has been reported that passwords should be enclosed with ticks/quotes -# escpecially if they contain "#" chars... -# Remember to edit /etc/mysql/debian.cnf when changing the socket location. -[client] -port = 3306 -socket = /var/run/mysqld/mysqld.sock - -# Here is entries for some specific programs -# The following values assume you have at least 32M ram - -# This was formally known as [safe_mysqld]. Both versions are currently parsed. -[mysqld_safe] -socket = /var/run/mysqld/mysqld.sock -nice = 0 - -[mysqld] -# -# * Basic Settings -# -user = mysql -pid-file = /var/run/mysqld/mysqld.pid -socket = /var/run/mysqld/mysqld.sock -port = 3306 -basedir = /usr -datadir = /var/lib/mysql -tmpdir = /tmp -lc_messages_dir = /usr/share/mysql -lc_messages = en_US -skip-external-locking -# -# Instead of skip-networking the default is now to listen only on -# localhost which is more compatible and is not less secure. -bind-address = 0.0.0.0 -# -# * Fine Tuning -# -max_connections = 100 -connect_timeout = 5 -wait_timeout = 600 -max_allowed_packet = 16M -thread_cache_size = 128 -sort_buffer_size = 4M -bulk_insert_buffer_size = 16M -tmp_table_size = 32M -max_heap_table_size = 32M -# -# * MyISAM -# -# This replaces the startup script and checks MyISAM tables if needed -# the first time they are touched. On error, make copy and try a repair. -myisam_recover_options = BACKUP -key_buffer_size = 128M -#open-files-limit = 2000 -table_open_cache = 400 -myisam_sort_buffer_size = 512M -concurrent_insert = 2 -read_buffer_size = 2M -read_rnd_buffer_size = 1M -# -# * Query Cache Configuration -# -# Cache only tiny result sets, so we can fit more in the query cache. -query_cache_limit = 128K -query_cache_size = 64M -# for more write intensive setups, set to DEMAND or OFF -#query_cache_type = DEMAND -# -# * Logging and Replication -# -# Both location gets rotated by the cronjob. -# Be aware that this log type is a performance killer. -# As of 5.1 you can enable the log at runtime! -#general_log_file = /var/log/mysql/mysql.log -#general_log = 1 -# -# Error logging goes to syslog due to /etc/mysql/conf.d/mysqld_safe_syslog.cnf. -# -# we do want to know about network errors and such -log_warnings = 2 -# -# Enable the slow query log to see queries with especially long duration -#slow_query_log[={0|1}] -slow_query_log_file = /var/log/mysql/mariadb-slow.log -long_query_time = 10 -#log_slow_rate_limit = 1000 -log_slow_verbosity = query_plan - -#log-queries-not-using-indexes -#log_slow_admin_statements -# -# The following can be used as easy to replay backup logs or for replication. -# note: if you are setting up a replication slave, see README.Debian about -# other settings you may need to change. -#server-id = 1 -#report_host = master1 -#auto_increment_increment = 2 -#auto_increment_offset = 1 -log_bin = /var/log/mysql/mariadb-bin -log_bin_index = /var/log/mysql/mariadb-bin.index -# not fab for performance, but safer -#sync_binlog = 1 -expire_logs_days = 10 -max_binlog_size = 100M -# slaves -#relay_log = /var/log/mysql/relay-bin -#relay_log_index = /var/log/mysql/relay-bin.index -#relay_log_info_file = /var/log/mysql/relay-bin.info -#log_slave_updates -#read_only -# -# If applications support it, this stricter sql_mode prevents some -# mistakes like inserting invalid dates etc. -#sql_mode = NO_ENGINE_SUBSTITUTION,TRADITIONAL -# -# * InnoDB -# -# InnoDB is enabled by default with a 10MB datafile in /var/lib/mysql/. -# Read the manual for more InnoDB related options. There are many! -default_storage_engine = InnoDB -# you can't just change log file size, requires special procedure -#innodb_log_file_size = 50M -innodb_buffer_pool_size = 256M -innodb_log_buffer_size = 8M -innodb_file_per_table = 1 -innodb_open_files = 400 -innodb_io_capacity = 400 -innodb_flush_method = O_DIRECT -# -# * Security Features -# -# Read the manual, too, if you want chroot! -# chroot = /var/lib/mysql/ -# -# For generating SSL certificates I recommend the OpenSSL GUI "tinyca". -# -# ssl-ca=/etc/mysql/cacert.pem -# ssl-cert=/etc/mysql/server-cert.pem -# ssl-key=/etc/mysql/server-key.pem - -# -# * Galera-related settings -# -[galera] -# Mandatory settings -#wsrep_on=ON -#wsrep_provider= -#wsrep_cluster_address= -#binlog_format=row -#default_storage_engine=InnoDB -#innodb_autoinc_lock_mode=2 -# -# Allow server to accept connections on all interfaces. -# -#bind-address=0.0.0.0 -# -# Optional setting -#wsrep_slave_threads=1 -#innodb_flush_log_at_trx_commit=0 - -[mysqldump] -quick -quote-names -max_allowed_packet = 16M - -[mysql] -#no-auto-rehash # faster start of mysql but no tab completion - -[isamchk] -key_buffer = 16M - -# -# * IMPORTANT: Additional settings that can override those from this file! -# The files must end with '.cnf', otherwise they'll be ignored. -# -!includedir /etc/mysql/conf.d/ diff --git a/.dev/vagrant/motd.txt b/.dev/vagrant/motd.txt deleted file mode 100644 index 22089d55a..000000000 --- a/.dev/vagrant/motd.txt +++ /dev/null @@ -1,17 +0,0 @@ -##################################################### - Pterodactyl Panel Vagrant VM - -Install: /var/www/html/pterodactyl -Ports: - Panel: 80 (50080 on host) - MailHog: 8025 (58025 on host) - MySQL: 3306 (53306 on host) - -Default panel users: - user: admin passwd: Ptero123 (admin user) - user: user passwd: Ptero123 (standard user) - -MySQL is accessible using root/pterodactyl or pterodactyl/pterodactyl - -Service for pteroq and mailhog are running -##################################################### diff --git a/.dev/vagrant/provision.sh b/.dev/vagrant/provision.sh deleted file mode 100644 index 35a2219f0..000000000 --- a/.dev/vagrant/provision.sh +++ /dev/null @@ -1,90 +0,0 @@ -#!/bin/bash - -echo "Provisioning development environment for Pterodactyl Panel." -cp /var/www/html/pterodactyl/.dev/vagrant/motd.txt /etc/motd -chmod -x /etc/update-motd.d/10-help-text /etc/update-motd.d/51-cloudguest - -apt-get install -y software-properties-common > /dev/null - -echo "Add the ondrej/php ppa repository" -add-apt-repository -y ppa:ondrej/php > /dev/null -echo "Add the mariadb repository" -curl -sS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | bash > /dev/null - -apt-get update > /dev/null - -echo "Install the dependencies" -export DEBIAN_FRONTEND=noninteractive -# set the mariadb root password because mariadb asks for it -debconf-set-selections <<< 'mariadb-server-5.5 mysql-server/root_password password pterodactyl' -debconf-set-selections <<< 'mariadb-server-5.5 mysql-server/root_password_again password pterodactyl' -# actually install -apt-get install -y php7.2 php7.2-cli php7.2-gd php7.2-mysql php7.2-pdo php7.2-mbstring php7.2-tokenizer php7.2-bcmath php7.2-xml php7.2-fpm php7.2-memcached php7.2-curl php7.2-zip php-xdebug mariadb-server nginx curl tar unzip git memcached > /dev/null - -echo "Install nodejs and yarn" -curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - -echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list -curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - -apt-get -y install nodejs yarn > /dev/null - -echo "Install composer" -curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer - -echo "Install and run mailhog" -curl -sL -o /usr/bin/mailhog https://github.com/mailhog/MailHog/releases/download/v1.0.0/MailHog_linux_amd64 -chmod +x /usr/bin/mailhog -cp /var/www/html/pterodactyl/.dev/vagrant/mailhog.service /etc/systemd/system/ -systemctl enable mailhog.service -systemctl start mailhog - -echo "Configure xDebug" -cp /var/www/html/pterodactyl/.dev/vagrant/xdebug.ini /etc/php/7.2/mods-available/ -systemctl restart php7.2-fpm - -echo "Configure nginx" -cp /var/www/html/pterodactyl/.dev/vagrant/pterodactyl.conf /etc/nginx/sites-available/ -rm /etc/nginx/sites-available/default -ln -s /etc/nginx/sites-available/pterodactyl.conf /etc/nginx/sites-enabled/pterodactyl.conf -systemctl restart nginx - -echo "Setup database" -# Replace default config with custom one to bind mysql to 0.0.0.0 to make it accessible from the host -cp /var/www/html/pterodactyl/.dev/vagrant/mariadb.cnf /etc/mysql/my.cnf -systemctl restart mariadb -mysql -u root -ppterodactyl << SQL -CREATE DATABASE panel; -GRANT ALL PRIVILEGES ON panel.* TO 'pterodactyl'@'%' IDENTIFIED BY 'pterodactyl' WITH GRANT OPTION; -GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'pterodactyl' WITH GRANT OPTION; -FLUSH PRIVILEGES; -SQL - -echo "Setup pterodactyl queue worker service" -cp /var/www/html/pterodactyl/.dev/vagrant/pteroq.service /etc/systemd/system/ -systemctl enable pteroq.service - - -echo "Setup panel with base settings" -cp /var/www/html/pterodactyl/.dev/vagrant/.env.vagrant /var/www/html/pterodactyl/.env -cd /var/www/html/pterodactyl -chmod -R 755 storage/* bootstrap/cache -composer install --no-progress -php artisan key:generate --force -php artisan migrate -php artisan db:seed -php artisan p:user:make --name-first Test --name-last Admin --username admin --email testadmin@pterodactyl.io --password Ptero123 --admin 1 -php artisan p:user:make --name-first Test --name-last User --username user --email testuser@pterodactyl.io --password Ptero123 --admin 0 - -echo "Add queue cronjob and start queue worker" -(crontab -l 2>/dev/null; echo "* * * * * php /var/www/html/pterodactyl/artisan schedule:run >> /dev/null 2>&1") | crontab - -systemctl start pteroq - -echo " ----------------" -echo "Provisioning is completed." -echo "The panel should be available at http://localhost:50080/" -echo "You may use the default admin user to login: admin/Ptero123" -echo "A normal user has also been created: user/Ptero123" -echo "MailHog is available at http://localhost:58025/" -echo "Connect to the database using root/pterodactyl or pterodactyl/pterodactyl on localhost:53306" -echo "If you want to access the panel using http://pterodactyl.app you can use the vagrant-dns plugin" -echo "Install it with 'vagrant plugin install vagrant-dns', then run 'vagrant dns --install' once" -echo "On first use you'll have to manually start vagrant-dns with 'vagrant dns --start'" diff --git a/.dev/vagrant/pterodactyl.conf b/.dev/vagrant/pterodactyl.conf deleted file mode 100644 index 343cbad5f..000000000 --- a/.dev/vagrant/pterodactyl.conf +++ /dev/null @@ -1,51 +0,0 @@ -# If using Ubuntu this file should be placed in: -# /etc/nginx/sites-available/ -# -# If using CentOS this file should be placed in: -# /etc/nginx/conf.d/ -# -server { - listen 80; - server_name 0.0.0.0; - - root /var/www/html/pterodactyl/public; - index index.html index.htm index.php; - charset utf-8; - - location / { - try_files $uri $uri/ /index.php?$query_string; - } - - location = /favicon.ico { access_log off; log_not_found off; } - location = /robots.txt { access_log off; log_not_found off; } - - access_log off; - error_log /var/log/nginx/pterodactyl.app-error.log error; - - # allow larger file uploads and longer script runtimes - client_max_body_size 100m; - client_body_timeout 120s; - - sendfile off; - - location ~ \.php$ { - fastcgi_split_path_info ^(.+\.php)(/.+)$; - # the fastcgi_pass path needs to be changed accordingly when using CentOS - fastcgi_pass unix:/var/run/php/php7.2-fpm.sock; - fastcgi_index index.php; - include fastcgi_params; - fastcgi_param PHP_VALUE "upload_max_filesize = 100M \n post_max_size=100M"; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - fastcgi_param HTTP_PROXY ""; - fastcgi_intercept_errors off; - fastcgi_buffer_size 16k; - fastcgi_buffers 4 16k; - fastcgi_connect_timeout 300; - fastcgi_send_timeout 300; - fastcgi_read_timeout 300; - } - - location ~ /\.ht { - deny all; - } -} diff --git a/.dev/vagrant/pteroq.service b/.dev/vagrant/pteroq.service deleted file mode 100644 index 7828ee91b..000000000 --- a/.dev/vagrant/pteroq.service +++ /dev/null @@ -1,20 +0,0 @@ -# Pterodactyl Queue Worker File -# ---------------------------------- -# File should be placed in: -# /etc/systemd/system -# -# nano /etc/systemd/system/pteroq.service - -[Unit] -Description=Pterodactyl Queue Worker - -[Service] -# On some systems the user and group might be different. -# Some systems use `apache` as the user and group. -User=www-data -Group=www-data -Restart=on-failure -ExecStart=/usr/bin/php /var/www/html/pterodactyl/artisan queue:work database --queue=high,standard,low --sleep=3 --tries=3 - -[Install] -WantedBy=multi-user.target diff --git a/.dev/vagrant/xdebug.ini b/.dev/vagrant/xdebug.ini deleted file mode 100644 index 1725b8e84..000000000 --- a/.dev/vagrant/xdebug.ini +++ /dev/null @@ -1,10 +0,0 @@ -zend_extension=xdebug.so - -xdebug.remote_enable=1 -xdebug.remote_connect_back=1 -xdebug.remote_port=9000 -xdebug.scream=0 -xdebug.show_local_vars=1 -xdebug.idekey=PHPSTORM - -xdebug.remote_log=/tmp/xdebug.log \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 54ea1d760..0525e5b39 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,17 +1,16 @@ -FROM php:7.2-fpm-alpine +FROM php:7.4-fpm-alpine WORKDIR /app -RUN apk add --no-cache --update ca-certificates dcron curl git supervisor tar unzip; \ +RUN apk add --no-cache --update ca-certificates dcron curl git supervisor tar unzip nginx libpng-dev libxml2-dev libzip-dev certbot yarn; \ docker-php-ext-install bcmath; \ - apk add --no-cache libpng-dev; \ docker-php-ext-install gd; \ docker-php-ext-install mbstring; \ docker-php-ext-install pdo; \ docker-php-ext-install pdo_mysql; \ docker-php-ext-install tokenizer; \ - apk add --no-cache libxml2-dev; \ docker-php-ext-install xml; \ + docker-php-ext-configure zip --with-libzip=/usr/include; \ docker-php-ext-install zip; \ curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer @@ -22,15 +21,16 @@ RUN cp .env.example .env \ && rm .env \ && chown -R nginx:nginx . && chmod -R 777 storage/* bootstrap/cache -RUN cp .dev/docker/default.conf /etc/nginx/conf.d/default.conf \ - && cp .dev/docker/www.conf /etc/php7/php-fpm.d/www.conf \ - && cat .dev/docker/supervisord.conf > /etc/supervisord.conf \ +RUN cp docker/default.conf /etc/nginx/conf.d/default.conf \ + && cat docker/www.conf > /usr/local/etc/php-fpm.d/www.conf \ + && rm /usr/local/etc/php-fpm.d/www.conf.default \ + && cat docker/supervisord.conf > /etc/supervisord.conf \ && echo "* * * * * /usr/bin/php /app/artisan schedule:run >> /dev/null 2>&1" >> /var/spool/cron/crontabs/root \ && sed -i s/ssl_session_cache/#ssl_session_cache/g /etc/nginx/nginx.conf \ && mkdir -p /var/run/php /var/run/nginx EXPOSE 80 443 -ENTRYPOINT ["/bin/ash", ".dev/docker/entrypoint.sh"] +ENTRYPOINT ["/bin/ash", "docker/entrypoint.sh"] -CMD [ "supervisord", "-n", "-c", "/etc/supervisord.conf" ] +CMD [ "supervisord", "-n", "-c", "/etc/supervisord.conf" ] \ No newline at end of file diff --git a/app/Console/Commands/Environment/EmailSettingsCommand.php b/app/Console/Commands/Environment/EmailSettingsCommand.php index b8604757f..add1296e6 100644 --- a/app/Console/Commands/Environment/EmailSettingsCommand.php +++ b/app/Console/Commands/Environment/EmailSettingsCommand.php @@ -65,14 +65,14 @@ class EmailSettingsCommand extends Command public function handle() { $this->variables['MAIL_DRIVER'] = $this->option('driver') ?? $this->choice( - trans('command/messages.environment.mail.ask_driver'), [ + trans('command/messages.environment.mail.ask_driver'), [ 'smtp' => 'SMTP Server', 'mail' => 'PHP\'s Internal Mail Function', 'mailgun' => 'Mailgun Transactional Email', 'mandrill' => 'Mandrill Transactional Email', 'postmark' => 'Postmarkapp Transactional Email', ], $this->config->get('mail.driver', 'smtp') - ); + ); $method = 'setup' . studly_case($this->variables['MAIL_DRIVER']) . 'DriverVariables'; if (method_exists($this, $method)) { diff --git a/app/Contracts/Repository/UserRepositoryInterface.php b/app/Contracts/Repository/UserRepositoryInterface.php index 41649f48b..f5ba47b4b 100644 --- a/app/Contracts/Repository/UserRepositoryInterface.php +++ b/app/Contracts/Repository/UserRepositoryInterface.php @@ -22,4 +22,12 @@ interface UserRepositoryInterface extends RepositoryInterface, SearchableInterfa * @return \Illuminate\Support\Collection */ public function filterUsersByQuery(?string $query): Collection; + + /** + * Returns a user with the given id in a format that can be used for dropdowns. + * + * @param int $id + * @return \Pterodactyl\Models\Model + */ + public function filterById(int $id): \Pterodactyl\Models\Model; } diff --git a/app/Helpers/Utilities.php b/app/Helpers/Utilities.php index 2a0689124..d900425d9 100644 --- a/app/Helpers/Utilities.php +++ b/app/Helpers/Utilities.php @@ -6,6 +6,7 @@ use Exception; use Carbon\Carbon; use Cron\CronExpression; use Illuminate\Support\Facades\Log; +use Illuminate\Support\ViewErrorBag; class Utilities { @@ -50,4 +51,15 @@ class Utilities sprintf('%s %s %s * %s', $minute, $hour, $dayOfMonth, $dayOfWeek) )->getNextRunDate()); } + + public static function checked($name, $default) + { + $errors = session('errors'); + + if (isset($errors) && $errors instanceof ViewErrorBag && $errors->any()) { + return old($name) ? 'checked' : ''; + } + + return ($default) ? 'checked' : ''; + } } diff --git a/app/Http/Controllers/Admin/DatabaseController.php b/app/Http/Controllers/Admin/DatabaseController.php index 569fec1d7..0aee86806 100644 --- a/app/Http/Controllers/Admin/DatabaseController.php +++ b/app/Http/Controllers/Admin/DatabaseController.php @@ -165,6 +165,7 @@ class DatabaseController extends Controller $this->alert->danger( sprintf('There was an error while trying to connect to the host or while executing a query: "%s"', $exception->getMessage()) )->flash(); + return $redirect->withInput($request->normalize()); } else { throw $exception; diff --git a/app/Http/Controllers/Admin/NodeAutoDeployController.php b/app/Http/Controllers/Admin/NodeAutoDeployController.php new file mode 100644 index 000000000..be6301c31 --- /dev/null +++ b/app/Http/Controllers/Admin/NodeAutoDeployController.php @@ -0,0 +1,88 @@ +keyCreationService = $keyCreationService; + $this->repository = $repository; + $this->encrypter = $encrypter; + } + + /** + * Generates a new API key for the logged in user with only permission to read + * nodes, and returns that as the deployment key for a node. + * + * @param \Illuminate\Http\Request $request + * @param \Pterodactyl\Models\Node $node + * @return \Illuminate\Http\JsonResponse + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + */ + public function __invoke(Request $request, Node $node) + { + /** @var \Pterodactyl\Models\ApiKey|null $key */ + $key = $this->repository->getApplicationKeys($request->user()) + ->filter(function (ApiKey $key) { + foreach ($key->getAttributes() as $permission => $value) { + if ($permission === 'r_nodes' && $value === 1) { + return true; + } + } + + return false; + }) + ->first(); + + // We couldn't find a key that exists for this user with only permission for + // reading nodes. Go ahead and create it now. + if (! $key) { + $key = $this->keyCreationService->setKeyType(ApiKey::TYPE_APPLICATION)->handle([ + 'user_id' => $request->user()->id, + 'memo' => 'Automatically generated node deployment key.', + 'allowed_ips' => [], + ], ['r_nodes' => 1]); + } + + return JsonResponse::create([ + 'node' => $node->id, + 'token' => $key->identifier . $this->encrypter->decrypt($key->token), + ]); + } +} diff --git a/app/Http/Controllers/Admin/NodesController.php b/app/Http/Controllers/Admin/NodesController.php index a7d710a10..d482c47ae 100644 --- a/app/Http/Controllers/Admin/NodesController.php +++ b/app/Http/Controllers/Admin/NodesController.php @@ -9,7 +9,6 @@ namespace Pterodactyl\Http\Controllers\Admin; -use Cake\Chronos\Chronos; use Illuminate\Http\Request; use Pterodactyl\Models\Node; use Illuminate\Http\Response; @@ -300,18 +299,4 @@ class NodesController extends Controller return redirect()->route('admin.nodes'); } - - /** - * Returns the configuration token to auto-deploy a node. - * - * @param \Pterodactyl\Models\Node $node - * @return \Illuminate\Http\JsonResponse - */ - public function setToken(Node $node) - { - $token = bin2hex(random_bytes(16)); - $this->cache->put('Node:Configuration:' . $token, $node->id, Chronos::now()->addMinutes(5)); - - return response()->json(['token' => $token]); - } } diff --git a/app/Http/Controllers/Admin/StatisticsController.php b/app/Http/Controllers/Admin/StatisticsController.php index 9e6ff1ad2..612f04b62 100644 --- a/app/Http/Controllers/Admin/StatisticsController.php +++ b/app/Http/Controllers/Admin/StatisticsController.php @@ -9,6 +9,7 @@ use Pterodactyl\Contracts\Repository\NodeRepositoryInterface; use Pterodactyl\Contracts\Repository\UserRepositoryInterface; use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface; class StatisticsController extends Controller @@ -45,6 +46,7 @@ class StatisticsController extends Controller public function index() { + throw new NotFoundHttpException; $servers = $this->serverRepository->all(); $nodes = $this->nodeRepository->all(); $usersCount = $this->userRepository->count(); diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index 14b3b594f..18e7e3b43 100644 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -177,10 +177,15 @@ class UserController extends Controller * Get a JSON response of users on the system. * * @param \Illuminate\Http\Request $request - * @return \Illuminate\Support\Collection + * @return \Illuminate\Support\Collection|\Pterodactyl\Models\Model */ public function json(Request $request) { + // Handle single user requests. + if ($request->query('user_id')) { + return $this->repository->filterById($request->input('user_id')); + } + return $this->repository->filterUsersByQuery($request->input('q')); } } diff --git a/app/Http/Controllers/Api/Application/Nodes/NodeConfigurationController.php b/app/Http/Controllers/Api/Application/Nodes/NodeConfigurationController.php new file mode 100644 index 000000000..fc0c35f75 --- /dev/null +++ b/app/Http/Controllers/Api/Application/Nodes/NodeConfigurationController.php @@ -0,0 +1,25 @@ +getConfiguration()); + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/ScheduleTaskController.php b/app/Http/Controllers/Api/Client/Servers/ScheduleTaskController.php index 340e2541a..7461a950a 100644 --- a/app/Http/Controllers/Api/Client/Servers/ScheduleTaskController.php +++ b/app/Http/Controllers/Api/Client/Servers/ScheduleTaskController.php @@ -86,7 +86,7 @@ class ScheduleTaskController extends ClientApiController } $this->repository->update($task->id, [ - 'action' => $request->input('action'), + 'action' => $request->input('action'), 'payload' => $request->input('payload'), 'time_offset' => $request->input('time_offset'), ]); diff --git a/app/Http/Controllers/Api/Remote/Servers/ServerBackupController.php b/app/Http/Controllers/Api/Remote/Servers/ServerBackupController.php index b4599ee0a..8daee56af 100644 --- a/app/Http/Controllers/Api/Remote/Servers/ServerBackupController.php +++ b/app/Http/Controllers/Api/Remote/Servers/ServerBackupController.php @@ -3,8 +3,6 @@ namespace Pterodactyl\Http\Controllers\Api\Remote\Servers; use Carbon\Carbon; -use Illuminate\Http\Request; -use Pterodactyl\Models\Server; use Illuminate\Http\JsonResponse; use Pterodactyl\Http\Controllers\Controller; use Pterodactyl\Repositories\Eloquent\BackupRepository; diff --git a/app/Repositories/Eloquent/UserRepository.php b/app/Repositories/Eloquent/UserRepository.php index 1ed6c5b74..b6468b5bb 100644 --- a/app/Repositories/Eloquent/UserRepository.php +++ b/app/Repositories/Eloquent/UserRepository.php @@ -54,4 +54,22 @@ class UserRepository extends EloquentRepository implements UserRepositoryInterfa return $item; }); } + + /** + * Returns a user with the given id in a format that can be used for dropdowns. + * + * @param int $id + * @return \Pterodactyl\Models\Model + */ + public function filterById(int $id): \Pterodactyl\Models\Model + { + $this->setColumns([ + 'id', 'email', 'username', 'name_first', 'name_last', + ]); + + $model = $this->getBuilder()->findOrFail($id, $this->getColumns())->getModel(); + $model->md5 = md5(strtolower($model->email)); + + return $model; + } } diff --git a/app/Services/Eggs/EggConfigurationService.php b/app/Services/Eggs/EggConfigurationService.php index 96b6a72bb..3d98cc33c 100644 --- a/app/Services/Eggs/EggConfigurationService.php +++ b/app/Services/Eggs/EggConfigurationService.php @@ -124,7 +124,22 @@ class EggConfigurationService } foreach ($value as $find => $replace) { - $append['replace'][] = ['match' => $find, 'value' => $replace]; + if (is_object($replace)) { + foreach ($replace as $match => $replaceWith) { + $append['replace'][] = [ + 'match' => $find, + 'if_value' => $match, + 'replace_with' => $replaceWith, + ]; + } + + continue; + } + + $append['replace'][] = [ + 'match' => $find, + 'replace_with' => $replace, + ]; } } @@ -155,17 +170,13 @@ class EggConfigurationService return self::NOT_MATCHED; } - // We don't want to do anything with config keys since the Daemon will need to handle - // that. For example, the Spigot egg uses "config.docker.interface" to identify the Docker - // interface to proxy through, but the Panel would be unaware of that. - if (Str::startsWith($key, 'config.')) { - return "{{{$key}}}"; - } - // The legacy Daemon would set SERVER_MEMORY, SERVER_IP, and SERVER_PORT with their // respective values on the Daemon side. Ensure that anything referencing those properly // replaces them with the matching config value. switch ($key) { + case 'config.docker.interface': + $key = 'config.docker.network.interface'; + break; case 'server.build.env.SERVER_MEMORY': case 'env.SERVER_MEMORY': $key = 'server.build.memory'; @@ -180,6 +191,13 @@ class EggConfigurationService break; } + // We don't want to do anything with config keys since the Daemon will need to handle + // that. For example, the Spigot egg uses "config.docker.interface" to identify the Docker + // interface to proxy through, but the Panel would be unaware of that. + if (Str::startsWith($key, 'config.')) { + return preg_replace('/{{(.*)}}/', "{{{$key}}}", $value); + } + // Replace anything starting with "server." with the value out of the server configuration // array that used to be created for the old daemon. if (Str::startsWith($key, 'server.')) { diff --git a/app/Services/Nodes/NodeCreationService.php b/app/Services/Nodes/NodeCreationService.php index 5090ba23c..261526a91 100644 --- a/app/Services/Nodes/NodeCreationService.php +++ b/app/Services/Nodes/NodeCreationService.php @@ -41,8 +41,8 @@ class NodeCreationService */ public function handle(array $data) { - $data['daemon_token'] = Str::random(Node::DAEMON_TOKEN_LENGTH); - $data['daemon_token_id'] = $this->encrypter->encrypt(Str::random(Node::DAEMON_TOKEN_ID_LENGTH)); + $data['daemon_token'] = $this->encrypter->encrypt(Str::random(Node::DAEMON_TOKEN_LENGTH)); + $data['daemon_token_id'] = Str::random(Node::DAEMON_TOKEN_ID_LENGTH); return $this->repository->create($data, true, true); } diff --git a/config/ide-helper.php b/config/ide-helper.php index 9f10873f6..5922f533c 100644 --- a/config/ide-helper.php +++ b/config/ide-helper.php @@ -168,8 +168,8 @@ return [ | Cast the given "real type" to the given "type". | */ - 'type_overrides' => [ + 'type_overrides' => [ 'integer' => 'int', 'boolean' => 'bool', - ], + ], ]; diff --git a/database/factories/ModelFactory.php b/database/factories/ModelFactory.php index 3b2d3e728..93fbac555 100644 --- a/database/factories/ModelFactory.php +++ b/database/factories/ModelFactory.php @@ -3,8 +3,8 @@ use Ramsey\Uuid\Uuid; use Cake\Chronos\Chronos; use Illuminate\Support\Str; -use Faker\Generator as Faker; use Pterodactyl\Models\Node; +use Faker\Generator as Faker; use Pterodactyl\Models\ApiKey; /* diff --git a/database/migrations/2017_10_02_202000_ChangeServicesToUseAMoreUniqueIdentifier.php b/database/migrations/2017_10_02_202000_ChangeServicesToUseAMoreUniqueIdentifier.php index 6bb36813d..dffa7687a 100644 --- a/database/migrations/2017_10_02_202000_ChangeServicesToUseAMoreUniqueIdentifier.php +++ b/database/migrations/2017_10_02_202000_ChangeServicesToUseAMoreUniqueIdentifier.php @@ -26,8 +26,8 @@ class ChangeServicesToUseAMoreUniqueIdentifier extends Migration DB::table('services')->get(['id', 'author', 'uuid'])->each(function ($service) { DB::table('services')->where('id', $service->id)->update([ - 'author' => ($service->author === 'ptrdctyl-v040-11e6-8b77-86f30ca893d3') ? 'support@pterodactyl.io' : 'unknown@unknown-author.com', - 'uuid' => Uuid::uuid4()->toString(), + 'author' => ($service->author === 'ptrdctyl-v040-11e6-8b77-86f30ca893d3') ? 'support@pterodactyl.io' : 'unknown@unknown-author.com', + 'uuid' => Uuid::uuid4()->toString(), ]); }); diff --git a/database/migrations/2020_03_22_163911_merge_permissions_table_into_subusers.php b/database/migrations/2020_03_22_163911_merge_permissions_table_into_subusers.php index 7cd0c5fce..621c9526b 100644 --- a/database/migrations/2020_03_22_163911_merge_permissions_table_into_subusers.php +++ b/database/migrations/2020_03_22_163911_merge_permissions_table_into_subusers.php @@ -44,12 +44,12 @@ class MergePermissionsTableIntoSubusers extends Migration { foreach (DB::select('SELECT id, permissions FROM subusers') as $datum) { $values = []; - foreach(json_decode($datum->permissions, true) as $permission) { + foreach (json_decode($datum->permissions, true) as $permission) { $values[] = $datum->id; $values[] = $permission; } - if (!empty($values)) { + if (! empty($values)) { $string = 'VALUES ' . implode(', ', array_fill(0, count($values) / 2, '(?, ?)')); DB::insert('INSERT INTO permissions(`subuser_id`, `permission`) ' . $string, $values); diff --git a/database/migrations/2020_04_03_203624_add_threads_column_to_servers_table.php b/database/migrations/2020_04_03_203624_add_threads_column_to_servers_table.php index 0371312de..9b0202cab 100644 --- a/database/migrations/2020_04_03_203624_add_threads_column_to_servers_table.php +++ b/database/migrations/2020_04_03_203624_add_threads_column_to_servers_table.php @@ -14,7 +14,7 @@ class AddThreadsColumnToServersTable extends Migration public function up() { Schema::table('servers', function (Blueprint $table) { - $table->string('threads')->nullable(); + $table->string('threads')->nullable()->after('cpu'); }); } diff --git a/database/migrations/2020_04_03_230614_create_backups_table.php b/database/migrations/2020_04_03_230614_create_backups_table.php index 63dad39a0..ead68105c 100644 --- a/database/migrations/2020_04_03_230614_create_backups_table.php +++ b/database/migrations/2020_04_03_230614_create_backups_table.php @@ -1,8 +1,8 @@ workshop.lua\r\n\r\ncd \/mnt\/server\/garrysmod\/cfg\r\necho '\r\n\/\/ Please do not set RCon in here, use the startup parameters.\r\n\r\nhostname\t\t\"New Gmod Server\"\r\nsv_password\t\t\"\"\r\nsv_loadingurl \"\"\r\n\r\n\/\/ Steam Server List Settings\r\nsv_region \"255\"\r\nsv_lan \"0\"\r\nsv_max_queries_sec_global \"30000\"\r\nsv_max_queries_window \"45\"\r\nsv_max_queries_sec \"5\"\r\n\r\n\/\/ Server Limits\r\nsbox_maxprops\t\t100\r\nsbox_maxragdolls\t5\r\nsbox_maxnpcs\t\t10\r\nsbox_maxballoons\t10\r\nsbox_maxeffects\t\t10\r\nsbox_maxdynamite\t10\r\nsbox_maxlamps\t\t10\r\nsbox_maxthrusters\t10\r\nsbox_maxwheels\t\t10\r\nsbox_maxhoverballs\t10\r\nsbox_maxvehicles\t20\r\nsbox_maxbuttons\t\t10\r\nsbox_maxsents\t\t20\r\nsbox_maxemitters\t5\r\nsbox_godmode\t\t0\r\nsbox_noclip\t\t 0\r\n\r\n\/\/ Network Settings - Please keep these set to default.\r\n\r\nsv_minrate\t\t75000\r\nsv_maxrate\t\t0\r\ngmod_physiterations\t2\r\nnet_splitpacket_maxrate\t45000\r\ndecalfrequency\t\t12 \r\n\r\n\/\/ Execute Ban Files - Please do not edit\r\nexec banned_ip.cfg \r\nexec banned_user.cfg \r\n\r\n\/\/ Add custom lines under here\r\n' > server.cfg", - "container": "ubuntu:16.04", + "script": "#!\/bin\/bash\r\n# steamcmd Base Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n# Image to install with is 'ubuntu:18.04'\r\napt -y update\r\napt -y --no-install-recommends install curl lib32gcc1 ca-certificates\r\n\r\n## just in case someone removed the defaults.\r\nif [ \"${STEAM_USER}\" == \"\" ]; then\r\n STEAM_USER=anonymous\r\n STEAM_PASS=\"\"\r\n STEAM_AUTH=\"\"\r\nfi\r\n\r\n## download and install steamcmd\r\ncd \/tmp\r\nmkdir -p \/mnt\/server\/steamcmd\r\ncurl -sSL -o steamcmd.tar.gz https:\/\/steamcdn-a.akamaihd.net\/client\/installer\/steamcmd_linux.tar.gz\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\r\ncd \/mnt\/server\/steamcmd\r\n\r\n# SteamCMD fails otherwise for some reason, even running as root.\r\n# This is changed at the end of the install process anyways.\r\nchown -R root:root \/mnt\r\nexport HOME=\/mnt\/server\r\n\r\n## install game using steamcmd\r\n.\/steamcmd.sh +login ${STEAM_USER} ${STEAM_PASS} ${STEAM_AUTH} +force_install_dir \/mnt\/server +app_update ${SRCDS_APPID} ${EXTRA_FLAGS} +quit ## other flags may be needed depending on install. looking at you cs 1.6\r\n\r\n## set up 32 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so\r\n\r\n## set up 64 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk64\r\ncp -v linux64\/steamclient.so ..\/.steam\/sdk64\/steamclient.so\r\n\r\n# Creating needed default files for the game\r\ncd \/mnt\/server\/garrysmod\/lua\/autorun\/server\r\necho '\r\n-- Docs: https:\/\/wiki.garrysmod.com\/page\/resource\/AddWorkshop\r\n-- Place the ID of the workshop addon you want to be downloaded to people who join your server, not the collection ID\r\n-- Use https:\/\/beta.configcreator.com\/create\/gmod\/resources.lua to easily create a list based on your collection ID\r\n\r\nresource.AddWorkshop( \"\" )\r\n' > workshop.lua\r\n\r\ncd \/mnt\/server\/garrysmod\/cfg\r\necho '\r\n\/\/ Please do not set RCon in here, use the startup parameters.\r\n\r\nhostname\t\t\"New Gmod Server\"\r\nsv_password\t\t\"\"\r\nsv_loadingurl \"\"\r\n\r\n\/\/ Steam Server List Settings\r\nsv_region \"255\"\r\nsv_lan \"0\"\r\nsv_max_queries_sec_global \"30000\"\r\nsv_max_queries_window \"45\"\r\nsv_max_queries_sec \"5\"\r\n\r\n\/\/ Server Limits\r\nsbox_maxprops\t\t100\r\nsbox_maxragdolls\t5\r\nsbox_maxnpcs\t\t10\r\nsbox_maxballoons\t10\r\nsbox_maxeffects\t\t10\r\nsbox_maxdynamite\t10\r\nsbox_maxlamps\t\t10\r\nsbox_maxthrusters\t10\r\nsbox_maxwheels\t\t10\r\nsbox_maxhoverballs\t10\r\nsbox_maxvehicles\t20\r\nsbox_maxbuttons\t\t10\r\nsbox_maxsents\t\t20\r\nsbox_maxemitters\t5\r\nsbox_godmode\t\t0\r\nsbox_noclip\t\t 0\r\n\r\n\/\/ Network Settings - Please keep these set to default.\r\n\r\nsv_minrate\t\t75000\r\nsv_maxrate\t\t0\r\ngmod_physiterations\t2\r\nnet_splitpacket_maxrate\t45000\r\ndecalfrequency\t\t12 \r\n\r\n\/\/ Execute Ban Files - Please do not edit\r\nexec banned_ip.cfg \r\nexec banned_user.cfg \r\n\r\n\/\/ Add custom lines under here\r\n' > server.cfg", + "container": "ubuntu:18.04", "entrypoint": "bash" } }, @@ -87,4 +87,4 @@ "rules": "required|integer|max:100" } ] -} +} \ No newline at end of file diff --git a/database/seeds/eggs/source-engine/egg-insurgency.json b/database/seeds/eggs/source-engine/egg-insurgency.json index 950743fdd..7f0c76be2 100644 --- a/database/seeds/eggs/source-engine/egg-insurgency.json +++ b/database/seeds/eggs/source-engine/egg-insurgency.json @@ -3,7 +3,7 @@ "meta": { "version": "PTDL_v1" }, - "exported_at": "2018-01-21T16:59:48-06:00", + "exported_at": "2019-12-08T10:57:32-05:00", "name": "Insurgency", "author": "support@pterodactyl.io", "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.", @@ -17,8 +17,8 @@ }, "scripts": { "installation": { - "script": "#!\/bin\/bash\n# SRCDS Base Installation Script\n#\n# Server Files: \/mnt\/server\napt -y update\napt -y --no-install-recommends install curl lib32gcc1 ca-certificates\n\ncd \/tmp\ncurl -sSL -o steamcmd.tar.gz http:\/\/media.steampowered.com\/installer\/steamcmd_linux.tar.gz\n\nmkdir -p \/mnt\/server\/steamcmd\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\ncd \/mnt\/server\/steamcmd\n\n# SteamCMD fails otherwise for some reason, even running as root.\n# This is changed at the end of the install process anyways.\nchown -R root:root \/mnt\n\nexport HOME=\/mnt\/server\n.\/steamcmd.sh +login anonymous +force_install_dir \/mnt\/server +app_update ${SRCDS_APPID} +quit\n\nmkdir -p \/mnt\/server\/.steam\/sdk32\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so", - "container": "ubuntu:16.04", + "script": "#!\/bin\/bash\r\n# steamcmd Base Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n# Image to install with is 'ubuntu:18.04'\r\napt -y update\r\napt -y --no-install-recommends install curl lib32gcc1 ca-certificates\r\n\r\n## just in case someone removed the defaults.\r\nif [ \"${STEAM_USER}\" == \"\" ]; then\r\n STEAM_USER=anonymous\r\n STEAM_PASS=\"\"\r\n STEAM_AUTH=\"\"\r\nfi\r\n\r\n## download and install steamcmd\r\ncd \/tmp\r\nmkdir -p \/mnt\/server\/steamcmd\r\ncurl -sSL -o steamcmd.tar.gz https:\/\/steamcdn-a.akamaihd.net\/client\/installer\/steamcmd_linux.tar.gz\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\r\ncd \/mnt\/server\/steamcmd\r\n\r\n# SteamCMD fails otherwise for some reason, even running as root.\r\n# This is changed at the end of the install process anyways.\r\nchown -R root:root \/mnt\r\nexport HOME=\/mnt\/server\r\n\r\n## install game using steamcmd\r\n.\/steamcmd.sh +login ${STEAM_USER} ${STEAM_PASS} ${STEAM_AUTH} +force_install_dir \/mnt\/server +app_update ${SRCDS_APPID} ${EXTRA_FLAGS} +quit ## other flags may be needed depending on install. looking at you cs 1.6\r\n\r\n## set up 32 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so\r\n\r\n## set up 64 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk64\r\ncp -v linux64\/steamclient.so ..\/.steam\/sdk64\/steamclient.so", + "container": "ubuntu:18.04", "entrypoint": "bash" } }, @@ -51,4 +51,4 @@ "rules": "required|regex:\/^(\\w{1,20})$\/" } ] -} +} \ No newline at end of file diff --git a/database/seeds/eggs/source-engine/egg-team-fortress2.json b/database/seeds/eggs/source-engine/egg-team-fortress2.json index ae443370c..159e7bf9b 100644 --- a/database/seeds/eggs/source-engine/egg-team-fortress2.json +++ b/database/seeds/eggs/source-engine/egg-team-fortress2.json @@ -3,7 +3,7 @@ "meta": { "version": "PTDL_v1" }, - "exported_at": "2018-01-21T16:59:45-06:00", + "exported_at": "2019-12-08T10:58:48-05:00", "name": "Team Fortress 2", "author": "support@pterodactyl.io", "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.", @@ -17,8 +17,8 @@ }, "scripts": { "installation": { - "script": "#!\/bin\/bash\n# SRCDS Base Installation Script\n#\n# Server Files: \/mnt\/server\napt -y update\napt -y --no-install-recommends install curl lib32gcc1 ca-certificates\n\ncd \/tmp\ncurl -sSL -o steamcmd.tar.gz http:\/\/media.steampowered.com\/installer\/steamcmd_linux.tar.gz\n\nmkdir -p \/mnt\/server\/steamcmd\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\ncd \/mnt\/server\/steamcmd\n\n# SteamCMD fails otherwise for some reason, even running as root.\n# This is changed at the end of the install process anyways.\nchown -R root:root \/mnt\n\nexport HOME=\/mnt\/server\n.\/steamcmd.sh +login anonymous +force_install_dir \/mnt\/server +app_update ${SRCDS_APPID} +quit\n\nmkdir -p \/mnt\/server\/.steam\/sdk32\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so", - "container": "ubuntu:16.04", + "script": "#!\/bin\/bash\r\n# steamcmd Base Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n# Image to install with is 'ubuntu:18.04'\r\napt -y update\r\napt -y --no-install-recommends install curl lib32gcc1 ca-certificates\r\n\r\n## just in case someone removed the defaults.\r\nif [ \"${STEAM_USER}\" == \"\" ]; then\r\n STEAM_USER=anonymous\r\n STEAM_PASS=\"\"\r\n STEAM_AUTH=\"\"\r\nfi\r\n\r\n## download and install steamcmd\r\ncd \/tmp\r\nmkdir -p \/mnt\/server\/steamcmd\r\ncurl -sSL -o steamcmd.tar.gz https:\/\/steamcdn-a.akamaihd.net\/client\/installer\/steamcmd_linux.tar.gz\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\r\ncd \/mnt\/server\/steamcmd\r\n\r\n# SteamCMD fails otherwise for some reason, even running as root.\r\n# This is changed at the end of the install process anyways.\r\nchown -R root:root \/mnt\r\nexport HOME=\/mnt\/server\r\n\r\n## install game using steamcmd\r\n.\/steamcmd.sh +login ${STEAM_USER} ${STEAM_PASS} ${STEAM_AUTH} +force_install_dir \/mnt\/server +app_update ${SRCDS_APPID} ${EXTRA_FLAGS} +quit ## other flags may be needed depending on install. looking at you cs 1.6\r\n\r\n## set up 32 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so\r\n\r\n## set up 64 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk64\r\ncp -v linux64\/steamclient.so ..\/.steam\/sdk64\/steamclient.so", + "container": "ubuntu:18.04", "entrypoint": "bash" } }, diff --git a/docker-compose.example.yml b/docker-compose.example.yml index eb1bfaddf..7f6d4ea1b 100644 --- a/docker-compose.example.yml +++ b/docker-compose.example.yml @@ -1,7 +1,7 @@ version: '2' services: database: - image: mariadb + image: mariadb:10.4 volumes: - "/srv/pterodactyl/database:/var/lib/mysql" environment: @@ -68,7 +68,7 @@ services: - "MAIL_PASSWORD=''" - "MAIL_ENCRYPTION=true" ## certbot settings - Used to automatically generate ssl certs and - - "LE_EMAIL=''" ## leave blank unless you aree generating certs. + # - "LE_EMAIL=" ## uncomment if you are using ssl networks: default: diff --git a/.dev/docker/README.md b/docker/README.md similarity index 100% rename from .dev/docker/README.md rename to docker/README.md diff --git a/.dev/docker/default.conf b/docker/default.conf similarity index 96% rename from .dev/docker/default.conf rename to docker/default.conf index 0944bf799..b6105e5fa 100644 --- a/.dev/docker/default.conf +++ b/docker/default.conf @@ -31,7 +31,7 @@ server { location ~ \.php$ { fastcgi_split_path_info ^(.+\.php)(/.+)$; # the fastcgi_pass path needs to be changed accordingly when using CentOS - fastcgi_pass unix:/var/run/php/php-fpm7.2.sock; + fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param PHP_VALUE "upload_max_filesize = 100M \n post_max_size=100M"; diff --git a/.dev/docker/default_ssl.conf b/docker/default_ssl.conf similarity index 96% rename from .dev/docker/default_ssl.conf rename to docker/default_ssl.conf index c2c2b6df0..9ec5c10db 100644 --- a/.dev/docker/default_ssl.conf +++ b/docker/default_ssl.conf @@ -1,4 +1,4 @@ -# If using Ubuntu this file should be placed in: +# If using Ubuntu this file should be placed in: # /etc/nginx/sites-available/ # server { @@ -49,7 +49,7 @@ server { location ~ \.php$ { fastcgi_split_path_info ^(.+\.php)(/.+)$; - fastcgi_pass unix:/var/run/php/php-fpm7.2.sock; + fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param PHP_VALUE "upload_max_filesize = 100M \n post_max_size=100M"; diff --git a/.dev/docker/entrypoint.sh b/docker/entrypoint.sh similarity index 75% rename from .dev/docker/entrypoint.sh rename to docker/entrypoint.sh index c2b58375a..84cf19576 100644 --- a/.dev/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -4,19 +4,19 @@ cd /app mkdir -p /var/log/panel/logs/ /var/log/supervisord/ /var/log/nginx/ /var/log/php7/ \ -&& rmdir /app/storage/logs/ \ +&& rm -rf /app/storage/logs/ \ && chmod 777 /var/log/panel/logs/ \ && ln -s /var/log/panel/logs/ /app/storage/ ## check for .env file and generate app keys if missing if [ -f /app/var/.env ]; then echo "external vars exist." - rm /app/.env + rm -rf /app/.env ln -s /app/var/.env /app/ else echo "external vars don't exist." - rm /app/.env + rm -rf /app/.env touch /app/var/.env ## manually generate a key because key generate --force fails @@ -31,15 +31,20 @@ fi echo "Checking if https is required." if [ -f /etc/nginx/conf.d/default.conf ]; then echo "Using nginx config already in place." + if [ $LE_EMAIL ]; then + echo "Checking for cert update" + certbot certonly -d $(echo $APP_URL | sed 's~http[s]*://~~g') --standalone -m $LE_EMAIL --agree-tos -n + else + echo "No letsencrypt email is set" + fi else echo "Checking if letsencrypt email is set." if [ -z $LE_EMAIL ]; then - echo "No letsencrypt email is set Failing to http." - cp .dev/docker/default.conf /etc/nginx/conf.d/default.conf - + echo "No letsencrypt email is set using http config." + cp docker/default.conf /etc/nginx/conf.d/default.conf else echo "writing ssl config" - cp .dev/docker/default_ssl.conf /etc/nginx/conf.d/default.conf + cp docker/default_ssl.conf /etc/nginx/conf.d/default.conf echo "updating ssl config for domain" sed -i "s||$(echo $APP_URL | sed 's~http[s]*://~~g')|g" /etc/nginx/conf.d/default.conf echo "generating certs" @@ -66,5 +71,10 @@ php artisan db:seed --force echo -e "Starting cron jobs." crond -L /var/log/crond -l 5 +## install yarn stuff +yarn install --production +yarn add cross-env +yarn run build:production + echo -e "Starting supervisord." exec "$@" \ No newline at end of file diff --git a/.dev/docker/supervisord.conf b/docker/supervisord.conf similarity index 91% rename from .dev/docker/supervisord.conf rename to docker/supervisord.conf index f2fd3a1b5..da6823aeb 100644 --- a/.dev/docker/supervisord.conf +++ b/docker/supervisord.conf @@ -20,12 +20,12 @@ supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL for a unix socket [program:php-fpm] -command=/usr/sbin/php-fpm7 -F +command=/usr/local/sbin/php-fpm -F autostart=true autorestart=true [program:queue-worker] -command=/usr/bin/php /app/artisan queue:work --queue=high,standard,low --sleep=3 --tries=3 +command=/usr/local/bin/php /app/artisan queue:work --queue=high,standard,low --sleep=3 --tries=3 user=nginx autostart=true autorestart=true diff --git a/.dev/docker/www.conf b/docker/www.conf similarity index 78% rename from .dev/docker/www.conf rename to docker/www.conf index 88142564c..c0c17903f 100644 --- a/.dev/docker/www.conf +++ b/docker/www.conf @@ -1,9 +1,9 @@ -[pterodactyl] +[www] user = nginx group = nginx -listen = /var/run/php/php-fpm7.2.sock +listen = 127.0.0.1:9000 listen.owner = nginx listen.group = nginx listen.mode = 0750 diff --git a/public/themes/pterodactyl/js/admin/new-server.js b/public/themes/pterodactyl/js/admin/new-server.js index 5a7393b4f..3e0718a46 100644 --- a/public/themes/pterodactyl/js/admin/new-server.js +++ b/public/themes/pterodactyl/js/admin/new-server.js @@ -21,65 +21,29 @@ $(document).ready(function() { $('#pNestId').select2({ placeholder: 'Select a Nest', }).change(); + $('#pEggId').select2({ placeholder: 'Select a Nest Egg', }); + $('#pPackId').select2({ placeholder: 'Select a Service Pack', }); + $('#pNodeId').select2({ placeholder: 'Select a Node', }).change(); + $('#pAllocation').select2({ placeholder: 'Select a Default Allocation', }); + $('#pAllocationAdditional').select2({ placeholder: 'Select Additional Allocations', }); - - $('#pUserId').select2({ - ajax: { - url: '/admin/users/accounts.json', - dataType: 'json', - delay: 250, - data: function (params) { - return { - q: params.term, // search term - page: params.page, - }; - }, - processResults: function (data, params) { - return { results: data }; - }, - cache: true, - }, - escapeMarkup: function (markup) { return markup; }, - minimumInputLength: 2, - templateResult: function (data) { - if (data.loading) return data.text; - - return '
\ - User Image \ - \ - ' + data.name_first + ' ' + data.name_last +' \ - \ - ' + data.email + ' - ' + data.username + ' \ -
'; - }, - templateSelection: function (data) { - return '
\ - \ - User Image \ - \ - \ - ' + data.name_first + ' ' + data.name_last + ' (' + data.email + ') \ - \ -
'; - } - }); }); -var lastActiveBox = null; +let lastActiveBox = null; $(document).on('click', function (event) { if (lastActiveBox !== null) { lastActiveBox.removeClass('box-primary'); @@ -97,10 +61,8 @@ $('#pNodeId').on('change', function () { data: v.allocations, placeholder: 'Select a Default Allocation', }); - $('#pAllocationAdditional').html('').select2({ - data: v.allocations, - placeholder: 'Select Additional Allocations', - }) + + updateAdditionalAllocations(); } }); }); @@ -117,8 +79,8 @@ $('#pNestId').on('change', function (event) { }); $('#pEggId').on('change', function (event) { - var parentChain = _.get(Pterodactyl.nests, $('#pNestId').val(), null); - var objectChain = _.get(parentChain, 'eggs.' + $(this).val(), null); + let parentChain = _.get(Pterodactyl.nests, $('#pNestId').val(), null); + let objectChain = _.get(parentChain, 'eggs.' + $(this).val(), null); $('#pDefaultContainer').val(_.get(objectChain, 'docker_image', 'not defined!')); @@ -139,10 +101,13 @@ $('#pEggId').on('change', function (event) { ), }); + const variableIds = {}; $('#appendVariablesTo').html(''); $.each(_.get(objectChain, 'variables', []), function (i, item) { - var isRequired = (item.required === 1) ? 'Required ' : ''; - var dataAppend = ' \ + variableIds[item.env_variable] = 'var_ref_' + item.id; + + let isRequired = (item.required === 1) ? 'Required ' : ''; + let dataAppend = ' \
\ \ \ @@ -153,4 +118,86 @@ $('#pEggId').on('change', function (event) { '; $('#appendVariablesTo').append(dataAppend); }); + + // If you receive a warning on this line, it should be fine to ignore. this function is + // defined in "resources/views/admin/servers/new.blade.php" near the bottom of the file. + serviceVariablesUpdated($('#pEggId').val(), variableIds); }); + +$('#pAllocation').on('change', function () { + updateAdditionalAllocations(); +}); + +function updateAdditionalAllocations() { + let currentAllocation = $('#pAllocation').val(); + let currentNode = $('#pNodeId').val(); + + $.each(Pterodactyl.nodeData, function (i, v) { + if (v.id == currentNode) { + let allocations = []; + + for (let i = 0; i < v.allocations.length; i++) { + const allocation = v.allocations[i]; + + if (allocation.id != currentAllocation) { + allocations.push(allocation); + } + } + + $('#pAllocationAdditional').html('').select2({ + data: allocations, + placeholder: 'Select Additional Allocations', + }); + } + }); +} + +function initUserIdSelect(data) { + $('#pUserId').select2({ + ajax: { + url: '/admin/users/accounts.json', + dataType: 'json', + delay: 250, + + data: function (params) { + return { + q: params.term, // search term + page: params.page, + }; + }, + + processResults: function (data, params) { + return { results: data }; + }, + + cache: true, + }, + + data: data, + escapeMarkup: function (markup) { return markup; }, + minimumInputLength: 2, + + templateResult: function (data) { + if (data.loading) return data.text; + + return '
\ + User Image \ + \ + ' + data.name_first + ' ' + data.name_last +' \ + \ + ' + data.email + ' - ' + data.username + ' \ +
'; + }, + + templateSelection: function (data) { + return '
\ + \ + User Image \ + \ + \ + ' + data.name_first + ' ' + data.name_last + ' (' + data.email + ') \ + \ +
'; + } + }); +} diff --git a/resources/scripts/api/http.ts b/resources/scripts/api/http.ts index d839887b0..c02714968 100644 --- a/resources/scripts/api/http.ts +++ b/resources/scripts/api/http.ts @@ -12,13 +12,17 @@ const http: AxiosInstance = axios.create({ }); http.interceptors.request.use(req => { - store.getActions().progress.startContinuous(); + if (!req.url?.endsWith('/resources') && (req.url?.indexOf('_debugbar') || -1) < 0) { + store.getActions().progress.startContinuous(); + } return req; }); http.interceptors.response.use(resp => { - store.getActions().progress.setComplete(); + if (!resp.request?.url?.endsWith('/resources') && (resp.request?.url?.indexOf('_debugbar') || -1) < 0) { + store.getActions().progress.setComplete(); + } return resp; }); diff --git a/resources/scripts/components/dashboard/search/SearchModal.tsx b/resources/scripts/components/dashboard/search/SearchModal.tsx index af6f6fcca..9c2a4eb9c 100644 --- a/resources/scripts/components/dashboard/search/SearchModal.tsx +++ b/resources/scripts/components/dashboard/search/SearchModal.tsx @@ -11,6 +11,7 @@ import { Server } from '@/api/server/getServer'; import { ApplicationStore } from '@/state'; import { httpErrorToHuman } from '@/api/http'; import { Link } from 'react-router-dom'; +import styled from 'styled-components'; type Props = RequiredModalProps; @@ -18,6 +19,19 @@ interface Values { term: string; } +const ServerResult = styled(Link)` + ${tw`flex items-center bg-neutral-900 p-4 rounded border-l-4 border-neutral-900 no-underline`}; + transition: all 250ms linear; + + &:hover { + ${tw`shadow border-cyan-500`}; + } + + &:not(:last-of-type) { + ${tw`mb-2`}; + } +`; + const SearchWatcher = () => { const { values, submitForm } = useFormikContext(); @@ -91,10 +105,9 @@ export default ({ ...props }: Props) => {
{ servers.map(server => ( - props.onDismissed()} >
@@ -112,7 +125,7 @@ export default ({ ...props }: Props) => { {server.node}
- + )) }
diff --git a/resources/scripts/components/server/ServerConsole.tsx b/resources/scripts/components/server/ServerConsole.tsx index 434589f10..8593216fc 100644 --- a/resources/scripts/components/server/ServerConsole.tsx +++ b/resources/scripts/components/server/ServerConsole.tsx @@ -6,6 +6,7 @@ import { faCircle } from '@fortawesome/free-solid-svg-icons/faCircle'; import classNames from 'classnames'; import { faMemory } from '@fortawesome/free-solid-svg-icons/faMemory'; import { faMicrochip } from '@fortawesome/free-solid-svg-icons/faMicrochip'; +import { faHdd } from '@fortawesome/free-solid-svg-icons/faHdd'; import { bytesToHuman } from '@/helpers'; import SuspenseSpinner from '@/components/elements/SuspenseSpinner'; import TitledGreyBox from '@/components/elements/TitledGreyBox'; @@ -42,6 +43,7 @@ const StopOrKillButton = ({ onPress }: { onPress: (action: PowerAction) => void export default () => { const [ memory, setMemory ] = useState(0); const [ cpu, setCpu ] = useState(0); + const [ disk, setDisk ] = useState(0); const server = ServerContext.useStoreState(state => state.server.data!); const status = ServerContext.useStoreState(state => state.status.value); @@ -58,6 +60,7 @@ export default () => { setMemory(stats.memory_bytes); setCpu(stats.cpu_absolute); + setDisk(stats.disk_bytes); }; const sendPowerCommand = (command: PowerAction) => { @@ -92,6 +95,14 @@ export default () => { />  {status}

+

+ +  {cpu.toFixed(2)} % +

{

-  {cpu.toFixed(2)} % +  {bytesToHuman(disk)} + / {server.limits.disk} MB

diff --git a/resources/scripts/routers/ServerRouter.tsx b/resources/scripts/routers/ServerRouter.tsx index 48d3e6db6..9e10e8e7a 100644 --- a/resources/scripts/routers/ServerRouter.tsx +++ b/resources/scripts/routers/ServerRouter.tsx @@ -3,7 +3,6 @@ import { NavLink, Route, RouteComponentProps, Switch } from 'react-router-dom'; import NavigationBar from '@/components/NavigationBar'; import ServerConsole from '@/components/server/ServerConsole'; import TransitionRouter from '@/TransitionRouter'; -import Spinner from '@/components/elements/Spinner'; import WebsocketHandler from '@/components/server/WebsocketHandler'; import { ServerContext } from '@/state/server'; import DatabasesContainer from '@/components/server/databases/DatabasesContainer'; @@ -17,74 +16,83 @@ import ScheduleEditContainer from '@/components/server/schedules/ScheduleEditCon import UsersContainer from '@/components/server/users/UsersContainer'; import Can from '@/components/elements/Can'; import BackupContainer from '@/components/server/backups/BackupContainer'; +import Spinner from '@/components/elements/Spinner'; const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>) => { const server = ServerContext.useStoreState(state => state.server.data); const getServer = ServerContext.useStoreActions(actions => actions.server.getServer); const clearServerState = ServerContext.useStoreActions(actions => actions.clearServerState); - if (!server) { - getServer(match.params.id); - } + useEffect(() => () => { + clearServerState(); + }, []); - useEffect(() => () => clearServerState(), [ clearServerState ]); + useEffect(() => { + getServer(match.params.id); + + return () => { + clearServerState(); + }; + }, [ match.params.id ]); return ( - -
-
- Console - - File Manager - - - Databases - - - Schedules - - - Users - - - Backups - - - Settings - -
+ {!server ? +
+
- - - - {!server ? -
- -
- : - - - - ( - - - - )} - exact - /> - - - - - - - - } -
+ : + <> + +
+
+ Console + + File Manager + + + Databases + + + Schedules + + + Users + + + Backups + + + Settings + +
+
+
+ + + + + + ( + + + + )} + exact + /> + + + + + + + + + + } ); }; diff --git a/resources/views/admin/eggs/view.blade.php b/resources/views/admin/eggs/view.blade.php index d483f049f..527d0f308 100644 --- a/resources/views/admin/eggs/view.blade.php +++ b/resources/views/admin/eggs/view.blade.php @@ -30,11 +30,6 @@
-
-
- Notice: Editing an Egg or any of the Process Management fields requires that each Daemon be rebooted in order to apply the changes. -
-
@@ -167,11 +162,6 @@
-
-
- Notice: Editing an Egg or any of the Process Management fields requires that each Daemon be rebooted in order to apply the changes. -
-
@endsection diff --git a/resources/views/admin/nodes/view/configuration.blade.php b/resources/views/admin/nodes/view/configuration.blade.php index f1c7b0f34..778c6a057 100644 --- a/resources/views/admin/nodes/view/configuration.blade.php +++ b/resources/views/admin/nodes/view/configuration.blade.php @@ -71,8 +71,7 @@ swal({ type: 'success', title: 'Token created.', - text: 'Your token will expire in 5 minutes.

' + - '

To auto-configure your node run the following command:

npm run configure -- --panel-url {{ config('app.url') }} --token ' + data.token + '

', + text: '

To auto-configure your node run the following command:

cd /srv/wings && ./wings configure --panel-url {{ config('app.url') }} --token ' + data.token + ' --node ' + data.node + '{{ config('app.debug') ? ' --allow-insecure' : '' }}

', html: true }) }).fail(function () { diff --git a/resources/views/admin/servers/new.blade.php b/resources/views/admin/servers/new.blade.php index 7aa4477e9..1d61f1a50 100644 --- a/resources/views/admin/servers/new.blade.php +++ b/resources/views/admin/servers/new.blade.php @@ -26,6 +26,7 @@

Core Details

+
@@ -33,20 +34,23 @@

Character limits: a-z A-Z 0-9 _ - . and [Space] (max 200 characters).

+
- +
+
- - + +

A brief description of this server.

+
- +
@@ -55,6 +59,7 @@
+
@@ -62,6 +67,7 @@

Allocation Management

+
@@ -78,22 +84,26 @@ @endforeach +

The node which this server will be deployed to.

+
- +

The main allocation that will be assigned to this server.

+
- +

Additional allocations to assign to this server on creation.

+
@@ -101,18 +111,20 @@

Application Feature Limits

+
- +
- +

The total number of databases a user is allowed to create for this server. Leave blank to allow unlimited.

+
- +
- +

The total number of allocations a user is allowed to create for this server. Leave blank to allow unlimited.

@@ -126,71 +138,90 @@

Resource Management

+
+
- + %
-

If you do not want to limit CPU usage, set the value to 0. To determine a value, take the number of physical cores and multiply it by 100. For example, on a quad core system (4 * 100 = 400) there is 400% available. To limit a server to using half of a single core, you would set the value to 50. To allow a server to use up to two physical cores, set the value to 200. BlockIO should be a value between 10 and 1000. Please see this documentation for more information about it.

+ +

If you do not want to limit CPU usage, set the value to 0. To determine a value, take the number of physical cores and multiply it by 100. For example, on a quad core system (4 * 100 = 400) there is 400% available. To limit a server to using half of a single core, you would set the value to 50. To allow a server to use up to two physical cores, set the value to 200. BlockIO should be a value between 10 and 1000. Please see this documentation for more information about it.

+
+
- +
+

Advanced: Enter the specific CPU cores that this process can run on, or leave blank to allow all cores. This can be a single number, or a comma seperated list. Example: 0, 0-1,3, or 0,1,3,4.

+
+
- + MB
+
+
- + MB
+ +
+
- - MB + + MB
+
+
- +
-

Advanced: The IO performance of this server relative to other running containers on the system. Value should be between 10 and 1000.

+ +

Advanced: The IO performance of this server relative to other running containers on the system. Value should be between 10 and 1000.

+

Nest Configuration

+
- @foreach($nests as $nest) @endforeach +

Select the Nest that this server will be grouped under.

+
- +

Select the Egg that will define how this server should operate.

+
- +

Select a data pack to be automatically installed on this server when first created.

+
- +
+

If the selected Egg has an install script attached to it, the script will run during install after the pack is installed. If you would like to skip this step, check this box.

+

Docker Configuration

+
@@ -236,23 +274,28 @@
+

Startup Configuration

+
- +

The following data substitutes are available for the startup command: @{{SERVER_MEMORY}}, @{{SERVER_IP}}, and @{{SERVER_PORT}}. They will be replaced with the allocated memory, server IP, and server port respectively.

+

Service Variables

+
+ @section('footer-scripts') - {!! Theme::js('js/keyboard.polyfill.js') !!} + {!! Theme::js('vendor/jquery/jquery.min.js?t={cache-version}') !!} @@ -185,7 +180,7 @@ {!! Theme::js('vendor/bootstrap-notify/bootstrap-notify.min.js?t={cache-version}') !!} {!! Theme::js('vendor/select2/select2.full.min.js?t={cache-version}') !!} {!! Theme::js('js/admin/functions.js?t={cache-version}') !!} - {!! Theme::js('js/autocomplete.js?t={cache-version}') !!} + @if(Auth::user()->root_admin) {!! Theme::js('vendor/particlesjs/particles.min.js?t={cache-version}') !!} {!! Theme::js('vendor/jquery/jquery.min.js?t={cache-version}') !!} @@ -284,7 +284,7 @@ {!! Theme::js('vendor/adminlte/app.min.js?t={cache-version}') !!} {!! Theme::js('vendor/socketio/socket.io.v203.min.js?t={cache-version}') !!} {!! Theme::js('vendor/bootstrap-notify/bootstrap-notify.min.js?t={cache-version}') !!} - {!! Theme::js('js/autocomplete.js?t={cache-version}') !!} + @if(Auth::user()->root_admin)