From f859d37b251d4434c26f52700ca1420d91dd5788 Mon Sep 17 00:00:00 2001
From: Ward Pieters
Date: Sun, 18 Oct 2020 00:02:46 +0200
Subject: [PATCH 01/41] fix: duplicate 2FA error messages
(https://github.com/pterodactyl/panel/issues/2455)
---
.../components/auth/LoginCheckpointContainer.tsx | 15 +++++----------
1 file changed, 5 insertions(+), 10 deletions(-)
diff --git a/resources/scripts/components/auth/LoginCheckpointContainer.tsx b/resources/scripts/components/auth/LoginCheckpointContainer.tsx
index f45073281..4c3dff599 100644
--- a/resources/scripts/components/auth/LoginCheckpointContainer.tsx
+++ b/resources/scripts/components/auth/LoginCheckpointContainer.tsx
@@ -1,7 +1,6 @@
import React, { useState } from 'react';
import { Link, RouteComponentProps } from 'react-router-dom';
import loginCheckpoint from '@/api/auth/loginCheckpoint';
-import { httpErrorToHuman } from '@/api/http';
import LoginFormContainer from '@/components/auth/LoginFormContainer';
import { ActionCreator } from 'easy-peasy';
import { StaticContext } from 'react-router';
@@ -20,8 +19,7 @@ interface Values {
type OwnProps = RouteComponentProps, StaticContext, { token?: string }>
type Props = OwnProps & {
- addError: ActionCreator;
- clearFlashes: ActionCreator;
+ clearAndAddHttpError: ActionCreator;
}
const LoginCheckpointContainer = () => {
@@ -79,9 +77,7 @@ const LoginCheckpointContainer = () => {
};
const EnhancedForm = withFormik({
- handleSubmit: ({ code, recoveryCode }, { setSubmitting, props: { addError, clearFlashes, location } }) => {
- clearFlashes();
-
+ handleSubmit: ({ code, recoveryCode }, { setSubmitting, props: { clearAndAddHttpError, location } }) => {
loginCheckpoint(location.state?.token || '', code, recoveryCode)
.then(response => {
if (response.complete) {
@@ -95,7 +91,7 @@ const EnhancedForm = withFormik({
.catch(error => {
console.error(error);
setSubmitting(false);
- addError({ message: httpErrorToHuman(error) });
+ clearAndAddHttpError({ error: error });
});
},
@@ -106,7 +102,7 @@ const EnhancedForm = withFormik({
})(LoginCheckpointContainer);
export default ({ history, location, ...props }: OwnProps) => {
- const { addError, clearFlashes } = useFlash();
+ const { clearAndAddHttpError } = useFlash();
if (!location.state?.token) {
history.replace('/auth/login');
@@ -115,8 +111,7 @@ export default ({ history, location, ...props }: OwnProps) => {
}
return
Date: Sun, 18 Oct 2020 00:42:52 +0200
Subject: [PATCH 02/41] fix: duplicate disable 2FA error messages
---
.../components/dashboard/forms/DisableTwoFactorModal.tsx | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/resources/scripts/components/dashboard/forms/DisableTwoFactorModal.tsx b/resources/scripts/components/dashboard/forms/DisableTwoFactorModal.tsx
index 170cd01d3..d4f986942 100644
--- a/resources/scripts/components/dashboard/forms/DisableTwoFactorModal.tsx
+++ b/resources/scripts/components/dashboard/forms/DisableTwoFactorModal.tsx
@@ -7,7 +7,6 @@ import { object, string } from 'yup';
import { Actions, useStoreActions } from 'easy-peasy';
import { ApplicationStore } from '@/state';
import disableAccountTwoFactor from '@/api/account/disableAccountTwoFactor';
-import { httpErrorToHuman } from '@/api/http';
import tw from 'twin.macro';
import Button from '@/components/elements/Button';
@@ -16,11 +15,10 @@ interface Values {
}
export default ({ ...props }: RequiredModalProps) => {
- const { addError, clearFlashes } = useStoreActions((actions: Actions) => actions.flashes);
+ const { clearAndAddHttpError } = useStoreActions((actions: Actions) => actions.flashes);
const updateUserData = useStoreActions((actions: Actions) => actions.user.updateUserData);
const submit = ({ password }: Values, { setSubmitting }: FormikHelpers) => {
- clearFlashes('account:two-factor');
disableAccountTwoFactor(password)
.then(() => {
updateUserData({ useTotp: false });
@@ -29,7 +27,7 @@ export default ({ ...props }: RequiredModalProps) => {
.catch(error => {
console.error(error);
- addError({ message: httpErrorToHuman(error), key: 'account:two-factor' });
+ clearAndAddHttpError({ error: error, key: 'account:two-factor' });
setSubmitting(false);
});
};
From 1c4ee31491212ada682a4caed01c67edfc8ac945 Mon Sep 17 00:00:00 2001
From: Ward Pieters
Date: Sun, 18 Oct 2020 00:46:46 +0200
Subject: [PATCH 03/41] fix: duplicate enable 2FA error messages
---
.../components/dashboard/forms/SetupTwoFactorModal.tsx | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/resources/scripts/components/dashboard/forms/SetupTwoFactorModal.tsx b/resources/scripts/components/dashboard/forms/SetupTwoFactorModal.tsx
index 619067486..eb8e1a890 100644
--- a/resources/scripts/components/dashboard/forms/SetupTwoFactorModal.tsx
+++ b/resources/scripts/components/dashboard/forms/SetupTwoFactorModal.tsx
@@ -6,7 +6,6 @@ import getTwoFactorTokenUrl from '@/api/account/getTwoFactorTokenUrl';
import enableAccountTwoFactor from '@/api/account/enableAccountTwoFactor';
import { Actions, useStoreActions } from 'easy-peasy';
import { ApplicationStore } from '@/state';
-import { httpErrorToHuman } from '@/api/http';
import FlashMessageRender from '@/components/FlashMessageRender';
import Field from '@/components/elements/Field';
import tw from 'twin.macro';
@@ -22,20 +21,18 @@ export default ({ onDismissed, ...props }: RequiredModalProps) => {
const [ recoveryTokens, setRecoveryTokens ] = useState([]);
const updateUserData = useStoreActions((actions: Actions) => actions.user.updateUserData);
- const { addError, clearFlashes } = useStoreActions((actions: Actions) => actions.flashes);
+ const { clearAndAddHttpError } = useStoreActions((actions: Actions) => actions.flashes);
useEffect(() => {
- clearFlashes('account:two-factor');
getTwoFactorTokenUrl()
.then(setToken)
.catch(error => {
console.error(error);
- addError({ message: httpErrorToHuman(error), key: 'account:two-factor' });
+ clearAndAddHttpError({ error: error, key: 'account:two-factor' });
});
}, []);
const submit = ({ code }: Values, { setSubmitting }: FormikHelpers) => {
- clearFlashes('account:two-factor');
enableAccountTwoFactor(code)
.then(tokens => {
setRecoveryTokens(tokens);
@@ -43,7 +40,7 @@ export default ({ onDismissed, ...props }: RequiredModalProps) => {
.catch(error => {
console.error(error);
- addError({ message: httpErrorToHuman(error), key: 'account:two-factor' });
+ clearAndAddHttpError({ error: error, key: 'account:two-factor' });
})
.then(() => setSubmitting(false));
};
From b3598b3b9874fabb853c9d5f66a9ed91f67b5b1a Mon Sep 17 00:00:00 2001
From: Dane Everitt
Date: Mon, 19 Oct 2020 15:27:06 -0700
Subject: [PATCH 04/41] Update README.md
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index 2bef0557f..27feb040b 100644
--- a/README.md
+++ b/README.md
@@ -28,6 +28,7 @@ I would like to extend my sincere thanks to the following sponsors for helping f
| [**XCORE-SERVER.de**](https://xcore-server.de/) | XCORE-SERVER.de offers High-End Servers for hosting and gaming since 2012. Fast, excellent and well-known for eSports Gaming. |
| [**RoyaleHosting**](https://royalehosting.net/) | Build your dreams and deploy them with RoyaleHosting’s reliable servers and network. Easy to use, provisioned in a couple of minutes. |
| [**Spill Hosting**](https://spillhosting.no/) | Spill Hosting is a Norwegian hosting service, which aims to cheap 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. |
## Documentation
* [Panel Documentation](https://pterodactyl.io/panel/1.0/getting_started.html)
From 1f5e0c033415eaf26aea1eb1b89f773ec8a0ce02 Mon Sep 17 00:00:00 2001
From: Dane Everitt
Date: Mon, 19 Oct 2020 21:07:07 -0700
Subject: [PATCH 05/41] Update build modification service and cover logic with
test cases
closes #2553
---
.../Servers/BuildModificationService.php | 145 ++++-------
.../Servers/BuildModificationServiceTest.php | 241 ++++++++++++++++++
2 files changed, 296 insertions(+), 90 deletions(-)
create mode 100644 tests/Integration/Services/Servers/BuildModificationServiceTest.php
diff --git a/app/Services/Servers/BuildModificationService.php b/app/Services/Servers/BuildModificationService.php
index e56d8f9b4..c42264f8d 100644
--- a/app/Services/Servers/BuildModificationService.php
+++ b/app/Services/Servers/BuildModificationService.php
@@ -4,22 +4,14 @@ namespace Pterodactyl\Services\Servers;
use Illuminate\Support\Arr;
use Pterodactyl\Models\Server;
-use GuzzleHttp\Exception\RequestException;
+use Pterodactyl\Models\Allocation;
use Illuminate\Database\ConnectionInterface;
use Pterodactyl\Exceptions\DisplayException;
+use Illuminate\Database\Eloquent\ModelNotFoundException;
use Pterodactyl\Repositories\Wings\DaemonServerRepository;
-use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
-use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
-use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
-use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
class BuildModificationService
{
- /**
- * @var \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface
- */
- private $allocationRepository;
-
/**
* @var \Illuminate\Database\ConnectionInterface
*/
@@ -30,11 +22,6 @@ class BuildModificationService
*/
private $daemonServerRepository;
- /**
- * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
- */
- private $repository;
-
/**
* @var \Pterodactyl\Services\Servers\ServerConfigurationStructureService
*/
@@ -43,23 +30,17 @@ class BuildModificationService
/**
* BuildModificationService constructor.
*
- * @param \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface $allocationRepository
* @param \Pterodactyl\Services\Servers\ServerConfigurationStructureService $structureService
* @param \Illuminate\Database\ConnectionInterface $connection
* @param \Pterodactyl\Repositories\Wings\DaemonServerRepository $daemonServerRepository
- * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository
*/
public function __construct(
- AllocationRepositoryInterface $allocationRepository,
ServerConfigurationStructureService $structureService,
ConnectionInterface $connection,
- DaemonServerRepository $daemonServerRepository,
- ServerRepositoryInterface $repository
+ DaemonServerRepository $daemonServerRepository
) {
- $this->allocationRepository = $allocationRepository;
$this->daemonServerRepository = $daemonServerRepository;
$this->connection = $connection;
- $this->repository = $repository;
$this->structureService = $structureService;
}
@@ -70,9 +51,8 @@ class BuildModificationService
* @param array $data
* @return \Pterodactyl\Models\Server
*
+ * @throws \Throwable
* @throws \Pterodactyl\Exceptions\DisplayException
- * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
- * @throws \Pterodactyl\Exceptions\Model\DataValidationException
*/
public function handle(Server $server, array $data)
{
@@ -82,48 +62,35 @@ class BuildModificationService
if (isset($data['allocation_id']) && $data['allocation_id'] != $server->allocation_id) {
try {
- $this->allocationRepository->findFirstWhere([
- ['id', '=', $data['allocation_id']],
- ['server_id', '=', $server->id],
- ]);
- } catch (RecordNotFoundException $ex) {
- throw new DisplayException(trans('admin/server.exceptions.default_allocation_not_found'));
+ Allocation::query()->where('id', $data['allocation_id'])->where('server_id', $server->id)->firstOrFail();
+ } catch (ModelNotFoundException $ex) {
+ throw new DisplayException('The requested default allocation is not currently assigned to this server.');
}
}
- /* @var \Pterodactyl\Models\Server $server */
- $server = $this->repository->withFreshModel()->update($server->id, [
- 'oom_disabled' => array_get($data, 'oom_disabled'),
- 'memory' => array_get($data, 'memory'),
- 'swap' => array_get($data, 'swap'),
- 'io' => array_get($data, 'io'),
- 'cpu' => array_get($data, 'cpu'),
- 'threads' => array_get($data, 'threads'),
- 'disk' => array_get($data, 'disk'),
- 'allocation_id' => array_get($data, 'allocation_id'),
- 'database_limit' => array_get($data, 'database_limit', 0) ?? null,
- 'allocation_limit' => array_get($data, 'allocation_limit', 0) ?? null,
- 'backup_limit' => array_get($data, 'backup_limit', 0) ?? null,
- ]);
+ // If any of these values are passed through in the data array go ahead and set
+ // them correctly on the server model.
+ $merge = Arr::only($data, ['oom_disabled', 'memory', 'swap', 'io', 'cpu', 'threads', 'disk', 'allocation_id']);
+
+ $server->forceFill(array_merge($merge, [
+ 'database_limit' => Arr::get($data, 'database_limit', 0) ?? null,
+ 'allocation_limit' => Arr::get($data, 'allocation_limit', 0) ?? null,
+ 'backup_limit' => Arr::get($data, 'backup_limit', 0) ?? 0,
+ ]))->saveOrFail();
+
+ $server = $server->fresh();
$updateData = $this->structureService->handle($server);
- try {
- $this->daemonServerRepository
- ->setServer($server)
- ->update(Arr::only($updateData, ['build']));
+ $this->daemonServerRepository->setServer($server)->update($updateData['build'] ?? []);
- $this->connection->commit();
- } catch (RequestException $exception) {
- throw new DaemonConnectionException($exception);
- }
+ $this->connection->commit();
return $server;
}
/**
- * Process the allocations being assigned in the data and ensure they
- * are available for a server.
+ * Process the allocations being assigned in the data and ensure they are available for a server.
*
* @param \Pterodactyl\Models\Server $server
* @param array $data
@@ -132,55 +99,53 @@ class BuildModificationService
*/
private function processAllocations(Server $server, array &$data)
{
- $firstAllocationId = null;
-
- if (! array_key_exists('add_allocations', $data) && ! array_key_exists('remove_allocations', $data)) {
+ if (empty($data['add_allocations']) && empty($data['remove_allocations'])) {
return;
}
- // Handle the addition of allocations to this server.
- if (array_key_exists('add_allocations', $data) && ! empty($data['add_allocations'])) {
- $unassigned = $this->allocationRepository->getUnassignedAllocationIds($server->node_id);
+ // Handle the addition of allocations to this server. Only assign allocations that are not currently
+ // assigned to a different server, and only allocations on the same node as the server.
+ if (! empty($data['add_allocations'])) {
+ $query = Allocation::query()
+ ->where('node_id', $server->node_id)
+ ->whereIn('id', $data['add_allocations'])
+ ->whereNull('server_id');
- $updateIds = [];
- foreach ($data['add_allocations'] as $allocation) {
- if (! in_array($allocation, $unassigned)) {
- continue;
- }
+ // Keep track of all the allocations we're just now adding so that we can use the first
+ // one to reset the default allocation to.
+ $freshlyAllocated = $query->pluck('id')->first();
- $firstAllocationId = $firstAllocationId ?? $allocation;
- $updateIds[] = $allocation;
- }
-
- if (! empty($updateIds)) {
- $this->allocationRepository->updateWhereIn('id', $updateIds, ['server_id' => $server->id]);
- }
+ $query->update(['server_id' => $server->id]);
}
- // Handle removal of allocations from this server.
- if (array_key_exists('remove_allocations', $data) && ! empty($data['remove_allocations'])) {
- $assigned = $server->allocations->pluck('id')->toArray();
-
- $updateIds = [];
+ if (! empty($data['remove_allocations'])) {
foreach ($data['remove_allocations'] as $allocation) {
- if (! in_array($allocation, $assigned)) {
- continue;
- }
-
- if ($allocation == $data['allocation_id']) {
- if (is_null($firstAllocationId)) {
- throw new DisplayException(trans('admin/server.exceptions.no_new_default_allocation'));
+ // If we are attempting to remove the default allocation for the server, see if we can reassign
+ // to the first provided value in add_allocations. If there is no new first allocation then we
+ // will throw an exception back.
+ if ($allocation === ($data['allocation_id'] ?? $server->allocation_id)) {
+ if (empty($freshlyAllocated)) {
+ throw new DisplayException(
+ 'You are attempting to delete the default allocation for this server but there is no fallback allocation to use.'
+ );
}
- $data['allocation_id'] = $firstAllocationId;
+ // Update the default allocation to be the first allocation that we are creating.
+ $data['allocation_id'] = $freshlyAllocated;
}
-
- $updateIds[] = $allocation;
}
- if (! empty($updateIds)) {
- $this->allocationRepository->updateWhereIn('id', $updateIds, ['server_id' => null]);
- }
+ // Remove any of the allocations we got that are currently assigned to this server on
+ // this node. Also set the notes to null, otherwise when re-allocated to a new server those
+ // notes will be carried over.
+ Allocation::query()->where('node_id', $server->node_id)
+ ->where('server_id', $server->id)
+ // Only remove the allocations that we didn't also attempt to add to the server...
+ ->whereIn('id', array_diff($data['remove_allocations'], $data['add_allocations'] ?? []))
+ ->update([
+ 'notes' => null,
+ 'server_id' => null,
+ ]);
}
}
}
diff --git a/tests/Integration/Services/Servers/BuildModificationServiceTest.php b/tests/Integration/Services/Servers/BuildModificationServiceTest.php
new file mode 100644
index 000000000..ab3d6160f
--- /dev/null
+++ b/tests/Integration/Services/Servers/BuildModificationServiceTest.php
@@ -0,0 +1,241 @@
+daemonServerRepository = Mockery::mock(DaemonServerRepository::class);
+ $this->swap(DaemonServerRepository::class, $this->daemonServerRepository);
+ }
+
+ /**
+ * Test that allocations can be added and removed from a server. Only the allocations on the
+ * current node and belonging to this server should be modified.
+ */
+ public function testAllocationsCanBeModifiedForTheServer()
+ {
+ $server = $this->createServerModel();
+ $server2 = $this->createServerModel();
+
+ /** @var \Pterodactyl\Models\Allocation[] $allocations */
+ $allocations = factory(Allocation::class)->times(4)->create(['node_id' => $server->node_id]);
+
+ $initialAllocationId = $server->allocation_id;
+ $allocations[0]->update(['server_id' => $server->id, 'notes' => 'Test notes']);
+
+ // Some additional test allocations for the other server, not the server we are attempting
+ // to modify.
+ $allocations[2]->update(['server_id' => $server2->id]);
+ $allocations[3]->update(['server_id' => $server2->id]);
+
+ $this->daemonServerRepository->expects('setServer->update')->andReturnUndefined();
+
+ $response = $this->getService()->handle($server, [
+ // Attempt to add one new allocation, and an allocation assigned to another server. The
+ // other server allocation should be ignored, and only the allocation for this server should
+ // be used.
+ 'add_allocations' => [$allocations[2]->id, $allocations[1]->id],
+ // Remove the default server allocation, ensuring that the new allocation passed through
+ // in the data becomes the default allocation.
+ 'remove_allocations' => [$server->allocation_id, $allocations[0]->id, $allocations[3]->id],
+ ]);
+
+ $this->assertInstanceOf(Server::class, $response);
+
+ // Only one allocation should exist for this server now.
+ $this->assertCount(1, $response->allocations);
+ $this->assertSame($allocations[1]->id, $response->allocation_id);
+
+ // These two allocations should not have been touched.
+ $this->assertDatabaseHas('allocations', ['id' => $allocations[2]->id, 'server_id' => $server2->id]);
+ $this->assertDatabaseHas('allocations', ['id' => $allocations[3]->id, 'server_id' => $server2->id]);
+
+ // Both of these allocations should have been removed from the server, and have had their
+ // notes properly reset.
+ $this->assertDatabaseHas('allocations', ['id' => $initialAllocationId, 'server_id' => null, 'notes' => null]);
+ $this->assertDatabaseHas('allocations', ['id' => $allocations[0]->id, 'server_id' => null, 'notes' => null]);
+ }
+
+ /**
+ * Test that an exception is thrown if removing the default allocation without also assigning
+ * new allocations to the server.
+ */
+ public function testExceptionIsThrownIfRemovingTheDefaultAllocation()
+ {
+ $server = $this->createServerModel();
+ /** @var \Pterodactyl\Models\Allocation[] $allocations */
+ $allocations = factory(Allocation::class)->times(4)->create(['node_id' => $server->node_id]);
+
+ $allocations[0]->update(['server_id' => $server->id]);
+
+ $this->expectException(DisplayException::class);
+ $this->expectExceptionMessage('You are attempting to delete the default allocation for this server but there is no fallback allocation to use.');
+
+ $this->getService()->handle($server, [
+ 'add_allocations' => [],
+ 'remove_allocations' => [$server->allocation_id, $allocations[0]->id],
+ ]);
+ }
+
+ /**
+ * Test that the build data for the server is properly passed along to the Wings instance so that
+ * the server data is updated in realtime. This test also ensures that only certain fields get updated
+ * for the server, and not just any arbitrary field.
+ */
+ public function testServerBuildDataIsProperlyUpdatedOnWings()
+ {
+ $server = $this->createServerModel();
+
+ $this->daemonServerRepository->expects('setServer')->with(Mockery::on(function (Server $s) use ($server) {
+ return $s->id === $server->id;
+ }))->andReturnSelf();
+
+ $this->daemonServerRepository->expects('update')->with(Mockery::on(function ($data) {
+ $this->assertEquals([
+ 'memory_limit' => 256,
+ 'swap' => 128,
+ 'io_weight' => 600,
+ 'cpu_limit' => 150,
+ 'threads' => '1,2',
+ 'disk_space' => 1024,
+ ], $data);
+
+ return true;
+ }))->andReturnUndefined();
+
+ $response = $this->getService()->handle($server, [
+ 'oom_disabled' => false,
+ 'memory' => 256,
+ 'swap' => 128,
+ 'io' => 600,
+ 'cpu' => 150,
+ 'threads' => '1,2',
+ 'disk' => 1024,
+ 'backup_limit' => null,
+ 'database_limit' => 10,
+ 'allocation_limit' => 20,
+ ]);
+
+ $this->assertFalse($response->oom_disabled);
+ $this->assertSame(256, $response->memory);
+ $this->assertSame(128, $response->swap);
+ $this->assertSame(600, $response->io);
+ $this->assertSame(150, $response->cpu);
+ $this->assertSame('1,2', $response->threads);
+ $this->assertSame(1024, $response->disk);
+ $this->assertSame(0, $response->backup_limit);
+ $this->assertSame(10, $response->database_limit);
+ $this->assertSame(20, $response->allocation_limit);
+ }
+
+ /**
+ * Test that no exception is thrown if we are only removing an allocation.
+ */
+ public function testNoExceptionIsThrownIfOnlyRemovingAllocation()
+ {
+ $server = $this->createServerModel();
+ /** @var \Pterodactyl\Models\Allocation[] $allocations */
+ $allocation = factory(Allocation::class)->create(['node_id' => $server->node_id, 'server_id' => $server->id]);
+
+ $this->daemonServerRepository->expects('setServer->update')->andReturnUndefined();
+
+ $this->getService()->handle($server, [
+ 'remove_allocations' => [$allocation->id],
+ ]);
+
+ $this->assertDatabaseHas('allocations', ['id' => $allocation->id, 'server_id' => null]);
+ }
+
+ /**
+ * Test that allocations in both the add and remove arrays are only added, and not removed.
+ * This scenario wouldn't really happen in the UI, but it is possible to perform via the API
+ * so we want to make sure that the logic being used doesn't break if the allocation exists
+ * in both arrays.
+ *
+ * We'll default to adding the allocation in this case.
+ */
+ public function testAllocationInBothAddAndRemoveIsAdded()
+ {
+ $server = $this->createServerModel();
+ /** @var \Pterodactyl\Models\Allocation[] $allocations */
+ $allocation = factory(Allocation::class)->create(['node_id' => $server->node_id]);
+
+ $this->daemonServerRepository->expects('setServer->update')->andReturnUndefined();
+
+ $this->getService()->handle($server, [
+ 'add_allocations' => [$allocation->id],
+ 'remove_allocations' => [$allocation->id],
+ ]);
+
+ $this->assertDatabaseHas('allocations', ['id' => $allocation->id, 'server_id' => $server->id]);
+ }
+
+ /**
+ * Test that using the same allocation ID multiple times in the array does not cause an error.
+ */
+ public function testUsingSameAllocationIdMultipleTimesDoesNotError()
+ {
+ $server = $this->createServerModel();
+ /** @var \Pterodactyl\Models\Allocation[] $allocations */
+ $allocation = factory(Allocation::class)->create(['node_id' => $server->node_id, 'server_id' => $server->id]);
+ $allocation2 = factory(Allocation::class)->create(['node_id' => $server->node_id]);
+
+ $this->daemonServerRepository->expects('setServer->update')->andReturnUndefined();
+
+ $this->getService()->handle($server, [
+ 'add_allocations' => [$allocation2->id, $allocation2->id],
+ 'remove_allocations' => [$allocation->id, $allocation->id],
+ ]);
+
+ $this->assertDatabaseHas('allocations', ['id' => $allocation->id, 'server_id' => null]);
+ $this->assertDatabaseHas('allocations', ['id' => $allocation2->id, 'server_id' => $server->id]);
+ }
+
+ /**
+ * Test that any changes we made to the server or allocations are rolled back if there is an
+ * exception while performing any action.
+ */
+ public function testThatUpdatesAreRolledBackIfExceptionIsEncountered()
+ {
+ $server = $this->createServerModel();
+ /** @var \Pterodactyl\Models\Allocation[] $allocations */
+ $allocation = factory(Allocation::class)->create(['node_id' => $server->node_id]);
+
+ $this->daemonServerRepository->expects('setServer->update')->andThrows(new DisplayException('Test'));
+
+ $this->expectException(DisplayException::class);
+
+ $this->getService()->handle($server, ['add_allocations' => [$allocation->id]]);
+
+ $this->assertDatabaseHas('allocations', ['id' => $allocation->id, 'server_id' => null]);
+ }
+
+ /**
+ * @return \Pterodactyl\Services\Servers\BuildModificationService
+ */
+ private function getService()
+ {
+ return $this->app->make(BuildModificationService::class);
+ }
+}
From 26de4493dda2a7ba65a0076f02c187450805ca80 Mon Sep 17 00:00:00 2001
From: Dane Everitt
Date: Mon, 19 Oct 2020 21:08:40 -0700
Subject: [PATCH 06/41] Set notes to null when assigning allocation; ref #2553
---
app/Services/Servers/BuildModificationService.php | 2 +-
.../Services/Servers/BuildModificationServiceTest.php | 3 ++-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/app/Services/Servers/BuildModificationService.php b/app/Services/Servers/BuildModificationService.php
index c42264f8d..43c863831 100644
--- a/app/Services/Servers/BuildModificationService.php
+++ b/app/Services/Servers/BuildModificationService.php
@@ -115,7 +115,7 @@ class BuildModificationService
// one to reset the default allocation to.
$freshlyAllocated = $query->pluck('id')->first();
- $query->update(['server_id' => $server->id]);
+ $query->update(['server_id' => $server->id, 'notes' => null]);
}
if (! empty($data['remove_allocations'])) {
diff --git a/tests/Integration/Services/Servers/BuildModificationServiceTest.php b/tests/Integration/Services/Servers/BuildModificationServiceTest.php
index ab3d6160f..1a4e81e2c 100644
--- a/tests/Integration/Services/Servers/BuildModificationServiceTest.php
+++ b/tests/Integration/Services/Servers/BuildModificationServiceTest.php
@@ -39,7 +39,7 @@ class BuildModificationServiceTest extends IntegrationTestCase
$server2 = $this->createServerModel();
/** @var \Pterodactyl\Models\Allocation[] $allocations */
- $allocations = factory(Allocation::class)->times(4)->create(['node_id' => $server->node_id]);
+ $allocations = factory(Allocation::class)->times(4)->create(['node_id' => $server->node_id, 'notes' => 'Random notes']);
$initialAllocationId = $server->allocation_id;
$allocations[0]->update(['server_id' => $server->id, 'notes' => 'Test notes']);
@@ -66,6 +66,7 @@ class BuildModificationServiceTest extends IntegrationTestCase
// Only one allocation should exist for this server now.
$this->assertCount(1, $response->allocations);
$this->assertSame($allocations[1]->id, $response->allocation_id);
+ $this->assertNull($response->allocation->notes);
// These two allocations should not have been touched.
$this->assertDatabaseHas('allocations', ['id' => $allocations[2]->id, 'server_id' => $server2->id]);
From 16422ebf7b6413fed87475c8ebbc3f76d9697ba3 Mon Sep 17 00:00:00 2001
From: parkervcp
Date: Mon, 19 Oct 2020 20:27:23 -0400
Subject: [PATCH 07/41] remove unused eggs
---
.../egg-terraria-server--t-shock.json | 45 -------------------
1 file changed, 45 deletions(-)
delete mode 100644 database/seeds/eggs/terraria/egg-terraria-server--t-shock.json
diff --git a/database/seeds/eggs/terraria/egg-terraria-server--t-shock.json b/database/seeds/eggs/terraria/egg-terraria-server--t-shock.json
deleted file mode 100644
index 9381890d1..000000000
--- a/database/seeds/eggs/terraria/egg-terraria-server--t-shock.json
+++ /dev/null
@@ -1,45 +0,0 @@
-{
- "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO",
- "meta": {
- "version": "PTDL_v1"
- },
- "exported_at": "2018-01-21T17:01:31-06:00",
- "name": "Terraria Server (TShock)",
- "author": "support@pterodactyl.io",
- "description": "TShock is a server modification for Terraria, written in C#, and based upon the Terraria Server API. It uses JSON for configuration management, and offers several features not present in the Terraria Server normally.",
- "image": "quay.io\/pterodactyl\/core:mono",
- "startup": null,
- "config": {
- "files": "{\"tshock\/config.json\":{\"parser\": \"json\", \"find\":{\"ServerPort\": \"{{server.build.default.port}}\", \"MaxSlots\": \"{{server.build.env.MAX_SLOTS}}\"}}}",
- "startup": "{\"done\": \"Type 'help' for a list of commands\", \"userInteraction\": []}",
- "logs": "{\"custom\": false, \"location\": \"ServerLog.txt\"}",
- "stop": "exit"
- },
- "scripts": {
- "installation": {
- "script": "#!\/bin\/ash\n# TShock Installation Script\n#\n# Server Files: \/mnt\/server\napk update\napk add curl unzip\n\ncd \/tmp\n\ncurl -sSLO https:\/\/github.com\/NyxStudios\/TShock\/releases\/download\/v${T_VERSION}\/tshock_${T_VERSION}.zip\n\nunzip -o tshock_${T_VERSION}.zip -d \/mnt\/server",
- "container": "alpine:3.9",
- "entrypoint": "ash"
- }
- },
- "variables": [
- {
- "name": "TShock Version",
- "description": "Which version of TShock to install and use.",
- "env_variable": "T_VERSION",
- "default_value": "4.3.22",
- "user_viewable": 1,
- "user_editable": 1,
- "rules": "required|regex:\/^([0-9_\\.-]{5,10})$\/"
- },
- {
- "name": "Maximum Slots",
- "description": "Total number of slots to allow on the server.",
- "env_variable": "MAX_SLOTS",
- "default_value": "20",
- "user_viewable": 1,
- "user_editable": 0,
- "rules": "required|numeric|digits_between:1,3"
- }
- ]
-}
From d522bc9150e6c2681ee3b074c628da15a8550cd0 Mon Sep 17 00:00:00 2001
From: parkervcp
Date: Mon, 19 Oct 2020 20:29:03 -0400
Subject: [PATCH 08/41] update install scripts
change all install scripts to use debian:buster-slim
update mc install scripts
update steamcmd install scripts
update voice install scripts.
---
.../seeds/eggs/minecraft/egg-bungeecord.json | 16 +--
.../eggs/minecraft/egg-forge-minecraft.json | 20 +--
database/seeds/eggs/minecraft/egg-paper.json | 24 ++--
.../minecraft/egg-sponge--sponge-vanilla.json | 18 +--
.../eggs/minecraft/egg-vanilla-minecraft.json | 16 +--
database/seeds/eggs/rust/egg-rust.json | 62 ++++-----
.../egg-ark--survival-evolved.json | 124 +++++++++---------
...egg-counter--strike--global-offensive.json | 14 +-
.../egg-custom-source-engine-game.json | 18 +--
.../eggs/source-engine/egg-garrys-mod.json | 34 ++---
.../eggs/source-engine/egg-insurgency.json | 18 +--
.../source-engine/egg-team-fortress2.json | 18 +--
.../eggs/voice-servers/egg-mumble-server.json | 18 +--
.../voice-servers/egg-teamspeak3-server.json | 24 ++--
14 files changed, 212 insertions(+), 212 deletions(-)
diff --git a/database/seeds/eggs/minecraft/egg-bungeecord.json b/database/seeds/eggs/minecraft/egg-bungeecord.json
index b0c07e506..3f28d96b2 100644
--- a/database/seeds/eggs/minecraft/egg-bungeecord.json
+++ b/database/seeds/eggs/minecraft/egg-bungeecord.json
@@ -3,7 +3,7 @@
"meta": {
"version": "PTDL_v1"
},
- "exported_at": "2020-04-12T16:00:51-07:00",
+ "exported_at": "2020-10-19T23:22:26+00: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.",
@@ -17,9 +17,9 @@
},
"scripts": {
"installation": {
- "script": "#!\/bin\/ash\n# Bungeecord Installation Script\n#\n# Server Files: \/mnt\/server\napk update\napk add curl\n\ncd \/mnt\/server\n\nif [ -z \"${BUNGEE_VERSION}\" ] || [ \"${BUNGEE_VERSION}\" == \"latest\" ]; then\n BUNGEE_VERSION=\"lastStableBuild\"\nfi\n\ncurl -o ${SERVER_JARFILE} https:\/\/ci.md-5.net\/job\/BungeeCord\/${BUNGEE_VERSION}\/artifact\/bootstrap\/target\/BungeeCord.jar",
- "container": "alpine:3.9",
- "entrypoint": "ash"
+ "script": "#!\/bin\/bash\r\n# Bungeecord Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napt update\r\napt install -y curl\r\n\r\ncd \/mnt\/server\r\n\r\nif [ -z \"${BUNGEE_VERSION}\" ] || [ \"${BUNGEE_VERSION}\" == \"latest\" ]; then\r\n BUNGEE_VERSION=\"lastStableBuild\"\r\nfi\r\n\r\ncurl -o ${SERVER_JARFILE} https:\/\/ci.md-5.net\/job\/BungeeCord\/${BUNGEE_VERSION}\/artifact\/bootstrap\/target\/BungeeCord.jar",
+ "container": "debian:buster-slim",
+ "entrypoint": "bash"
}
},
"variables": [
@@ -28,8 +28,8 @@
"description": "The version of Bungeecord to download and use.",
"env_variable": "BUNGEE_VERSION",
"default_value": "latest",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|alpha_num|between:1,6"
},
{
@@ -37,8 +37,8 @@
"description": "The name of the Jarfile to use when running Bungeecord.",
"env_variable": "SERVER_JARFILE",
"default_value": "bungeecord.jar",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/"
}
]
diff --git a/database/seeds/eggs/minecraft/egg-forge-minecraft.json b/database/seeds/eggs/minecraft/egg-forge-minecraft.json
index 96b707e51..6f404d1c7 100644
--- a/database/seeds/eggs/minecraft/egg-forge-minecraft.json
+++ b/database/seeds/eggs/minecraft/egg-forge-minecraft.json
@@ -3,7 +3,7 @@
"meta": {
"version": "PTDL_v1"
},
- "exported_at": "2020-05-24T12:15:13-04:00",
+ "exported_at": "2020-10-19T23:22:28+00:00",
"name": "Forge Minecraft",
"author": "support@pterodactyl.io",
"description": "Minecraft Forge Server. Minecraft Forge is a modding API (Application Programming Interface), which makes it easier to create mods, and also make sure mods are compatible with each other.",
@@ -17,7 +17,7 @@
},
"scripts": {
"installation": {
- "script": "#!\/bin\/bash\r\n# Forge Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napt update\r\napt install -y curl jq\r\n\r\n#Go into main direction\r\nif [ ! -d \/mnt\/server ]; then\r\n mkdir \/mnt\/server\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\nif [ ! -z ${FORGE_VERSION} ]; then\r\n DOWNLOAD_LINK=https:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/${FORGE_VERSION}\/forge-${FORGE_VERSION}\r\nelse\r\n JSON_DATA=$(curl -sSL https:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/promotions_slim.json)\r\n\r\n if [ \"${MC_VERSION}\" == \"latest\" ] || [ \"${MC_VERSION}\" == \"\" ] ; then\r\n echo -e \"getting latest recommended version of forge.\"\r\n MC_VERSION=$(echo -e ${JSON_DATA} | jq -r '.promos | del(.\"latest-1.7.10\") | del(.\"1.7.10-latest-1.7.10\") | to_entries[] | .key | select(contains(\"recommended\")) | split(\"-\")[0]' | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n | tail -1)\r\n \tBUILD_TYPE=recommended\r\n fi\r\n\r\n if [ \"${BUILD_TYPE}\" != \"recommended\" ] && [ \"${BUILD_TYPE}\" != \"latest\" ]; then\r\n BUILD_TYPE=recommended\r\n fi\r\n\r\n echo -e \"minecraft version: ${MC_VERSION}\"\r\n echo -e \"build type: ${BUILD_TYPE}\"\r\n\r\n ## some variables for getting versions and things\r\n FILE_SITE=$(echo -e ${JSON_DATA} | jq -r '.homepage' | sed \"s\/http:\/https:\/g\")\r\n VERSION_KEY=$(echo -e ${JSON_DATA} | jq -r --arg MC_VERSION \"${MC_VERSION}\" --arg BUILD_TYPE \"${BUILD_TYPE}\" '.promos | del(.\"latest-1.7.10\") | del(.\"1.7.10-latest-1.7.10\") | to_entries[] | .key | select(contains($MC_VERSION)) | select(contains($BUILD_TYPE))')\r\n\r\n ## locating the forge version\r\n if [ \"${VERSION_KEY}\" == \"\" ] && [ \"${BUILD_TYPE}\" == \"recommended\" ]; then\r\n echo -e \"dropping back to latest from recommended due to there not being a recommended version of forge for the mc version requested.\"\r\n VERSION_KEY=$(echo -e ${JSON_DATA} | jq -r --arg MC_VERSION \"${MC_VERSION}\" '.promos | del(.\"latest-1.7.10\") | del(.\"1.7.10-latest-1.7.10\") | to_entries[] | .key | select(contains($MC_VERSION)) | select(contains(\"recommended\"))')\r\n fi\r\n\r\n ## Error if the mc version set wasn't valid.\r\n if [ \"${VERSION_KEY}\" == \"\" ] || [ \"${VERSION_KEY}\" == \"null\" ]; then\r\n \techo -e \"The install failed because there is no valid version of forge for the version on minecraft selected.\"\r\n \texit 1\r\n fi\r\n\r\n FORGE_VERSION=$(echo -e ${JSON_DATA} | jq -r --arg VERSION_KEY \"$VERSION_KEY\" '.promos | .[$VERSION_KEY]')\r\n\r\n if [ \"${MC_VERSION}\" == \"1.7.10\" ] || [ \"${MC_VERSION}\" == \"1.8.9\" ]; then\r\n DOWNLOAD_LINK=${FILE_SITE}${MC_VERSION}-${FORGE_VERSION}-${MC_VERSION}\/forge-${MC_VERSION}-${FORGE_VERSION}-${MC_VERSION}\r\n FORGE_JAR=forge-${MC_VERSION}-${FORGE_VERSION}-${MC_VERSION}.jar\r\n if [ \"${MC_VERSION}\" == \"1.7.10\" ]; then\r\n FORGE_JAR=forge-${MC_VERSION}-${FORGE_VERSION}-${MC_VERSION}-universal.jar\r\n fi\r\n else\r\n DOWNLOAD_LINK=${FILE_SITE}${MC_VERSION}-${FORGE_VERSION}\/forge-${MC_VERSION}-${FORGE_VERSION}\r\n FORGE_JAR=forge-${MC_VERSION}-${FORGE_VERSION}.jar\r\n fi\r\nfi\r\n\r\n\r\n#Adding .jar when not eding by SERVER_JARFILE\r\nif [[ ! $SERVER_JARFILE = *\\.jar ]]; then\r\n SERVER_JARFILE=\"$SERVER_JARFILE.jar\"\r\nfi\r\n\r\n#Downloading jars\r\necho -e \"Downloading forge version ${FORGE_VERSION}\"\r\nif [ ! -z \"${DOWNLOAD_LINK}\" ]; then \r\n if curl --output \/dev\/null --silent --head --fail ${DOWNLOAD_LINK}-installer.jar; then\r\n echo -e \"installer jar download link is valid.\"\r\n else\r\n echo -e \"link is invalid closing out\"\r\n exit 2\r\n fi\r\n\r\n echo -e \"no download link closing out\"\r\n exit 3\r\nfi\r\n\r\ncurl -s -o installer.jar -sS ${DOWNLOAD_LINK}-installer.jar\r\n\r\n#Checking if downloaded jars exist\r\nif [ ! -f .\/installer.jar ]; then\r\n echo \"!!! Error by downloading forge version ${FORGE_VERSION} !!!\"\r\n exit\r\nfi\r\n\r\n#Installing server\r\necho -e \"Installing forge server.\\n\"\r\njava -jar installer.jar --installServer || { echo -e \"install failed\"; exit 4; }\r\n\r\nmv $FORGE_JAR $SERVER_JARFILE\r\n\r\n#Deleting installer.jar\r\necho -e \"Deleting installer.jar file.\\n\"\r\nrm -rf installer.jar",
+ "script": "#!\/bin\/bash\r\n# Forge Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napt update\r\napt install -y curl jq\r\n\r\n#Go into main direction\r\nif [ ! -d \/mnt\/server ]; then\r\n mkdir \/mnt\/server\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\nif [ ! -z ${FORGE_VERSION} ]; then\r\n DOWNLOAD_LINK=https:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/${FORGE_VERSION}\/forge-${FORGE_VERSION}\r\nelse\r\n JSON_DATA=$(curl -sSL https:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/promotions_slim.json)\r\n\r\n if [ \"${MC_VERSION}\" == \"latest\" ] || [ \"${MC_VERSION}\" == \"\" ] ; then\r\n echo -e \"getting latest recommended version of forge.\"\r\n MC_VERSION=$(echo -e ${JSON_DATA} | jq -r '.promos | del(.\"latest-1.7.10\") | del(.\"1.7.10-latest-1.7.10\") | to_entries[] | .key | select(contains(\"recommended\")) | split(\"-\")[0]' | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n | tail -1)\r\n \tBUILD_TYPE=recommended\r\n fi\r\n\r\n if [ \"${BUILD_TYPE}\" != \"recommended\" ] && [ \"${BUILD_TYPE}\" != \"latest\" ]; then\r\n BUILD_TYPE=recommended\r\n fi\r\n\r\n echo -e \"minecraft version: ${MC_VERSION}\"\r\n echo -e \"build type: ${BUILD_TYPE}\"\r\n\r\n ## some variables for getting versions and things\r\n FILE_SITE=$(echo -e ${JSON_DATA} | jq -r '.homepage' | sed \"s\/http:\/https:\/g\")\r\n VERSION_KEY=$(echo -e ${JSON_DATA} | jq -r --arg MC_VERSION \"${MC_VERSION}\" --arg BUILD_TYPE \"${BUILD_TYPE}\" '.promos | del(.\"latest-1.7.10\") | del(.\"1.7.10-latest-1.7.10\") | to_entries[] | .key | select(contains($MC_VERSION)) | select(contains($BUILD_TYPE))')\r\n\r\n ## locating the forge version\r\n if [ \"${VERSION_KEY}\" == \"\" ] && [ \"${BUILD_TYPE}\" == \"recommended\" ]; then\r\n echo -e \"dropping back to latest from recommended due to there not being a recommended version of forge for the mc version requested.\"\r\n VERSION_KEY=$(echo -e ${JSON_DATA} | jq -r --arg MC_VERSION \"${MC_VERSION}\" '.promos | del(.\"latest-1.7.10\") | del(.\"1.7.10-latest-1.7.10\") | to_entries[] | .key | select(contains($MC_VERSION)) | select(contains(\"recommended\"))')\r\n fi\r\n\r\n ## Error if the mc version set wasn't valid.\r\n if [ \"${VERSION_KEY}\" == \"\" ] || [ \"${VERSION_KEY}\" == \"null\" ]; then\r\n \techo -e \"The install failed because there is no valid version of forge for the version on minecraft selected.\"\r\n \texit 1\r\n fi\r\n\r\n FORGE_VERSION=$(echo -e ${JSON_DATA} | jq -r --arg VERSION_KEY \"$VERSION_KEY\" '.promos | .[$VERSION_KEY]')\r\n\r\n if [ \"${MC_VERSION}\" == \"1.7.10\" ] || [ \"${MC_VERSION}\" == \"1.8.9\" ]; then\r\n DOWNLOAD_LINK=${FILE_SITE}${MC_VERSION}-${FORGE_VERSION}-${MC_VERSION}\/forge-${MC_VERSION}-${FORGE_VERSION}-${MC_VERSION}\r\n FORGE_JAR=forge-${MC_VERSION}-${FORGE_VERSION}-${MC_VERSION}.jar\r\n if [ \"${MC_VERSION}\" == \"1.7.10\" ]; then\r\n FORGE_JAR=forge-${MC_VERSION}-${FORGE_VERSION}-${MC_VERSION}-universal.jar\r\n fi\r\n else\r\n DOWNLOAD_LINK=${FILE_SITE}${MC_VERSION}-${FORGE_VERSION}\/forge-${MC_VERSION}-${FORGE_VERSION}\r\n FORGE_JAR=forge-${MC_VERSION}-${FORGE_VERSION}.jar\r\n fi\r\nfi\r\n\r\n\r\n#Adding .jar when not eding by SERVER_JARFILE\r\nif [[ ! $SERVER_JARFILE = *\\.jar ]]; then\r\n SERVER_JARFILE=\"$SERVER_JARFILE.jar\"\r\nfi\r\n\r\n#Downloading jars\r\necho -e \"Downloading forge version ${FORGE_VERSION}\"\r\necho -e \"Download link is ${DOWNLOAD_LINK}\"\r\nif [ ! -z \"${DOWNLOAD_LINK}\" ]; then \r\n if curl --output \/dev\/null --silent --head --fail ${DOWNLOAD_LINK}-installer.jar; then\r\n echo -e \"installer jar download link is valid.\"\r\n else\r\n echo -e \"link is invalid closing out\"\r\n exit 2\r\n fi\r\nelse\r\n echo -e \"no download link closing out\"\r\n exit 3\r\nfi\r\n\r\ncurl -s -o installer.jar -sS ${DOWNLOAD_LINK}-installer.jar\r\n\r\n#Checking if downloaded jars exist\r\nif [ ! -f .\/installer.jar ]; then\r\n echo \"!!! Error by downloading forge version ${FORGE_VERSION} !!!\"\r\n exit\r\nfi\r\n\r\n#Installing server\r\necho -e \"Installing forge server.\\n\"\r\njava -jar installer.jar --installServer || { echo -e \"install failed\"; exit 4; }\r\n\r\nmv $FORGE_JAR $SERVER_JARFILE\r\n\r\n#Deleting installer.jar\r\necho -e \"Deleting installer.jar file.\\n\"\r\nrm -rf installer.jar",
"container": "openjdk:8-jdk-slim",
"entrypoint": "bash"
}
@@ -28,8 +28,8 @@
"description": "The name of the Jarfile to use when running Forge Mod.",
"env_variable": "SERVER_JARFILE",
"default_value": "server.jar",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/"
},
{
@@ -37,8 +37,8 @@
"description": "The version of minecraft you want to install for.\r\n\r\nLeaving latest will install the latest recommended version.",
"env_variable": "MC_VERSION",
"default_value": "latest",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|string|max:9"
},
{
@@ -46,8 +46,8 @@
"description": "The type of server jar to download from forge.\r\n\r\nValid types are \"recommended\" and \"latest\".",
"env_variable": "BUILD_TYPE",
"default_value": "recommended",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|string|max:20"
},
{
@@ -55,8 +55,8 @@
"description": "Gets an exact version.\r\n\r\nEx. 1.15.2-31.2.4\r\n\r\nOverrides MC_VERSION and BUILD_TYPE. If it fails to download the server files it will fail to install.",
"env_variable": "FORGE_VERSION",
"default_value": "",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|string|max:20"
}
]
diff --git a/database/seeds/eggs/minecraft/egg-paper.json b/database/seeds/eggs/minecraft/egg-paper.json
index 60fde38e0..e402cb50e 100644
--- a/database/seeds/eggs/minecraft/egg-paper.json
+++ b/database/seeds/eggs/minecraft/egg-paper.json
@@ -3,7 +3,7 @@
"meta": {
"version": "PTDL_v1"
},
- "exported_at": "2019-08-01T04:49:37-04:00",
+ "exported_at": "2020-10-19T23:26:07+00:00",
"name": "Paper",
"author": "parker@pterodactyl.io",
"description": "High performance Spigot fork that aims to fix gameplay and mechanics inconsistencies.",
@@ -17,9 +17,9 @@
},
"scripts": {
"installation": {
- "script": "#!\/bin\/ash\r\n# Paper Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napk add --no-cache --update curl jq\r\n\r\nif [ -n \"${DL_PATH}\" ]; then\r\n echo -e \"using supplied download url\"\r\n DOWNLOAD_URL=`eval echo $(echo ${DL_PATH} | sed -e 's\/{{\/${\/g' -e 's\/}}\/}\/g')`\r\nelse\r\n VER_EXISTS=`curl -s https:\/\/papermc.io\/api\/v1\/paper | jq -r --arg VERSION $MINECRAFT_VERSION '.versions[] | IN($VERSION)' | grep true`\r\n LATEST_PAPER_VERSION=`curl -s https:\/\/papermc.io\/api\/v1\/paper | jq -r '.versions' | jq -r '.[0]'`\r\n \r\n if [ \"${VER_EXISTS}\" == \"true\" ]; then\r\n echo -e \"Version is valid. Using version ${MINECRAFT_VERSION}\"\r\n else\r\n echo -e \"Using the latest paper version\"\r\n MINECRAFT_VERSION=${LATEST_PAPER_VERSION}\r\n fi\r\n \r\n BUILD_EXISTS=`curl -s https:\/\/papermc.io\/api\/v1\/paper\/${MINECRAFT_VERSION} | jq -r --arg BUILD ${BUILD_NUMBER} '.builds.all[] | IN($BUILD)' | grep true`\r\n LATEST_PAPER_BUILD=`curl -s https:\/\/papermc.io\/api\/v1\/paper\/${MINECRAFT_VERSION} | jq -r '.builds.latest'`\r\n \r\n if [ \"${BUILD_EXISTS}\" == \"true\" ] || [ ${BUILD_NUMBER} == \"latest\" ]; then\r\n echo -e \"Build is valid. Using version ${BUILD_NUMBER}\"\r\n else\r\n echo -e \"Using the latest paper build\"\r\n BUILD_NUMBER=${LATEST_PAPER_BUILD}\r\n fi\r\n \r\n echo \"Version being downloaded\"\r\n echo -e \"MC Version: ${MINECRAFT_VERSION}\"\r\n echo -e \"Build: ${BUILD_NUMBER}\"\r\n DOWNLOAD_URL=https:\/\/papermc.io\/api\/v1\/paper\/${MINECRAFT_VERSION}\/${BUILD_NUMBER}\/download \r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\necho -e \"running curl -o ${SERVER_JARFILE} ${DOWNLOAD_URL}\"\r\n\r\nif [ -f ${SERVER_JARFILE} ]; then\r\n mv ${SERVER_JARFILE} ${SERVER_JARFILE}.old\r\nfi\r\n\r\ncurl -o ${SERVER_JARFILE} ${DOWNLOAD_URL}\r\n\r\nif [ ! -f server.properties ]; then\r\n echo -e \"Downloading MC server.properties\"\r\n curl -o server.properties https:\/\/raw.githubusercontent.com\/parkervcp\/eggs\/master\/minecraft_java\/server.properties\r\nfi",
- "container": "alpine:3.9",
- "entrypoint": "ash"
+ "script": "#!\/bin\/bash\r\n# Paper Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napt update\r\napt install -y curl jq\r\n\r\nif [ -n \"${DL_PATH}\" ]; then\r\n echo -e \"using supplied download url\"\r\n DOWNLOAD_URL=`eval echo $(echo ${DL_PATH} | sed -e 's\/{{\/${\/g' -e 's\/}}\/}\/g')`\r\nelse\r\n VER_EXISTS=`curl -s https:\/\/papermc.io\/api\/v1\/paper | jq -r --arg VERSION $MINECRAFT_VERSION '.versions[] | IN($VERSION)' | grep true`\r\n LATEST_PAPER_VERSION=`curl -s https:\/\/papermc.io\/api\/v1\/paper | jq -r '.versions' | jq -r '.[0]'`\r\n \r\n if [ \"${VER_EXISTS}\" == \"true\" ]; then\r\n echo -e \"Version is valid. Using version ${MINECRAFT_VERSION}\"\r\n else\r\n echo -e \"Using the latest paper version\"\r\n MINECRAFT_VERSION=${LATEST_PAPER_VERSION}\r\n fi\r\n \r\n BUILD_EXISTS=`curl -s https:\/\/papermc.io\/api\/v1\/paper\/${MINECRAFT_VERSION} | jq -r --arg BUILD ${BUILD_NUMBER} '.builds.all[] | IN($BUILD)' | grep true`\r\n LATEST_PAPER_BUILD=`curl -s https:\/\/papermc.io\/api\/v1\/paper\/${MINECRAFT_VERSION} | jq -r '.builds.latest'`\r\n \r\n if [ \"${BUILD_EXISTS}\" == \"true\" ] || [ ${BUILD_NUMBER} == \"latest\" ]; then\r\n echo -e \"Build is valid. Using version ${BUILD_NUMBER}\"\r\n else\r\n echo -e \"Using the latest paper build\"\r\n BUILD_NUMBER=${LATEST_PAPER_BUILD}\r\n fi\r\n \r\n echo \"Version being downloaded\"\r\n echo -e \"MC Version: ${MINECRAFT_VERSION}\"\r\n echo -e \"Build: ${BUILD_NUMBER}\"\r\n DOWNLOAD_URL=https:\/\/papermc.io\/api\/v1\/paper\/${MINECRAFT_VERSION}\/${BUILD_NUMBER}\/download \r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\necho -e \"running curl -o ${SERVER_JARFILE} ${DOWNLOAD_URL}\"\r\n\r\nif [ -f ${SERVER_JARFILE} ]; then\r\n mv ${SERVER_JARFILE} ${SERVER_JARFILE}.old\r\nfi\r\n\r\ncurl -o ${SERVER_JARFILE} ${DOWNLOAD_URL}\r\n\r\nif [ ! -f server.properties ]; then\r\n echo -e \"Downloading MC server.properties\"\r\n curl -o server.properties https:\/\/raw.githubusercontent.com\/parkervcp\/eggs\/master\/minecraft\/java\/server.properties\r\nfi",
+ "container": "debian:buster-slim",
+ "entrypoint": "bash"
}
},
"variables": [
@@ -28,8 +28,8 @@
"description": "The version of minecraft to download. \r\n\r\nLeave at latest to always get the latest version. Invalid versions will default to latest.",
"env_variable": "MINECRAFT_VERSION",
"default_value": "latest",
- "user_viewable": 1,
- "user_editable": 0,
+ "user_viewable": true,
+ "user_editable": false,
"rules": "nullable|string|max:20"
},
{
@@ -37,8 +37,8 @@
"description": "The name of the server jarfile to run the server with.",
"env_variable": "SERVER_JARFILE",
"default_value": "server.jar",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|string|max:20"
},
{
@@ -46,8 +46,8 @@
"description": "A URL to use to download a server.jar rather than the ones in the install script. This is not user viewable.",
"env_variable": "DL_PATH",
"default_value": "",
- "user_viewable": 0,
- "user_editable": 0,
+ "user_viewable": false,
+ "user_editable": false,
"rules": "nullable|string"
},
{
@@ -55,8 +55,8 @@
"description": "The build number for the paper release.\r\n\r\nLeave at latest to always get the latest version. Invalid versions will default to latest.",
"env_variable": "BUILD_NUMBER",
"default_value": "latest",
- "user_viewable": 1,
- "user_editable": 0,
+ "user_viewable": true,
+ "user_editable": false,
"rules": "required|string|max:20"
}
]
diff --git a/database/seeds/eggs/minecraft/egg-sponge--sponge-vanilla.json b/database/seeds/eggs/minecraft/egg-sponge--sponge-vanilla.json
index 2bbfba23a..cf1a9396d 100644
--- a/database/seeds/eggs/minecraft/egg-sponge--sponge-vanilla.json
+++ b/database/seeds/eggs/minecraft/egg-sponge--sponge-vanilla.json
@@ -3,7 +3,7 @@
"meta": {
"version": "PTDL_v1"
},
- "exported_at": "2017-11-03T22:20:03-05:00",
+ "exported_at": "2020-10-19T23:26:54+00:00",
"name": "Sponge (SpongeVanilla)",
"author": "support@pterodactyl.io",
"description": "SpongeVanilla is the SpongeAPI implementation for Vanilla Minecraft.",
@@ -17,9 +17,9 @@
},
"scripts": {
"installation": {
- "script": "#!\/bin\/ash\n# Sponge Installation Script\n#\n# Server Files: \/mnt\/server\n\napk update\napk add curl\n\ncd \/mnt\/server\n\ncurl -sSL \"https:\/\/repo.spongepowered.org\/maven\/org\/spongepowered\/spongevanilla\/${SPONGE_VERSION}\/spongevanilla-${SPONGE_VERSION}.jar\" -o ${SERVER_JARFILE}",
- "container": "alpine:3.9",
- "entrypoint": "ash"
+ "script": "#!\/bin\/bash\r\n# Sponge Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n\r\napt update\r\napt install -y curl\r\n\r\ncd \/mnt\/server\r\n\r\ncurl -sSL \"https:\/\/repo.spongepowered.org\/maven\/org\/spongepowered\/spongevanilla\/${SPONGE_VERSION}\/spongevanilla-${SPONGE_VERSION}.jar\" -o ${SERVER_JARFILE}",
+ "container": "debian:buster-slim",
+ "entrypoint": "bash"
}
},
"variables": [
@@ -28,8 +28,8 @@
"description": "The version of SpongeVanilla to download and use.",
"env_variable": "SPONGE_VERSION",
"default_value": "1.11.2-6.1.0-BETA-21",
- "user_viewable": 1,
- "user_editable": 0,
+ "user_viewable": true,
+ "user_editable": false,
"rules": "required|regex:\/^([a-zA-Z0-9.\\-_]+)$\/"
},
{
@@ -37,9 +37,9 @@
"description": "The name of the Jarfile to use when running SpongeVanilla.",
"env_variable": "SERVER_JARFILE",
"default_value": "server.jar",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/"
}
]
-}
+}
\ No newline at end of file
diff --git a/database/seeds/eggs/minecraft/egg-vanilla-minecraft.json b/database/seeds/eggs/minecraft/egg-vanilla-minecraft.json
index f613c8150..80d697949 100644
--- a/database/seeds/eggs/minecraft/egg-vanilla-minecraft.json
+++ b/database/seeds/eggs/minecraft/egg-vanilla-minecraft.json
@@ -3,7 +3,7 @@
"meta": {
"version": "PTDL_v1"
},
- "exported_at": "2019-12-25T19:55:01-05:00",
+ "exported_at": "2020-10-19T23:28:18+00:00",
"name": "Vanilla Minecraft",
"author": "support@pterodactyl.io",
"description": "Minecraft is a game about placing blocks and going on adventures. Explore randomly generated worlds and build amazing things from the simplest of homes to the grandest of castles. Play in Creative Mode with unlimited resources or mine deep in Survival Mode, crafting weapons and armor to fend off dangerous mobs. Do all this alone or with friends.",
@@ -17,9 +17,9 @@
},
"scripts": {
"installation": {
- "script": "#!\/bin\/ash\r\n# Vanilla MC Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napk add curl --no-cache --update jq\r\n\r\nmkdir -p \/mnt\/server\r\ncd \/mnt\/server\r\n\r\nLATEST_VERSION=`curl https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq -r '.latest.release'`\r\n\r\necho -e \"latest version is $LATEST_VERSION\"\r\n\r\nif [ -z \"$VANILLA_VERSION\" ] || [ \"$VANILLA_VERSION\" == \"latest\" ]; then\r\n MANIFEST_URL=$(curl -sSL https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq --arg VERSION $LATEST_VERSION -r '.versions | .[] | select(.id== $VERSION )|.url')\r\nelse\r\n MANIFEST_URL=$(curl -sSL https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq --arg VERSION $VANILLA_VERSION -r '.versions | .[] | select(.id== $VERSION )|.url')\r\nfi\r\n\r\nDOWNLOAD_URL=$(curl ${MANIFEST_URL} | jq .downloads.server | jq -r '. | .url')\r\n\r\necho -e \"running: curl -o ${SERVER_JARFILE} $DOWNLOAD_URL\"\r\ncurl -o ${SERVER_JARFILE} $DOWNLOAD_URL\r\n\r\necho -e \"Install Complete\"",
- "container": "alpine:3.10",
- "entrypoint": "ash"
+ "script": "#!\/bin\/bash\r\n# Vanilla MC Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napt update\r\napt install -y jq\r\n\r\nmkdir -p \/mnt\/server\r\ncd \/mnt\/server\r\n\r\nLATEST_VERSION=`curl https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq -r '.latest.release'`\r\n\r\necho -e \"latest version is $LATEST_VERSION\"\r\n\r\nif [ -z \"$VANILLA_VERSION\" ] || [ \"$VANILLA_VERSION\" == \"latest\" ]; then\r\n MANIFEST_URL=$(curl -sSL https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq --arg VERSION $LATEST_VERSION -r '.versions | .[] | select(.id== $VERSION )|.url')\r\nelse\r\n MANIFEST_URL=$(curl -sSL https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq --arg VERSION $VANILLA_VERSION -r '.versions | .[] | select(.id== $VERSION )|.url')\r\nfi\r\n\r\nDOWNLOAD_URL=$(curl ${MANIFEST_URL} | jq .downloads.server | jq -r '. | .url')\r\n\r\necho -e \"running: curl -o ${SERVER_JARFILE} $DOWNLOAD_URL\"\r\ncurl -o ${SERVER_JARFILE} $DOWNLOAD_URL\r\n\r\necho -e \"Install Complete\"",
+ "container": "debian:buster-slim",
+ "entrypoint": "bash"
}
},
"variables": [
@@ -28,8 +28,8 @@
"description": "The name of the server jarfile to run the server with.",
"env_variable": "SERVER_JARFILE",
"default_value": "server.jar",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/"
},
{
@@ -37,8 +37,8 @@
"description": "The version of Minecraft Vanilla to install. Use \"latest\" to install the latest version.",
"env_variable": "VANILLA_VERSION",
"default_value": "latest",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|string|between:3,15"
}
]
diff --git a/database/seeds/eggs/rust/egg-rust.json b/database/seeds/eggs/rust/egg-rust.json
index 7d794cc89..d805da08f 100644
--- a/database/seeds/eggs/rust/egg-rust.json
+++ b/database/seeds/eggs/rust/egg-rust.json
@@ -3,7 +3,7 @@
"meta": {
"version": "PTDL_v1"
},
- "exported_at": "2018-01-21T16:58:36-06:00",
+ "exported_at": "2020-10-20T00:03:09+00: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.",
@@ -17,8 +17,8 @@
},
"scripts": {
"installation": {
- "script": "apt update\r\napt -y --no-install-recommends install curl unzip lib32gcc1 ca-certificates\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\/steam\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steam\r\ncd \/mnt\/server\/steam\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 258550 +quit\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v \/mnt\/server\/steam\/linux32\/steamclient.so \/mnt\/server\/.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 'debian:buster-slim'\r\napt -y update\r\napt -y --no-install-recommends install curl lib32gcc1 ca-certificates\r\n\r\nSRCDS_APPID=258550\r\n\r\n## just in case someone removed the defaults.\r\nif [ \"${STEAM_USER}\" == \"\" ]; then\r\n echo -e \"steam user is not set.\\n\"\r\n echo -e \"Using anonymous user.\\n\"\r\n STEAM_USER=anonymous\r\n STEAM_PASS=\"\"\r\n STEAM_AUTH=\"\"\r\nelse\r\n echo -e \"user set to ${STEAM_USER}\"\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} validate +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": "debian:buster-slim",
"entrypoint": "bash"
}
},
@@ -28,8 +28,8 @@
"description": "The name of your server in the public server list.",
"env_variable": "HOSTNAME",
"default_value": "A Rust Server",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|string|max:40"
},
{
@@ -37,8 +37,8 @@
"description": "Set whether you want the server to use and auto update OxideMod or not. Valid options are \"1\" for true and \"0\" for false.",
"env_variable": "OXIDE",
"default_value": "0",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|boolean"
},
{
@@ -46,8 +46,8 @@
"description": "The world file for Rust to use.",
"env_variable": "LEVEL",
"default_value": "Procedural Map",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|string|max:20"
},
{
@@ -55,8 +55,8 @@
"description": "The description under your server title. Commonly used for rules & info. Use \\n for newlines.",
"env_variable": "DESCRIPTION",
"default_value": "Powered by Pterodactyl",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|string"
},
{
@@ -64,8 +64,8 @@
"description": "The URL for your server. This is what comes up when clicking the \"Visit Website\" button.",
"env_variable": "SERVER_URL",
"default_value": "http:\/\/pterodactyl.io",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "nullable|url"
},
{
@@ -73,8 +73,8 @@
"description": "The world size for a procedural map.",
"env_variable": "WORLD_SIZE",
"default_value": "3000",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|integer"
},
{
@@ -82,8 +82,8 @@
"description": "The seed for a procedural map.",
"env_variable": "WORLD_SEED",
"default_value": "",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "nullable|string"
},
{
@@ -91,8 +91,8 @@
"description": "The maximum amount of players allowed in the server at once.",
"env_variable": "MAX_PLAYERS",
"default_value": "40",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|integer"
},
{
@@ -100,8 +100,8 @@
"description": "The header image for the top of your server listing.",
"env_variable": "SERVER_IMG",
"default_value": "",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "nullable|url"
},
{
@@ -109,8 +109,8 @@
"description": "Port for RCON connections.",
"env_variable": "RCON_PORT",
"default_value": "28016",
- "user_viewable": 1,
- "user_editable": 0,
+ "user_viewable": true,
+ "user_editable": false,
"rules": "required|integer"
},
{
@@ -118,17 +118,17 @@
"description": "RCON access password.",
"env_variable": "RCON_PASS",
"default_value": "CHANGEME",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|string|max:64"
},
{
"name": "Save Interval",
- "description": "Sets the server’s auto-save interval in seconds.",
+ "description": "Sets the server\u2019s auto-save interval in seconds.",
"env_variable": "SAVEINTERVAL",
"default_value": "60",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|integer"
},
{
@@ -136,9 +136,9 @@
"description": "Add additional startup parameters to the server.",
"env_variable": "ADDITIONAL_ARGS",
"default_value": "",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "nullable|string"
}
]
-}
+}
\ No newline at end of file
diff --git a/database/seeds/eggs/source-engine/egg-ark--survival-evolved.json b/database/seeds/eggs/source-engine/egg-ark--survival-evolved.json
index e86a89a9a..627a384a8 100644
--- a/database/seeds/eggs/source-engine/egg-ark--survival-evolved.json
+++ b/database/seeds/eggs/source-engine/egg-ark--survival-evolved.json
@@ -3,78 +3,87 @@
"meta": {
"version": "PTDL_v1"
},
- "exported_at": "2018-10-29T20:51:32+01:00",
+ "exported_at": "2020-10-19T23:29:06+00:00",
"name": "Ark: Survival Evolved",
- "author": "support@pterodactyl.io",
+ "author": "dev@shepper.fr",
"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": "\"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\"",
+ "image": "quay.io\/parkervcp\/pterodactyl-images:debian_source",
+ "startup": "rmv() { echo -e \"stoppping server\"; rcon -a 127.0.0.1:${RCON_PORT} -p ${ARK_ADMIN_PASSWORD} -c saveworld && rcon -a 127.0.0.1:${RCON_PORT} -p ${ARK_ADMIN_PASSWORD} -c DoExit; }; trap rmv 15; cd ShooterGame\/Binaries\/Linux && .\/ShooterGameServer {{SERVER_MAP}}?listen?SessionName=\"{{SESSION_NAME}}\"?ServerPassword={{ARK_PASSWORD}}?ServerAdminPassword={{ARK_ADMIN_PASSWORD}}?Port={{SERVER_PORT}}?RCONPort={{RCON_PORT}}?QueryPort={{QUERY_PORT}}?RCONEnabled={{ENABLE_RCON}}$( [ \"$BATTLE_EYE\" == \"0\" ] || printf %s '?-NoBattlEye' ) -server -log & until echo \"waiting for rcon connection...\"; rcon -a 127.0.0.1:${RCON_PORT} -p ${ARK_ADMIN_PASSWORD}; do sleep 5; done",
"config": {
"files": "{}",
- "startup": "{\r\n \"done\": \"Setting breakpad minidump AppID = 346110\",\r\n \"userInteraction\": []\r\n}",
+ "startup": "{\r\n \"done\": \"Waiting commands for 127.0.0.1:\",\r\n \"userInteraction\": []\r\n}",
"logs": "{\r\n \"custom\": true,\r\n \"location\": \"logs\/latest.log\"\r\n}",
"stop": "^C"
},
"scripts": {
"installation": {
- "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",
+ "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 --no-install-suggests 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\n\r\nmkdir -p \/mnt\/server\/Engine\/Binaries\/ThirdParty\/SteamCMD\/Linux\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\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## create a symbolic link for loading mods\r\ncd \/mnt\/server\/Engine\/Binaries\/ThirdParty\/SteamCMD\/Linux\r\nln -sf ..\/..\/..\/..\/..\/Steam\/steamapps steamapps\r\ncd \/mnt\/server",
+ "container": "debian:buster-slim",
"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.",
"env_variable": "ARK_PASSWORD",
"default_value": "",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "nullable|alpha_dash|between:1,100"
},
{
"name": "Admin Password",
"description": "If specified, players must provide this password (via the in-game console) to gain access to administrator commands on the server.",
"env_variable": "ARK_ADMIN_PASSWORD",
- "default_value": "",
- "user_viewable": 1,
- "user_editable": 1,
- "rules": "nullable|alpha_dash|between:1,100"
+ "default_value": "PleaseChangeMe",
+ "user_viewable": true,
+ "user_editable": true,
+ "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": "Server Map",
+ "description": "Available Maps: TheIsland, TheCenter, Ragnarok, ScorchedEarth_P, Aberration_P, Extinction, Valguero_P, Genesis",
+ "env_variable": "SERVER_MAP",
+ "default_value": "TheIsland",
+ "user_viewable": true,
+ "user_editable": true,
+ "rules": "required|string|max:20"
+ },
+ {
+ "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": true,
+ "user_editable": false,
+ "rules": "nullable|numeric"
+ },
+ {
+ "name": "Server Name",
+ "description": "ARK server name",
+ "env_variable": "SESSION_NAME",
+ "default_value": "A Pterodactyl Hosted ARK Server",
+ "user_viewable": true,
+ "user_editable": true,
+ "rules": "required|string|max:128"
},
{
"name": "Use Rcon",
- "description": "Enable or disable rcon system. (true or false)",
+ "description": "Enable or disable rcon system. (true or false)\r\n\r\nDefault True for the console to work.",
"env_variable": "ENABLE_RCON",
- "default_value": "false",
- "user_viewable": 1,
- "user_editable": 1,
- "rules": "required|string|max:5"
+ "default_value": "True",
+ "user_viewable": true,
+ "user_editable": false,
+ "rules": "required|string|in:True,False"
},
{
"name": "Rcon Port",
"description": "ARK rcon port used by rcon tools.",
"env_variable": "RCON_PORT",
"default_value": "27020",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|numeric"
},
{
@@ -82,36 +91,27 @@
"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,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|numeric"
},
{
- "name": "Maximum Players",
- "description": "Specifies the maximum number of players that can play on the server simultaneously.",
- "env_variable": "SERVER_MAX_PLAYERS",
- "default_value": "20",
- "user_viewable": 1,
- "user_editable": 1,
- "rules": "required|numeric|digits_between:1,4"
+ "name": "Auto-update server",
+ "description": "This is to enable auto-updating for servers.\r\n\r\nDefault is 0. Set to 1 to update",
+ "env_variable": "AUTO_UPDATE",
+ "default_value": "0",
+ "user_viewable": true,
+ "user_editable": true,
+ "rules": "required|boolean"
},
{
- "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, Extinction",
- "env_variable": "SERVER_MAP",
- "default_value": "TheIsland",
- "user_viewable": 1,
- "user_editable": 1,
- "rules": "required|string|max:20"
+ "name": "Ballte Eye",
+ "description": "Enable BattleEye\r\n\r\n0 to disable\r\n1 to enable\r\n\r\ndefault=\"1\"",
+ "env_variable": "BATTLE_EYE",
+ "default_value": "1",
+ "user_viewable": true,
+ "user_editable": true,
+ "rules": "required|boolean"
}
]
-}
+}
\ No newline at end of file
diff --git a/database/seeds/eggs/source-engine/egg-counter--strike--global-offensive.json b/database/seeds/eggs/source-engine/egg-counter--strike--global-offensive.json
index 191aa99ed..b093aa793 100644
--- a/database/seeds/eggs/source-engine/egg-counter--strike--global-offensive.json
+++ b/database/seeds/eggs/source-engine/egg-counter--strike--global-offensive.json
@@ -3,7 +3,7 @@
"meta": {
"version": "PTDL_v1"
},
- "exported_at": "2019-12-08T10:52:19-05:00",
+ "exported_at": "2020-10-19T23:29:57+00: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.",
@@ -28,8 +28,8 @@
"description": "The default map for the server.",
"env_variable": "SRCDS_MAP",
"default_value": "de_dust2",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|string|alpha_dash"
},
{
@@ -37,8 +37,8 @@
"description": "The Steam Account Token required for the server to be displayed publicly.",
"env_variable": "STEAM_ACC",
"default_value": "",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|string|alpha_num|size:32"
},
{
@@ -46,8 +46,8 @@
"description": "Required for game to update on server restart. Do not modify this.",
"env_variable": "SRCDS_APPID",
"default_value": "740",
- "user_viewable": 0,
- "user_editable": 0,
+ "user_viewable": false,
+ "user_editable": false,
"rules": "required|string|max:20"
}
]
diff --git a/database/seeds/eggs/source-engine/egg-custom-source-engine-game.json b/database/seeds/eggs/source-engine/egg-custom-source-engine-game.json
index aae3b3f35..fd6fe014c 100644
--- a/database/seeds/eggs/source-engine/egg-custom-source-engine-game.json
+++ b/database/seeds/eggs/source-engine/egg-custom-source-engine-game.json
@@ -3,7 +3,7 @@
"meta": {
"version": "PTDL_v1"
},
- "exported_at": "2019-12-08T10:54:26-05:00",
+ "exported_at": "2020-10-19T23:33:52+00: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\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",
+ "script": "#!\/bin\/bash\r\n# steamcmd Base Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n# Image to install with is 'debian:buster-slim'\r\n\r\n##\r\n#\r\n# Variables\r\n# STEAM_USER, STEAM_PASS, STEAM_AUTH - Steam user setup. If a user has 2fa enabled it will most likely fail due to timeout. Leave blank for anon install.\r\n# WINDOWS_INSTALL - if it's a windows server you want to install set to 1\r\n# SRCDS_APPID - steam app id ffound here - https:\/\/developer.valvesoftware.com\/wiki\/Dedicated_Servers_List\r\n# EXTRA_FLAGS - when a server has extra glas for things like beta installs or updates.\r\n#\r\n##\r\n\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 echo -e \"steam user is not set.\\n\"\r\n echo -e \"Using anonymous user.\\n\"\r\n STEAM_USER=anonymous\r\n STEAM_PASS=\"\"\r\n STEAM_AUTH=\"\"\r\nelse\r\n echo -e \"user set to ${STEAM_USER}\"\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} $( [[ \"${WINDOWS_INSTALL}\" == \"1\" ]] && printf %s '+@sSteamCmdForcePlatformType windows' ) +force_install_dir \/mnt\/server +app_update ${SRCDS_APPID} ${EXTRA_FLAGS} validate +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": "debian:buster-slim",
"entrypoint": "bash"
}
},
@@ -28,8 +28,8 @@
"description": "The ID corresponding to the game to download and run using SRCDS.",
"env_variable": "SRCDS_APPID",
"default_value": "",
- "user_viewable": 1,
- "user_editable": 0,
+ "user_viewable": true,
+ "user_editable": false,
"rules": "required|numeric|digits_between:1,6"
},
{
@@ -37,8 +37,8 @@
"description": "The name corresponding to the game to download and run using SRCDS.",
"env_variable": "SRCDS_GAME",
"default_value": "",
- "user_viewable": 1,
- "user_editable": 0,
+ "user_viewable": true,
+ "user_editable": false,
"rules": "required|alpha_dash|between:1,100"
},
{
@@ -46,8 +46,8 @@
"description": "The default map for the server.",
"env_variable": "SRCDS_MAP",
"default_value": "",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|string|alpha_dash"
}
]
diff --git a/database/seeds/eggs/source-engine/egg-garrys-mod.json b/database/seeds/eggs/source-engine/egg-garrys-mod.json
index 17940e8fb..d2201a6c3 100644
--- a/database/seeds/eggs/source-engine/egg-garrys-mod.json
+++ b/database/seeds/eggs/source-engine/egg-garrys-mod.json
@@ -3,7 +3,7 @@
"meta": {
"version": "PTDL_v1"
},
- "exported_at": "2019-12-08T10:56:42-05:00",
+ "exported_at": "2020-10-19T23:34:44+00: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# 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",
+ "script": "#!\/bin\/bash\r\n# steamcmd Base Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n# Image to install with is 'debian:buster-slim'\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 echo -e \"steam user is not set.\\n\"\r\n echo -e \"Using anonymous user.\\n\"\r\n STEAM_USER=anonymous\r\n STEAM_PASS=\"\"\r\n STEAM_AUTH=\"\"\r\nelse\r\n echo -e \"user set to ${STEAM_USER}\"\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} $( [[ \"${WINDOWS_INSTALL}\" == \"1\" ]] && printf %s '+@sSteamCmdForcePlatformType windows' ) +force_install_dir \/mnt\/server +app_update ${SRCDS_APPID} ${EXTRA_FLAGS} validate +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": "debian:buster-slim",
"entrypoint": "bash"
}
},
@@ -28,8 +28,8 @@
"description": "The default map for the server.",
"env_variable": "SRCDS_MAP",
"default_value": "gm_flatgrass",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|string|alpha_dash"
},
{
@@ -37,8 +37,8 @@
"description": "The Steam Account Token required for the server to be displayed publicly.",
"env_variable": "STEAM_ACC",
"default_value": "",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "nullable|string|alpha_num|size:32"
},
{
@@ -46,8 +46,8 @@
"description": "Required for game to update on server restart. Do not modify this.",
"env_variable": "SRCDS_APPID",
"default_value": "4020",
- "user_viewable": 0,
- "user_editable": 0,
+ "user_viewable": false,
+ "user_editable": false,
"rules": "required|string|max:20"
},
{
@@ -55,8 +55,8 @@
"description": "The ID of your workshop collection (the numbers at the end of the URL)",
"env_variable": "WORKSHOP_ID",
"default_value": "",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "nullable|integer"
},
{
@@ -64,8 +64,8 @@
"description": "The gamemode of your server.",
"env_variable": "GAMEMODE",
"default_value": "sandbox",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|string"
},
{
@@ -73,8 +73,8 @@
"description": "The maximum amount of players allowed on your game server.",
"env_variable": "MAX_PLAYERS",
"default_value": "32",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|integer|max:128"
},
{
@@ -82,8 +82,8 @@
"description": "The tickrate defines how fast the server will update each entities location.",
"env_variable": "TICKRATE",
"default_value": "22",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|integer|max:100"
}
]
diff --git a/database/seeds/eggs/source-engine/egg-insurgency.json b/database/seeds/eggs/source-engine/egg-insurgency.json
index 7f0c76be2..3103ac244 100644
--- a/database/seeds/eggs/source-engine/egg-insurgency.json
+++ b/database/seeds/eggs/source-engine/egg-insurgency.json
@@ -3,7 +3,7 @@
"meta": {
"version": "PTDL_v1"
},
- "exported_at": "2019-12-08T10:57:32-05:00",
+ "exported_at": "2020-10-19T23:35:42+00: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\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",
+ "script": "#!\/bin\/bash\r\n# steamcmd Base Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n# Image to install with is 'debian:buster-slim'\r\n\r\n##\r\n#\r\n# Variables\r\n# STEAM_USER, STEAM_PASS, STEAM_AUTH - Steam user setup. If a user has 2fa enabled it will most likely fail due to timeout. Leave blank for anon install.\r\n# WINDOWS_INSTALL - if it's a windows server you want to install set to 1\r\n# SRCDS_APPID - steam app id ffound here - https:\/\/developer.valvesoftware.com\/wiki\/Dedicated_Servers_List\r\n# EXTRA_FLAGS - when a server has extra glas for things like beta installs or updates.\r\n#\r\n##\r\n\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 echo -e \"steam user is not set.\\n\"\r\n echo -e \"Using anonymous user.\\n\"\r\n STEAM_USER=anonymous\r\n STEAM_PASS=\"\"\r\n STEAM_AUTH=\"\"\r\nelse\r\n echo -e \"user set to ${STEAM_USER}\"\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} $( [[ \"${WINDOWS_INSTALL}\" == \"1\" ]] && printf %s '+@sSteamCmdForcePlatformType windows' ) +force_install_dir \/mnt\/server +app_update ${SRCDS_APPID} ${EXTRA_FLAGS} validate +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": "debian:buster-slim",
"entrypoint": "bash"
}
},
@@ -28,8 +28,8 @@
"description": "The ID corresponding to the game to download and run using SRCDS.",
"env_variable": "SRCDS_APPID",
"default_value": "237410",
- "user_viewable": 1,
- "user_editable": 0,
+ "user_viewable": true,
+ "user_editable": false,
"rules": "required|regex:\/^(237410)$\/"
},
{
@@ -37,8 +37,8 @@
"description": "The name corresponding to the game to download and run using SRCDS.",
"env_variable": "SRCDS_GAME",
"default_value": "insurgency",
- "user_viewable": 1,
- "user_editable": 0,
+ "user_viewable": true,
+ "user_editable": false,
"rules": "required|regex:\/^(insurgency)$\/"
},
{
@@ -46,8 +46,8 @@
"description": "The default map to use when starting the server.",
"env_variable": "SRCDS_MAP",
"default_value": "sinjar",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|regex:\/^(\\w{1,20})$\/"
}
]
diff --git a/database/seeds/eggs/source-engine/egg-team-fortress2.json b/database/seeds/eggs/source-engine/egg-team-fortress2.json
index 159e7bf9b..8b619642f 100644
--- a/database/seeds/eggs/source-engine/egg-team-fortress2.json
+++ b/database/seeds/eggs/source-engine/egg-team-fortress2.json
@@ -3,7 +3,7 @@
"meta": {
"version": "PTDL_v1"
},
- "exported_at": "2019-12-08T10:58:48-05:00",
+ "exported_at": "2020-10-19T23:36:44+00: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\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",
+ "script": "#!\/bin\/bash\r\n# steamcmd Base Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n# Image to install with is 'debian:buster-slim'\r\n\r\n##\r\n#\r\n# Variables\r\n# STEAM_USER, STEAM_PASS, STEAM_AUTH - Steam user setup. If a user has 2fa enabled it will most likely fail due to timeout. Leave blank for anon install.\r\n# WINDOWS_INSTALL - if it's a windows server you want to install set to 1\r\n# SRCDS_APPID - steam app id ffound here - https:\/\/developer.valvesoftware.com\/wiki\/Dedicated_Servers_List\r\n# EXTRA_FLAGS - when a server has extra glas for things like beta installs or updates.\r\n#\r\n##\r\n\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 echo -e \"steam user is not set.\\n\"\r\n echo -e \"Using anonymous user.\\n\"\r\n STEAM_USER=anonymous\r\n STEAM_PASS=\"\"\r\n STEAM_AUTH=\"\"\r\nelse\r\n echo -e \"user set to ${STEAM_USER}\"\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} $( [[ \"${WINDOWS_INSTALL}\" == \"1\" ]] && printf %s '+@sSteamCmdForcePlatformType windows' ) +force_install_dir \/mnt\/server +app_update ${SRCDS_APPID} ${EXTRA_FLAGS} validate +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": "debian:buster-slim",
"entrypoint": "bash"
}
},
@@ -28,8 +28,8 @@
"description": "The ID corresponding to the game to download and run using SRCDS.",
"env_variable": "SRCDS_APPID",
"default_value": "232250",
- "user_viewable": 1,
- "user_editable": 0,
+ "user_viewable": true,
+ "user_editable": false,
"rules": "required|regex:\/^(232250)$\/"
},
{
@@ -37,8 +37,8 @@
"description": "The name corresponding to the game to download and run using SRCDS.",
"env_variable": "SRCDS_GAME",
"default_value": "tf",
- "user_viewable": 1,
- "user_editable": 0,
+ "user_viewable": true,
+ "user_editable": false,
"rules": "required|regex:\/^(tf)$\/"
},
{
@@ -46,8 +46,8 @@
"description": "The default map to use when starting the server.",
"env_variable": "SRCDS_MAP",
"default_value": "cp_dustbowl",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|regex:\/^(\\w{1,20})$\/"
}
]
diff --git a/database/seeds/eggs/voice-servers/egg-mumble-server.json b/database/seeds/eggs/voice-servers/egg-mumble-server.json
index 00c87d21e..e9215ca9f 100644
--- a/database/seeds/eggs/voice-servers/egg-mumble-server.json
+++ b/database/seeds/eggs/voice-servers/egg-mumble-server.json
@@ -3,7 +3,7 @@
"meta": {
"version": "PTDL_v1"
},
- "exported_at": "2018-01-21T17:01:44-06:00",
+ "exported_at": "2020-10-20T00:22:14+00:00",
"name": "Mumble Server",
"author": "support@pterodactyl.io",
"description": "Mumble is an open source, low-latency, high quality voice chat software primarily intended for use while gaming.",
@@ -17,9 +17,9 @@
},
"scripts": {
"installation": {
- "script": "#!\/bin\/ash\n# Mumble Installation Script\n#\n# Server Files: \/mnt\/server\napk update\napk add tar curl\n\ncd \/tmp\n\ncurl -sSLO https:\/\/github.com\/mumble-voip\/mumble\/releases\/download\/${MUMBLE_VERSION}\/murmur-static_x86-${MUMBLE_VERSION}.tar.bz2\n\ntar -xjvf murmur-static_x86-${MUMBLE_VERSION}.tar.bz2\ncp -r murmur-static_x86-${MUMBLE_VERSION}\/* \/mnt\/server",
- "container": "alpine:3.9",
- "entrypoint": "ash"
+ "script": "#!\/bin\/bash\r\n# Mumble Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\nGITHUB_PACKAGE=mumble-voip\/mumble\r\nMATCH=murmur-static\r\n\r\napt update\r\napt install -y tar curl jq\r\n\r\nif [ ! -d \/mnt\/server\/ ]; then\r\n mkdir \/mnt\/server\/\r\nfi\r\n\r\ncd \/tmp\r\n\r\nif [ -z \"${GITHUB_USER}\" ] && [ -z \"${GITHUB_OAUTH_TOKEN}\" ] ; then\r\n echo -e \"using anon api call\"\r\nelse\r\n echo -e \"user and oauth token set\"\r\n alias curl='curl -u ${GITHUB_USER}:${GITHUB_OAUTH_TOKEN} '\r\nfi\r\n\r\n## get release info and download links\r\nLATEST_JSON=$(curl --silent \"https:\/\/api.github.com\/repos\/${GITHUB_PACKAGE}\/releases\/latest\")\r\nRELEASES=$(curl --silent \"https:\/\/api.github.com\/repos\/${GITHUB_PACKAGE}\/releases\")\r\n\r\nif [ -z \"${VERSION}\" ] || [ \"${VERSION}\" == \"latest\" ]; then\r\n DOWNLOAD_LINK=$(echo ${LATEST_JSON} | jq .assets | jq -r .[].browser_download_url | grep -m 1 -i ${MATCH})\r\nelse\r\n VERSION_CHECK=$(echo ${RELEASES} | jq -r --arg VERSION \"${VERSION}\" '.[] | select(.tag_name==$VERSION) | .tag_name')\r\n if [ \"${VERSION}\" == \"${VERSION_CHECK}\" ]; then\r\n DOWNLOAD_LINK=$(echo ${RELEASES} | jq -r --arg VERSION \"${VERSION}\" '.[] | select(.tag_name==$VERSION) | .assets[].browser_download_url' | grep -m 1 -i ${MATCH})\r\n else\r\n echo -e \"defaulting to latest release\"\r\n DOWNLOAD_LINK=$(echo ${LATEST_JSON} | jq .assets | jq -r .[].browser_download_url)\r\n fi\r\nfi\r\n\r\ncurl -L ${DOWNLOAD_LINK} -o mumble-server.tar.bz2\r\n\r\ntar -xjvf mumble-server.tar.bz2\r\ncp -r murmur-static_x86-*\/* \/mnt\/server",
+ "container": "debian:buster-slim",
+ "entrypoint": "bash"
}
},
"variables": [
@@ -28,8 +28,8 @@
"description": "Maximum concurrent users on the mumble server.",
"env_variable": "MAX_USERS",
"default_value": "100",
- "user_viewable": 1,
- "user_editable": 0,
+ "user_viewable": true,
+ "user_editable": false,
"rules": "required|numeric|digits_between:1,5"
},
{
@@ -37,9 +37,9 @@
"description": "Version of Mumble Server to download and use.",
"env_variable": "MUMBLE_VERSION",
"default_value": "1.3.1",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|regex:\/^([0-9_\\.-]{5,8})$\/"
}
]
-}
+}
\ No newline at end of file
diff --git a/database/seeds/eggs/voice-servers/egg-teamspeak3-server.json b/database/seeds/eggs/voice-servers/egg-teamspeak3-server.json
index d3d889bc0..e0ae85d8d 100644
--- a/database/seeds/eggs/voice-servers/egg-teamspeak3-server.json
+++ b/database/seeds/eggs/voice-servers/egg-teamspeak3-server.json
@@ -3,23 +3,23 @@
"meta": {
"version": "PTDL_v1"
},
- "exported_at": "2019-07-05T11:59:29-04:00",
+ "exported_at": "2020-10-20T00:24:22+00:00",
"name": "Teamspeak3 Server",
"author": "support@pterodactyl.io",
"description": "VoIP software designed with security in mind, featuring crystal clear voice quality, endless customization options, and scalabilty up to thousands of simultaneous users.",
- "image": "quay.io/parkervcp/pterodactyl-images:base_debian",
- "startup": "./ts3server default_voice_port={{SERVER_PORT}} query_port={{SERVER_PORT}} filetransfer_ip=0.0.0.0 filetransfer_port={{FILE_TRANSFER}} license_accepted=1",
+ "image": "quay.io\/parkervcp\/pterodactyl-images:base_debian",
+ "startup": ".\/ts3server default_voice_port={{SERVER_PORT}} query_port={{SERVER_PORT}} filetransfer_ip=0.0.0.0 filetransfer_port={{FILE_TRANSFER}} license_accepted=1",
"config": {
"files": "{}",
"startup": "{\r\n \"done\": \"listening on 0.0.0.0:\",\r\n \"userInteraction\": []\r\n}",
- "logs": "{\r\n \"custom\": true,\r\n \"location\": \"logs/ts3.log\"\r\n}",
+ "logs": "{\r\n \"custom\": true,\r\n \"location\": \"logs\/ts3.log\"\r\n}",
"stop": "^C"
},
"scripts": {
"installation": {
- "script": "#!/bin/ash\r\n# TS3 Installation Script\r\n#\r\n# Server Files: /mnt/server\r\napk add --no-cache tar curl jq\r\n\r\nif [ -z ${TS_VERSION} ] || [ ${TS_VERSION} == latest ]; then\r\n TS_VERSION=$(wget https://teamspeak.com/versions/server.json -qO - | jq -r '.linux.x86_64.version')\r\nfi\r\n\r\ncd /mnt/server\r\n\r\n\r\necho -e \"getting files from http://files.teamspeak-services.com/releases/server/${TS_VERSION}/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2\"\r\ncurl http://files.teamspeak-services.com/releases/server/${TS_VERSION}/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2 | tar xj --strip-components=1",
- "container": "alpine:3.9",
- "entrypoint": "ash"
+ "script": "#!\/bin\/bash\r\n# TS3 Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napk add --no-cache tar curl jq\r\n\r\nif [ -z ${TS_VERSION} ] || [ ${TS_VERSION} == latest ]; then\r\n TS_VERSION=$(wget https:\/\/teamspeak.com\/versions\/server.json -qO - | jq -r '.linux.x86_64.version')\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\n\r\necho -e \"getting files from http:\/\/files.teamspeak-services.com\/releases\/server\/${TS_VERSION}\/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2\"\r\ncurl http:\/\/files.teamspeak-services.com\/releases\/server\/${TS_VERSION}\/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2 | tar xj --strip-components=1",
+ "container": "debian:buster-slim",
+ "entrypoint": "bash"
}
},
"variables": [
@@ -28,8 +28,8 @@
"description": "The version of Teamspeak 3 to use when running the server.",
"env_variable": "TS_VERSION",
"default_value": "latest",
- "user_viewable": 1,
- "user_editable": 1,
+ "user_viewable": true,
+ "user_editable": true,
"rules": "required|string|max:6"
},
{
@@ -37,9 +37,9 @@
"description": "The Teamspeak file transfer port",
"env_variable": "FILE_TRANSFER",
"default_value": "30033",
- "user_viewable": 1,
- "user_editable": 0,
+ "user_viewable": true,
+ "user_editable": false,
"rules": "required|integer|between:1,65535"
}
]
-}
+}
\ No newline at end of file
From 65d04d0c051a8e0013d63fd43a347e06daf81e20 Mon Sep 17 00:00:00 2001
From: Dane Everitt
Date: Thu, 22 Oct 2020 20:54:58 -0700
Subject: [PATCH 09/41] Correctly handle schedule task deletion and avoid
errors; closes #2534
---
.../Client/Servers/ScheduleTaskController.php | 16 +++-
.../Schedules/ProcessScheduleService.php | 2 +-
.../ScheduleTask/DeleteScheduleTaskTest.php | 84 +++++++++++++++++++
.../Schedules/ProcessScheduleServiceTest.php | 31 +++++++
4 files changed, 128 insertions(+), 5 deletions(-)
create mode 100644 tests/Integration/Api/Client/Server/ScheduleTask/DeleteScheduleTaskTest.php
diff --git a/app/Http/Controllers/Api/Client/Servers/ScheduleTaskController.php b/app/Http/Controllers/Api/Client/Servers/ScheduleTaskController.php
index 0d613b6b0..c6f8ee339 100644
--- a/app/Http/Controllers/Api/Client/Servers/ScheduleTaskController.php
+++ b/app/Http/Controllers/Api/Client/Servers/ScheduleTaskController.php
@@ -56,7 +56,8 @@ class ScheduleTaskController extends ClientApiController
);
}
- $lastTask = $schedule->tasks->last();
+ /** @var \Pterodactyl\Models\Task|null $lastTask */
+ $lastTask = $schedule->tasks()->orderByDesc('sequence_id')->first();
/** @var \Pterodactyl\Models\Task $task */
$task = $this->repository->create([
@@ -102,13 +103,16 @@ class ScheduleTaskController extends ClientApiController
}
/**
- * Determines if a user can delete the task for a given server.
+ * Delete a given task for a schedule. If there are subsequent tasks stored in the database
+ * for this schedule their sequence IDs are decremented properly.
*
* @param \Pterodactyl\Http\Requests\Api\Client\ClientApiRequest $request
* @param \Pterodactyl\Models\Server $server
* @param \Pterodactyl\Models\Schedule $schedule
* @param \Pterodactyl\Models\Task $task
* @return \Illuminate\Http\JsonResponse
+ *
+ * @throws \Exception
*/
public function delete(ClientApiRequest $request, Server $server, Schedule $schedule, Task $task)
{
@@ -120,8 +124,12 @@ class ScheduleTaskController extends ClientApiController
throw new HttpForbiddenException('You do not have permission to perform this action.');
}
- $this->repository->delete($task->id);
+ $schedule->tasks()->where('sequence_id', '>', $task->sequence_id)->update([
+ 'sequence_id' => $schedule->tasks()->getConnection()->raw('(sequence_id - 1)'),
+ ]);
- return JsonResponse::create(null, Response::HTTP_NO_CONTENT);
+ $task->delete();
+
+ return new JsonResponse(null, Response::HTTP_NO_CONTENT);
}
}
diff --git a/app/Services/Schedules/ProcessScheduleService.php b/app/Services/Schedules/ProcessScheduleService.php
index 1f810d6f5..ec16bc36a 100644
--- a/app/Services/Schedules/ProcessScheduleService.php
+++ b/app/Services/Schedules/ProcessScheduleService.php
@@ -43,7 +43,7 @@ class ProcessScheduleService
public function handle(Schedule $schedule, bool $now = false)
{
/** @var \Pterodactyl\Models\Task $task */
- $task = $schedule->tasks()->where('sequence_id', 1)->first();
+ $task = $schedule->tasks()->orderBy('sequence_id', 'asc')->first();
if (is_null($task)) {
throw new DisplayException(
diff --git a/tests/Integration/Api/Client/Server/ScheduleTask/DeleteScheduleTaskTest.php b/tests/Integration/Api/Client/Server/ScheduleTask/DeleteScheduleTaskTest.php
new file mode 100644
index 000000000..ef3e1dee4
--- /dev/null
+++ b/tests/Integration/Api/Client/Server/ScheduleTask/DeleteScheduleTaskTest.php
@@ -0,0 +1,84 @@
+createServerModel();
+ [$user] = $this->generateTestAccount();
+
+ $schedule = factory(Schedule::class)->create(['server_id' => $server2->id]);
+ $task = factory(Task::class)->create(['schedule_id' => $schedule->id]);
+
+ $this->actingAs($user)->deleteJson($this->link($task))->assertNotFound();
+ }
+
+ /**
+ * Test that an error is returned if the task and schedule in the URL do not line up
+ * with eachother.
+ */
+ public function testTaskBelongingToDifferentScheduleReturnsError()
+ {
+ [$user, $server] = $this->generateTestAccount();
+
+ $schedule = factory(Schedule::class)->create(['server_id' => $server->id]);
+ $schedule2 = factory(Schedule::class)->create(['server_id' => $server->id]);
+ $task = factory(Task::class)->create(['schedule_id' => $schedule->id]);
+
+ $this->actingAs($user)->deleteJson("/api/client/servers/{$server->uuid}/schedules/{$schedule2->id}/tasks/{$task->id}")->assertNotFound();
+ }
+
+ /**
+ * Test that a user without the required permissions returns an error.
+ */
+ public function testUserWithoutPermissionReturnsError()
+ {
+ [$user, $server] = $this->generateTestAccount([Permission::ACTION_SCHEDULE_CREATE]);
+
+ $schedule = factory(Schedule::class)->create(['server_id' => $server->id]);
+ $task = factory(Task::class)->create(['schedule_id' => $schedule->id]);
+
+ $this->actingAs($user)->deleteJson($this->link($task))->assertForbidden();
+
+ $user2 = factory(User::class)->create();
+
+ $this->actingAs($user2)->deleteJson($this->link($task))->assertNotFound();
+ }
+
+ /**
+ * Test that a schedule task is deleted and items with a higher sequence ID are decremented
+ * properly in the database.
+ */
+ public function testScheduleTaskIsDeletedAndSubsequentTasksAreUpdated()
+ {
+ [$user, $server] = $this->generateTestAccount();
+
+ $schedule = factory(Schedule::class)->create(['server_id' => $server->id]);
+ $tasks = [
+ factory(Task::class)->create(['schedule_id' => $schedule->id, 'sequence_id' => 1]),
+ factory(Task::class)->create(['schedule_id' => $schedule->id, 'sequence_id' => 2]),
+ factory(Task::class)->create(['schedule_id' => $schedule->id, 'sequence_id' => 3]),
+ factory(Task::class)->create(['schedule_id' => $schedule->id, 'sequence_id' => 4]),
+ ];
+
+ $response = $this->actingAs($user)->deleteJson($this->link($tasks[1]));
+ $response->assertStatus(Response::HTTP_NO_CONTENT);
+
+ $this->assertDatabaseHas('tasks', ['id' => $tasks[0]->id, 'sequence_id' => 1]);
+ $this->assertDatabaseHas('tasks', ['id' => $tasks[2]->id, 'sequence_id' => 2]);
+ $this->assertDatabaseHas('tasks', ['id' => $tasks[3]->id, 'sequence_id' => 3]);
+ $this->assertDatabaseMissing('tasks', ['id' => $tasks[1]->id]);
+ }
+}
diff --git a/tests/Integration/Services/Schedules/ProcessScheduleServiceTest.php b/tests/Integration/Services/Schedules/ProcessScheduleServiceTest.php
index 4941a9bd9..a5a4f65f3 100644
--- a/tests/Integration/Services/Schedules/ProcessScheduleServiceTest.php
+++ b/tests/Integration/Services/Schedules/ProcessScheduleServiceTest.php
@@ -81,6 +81,37 @@ class ProcessScheduleServiceTest extends IntegrationTestCase
$this->assertDatabaseHas('tasks', ['id' => $task->id, 'is_queued' => true]);
}
+ /**
+ * Test that even if a schedule's task sequence gets messed up the first task based on
+ * the ascending order of tasks is used.
+ *
+ * @see https://github.com/pterodactyl/panel/issues/2534
+ */
+ public function testFirstSequenceTaskIsFound()
+ {
+ $this->swap(Dispatcher::class, $dispatcher = Mockery::mock(Dispatcher::class));
+
+ $server = $this->createServerModel();
+ /** @var \Pterodactyl\Models\Schedule $schedule */
+ $schedule = factory(Schedule::class)->create(['server_id' => $server->id]);
+
+ /** @var \Pterodactyl\Models\Task $task */
+ $task2 = factory(Task::class)->create(['schedule_id' => $schedule->id, 'sequence_id' => 4]);
+ $task = factory(Task::class)->create(['schedule_id' => $schedule->id, 'sequence_id' => 2]);
+ $task3 = factory(Task::class)->create(['schedule_id' => $schedule->id, 'sequence_id' => 3]);
+
+ $dispatcher->expects('dispatch')->with(Mockery::on(function (RunTaskJob $job) use ($task) {
+ return $task->id === $job->task->id;
+ }));
+
+ $this->getService()->handle($schedule);
+
+ $this->assertDatabaseHas('schedules', ['id' => $schedule->id, 'is_processing' => true]);
+ $this->assertDatabaseHas('tasks', ['id' => $task->id, 'is_queued' => true]);
+ $this->assertDatabaseHas('tasks', ['id' => $task2->id, 'is_queued' => false]);
+ $this->assertDatabaseHas('tasks', ['id' => $task3->id, 'is_queued' => false]);
+ }
+
/**
* @return array
*/
From 903b5795dbf74f1ca01093b799703792faa7d0ad Mon Sep 17 00:00:00 2001
From: Dane Everitt
Date: Thu, 22 Oct 2020 21:18:46 -0700
Subject: [PATCH 10/41] Avoid breaking the entire UI when naughty characters
are present in the file name or directory; closes #2575
---
.../api/server/files/saveFileContents.ts | 20 +++-------
.../components/elements/ErrorBoundary.tsx | 39 +++++++++++++++++++
.../server/files/FileEditContainer.tsx | 9 +++--
.../server/files/FileManagerBreadcrumbs.tsx | 6 +--
.../server/files/FileManagerContainer.tsx | 31 ++++++++-------
.../server/files/NewDirectoryButton.tsx | 7 ++--
resources/scripts/routers/ServerRouter.tsx | 5 ++-
7 files changed, 78 insertions(+), 39 deletions(-)
create mode 100644 resources/scripts/components/elements/ErrorBoundary.tsx
diff --git a/resources/scripts/api/server/files/saveFileContents.ts b/resources/scripts/api/server/files/saveFileContents.ts
index 22f1766c3..b97e60a6b 100644
--- a/resources/scripts/api/server/files/saveFileContents.ts
+++ b/resources/scripts/api/server/files/saveFileContents.ts
@@ -1,18 +1,10 @@
import http from '@/api/http';
-export default (uuid: string, file: string, content: string): Promise => {
- return new Promise((resolve, reject) => {
- http.post(
- `/api/client/servers/${uuid}/files/write`,
- content,
- {
- params: { file },
- headers: {
- 'Content-Type': 'text/plain',
- },
- },
- )
- .then(() => resolve())
- .catch(reject);
+export default async (uuid: string, file: string, content: string): Promise => {
+ await http.post(`/api/client/servers/${uuid}/files/write`, content, {
+ params: { file },
+ headers: {
+ 'Content-Type': 'text/plain',
+ },
});
};
diff --git a/resources/scripts/components/elements/ErrorBoundary.tsx b/resources/scripts/components/elements/ErrorBoundary.tsx
new file mode 100644
index 000000000..418d9e06f
--- /dev/null
+++ b/resources/scripts/components/elements/ErrorBoundary.tsx
@@ -0,0 +1,39 @@
+import React from 'react';
+import tw from 'twin.macro';
+import Icon from '@/components/elements/Icon';
+import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
+
+interface State {
+ hasError: boolean;
+}
+
+// eslint-disable-next-line @typescript-eslint/ban-types
+class ErrorBoundary extends React.Component<{}, State> {
+ state: State = {
+ hasError: false,
+ };
+
+ static getDerivedStateFromError () {
+ return { hasError: true };
+ }
+
+ componentDidCatch (error: Error) {
+ console.error(error);
+ }
+
+ render () {
+ return this.state.hasError ?
+
+
+
+
+ An error was encountered by the application while rendering this view. Try refreshing the page.
+
+
+
+ :
+ this.props.children;
+ }
+}
+
+export default ErrorBoundary;
diff --git a/resources/scripts/components/server/files/FileEditContainer.tsx b/resources/scripts/components/server/files/FileEditContainer.tsx
index 1faf99e5a..8305e04bd 100644
--- a/resources/scripts/components/server/files/FileEditContainer.tsx
+++ b/resources/scripts/components/server/files/FileEditContainer.tsx
@@ -16,6 +16,7 @@ import Select from '@/components/elements/Select';
import modes from '@/modes';
import useFlash from '@/plugins/useFlash';
import { ServerContext } from '@/state/server';
+import ErrorBoundary from '@/components/elements/ErrorBoundary';
const LazyCodemirrorEditor = lazy(() => import(/* webpackChunkName: "editor" */'@/components/elements/CodemirrorEditor'));
@@ -60,9 +61,7 @@ export default () => {
setLoading(true);
clearFlashes('files:view');
fetchFileContent()
- .then(content => {
- return saveFileContents(uuid, name || hash.replace(/^#/, ''), content);
- })
+ .then(content => saveFileContents(uuid, encodeURIComponent(name || hash.replace(/^#/, '')), content))
.then(() => {
if (name) {
history.push(`/server/${id}/files/edit#/${name}`);
@@ -87,7 +86,9 @@ export default () => {
return (
-
+
+
+
{hash.replace(/^#/, '').endsWith('.pteroignore') &&
diff --git a/resources/scripts/components/server/files/FileManagerBreadcrumbs.tsx b/resources/scripts/components/server/files/FileManagerBreadcrumbs.tsx
index ba43dbd61..968a651db 100644
--- a/resources/scripts/components/server/files/FileManagerBreadcrumbs.tsx
+++ b/resources/scripts/components/server/files/FileManagerBreadcrumbs.tsx
@@ -33,10 +33,10 @@ export default ({ withinFileEditor, isNewFile }: Props) => {
.filter(directory => !!directory)
.map((directory, index, dirs) => {
if (!withinFileEditor && index === dirs.length - 1) {
- return { name: decodeURIComponent(directory) };
+ return { name: decodeURIComponent(encodeURIComponent(directory)) };
}
- return { name: decodeURIComponent(directory), path: `/${dirs.slice(0, index + 1).join('/')}` };
+ return { name: decodeURIComponent(encodeURIComponent(directory)), path: `/${dirs.slice(0, index + 1).join('/')}` };
});
const onSelectAllClick = (e: React.ChangeEvent) => {
@@ -79,7 +79,7 @@ export default ({ withinFileEditor, isNewFile }: Props) => {
}
{file &&
- {decodeURIComponent(file)}
+ {decodeURIComponent(encodeURIComponent(file))}
}
diff --git a/resources/scripts/components/server/files/FileManagerContainer.tsx b/resources/scripts/components/server/files/FileManagerContainer.tsx
index 4c46edc16..31985e940 100644
--- a/resources/scripts/components/server/files/FileManagerContainer.tsx
+++ b/resources/scripts/components/server/files/FileManagerContainer.tsx
@@ -17,6 +17,7 @@ import MassActionsBar from '@/components/server/files/MassActionsBar';
import UploadButton from '@/components/server/files/UploadButton';
import ServerContentBlock from '@/components/elements/ServerContentBlock';
import { useStoreActions } from '@/state/hooks';
+import ErrorBoundary from '@/components/elements/ErrorBoundary';
const sortFiles = (files: FileObject[]): FileObject[] => {
return files.sort((a, b) => a.name.localeCompare(b.name))
@@ -50,7 +51,9 @@ export default () => {
return (
-
+
+
+
{
!files ?
@@ -81,18 +84,20 @@ export default () => {
}
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
>
}
diff --git a/resources/scripts/components/server/files/NewDirectoryButton.tsx b/resources/scripts/components/server/files/NewDirectoryButton.tsx
index 2be673db9..e43f0aff9 100644
--- a/resources/scripts/components/server/files/NewDirectoryButton.tsx
+++ b/resources/scripts/components/server/files/NewDirectoryButton.tsx
@@ -13,6 +13,7 @@ import useFlash from '@/plugins/useFlash';
import useFileManagerSwr from '@/plugins/useFileManagerSwr';
import { WithClassname } from '@/components/types';
import FlashMessageRender from '@/components/FlashMessageRender';
+import ErrorBoundary from '@/components/elements/ErrorBoundary';
interface Values {
directoryName: string;
@@ -92,9 +93,9 @@ export default ({ className }: WithClassname) => {
This directory will be created as
/home/container/
- {decodeURIComponent(
- join(directory, values.directoryName).replace(/^(\.\.\/|\/)+/, ''),
- )}
+ {decodeURIComponent(encodeURIComponent(
+ join(directory, values.directoryName).replace(/^(\.\.\/|\/)+/, '')
+ ))}
diff --git a/resources/scripts/routers/ServerRouter.tsx b/resources/scripts/routers/ServerRouter.tsx
index a90ff652b..ec6bc20b4 100644
--- a/resources/scripts/routers/ServerRouter.tsx
+++ b/resources/scripts/routers/ServerRouter.tsx
@@ -28,6 +28,7 @@ import NetworkContainer from '@/components/server/network/NetworkContainer';
import InstallListener from '@/components/server/InstallListener';
import StartupContainer from '@/components/server/startup/StartupContainer';
import requireServerPermission from '@/hoc/requireServerPermission';
+import ErrorBoundary from '@/components/elements/ErrorBoundary';
const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>) => {
const rootAdmin = useStoreState(state => state.user.data!.rootAdmin);
@@ -120,7 +121,7 @@ const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>)
message={'Please check back in a few minutes.'}
/>
:
- <>
+
@@ -173,7 +174,7 @@ const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>)
- >
+
}
>
}
From fd3b11e9cc91b9a2bf74f068ace40638df5d3d5d Mon Sep 17 00:00:00 2001
From: Dane Everitt
Date: Thu, 22 Oct 2020 21:27:15 -0700
Subject: [PATCH 11/41] Update CHANGELOG.md
---
CHANGELOG.md | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c594f9437..591eb44f5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,33 @@ This file is a running track of new features and fixes to each version of the pa
This project follows [Semantic Versioning](http://semver.org) guidelines.
+## v1.0.2
+### Added
+* Adds support for searching inside the file editor.
+* Adds support for manually executing a schedule regardless of if it is currently queued or not.
+* Adds an indicator to the schedule UI to show when a schedule is currently processing.
+* Adds support for setting the `backup_limit` of a server via the API.
+* **[Security]** Adds login throttling to the 2FA verification endpoint.
+
+### Fixed
+* Fixes subuser page title missing server name.
+* Fixes schedule task `sequence_id` not properly being reset when a schedule's task is deleted.
+* Fixes misc. UI bugs throughout the frontend when long text overflows its bounds.
+* Fixes user deletion command to properly handle email & ID searching.
+* Fixes color issues in the terminal causing certain text & background combinations to be illegible.
+* Fixes reCAPTCHA not properly resetting on login failure.
+* Fixes error messages not properly resetting between login screens.
+* Fixes a UI crash when attempting to create or view a directory or file that contained the `%` somewhere in the name.
+
+### Changed
+* Updated the search modal to close itself when the ESC key is pressed.
+* Updates the schedule view and editing UI to better display information to users.
+* Changed logic powering server searching on the frontend to return more accurate results and return all servers when executing the query as an admin.
+* Admin CP link no longer opens in a new tab.
+* Mounts will no longer allow a user to mount certain directory combinations. This blocks mounting one server's files into another server, and blocks using the server data directory as a mount destination.
+* Cleaned up assorted server build modification code.
+* Updates default eggs to have improved install scripts and more consistent container usage.
+
## v1.0.1
### Fixed
* Fixes 500 error when mounting a mount to a server, and other related errors when handling mounts.
From 23872b844a5048959555ae9dd9547ce76d9f24e8 Mon Sep 17 00:00:00 2001
From: Dane Everitt
Date: Thu, 22 Oct 2020 21:33:06 -0700
Subject: [PATCH 12/41] Fix unnecessary object structuring
---
.../scripts/components/auth/LoginCheckpointContainer.tsx | 2 +-
.../components/dashboard/forms/DisableTwoFactorModal.tsx | 2 +-
.../components/dashboard/forms/SetupTwoFactorModal.tsx | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/resources/scripts/components/auth/LoginCheckpointContainer.tsx b/resources/scripts/components/auth/LoginCheckpointContainer.tsx
index 4c3dff599..df2018ee7 100644
--- a/resources/scripts/components/auth/LoginCheckpointContainer.tsx
+++ b/resources/scripts/components/auth/LoginCheckpointContainer.tsx
@@ -91,7 +91,7 @@ const EnhancedForm = withFormik({
.catch(error => {
console.error(error);
setSubmitting(false);
- clearAndAddHttpError({ error: error });
+ clearAndAddHttpError({ error });
});
},
diff --git a/resources/scripts/components/dashboard/forms/DisableTwoFactorModal.tsx b/resources/scripts/components/dashboard/forms/DisableTwoFactorModal.tsx
index d4f986942..8500711fc 100644
--- a/resources/scripts/components/dashboard/forms/DisableTwoFactorModal.tsx
+++ b/resources/scripts/components/dashboard/forms/DisableTwoFactorModal.tsx
@@ -27,7 +27,7 @@ export default ({ ...props }: RequiredModalProps) => {
.catch(error => {
console.error(error);
- clearAndAddHttpError({ error: error, key: 'account:two-factor' });
+ clearAndAddHttpError({ error, key: 'account:two-factor' });
setSubmitting(false);
});
};
diff --git a/resources/scripts/components/dashboard/forms/SetupTwoFactorModal.tsx b/resources/scripts/components/dashboard/forms/SetupTwoFactorModal.tsx
index eb8e1a890..57eefa680 100644
--- a/resources/scripts/components/dashboard/forms/SetupTwoFactorModal.tsx
+++ b/resources/scripts/components/dashboard/forms/SetupTwoFactorModal.tsx
@@ -28,7 +28,7 @@ export default ({ onDismissed, ...props }: RequiredModalProps) => {
.then(setToken)
.catch(error => {
console.error(error);
- clearAndAddHttpError({ error: error, key: 'account:two-factor' });
+ clearAndAddHttpError({ error, key: 'account:two-factor' });
});
}, []);
@@ -40,7 +40,7 @@ export default ({ onDismissed, ...props }: RequiredModalProps) => {
.catch(error => {
console.error(error);
- clearAndAddHttpError({ error: error, key: 'account:two-factor' });
+ clearAndAddHttpError({ error, key: 'account:two-factor' });
})
.then(() => setSubmitting(false));
};
From 404ad68e0d5485bf7b217ea12d313ba922cae177 Mon Sep 17 00:00:00 2001
From: Charles Morgan
Date: Fri, 23 Oct 2020 03:37:35 -0400
Subject: [PATCH 13/41] find != fund
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 27feb040b..f1cf28848 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@ Stop settling for less. Make game servers a first class citizen on your platform
![Image](https://cdn.pterodactyl.io/site-assets/pterodactyl_v1_demo.gif)
## Sponsors
-I would like to extend my sincere thanks to the following sponsors for helping find Pterodactyl's developement.
+I would like to extend my sincere thanks to the following sponsors for helping fund Pterodactyl's developement.
[Interested in becoming a sponsor?](https://github.com/sponsors/DaneEveritt)
| Company | About |
From a271b590925fca8b05bf6bf13ed4a8db627f0ca5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Anders=20G=2E=20J=C3=B8rgensen?=
Date: Sun, 25 Oct 2020 21:15:49 +0100
Subject: [PATCH 14/41] Change SameSite attribute on session cookies to "lax"
(#2592)
---
app/Console/Commands/Environment/AppSettingsCommand.php | 5 +++++
config/session.php | 2 +-
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/app/Console/Commands/Environment/AppSettingsCommand.php b/app/Console/Commands/Environment/AppSettingsCommand.php
index 60254a9ef..01518d610 100644
--- a/app/Console/Commands/Environment/AppSettingsCommand.php
+++ b/app/Console/Commands/Environment/AppSettingsCommand.php
@@ -144,6 +144,11 @@ class AppSettingsCommand extends Command
$this->variables['APP_ENVIRONMENT_ONLY'] = $this->confirm(trans('command/messages.environment.app.settings'), true) ? 'false' : 'true';
}
+ // Make sure session cookies are set as "secure" when using HTTPS
+ if (strpos($this->variables['APP_URL'], 'https://') === 0) {
+ $this->variables['SESSION_SECURE_COOKIE'] = 'true';
+ }
+
$this->checkForRedis();
$this->writeToEnvironment($this->variables);
diff --git a/config/session.php b/config/session.php
index 2007acb2e..8605db59b 100644
--- a/config/session.php
+++ b/config/session.php
@@ -188,5 +188,5 @@ return [
|
*/
- 'same_site' => null,
+ 'same_site' => env('SESSION_SAMESITE_COOKIE', 'lax'),
];
From 3ecf14d4196e22933287e9fa9e18d92c697b184d Mon Sep 17 00:00:00 2001
From: "Michael (Parker) Parker"
Date: Sun, 25 Oct 2020 16:16:18 -0400
Subject: [PATCH 15/41] fix install scripts (#2587)
---
database/seeds/eggs/minecraft/egg-vanilla-minecraft.json | 4 ++--
database/seeds/eggs/voice-servers/egg-teamspeak3-server.json | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/database/seeds/eggs/minecraft/egg-vanilla-minecraft.json b/database/seeds/eggs/minecraft/egg-vanilla-minecraft.json
index 80d697949..fadd91a4e 100644
--- a/database/seeds/eggs/minecraft/egg-vanilla-minecraft.json
+++ b/database/seeds/eggs/minecraft/egg-vanilla-minecraft.json
@@ -3,7 +3,7 @@
"meta": {
"version": "PTDL_v1"
},
- "exported_at": "2020-10-19T23:28:18+00:00",
+ "exported_at": "2020-10-23T23:04:17+00:00",
"name": "Vanilla Minecraft",
"author": "support@pterodactyl.io",
"description": "Minecraft is a game about placing blocks and going on adventures. Explore randomly generated worlds and build amazing things from the simplest of homes to the grandest of castles. Play in Creative Mode with unlimited resources or mine deep in Survival Mode, crafting weapons and armor to fend off dangerous mobs. Do all this alone or with friends.",
@@ -17,7 +17,7 @@
},
"scripts": {
"installation": {
- "script": "#!\/bin\/bash\r\n# Vanilla MC Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napt update\r\napt install -y jq\r\n\r\nmkdir -p \/mnt\/server\r\ncd \/mnt\/server\r\n\r\nLATEST_VERSION=`curl https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq -r '.latest.release'`\r\n\r\necho -e \"latest version is $LATEST_VERSION\"\r\n\r\nif [ -z \"$VANILLA_VERSION\" ] || [ \"$VANILLA_VERSION\" == \"latest\" ]; then\r\n MANIFEST_URL=$(curl -sSL https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq --arg VERSION $LATEST_VERSION -r '.versions | .[] | select(.id== $VERSION )|.url')\r\nelse\r\n MANIFEST_URL=$(curl -sSL https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq --arg VERSION $VANILLA_VERSION -r '.versions | .[] | select(.id== $VERSION )|.url')\r\nfi\r\n\r\nDOWNLOAD_URL=$(curl ${MANIFEST_URL} | jq .downloads.server | jq -r '. | .url')\r\n\r\necho -e \"running: curl -o ${SERVER_JARFILE} $DOWNLOAD_URL\"\r\ncurl -o ${SERVER_JARFILE} $DOWNLOAD_URL\r\n\r\necho -e \"Install Complete\"",
+ "script": "#!\/bin\/bash\r\n# Vanilla MC Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napt update\r\napt install -y jq curl\r\n\r\nmkdir -p \/mnt\/server\r\ncd \/mnt\/server\r\n\r\nLATEST_VERSION=`curl https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq -r '.latest.release'`\r\n\r\necho -e \"latest version is $LATEST_VERSION\"\r\n\r\nif [ -z \"$VANILLA_VERSION\" ] || [ \"$VANILLA_VERSION\" == \"latest\" ]; then\r\n MANIFEST_URL=$(curl -sSL https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq --arg VERSION $LATEST_VERSION -r '.versions | .[] | select(.id== $VERSION )|.url')\r\nelse\r\n MANIFEST_URL=$(curl -sSL https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq --arg VERSION $VANILLA_VERSION -r '.versions | .[] | select(.id== $VERSION )|.url')\r\nfi\r\n\r\nDOWNLOAD_URL=$(curl ${MANIFEST_URL} | jq .downloads.server | jq -r '. | .url')\r\n\r\necho -e \"running: curl -o ${SERVER_JARFILE} $DOWNLOAD_URL\"\r\ncurl -o ${SERVER_JARFILE} $DOWNLOAD_URL\r\n\r\necho -e \"Install Complete\"",
"container": "debian:buster-slim",
"entrypoint": "bash"
}
diff --git a/database/seeds/eggs/voice-servers/egg-teamspeak3-server.json b/database/seeds/eggs/voice-servers/egg-teamspeak3-server.json
index e0ae85d8d..aeba79316 100644
--- a/database/seeds/eggs/voice-servers/egg-teamspeak3-server.json
+++ b/database/seeds/eggs/voice-servers/egg-teamspeak3-server.json
@@ -3,7 +3,7 @@
"meta": {
"version": "PTDL_v1"
},
- "exported_at": "2020-10-20T00:24:22+00:00",
+ "exported_at": "2020-10-23T22:27:50+00:00",
"name": "Teamspeak3 Server",
"author": "support@pterodactyl.io",
"description": "VoIP software designed with security in mind, featuring crystal clear voice quality, endless customization options, and scalabilty up to thousands of simultaneous users.",
@@ -17,7 +17,7 @@
},
"scripts": {
"installation": {
- "script": "#!\/bin\/bash\r\n# TS3 Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napk add --no-cache tar curl jq\r\n\r\nif [ -z ${TS_VERSION} ] || [ ${TS_VERSION} == latest ]; then\r\n TS_VERSION=$(wget https:\/\/teamspeak.com\/versions\/server.json -qO - | jq -r '.linux.x86_64.version')\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\n\r\necho -e \"getting files from http:\/\/files.teamspeak-services.com\/releases\/server\/${TS_VERSION}\/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2\"\r\ncurl http:\/\/files.teamspeak-services.com\/releases\/server\/${TS_VERSION}\/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2 | tar xj --strip-components=1",
+ "script": "#!\/bin\/bash\r\n# TS3 Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napt update\r\napt install -y tar curl jq\r\n\r\nif [ -z ${TS_VERSION} ] || [ ${TS_VERSION} == latest ]; then\r\n TS_VERSION=$(wget https:\/\/teamspeak.com\/versions\/server.json -qO - | jq -r '.linux.x86_64.version')\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\n\r\necho -e \"getting files from http:\/\/files.teamspeak-services.com\/releases\/server\/${TS_VERSION}\/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2\"\r\ncurl http:\/\/files.teamspeak-services.com\/releases\/server\/${TS_VERSION}\/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2 | tar xj --strip-components=1",
"container": "debian:buster-slim",
"entrypoint": "bash"
}
From 8c6327fd3253e009f86313695a57469728fd7807 Mon Sep 17 00:00:00 2001
From: Dane Everitt
Date: Sun, 25 Oct 2020 15:06:54 -0700
Subject: [PATCH 16/41] Let MySQL do the time logic when looking for tasks
---
.../Schedule/ProcessRunnableCommand.php | 60 ++++-------
.../ScheduleRepositoryInterface.php | 8 --
app/Helpers/Utilities.php | 7 +-
.../Eloquent/ScheduleRepository.php | 15 ---
.../Schedule/ProcessRunnableCommandTest.php | 102 ------------------
5 files changed, 24 insertions(+), 168 deletions(-)
delete mode 100644 tests/Unit/Commands/Schedule/ProcessRunnableCommandTest.php
diff --git a/app/Console/Commands/Schedule/ProcessRunnableCommand.php b/app/Console/Commands/Schedule/ProcessRunnableCommand.php
index 67654708c..4d5981fd3 100644
--- a/app/Console/Commands/Schedule/ProcessRunnableCommand.php
+++ b/app/Console/Commands/Schedule/ProcessRunnableCommand.php
@@ -1,62 +1,38 @@
.
- *
- * This software is licensed under the terms of the MIT license.
- * https://opensource.org/licenses/MIT
- */
namespace Pterodactyl\Console\Commands\Schedule;
-use Cake\Chronos\Chronos;
use Illuminate\Console\Command;
-use Illuminate\Support\Collection;
+use Pterodactyl\Models\Schedule;
use Pterodactyl\Services\Schedules\ProcessScheduleService;
-use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface;
class ProcessRunnableCommand extends Command
{
/**
* @var string
*/
- protected $description = 'Process schedules in the database and determine which are ready to run.';
-
- /**
- * @var \Pterodactyl\Services\Schedules\ProcessScheduleService
- */
- protected $processScheduleService;
-
- /**
- * @var \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface
- */
- protected $repository;
+ protected $signature = 'p:schedule:process';
/**
* @var string
*/
- protected $signature = 'p:schedule:process';
-
- /**
- * ProcessRunnableCommand constructor.
- *
- * @param \Pterodactyl\Services\Schedules\ProcessScheduleService $processScheduleService
- * @param \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface $repository
- */
- public function __construct(ProcessScheduleService $processScheduleService, ScheduleRepositoryInterface $repository)
- {
- parent::__construct();
-
- $this->processScheduleService = $processScheduleService;
- $this->repository = $repository;
- }
+ protected $description = 'Process schedules in the database and determine which are ready to run.';
/**
* Handle command execution.
+ *
+ * @param \Pterodactyl\Services\Schedules\ProcessScheduleService $service
+ *
+ * @throws \Throwable
*/
- public function handle()
+ public function handle(ProcessScheduleService $service)
{
- $schedules = $this->repository->getSchedulesToProcess(Chronos::now()->toAtomString());
+ $schedules = Schedule::query()->with('tasks')
+ ->where('is_active', true)
+ ->where('is_processing', false)
+ ->whereRaw('next_run_at <= NOW()')
+ ->get();
+
if ($schedules->count() < 1) {
$this->line('There are no scheduled tasks for servers that need to be run.');
@@ -64,9 +40,9 @@ class ProcessRunnableCommand extends Command
}
$bar = $this->output->createProgressBar(count($schedules));
- $schedules->each(function ($schedule) use ($bar) {
- if ($schedule->tasks instanceof Collection && count($schedule->tasks) > 0) {
- $this->processScheduleService->handle($schedule);
+ foreach ($schedules as $schedule) {
+ if ($schedule->tasks->isNotEmpty()) {
+ $service->handle($schedule);
if ($this->input->isInteractive()) {
$bar->clear();
@@ -79,7 +55,7 @@ class ProcessRunnableCommand extends Command
$bar->advance();
$bar->display();
- });
+ }
$this->line('');
}
diff --git a/app/Contracts/Repository/ScheduleRepositoryInterface.php b/app/Contracts/Repository/ScheduleRepositoryInterface.php
index 32650bdcf..b6e73a2de 100644
--- a/app/Contracts/Repository/ScheduleRepositoryInterface.php
+++ b/app/Contracts/Repository/ScheduleRepositoryInterface.php
@@ -24,12 +24,4 @@ interface ScheduleRepositoryInterface extends RepositoryInterface
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function getScheduleWithTasks(int $schedule): Schedule;
-
- /**
- * Return all of the schedules that should be processed.
- *
- * @param string $timestamp
- * @return \Illuminate\Support\Collection
- */
- public function getSchedulesToProcess(string $timestamp): Collection;
}
diff --git a/app/Helpers/Utilities.php b/app/Helpers/Utilities.php
index d900425d9..d6b4e752f 100644
--- a/app/Helpers/Utilities.php
+++ b/app/Helpers/Utilities.php
@@ -52,7 +52,12 @@ class Utilities
)->getNextRunDate());
}
- public static function checked($name, $default)
+ /**
+ * @param string $name
+ * @param mixed $default
+ * @return string
+ */
+ public static function checked(string $name, $default)
{
$errors = session('errors');
diff --git a/app/Repositories/Eloquent/ScheduleRepository.php b/app/Repositories/Eloquent/ScheduleRepository.php
index 030939da7..cb1b2cbf0 100644
--- a/app/Repositories/Eloquent/ScheduleRepository.php
+++ b/app/Repositories/Eloquent/ScheduleRepository.php
@@ -47,19 +47,4 @@ class ScheduleRepository extends EloquentRepository implements ScheduleRepositor
throw new RecordNotFoundException;
}
}
-
- /**
- * Return all of the schedules that should be processed.
- *
- * @param string $timestamp
- * @return \Illuminate\Support\Collection
- */
- public function getSchedulesToProcess(string $timestamp): Collection
- {
- return $this->getBuilder()->with('tasks')
- ->where('is_active', true)
- ->where('is_processing', false)
- ->where('next_run_at', '<=', $timestamp)
- ->get($this->getColumns());
- }
}
diff --git a/tests/Unit/Commands/Schedule/ProcessRunnableCommandTest.php b/tests/Unit/Commands/Schedule/ProcessRunnableCommandTest.php
deleted file mode 100644
index 5efbef6c4..000000000
--- a/tests/Unit/Commands/Schedule/ProcessRunnableCommandTest.php
+++ /dev/null
@@ -1,102 +0,0 @@
-processScheduleService = m::mock(ProcessScheduleService::class);
- $this->repository = m::mock(ScheduleRepositoryInterface::class);
-
- $this->command = new ProcessRunnableCommand($this->processScheduleService, $this->repository);
- }
-
- /**
- * Test that a schedule can be queued up correctly.
- */
- public function testScheduleIsQueued()
- {
- $schedule = factory(Schedule::class)->make();
- $schedule->tasks = collect([factory(Task::class)->make()]);
-
- $this->repository->shouldReceive('getSchedulesToProcess')->with(Chronos::now()->toAtomString())->once()->andReturn(collect([$schedule]));
- $this->processScheduleService->shouldReceive('handle')->with($schedule)->once()->andReturnNull();
-
- $display = $this->runCommand($this->command);
-
- $this->assertNotEmpty($display);
- $this->assertStringContainsString(trans('command/messages.schedule.output_line', [
- 'schedule' => $schedule->name,
- 'hash' => $schedule->hashid,
- ]), $display);
- }
-
- /**
- * If tasks is an empty collection, don't process it.
- */
- public function testScheduleWithNoTasksIsNotProcessed()
- {
- $schedule = factory(Schedule::class)->make();
- $schedule->tasks = collect([]);
-
- $this->repository->shouldReceive('getSchedulesToProcess')->with(Chronos::now()->toAtomString())->once()->andReturn(collect([$schedule]));
-
- $display = $this->runCommand($this->command);
-
- $this->assertNotEmpty($display);
- $this->assertStringNotContainsString(trans('command/messages.schedule.output_line', [
- 'schedule' => $schedule->name,
- 'hash' => $schedule->hashid,
- ]), $display);
- }
-
- /**
- * If tasks isn't an instance of a collection, don't process it.
- */
- public function testScheduleWithTasksObjectThatIsNotInstanceOfCollectionIsNotProcessed()
- {
- $schedule = factory(Schedule::class)->make(['tasks' => null]);
-
- $this->repository->shouldReceive('getSchedulesToProcess')->with(Chronos::now()->toAtomString())->once()->andReturn(collect([$schedule]));
-
- $display = $this->runCommand($this->command);
-
- $this->assertNotEmpty($display);
- $this->assertStringNotContainsString(trans('command/messages.schedule.output_line', [
- 'schedule' => $schedule->name,
- 'hash' => $schedule->hashid,
- ]), $display);
- }
-}
From 996fb5b46f6ebaa9e38d58d3aeeb2feae654e814 Mon Sep 17 00:00:00 2001
From: Dane Everitt
Date: Sun, 25 Oct 2020 15:07:11 -0700
Subject: [PATCH 17/41] Set the DB timezone on each connection to match the
APP_TIMEZONE value
---
app/Helpers/Time.php | 25 +++++++++++++++++++++++++
config/database.php | 4 ++++
2 files changed, 29 insertions(+)
create mode 100644 app/Helpers/Time.php
diff --git a/app/Helpers/Time.php b/app/Helpers/Time.php
new file mode 100644
index 000000000..248ff55f0
--- /dev/null
+++ b/app/Helpers/Time.php
@@ -0,0 +1,25 @@
+getTimezone()->getOffset(CarbonImmutable::now('UTC')) / 3600);
+
+ return sprintf('%s%s:00', $offset > 0 ? '+' : '-', str_pad(abs($offset), 2, '0', STR_PAD_LEFT));
+ }
+}
diff --git a/config/database.php b/config/database.php
index 32fca7581..5a4e8bc77 100644
--- a/config/database.php
+++ b/config/database.php
@@ -1,5 +1,7 @@
'utf8mb4_unicode_ci',
'prefix' => env('DB_PREFIX', ''),
'strict' => env('DB_STRICT_MODE', false),
+ 'timezone' => env('DB_TIMEZONE', Time::getMySQLTimezoneOffset(env('APP_TIMEZONE')))
],
/*
@@ -65,6 +68,7 @@ return [
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'strict' => false,
+ 'timezone' => env('DB_TIMEZONE', Time::getMySQLTimezoneOffset(env('APP_TIMEZONE')))
],
],
From 39dddba1d6728c4f6bcca09f7e578afa5753aea9 Mon Sep 17 00:00:00 2001
From: Dane Everitt
Date: Sun, 25 Oct 2020 15:47:50 -0700
Subject: [PATCH 18/41] Refactor subuser modal and fix to be less of a code
monstrosity; closes #2583
---
.../server/users/AddSubuserButton.tsx | 2 +-
.../server/users/EditSubuserModal.tsx | 266 +++++-------------
.../components/server/users/PermissionRow.tsx | 65 +++++
.../server/users/PermissionTitleBox.tsx | 50 ++++
.../components/server/users/UserRow.tsx | 7 +-
resources/scripts/hoc/asModal.tsx | 71 +++--
6 files changed, 228 insertions(+), 233 deletions(-)
create mode 100644 resources/scripts/components/server/users/PermissionRow.tsx
create mode 100644 resources/scripts/components/server/users/PermissionTitleBox.tsx
diff --git a/resources/scripts/components/server/users/AddSubuserButton.tsx b/resources/scripts/components/server/users/AddSubuserButton.tsx
index 210578bb9..fa8b2f8e6 100644
--- a/resources/scripts/components/server/users/AddSubuserButton.tsx
+++ b/resources/scripts/components/server/users/AddSubuserButton.tsx
@@ -10,7 +10,7 @@ export default () => {
return (
<>
- {visible && setVisible(false)}/>}
+ setVisible(false)}/>
diff --git a/resources/scripts/components/server/users/EditSubuserModal.tsx b/resources/scripts/components/server/users/EditSubuserModal.tsx
index d81d15f08..9479b0ed8 100644
--- a/resources/scripts/components/server/users/EditSubuserModal.tsx
+++ b/resources/scripts/components/server/users/EditSubuserModal.tsx
@@ -1,14 +1,10 @@
-import React, { forwardRef, memo, useCallback, useEffect, useRef } from 'react';
+import React, { useContext, useEffect, useRef } from 'react';
import { Subuser } from '@/state/server/subusers';
-import { Form, Formik, FormikHelpers, useFormikContext } from 'formik';
+import { Form, Formik } from 'formik';
import { array, object, string } from 'yup';
-import Modal, { RequiredModalProps } from '@/components/elements/Modal';
import Field from '@/components/elements/Field';
import { Actions, useStoreActions, useStoreState } from 'easy-peasy';
import { ApplicationStore } from '@/state';
-import TitledGreyBox from '@/components/elements/TitledGreyBox';
-import Checkbox from '@/components/elements/Checkbox';
-import styled from 'styled-components/macro';
import createOrUpdateSubuser from '@/api/server/users/createOrUpdateSubuser';
import { ServerContext } from '@/state/server';
import FlashMessageRender from '@/components/FlashMessageRender';
@@ -17,104 +13,33 @@ import { usePermissions } from '@/plugins/usePermissions';
import { useDeepCompareMemo } from '@/plugins/useDeepCompareMemo';
import tw from 'twin.macro';
import Button from '@/components/elements/Button';
-import Label from '@/components/elements/Label';
-import Input from '@/components/elements/Input';
-import isEqual from 'react-fast-compare';
+import PermissionTitleBox from '@/components/server/users/PermissionTitleBox';
+import asModal from '@/hoc/asModal';
+import PermissionRow from '@/components/server/users/PermissionRow';
+import ModalContext from '@/context/ModalContext';
type Props = {
subuser?: Subuser;
-} & RequiredModalProps;
+};
interface Values {
email: string;
permissions: string[];
}
-const PermissionLabel = styled.label`
- ${tw`flex items-center border border-transparent rounded md:p-2 transition-colors duration-75`};
- text-transform: none;
+const EditSubuserModal = ({ subuser }: Props) => {
+ const ref = useRef(null);
+ const uuid = ServerContext.useStoreState(state => state.server.data!.uuid);
+ const appendSubuser = ServerContext.useStoreActions(actions => actions.subusers.appendSubuser);
+ const { clearFlashes, clearAndAddHttpError } = useStoreActions((actions: Actions) => actions.flashes);
+ const { dismiss, toggleSpinner } = useContext(ModalContext);
- &:not(.disabled) {
- ${tw`cursor-pointer`};
-
- &:hover {
- ${tw`border-neutral-500 bg-neutral-800`};
- }
- }
-
- &:not(:first-of-type) {
- ${tw`mt-4 sm:mt-2`};
- }
-
- &.disabled {
- ${tw`opacity-50`};
-
- & input[type="checkbox"]:not(:checked) {
- ${tw`border-0`};
- }
- }
-`;
-
-interface TitleProps {
- isEditable: boolean;
- permission: string;
- permissions: string[];
- children: React.ReactNode;
- className?: string;
-}
-
-const PermissionTitledBox = memo(({ isEditable, permission, permissions, className, children }: TitleProps) => {
- const { values, setFieldValue } = useFormikContext();
-
- const onCheckboxClicked = useCallback((e: React.ChangeEvent) => {
- console.log(e.currentTarget.checked, [
- ...values.permissions,
- ...permissions.filter(p => !values.permissions.includes(p)),
- ]);
-
- if (e.currentTarget.checked) {
- setFieldValue('permissions', [
- ...values.permissions,
- ...permissions.filter(p => !values.permissions.includes(p)),
- ]);
- } else {
- setFieldValue('permissions', [
- ...values.permissions.filter(p => !permissions.includes(p)),
- ]);
- }
- }, [ permissions, values.permissions ]);
-
- return (
-
- {permission}
- {isEditable &&
- values.permissions.includes(p))}
- onChange={onCheckboxClicked}
- />
- }
-
- }
- className={className}
- >
- {children}
-
- );
-}, isEqual);
-
-const EditSubuserModal = forwardRef(({ subuser, ...props }, ref) => {
- const { isSubmitting } = useFormikContext();
- const [ canEditUser ] = usePermissions(subuser ? [ 'user.update' ] : [ 'user.create' ]);
+ const isRootAdmin = useStoreState(state => state.user.data!.rootAdmin);
const permissions = useStoreState(state => state.permissions.data);
-
- const user = useStoreState(state => state.user.data!);
-
// The currently logged in user's permissions. We're going to filter out any permissions
// that they should not need.
const loggedInPermissions = ServerContext.useStoreState(state => state.server.permissions);
+ const [ canEditUser ] = usePermissions(subuser ? [ 'user.update' ] : [ 'user.create' ]);
// The permissions that can be modified by this user.
const editablePermissions = useDeepCompareMemo(() => {
@@ -123,111 +48,25 @@ const EditSubuserModal = forwardRef(({ subuser, ...pr
const list: string[] = ([] as string[]).concat.apply([], Object.values(cleaned));
- if (user.rootAdmin || (loggedInPermissions.length === 1 && loggedInPermissions[0] === '*')) {
+ if (isRootAdmin || (loggedInPermissions.length === 1 && loggedInPermissions[0] === '*')) {
return list;
}
return list.filter(key => loggedInPermissions.indexOf(key) >= 0);
- }, [ permissions, loggedInPermissions ]);
+ }, [ isRootAdmin, permissions, loggedInPermissions ]);
- return (
-
-
- {subuser ?
- `${canEditUser ? 'Modify' : 'View'} permissions for ${subuser.email}`
- :
- 'Create new subuser'
- }
-
-
- {(!user.rootAdmin && loggedInPermissions[0] !== '*') &&
-
-
- Only permissions which your account is currently assigned may be selected when creating or
- modifying other users.
-
-
- }
- {!subuser &&
-
-
-
- }
-
- {Object.keys(permissions).filter(key => key !== 'websocket').map((key, index) => {
- const group = Object.keys(permissions[key].keys).map(pkey => `${key}.${pkey}`);
-
- return (
-
0 ? tw`mt-4` : undefined}
- >
-
- {permissions[key].description}
-
- {Object.keys(permissions[key].keys).map(pkey => (
-
-
-
-
-
-
- {permissions[key].keys[pkey].length > 0 &&
-
- {permissions[key].keys[pkey]}
-
- }
-
-
- ))}
-
- );
- })}
-
-
-
-
-
-
-
- );
-});
-
-export default ({ subuser, ...props }: Props) => {
- const ref = useRef(null);
- const uuid = ServerContext.useStoreState(state => state.server.data!.uuid);
- const appendSubuser = ServerContext.useStoreActions(actions => actions.subusers.appendSubuser);
- const { clearFlashes, clearAndAddHttpError } = useStoreActions((actions: Actions) => actions.flashes);
-
- const submit = (values: Values, { setSubmitting }: FormikHelpers) => {
+ const submit = (values: Values) => {
+ toggleSpinner(true);
clearFlashes('user:edit');
+
createOrUpdateSubuser(uuid, values, subuser)
.then(subuser => {
appendSubuser(subuser);
- props.onDismissed();
+ dismiss();
})
.catch(error => {
console.error(error);
- setSubmitting(false);
+ toggleSpinner(false);
clearAndAddHttpError({ key: 'user:edit', error });
if (ref.current) {
@@ -236,10 +75,8 @@ export default ({ subuser, ...props }: Props) => {
});
};
- useEffect(() => {
- return () => {
- clearFlashes('user:edit');
- };
+ useEffect(() => () => {
+ clearFlashes('user:edit');
}, []);
return (
@@ -258,8 +95,61 @@ export default ({ subuser, ...props }: Props) => {
})}
>
);
};
+
+export default asModal({
+ top: false,
+})(EditSubuserModal);
diff --git a/resources/scripts/components/server/users/PermissionRow.tsx b/resources/scripts/components/server/users/PermissionRow.tsx
new file mode 100644
index 000000000..1f48eaaf0
--- /dev/null
+++ b/resources/scripts/components/server/users/PermissionRow.tsx
@@ -0,0 +1,65 @@
+import styled from 'styled-components/macro';
+import tw from 'twin.macro';
+import Checkbox from '@/components/elements/Checkbox';
+import React from 'react';
+import { useStoreState } from 'easy-peasy';
+import Label from '@/components/elements/Label';
+
+const Container = styled.label`
+ ${tw`flex items-center border border-transparent rounded md:p-2 transition-colors duration-75`};
+ text-transform: none;
+
+ &:not(.disabled) {
+ ${tw`cursor-pointer`};
+
+ &:hover {
+ ${tw`border-neutral-500 bg-neutral-800`};
+ }
+ }
+
+ &:not(:first-of-type) {
+ ${tw`mt-4 sm:mt-2`};
+ }
+
+ &.disabled {
+ ${tw`opacity-50`};
+
+ & input[type="checkbox"]:not(:checked) {
+ ${tw`border-0`};
+ }
+ }
+`;
+
+interface Props {
+ permission: string;
+ disabled: boolean;
+}
+
+const PermissionRow = ({ permission, disabled }: Props) => {
+ const [ key, pkey ] = permission.split('.', 2);
+ const permissions = useStoreState(state => state.permissions.data);
+
+ return (
+
+
+
+
+
+
+ {permissions[key].keys[pkey].length > 0 &&
+
+ {permissions[key].keys[pkey]}
+
+ }
+
+
+ );
+};
+
+export default PermissionRow;
diff --git a/resources/scripts/components/server/users/PermissionTitleBox.tsx b/resources/scripts/components/server/users/PermissionTitleBox.tsx
new file mode 100644
index 000000000..a3e1453a4
--- /dev/null
+++ b/resources/scripts/components/server/users/PermissionTitleBox.tsx
@@ -0,0 +1,50 @@
+import React, { memo, useCallback } from 'react';
+import { useField } from 'formik';
+import TitledGreyBox from '@/components/elements/TitledGreyBox';
+import tw from 'twin.macro';
+import Input from '@/components/elements/Input';
+import isEqual from 'react-fast-compare';
+
+interface Props {
+ isEditable: boolean;
+ title: string;
+ permissions: string[];
+ className?: string;
+}
+
+const PermissionTitleBox: React.FC = memo(({ isEditable, title, permissions, className, children }) => {
+ const [ { value }, , { setValue } ] = useField('permissions');
+
+ const onCheckboxClicked = useCallback((e: React.ChangeEvent) => {
+ if (e.currentTarget.checked) {
+ setValue([
+ ...value,
+ ...permissions.filter(p => !value.includes(p)),
+ ]);
+ } else {
+ setValue(value.filter(p => !permissions.includes(p)));
+ }
+ }, [ permissions, value ]);
+
+ return (
+
+ {title}
+ {isEditable &&
+ value.includes(p))}
+ onChange={onCheckboxClicked}
+ />
+ }
+
+ }
+ className={className}
+ >
+ {children}
+
+ );
+}, isEqual);
+
+export default PermissionTitleBox;
diff --git a/resources/scripts/components/server/users/UserRow.tsx b/resources/scripts/components/server/users/UserRow.tsx
index 237621660..aa5e55a39 100644
--- a/resources/scripts/components/server/users/UserRow.tsx
+++ b/resources/scripts/components/server/users/UserRow.tsx
@@ -19,14 +19,11 @@ export default ({ subuser }: Props) => {
return (
- {visible &&
setVisible(false)}
+ visible={visible}
+ onModalDismissed={() => setVisible(false)}
/>
- }
diff --git a/resources/scripts/hoc/asModal.tsx b/resources/scripts/hoc/asModal.tsx
index 7db437c14..81027f5d3 100644
--- a/resources/scripts/hoc/asModal.tsx
+++ b/resources/scripts/hoc/asModal.tsx
@@ -1,7 +1,6 @@
import React from 'react';
import Modal, { ModalProps } from '@/components/elements/Modal';
import ModalContext from '@/context/ModalContext';
-import isEqual from 'react-fast-compare';
export interface AsModalProps {
visible: boolean;
@@ -13,7 +12,7 @@ type ExtendedModalProps = Omit
interface State {
render: boolean;
visible: boolean;
- modalProps: ExtendedModalProps | undefined;
+ showSpinnerOverlay?: boolean;
}
type ExtendedComponentType = (C: React.ComponentType) => React.ComponentType;
@@ -30,17 +29,18 @@ function asModal (modalProps?: ExtendedModalProps | ((props: P
this.state = {
render: props.visible,
visible: props.visible,
- modalProps: typeof modalProps === 'function' ? modalProps(this.props) : modalProps,
+ showSpinnerOverlay: undefined,
+ };
+ }
+
+ get modalProps () {
+ return {
+ ...(typeof modalProps === 'function' ? modalProps(this.props) : modalProps),
+ showSpinnerOverlay: this.state.showSpinnerOverlay,
};
}
componentDidUpdate (prevProps: Readonly
) {
- const mapped = typeof modalProps === 'function' ? modalProps(this.props) : modalProps;
- if (!isEqual(this.state.modalProps, mapped)) {
- // noinspection JSPotentiallyInvalidUsageOfThis
- this.setState({ modalProps: mapped });
- }
-
if (prevProps.visible && !this.props.visible) {
// noinspection JSPotentiallyInvalidUsageOfThis
this.setState({ visible: false });
@@ -52,39 +52,32 @@ function asModal
(modalProps?: ExtendedModalProps | ((props: P
dismiss = () => this.setState({ visible: false });
- toggleSpinner = (value?: boolean) => this.setState(s => ({
- modalProps: {
- ...s.modalProps,
- showSpinnerOverlay: value || false,
- },
- }));
+ toggleSpinner = (value?: boolean) => this.setState({ showSpinnerOverlay: value });
render () {
return (
-
- {
- this.state.render ?
- this.setState({ render: false }, () => {
- if (typeof this.props.onModalDismissed === 'function') {
- this.props.onModalDismissed();
- }
- })}
- {...this.state.modalProps}
- >
-
-
- :
- null
- }
-
+ this.state.render ?
+ this.setState({ render: false }, () => {
+ if (typeof this.props.onModalDismissed === 'function') {
+ this.props.onModalDismissed();
+ }
+ })}
+ {...this.modalProps}
+ >
+
+
+
+
+ :
+ null
);
}
};
From 092c942764e7bb8b116e8c1b1d3923e31a8b890f Mon Sep 17 00:00:00 2001
From: Dane Everitt
Date: Sun, 25 Oct 2020 17:29:57 -0700
Subject: [PATCH 19/41] Fix server owner filtering; improve searching for
servers; closes #2581
---
.../Admin/Servers/ServerController.php | 8 +++-
app/Models/Filters/AdminServerFilter.php | 40 +++++++++++++++++++
resources/views/admin/servers/index.blade.php | 2 +-
resources/views/admin/users/index.blade.php | 2 +-
4 files changed, 48 insertions(+), 4 deletions(-)
create mode 100644 app/Models/Filters/AdminServerFilter.php
diff --git a/app/Http/Controllers/Admin/Servers/ServerController.php b/app/Http/Controllers/Admin/Servers/ServerController.php
index a0b73f552..c8e54b1c3 100644
--- a/app/Http/Controllers/Admin/Servers/ServerController.php
+++ b/app/Http/Controllers/Admin/Servers/ServerController.php
@@ -6,7 +6,9 @@ use Illuminate\Http\Request;
use Pterodactyl\Models\Server;
use Spatie\QueryBuilder\QueryBuilder;
use Illuminate\Contracts\View\Factory;
+use Spatie\QueryBuilder\AllowedFilter;
use Pterodactyl\Http\Controllers\Controller;
+use Pterodactyl\Models\Filters\AdminServerFilter;
use Pterodactyl\Repositories\Eloquent\ServerRepository;
class ServerController extends Controller
@@ -45,8 +47,10 @@ class ServerController extends Controller
public function index(Request $request)
{
$servers = QueryBuilder::for(Server::query()->with('node', 'user', 'allocation'))
- ->allowedFilters(['uuid', 'name', 'image'])
- ->allowedSorts(['id', 'uuid'])
+ ->allowedFilters([
+ AllowedFilter::exact('owner_id'),
+ AllowedFilter::custom('*', new AdminServerFilter),
+ ])
->paginate(config()->get('pterodactyl.paginate.admin.servers'));
return $this->view->make('admin.servers.index', ['servers' => $servers]);
diff --git a/app/Models/Filters/AdminServerFilter.php b/app/Models/Filters/AdminServerFilter.php
new file mode 100644
index 000000000..d523e48e0
--- /dev/null
+++ b/app/Models/Filters/AdminServerFilter.php
@@ -0,0 +1,40 @@
+getQuery()->from !== 'servers') {
+ throw new BadMethodCallException(
+ 'Cannot use the AdminServerFilter against a non-server model.'
+ );
+ }
+ $query
+ ->select('servers.*')
+ ->leftJoin('users', 'users.id', '=', 'servers.owner_id')
+ ->where(function (Builder $builder) use ($value) {
+ $builder->where('servers.uuid', $value)
+ ->orWhere('servers.uuid', 'LIKE', "$value%")
+ ->orWhere('servers.uuidShort', $value)
+ ->orWhere('servers.external_id', $value)
+ ->orWhereRaw('LOWER(users.username) LIKE ?', ["%$value%"])
+ ->orWhereRaw('LOWER(users.email) LIKE ?', ["$value%"])
+ ->orWhereRaw('LOWER(servers.name) LIKE ?', ["%$value%"]);
+ })
+ ->groupBy('servers.id');
+ }
+}
diff --git a/resources/views/admin/servers/index.blade.php b/resources/views/admin/servers/index.blade.php
index f76ab1c33..b37044103 100644
--- a/resources/views/admin/servers/index.blade.php
+++ b/resources/views/admin/servers/index.blade.php
@@ -26,7 +26,7 @@