Skip to content

Commit 54f9c5f

Browse files
committed
Merge branch 'develop' into feature/file-uploads
2 parents 67ba3ba + 91cdbd6 commit 54f9c5f

File tree

136 files changed

+2178
-971
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

136 files changed

+2178
-971
lines changed

.github/FUNDING.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
#github: [DaneEveritt]
1+
github: [DaneEveritt]
22
custom: ["https://paypal.me/PterodactylSoftware"]

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
- name: Create release archive
3737
run: |
3838
rm -rf node_modules/ test/ codecov.yml CODE_OF_CONDUCT.md CONTRIBUTING.md phpunit.dusk.xml phpunit.xml Vagrantfile
39-
tar -czf panel.tar.gz *
39+
tar -czf panel.tar.gz * .env.example
4040
4141
- name: Extract changelog
4242
id: extract_changelog

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@ RUN cp docker/default.conf /etc/nginx/conf.d/default.conf \
2525
&& cat docker/www.conf > /usr/local/etc/php-fpm.d/www.conf \
2626
&& rm /usr/local/etc/php-fpm.d/www.conf.default \
2727
&& cat docker/supervisord.conf > /etc/supervisord.conf \
28-
&& echo "* * * * * /usr/bin/php /app/artisan schedule:run >> /dev/null 2>&1" >> /var/spool/cron/crontabs/root \
28+
&& echo "* * * * * /usr/local/bin/php /app/artisan schedule:run >> /dev/null 2>&1" >> /var/spool/cron/crontabs/root \
2929
&& sed -i s/ssl_session_cache/#ssl_session_cache/g /etc/nginx/nginx.conf \
3030
&& mkdir -p /var/run/php /var/run/nginx
3131

3232
EXPOSE 80 443
3333

3434
ENTRYPOINT ["/bin/ash", "docker/entrypoint.sh"]
3535

36-
CMD [ "supervisord", "-n", "-c", "/etc/supervisord.conf" ]
36+
CMD [ "supervisord", "-n", "-c", "/etc/supervisord.conf" ]

README.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,30 @@ What more are you waiting for? Make game servers a first class citizen on your p
1212

1313
![Image](https://cdn.pterodactyl.io/site-assets/mockup-macbook-grey.png)
1414

15+
## Sponsors
16+
I would like to extend my sincere thanks to the following sponsors for funding Pterodactyl's developement. [Interested
17+
in becoming a sponsor?](https://github.com/sponsors/DaneEveritt)
18+
19+
#### [BloomVPS](https://bloomvps.com)
20+
> BloomVPS offers dedicated core VPS and Minecraft hosting with Ryzen 9 processors. With owned-hardware, we offer truly
21+
> unbeatable prices on high-performance hosting.
22+
23+
#### [VersatileNode](https://versatilenode.com/)
24+
> Looking to host a minecraft server, vps, or a website? VersatileNode is one of the most affordable hosting providers
25+
> to provide quality yet cheap services with incredible support.
26+
27+
#### [MineStrator](https://minestrator.com/)
28+
> Looking for a French highend hosting company for you minecraft server? More than 14,000 members on our discord
29+
> trust us.
30+
31+
#### [DedicatedMC](https://dedicatedmc.io/)
32+
> DedicatedMC provides Raw Power hosting at affordable pricing, making sure to never compromise on your performance
33+
> and giving you the best performance money can buy.
34+
35+
#### [Skynode](https://www.skynode.pro/)
36+
> Skynode provides blazing fast game servers along with a top notch user experience. Whatever our clients are looking
37+
> for, we're able to provide it!
38+
1539
## Support & Documentation
1640
Support for using Pterodactyl can be found on our [Documentation Website](https://pterodactyl.io/project/introduction.html), [Guides Website](https://pterodactyl.io/community/about.html), or via our [Discord Chat](https://discord.gg/QRDZvVm).
1741

@@ -43,7 +67,7 @@ In addition to our standard nest of supported games, our community is constantly
4367
## Credits
4468
This software would not be possible without the work of other open-source authors who provide tools such as:
4569

46-
[Ace Editor](https://ace.c9.io), [AdminLTE](https://almsaeedstudio.com), [Animate.css](http://daneden.github.io/animate.css/), [AnsiUp](https://github.com/drudru/ansi_up), [Async.js](https://github.com/caolan/async),
70+
[Ace Editor](https://ace.c9.io), [AdminLTE](https://adminlte.io), [Animate.css](http://daneden.github.io/animate.css/), [AnsiUp](https://github.com/drudru/ansi_up), [Async.js](https://github.com/caolan/async),
4771
[Bootstrap](http://getbootstrap.com), [Bootstrap Notify](http://bootstrap-notify.remabledesigns.com), [Chart.js](http://www.chartjs.org), [FontAwesome](http://fontawesome.io),
4872
[FontAwesome Animations](https://github.com/l-lin/font-awesome-animation), [jQuery](http://jquery.com), [Laravel](https://laravel.com), [Lodash](https://lodash.com),
4973
[Select2](https://select2.github.io), [Socket.io](http://socket.io), [Socket.io File Upload](https://github.com/vote539/socketio-file-upload), [SweetAlert](http://t4t5.github.io/sweetalert),
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
namespace Pterodactyl\Console\Commands\Maintenance;
4+
5+
use Carbon\CarbonImmutable;
6+
use InvalidArgumentException;
7+
use Illuminate\Console\Command;
8+
use Pterodactyl\Repositories\Eloquent\BackupRepository;
9+
10+
class PruneOrphanedBackupsCommand extends Command
11+
{
12+
/**
13+
* @var string
14+
*/
15+
protected $signature = 'p:maintenance:prune-backups {--since-minutes=30}';
16+
17+
/**
18+
* @var string
19+
*/
20+
protected $description = 'Marks all backups that have not completed in the last "n" minutes as being failed.';
21+
22+
/**
23+
* @param \Pterodactyl\Repositories\Eloquent\BackupRepository $repository
24+
*/
25+
public function handle(BackupRepository $repository)
26+
{
27+
$since = $this->option('since-minutes');
28+
if (! is_digit($since)) {
29+
throw new InvalidArgumentException('The --since-minutes option must be a valid numeric digit.');
30+
}
31+
32+
$query = $repository->getBuilder()
33+
->whereNull('completed_at')
34+
->whereDate('created_at', '<=', CarbonImmutable::now()->subMinutes($since));
35+
36+
$count = $query->count();
37+
if (! $count) {
38+
$this->info('There are no orphaned backups to be marked as failed.');
39+
40+
return;
41+
}
42+
43+
$this->warn("Marking {$count} backups that have not been marked as completed in the last {$since} minutes as failed.");
44+
45+
$query->update([
46+
'is_successful' => false,
47+
'completed_at' => CarbonImmutable::now(),
48+
'updated_at' => CarbonImmutable::now(),
49+
]);
50+
}
51+
}

app/Console/Kernel.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,16 @@ protected function commands()
2222
*/
2323
protected function schedule(Schedule $schedule)
2424
{
25+
// Execute scheduled commands for servers every minute, as if there was a normal cron running.
2526
$schedule->command('p:schedule:process')->everyMinute()->withoutOverlapping();
27+
28+
// Every 30 minutes, run the backup pruning command so that any abandoned backups can be removed
29+
// from the UI view for the server.
30+
$schedule->command('p:maintenance:prune-backups', [
31+
'--since-minutes' => '30',
32+
])->everyThirtyMinutes();
33+
34+
// Every day cleanup any internal backups of service files.
2635
$schedule->command('p:maintenance:clean-service-backups')->daily();
2736
}
2837
}

app/Exceptions/Handler.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,13 @@ public static function convertToArray(Throwable $exception, array $override = []
213213
'detail' => 'An error was encountered while processing this request.',
214214
];
215215

216+
if ($exception instanceof ModelNotFoundException || $exception->getPrevious() instanceof ModelNotFoundException) {
217+
// Show a nicer error message compared to the standard "No query results for model"
218+
// response that is normally returned. If we are in debug mode this will get overwritten
219+
// with a more specific error message to help narrow down things.
220+
$error['detail'] = 'The requested resource could not be found on the server.';
221+
}
222+
216223
if (config('app.debug')) {
217224
$error = array_merge($error, [
218225
'detail' => $exception->getMessage(),

app/Exceptions/Http/Connection/DaemonConnectionException.php

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Pterodactyl\Exceptions\Http\Connection;
44

5+
use Illuminate\Support\Arr;
56
use Illuminate\Http\Response;
67
use GuzzleHttp\Exception\GuzzleException;
78
use Pterodactyl\Exceptions\DisplayException;
@@ -22,18 +23,34 @@ class DaemonConnectionException extends DisplayException
2223
* @param \GuzzleHttp\Exception\GuzzleException $previous
2324
* @param bool $useStatusCode
2425
*/
25-
public function __construct(GuzzleException $previous, bool $useStatusCode = false)
26+
public function __construct(GuzzleException $previous, bool $useStatusCode = true)
2627
{
2728
/** @var \GuzzleHttp\Psr7\Response|null $response */
2829
$response = method_exists($previous, 'getResponse') ? $previous->getResponse() : null;
2930

3031
if ($useStatusCode) {
31-
$this->statusCode = is_null($response) ? 500 : $response->getStatusCode();
32+
$this->statusCode = is_null($response) ? $this->statusCode : $response->getStatusCode();
3233
}
3334

34-
parent::__construct(trans('admin/server.exceptions.daemon_exception', [
35+
$message = trans('admin/server.exceptions.daemon_exception', [
3536
'code' => is_null($response) ? 'E_CONN_REFUSED' : $response->getStatusCode(),
36-
]), $previous, DisplayException::LEVEL_WARNING);
37+
]);
38+
39+
// Attempt to pull the actual error message off the response and return that if it is not
40+
// a 500 level error.
41+
if ($this->statusCode < 500 && ! is_null($response)) {
42+
$body = $response->getBody();
43+
if (is_string($body) || (is_object($body) && method_exists($body, '__toString'))) {
44+
$body = json_decode(is_string($body) ? $body : $body->__toString(), true);
45+
$message = "[Wings Error]: " . Arr::get($body, 'error', $message);
46+
}
47+
}
48+
49+
$level = $this->statusCode >= 500 && $this->statusCode !== 504
50+
? DisplayException::LEVEL_ERROR
51+
: DisplayException::LEVEL_WARNING;
52+
53+
parent::__construct($message, $previous, $level);
3754
}
3855

3956
/**

app/Http/Controllers/Api/Client/ClientController.php

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
namespace Pterodactyl\Http\Controllers\Api\Client;
44

5-
use Pterodactyl\Models\User;
65
use Pterodactyl\Models\Server;
76
use Pterodactyl\Models\Permission;
87
use Spatie\QueryBuilder\QueryBuilder;
@@ -39,31 +38,27 @@ public function __construct(ServerRepository $repository)
3938
public function index(GetServersRequest $request): array
4039
{
4140
$user = $request->user();
42-
$level = $request->getFilterLevel();
4341
$transformer = $this->getTransformer(ServerTransformer::class);
4442

4543
// Start the query builder and ensure we eager load any requested relationships from the request.
46-
$builder = Server::query()->with($this->getIncludesForTransformer($transformer, ['node']));
44+
$builder = QueryBuilder::for(
45+
Server::query()->with($this->getIncludesForTransformer($transformer, ['node']))
46+
)->allowedFilters('uuid', 'name', 'external_id');
4747

48-
if ($level === User::FILTER_LEVEL_OWNER) {
49-
$builder = $builder->where('owner_id', $request->user()->id);
50-
}
51-
// If set to all, display all servers they can access, including those they access as an
52-
// admin. If set to subuser, only return the servers they can access because they are owner,
53-
// or marked as a subuser of the server.
54-
elseif (($level === User::FILTER_LEVEL_ALL && ! $user->root_admin) || $level === User::FILTER_LEVEL_SUBUSER) {
48+
// Either return all of the servers the user has access to because they are an admin `?type=admin` or
49+
// just return all of the servers the user has access to because they are the owner or a subuser of the
50+
// server.
51+
if ($request->input('type') === 'admin') {
52+
$builder = $user->root_admin
53+
? $builder->whereNotIn('id', $user->accessibleServers()->pluck('id')->all())
54+
// If they aren't an admin but want all the admin servers don't fail the request, just
55+
// make it a query that will never return any results back.
56+
: $builder->whereRaw('1 = 2');
57+
} elseif ($request->input('type') === 'owner') {
58+
$builder = $builder->where('owner_id', $user->id);
59+
} else {
5560
$builder = $builder->whereIn('id', $user->accessibleServers()->pluck('id')->all());
5661
}
57-
// If set to admin, only display the servers a user can access because they are an administrator.
58-
// This means only servers the user would not have access to if they were not an admin (because they
59-
// are not an owner or subuser) are returned.
60-
elseif ($level === User::FILTER_LEVEL_ADMIN && $user->root_admin) {
61-
$builder = $builder->whereNotIn('id', $user->accessibleServers()->pluck('id')->all());
62-
}
63-
64-
$builder = QueryBuilder::for($builder)->allowedFilters(
65-
'uuid', 'name', 'external_id'
66-
);
6762

6863
$servers = $builder->paginate(min($request->query('per_page', 50), 100))->appends($request->query());
6964

app/Http/Controllers/Api/Client/Servers/CommandController.php

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use Illuminate\Http\Response;
66
use Pterodactyl\Models\Server;
77
use Psr\Http\Message\ResponseInterface;
8-
use GuzzleHttp\Exception\RequestException;
98
use GuzzleHttp\Exception\BadResponseException;
109
use Symfony\Component\HttpKernel\Exception\HttpException;
1110
use Pterodactyl\Repositories\Wings\DaemonCommandRepository;
@@ -45,19 +44,21 @@ public function index(SendCommandRequest $request, Server $server): Response
4544
{
4645
try {
4746
$this->repository->setServer($server)->send($request->input('command'));
48-
} catch (RequestException $exception) {
49-
if ($exception instanceof BadResponseException) {
47+
} catch (DaemonConnectionException $exception) {
48+
$previous = $exception->getPrevious();
49+
50+
if ($previous instanceof BadResponseException) {
5051
if (
51-
$exception->getResponse() instanceof ResponseInterface
52-
&& $exception->getResponse()->getStatusCode() === Response::HTTP_BAD_GATEWAY
52+
$previous->getResponse() instanceof ResponseInterface
53+
&& $previous->getResponse()->getStatusCode() === Response::HTTP_BAD_GATEWAY
5354
) {
5455
throw new HttpException(
5556
Response::HTTP_BAD_GATEWAY, 'Server must be online in order to send commands.', $exception
5657
);
5758
}
5859
}
5960

60-
throw new DaemonConnectionException($exception);
61+
throw $exception;
6162
}
6263

6364
return $this->returnNoContent();

0 commit comments

Comments
 (0)