Merge branch 'develop' into patch-1
This commit is contained in:
commit
7700919d21
58 changed files with 686 additions and 699 deletions
|
@ -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
|
|
@ -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
|
|
@ -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/
|
|
@ -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
|
||||
#####################################################
|
|
@ -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'"
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
16
Dockerfile
16
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" ]
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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' : '';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
88
app/Http/Controllers/Admin/NodeAutoDeployController.php
Normal file
88
app/Http/Controllers/Admin/NodeAutoDeployController.php
Normal file
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Admin;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Pterodactyl\Models\Node;
|
||||
use Pterodactyl\Models\ApiKey;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Illuminate\Contracts\Encryption\Encrypter;
|
||||
use Pterodactyl\Services\Api\KeyCreationService;
|
||||
use Pterodactyl\Repositories\Eloquent\ApiKeyRepository;
|
||||
|
||||
class NodeAutoDeployController extends Controller
|
||||
{
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Api\KeyCreationService
|
||||
*/
|
||||
private $keyCreationService;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Repositories\Eloquent\ApiKeyRepository
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Encryption\Encrypter
|
||||
*/
|
||||
private $encrypter;
|
||||
|
||||
/**
|
||||
* NodeAutoDeployController constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Repositories\Eloquent\ApiKeyRepository $repository
|
||||
* @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
|
||||
* @param \Pterodactyl\Services\Api\KeyCreationService $keyCreationService
|
||||
*/
|
||||
public function __construct(
|
||||
ApiKeyRepository $repository,
|
||||
Encrypter $encrypter,
|
||||
KeyCreationService $keyCreationService
|
||||
) {
|
||||
$this->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),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Api\Application\Nodes;
|
||||
|
||||
use Pterodactyl\Models\Node;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Pterodactyl\Http\Requests\Api\Application\Nodes\GetNodeRequest;
|
||||
use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController;
|
||||
|
||||
class NodeConfigurationController extends ApplicationApiController
|
||||
{
|
||||
/**
|
||||
* Returns the configuration information for a node. This allows for automated deployments
|
||||
* to remote machines so long as an API key is provided to the machine to make the request
|
||||
* with, and the node is known.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Api\Application\Nodes\GetNodeRequest $request
|
||||
* @param \Pterodactyl\Models\Node $node
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function __invoke(GetNodeRequest $request, Node $node)
|
||||
{
|
||||
return JsonResponse::create($node->getConfiguration());
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.')) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
/*
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateBackupsTable extends Migration
|
||||
{
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
use Ramsey\Uuid\Uuid;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Container\Container;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Contracts\Encryption\Encrypter;
|
||||
|
||||
class StoreNodeTokensAsEncryptedValue extends Migration
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
"meta": {
|
||||
"version": "PTDL_v1"
|
||||
},
|
||||
"exported_at": "2017-11-03T22:15:10-05:00",
|
||||
"exported_at": "2020-04-12T16:00:51-07:00",
|
||||
"name": "Bungeecord",
|
||||
"author": "support@pterodactyl.io",
|
||||
"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.",
|
||||
"image": "quay.io\/pterodactyl\/core:java",
|
||||
"startup": "java -Xms128M -Xmx{{SERVER_MEMORY}}M -jar {{SERVER_JARFILE}}",
|
||||
"config": {
|
||||
"files": "{\r\n \"config.yml\": {\r\n \"parser\": \"yaml\",\r\n \"find\": {\r\n \"listeners[0].query_enabled\": true,\r\n \"listeners[0].query_port\": \"{{server.build.default.port}}\",\r\n \"listeners[0].host\": \"0.0.0.0:{{server.build.default.port}}\",\r\n \"servers.*.address\": {\r\n \"127.0.0.1\": \"{{config.docker.interface}}\",\r\n \"localhost\": \"{{config.docker.interface}}\"\r\n }\r\n }\r\n }\r\n}",
|
||||
"files": "{\r\n \"config.yml\": {\r\n \"parser\": \"yaml\",\r\n \"find\": {\r\n \"listeners[0].query_enabled\": true,\r\n \"listeners[0].query_port\": \"{{server.build.default.port}}\",\r\n \"listeners[0].host\": \"0.0.0.0:{{server.build.default.port}}\",\r\n \"servers.*.address\": {\r\n \"regex:^(127\\\\.0\\\\.0\\\\.1|localhost)(:\\\\d{1,5})?$\": \"{{config.docker.interface}}$2\"\r\n }\r\n }\r\n }\r\n}",
|
||||
"startup": "{\r\n \"done\": \"Listening on \",\r\n \"userInteraction\": [\r\n \"Listening on \/0.0.0.0:25577\"\r\n ]\r\n}",
|
||||
"logs": "{\r\n \"custom\": false,\r\n \"location\": \"proxy.log.0\"\r\n}",
|
||||
"stop": "end"
|
||||
|
|
|
@ -3,26 +3,35 @@
|
|||
"meta": {
|
||||
"version": "PTDL_v1"
|
||||
},
|
||||
"exported_at": "2019-02-13T12:30:54-05:00",
|
||||
"exported_at": "2018-10-29T20:51:32+01:00",
|
||||
"name": "Ark: Survival Evolved",
|
||||
"author": "support@pterodactyl.io",
|
||||
"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! \u2014 Gamepedia: ARK",
|
||||
"image": "quay.io\/pterodactyl\/core:source",
|
||||
"startup": ".\/ShooterGame\/Binaries\/Linux\/ShooterGameServer {{SERVER_MAP}}?listen?ServerPassword={{ARK_PASSWORD}}?ServerAdminPassword={{ARK_ADMIN_PASSWORD}}?Port={{SERVER_PORT}}?MaxPlayers={{SERVER_MAX_PLAYERS}}",
|
||||
"startup": "\"cd ShooterGame\/Binaries\/Linux && .\/ShooterGameServer {{SERVER_MAP}}?listen?SessionName='{{SESSION_NAME}}'?ServerPassword={{ARK_PASSWORD}}?ServerAdminPassword={{ARK_ADMIN_PASSWORD}}?Port={{PORT}}?MaxPlayers={{SERVER_MAX_PLAYERS}}?RCONPort={{RCON_PORT}}?QueryPort={{QUERY_PORT}}?RCONEnabled={{ENABLE_RCON}} -server -log\"",
|
||||
"config": {
|
||||
"files": "{}",
|
||||
"startup": "{\r\n \"done\": \"Setting breakpad minidump AppID = 346110\",\r\n \"userInteraction\": []\r\n}",
|
||||
"logs": "{\r\n \"custom\": true,\r\n \"location\": \"logs\/latest.log\"\r\n}",
|
||||
"stop": "quit"
|
||||
"stop": "^C"
|
||||
},
|
||||
"scripts": {
|
||||
"installation": {
|
||||
"script": "#!\/bin\/bash\n# ARK: 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\nmkdir -p \/mnt\/server\/Engine\/Binaries\/ThirdParty\/SteamCMD\/Linux\n\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/Engine\/Binaries\/ThirdParty\/SteamCMD\/Linux\n\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 376030 +quit\n\nmkdir -p \/mnt\/server\/.steam\/sdk32\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so",
|
||||
"script": "#!\/bin\/bash\r\n# ARK: Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napt -y update\r\napt -y --no-install-recommends install curl lib32gcc1 ca-certificates\r\n\r\ncd \/tmp\r\ncurl -sSL -o steamcmd.tar.gz http:\/\/media.steampowered.com\/installer\/steamcmd_linux.tar.gz\r\n\r\nmkdir -p \/mnt\/server\/steamcmd\r\nmkdir -p \/mnt\/server\/Engine\/Binaries\/ThirdParty\/SteamCMD\/Linux\r\n\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/Engine\/Binaries\/ThirdParty\/SteamCMD\/Linux\r\n\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\n\r\nexport HOME=\/mnt\/server\r\n.\/steamcmd.sh +login anonymous +force_install_dir \/mnt\/server +app_update 376030 +quit\r\n\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so\r\n\r\ncd \/mnt\/server\/Engine\/Binaries\/ThirdParty\/SteamCMD\/Linux\r\n\r\nln -sf ..\/..\/..\/..\/..\/Steam\/steamapps steamapps\r\n\r\ncd \/mnt\/server",
|
||||
"container": "ubuntu:16.04",
|
||||
"entrypoint": "bash"
|
||||
}
|
||||
},
|
||||
"variables": [
|
||||
{
|
||||
"name": "Server Name",
|
||||
"description": "ARK server name",
|
||||
"env_variable": "SESSION_NAME",
|
||||
"default_value": "ARK SERVER",
|
||||
"user_viewable": 1,
|
||||
"user_editable": 1,
|
||||
"rules": "required|string|max:128"
|
||||
},
|
||||
{
|
||||
"name": "Server Password",
|
||||
"description": "If specified, players must provide this password to join the server.",
|
||||
|
@ -41,6 +50,42 @@
|
|||
"user_editable": 1,
|
||||
"rules": "nullable|alpha_dash|between:1,100"
|
||||
},
|
||||
{
|
||||
"name": "Server Port",
|
||||
"description": "ARK server port used by client.",
|
||||
"env_variable": "PORT",
|
||||
"default_value": "7777",
|
||||
"user_viewable": 1,
|
||||
"user_editable": 1,
|
||||
"rules": "required|numeric"
|
||||
},
|
||||
{
|
||||
"name": "Use Rcon",
|
||||
"description": "Enable or disable rcon system. (true or false)",
|
||||
"env_variable": "ENABLE_RCON",
|
||||
"default_value": "false",
|
||||
"user_viewable": 1,
|
||||
"user_editable": 1,
|
||||
"rules": "required|string|max:5"
|
||||
},
|
||||
{
|
||||
"name": "Rcon Port",
|
||||
"description": "ARK rcon port used by rcon tools.",
|
||||
"env_variable": "RCON_PORT",
|
||||
"default_value": "27020",
|
||||
"user_viewable": 1,
|
||||
"user_editable": 1,
|
||||
"rules": "required|numeric"
|
||||
},
|
||||
{
|
||||
"name": "Query Port",
|
||||
"description": "ARK query port used by steam server browser and ark client server browser.",
|
||||
"env_variable": "QUERY_PORT",
|
||||
"default_value": "27015",
|
||||
"user_viewable": 1,
|
||||
"user_editable": 1,
|
||||
"rules": "required|numeric"
|
||||
},
|
||||
{
|
||||
"name": "Maximum Players",
|
||||
"description": "Specifies the maximum number of players that can play on the server simultaneously.",
|
||||
|
@ -50,9 +95,18 @@
|
|||
"user_editable": 1,
|
||||
"rules": "required|numeric|digits_between:1,4"
|
||||
},
|
||||
{
|
||||
"name": "App ID",
|
||||
"description": "ARK steam app id for auto updates. Leave blank to avoid auto update.",
|
||||
"env_variable": "SRCDS_APPID",
|
||||
"default_value": "376030",
|
||||
"user_viewable": 1,
|
||||
"user_editable": 0,
|
||||
"rules": "nullable|numeric"
|
||||
},
|
||||
{
|
||||
"name": "Server Map",
|
||||
"description": "Available Maps: TheIsland, TheCenter, Ragnarok, ScorchedEarth_P, Aberration_P",
|
||||
"description": "Available Maps: TheIsland, TheCenter, Ragnarok, ScorchedEarth_P, Aberration_P, Extinction",
|
||||
"env_variable": "SERVER_MAP",
|
||||
"default_value": "TheIsland",
|
||||
"user_viewable": 1,
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"meta": {
|
||||
"version": "PTDL_v1"
|
||||
},
|
||||
"exported_at": "2018-06-19T07:46:06-04:00",
|
||||
"exported_at": "2019-12-08T10:52:19-05:00",
|
||||
"name": "Counter-Strike: Global Offensive",
|
||||
"author": "support@pterodactyl.io",
|
||||
"description": "Counter-Strike: Global Offensive is a multiplayer first-person shooter video game developed by Hidden Path Entertainment and Valve Corporation.",
|
||||
|
@ -17,8 +17,8 @@
|
|||
},
|
||||
"scripts": {
|
||||
"installation": {
|
||||
"script": "#!\/bin\/bash\n# CSGO 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 740 +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"
|
||||
}
|
||||
},
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"meta": {
|
||||
"version": "PTDL_v1"
|
||||
},
|
||||
"exported_at": "2018-01-21T16:59:45-06:00",
|
||||
"exported_at": "2019-12-08T10:54:26-05:00",
|
||||
"name": "Custom Source Engine Game",
|
||||
"author": "support@pterodactyl.io",
|
||||
"description": "This option allows modifying the startup arguments and other details to run a custom SRCDS based game on the panel.",
|
||||
|
@ -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"
|
||||
}
|
||||
},
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"meta": {
|
||||
"version": "PTDL_v1"
|
||||
},
|
||||
"exported_at": "2019-02-16T14:20:52-05:00",
|
||||
"exported_at": "2019-12-08T10:56:42-05:00",
|
||||
"name": "Garrys Mod",
|
||||
"author": "support@pterodactyl.io",
|
||||
"description": "Garrys Mod, is a sandbox physics game created by Garry Newman, and developed by his company, Facepunch Studios.",
|
||||
|
@ -17,8 +17,8 @@
|
|||
},
|
||||
"scripts": {
|
||||
"installation": {
|
||||
"script": "#!\/bin\/bash\r\n# Garry's Mod Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napt -y update\r\napt -y --no-install-recommends install curl lib32gcc1 ca-certificates\r\n\r\ncd \/tmp\r\ncurl -sSL -o steamcmd.tar.gz http:\/\/media.steampowered.com\/installer\/steamcmd_linux.tar.gz\r\n\r\nmkdir -p \/mnt\/server\/steamcmd\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\n\r\nexport HOME=\/mnt\/server\r\n.\/steamcmd.sh +login anonymous +force_install_dir \/mnt\/server +app_update 4020 +quit\r\n\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/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: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"
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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";
|
|
@ -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";
|
|
@ -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|<domain>|$(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 "$@"
|
|
@ -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
|
|
@ -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
|
|
@ -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 '<div class="user-block"> \
|
||||
<img class="img-circle img-bordered-xs" src="https://www.gravatar.com/avatar/' + data.md5 + '?s=120" alt="User Image"> \
|
||||
<span class="username"> \
|
||||
<a href="#">' + data.name_first + ' ' + data.name_last +'</a> \
|
||||
</span> \
|
||||
<span class="description"><strong>' + data.email + '</strong> - ' + data.username + '</span> \
|
||||
</div>';
|
||||
},
|
||||
templateSelection: function (data) {
|
||||
return '<div> \
|
||||
<span> \
|
||||
<img class="img-rounded img-bordered-xs" src="https://www.gravatar.com/avatar/' + data.md5 + '?s=120" style="height:28px;margin-top:-4px;" alt="User Image"> \
|
||||
</span> \
|
||||
<span style="padding-left:5px;"> \
|
||||
' + data.name_first + ' ' + data.name_last + ' (<strong>' + data.email + '</strong>) \
|
||||
</span> \
|
||||
</div>';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
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) ? '<span class="label label-danger">Required</span> ' : '';
|
||||
var dataAppend = ' \
|
||||
variableIds[item.env_variable] = 'var_ref_' + item.id;
|
||||
|
||||
let isRequired = (item.required === 1) ? '<span class="label label-danger">Required</span> ' : '';
|
||||
let dataAppend = ' \
|
||||
<div class="form-group col-sm-6"> \
|
||||
<label for="var_ref_' + item.id + '" class="control-label">' + isRequired + item.name + '</label> \
|
||||
<input type="text" id="var_ref_' + item.id + '" autocomplete="off" name="environment[' + item.env_variable + ']" class="form-control" value="' + item.default_value + '" /> \
|
||||
|
@ -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 '<div class="user-block"> \
|
||||
<img class="img-circle img-bordered-xs" src="https://www.gravatar.com/avatar/' + data.md5 + '?s=120" alt="User Image"> \
|
||||
<span class="username"> \
|
||||
<a href="#">' + data.name_first + ' ' + data.name_last +'</a> \
|
||||
</span> \
|
||||
<span class="description"><strong>' + data.email + '</strong> - ' + data.username + '</span> \
|
||||
</div>';
|
||||
},
|
||||
|
||||
templateSelection: function (data) {
|
||||
return '<div> \
|
||||
<span> \
|
||||
<img class="img-rounded img-bordered-xs" src="https://www.gravatar.com/avatar/' + data.md5 + '?s=120" style="height:28px;margin-top:-4px;" alt="User Image"> \
|
||||
</span> \
|
||||
<span style="padding-left:5px;"> \
|
||||
' + data.name_first + ' ' + data.name_last + ' (<strong>' + data.email + '</strong>) \
|
||||
</span> \
|
||||
</div>';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -12,13 +12,17 @@ const http: AxiosInstance = axios.create({
|
|||
});
|
||||
|
||||
http.interceptors.request.use(req => {
|
||||
if (!req.url?.endsWith('/resources') && (req.url?.indexOf('_debugbar') || -1) < 0) {
|
||||
store.getActions().progress.startContinuous();
|
||||
}
|
||||
|
||||
return req;
|
||||
});
|
||||
|
||||
http.interceptors.response.use(resp => {
|
||||
if (!resp.request?.url?.endsWith('/resources') && (resp.request?.url?.indexOf('_debugbar') || -1) < 0) {
|
||||
store.getActions().progress.setComplete();
|
||||
}
|
||||
|
||||
return resp;
|
||||
});
|
||||
|
|
|
@ -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<Values>();
|
||||
|
||||
|
@ -91,10 +105,9 @@ export default ({ ...props }: Props) => {
|
|||
<div className={'mt-6'}>
|
||||
{
|
||||
servers.map(server => (
|
||||
<Link
|
||||
<ServerResult
|
||||
key={server.uuid}
|
||||
to={`/server/${server.id}`}
|
||||
className={'flex items-center block bg-neutral-900 p-4 rounded border-l-4 border-neutral-900 no-underline hover:shadow hover:border-cyan-500 transition-colors duration-250'}
|
||||
onClick={() => props.onDismissed()}
|
||||
>
|
||||
<div>
|
||||
|
@ -112,7 +125,7 @@ export default ({ ...props }: Props) => {
|
|||
{server.node}
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
</ServerResult>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
|
|
|
@ -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}
|
||||
</p>
|
||||
<p className={'text-xs mt-2'}>
|
||||
<FontAwesomeIcon
|
||||
icon={faMicrochip}
|
||||
fixedWidth={true}
|
||||
className={'mr-1'}
|
||||
/>
|
||||
{cpu.toFixed(2)} %
|
||||
</p>
|
||||
<p className={'text-xs mt-2'}>
|
||||
<FontAwesomeIcon
|
||||
icon={faMemory}
|
||||
|
@ -103,11 +114,12 @@ export default () => {
|
|||
</p>
|
||||
<p className={'text-xs mt-2'}>
|
||||
<FontAwesomeIcon
|
||||
icon={faMicrochip}
|
||||
icon={faHdd}
|
||||
fixedWidth={true}
|
||||
className={'mr-1'}
|
||||
/>
|
||||
{cpu.toFixed(2)} %
|
||||
{bytesToHuman(disk)}
|
||||
<span className={'text-neutral-500'}>/ {server.limits.disk} MB</span>
|
||||
</p>
|
||||
</TitledGreyBox>
|
||||
<Can action={[ 'control.start', 'control.stop', 'control.restart' ]} matchAny={true}>
|
||||
|
|
|
@ -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,21 +16,34 @@ 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 (
|
||||
<React.Fragment key={'server-router'}>
|
||||
<NavigationBar/>
|
||||
{!server ?
|
||||
<div className={'flex justify-center m-20'}>
|
||||
<Spinner size={'large'}/>
|
||||
</div>
|
||||
:
|
||||
<>
|
||||
<CSSTransition timeout={250} classNames={'fade'} appear={true} in={true}>
|
||||
<div id={'sub-navigation'}>
|
||||
<div className={'items'}>
|
||||
|
@ -59,11 +71,6 @@ const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>)
|
|||
</CSSTransition>
|
||||
<WebsocketHandler/>
|
||||
<TransitionRouter>
|
||||
{!server ?
|
||||
<div className={'flex justify-center m-20'}>
|
||||
<Spinner size={'large'}/>
|
||||
</div>
|
||||
:
|
||||
<Switch location={location}>
|
||||
<Route path={`${match.path}`} component={ServerConsole} exact/>
|
||||
<Route path={`${match.path}/files`} component={FileManagerContainer} exact/>
|
||||
|
@ -83,8 +90,9 @@ const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>)
|
|||
<Route path={`${match.path}/backups`} component={BackupContainer} exact/>
|
||||
<Route path={`${match.path}/settings`} component={SettingsContainer} exact/>
|
||||
</Switch>
|
||||
}
|
||||
</TransitionRouter>
|
||||
</>
|
||||
}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -30,11 +30,6 @@
|
|||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12">
|
||||
<div class="alert alert-info">
|
||||
<strong>Notice:</strong> Editing an Egg or any of the Process Management fields <em>requires</em> that each Daemon be rebooted in order to apply the changes.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<form action="{{ route('admin.nests.egg.view', $egg->id) }}" enctype="multipart/form-data" method="POST">
|
||||
<div class="row">
|
||||
|
@ -167,11 +162,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12">
|
||||
<div class="alert alert-info">
|
||||
<strong>Notice:</strong> Editing an Egg or any of the Process Management fields <em>requires</em> that each Daemon be rebooted in order to apply the changes.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@endsection
|
||||
|
|
|
@ -71,8 +71,7 @@
|
|||
swal({
|
||||
type: 'success',
|
||||
title: 'Token created.',
|
||||
text: 'Your token will expire <strong>in 5 minutes.</strong><br /><br />' +
|
||||
'<p>To auto-configure your node run the following command:<br /><small><pre>npm run configure -- --panel-url {{ config('app.url') }} --token ' + data.token + '</pre></small></p>',
|
||||
text: '<p>To auto-configure your node run the following command:<br /><small><pre>cd /srv/wings && ./wings configure --panel-url {{ config('app.url') }} --token ' + data.token + ' --node ' + data.node + '{{ config('app.debug') ? ' --allow-insecure' : '' }}</pre></small></p>',
|
||||
html: true
|
||||
})
|
||||
}).fail(function () {
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Core Details</h3>
|
||||
</div>
|
||||
|
||||
<div class="box-body row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
|
@ -33,20 +34,23 @@
|
|||
<input type="text" class="form-control" id="pName" name="name" value="{{ old('name') }}" placeholder="Server Name">
|
||||
<p class="small text-muted no-margin">Character limits: <code>a-z A-Z 0-9 _ - .</code> and <code>[Space]</code> (max 200 characters).</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="pUserId">Server Owner</label>
|
||||
<select class="form-control" style="padding-left:0;" name="owner_id" id="pUserId"></select>
|
||||
<select id="pUserId" name="owner_id" class="form-control" style="padding-left:0;"></select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="description" class="control-label">Server Description</label>
|
||||
<textarea name="description" rows="3" class="form-control">{{ old('description') }}</textarea>
|
||||
<label for="pDescription" class="control-label">Server Description</label>
|
||||
<textarea id="pDescription" name="description" rows="3" class="form-control">{{ old('description') }}</textarea>
|
||||
<p class="text-muted small">A brief description of this server.</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="checkbox checkbox-primary no-margin-bottom">
|
||||
<input id="pStartOnCreation" name="start_on_completion" type="checkbox" value="1" checked />
|
||||
<input id="pStartOnCreation" name="start_on_completion" type="checkbox" {{ \Pterodactyl\Helpers\Utilities::checked('start_on_completion', 1) }} />
|
||||
<label for="pStartOnCreation" class="strong">Start Server when Installed</label>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -55,6 +59,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="box">
|
||||
|
@ -62,6 +67,7 @@
|
|||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Allocation Management</h3>
|
||||
</div>
|
||||
|
||||
<div class="box-body row">
|
||||
<div class="form-group col-sm-4">
|
||||
<label for="pNodeId">Node</label>
|
||||
|
@ -78,22 +84,26 @@
|
|||
</optgroup>
|
||||
@endforeach
|
||||
</select>
|
||||
|
||||
<p class="small text-muted no-margin">The node which this server will be deployed to.</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-sm-4">
|
||||
<label for="pAllocation">Default Allocation</label>
|
||||
<select name="allocation_id" id="pAllocation" class="form-control"></select>
|
||||
<select id="pAllocation" name="allocation_id" class="form-control"></select>
|
||||
<p class="small text-muted no-margin">The main allocation that will be assigned to this server.</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-sm-4">
|
||||
<label for="pAllocationAdditional">Additional Allocation(s)</label>
|
||||
<select name="allocation_additional[]" id="pAllocationAdditional" class="form-control" multiple></select>
|
||||
<select id="pAllocationAdditional" name="allocation_additional[]" class="form-control" multiple></select>
|
||||
<p class="small text-muted no-margin">Additional allocations to assign to this server on creation.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="box">
|
||||
|
@ -101,18 +111,20 @@
|
|||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Application Feature Limits</h3>
|
||||
</div>
|
||||
|
||||
<div class="box-body row">
|
||||
<div class="form-group col-xs-6">
|
||||
<label for="cpu" class="control-label">Database Limit</label>
|
||||
<label for="pDatabaseLimit" class="control-label">Database Limit</label>
|
||||
<div>
|
||||
<input type="text" name="database_limit" class="form-control" value="{{ old('database_limit', 0) }}"/>
|
||||
<input type="text" id="pDatabaseLimit" name="database_limit" class="form-control" value="{{ old('database_limit', 0) }}"/>
|
||||
</div>
|
||||
<p class="text-muted small">The total number of databases a user is allowed to create for this server. Leave blank to allow unlimited.</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-xs-6">
|
||||
<label for="cpu" class="control-label">Allocation Limit</label>
|
||||
<label for="pAllocationLimit" class="control-label">Allocation Limit</label>
|
||||
<div>
|
||||
<input type="text" name="allocation_limit" class="form-control" value="{{ old('allocation_limit', 0) }}"/>
|
||||
<input type="text" id="pAllocationLimit" name="allocation_limit" class="form-control" value="{{ old('allocation_limit', 0) }}"/>
|
||||
</div>
|
||||
<p class="text-muted small">The total number of allocations a user is allowed to create for this server. Leave blank to allow unlimited.</p>
|
||||
</div>
|
||||
|
@ -126,71 +138,90 @@
|
|||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Resource Management</h3>
|
||||
</div>
|
||||
|
||||
<div class="box-body row">
|
||||
<div class="form-group col-xs-6">
|
||||
<label for="pCPU">CPU Limit</label>
|
||||
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" value="{{ old('cpu', 0) }}" name="cpu" id="pCPU" />
|
||||
<input type="text" id="pCPU" name="cpu" class="form-control" value="{{ old('cpu', 0) }}" />
|
||||
<span class="input-group-addon">%</span>
|
||||
</div>
|
||||
|
||||
<p class="text-muted small">If you do not want to limit CPU usage, set the value to <code>0</code>. To determine a value, take the number of <em>physical</em> cores and multiply it by 100. For example, on a quad core system <code>(4 * 100 = 400)</code> there is <code>400%</code> available. To limit a server to using half of a single core, you would set the value to <code>50</code>. To allow a server to use up to two physical cores, set the value to <code>200</code>. BlockIO should be a value between <code>10</code> and <code>1000</code>. Please see <a href="https://docs.docker.com/engine/reference/run/#/block-io-bandwidth-blkio-constraint" target="_blank">this documentation</a> for more information about it.<p>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-xs-6">
|
||||
<label for="pThreads">CPU Threads</label>
|
||||
|
||||
<div>
|
||||
<input type="text" class="form-control" value="{{ old('threads') }}" name="threads" id="pThreads" />
|
||||
<input type="text" id="pThreads" name="threads" class="form-control" value="{{ old('threads') }}" />
|
||||
</div>
|
||||
|
||||
<p class="text-muted small"><strong>Advanced:</strong> 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: <code>0</code>, <code>0-1,3</code>, or <code>0,1,3,4</code>.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="box-body row">
|
||||
<div class="form-group col-xs-6">
|
||||
<label for="pMemory">Memory</label>
|
||||
|
||||
<div class="input-group">
|
||||
<input type="text" value="{{ old('memory') }}" class="form-control" name="memory" id="pMemory" />
|
||||
<input type="text" id="pMemory" name="memory" class="form-control" value="{{ old('memory') }}" />
|
||||
<span class="input-group-addon">MB</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-xs-6">
|
||||
<label for="pSwap">Swap</label>
|
||||
|
||||
<div class="input-group">
|
||||
<input type="text" value="{{ old('swap', 0) }}" class="form-control" name="swap" id="pSwap" />
|
||||
<input type="text" id="pSwap" name="swap" class="form-control" value="{{ old('swap', 0) }}" />
|
||||
<span class="input-group-addon">MB</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="box-footer no-border no-pad-top no-pad-bottom">
|
||||
<p class="text-muted small">If you do not want to assign swap space to a server, simply put <code>0</code> for the value, or <code>-1</code> to allow unlimited swap space. If you want to disable memory limiting on a server, simply enter <code>0</code> into the memory field.<p>
|
||||
</div>
|
||||
|
||||
<div class="box-body row">
|
||||
<div class="form-group col-xs-6">
|
||||
<label for="pDisk">Disk Space</label>
|
||||
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" value="{{ old('disk') }}" name="disk" id="pDisk" />
|
||||
<input type="text" id="pDisk" name="disk" class="form-control" value="{{ old('disk') }}" />
|
||||
<span class="input-group-addon">MB</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-xs-6">
|
||||
<label for="pIO">Block IO Weight</label>
|
||||
|
||||
<div>
|
||||
<input type="text" class="form-control" value="{{ old('io', 500) }}" name="io" id="pIO" />
|
||||
<input type="text" id="pIO" name="io" class="form-control" value="{{ old('io', 500) }}" />
|
||||
</div>
|
||||
<p class="text-muted small"><strong>Advanced</strong>: The IO performance of this server relative to other <em>running</em> containers on the system. Value should be between <code>10</code> and <code>1000</code>.</code></p>
|
||||
|
||||
<p class="text-muted small"><strong>Advanced</strong>: The IO performance of this server relative to other <em>running</em> containers on the system. Value should be between <code>10</code> and <code>1000</code>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Nest Configuration</h3>
|
||||
</div>
|
||||
|
||||
<div class="box-body row">
|
||||
<div class="form-group col-xs-12">
|
||||
<label for="pNestId">Nest</label>
|
||||
<select name="nest_id" id="pNestId" class="form-control">
|
||||
|
||||
<select id="pNestId" name="nest_id" class="form-control">
|
||||
@foreach($nests as $nest)
|
||||
<option value="{{ $nest->id }}"
|
||||
@if($nest->id === old('nest_id'))
|
||||
|
@ -199,33 +230,40 @@
|
|||
>{{ $nest->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
||||
<p class="small text-muted no-margin">Select the Nest that this server will be grouped under.</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-xs-12">
|
||||
<label for="pEggId">Egg</label>
|
||||
<select name="egg_id" id="pEggId" class="form-control"></select>
|
||||
<select id="pEggId" name="egg_id" class="form-control"></select>
|
||||
<p class="small text-muted no-margin">Select the Egg that will define how this server should operate.</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-xs-12">
|
||||
<label for="pPackId">Data Pack</label>
|
||||
<select name="pack_id" id="pPackId" class="form-control"></select>
|
||||
<select id="pPackId" name="pack_id" class="form-control"></select>
|
||||
<p class="small text-muted no-margin">Select a data pack to be automatically installed on this server when first created.</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-xs-12">
|
||||
<div class="checkbox checkbox-primary no-margin-bottom">
|
||||
<input id="pSkipScripting" name="skip_scripts" type="checkbox" value="1" />
|
||||
<input type="checkbox" id="pSkipScripting" name="skip_scripts" {{ \Pterodactyl\Helpers\Utilities::checked('skip_scripts', 0) }} />
|
||||
<label for="pSkipScripting" class="strong">Skip Egg Install Script</label>
|
||||
</div>
|
||||
|
||||
<p class="small text-muted no-margin">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.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Docker Configuration</h3>
|
||||
</div>
|
||||
|
||||
<div class="box-body row">
|
||||
<div class="form-group col-xs-12">
|
||||
<label for="pDefaultContainer">Docker Image</label>
|
||||
|
@ -236,23 +274,28 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Startup Configuration</h3>
|
||||
</div>
|
||||
|
||||
<div class="box-body row">
|
||||
<div class="form-group col-xs-12">
|
||||
<label for="pStartup">Startup Command</label>
|
||||
<input type="text" id="pStartup" value="{{ old('startup') }}" class="form-control" name="startup" />
|
||||
<input type="text" id="pStartup" name="startup" value="{{ old('startup') }}" class="form-control" />
|
||||
<p class="small text-muted no-margin">The following data substitutes are available for the startup command: <code>@{{SERVER_MEMORY}}</code>, <code>@{{SERVER_IP}}</code>, and <code>@{{SERVER_PORT}}</code>. They will be replaced with the allocated memory, server IP, and server port respectively.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="box-header with-border" style="margin-top:-10px;">
|
||||
<h3 class="box-title">Service Variables</h3>
|
||||
</div>
|
||||
|
||||
<div class="box-body row" id="appendVariablesTo"></div>
|
||||
|
||||
<div class="box-footer">
|
||||
{!! csrf_field() !!}
|
||||
<input type="submit" class="btn btn-success pull-right" value="Create Server" />
|
||||
|
@ -266,5 +309,84 @@
|
|||
@section('footer-scripts')
|
||||
@parent
|
||||
{!! Theme::js('vendor/lodash/lodash.js') !!}
|
||||
|
||||
<script type="application/javascript">
|
||||
// Persist 'Service Variables'
|
||||
function serviceVariablesUpdated(eggId, ids) {
|
||||
@if (old('egg_id'))
|
||||
// Check if the egg id matches.
|
||||
if (eggId != '{{ old('egg_id') }}') {
|
||||
return;
|
||||
}
|
||||
|
||||
@if (old('environment'))
|
||||
@foreach (old('environment') as $key => $value)
|
||||
$('#' + ids['{{ $key }}']).val('{{ $value }}');
|
||||
@endforeach
|
||||
@endif
|
||||
@endif
|
||||
}
|
||||
// END Persist 'Service Variables'
|
||||
</script>
|
||||
|
||||
{!! Theme::js('js/admin/new-server.js') !!}
|
||||
|
||||
<script type="application/javascript">
|
||||
$(document).ready(function() {
|
||||
// Persist 'Server Owner' select2
|
||||
@if (old('owner_id'))
|
||||
$.ajax({
|
||||
url: '/admin/users/accounts.json?user_id={{ old('owner_id') }}',
|
||||
dataType: 'json',
|
||||
}).then(function (data) {
|
||||
initUserIdSelect([ data ]);
|
||||
});
|
||||
@else
|
||||
initUserIdSelect();
|
||||
@endif
|
||||
// END Persist 'Server Owner' select2
|
||||
|
||||
// Persist 'Node' select2
|
||||
@if (old('node_id'))
|
||||
$('#pNodeId').val('{{ old('node_id') }}').change();
|
||||
|
||||
// Persist 'Default Allocation' select2
|
||||
@if (old('allocation_id'))
|
||||
$('#pAllocation').val('{{ old('allocation_id') }}').change();
|
||||
@endif
|
||||
// END Persist 'Default Allocation' select2
|
||||
|
||||
// Persist 'Additional Allocations' select2
|
||||
@if (old('allocation_additional'))
|
||||
const additional_allocations = [];
|
||||
|
||||
@for ($i = 0; $i < count(old('allocation_additional')); $i++)
|
||||
additional_allocations.push('{{ old('allocation_additional.'.$i)}}');
|
||||
@endfor
|
||||
|
||||
$('#pAllocationAdditional').val(additional_allocations).change();
|
||||
@endif
|
||||
// END Persist 'Additional Allocations' select2
|
||||
@endif
|
||||
// END Persist 'Node' select2
|
||||
|
||||
// Persist 'Nest' select2
|
||||
@if (old('nest_id'))
|
||||
$('#pNestId').val('{{ old('nest_id') }}').change();
|
||||
|
||||
// Persist 'Egg' select2
|
||||
@if (old('egg_id'))
|
||||
$('#pEggId').val('{{ old('egg_id') }}').change();
|
||||
@endif
|
||||
// END Persist 'Egg' select2
|
||||
|
||||
// Persist 'Data Pack' select2
|
||||
@if (old('pack_id'))
|
||||
$('#pPackId').val('{{ old('pack_id') }}').change();
|
||||
@endif
|
||||
// END Persist 'Data Pack' select2
|
||||
@endif
|
||||
// END Persist 'Nest' select2
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
|
|
|
@ -80,11 +80,6 @@
|
|||
<i class="fa fa-home"></i> <span>Overview</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="{{ Route::currentRouteName() !== 'admin.statistics' ?: 'active' }}">
|
||||
<a href="{{ route('admin.statistics') }}">
|
||||
<i class="fa fa-tachometer"></i> <span>Statistics</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="{{ ! starts_with(Route::currentRouteName(), 'admin.settings') ?: 'active' }}">
|
||||
<a href="{{ route('admin.settings')}}">
|
||||
<i class="fa fa-wrench"></i> <span>Settings</span>
|
||||
|
@ -173,7 +168,7 @@
|
|||
</footer>
|
||||
</div>
|
||||
@section('footer-scripts')
|
||||
{!! Theme::js('js/keyboard.polyfill.js') !!}
|
||||
<script src="/js/keyboard.polyfill.js" type="application/javascript"></script>
|
||||
<script>keyboardeventKeyPolyfill.polyfill();</script>
|
||||
|
||||
{!! 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}') !!}
|
||||
<script src="/js/autocomplete.js" type="application/javascript"></script>
|
||||
|
||||
@if(Auth::user()->root_admin)
|
||||
<script>
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
|
||||
{!! Theme::js('vendor/jquery/jquery.min.js?t={cache-version}') !!}
|
||||
{!! Theme::js('vendor/bootstrap/bootstrap.min.js?t={cache-version}') !!}
|
||||
{!! Theme::js('js/autocomplete.js?t={cache-version}') !!}
|
||||
<script src="/js/autocomplete.js" type="application/javascript"></script>
|
||||
{!! Theme::js('vendor/particlesjs/particles.min.js?t={cache-version}') !!}
|
||||
<script type="text/javascript">
|
||||
/* particlesJS.load(@dom-id, @path-json, @callback (optional)); */
|
||||
|
|
|
@ -274,7 +274,7 @@
|
|||
<div class="control-sidebar-bg"></div>
|
||||
</div>
|
||||
@section('footer-scripts')
|
||||
{!! Theme::js('js/keyboard.polyfill.js?t={cache-version}') !!}
|
||||
<script src="/js/keyboard.polyfill.js" type="application/javascript"></script>
|
||||
<script>keyboardeventKeyPolyfill.polyfill();</script>
|
||||
|
||||
{!! 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}') !!}
|
||||
<script src="/js/autocomplete.js" type="application/javascript"></script>
|
||||
|
||||
@if(Auth::user()->root_admin)
|
||||
<script>
|
||||
|
|
|
@ -151,7 +151,7 @@ Route::group(['prefix' => 'nodes'], function () {
|
|||
Route::get('/view/{node}/allocation', 'Nodes\NodeViewController@allocations')->name('admin.nodes.view.allocation');
|
||||
Route::get('/view/{node}/servers', 'Nodes\NodeViewController@servers')->name('admin.nodes.view.servers');
|
||||
Route::get('/view/{node}/system-information', 'Nodes\SystemInformationController');
|
||||
Route::get('/view/{node}/settings/token', 'NodesController@setToken')->name('admin.nodes.view.configuration.token');
|
||||
Route::get('/view/{node}/settings/token', 'NodeAutoDeployController')->name('admin.nodes.view.configuration.token');
|
||||
|
||||
Route::post('/new', 'NodesController@store');
|
||||
Route::post('/view/{node}/allocation', 'NodesController@createAllocation');
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| User Controller Routes
|
||||
|
@ -8,6 +10,7 @@
|
|||
| Endpoint: /api/application/users
|
||||
|
|
||||
*/
|
||||
|
||||
Route::group(['prefix' => '/users'], function () {
|
||||
Route::get('/', 'Users\UserController@index')->name('api.application.users');
|
||||
Route::get('/{user}', 'Users\UserController@view')->name('api.application.users.view');
|
||||
|
@ -30,6 +33,7 @@ Route::group(['prefix' => '/users'], function () {
|
|||
Route::group(['prefix' => '/nodes'], function () {
|
||||
Route::get('/', 'Nodes\NodeController@index')->name('api.application.nodes');
|
||||
Route::get('/{node}', 'Nodes\NodeController@view')->name('api.application.nodes.view');
|
||||
Route::get('/{node}/configuration', 'Nodes\NodeConfigurationController');
|
||||
|
||||
Route::post('/', 'Nodes\NodeController@store');
|
||||
Route::patch('/{node}', 'Nodes\NodeController@update');
|
||||
|
@ -38,9 +42,7 @@ Route::group(['prefix' => '/nodes'], function () {
|
|||
|
||||
Route::group(['prefix' => '/{node}/allocations'], function () {
|
||||
Route::get('/', 'Nodes\AllocationController@index')->name('api.application.allocations');
|
||||
|
||||
Route::post('/', 'Nodes\AllocationController@store');
|
||||
|
||||
Route::delete('/{allocation}', 'Nodes\AllocationController@delete')->name('api.application.allocations.view');
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue