Merge branch 'develop' into v2
This commit is contained in:
commit
b966069946
12 changed files with 164 additions and 78 deletions
10
.github/docker/entrypoint.sh
vendored
10
.github/docker/entrypoint.sh
vendored
|
@ -30,7 +30,7 @@ else
|
|||
fi
|
||||
|
||||
echo "Checking if https is required."
|
||||
if [ -f /etc/nginx/conf.d/default.conf ]; then
|
||||
if [ -f /etc/nginx/http.d/panel.conf ]; then
|
||||
echo "Using nginx config already in place."
|
||||
if [ $LE_EMAIL ]; then
|
||||
echo "Checking for cert update"
|
||||
|
@ -42,15 +42,17 @@ else
|
|||
echo "Checking if letsencrypt email is set."
|
||||
if [ -z $LE_EMAIL ]; then
|
||||
echo "No letsencrypt email is set using http config."
|
||||
cp .github/docker/default.conf /etc/nginx/conf.d/default.conf
|
||||
cp .github/docker/default.conf /etc/nginx/http.d/panel.conf
|
||||
else
|
||||
echo "writing ssl config"
|
||||
cp .github/docker/default_ssl.conf /etc/nginx/conf.d/default.conf
|
||||
cp .github/docker/default_ssl.conf /etc/nginx/http.d/panel.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
|
||||
sed -i "s|<domain>|$(echo $APP_URL | sed 's~http[s]*://~~g')|g" /etc/nginx/http.d/panel.conf
|
||||
echo "generating certs"
|
||||
certbot certonly -d $(echo $APP_URL | sed 's~http[s]*://~~g') --standalone -m $LE_EMAIL --agree-tos -n
|
||||
fi
|
||||
echo "Removing the default nginx config"
|
||||
rm -rf /etc/nginx/http.d/default.conf
|
||||
fi
|
||||
|
||||
## check for DB up before starting the panel
|
||||
|
|
|
@ -32,7 +32,7 @@ I would like to extend my sincere thanks to the following sponsors for helping f
|
|||
| [**Spill Hosting**](https://spillhosting.no/) | Spill Hosting is a Norwegian hosting service, which aims for inexpensive services on quality servers. Premium i9-9900K processors will run your game like a dream. |
|
||||
| [**DeinServerHost**](https://deinserverhost.de/) | DeinServerHost offers Dedicated, vps and Gameservers for many popular Games like Minecraft and Rust in Germany since 2013. |
|
||||
| [**HostBend**](https://hostbend.com/) | HostBend offers a variety of solutions for developers, students, and others who have a tight budget but don't want to compromise quality and support. |
|
||||
| [**Capitol Hosting Solutions**](https://capitolsolutions.cloud/) | CHS is *the* budget friendly hosting company for Australian and American gamers, offering a variety of plans from Web Hosting to Game Servers; Custom Solutions too! |
|
||||
| [**Capitol Hosting Solutions**](https://chs.gg/) | CHS is *the* budget friendly hosting company for Australian and American gamers, offering a variety of plans from Web Hosting to Game Servers; Custom Solutions too! |
|
||||
| [**ByteAnia**](https://byteania.com/?utm_source=pterodactyl) | ByteAnia offers the best performing and most affordable **Ryzen 5000 Series hosting** on the market for *unbeatable prices*! |
|
||||
| [**Aussie Server Hosts**](https://aussieserverhosts.com/) | No frills Australian Owned and operated High Performance Server hosting for some of the most demanding games serving Australia and New Zealand. |
|
||||
| [**VibeGAMES**](https://vibegames.net/) | VibeGAMES is a game server provider that specializes in DDOS protection for the games we offer. We have multiple locations in the US, Brazil, France, Germany, Singapore, Australia and South Africa.|
|
||||
|
|
|
@ -5,14 +5,9 @@ The following versions of Pterodactyl are receiving active support and maintenan
|
|||
|
||||
| Panel | Daemon | Supported |
|
||||
| ----- | ------------ | ------------------ |
|
||||
| 1.4.x | wings@1.4.x | :white_check_mark: |
|
||||
| 1.3.x | wings@1.3.x | :x: |
|
||||
| 1.2.x | wings@1.2.x | :x: |
|
||||
| 1.1.x | wings@1.1.x | :x: |
|
||||
| 1.0.x | wings@1.0.x | :x: |
|
||||
| 1.6.x | wings@1.5.x | :white_check_mark: |
|
||||
| 0.7.x | daemon@0.6.x | :x: |
|
||||
| 0.6.x | daemon@0.5.x | :x: |
|
||||
| 0.5.x | daemon@0.4.x | :x: |
|
||||
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ class UpgradeCommand extends Command
|
|||
$userDetails = posix_getpwuid(fileowner('public'));
|
||||
$user = $userDetails['name'] ?? 'www-data';
|
||||
|
||||
if (!$this->confirm("Your webserver user has been detected as [{$user}]: is this correct?", true)) {
|
||||
if (!$this->confirm("Your webserver user has been detected as <fg=blue>[{$user}]:</> is this correct?", true)) {
|
||||
$user = $this->anticipate(
|
||||
'Please enter the name of the user running your webserver process. This varies from system to system, but is generally "www-data", "nginx", or "apache".',
|
||||
[
|
||||
|
@ -73,7 +73,7 @@ class UpgradeCommand extends Command
|
|||
$groupDetails = posix_getgrgid(filegroup('public'));
|
||||
$group = $groupDetails['name'] ?? 'www-data';
|
||||
|
||||
if (!$this->confirm("Your webserver group has been detected as [{$group}]: is this correct?", true)) {
|
||||
if (!$this->confirm("Your webserver group has been detected as <fg=blue>[{$group}]:</> is this correct?", true)) {
|
||||
$group = $this->anticipate(
|
||||
'Please enter the name of the group running your webserver process. Normally this is the same as your user.',
|
||||
[
|
||||
|
@ -86,6 +86,7 @@ class UpgradeCommand extends Command
|
|||
}
|
||||
|
||||
if (!$this->confirm('Are you sure you want to run the upgrade process for your Panel?')) {
|
||||
$this->warn('Upgrade process terminated by user.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -173,8 +174,8 @@ class UpgradeCommand extends Command
|
|||
$this->call('up');
|
||||
});
|
||||
|
||||
$this->newLine();
|
||||
$this->info('Finished running upgrade.');
|
||||
$this->newLine(2);
|
||||
$this->info('Panel has been successfully upgraded. Please ensure you also update any Wings instances: https://pterodactyl.io/wings/1.0/upgrading.html');
|
||||
}
|
||||
|
||||
protected function withProgress(ProgressBar $bar, Closure $callback)
|
||||
|
|
|
@ -109,5 +109,7 @@ class Kernel extends HttpKernel
|
|||
'bindings' => SubstituteBindings::class,
|
||||
'recaptcha' => VerifyReCaptcha::class,
|
||||
'node.maintenance' => MaintenanceMiddleware::class,
|
||||
// API Specific Middleware
|
||||
'api..key' => AuthenticateKey::class,
|
||||
];
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Pterodactyl\Providers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Cache\RateLimiting\Limit;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
|
@ -19,44 +20,87 @@ class RouteServiceProvider extends ServiceProvider
|
|||
protected $namespace = 'Pterodactyl\Http\Controllers';
|
||||
|
||||
/**
|
||||
* Define the routes for the application.
|
||||
* Define your route model bindings, pattern filters, etc.
|
||||
*/
|
||||
public function map()
|
||||
public function boot()
|
||||
{
|
||||
Route::middleware(['web', 'auth', 'csrf'])
|
||||
->namespace($this->namespace . '\Base')
|
||||
->group(base_path('routes/base.php'));
|
||||
$this->configureRateLimiting();
|
||||
|
||||
Route::middleware(['web', 'auth', 'admin', 'csrf'])->prefix('/admin')
|
||||
->namespace($this->namespace . '\Admin')
|
||||
->group(base_path('routes/admin.php'));
|
||||
$this->routes(function () {
|
||||
Route::middleware(['web', 'auth', 'csrf'])
|
||||
->namespace("$this->namespace\\Base")
|
||||
->group(base_path('routes/base.php'));
|
||||
|
||||
Route::middleware(['web', 'csrf'])->prefix('/auth')
|
||||
->namespace($this->namespace . '\Auth')
|
||||
->group(base_path('routes/auth.php'));
|
||||
Route::middleware(['web', 'auth', 'admin', 'csrf'])->prefix('/admin')
|
||||
->namespace("$this->namespace\\Admin")
|
||||
->group(base_path('routes/admin.php'));
|
||||
|
||||
Route::middleware(['web', 'csrf', 'auth', 'server', 'node.maintenance'])
|
||||
->prefix('/api/server/{server}')
|
||||
->namespace($this->namespace . '\Server')
|
||||
->group(base_path('routes/server.php'));
|
||||
Route::middleware(['web', 'csrf'])->prefix('/auth')
|
||||
->namespace("$this->namespace\\Auth")
|
||||
->group(base_path('routes/auth.php'));
|
||||
|
||||
Route::middleware([
|
||||
sprintf('throttle:%s,%s', config('http.rate_limit.application'), config('http.rate_limit.application_period')),
|
||||
'api',
|
||||
])->prefix('/api/application')
|
||||
->namespace($this->namespace . '\Api\Application')
|
||||
->group(base_path('routes/api-application.php'));
|
||||
Route::middleware(['web', 'csrf', 'auth', 'server', 'node.maintenance'])
|
||||
->prefix('/api/server/{server}')
|
||||
->namespace("$this->namespace\\Server")
|
||||
->group(base_path('routes/server.php'));
|
||||
|
||||
Route::middleware([
|
||||
//sprintf('throttle:%s,%s', config('http.rate_limit.client'), config('http.rate_limit.client_period')),
|
||||
'client-api',
|
||||
])->prefix('/api/client')
|
||||
->namespace($this->namespace . '\Api\Client')
|
||||
->group(base_path('routes/api-client.php'));
|
||||
Route::middleware(['api', 'throttle:api.application'])
|
||||
->prefix('/api/application')
|
||||
->namespace("$this->namespace\\Api\\Application")
|
||||
->group(base_path('routes/api-application.php'));
|
||||
|
||||
Route::middleware(['daemon'])->prefix('/api/remote')
|
||||
->namespace($this->namespace . '\Api\Remote')
|
||||
->group(base_path('routes/api-remote.php'));
|
||||
Route::middleware(['client-api', 'throttle:api.client'])
|
||||
->prefix('/api/client')
|
||||
->namespace("$this->namespace\\Api\\Client")
|
||||
->group(base_path('routes/api-client.php'));
|
||||
|
||||
Route::middleware(['daemon'])->prefix('/api/remote')
|
||||
->namespace("$this->namespace\\Api\\Remote")
|
||||
->group(base_path('routes/api-remote.php'));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the rate limiters for the application.
|
||||
*/
|
||||
protected function configureRateLimiting()
|
||||
{
|
||||
// Authentication rate limiting. For login and checkpoint endpoints we'll apply
|
||||
// a limit of 10 requests per minute, for the forgot password endpoint apply a
|
||||
// limit of two per minute for the requester so that there is less ability to
|
||||
// trigger email spam.
|
||||
RateLimiter::for('authentication', function (Request $request) {
|
||||
if ($request->route()->named('auth.post.forgot-password')) {
|
||||
return Limit::perMinute(2)->by($request->ip());
|
||||
}
|
||||
|
||||
return Limit::perMinute(10);
|
||||
});
|
||||
|
||||
// Configure the throttles for both the application and client APIs below.
|
||||
// This is configurable per-instance in "config/http.php". By default this
|
||||
// limiter will be tied to the specific request user, and falls back to the
|
||||
// request IP if there is no request user present for the key.
|
||||
//
|
||||
// This means that an authenticated API user cannot use IP switching to get
|
||||
// around the limits.
|
||||
RateLimiter::for('api.client', function (Request $request) {
|
||||
$key = optional($request->user())->uuid ?: $request->ip();
|
||||
|
||||
return Limit::perMinutes(
|
||||
config('http.rate_limit.client_period'),
|
||||
config('http.rate_limit.client')
|
||||
)->by($key);
|
||||
});
|
||||
|
||||
RateLimiter::for('api.application', function (Request $request) {
|
||||
$key = optional($request->user())->uuid ?: $request->ip();
|
||||
|
||||
return Limit::perMinutes(
|
||||
config('http.rate_limit.application_period'),
|
||||
config('http.rate_limit.application')
|
||||
)->by($key);
|
||||
});
|
||||
|
||||
RateLimiter::for('pull', function () {
|
||||
return Limit::perMinute(10);
|
||||
|
|
|
@ -58,15 +58,20 @@ class SuspensionService
|
|||
throw new ConflictHttpException('Cannot toggle suspension status on a server that is currently being transferred.');
|
||||
}
|
||||
|
||||
$this->connection->transaction(function () use ($action, $server, $isSuspending) {
|
||||
$server->update([
|
||||
'status' => $isSuspending ? Server::STATUS_SUSPENDED : null,
|
||||
]);
|
||||
// Update the server's suspension status.
|
||||
$server->update([
|
||||
'status' => $isSuspending ? Server::STATUS_SUSPENDED : null,
|
||||
]);
|
||||
|
||||
// Only trigger a Wings server sync if it is not currently being transferred.
|
||||
if (is_null($server->transfer)) {
|
||||
$this->daemonServerRepository->setServer($server)->sync();
|
||||
}
|
||||
});
|
||||
try {
|
||||
// Tell wings to re-sync the server state.
|
||||
$this->daemonServerRepository->setServer($server)->sync();
|
||||
} catch (\Exception $exception) {
|
||||
// Rollback the server's suspension status if wings fails to sync the server.
|
||||
$server->update([
|
||||
'status' => $isSuspending ? null : Server::STATUS_SUSPENDED,
|
||||
]);
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -4,7 +4,7 @@
|
|||
"version": "PTDL_v1",
|
||||
"update_url": null
|
||||
},
|
||||
"exported_at": "2021-05-29T19:02:43-04:00",
|
||||
"exported_at": "2021-09-15T17:07:50-04:00",
|
||||
"name": "Rust",
|
||||
"author": "support@pterodactyl.io",
|
||||
"description": "The only aim in Rust is to survive. To do this you will need to overcome struggles such as hunger, thirst and cold. Build a fire. Build a shelter. Kill animals for meat. Protect yourself from other players, and kill them for meat. Create alliances with other players and form a town. Do whatever it takes to survive.",
|
||||
|
@ -13,11 +13,11 @@
|
|||
"quay.io\/pterodactyl\/core:rust"
|
||||
],
|
||||
"file_denylist": [],
|
||||
"startup": ".\/RustDedicated -batchmode +server.port {{SERVER_PORT}} +server.identity \"rust\" +rcon.port {{RCON_PORT}} +rcon.web true +server.hostname \\\"{{HOSTNAME}}\\\" +server.level \\\"{{LEVEL}}\\\" +server.description \\\"{{DESCRIPTION}}\\\" +server.url \\\"{{SERVER_URL}}\\\" +server.headerimage \\\"{{SERVER_IMG}}\\\" +server.logoimage \\\"{{SERVER_LOGO}}\\\" +server.worldsize \\\"{{WORLD_SIZE}}\\\" +server.seed \\\"{{WORLD_SEED}}\\\" +server.maxplayers {{MAX_PLAYERS}} +rcon.password \\\"{{RCON_PASS}}\\\" +server.saveinterval {{SAVEINTERVAL}} +app.port {{APP_PORT}} {{ADDITIONAL_ARGS}}",
|
||||
"startup": ".\/RustDedicated -batchmode +server.port {{SERVER_PORT}} +server.identity \"rust\" +rcon.port {{RCON_PORT}} +rcon.web true +server.hostname \\\"{{HOSTNAME}}\\\" +server.level \\\"{{LEVEL}}\\\" +server.description \\\"{{DESCRIPTION}}\\\" +server.url \\\"{{SERVER_URL}}\\\" +server.headerimage \\\"{{SERVER_IMG}}\\\" +server.logoimage \\\"{{SERVER_LOGO}}\\\" +server.maxplayers {{MAX_PLAYERS}} +rcon.password \\\"{{RCON_PASS}}\\\" +server.saveinterval {{SAVEINTERVAL}} +app.port {{APP_PORT}} $( [ -z ${MAP_URL} ] && printf %s \"+server.worldsize \\\"{{WORLD_SIZE}}\\\" +server.seed \\\"{{WORLD_SEED}}\\\"\" || printf %s \"+server.levelurl {{MAP_URL}}\" ) {{ADDITIONAL_ARGS}}",
|
||||
"config": {
|
||||
"files": "{}",
|
||||
"startup": "{\r\n \"done\": \"Server startup complete\",\r\n \"userInteraction\": []\r\n}",
|
||||
"logs": "{\r\n \"custom\": false,\r\n \"location\": \"latest.log\"\r\n}",
|
||||
"startup": "{\r\n \"done\": \"Server startup complete\"\r\n}",
|
||||
"logs": "{}",
|
||||
"stop": "quit"
|
||||
},
|
||||
"scripts": {
|
||||
|
@ -162,6 +162,15 @@
|
|||
"user_viewable": true,
|
||||
"user_editable": true,
|
||||
"rules": "nullable|url"
|
||||
},
|
||||
{
|
||||
"name": "Custom Map URL",
|
||||
"description": "Overwrites the map with the one from the direct download URL. Invalid URLs will cause the server to crash.",
|
||||
"env_variable": "MAP_URL",
|
||||
"default_value": "",
|
||||
"user_viewable": true,
|
||||
"user_editable": true,
|
||||
"rules": "nullable|url"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -7,14 +7,16 @@ import TitledGreyBox from '@/components/elements/TitledGreyBox';
|
|||
import { ServerContext } from '@/state/server';
|
||||
import CopyOnClick from '@/components/elements/CopyOnClick';
|
||||
import { SocketEvent, SocketRequest } from '@/components/server/events';
|
||||
import UptimeDuration from '@/components/server/UptimeDuration';
|
||||
|
||||
interface Stats {
|
||||
memory: number;
|
||||
cpu: number;
|
||||
disk: number;
|
||||
uptime: number;
|
||||
}
|
||||
|
||||
function statusToColor (status: string|null, installing: boolean): TwStyle {
|
||||
function statusToColor (status: string | null, installing: boolean): TwStyle {
|
||||
if (installing) {
|
||||
status = '';
|
||||
}
|
||||
|
@ -30,7 +32,7 @@ function statusToColor (status: string|null, installing: boolean): TwStyle {
|
|||
}
|
||||
|
||||
const ServerDetailsBlock = () => {
|
||||
const [ stats, setStats ] = useState<Stats>({ memory: 0, cpu: 0, disk: 0 });
|
||||
const [ stats, setStats ] = useState<Stats>({ memory: 0, cpu: 0, disk: 0, uptime: 0 });
|
||||
|
||||
const status = ServerContext.useStoreState(state => state.status.value);
|
||||
const connected = ServerContext.useStoreState(state => state.socket.connected);
|
||||
|
@ -48,6 +50,7 @@ const ServerDetailsBlock = () => {
|
|||
memory: stats.memory_bytes,
|
||||
cpu: stats.cpu_absolute,
|
||||
disk: stats.disk_bytes,
|
||||
uptime: stats.uptime || 0,
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -69,7 +72,7 @@ const ServerDetailsBlock = () => {
|
|||
const isTransferring = ServerContext.useStoreState(state => state.server.data!.isTransferring);
|
||||
const limits = ServerContext.useStoreState(state => state.server.data!.limits);
|
||||
const primaryAllocation = ServerContext.useStoreState(state => state.server.data!.allocations.filter(alloc => alloc.isDefault).map(
|
||||
allocation => (allocation.alias || allocation.ip) + ':' + allocation.port
|
||||
allocation => (allocation.alias || allocation.ip) + ':' + allocation.port,
|
||||
)).toString();
|
||||
|
||||
const diskLimit = limits.disk ? megabytesToHuman(limits.disk) : 'Unlimited';
|
||||
|
@ -88,6 +91,11 @@ const ServerDetailsBlock = () => {
|
|||
]}
|
||||
/>
|
||||
{!status ? 'Connecting...' : (isInstalling ? 'Installing' : (isTransferring) ? 'Transferring' : status)}
|
||||
{stats.uptime > 0 &&
|
||||
<span css={tw`ml-2`}>
|
||||
(<UptimeDuration uptime={stats.uptime / 1000}/>)
|
||||
</span>
|
||||
}
|
||||
</p>
|
||||
<CopyOnClick text={primaryAllocation}>
|
||||
<p css={tw`text-xs mt-2`}>
|
||||
|
|
14
resources/scripts/components/server/UptimeDuration.tsx
Normal file
14
resources/scripts/components/server/UptimeDuration.tsx
Normal file
|
@ -0,0 +1,14 @@
|
|||
import React from 'react';
|
||||
|
||||
export default ({ uptime }: { uptime: number }) => {
|
||||
const hours = Math.floor(Math.floor(uptime) / 60 / 60);
|
||||
const remainder = Math.floor(uptime - (hours * 60 * 60));
|
||||
const minutes = Math.floor(remainder / 60);
|
||||
const seconds = remainder % 60;
|
||||
|
||||
return (
|
||||
<>
|
||||
{hours.toString().padStart(2, '0')}:{minutes.toString().padStart(2, '0')}:{seconds.toString().padStart(2, '0')}
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -1,7 +1,5 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Authentication Routes
|
||||
|
@ -17,14 +15,22 @@ Route::group(['middleware' => 'guest'], function () {
|
|||
Route::get('/password', 'LoginController@index')->name('auth.forgot-password');
|
||||
Route::get('/password/reset/{token}', 'LoginController@index')->name('auth.reset');
|
||||
|
||||
// Login endpoints.
|
||||
Route::post('/login', 'LoginController@login')->middleware('recaptcha');
|
||||
Route::post('/login/checkpoint', 'LoginCheckpointController')->name('auth.checkpoint');
|
||||
Route::post('/login/checkpoint/key', 'WebauthnController@auth')->name('auth.checkpoint.key');
|
||||
// Apply a throttle to authentication action endpoints, in addition to the
|
||||
// recaptcha endpoints to slow down manual attack spammers even more. 🤷
|
||||
//
|
||||
// @see \Pterodactyl\Providers\RouteServiceProvider
|
||||
Route::middleware(['throttle:authentication'])->group(function () {
|
||||
// Login endpoints.
|
||||
Route::post('/login', 'LoginController@login')->middleware('recaptcha');
|
||||
Route::post('/login/checkpoint', 'LoginCheckpointController')->name('auth.login-checkpoint');
|
||||
Route::post('/login/checkpoint/key', 'WebauthnController@auth')->name('auth.login-checkpoint-key');
|
||||
|
||||
// Forgot password route. A post to this endpoint will trigger an
|
||||
// email to be sent containing a reset token.
|
||||
Route::post('/password', 'ForgotPasswordController@sendResetLinkEmail')->middleware('recaptcha');
|
||||
// Forgot password route. A post to this endpoint will trigger an
|
||||
// email to be sent containing a reset token.
|
||||
Route::post('/password', 'ForgotPasswordController@sendResetLinkEmail')
|
||||
->name('auth.post.forgot-password')
|
||||
->middleware('recaptcha');
|
||||
});
|
||||
|
||||
// Password reset routes. This endpoint is hit after going through
|
||||
// the forgot password routes to acquire a token (or after an account
|
||||
|
|
Loading…
Reference in a new issue