1
1
mirror of https://github.com/pterodactyl/panel.git synced 2024-11-23 01:22:30 +01:00

Merge branch 'develop' into pr/1128

This commit is contained in:
Dane Everitt 2018-09-03 15:10:23 -07:00
commit 4d62e4c7b9
No known key found for this signature in database
GPG Key ID: EEA66103B3D71F53
96 changed files with 1966 additions and 874 deletions

View File

@ -1,26 +0,0 @@
<!---
Please take a little time to submit a good issue. It makes our life easier and the issue will be resolved quicker.
!!! GitHub is NOT the place for difficulties setting up this software. Please use it for bugs and feature requests only. If you have issues setting up the panel or the daemon visit our Discord server: https://pterodactyl.io/discord
If you are submitting a feature request please remove everything in here. Then give a detailed explanation what you want to have implemented and why that would be a good addition.
Please also try to give the issue a good title: It should summarize your issue in a few words and help us see what the issue is about in a glance. Things like "Panel is not working" do not help.
--- You can delete everything above this line. --->
<!--- Please fill in the following basic information --->
* Panel or Daemon:
* Version of Panel/Daemon:
* Server's OS:
* Your Computer's OS & Browser:
------------------------
<!---
Please provide as much information about your issue as needed. Include precise steps to reproduce the issue and provide logs of the components that didn't work as expected.
Please provide additional information, depending on what you have issues with:
Panel: `php -v` (the php version in use).
Daemon: `uname -a` and `docker info` (your kernel version and information regarding docker)
--->

27
.github/ISSUE_TEMPLATE/---bug-report.md vendored Normal file
View File

@ -0,0 +1,27 @@
---
name: "\U0001F41B Bug Report"
about: Create a report to help us resolve a bug or error
---
**Background (please complete the following information):**
* Panel or Daemon:
* Version of Panel/Daemon:
* Server's OS:
* Your Computer's OS & Browser:
**Describe the bug**
A clear and concise description of what the bug is.
Please provide additional information too, depending on what you have issues with:
Panel: `php -v` (the php version in use).
Daemon: `uname -a` and `docker info` (your kernel version and information regarding docker)
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen. If applicable, add screenshots or a recording to help explain your problem.

View File

@ -0,0 +1,17 @@
---
name: "\U0001F680 Feature Request"
about: Suggest an idea for this project
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -0,0 +1,8 @@
---
name: "⛔ Installation Help"
about: 'Visit our Discord for installation help: https://pterodactyl.io/discord'
---
We use GitHub issues only to discuss about Pterodactyl bugs and new features. For
this kind of questions about using Pterodactyl, please visit our Discord for assistance: https://pterodactyl.io/discord

View File

@ -1,5 +1,17 @@
language: php
dist: trusty
git:
depth: 3
quiet: true
matrix:
fast_finish: true
allow_failures:
- env: TEST_SUITE=Coverage
env:
matrix:
- TEST_SUITE=Unit
- TEST_SUITE=Coverage
- TEST_SUITE=Integration
php:
- 7.2
sudo: false
@ -15,8 +27,9 @@ before_script:
- cp .env.travis .env
- travis_retry composer install --no-interaction --prefer-dist --no-suggest
script:
- vendor/bin/phpunit --bootstrap vendor/autoload.php --coverage-clover coverage.xml tests/Unit
- vendor/bin/phpunit tests/Integration
- if [ "$TEST_SUITE" = "Unit" ]; then vendor/bin/phpunit --bootstrap vendor/autoload.php tests/Unit; fi;
- if [ "$TEST_SUITE" = "Coverage" ]; then vendor/bin/phpunit --bootstrap vendor/autoload.php --coverage-clover coverage.xml tests/Unit; fi;
- if [ "$TEST_SUITE" = "Integration" ]; then vendor/bin/phpunit tests/Integration; fi;
notifications:
email: false
webhooks:

View File

@ -3,17 +3,39 @@ 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.
## v0.7.10 (Derelict Dermodactylus)
### Fixed
* Scheduled tasks triggered manually no longer improperly change the `next_run_at` time and do not run twice in a row anymore.
* Changing the maximum web-based file upload size for a node now properly validates and updates.
* Changing configuration values for a node now correctly updates them on the daemon on the first request, rather than requiring a second request to set them.
### Changed
* Egg and server variable values are no longer limited to 191 characters. Turns out some games require a large number of characters in these fields.
## v0.7.9 (Derelict Dermodactylus)
### Fixed
* Fixes a two-factor authentication bypass present in the password reset process for an account.
## v0.7.8 (Derelict Dermodactylus)
### Added
* Nodes can now be put into maintenance mode to deny access to servers temporarily.
* Basic statistics about your panel are now available in the Admin CP.
* Added support for using a MySQL socket location for connections rather than a TCP connection. Set a `DB_SOCKET` variable in your `.env` file to use this.
### Fixed
* Hitting Ctrl+Z when editing a file on the web now works as expected.
* Logo now links to the correct location on all pages.
* Permissions checking to determine if a user can see the task management page now works correctly.
* Fixed `pterodactyl.environment_variables` to be used correctly for global environment variables. The wrong config variable name was being using previously.
* Fixes tokens being sent to users when their account is created to actually work. Implements Laravel's internal token creation mechanisms rather than trying to do it custom.
* Updates some eggs to ensure they have the correct data and will continue working down the road. Fixes autoupdating on some source servers and MC related download links.
* Emails should send properly now when a server is marked as installed to let the owner know it is ready for action.
* Cancelling a file manager operation should cancel correctly across all browsers now.
### Changed
* Attempting to upload a folder via the web file manager will now display a warning telling the user to use SFTP.
* Changing your account password will now log out all other sessions that currently exist for that user.
* Subusers with no permissions selected can be created.
## v0.7.7 (Derelict Dermodactylus)
### Fixed

View File

@ -1,15 +1,19 @@
[![Logo Image](https://cdn.pterodactyl.io/logos/Banner%20Logo%20Black@2x.png)](https://pterodactyl.io)
[![Build Status](https://travis-ci.org/pterodactyl/panel.svg?branch=develop)](https://travis-ci.org/pterodactyl/panel) [![StyleCI](https://styleci.io/repos/47508644/shield?branch=develop)](https://styleci.io/repos/47508644) [![codecov](https://codecov.io/gh/pterodactyl/panel/branch/develop/graph/badge.svg)](https://codecov.io/gh/Pterodactyl/Panel)
[![Build status](https://img.shields.io/travis/pterodactyl/panel/develop.svg?style=flat-square)](https://travis-ci.org/pterodactyl/panel)
[![StyleCI](https://styleci.io/repos/47508644/shield?branch=develop)](https://styleci.io/repos/47508644)
[![Codecov](https://img.shields.io/codecov/c/github/pterodactyl/panel/develop.svg?style=flat-square)](https://codecov.io/gh/Pterodactyl/Panel)
[![Discord](https://img.shields.io/discord/122900397965705216.svg?style=flat-square&label=Discord)](https://pterodactyl.io/discord)
# Pterodactyl Panel
Pterodactyl is the open-source game server management panel built with PHP7, Nodejs, and Go. Designed with security in mind, Pterodactyl runs all game servers in isolated Docker containers while exposing a beautiful and intuitive UI to administrators and users.
What more are you waiting for? Make game servers a first class citizen on your platform today.
![Image](https://cdn.pterodactyl.io/site-assets/mockup-macbook-grey.png)
## Support & Documentation
Support for using Pterodactyl can be found on our [Documentation Website](https://docs.pterodactyl.io), [Guides Website](https://guides.pterodactyl.io), or via our [Discord Chat](https://discord.gg/QRDZvVm).
Support for using Pterodactyl can be found on our [Documentation Website](https://pterodactyl.io/project/introduction.html), [Guides Website](https://guides.pterodactyl.io), or via our [Discord Chat](https://discord.gg/QRDZvVm).
### Supported Games
We support a huge variety of games by utilizing Docker containers to isolate each instance, giving you the power to host your games across the world without having to bloat each physical machine with additional dependencies.

View File

@ -9,7 +9,7 @@
namespace Pterodactyl\Console\Commands\Schedule;
use Carbon\Carbon;
use Cake\Chronos\Chronos;
use Illuminate\Console\Command;
use Illuminate\Support\Collection;
use Pterodactyl\Services\Schedules\ProcessScheduleService;
@ -17,11 +17,6 @@ use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface;
class ProcessRunnableCommand extends Command
{
/**
* @var \Carbon\Carbon
*/
protected $carbon;
/**
* @var string
*/
@ -45,31 +40,28 @@ class ProcessRunnableCommand extends Command
/**
* ProcessRunnableCommand constructor.
*
* @param \Carbon\Carbon $carbon
* @param \Pterodactyl\Services\Schedules\ProcessScheduleService $processScheduleService
* @param \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface $repository
*/
public function __construct(
Carbon $carbon,
ProcessScheduleService $processScheduleService,
ScheduleRepositoryInterface $repository
) {
public function __construct(ProcessScheduleService $processScheduleService, ScheduleRepositoryInterface $repository)
{
parent::__construct();
$this->carbon = $carbon;
$this->processScheduleService = $processScheduleService;
$this->repository = $repository;
}
/**
* Handle command execution.
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function handle()
{
$schedules = $this->repository->getSchedulesToProcess($this->carbon->now()->toAtomString());
$schedules = $this->repository->getSchedulesToProcess(Chronos::now()->toAtomString());
if ($schedules->count() < 1) {
$this->line('There are no scheduled tasks for servers that need to be run.');
return;
}
$bar = $this->output->createProgressBar(count($schedules));
$schedules->each(function ($schedule) use ($bar) {

View File

@ -0,0 +1,15 @@
<?php
namespace Pterodactyl\Contracts\Core;
use Pterodactyl\Events\Event;
interface ReceivesEvents
{
/**
* Handles receiving an event from the application.
*
* @param \Pterodactyl\Events\Event $notification
*/
public function handle(Event $notification): void;
}

View File

@ -202,7 +202,7 @@ interface RepositoryInterface
public function insertIgnore(array $values): bool;
/**
* Get the amount of entries in the database
* Get the amount of entries in the database.
*
* @return int
*/

View File

@ -147,7 +147,7 @@ interface ServerRepositoryInterface extends RepositoryInterface, SearchableInter
public function isUniqueUuidCombo(string $uuid, string $short): bool;
/**
* Get the amount of servers that are suspended
* Get the amount of servers that are suspended.
*
* @return int
*/

View File

@ -0,0 +1,27 @@
<?php
namespace Pterodactyl\Events\Server;
use Pterodactyl\Events\Event;
use Pterodactyl\Models\Server;
use Illuminate\Queue\SerializesModels;
class Installed extends Event
{
use SerializesModels;
/**
* @var \Pterodactyl\Models\Server
*/
public $server;
/**
* Create a new event instance.
*
* @var \Pterodactyl\Models\Server
*/
public function __construct(Server $server)
{
$this->server = $server;
}
}

View File

@ -2,16 +2,14 @@
namespace Pterodactyl\Http\Controllers\Admin;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
use Pterodactyl\Contracts\Repository\NodeRepositoryInterface;
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
use Pterodactyl\Traits\Controllers\PlainJavascriptInjection;
use Pterodactyl\Contracts\Repository\NodeRepositoryInterface;
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
class StatisticsController extends Controller
{
@ -29,15 +27,14 @@ class StatisticsController extends Controller
private $userRepository;
function __construct(
public function __construct(
AllocationRepositoryInterface $allocationRepository,
DatabaseRepositoryInterface $databaseRepository,
EggRepositoryInterface $eggRepository,
NodeRepositoryInterface $nodeRepository,
ServerRepositoryInterface $serverRepository,
UserRepositoryInterface $userRepository
)
{
) {
$this->allocationRepository = $allocationRepository;
$this->databaseRepository = $databaseRepository;
$this->eggRepository = $eggRepository;
@ -83,7 +80,7 @@ class StatisticsController extends Controller
'nodes' => $nodes,
'tokens' => $tokens,
]);
return view('admin.statistics', [
'servers' => $servers,
'nodes' => $nodes,
@ -97,5 +94,4 @@ class StatisticsController extends Controller
'totalAllocations' => $totalAllocations,
]);
}
}

View File

@ -124,7 +124,7 @@ class NodeController extends ApplicationApiController
*/
public function update(UpdateNodeRequest $request): array
{
$node = $this->updateService->returnUpdatedModel()->handle(
$node = $this->updateService->handle(
$request->getModel(Node::class), $request->validated()
);

View File

@ -2,8 +2,14 @@
namespace Pterodactyl\Http\Controllers\Auth;
use Illuminate\Support\Str;
use Prologue\Alerts\AlertsMessageBag;
use Illuminate\Contracts\Hashing\Hasher;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Contracts\Events\Dispatcher;
use Pterodactyl\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
class ResetPasswordController extends Controller
{
@ -16,6 +22,47 @@ class ResetPasswordController extends Controller
*/
public $redirectTo = '/';
/**
* @var bool
*/
protected $hasTwoFactor = false;
/**
* @var \Prologue\Alerts\AlertsMessageBag
*/
private $alerts;
/**
* @var \Illuminate\Contracts\Events\Dispatcher
*/
private $dispatcher;
/**
* @var \Illuminate\Contracts\Hashing\Hasher
*/
private $hasher;
/**
* @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface
*/
private $userRepository;
/**
* ResetPasswordController constructor.
*
* @param \Prologue\Alerts\AlertsMessageBag $alerts
* @param \Illuminate\Contracts\Events\Dispatcher $dispatcher
* @param \Illuminate\Contracts\Hashing\Hasher $hasher
* @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $userRepository
*/
public function __construct(AlertsMessageBag $alerts, Dispatcher $dispatcher, Hasher $hasher, UserRepositoryInterface $userRepository)
{
$this->alerts = $alerts;
$this->dispatcher = $dispatcher;
$this->hasher = $hasher;
$this->userRepository = $userRepository;
}
/**
* Return the rules used when validating password reset.
*
@ -29,4 +76,49 @@ class ResetPasswordController extends Controller
'password' => 'required|confirmed|min:8',
];
}
/**
* Reset the given user's password. If the user has two-factor authentication enabled on their
* account do not automatically log them in. In those cases, send the user back to the login
* form with a note telling them their password was changed and to log back in.
*
* @param \Illuminate\Contracts\Auth\CanResetPassword|\Pterodactyl\Models\User $user
* @param string $password
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
protected function resetPassword($user, $password)
{
$user = $this->userRepository->update($user->id, [
'password' => $this->hasher->make($password),
$user->getRememberTokenName() => Str::random(60),
]);
$this->dispatcher->dispatch(new PasswordReset($user));
// If the user is not using 2FA log them in, otherwise skip this step and force a
// fresh login where they'll be prompted to enter a token.
if (! $user->use_totp) {
$this->guard()->login($user);
}
$this->hasTwoFactor = $user->use_totp;
}
/**
* Get the response for a successful password reset.
*
* @param string $response
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
*/
protected function sendResetResponse($response)
{
if ($this->hasTwoFactor) {
$this->alerts->success('Your password was successfully updated. Please log in to continue.')->flash();
}
return redirect($this->hasTwoFactor ? route('auth.login') : $this->redirectPath())
->with('status', trans($response));
}
}

View File

@ -3,7 +3,9 @@
namespace Pterodactyl\Http\Controllers\Base;
use Pterodactyl\Models\User;
use Illuminate\Auth\AuthManager;
use Prologue\Alerts\AlertsMessageBag;
use Illuminate\Contracts\Session\Session;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Services\Users\UserUpdateService;
use Pterodactyl\Traits\Helpers\AvailableLanguages;
@ -18,6 +20,11 @@ class AccountController extends Controller
*/
protected $alert;
/**
* @var \Illuminate\Auth\SessionGuard
*/
protected $sessionGuard;
/**
* @var \Pterodactyl\Services\Users\UserUpdateService
*/
@ -27,12 +34,14 @@ class AccountController extends Controller
* AccountController constructor.
*
* @param \Prologue\Alerts\AlertsMessageBag $alert
* @param \Illuminate\Auth\AuthManager $authManager
* @param \Pterodactyl\Services\Users\UserUpdateService $updateService
*/
public function __construct(AlertsMessageBag $alert, UserUpdateService $updateService)
public function __construct(AlertsMessageBag $alert, AuthManager $authManager, UserUpdateService $updateService)
{
$this->alert = $alert;
$this->updateService = $updateService;
$this->sessionGuard = $authManager->guard();
}
/**
@ -55,21 +64,26 @@ class AccountController extends Controller
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
*/
public function update(AccountDataFormRequest $request)
{
$data = [];
// Prevent logging this specific session out when the password is changed. This will
// automatically update the user's password anyways, so no need to do anything else here.
if ($request->input('do_action') === 'password') {
$data['password'] = $request->input('new_password');
} elseif ($request->input('do_action') === 'email') {
$data['email'] = $request->input('new_email');
} elseif ($request->input('do_action') === 'identity') {
$data = $request->only(['name_first', 'name_last', 'username', 'language']);
$this->sessionGuard->logoutOtherDevices($request->input('new_password'));
} else {
if ($request->input('do_action') === 'email') {
$data = ['email' => $request->input('new_email')];
} elseif ($request->input('do_action') === 'identity') {
$data = $request->only(['name_first', 'name_last', 'username', 'language']);
} else {
$data = [];
}
$this->updateService->setUserLevel(User::USER_LEVEL_USER);
$this->updateService->handle($request->user(), $data);
}
$this->updateService->setUserLevel(User::USER_LEVEL_USER);
$this->updateService->handle($request->user(), $data);
$this->alert->success(trans('base.account.details_updated'))->flash();
return redirect()->route('account');

View File

@ -7,9 +7,26 @@ use Illuminate\Http\Request;
use Pterodactyl\Models\Node;
use Pterodactyl\Models\Server;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Events\Server\Installed as ServerInstalled;
use Illuminate\Contracts\Events\Dispatcher as EventDispatcher;
class ActionController extends Controller
{
/**
* @var \Illuminate\Contracts\Events\Dispatcher
*/
private $eventDispatcher;
/**
* ActionController constructor.
*
* @param \Illuminate\Contracts\Events\Dispatcher $eventDispatcher
*/
public function __construct(EventDispatcher $eventDispatcher)
{
$this->eventDispatcher = $eventDispatcher;
}
/**
* Handles install toggle request from daemon.
*
@ -37,6 +54,11 @@ class ActionController extends Controller
$server->installed = ($status === 'installed') ? 1 : 2;
$server->save();
// Only fire event if server installed successfully.
if ($server->installed === 1) {
$this->eventDispatcher->dispatch(new ServerInstalled($server));
}
return response()->json([]);
}

View File

@ -157,7 +157,6 @@ class SubuserController extends Controller
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Exception
* @throws \Illuminate\Auth\Access\AuthorizationException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Pterodactyl\Exceptions\Service\Subuser\ServerSubuserExistsException
@ -171,7 +170,7 @@ class SubuserController extends Controller
$this->alert->success(trans('server.users.user_assigned'))->flash();
return redirect()->route('server.subusers.view', [
'uuid' => $server->uuid,
'uuid' => $server->uuidShort,
'id' => $subuser->hashid,
]);
}

View File

@ -2,7 +2,6 @@
namespace Pterodactyl\Http\Controllers\Server\Tasks;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Pterodactyl\Http\Controllers\Controller;
@ -11,12 +10,22 @@ use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface;
class ActionController extends Controller
{
/**
* @var \Pterodactyl\Services\Schedules\ProcessScheduleService
*/
private $processScheduleService;
/**
* @var \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface
*/
private $repository;
/**
* ActionController constructor.
*
* @param \Pterodactyl\Services\Schedules\ProcessScheduleService $processScheduleService
* @param \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface $repository
*/
public function __construct(ProcessScheduleService $processScheduleService, ScheduleRepositoryInterface $repository)
{
$this->processScheduleService = $processScheduleService;
@ -61,7 +70,7 @@ class ActionController extends Controller
$server = $request->attributes->get('server');
$this->authorize('toggle-schedule', $server);
$this->processScheduleService->setRunTimeOverride(Carbon::now())->handle(
$this->processScheduleService->handle(
$request->attributes->get('schedule')
);

View File

@ -2,7 +2,6 @@
namespace Pterodactyl\Http;
use Pterodactyl\Http\Middleware\MaintenanceMiddleware;
use Pterodactyl\Models\ApiKey;
use Illuminate\Auth\Middleware\Authorize;
use Illuminate\Auth\Middleware\Authenticate;
@ -18,15 +17,17 @@ use Pterodactyl\Http\Middleware\LanguageMiddleware;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
use Pterodactyl\Http\Middleware\Api\AuthenticateKey;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Pterodactyl\Http\Middleware\AccessingValidServer;
use Pterodactyl\Http\Middleware\Api\SetSessionDriver;
use Illuminate\Session\Middleware\AuthenticateSession;
use Illuminate\View\Middleware\ShareErrorsFromSession;
use Pterodactyl\Http\Middleware\MaintenanceMiddleware;
use Pterodactyl\Http\Middleware\RedirectIfAuthenticated;
use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth;
use Pterodactyl\Http\Middleware\Api\AuthenticateIPAccess;
use Pterodactyl\Http\Middleware\Api\ApiSubstituteBindings;
use Illuminate\Foundation\Http\Middleware\ValidatePostSize;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
use Pterodactyl\Http\Middleware\Server\AccessingValidServer;
use Pterodactyl\Http\Middleware\Server\AuthenticateAsSubuser;
use Pterodactyl\Http\Middleware\Api\Daemon\DaemonAuthenticate;
use Pterodactyl\Http\Middleware\Server\SubuserBelongsToServer;
@ -64,6 +65,7 @@ class Kernel extends HttpKernel
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartSession::class,
AuthenticateSession::class,
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
SubstituteBindings::class,

View File

@ -1,6 +1,6 @@
<?php
namespace Pterodactyl\Http\Middleware;
namespace Pterodactyl\Http\Middleware\Server;
use Closure;
use Illuminate\Http\Request;

View File

@ -74,7 +74,7 @@ class StoreNodeRequest extends ApplicationApiRequest
$response = parent::validated();
$response['daemonListen'] = $response['daemon_listen'];
$response['daemonSFTP'] = $response['daemon_sftp'];
$response['daemonBase'] = $response['daemon_base'];
$response['daemonBase'] = $response['daemon_base'] ?? (new Node)->getAttribute('daemonBase');
unset($response['daemon_base'], $response['daemon_listen'], $response['daemon_sftp']);

View File

@ -25,7 +25,7 @@ class SubuserStoreFormRequest extends ServerFormRequest
{
return [
'email' => 'required|email',
'permissions' => 'present|array',
'permissions' => 'sometimes|array',
];
}
}

View File

@ -3,7 +3,7 @@
namespace Pterodactyl\Jobs\Schedule;
use Exception;
use Carbon\Carbon;
use Cake\Chronos\Chronos;
use Pterodactyl\Jobs\Job;
use InvalidArgumentException;
use Illuminate\Queue\SerializesModels;
@ -158,7 +158,7 @@ class RunTaskJob extends Job implements ShouldQueue
$repository = app()->make(ScheduleRepositoryInterface::class);
$repository->withoutFreshModel()->update($this->schedule, [
'is_processing' => false,
'last_run_at' => Carbon::now()->toDateTimeString(),
'last_run_at' => Chronos::now()->toDateTimeString(),
]);
}

View File

View File

@ -113,6 +113,7 @@ class Node extends Model implements CleansAttributes, ValidableContract
'daemonSFTP' => 'numeric|between:1024,65535',
'daemonListen' => 'numeric|between:1024,65535',
'maintenance_mode' => 'boolean',
'upload_size' => 'int|between:1,1024',
];
/**

View File

@ -23,7 +23,7 @@ class AccountCreated extends Notification implements ShouldQueue
/**
* The user model for the created user.
*
* @var object
* @var \Pterodactyl\Models\User
*/
public $user;
@ -65,7 +65,7 @@ class AccountCreated extends Notification implements ShouldQueue
->line('Email: ' . $this->user->email);
if (! is_null($this->token)) {
return $message->action('Setup Your Account', url('/auth/password/reset/' . $this->token . '?email=' . $this->user->email));
return $message->action('Setup Your Account', url('/auth/password/reset/' . $this->token . '?email=' . urlencode($this->user->email)));
}
return $message;

View File

@ -0,0 +1,69 @@
<?php
namespace Pterodactyl\Notifications;
use Illuminate\Bus\Queueable;
use Pterodactyl\Events\Event;
use Illuminate\Container\Container;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Pterodactyl\Contracts\Core\ReceivesEvents;
use Illuminate\Contracts\Notifications\Dispatcher;
use Illuminate\Notifications\Messages\MailMessage;
class ServerInstalled extends Notification implements ShouldQueue, ReceivesEvents
{
use Queueable;
/**
* @var \Pterodactyl\Models\Server
*/
public $server;
/**
* @var \Pterodactyl\Models\User
*/
public $user;
/**
* Handle a direct call to this notification from the server installed event. This is configured
* in the event service provider.
*
* @param \Pterodactyl\Events\Event|\Pterodactyl\Events\Server\Installed $event
*/
public function handle(Event $event): void
{
$event->server->loadMissing('user');
$this->server = $event->server;
$this->user = $event->server->user;
// Since we are calling this notification directly from an event listener we need to fire off the dispatcher
// to send the email now. Don't use send() or you'll end up firing off two different events.
Container::getInstance()->make(Dispatcher::class)->sendNow($this->user, $this);
}
/**
* Get the notification's delivery channels.
*
* @return array
*/
public function via()
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail()
{
return (new MailMessage)
->greeting('Hello ' . $this->user->username . '.')
->line('Your server has finished installing and is now ready for you to use.')
->line('Server Name: ' . $this->server->name)
->action('Login and Begin Using', route('index'));
}
}

View File

@ -2,6 +2,8 @@
namespace Pterodactyl\Providers;
use Pterodactyl\Events\Server\Installed as ServerInstalledEvent;
use Pterodactyl\Notifications\ServerInstalled as ServerInstalledNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
@ -11,5 +13,9 @@ class EventServiceProvider extends ServiceProvider
*
* @var array
*/
protected $listen = [];
protected $listen = [
ServerInstalledEvent::class => [
ServerInstalledNotification::class,
],
];
}

View File

@ -144,6 +144,7 @@ abstract class BaseRepository implements BaseRepositoryInterface
$headers['X-Access-Token'] = $this->getToken() ?? $this->getNode()->daemonSecret;
return new Client([
'verify' => config('app.env') === 'production',
'base_uri' => sprintf('%s://%s:%s/v1/', $this->getNode()->scheme, $this->getNode()->fqdn, $this->getNode()->daemonListen),
'timeout' => config('pterodactyl.guzzle.timeout'),
'connect_timeout' => config('pterodactyl.guzzle.connect_timeout'),

View File

@ -298,7 +298,7 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf
}
/**
* Get the amount of entries in the database
* Get the amount of entries in the database.
*
* @return int
*/

View File

@ -75,6 +75,7 @@ class ScheduleRepository extends EloquentRepository implements ScheduleRepositor
{
return $this->getBuilder()->with('tasks')
->where('is_active', true)
->where('is_processing', false)
->where('next_run_at', '<=', $timestamp)
->get($this->getColumns());
}

View File

@ -330,7 +330,7 @@ class ServerRepository extends EloquentRepository implements ServerRepositoryInt
}
/**
* Get the amount of servers that are suspended
* Get the amount of servers that are suspended.
*
* @return int
*/

View File

@ -1,59 +0,0 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* This software is licensed under the terms of the MIT license.
* https://opensource.org/licenses/MIT
*/
namespace Pterodactyl\Services\Helpers;
use Ramsey\Uuid\Uuid;
use Illuminate\Contracts\Hashing\Hasher;
use Illuminate\Database\ConnectionInterface;
class TemporaryPasswordService
{
const HMAC_ALGO = 'sha256';
/**
* @var \Illuminate\Database\ConnectionInterface
*/
protected $connection;
/**
* @var \Illuminate\Contracts\Hashing\Hasher
*/
protected $hasher;
/**
* TemporaryPasswordService constructor.
*
* @param \Illuminate\Database\ConnectionInterface $connection
* @param \Illuminate\Contracts\Hashing\Hasher $hasher
*/
public function __construct(ConnectionInterface $connection, Hasher $hasher)
{
$this->connection = $connection;
$this->hasher = $hasher;
}
/**
* Store a password reset token for a specific email address.
*
* @param string $email
* @return string
*/
public function handle($email)
{
$token = hash_hmac(self::HMAC_ALGO, Uuid::uuid4()->toString(), config('app.key'));
$this->connection->table('password_resets')->insert([
'email' => $email,
'token' => $this->hasher->make($token),
]);
return $token;
}
}

View File

@ -1,11 +1,4 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* This software is licensed under the terms of the MIT license.
* https://opensource.org/licenses/MIT
*/
namespace Pterodactyl\Services\Nodes;
@ -13,7 +6,6 @@ use Pterodactyl\Models\Node;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\RequestException;
use Illuminate\Database\ConnectionInterface;
use Pterodactyl\Traits\Services\ReturnsUpdatedModels;
use Pterodactyl\Contracts\Repository\NodeRepositoryInterface;
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
use Pterodactyl\Exceptions\Service\Node\ConfigurationNotPersistedException;
@ -21,8 +13,6 @@ use Pterodactyl\Contracts\Repository\Daemon\ConfigurationRepositoryInterface;
class NodeUpdateService
{
use ReturnsUpdatedModels;
/**
* @var \Illuminate\Database\ConnectionInterface
*/
@ -60,7 +50,7 @@ class NodeUpdateService
*
* @param \Pterodactyl\Models\Node $node
* @param array $data
* @return \Pterodactyl\Models\Node|mixed
* @return \Pterodactyl\Models\Node
*
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
@ -74,14 +64,10 @@ class NodeUpdateService
}
$this->connection->beginTransaction();
if ($this->getUpdatedModel()) {
$response = $this->repository->update($node->id, $data);
} else {
$response = $this->repository->withoutFreshModel()->update($node->id, $data);
}
$updatedModel = $this->repository->update($node->id, $data);
try {
$this->configRepository->setNode($node)->update();
$this->configRepository->setNode($updatedModel)->update();
$this->connection->commit();
} catch (RequestException $exception) {
// Failed to connect to the Daemon. Let's go ahead and save the configuration
@ -95,6 +81,6 @@ class NodeUpdateService
throw new DaemonConnectionException($exception);
}
return $response;
return $updatedModel;
}
}

View File

@ -2,53 +2,45 @@
namespace Pterodactyl\Services\Schedules;
use Carbon\Carbon;
use Cron\CronExpression;
use Pterodactyl\Models\Schedule;
use Pterodactyl\Services\Schedules\Tasks\RunTaskService;
use Illuminate\Contracts\Bus\Dispatcher;
use Pterodactyl\Jobs\Schedule\RunTaskJob;
use Pterodactyl\Contracts\Repository\TaskRepositoryInterface;
use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface;
class ProcessScheduleService
{
/**
* @var \Illuminate\Contracts\Bus\Dispatcher
*/
private $dispatcher;
/**
* @var \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface
*/
private $repository;
private $scheduleRepository;
/**
* @var \Pterodactyl\Services\Schedules\Tasks\RunTaskService
* @var \Pterodactyl\Contracts\Repository\TaskRepositoryInterface
*/
private $runnerService;
/**
* @var \Carbon\Carbon|null
*/
private $runTimeOverride;
private $taskRepository;
/**
* ProcessScheduleService constructor.
*
* @param \Pterodactyl\Services\Schedules\Tasks\RunTaskService $runnerService
* @param \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface $repository
* @param \Illuminate\Contracts\Bus\Dispatcher $dispatcher
* @param \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface $scheduleRepository
* @param \Pterodactyl\Contracts\Repository\TaskRepositoryInterface $taskRepository
*/
public function __construct(RunTaskService $runnerService, ScheduleRepositoryInterface $repository)
{
$this->repository = $repository;
$this->runnerService = $runnerService;
}
/**
* Set the time that this schedule should be run at. This will override the time
* defined on the schedule itself. Useful for triggering one-off task runs.
*
* @param \Carbon\Carbon $time
* @return $this
*/
public function setRunTimeOverride(Carbon $time)
{
$this->runTimeOverride = $time;
return $this;
public function __construct(
Dispatcher $dispatcher,
ScheduleRepositoryInterface $scheduleRepository,
TaskRepositoryInterface $taskRepository
) {
$this->dispatcher = $dispatcher;
$this->scheduleRepository = $scheduleRepository;
$this->taskRepository = $taskRepository;
}
/**
@ -61,7 +53,10 @@ class ProcessScheduleService
*/
public function handle(Schedule $schedule)
{
$this->repository->loadTasks($schedule);
$this->scheduleRepository->loadTasks($schedule);
/** @var \Pterodactyl\Models\Task $task */
$task = $schedule->getRelation('tasks')->where('sequence_id', 1)->first();
$formattedCron = sprintf('%s %s %s * %s',
$schedule->cron_minute,
@ -70,27 +65,15 @@ class ProcessScheduleService
$schedule->cron_day_of_week
);
$this->repository->update($schedule->id, [
$this->scheduleRepository->update($schedule->id, [
'is_processing' => true,
'next_run_at' => $this->getRunAtTime($formattedCron),
'next_run_at' => CronExpression::factory($formattedCron)->getNextRunDate(),
]);
$task = $schedule->getRelation('tasks')->where('sequence_id', 1)->first();
$this->runnerService->handle($task);
}
$this->taskRepository->update($task->id, ['is_queued' => true]);
/**
* Get the timestamp to store in the database as the next_run time for a schedule.
*
* @param string $formatted
* @return \DateTime|string
*/
private function getRunAtTime(string $formatted)
{
if ($this->runTimeOverride instanceof Carbon) {
return $this->runTimeOverride->toDateTimeString();
}
return CronExpression::factory($formatted)->getNextRunDate();
$this->dispatcher->dispatch(
(new RunTaskJob($task->id, $schedule->id))->delay($task->time_offset)
);
}
}

View File

@ -1,53 +0,0 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* This software is licensed under the terms of the MIT license.
* https://opensource.org/licenses/MIT
*/
namespace Pterodactyl\Services\Schedules\Tasks;
use Pterodactyl\Models\Task;
use Pterodactyl\Jobs\Schedule\RunTaskJob;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Pterodactyl\Contracts\Repository\TaskRepositoryInterface;
class RunTaskService
{
use DispatchesJobs;
/**
* @var \Pterodactyl\Contracts\Repository\TaskRepositoryInterface
*/
protected $repository;
/**
* RunTaskService constructor.
*
* @param \Pterodactyl\Contracts\Repository\TaskRepositoryInterface $repository
*/
public function __construct(TaskRepositoryInterface $repository)
{
$this->repository = $repository;
}
/**
* Push a single task onto the queue.
*
* @param int|\Pterodactyl\Models\Task $task
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function handle($task)
{
if (! $task instanceof Task) {
$task = $this->repository->find($task);
}
$this->repository->update($task->id, ['is_queued' => true]);
$this->dispatch((new RunTaskJob($task->id, $task->schedule_id))->delay($task->time_offset));
}
}

View File

@ -78,7 +78,7 @@ class EnvironmentService
}
// Process variables set in the configuration file.
foreach ($this->config->get('pterodactyl.environment_mappings', []) as $key => $object) {
foreach ($this->config->get('pterodactyl.environment_variables', []) as $key => $object) {
if (is_callable($object)) {
$variables[$key] = call_user_func($object, $server);
} else {

View File

@ -56,6 +56,8 @@ class PermissionCreationService
}
}
$this->repository->withoutFreshModel()->insert($insertPermissions);
if (! empty($insertPermissions)) {
$this->repository->withoutFreshModel()->insert($insertPermissions);
}
}
}

View File

@ -5,8 +5,8 @@ namespace Pterodactyl\Services\Users;
use Ramsey\Uuid\Uuid;
use Illuminate\Contracts\Hashing\Hasher;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Contracts\Auth\PasswordBroker;
use Pterodactyl\Notifications\AccountCreated;
use Pterodactyl\Services\Helpers\TemporaryPasswordService;
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
class UserCreationService
@ -22,9 +22,9 @@ class UserCreationService
private $hasher;
/**
* @var \Pterodactyl\Services\Helpers\TemporaryPasswordService
* @var \Illuminate\Contracts\Auth\PasswordBroker
*/
private $passwordService;
private $passwordBroker;
/**
* @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface
@ -36,18 +36,18 @@ class UserCreationService
*
* @param \Illuminate\Database\ConnectionInterface $connection
* @param \Illuminate\Contracts\Hashing\Hasher $hasher
* @param \Pterodactyl\Services\Helpers\TemporaryPasswordService $passwordService
* @param \Illuminate\Contracts\Auth\PasswordBroker $passwordBroker
* @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $repository
*/
public function __construct(
ConnectionInterface $connection,
Hasher $hasher,
TemporaryPasswordService $passwordService,
PasswordBroker $passwordBroker,
UserRepositoryInterface $repository
) {
$this->connection = $connection;
$this->hasher = $hasher;
$this->passwordService = $passwordService;
$this->passwordBroker = $passwordBroker;
$this->repository = $repository;
}
@ -68,8 +68,8 @@ class UserCreationService
$this->connection->beginTransaction();
if (! isset($data['password']) || empty($data['password'])) {
$generateResetToken = true;
$data['password'] = $this->hasher->make(str_random(30));
$token = $this->passwordService->handle($data['email']);
}
/** @var \Pterodactyl\Models\User $user */
@ -77,6 +77,10 @@ class UserCreationService
'uuid' => Uuid::uuid4()->toString(),
]), true, true);
if (isset($generateResetToken)) {
$token = $this->passwordBroker->createToken($user);
}
$this->connection->commit();
$user->notify(new AccountCreated($user, $token ?? null));

View File

@ -3,7 +3,7 @@
* Created by PhpStorm.
* User: Stan
* Date: 26-5-2018
* Time: 20:56
* Time: 20:56.
*/
namespace Pterodactyl\Traits\Controllers;
@ -12,13 +12,11 @@ use JavaScript;
trait PlainJavascriptInjection
{
/**
* Injects statistics into javascript
* Injects statistics into javascript.
*/
public function injectJavascript($data)
{
Javascript::put($data);
}
}
}

View File

@ -1,8 +1,6 @@
coverage:
status:
project:
default:
target: 35
threshold: 1
project: off
patch: off
comment:
layout: "diff"

View File

@ -85,6 +85,6 @@
"config": {
"preferred-install": "dist",
"sort-packages": true,
"optimize-autoloader": true
"optimize-autoloader": false
}
}

561
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -34,6 +34,7 @@ return [
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'unix_socket' => env('DB_SOCKET'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'panel'),
'username' => env('DB_USERNAME', 'pterodactyl'),

View File

@ -14,7 +14,7 @@ class AddMaintenanceToNodes extends Migration
public function up()
{
Schema::table('nodes', function (Blueprint $table) {
$table->boolean('maintenance_mode')->after('behind_proxy')->default(false);
$table->boolean('maintenance_mode')->after('behind_proxy')->default(false);
});
}
@ -26,7 +26,7 @@ class AddMaintenanceToNodes extends Migration
public function down()
{
Schema::table('nodes', function (Blueprint $table) {
$table->dropColumn('maintenance_mode');
$table->dropColumn('maintenance_mode');
});
}
}

View File

@ -0,0 +1,32 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AllowEggVariablesToHaveLongerValues extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('egg_variables', function (Blueprint $table) {
$table->text('default_value')->change();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('egg_variables', function (Blueprint $table) {
$table->string('default_value')->change();
});
}
}

View File

@ -0,0 +1,32 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AllowServerVariablesToHaveLongerValues extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('server_variables', function (Blueprint $table) {
$table->text('variable_value')->change();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('server_variables', function (Blueprint $table) {
$table->string('variable_value')->change();
});
}
}

View File

@ -3,7 +3,7 @@
"meta": {
"version": "PTDL_v1"
},
"exported_at": "2018-02-27T00:57:04-06:00",
"exported_at": "2018-06-25T15:47:07-04: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\/ash\r\n# Forge Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napk update\r\napk add curl\r\n\r\nGET_VERSIONS=$(curl -sl http:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/ | grep -A1 Latest | grep -o -e '[1]\\.[0-9][0-9]]\\?\\.\\?[0-9]\\?[0-9] - [0-9][0-9]\\.[0-9][0-9]\\.[0-9]\\?[0-9]\\.[0-9][0-9][0-9][0-9]')\r\nLATEST_VERSION=$(echo $GET_VERSIONS | sed 's\/ \/\/g')\r\n\r\ncd \/mnt\/server\r\n\r\ncurl -sS http:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/$LATEST_VERSION\/forge-$LATEST_VERSION-installer.jar -o installer.jar\r\ncurl -sS http:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/$LATEST_VERSION\/forge-$LATEST_VERSION-universal.jar -o server.jar\r\n\r\njava -jar installer.jar --installServer\r\nrm -rf installer.jar",
"script": "#!\/bin\/ash\r\n# Forge Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napk update\r\napk add curl\r\n\r\nif [ -z \"$MC_VERSION\" ] || [ \"$MC_VERSION\" == \"latest\" ]; then\r\n FORGE_VERSION=$(curl -sl http:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/ | grep -A1 Latest | grep -o -e '[1]\\.[0-9][0-9]]\\?\\.\\?[0-9]\\?[0-9] - [0-9][0-9]\\.[0-9][0-9]\\.[0-9]\\?[0-9]\\.[0-9][0-9][0-9][0-9]' | sed 's\/ \/\/g')\r\nelse\r\n FORGE_VERSION=$(curl -sl http:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/index_$MC_VERSION.html | grep -A1 Latest | grep -o -e '[1]\\.[0-9][0-9]]\\?\\.\\?[0-9]\\?[0-9] - [0-9][0-9]\\.[0-9][0-9]\\.[0-9]\\?[0-9]\\.[0-9][0-9][0-9][0-9]' | sed 's\/ \/\/g')\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\necho -e \"\\nDownloading Forge Version $FORGE_VERSION\\n\"\r\ncurl -sS http:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/$FORGE_VERSION\/forge-$FORGE_VERSION-installer.jar -o installer.jar\r\ncurl -sS http:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/$FORGE_VERSION\/forge-$FORGE_VERSION-universal.jar -o $SERVER_JARFILE\r\n\r\necho -e \"\\nInstalling forge server usint the installer jar file.\\n\"\r\njava -jar installer.jar --installServer\r\n\r\necho -e \"\\nDeleting installer jar file and cleaning up.\\n\"\r\nrm -rf installer.jar",
"container": "frolvlad\/alpine-oraclejdk8:cleaned",
"entrypoint": "ash"
}
@ -31,6 +31,15 @@
"user_viewable": 1,
"user_editable": 1,
"rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/"
},
{
"name": "Minecraft version",
"description": "The version of minecraft that you want to run. Example (1.10.2).",
"env_variable": "MC_VERSION",
"default_value": "latest",
"user_viewable": 1,
"user_editable": 1,
"rules": "required|string|max:20"
}
]
}

View File

@ -3,7 +3,7 @@
"meta": {
"version": "PTDL_v1"
},
"exported_at": "2017-11-03T22:15:07-05:00",
"exported_at": "2018-06-19T17:09:18-04: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,8 +17,8 @@
},
"scripts": {
"installation": {
"script": "#!\/bin\/ash\r\n# Vanilla MC Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napk update\r\napk add curl\r\n\r\ncd \/mnt\/server\r\n\r\nLATEST_VERSION=`curl -s https:\/\/s3.amazonaws.com\/Minecraft.Download\/versions\/versions.json | grep -o \"[[:digit:]]\\.[0-9]*\\.[0-9]\" | head -n 1`\r\n\r\nif [ -z \"$VANILLA_VERSION\" ] || [ \"$VANILLA_VERSION\" == \"latest\" ]; then\r\n DL_VERSION=$LATEST_VERSION\r\nelse\r\n DL_VERSION=$VANILLA_VERSION\r\nfi\r\n\r\ncurl -o ${SERVER_JARFILE} https:\/\/s3.amazonaws.com\/Minecraft.Download\/versions\/${DL_VERSION}\/minecraft_server.${DL_VERSION}.jar",
"container": "alpine:3.4",
"script": "#!\/bin\/ash\r\n# Vanilla MC Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napk update\r\napk add curl jq\r\n\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\nif [ -z \"$VANILLA_VERSION\" ] || [ \"$VANILLA_VERSION\" == \"latest\" ]; then\r\n MANIFEST_URL=$(curl https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq .versions | jq -r '.[] | select(.id == \"'$LATEST_VERSION'\") | .url')\r\nelse\r\n MANIFEST_URL=$(curl https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq .versions | jq -r '.[] | select(.id == \"'$VANILLA_VERSION'\") | .url')\r\nfi\r\n\r\nDOWNLOAD_URL=`curl $MANIFEST_URL | jq .downloads.server | jq -r '. | .url'`\r\n\r\ncurl -o ${SERVER_JARFILE} $DOWNLOAD_URL",
"container": "alpine:3.7",
"entrypoint": "ash"
}
},
@ -39,7 +39,7 @@
"default_value": "latest",
"user_viewable": 1,
"user_editable": 1,
"rules": "required|string|between:3,7"
"rules": "required|string|between:3,15"
}
]
}
}

View File

@ -3,12 +3,12 @@
"meta": {
"version": "PTDL_v1"
},
"exported_at": "2018-01-21T16:59:40-06:00",
"exported_at": "2018-07-06T11:27:32+02:00",
"name": "Ark: Survival Evolved",
"author": "support@pterodactyl.io",
"description": "As a man or woman stranded, naked, freezing, and starving on the unforgiving shores of a mysterious island called ARK, use your skill and cunning to kill or tame and ride the plethora of leviathan dinosaurs and other primeval creatures roaming the land. Hunt, harvest resources, craft items, grow crops, research technologies, and build shelters to withstand the elements and store valuables, all while teaming up with (or preying upon) hundreds of other players to survive, dominate... and escape! \u2014 Gamepedia: ARK",
"image": "quay.io\/pterodactyl\/core:source",
"startup": ".\/ShooterGame\/Binaries\/Linux\/ShooterGameServer TheIsland?listen?ServerPassword={{ARK_PASSWORD}}?ServerAdminPassword={{ARK_ADMIN_PASSWORD}}?Port={{SERVER_PORT}}?MaxPlayers={{SERVER_MAX_PLAYERS}}",
"startup": ".\/ShooterGame\/Binaries\/Linux\/ShooterGameServer {{SERVER_MAP}}?listen?ServerPassword={{ARK_PASSWORD}}?ServerAdminPassword={{ARK_ADMIN_PASSWORD}}?Port={{SERVER_PORT}}?MaxPlayers={{SERVER_MAX_PLAYERS}}",
"config": {
"files": "{}",
"startup": "{\r\n \"done\": \"gameserver Steam ID\",\r\n \"userInteraction\": []\r\n}",
@ -49,6 +49,15 @@
"user_viewable": 1,
"user_editable": 1,
"rules": "required|numeric|digits_between:1,4"
},
{
"name": "Server Map",
"description": "Available Maps: TheIsland, TheCenter, Ragnarok, ScorchedEarth_P, Aberration_P",
"env_variable": "SERVER_MAP",
"default_value": "TheIsland",
"user_viewable": 1,
"user_editable": 1,
"rules": "required|string|max:20"
}
]
}
}

View File

@ -3,7 +3,7 @@
"meta": {
"version": "PTDL_v1"
},
"exported_at": "2018-01-21T16:59:47-06:00",
"exported_at": "2018-06-19T07:46:06-04: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.",
@ -11,7 +11,7 @@
"startup": ".\/srcds_run -game csgo -console -port {{SERVER_PORT}} +ip 0.0.0.0 +map {{SRCDS_MAP}} -strictportbind -norestart +sv_setsteamaccount {{STEAM_ACC}}",
"config": {
"files": "{}",
"startup": "{\r\n \"done\": \"gameserver Steam ID\",\r\n \"userInteraction\": []\r\n}",
"startup": "{\r\n \"done\": \"Connection to Steam servers successful\",\r\n \"userInteraction\": []\r\n}",
"logs": "{\r\n \"custom\": true,\r\n \"location\": \"logs\/latest.log\"\r\n}",
"stop": "quit"
},
@ -40,6 +40,15 @@
"user_viewable": 1,
"user_editable": 1,
"rules": "required|string|alpha_num|size:32"
},
{
"name": "Source AppID",
"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,
"rules": "required|string|max:20"
}
]
}
}

View File

@ -3,7 +3,7 @@
"meta": {
"version": "PTDL_v1"
},
"exported_at": "2018-01-21T16:59:47-06:00",
"exported_at": "2018-06-19T07:46:27-04: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.",
@ -40,6 +40,15 @@
"user_viewable": 1,
"user_editable": 1,
"rules": "required|string|alpha_num|size:32"
},
{
"name": "Source AppID",
"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,
"rules": "required|string|max:20"
}
]
}
}

View File

@ -474,6 +474,11 @@ label.control-label > span.field-optional:before {
width: auto;
}
.search01 {
width: 30%;
}
.number-info-box-content {
padding: 15px 10px 0;
}

View File

@ -26,6 +26,7 @@
#terminal > .cmd {
padding: 1px 0;
white-space: pre;
}
#terminal_input {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -55,6 +55,10 @@ class ActionsClass {
showLoaderOnConfirm: true,
inputValue: inputValue
}, (val) => {
if (val === false) {
return false;
}
$.ajax({
type: 'POST',
headers: {
@ -100,6 +104,10 @@ class ActionsClass {
showLoaderOnConfirm: true,
inputValue: `${currentPath}${currentName}`,
}, (val) => {
if (val === false) {
return false;
}
$.ajax({
type: 'POST',
headers: {
@ -233,6 +241,10 @@ class ActionsClass {
showLoaderOnConfirm: true,
inputValue: `${currentPath}${currentName}`,
}, (val) => {
if (val === false) {
return false;
}
$.ajax({
type: 'POST',
headers: {

View File

@ -0,0 +1,33 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* This software is licensed under the terms of the MIT license.
* https://opensource.org/licenses/MIT
*/
return [
'notices' => [
'created' => '一个新的管理模块, :name, 已成功创建。',
'deleted' => '成功从面板删除指定的管理模块。',
'updated' => '成功更新管理模块的选项。',
],
'eggs' => [
'notices' => [
'imported' => '成功导入一个管理模板。',
'updated_via_import' => '该管理模板已按照上传的文件完成更新。',
'deleted' => '成功删除指定的管路模板。',
'updated' => '成功更新管理模板的配置。',
'script_updated' => '管理模板的安装脚本已经成功更新并且会在安装新服务器时被执行。',
'egg_created' => '一个管理模板已经成功创建. 你需要重启所有正在运行的节点受控端来使该模板生效。',
],
],
'variables' => [
'notices' => [
'variable_deleted' => '参数 ":variable" 已被移除,在服务器重装之后将不在有效。',
'variable_updated' => '参数 ":variable" 已更新。 你需要重装所有服务器来使该参数生效.',
'variable_created' => '新的参数已经创建并被赋值,该操作会影响此管理模板下的所有服务器',
],
],
];

View File

@ -0,0 +1,23 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* This software is licensed under the terms of the MIT license.
* https://opensource.org/licenses/MIT
*/
return [
'validation' => [
'fqdn_not_resolvable' => '提供的域名或地址没有解析到一个合法的IP地址.',
'fqdn_required_for_ssl' => '这个节点要求解析到一个公共IP的域名必须使用SSL',
],
'notices' => [
'allocations_added' => '配额已经成功的被添加到这个节点.',
'node_deleted' => '节点成功从面板中移除.',
'location_required' => '在你可以添加一个节点之前必须至少有一个可用区配置。',
'node_created' => '节点新建成功! 使用 \'Configuration\' 标签,你可以在此节点上自动配置受控端. <strong>在你可以创建服务器之前你必须至少分配一个IP和端口</strong>',
'node_updated' => '节点信息更新成功!如果任何节点受控端的设置更改了,您需要重启受控端来使设置生效.',
'unallocated_deleted' => '已删除 <code>:ip</code> 上的所有未分配的端口',
],
];

View File

@ -0,0 +1,16 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* This software is licensed under the terms of the MIT license.
* https://opensource.org/licenses/MIT
*/
return [
'notices' => [
'pack_updated' => '整合包已经被更新。',
'pack_deleted' => '成功删除整合包: ":name" 。',
'pack_created' => '一个整合包已被成功创建,现在可以用它来部署服务器了。',
],
];

View File

@ -0,0 +1,31 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* This software is licensed under the terms of the MIT license.
* https://opensource.org/licenses/MIT
*/
return [
'exceptions' => [
'no_new_default_allocation' => '你正在尝试删除此服务器的默认配额,但是该服务器没有足够的后备配额。',
'marked_as_failed' => '这个服务器目前被标记为安装失败。 当前状态不能改变为此状态。',
'bad_variable' => '变量 :name 有一个已确认的错误 。',
'daemon_exception' => '连接受控端时发生意外 返回错误码 HTTP/:code response code. 此错误已被记录。',
'default_allocation_not_found' => '请求的默认配额没有在这台服务器上找到。',
],
'alerts' => [
'startup_changed' => '该服务器的启动配置已被更新. 如果此服务器所属的管理模块或管理模板更改,此时将发生一次配置重设',
'server_deleted' => '成功从系统中删除服务器',
'server_created' => '创建服务器成功。 请稍后几分钟,受控端将尽快完成服务器安装',
'build_updated' => '启动参数已更改。 一些修改需要重启该服务器后生效。',
'suspension_toggled' => '服务器状态已更改为 :status.',
'rebuild_on_boot' => '此服务器已被标记为需要在Docker容器中启动。 此操作会在下次重启后生效。',
'install_toggled' => '此服务器的安装状态已被更改',
'server_reinstalled' => '此服务器目前已置于重装队列中,即将开始重装',
'details_updated' => '服务器信息成功被更新',
'docker_image_updated' => '成功更改用于该服务器的默认的Docker镜像。 此操作需要重启后生效',
'node_required' => '你需要至少一个节点才能开始添加服务器',
],
];

View File

@ -0,0 +1,18 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* This software is licensed under the terms of the MIT license.
* https://opensource.org/licenses/MIT
*/
return [
'exceptions' => [
'user_has_servers' => '无法删除一个拥有活动状态服务器的用户. 请在继续此操作前删除他的服务器',
],
'notices' => [
'account_created' => '成功创建用户',
'account_updated' => '成功更新用户',
],
];

View File

@ -0,0 +1,22 @@
<?php
return [
'not_authorized' => '您无权执行此操作。',
'auth_error' => '尝试登录时发生错误.',
'authentication_required' => '需要认证才能继续操作',
'remember_me' => '记住我',
'sign_in' => '登陆',
'forgot_password' => '忘记密码',
'request_reset_text' => '忘记密码? 请在下方填入您的Email.',
'reset_password_text' => '重设您账户的密码.',
'reset_password' => '重设密码',
'email_sent' => '一封帮助您重置密码的电子邮件已发出,请查收并按提示操作(如未收到请检查垃圾箱)',
'failed' => '用户名或密码错误, 或者两步验证失败.',
'throttle' => '太多次登陆失败. 请在 :seconds 秒后尝试',
'password_requirements' => '密码至少包含大写字母小写字母数字并且在8位以上.',
'request_reset' => '查找账户',
'2fa_required' => '两步验证',
'2fa_failed' => '两步验证密码错误',
'totp_failed' => '错误的TOTP验证.',
'2fa_must_be_enabled' => '管理员要求您的账户必须开启两步验证才能使用此面板.',
];

View File

@ -0,0 +1,88 @@
<?php
return [
'validation_error' => '请求中有一个或多个字段出错',
'errors' => [
'return' => '返回上一个页面',
'home' => '返回主页',
'403' => [
'header' => '禁止访问',
'desc' => '您没有权限访问此服务器上的资源.',
],
'404' => [
'header' => 'Not Found',
'desc' => '未找到资源.',
],
'installing' => [
'header' => '服务器正在安装',
'desc' => '请求的服务器仍然在部署中,请稍等几分钟,完成后您将收到一封电子邮件',
],
'suspended' => [
'header' => '服务器已暂停',
'desc' => '此服务器已被暂停,无法访问,请联系管理员',
],
'maintenance' => [
'header' => '节点维护中',
'title' => '暂时不可用',
'desc' => '此节点正在维护,当前无法访问.',
],
],
'index' => [
'header' => '您的服务器',
'header_sub' => '您当前可访问的服务器.',
'list' => '服务器列表',
],
'api' => [
'index' => [
'list' => '您的密钥',
'header' => '账户 API',
'header_sub' => '管理访问密钥允许您使用API操作面板.',
'create_new' => '新建 API 密钥',
'keypair_created' => '新建API密钥成功.',
],
'new' => [
'header' => '新建 API 密钥',
'header_sub' => '创建一个新的账户API密钥.',
'form_title' => '选项',
'descriptive_memo' => [
'title' => '描述',
'description' => '添加一个关于此密钥的描述.',
],
'allowed_ips' => [
'title' => '允许的IP',
'description' => '添加IP地址限制来保护API安全. CIDR 标记是被允许的. 留空将允许所有IP.',
],
],
],
'account' => [
'details_updated' => '您账户的信息成功更新.',
'invalid_password' => '您提供的密码不正确.',
'header' => '您的账户',
'header_sub' => '管理您的账户信息.',
'update_pass' => '修改密码',
'update_email' => '修改 Email 地址',
'current_password' => '当前密码',
'new_password' => '新密码',
'new_password_again' => '重复密码',
'new_email' => '新 Email 地址',
'first_name' => '姓',
'last_name' => '名',
'update_identity' => '更新个人信息',
'username_help' => '您的用户名必须唯一(未被使用),并满足以下要求: :requirements.',
],
'security' => [
'session_mgmt_disabled' => '为了安全原因,您的此次会话无法访问用户管理.',
'header' => '账户安全',
'header_sub' => '管理活动会话和两步认证.',
'sessions' => '活动中的会话',
'2fa_header' => '两步验证',
'2fa_token_help' => '填入您两步验证生成器生成的密码 (Google Authenticator, Authy, etc.).',
'disable_2fa' => '关闭两步验证',
'2fa_enabled' => '两步验证已开启,在您登陆面板时会要求两步验证.如果您想关闭两步验证,只需输入两步验证的密码即可',
'2fa_disabled' => '两步验证已关闭! 您应该开启两步验证将其作为您账户的额外防护',
'enable_2fa' => '开启两步验证',
'2fa_qr' => '在您的设备上上配置两步验证',
'2fa_checkpoint_help' => '使用两步验证需要用您的应用扫左侧二维码, 或手动输入下方的代码.完成后请将生成的密码输入下方方框.',
'2fa_disable_error' => '两步验证密码错误. 关闭两步验证失败.',
],
];

View File

@ -0,0 +1,97 @@
<?php
return [
'key' => [
'warning' => '貌似您已经拥有一个应用加密密钥了. 继续操作会导致之前的密钥被覆盖,所有的加密文件都将损坏。 !!!危险操作,请注意文件安全!!!',
'confirm' => '我已了解此操作的后果,可以承受丢失文件的风险,请继续。',
'final_confirm' => '确定继续操作? 更改应用加密密钥 !!将会导致数据丢失!!.',
],
'location' => [
'no_location_found' => '可用区ID错误无发找到该可用区',
'ask_short' => '可用区ID',
'ask_long' => '可用区描述',
'created' => '成功创建可用区 (:name) 可用区ID :id.',
'deleted' => '成功删除指定的可用区。',
],
'user' => [
'search_users' => '输入用户名, UUID, 或 Email 地址',
'select_search_user' => '要删除的用户ID (键入 \'0\' 来重新搜索)',
'deleted' => '成功删除用户。',
'confirm_delete' => '确定要删除此用户吗',
'no_users_found' => '未找到指定的用户',
'multiple_found' => '找到多个用户, 无法删除用户,原因: --no-interaction 参数。',
'ask_admin' => '此用户是管理员吗?',
'ask_email' => '电子邮件地址',
'ask_username' => '用户名',
'ask_name_first' => '姓',
'ask_name_last' => '名',
'ask_password' => '密码',
'ask_password_tip' => '如果您想创建一个随机密码的用户,请重新执行指令(CTRL+C) 并输入 `--no-password` 参数.',
'ask_password_help' => '密码至少8位并包含一个字母和数字',
'2fa_help_text' => [
'此命令会关闭一个用户的两步验证(如果他打开了). 此命令应仅用于用户恢复或解锁(两步验证无法成功情况下)。',
'如果您不想这么做, 情书用 CTRL+C 退出此操作。',
],
'2fa_disabled' => '已成功禁用以下账户的两步验证: :email.',
],
'schedule' => [
'output_line' => '第一次任务已计划于 `:schedule` (:hash).',
],
'maintenance' => [
'deleting_service_backup' => '正在删除服务备份文件 :file.',
],
'server' => [
'rebuild_failed' => '重构操作 ":name" (#:id) ,位于节点 ":node" 失败,错误信息: :message',
'power' => [
'confirm' => '您即将执行 :action 在 :count 个服务器. 是否继续?',
'action_failed' => '电源命令 ":name" (#:id) 位于节点 ":node" 失败,错误信息: :message',
],
],
'environment' => [
'mail' => [
'ask_smtp_host' => 'SMTP 主机 (e.g. smtp.gmail.com)',
'ask_smtp_port' => 'SMTP 端口',
'ask_smtp_username' => 'SMTP 用户名',
'ask_smtp_password' => 'SMTP 密码',
'ask_mailgun_domain' => 'Mailgun 域名',
'ask_mailgun_secret' => 'Mailgun 密钥',
'ask_mandrill_secret' => 'Mandrill 密钥',
'ask_postmark_username' => 'Postmark API 密钥',
'ask_driver' => '哪个引擎应该用于发送邮件?',
'ask_mail_from' => 'Email来自哪个邮箱',
'ask_mail_name' => 'Email应该由谁发送发送者姓名',
'ask_encryption' => '应该使用的加密方法',
],
'database' => [
'host_warning' => '极度推荐不使用localhost作为主机地址可能有bug. 如果确实要使用本机作为MySQL地址请使用 "127.0.0.1".',
'host' => '数据库主机',
'port' => '数据库端口',
'database' => '数据库名',
'username_warning' => '使用 "root" 账户会导致安全漏洞, 翼龙面板不允许此账户作为面板数据库账户. 你应该为此程序创建MySQL庄户',
'username' => '数据库用户名',
'password_defined' => '您似乎已经指定了MySQL连接密码你想更改它吗',
'password' => '数据库密码',
'connection_error' => '无法连接数据库. 返回错误: ":error".',
'creds_not_saved' => '您的数据库访问信息未保存. 在继续之前您将需要提供有效的信息',
'try_again' => '返回再试一次?',
],
'app' => [
'settings' => '启用基于UI的设置编辑器?',
'author' => '管理模板作者Email',
'author_help' => '提供此面板到处的管理模板作者的电子邮件地址. 这应该是一个合法的电子邮件地址',
'app_url_help' => '这个应用的URL必须以 https:// or http:// 开头取决于是否启用SSL. 如果不包含这些您的电子邮件地址和其他内容可能会指向错误的地址.',
'app_url' => '应用 URL',
'timezone_help' => '设置的时区应该满足PHP支持的时区. 如果您不确定,请参阅 http://php.net/manual/en/timezones.php.',
'timezone' => '应用时区',
'cache_driver' => 'Cache Driver',
'session_driver' => 'Session Driver',
'queue_driver' => 'Queue Driver',
'using_redis' => '如果您选择使用Redis, 请在下方提供有效的连接信息. 一般使用默认信息即可,除非您更改过设置.',
'redis_host' => 'Redis 主机',
'redis_password' => 'Redis 密码',
'redis_pass_help' => '默认情况下Redis数据库不需要密码且仅运行于本地. 这种情况下,您什么都不用填.',
'redis_port' => 'Redis 端口',
'redis_pass_defined' => '似乎您已经设置过Redis密码了您需要更改吗?',
],
],
];

View File

@ -0,0 +1,68 @@
<?php
return [
'daemon_connection_failed' => '连接受控端时发生意外 返回错误码 HTTP/:code response code. 此错误已被记录',
'node' => [
'servers_attached' => '节点删除必须按先移除其所有的服务器.',
'daemon_off_config_updated' => '受控端配置 <strong>已被更新</strong>, 但是自动更新受控端上的配置文件时发生错误. 你需要手动将配置文件 (core.json) 更新至受控端来完成更新.',
],
'allocations' => [
'server_using' => '一个服务器已分配该地址. 一个地址只有在无服务器使用时才能删除.',
'too_many_ports' => '一次添加1000个以上的端口是不被支持的.',
'invalid_mapping' => '提供的端口: :port 无效,无法继续操作.',
'cidr_out_of_range' => 'CIDR 标记 只允许掩码在 /25 到 /32之间。',
'port_out_of_range' => '端口超过范围,范围必须在 1024 到 65535 之间.',
],
'nest' => [
'delete_has_servers' => '活动服务器使用的管理模块不能被删除.',
'egg' => [
'delete_has_servers' => '活动服务器使用的管理模板不能被删除.',
'invalid_copy_id' => '管理模板复制的脚本ID无效.',
'must_be_child' => ' "复制设置自"选项指定的目标必须是管理模块的附属.',
'has_children' => '此管理模版附属有一个或多个管理模板. 在删除之前请先删除所有附属.',
],
'variables' => [
'env_not_unique' => '环境变量 :name 必须唯一.',
'reserved_name' => '环境变量 :name 是被保护的,无法指定为变量.',
'bad_validation_rule' => '环境变量规则 ":rule" 对于这个应用不是一个有效的规则.',
],
'importer' => [
'json_error' => '尝试导入JSON 文件时发生错误: :error.',
'file_error' => '提供的JSON文件不合法.',
'invalid_json_provided' => '提供的JSON文件格式不正确无法被解析。',
],
],
'packs' => [
'delete_has_servers' => '活动服务器使用的整合包不能被删除',
'update_has_servers' => '当前有服务器附属于包时无法修改关联选项的ID.',
'invalid_upload' => '上传的文件不合法.',
'invalid_mime' => '上传的文件不符合要求的文件类型 :type',
'unreadable' => '服务器无法打开该压缩包.',
'zip_extraction' => '解压时发生错误.',
'invalid_archive_exception' => '压缩包缺失archive.tar.gz 或 import.json 文件在根目录.',
],
'subusers' => [
'editing_self' => '编辑您自己的子用户时不被允许的.',
'user_is_owner' => '子用户无法添加服主.',
'subuser_exists' => '那个电子邮件的用户已经是此服务器的子用户了.',
],
'databases' => [
'delete_has_databases' => '无法删除一个拥有活跃数据库的数据库服务器.',
],
'tasks' => [
'chain_interval_too_long' => '链接任务的最大间隔时间为15分钟。',
],
'locations' => [
'has_nodes' => '活动节点附属的可用区无法被删除.',
],
'users' => [
'node_revocation_failed' => '吊销密钥失败 <a href=":link">节点 #:node</a>. :error',
],
'deployment' => [
'no_viable_nodes' => '没有合适的节点来自动部署服务器',
'no_viable_allocations' => '没有合适的地址来自动部署服务器',
],
'api' => [
'resource_not_found' => '需求的资源未找到.',
],
];

View File

@ -0,0 +1,32 @@
<?php
return [
'home' => '主页',
'account' => [
'header' => '账户管理',
'my_account' => '我的账户',
'security_controls' => '安全控制',
'api_access' => 'API',
'my_servers' => '我的服务器',
],
'server' => [
'header' => '服务器管理',
'console' => '控制台',
'console-pop' => '全屏控制台',
'file_management' => '文件管理',
'file_browser' => '文件浏览器',
'create_file' => '新建文件',
'upload_files' => '上传文件',
'subusers' => '子用户',
'schedules' => '计划任务',
'configuration' => '配置',
'port_allocations' => '地址设置',
'sftp_settings' => 'SFTP 设置',
'startup_parameters' => '启动参数',
'databases' => '数据库',
'edit_file' => '编辑文件',
'admin_header' => '管理',
'admin' => '服务器配置',
'server_name' => '服务器名',
],
];

View File

@ -0,0 +1,17 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Pagination Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used by the paginator library to build
| the simple pagination links. You are free to change them to anything
| you want to customize your views to better match your application.
|
*/
'previous' => '&laquo; 上一步',
'next' => '下一步 &raquo;',
];

View File

@ -0,0 +1,19 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Password Reset Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are the default lines which match reasons
| that are given by the password broker for a password update attempt
| has failed, such as for an invalid token or invalid new password.
|
*/
'password' => '密码至少六位数,并且两次输入的密码一致.',
'reset' => '您的密码已重设!',
'sent' => '我们已发送密码重设电子邮件!',
'token' => '此密码重设连接的令牌已过期.',
'user' => '无法找到此Email的用户.',
];

View File

@ -0,0 +1,330 @@
<?php
return [
'index' => [
'title' => '服务器状态 :name',
'header' => '服务器控制台',
'header_sub' => '实时控制您的服务器.',
],
'schedule' => [
'header' => '计划任务',
'header_sub' => '在一处,轻松管理服务器任务.',
'current' => '当前计划',
'new' => [
'header' => '创建新任务',
'header_sub' => '创建一个新的定时任务.',
'submit' => '创建任务',
],
'manage' => [
'header' => '管理任务',
'submit' => '修改任务',
'delete' => '删除任务',
],
'task' => [
'time' => '在。。。之后',
'action' => '执行操作',
'payload' => '任务内容',
'add_more' => '添加另一个任务',
],
'actions' => [
'command' => '发送命令',
'power' => '电源命令',
],
'toggle' => '更改状态',
'run_now' => '触发任务(现在)',
'schedule_created' => '成功在服务器上创建一个计划任务.',
'schedule_updated' => '任务已被更新.',
'unnamed' => '未命名任务',
'setup' => '任务创建',
'day_of_week' => '星期',
'day_of_month' => '日',
'hour' => '小时',
'minute' => '分钟',
'time_help' => '任务系统在定义任务应该何时开始运行时支持使用Cronjob语法。 使用上面的字段指定何时应开始运行这些任务,或从多个选择菜单中选择选项。',
'task_help' => '任务的时间与先前定义的任务相关。 每个计划任务可能分配的任务不超过5个任务可能不会超过15分钟的时间安排。',
],
'tasks' => [
'task_created' => '成功在面板上创建一个新任务',
'task_updated' => '任务成功被更新. 列表中的所有任务会被取消,并在下一次设定的时间运行.',
'header' => '计划的任务',
'header_sub' => '自动化你的服务器.',
'current' => '当前计划的任务',
'actions' => [
'command' => '发送命令',
'power' => '发送电源命令',
],
'new_task' => '添加新任务',
'toggle' => '更改状态',
'new' => [
'header' => '新任务',
'header_sub' => '为这个服务器创建一个新任务。',
'task_name' => '任务名',
'day_of_week' => '星期',
'custom' => '自定义',
'day_of_month' => '日',
'hour' => '小时',
'minute' => '分',
'sun' => '周日',
'mon' => '周一',
'tues' => '周二',
'wed' => '周三',
'thurs' => '周四',
'fri' => '周五',
'sat' => '周六',
'submit' => '创建任务',
'type' => '任务类型',
'chain_then' => '然后, 之后',
'chain_do' => '执行',
'chain_arguments' => '参数',
'payload' => '任务内容',
'payload_help' => '例如, 如果你选择 <code>发送命令</code> ,就在此处填写要发送的命令. 如果您选择 <code>发送电源命令</code> 在这里填入电源命令 (e.g. <code>restart</code>).',
],
'edit' => [
'header' => '任务管理',
'submit' => '更新任务',
],
],
'users' => [
'header' => '用户管理',
'header_sub' => '控制访问你服务器的用户.',
'configure' => '配置权限',
'list' => '有权限的用户列表',
'add' => '添加一个新的子用户',
'update' => '修改子用户',
'user_assigned' => '成功连接到一个子用户连接到服务器.',
'user_updated' => '成功更新权限.',
'edit' => [
'header' => '编辑子用户',
'header_sub' => '管理用户在此服务器的访问权限.',
],
'new' => [
'header' => '添加新用户',
'header_sub' => '添加一个允许访问此服务器的用户.',
'email' => 'Email 地址',
'email_help' => '填入你希望邀请的协助您管理服务器的人的Email地址.',
'power_header' => '电源管理',
'file_header' => '文件管理',
'subuser_header' => '子用户管理',
'server_header' => '服务器管理',
'task_header' => '计划任务管理',
'database_header' => '数据库管理',
'power_start' => [
'title' => '启动服务器',
'description' => '允许该用户启动服务器.',
],
'power_stop' => [
'title' => '停止服务器',
'description' => '允许该用户停止服务器.',
],
'power_restart' => [
'title' => '重新启动服务器',
'description' => '允许该用户重新启动服务器',
],
'power_kill' => [
'title' => '强制结束服务器',
'description' => '允许该用户强行关闭服务器',
],
'send_command' => [
'title' => '发送控制台命令',
'description' => '允许用户发送控制台. 如果用户没有"停止服务器"权限那么他无法使用stop命令',
],
'access_sftp' => [
'title' => 'SFTP 权限',
'description' => '允许用户连接到受控端提供的SFTP服务器.',
],
'list_files' => [
'title' => '列出文件',
'description' => '允许用户列出所有文件及文件夹列表,但是无权访问文件.',
],
'edit_files' => [
'title' => '编辑文件',
'description' => '允许用户访问文件内容(但更改后无法保存). SFTP 不受此权限影响.',
],
'save_files' => [
'title' => 'Save Files',
'description' => '允许用户保存文件(和编辑文件权限联动). SFTP 不受此权限影响.',
],
'move_files' => [
'title' => '重命名和移动文件',
'description' => '允许用户在文件系统中重命名和移动文件及文件夹.',
],
'copy_files' => [
'title' => '复制文件',
'description' => '允许用户在文件系统中复制文件及文件夹.',
],
'compress_files' => [
'title' => '压缩文件',
'description' => '允许用户在文件系统中压缩文件及文件夹',
],
'decompress_files' => [
'title' => '解压文件',
'description' => '允许用户解压 .zip 和 .tar(.gz) 压缩文件.',
],
'create_files' => [
'title' => '创建文件',
'description' => '允许用户通过面板创建文件.',
],
'upload_files' => [
'title' => '上传文件',
'description' => '允许用户通过文件管理上传文件.',
],
'delete_files' => [
'title' => '删除文件',
'description' => '允许用户在文件系统中删除文件.',
],
'download_files' => [
'title' => '下载文件s',
'description' => '允许用户下载文件. 如果为用户分配该权限,那么他将自动拥有下载和查看文件内容的权限.',
],
'list_subusers' => [
'title' => '列出子用户',
'description' => '允许用户访问此服务器的子用户列表.',
],
'view_subuser' => [
'title' => '访问子用户',
'description' => '允许用户查看子用户的权限.',
],
'edit_subuser' => [
'title' => '编辑子用户',
'description' => '允许用户编辑此服务器上的子用户权限.',
],
'create_subuser' => [
'title' => '创建子用户',
'description' => '允许用户在此服务器上添加子用户.',
],
'delete_subuser' => [
'title' => '删除子用户',
'description' => '允许用户删除此服务器上的子用户.',
],
'view_allocations' => [
'title' => '访问分配表',
'description' => '允许用户访问所有分配到此服务器上的IP和端口列表.',
],
'edit_allocation' => [
'title' => '编辑默认连接',
'description' => '允许用户更改连接到此服务器的默认连接地址.',
],
'view_startup' => [
'title' => '访问启动参数',
'description' => '允许用户访问服务器的启动参数和变量.',
],
'edit_startup' => [
'title' => '编辑启动参数',
'description' => '允许用户更改服务器的启动参数和变量.',
],
'list_schedules' => [
'title' => '列出计划任务',
'description' => '允许用户列出服务器上的所有计划任务 (无论是否启用) .',
],
'view_schedule' => [
'title' => '访问计划任务',
'description' => '允许用户查看一个计划任务的具体信息,包括其执行的时间和命令.',
],
'toggle_schedule' => [
'title' => '开关计划任务',
'description' => '允许用户更改计划任务的启用或禁用状态.',
],
'queue_schedule' => [
'title' => '队列化计划任务',
'description' => '允许用户将一个计划任务队列,以便在下一个周期执行.',
],
'edit_schedule' => [
'title' => '编辑计划任务',
'description' => '允许用户编辑计划任务. 此权限允许用户删除所有的执行任务,但无法删除计划任务本身.',
],
'create_schedule' => [
'title' => '创建计划任务',
'description' => '允许用户创建一个计划任务.',
],
'delete_schedule' => [
'title' => '删除计划任务',
'description' => '允许用户从服务器删除一个计划任务.',
],
'view_databases' => [
'title' => '访问数据库信息',
'description' => '允许用户访问附属于此服务器的数据库信息,包含数据库的地址,用户名和密码',
],
'reset_db_password' => [
'title' => '重设数据库',
'description' => '允许用户重新设置服务器数据库的密码.',
],
'delete_database' => [
'title' => '删除数据库',
'description' => '允许用户从面板删除此服务器的数据库.',
],
'create_database' => [
'title' => '新建数据库',
'description' => '允许用户为这个服务器创建一个数据库.',
],
],
],
'files' => [
'exceptions' => [
'invalid_mime' => '这种类型的文件无法使用面板内建编辑器编辑.',
'max_size' => '此文件太大,无法使用面板内建编辑器编辑.',
],
'header' => '文件管理',
'header_sub' => '从网页直接管理您所有的文件.',
'loading' => '正在加载初始文件结构,这可能需要几秒钟.',
'path' => '当你在配置任何插件或服务器设置的文件路径时 :path 应该为您的根目录. 此节点的网页上传最大文件限制为 :size.',
'seconds_ago' => '几秒之前',
'file_name' => '文件名',
'size' => '大小',
'last_modified' => '最后修改',
'add_new' => '新建文件',
'add_folder' => '新建文件夹',
'mass_actions' => '更多操作',
'delete' => '删除文件',
'edit' => [
'header' => '编辑文件',
'header_sub' => '从网页更改一个文件.',
'save' => '保存文件',
'return' => '返回文件管理',
],
'add' => [
'header' => '新建文件',
'header_sub' => '在您服务器上创建一个新文件.',
'name' => '文件名',
'create' => '创建文件',
],
],
'config' => [
'name' => [
'header' => '服务器名',
'header_sub' => '更改您服务器的名称。',
'details' => '此服务器名只是为了让你更好的管理服务器,并不会对服务器内的玩家有所影响.',
],
'startup' => [
'header' => '启动配置',
'header_sub' => '控制服务器的启动参数.',
'command' => '启动命令',
'edit_params' => '编辑参数',
'update' => '更新启动参数',
'startup_regex' => '输入规则',
'edited' => '启动参数已成功更新. 更新的内容会在下一次启动时生效.',
],
'sftp' => [
'header' => 'SFTP 配置',
'header_sub' => 'SFTP 连接所需要的信息.',
'details' => 'SFTP 信息',
'conn_addr' => '连接地址',
'warning' => 'SFTP密码就是您的用户密码. 请确认你使用的时SFTP不是FTP,也不是FTPS, 这些都是不同的协议.',
],
'database' => [
'header' => '数据库',
'header_sub' => '此服务器可用的数据库.',
'your_dbs' => '已配置的数据库',
'host' => 'MySQL 主机',
'reset_password' => '重设密码',
'no_dbs' => '没有此服务器可用的数据库.',
'add_db' => '创建一个新数据库.',
],
'allocation' => [
'header' => '服务器连接信息',
'header_sub' => '控制此服务器可用的IP和端口.',
'available' => '可用的连接信息',
'help' => '连接信息版主',
'help_text' => '左边列出的所有IP和端口都是开放的是您连接到您服务器的地址',
],
],
];

View File

@ -0,0 +1,88 @@
<?php
return [
'email' => 'Email',
'user_identifier' => '用户名 或 Email',
'password' => '密码',
'confirm_password' => '确认密码',
'login' => '登陆',
'home' => '主页',
'servers' => '服务器',
'id' => 'ID',
'name' => '名称',
'node' => '节点',
'connection' => '连接',
'memory' => '内存',
'cpu' => 'CPU',
'status' => '状态',
'search' => '搜索',
'suspended' => '已暂停',
'account' => '用户',
'security' => '安全',
'ip' => 'IP 地址',
'last_activity' => '上次活动',
'revoke' => '吊销',
'2fa_token' => '认证密钥',
'submit' => '确认',
'close' => '关闭',
'settings' => '设置',
'configuration' => '配置',
'sftp' => 'SFTP',
'databases' => '数据库',
'memo' => 'Memo',
'created' => '已创建',
'expires' => '过期',
'public_key' => '令牌',
'api_access' => 'Api 访问',
'never' => '从未',
'sign_out' => '登出',
'admin_control' => '管理员面板',
'required' => '需要',
'port' => '端口',
'username' => '用户名',
'database' => '数据库',
'new' => '新',
'danger' => '危险',
'create' => '创建',
'select_all' => '全选',
'select_none' => '反选',
'alias' => '别名',
'primary' => '主要',
'make_primary' => '设置为主要',
'none' => '无',
'cancel' => '取消',
'created_at' => '创建于',
'action' => '操作',
'data' => '数据',
'queued' => '队列',
'last_run' => '上次运行',
'next_run' => '下次运行',
'not_run_yet' => '从未允许',
'yes' => '是',
'no' => '否',
'delete' => '删除',
'2fa' => '两步验证',
'logout' => '登出',
'admin_cp' => '管理员控制面板',
'optional' => '可选的',
'read_only' => '只读',
'relation' => '关系',
'owner' => '所有者',
'admin' => '管理员',
'subuser' => '子用户',
'captcha_invalid' => '输入的验证码错误.',
'tasks' => '任务',
'seconds' => '秒',
'minutes' => '分',
'under_maintenance' => '维护中',
'days' => [
'sun' => '周日',
'mon' => '周一',
'tues' => '周二',
'wed' => '周三',
'thurs' => '周四',
'fri' => '周五',
'sat' => '周六',
],
'last_used' => '上次使用',
];

View File

@ -0,0 +1,105 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Validation Language Lines
|--------------------------------------------------------------------------
|
| The following language lines contain the default error messages used by
| the validator class. Some of these rules have multiple versions such
| as the size rules. Feel free to tweak each of these messages here.
|
*/
'accepted' => ' :attribute 被接受.',
'active_url' => ' :attribute 不是一个有效的URL.',
'after' => ' :attribute 必须是一个位于 :date 之后的日期.',
'after_or_equal' => ' :attribute 必须是 :date 之后或同样的日期.',
'alpha' => ' :attribute 只能含有字母.',
'alpha_dash' => ':attribute 只能含有数字字母和分隔线.',
'alpha_num' => ' :attribute 只能含有数字和字母.',
'array' => ' :attribute 必须是个数组.',
'before' => ' :attribute 必须是一个位于 :date 之前的日前.',
'before_or_equal' => ' :attribute 必须是 :date 之前或同样的日期.',
'between' => [
'numeric' => ' :attribute 必须在 :min 到 :max 之间.',
'file' => ' :attribute 必须在 :min 到 :max KB 之间.',
'string' => ' :attribute m必须在 :min 到 :max 个字符之间.',
'array' => ' :attribute 必须在 :min 到 :max 个项目之间.',
],
'boolean' => ' :attribute 填入的必须为 true 或 false.',
'confirmed' => ' :attribute 确认不匹配.',
'date' => ' :attribute 不是一个合法的日期.',
'date_format' => ' :attribute 不是正确的格式: :format.',
'different' => ' :attribute 和 :other 必须不同.',
'digits' => ' :attribute 必须为 :digits 个数字.',
'digits_between' => ' :attribute 必须在 :min 到 :max 个数字间.',
'dimensions' => ' :attribute 有一个非法的镜像大小.',
'distinct' => ' :attribute 填入了一个重复的值.',
'email' => ' :attribute 必须是一个合法的Email地址.',
'exists' => '所选择的 :attribute 无效.',
'file' => ' :attribute 必须为一个文件.',
'filled' => ' :attribute 为必填项目.',
'image' => ' :attribute 必须是一个镜像.',
'in' => '所选择的 :attribute 无效.',
'in_array' => ' :attribute 填入的信息在 :other 不存在.',
'integer' => ' :attribute 必须是一个整数.',
'ip' => ' :attribute 必须是一个合法的IP地址.',
'json' => ' :attribute 必须是一个合法的JSON字符串.',
'max' => [
'numeric' => ' :attribute 不能大于 :max.',
'file' => ' :attribute 不能大于 :max KB.',
'string' => ' :attribute 不能多于 :max 个字符.',
'array' => ' :attribute 不能多于 :max 个项目.',
],
'mimes' => ' :attribute 文件类型必须为: :values.',
'mimetypes' => ' :attribute 文件类型必须为: :values.',
'min' => [
'numeric' => ' :attribute 至少应在 :min.',
'file' => ' :attribute 至少应在 :min KB.',
'string' => ' :attribute 至少应在 :min 个字符.',
'array' => ' :attribute 至少应有 :min 个项目.',
],
'not_in' => '所选择的 :attribute 不正确.',
'numeric' => ' :attribute 必须是个数字.',
'present' => ' :attribute 填入的必须存在.',
'regex' => ' :attribute 格式不正确.',
'required' => ' :attribute 为必填.',
'required_if' => ' :attribute 被要求填入, 当 :other 为 :value 的时候.',
'required_unless' => ' :attribute 被要求填入,除非 :other 为 :values.',
'required_with' => ' :attribute 被要求填入,当 :values 存在的时候.',
'required_with_all' => ' :attribute 被要求填入,当 :values 存在.',
'required_without' => ' :attribute 被要求填入,当 :values 不存在.',
'required_without_all' => ' :attribute 被要求填入,当 :values 都不存在.',
'same' => ' :attribute 和 :other 必须相同.',
'size' => [
'numeric' => ' :attribute 必须为 :size.',
'file' => ' :attribute 必须为 :size KB.',
'string' => ' :attribute 必须为 :size 个字符.',
'array' => ' :attribute 必须包含 :size 个项目.',
],
'string' => ' :attribute 必须为字符串.',
'timezone' => ' :attribute 必须是一个有效的时区.',
'unique' => ' :attribute 已经被使用.',
'uploaded' => ' :attribute 上传失败.',
'url' => ' :attribute 格式不合法.',
/*
|--------------------------------------------------------------------------
| Custom Validation Attributes
|--------------------------------------------------------------------------
|
| The following language lines are used to swap attribute place-holders
| with something more reader friendly such as E-Mail Address instead
| of "email". This simply helps us make messages a little cleaner.
|
*/
'attributes' => [],
// Internal validation logic for Pterodactyl
'internal' => [
'variable_value' => ':env variable',
],
];

View File

@ -40,7 +40,7 @@ return [
],
'environment' => [
'mail' => [
'ask_smtp_host' => 'SMTP Host (e.g. smtp.google.com)',
'ask_smtp_host' => 'SMTP Host (e.g. smtp.gmail.com)',
'ask_smtp_port' => 'SMTP Port',
'ask_smtp_username' => 'SMTP Benutzername',
'ask_smtp_password' => 'SMTP Password',

View File

@ -48,7 +48,7 @@ return [
'select_none' => 'Alles abwählen',
'alias' => 'Alias',
'primary' => 'Primär',
'make_primary' => 'Primät machen',
'make_primary' => 'Primär machen',
'none' => 'Nichts',
'cancel' => 'Abbrechen',
'created_at' => 'Erstellt am',

View File

@ -49,7 +49,7 @@ return [
],
'environment' => [
'mail' => [
'ask_smtp_host' => 'SMTP Host (e.g. smtp.google.com)',
'ask_smtp_host' => 'SMTP Host (e.g. smtp.gmail.com)',
'ask_smtp_port' => 'SMTP Port',
'ask_smtp_username' => 'SMTP Username',
'ask_smtp_password' => 'SMTP Password',

View File

@ -47,7 +47,7 @@ return [
],
'environment' => [
'mail' => [
'ask_smtp_host' => 'Host SMTP (e.g. smtp.google.com)',
'ask_smtp_host' => 'Host SMTP (e.g. smtp.gmail.com)',
'ask_smtp_port' => 'Puerto SMTP',
'ask_smtp_username' => 'El nombre de Usuario SMTP',
'ask_smtp_password' => 'Contraseña SMTP',

View File

@ -5,7 +5,7 @@
@endsection
@section('content-header')
<h1>Application API<small>Control access credentials for manging this Panel via the API.</small></h1>
<h1>Application API<small>Control access credentials for managing this Panel via the API.</small></h1>
<ol class="breadcrumb">
<li><a href="{{ route('admin.index') }}">Admin</a></li>
<li class="active">Application API</li>

View File

@ -57,7 +57,7 @@
<td><a href="{{ route('admin.servers.view', $server->id) }}">{{ $server->name }}</a></td>
<td><a href="{{ route('admin.users.view', $server->owner_id) }}">{{ $server->user->username }}</a></td>
<td>{{ $server->nest->name }} ({{ $server->egg->name }})</td>
<td class="text-center"><span data-action="memory">NaN</span> / {{ $server->memory === 0 ? '&infin;' : $server->memory }} MB</td>
<td class="text-center"><span data-action="memory">NaN</span> / {{ $server->memory === 0 ? '' : $server->memory }} MB</td>
<td class="text-center">{{ $server->disk }} MB</td>
<td class="text-center"><span data-action="cpu" data-cpumax="{{ $server->cpu }}">NaN</span> %</td>
<td class="text-center" data-action="status">NaN</td>

View File

@ -62,7 +62,7 @@
<h3 class="box-title">Force Delete Server</h3>
</div>
<div class="box-body">
<p>This action will attempt to delete the server from both the panel and daemon. The the daemon does not respond, or reports an error the deletion will continue.</p>
<p>This action will attempt to delete the server from both the panel and daemon. If the daemon does not respond, or reports an error the deletion will continue.</p>
<p class="text-danger small">Deleting a server is an irreversible action. <strong>All server data</strong> (including files and users) will be removed from the system. This method may leave dangling files on your daemon if it reports an error.</p>
</div>
<div class="box-footer">

View File

@ -23,10 +23,10 @@
<div class="box">
<div class="box-header">
<h3 class="box-title">@lang('base.index.list')</h3>
<div class="box-tools">
<div class="box-tools search01">
<form action="{{ route('index') }}" method="GET">
<div class="input-group input-group-sm">
<input type="text" name="query" class="form-control pull-right" style="width:30%;" value="{{ request()->input('query') }}" placeholder="@lang('strings.search')">
<input type="text" name="query" class="form-control pull-right" value="{{ request()->input('query') }}" placeholder="@lang('strings.search')">
<div class="input-group-btn">
<button type="submit" class="btn btn-default"><i class="fa fa-search"></i></button>
</div>
@ -53,7 +53,7 @@
<td><a href="{{ route('server.index', $server->uuidShort) }}">{{ $server->name }}</a></td>
<td>{{ $server->getRelation('node')->name }}</td>
<td><code>{{ $server->getRelation('allocation')->alias }}:{{ $server->getRelation('allocation')->port }}</code></td>
<td class="text-center hidden-sm hidden-xs"><span data-action="memory">--</span> / {{ $server->memory === 0 ? '&infin;' : $server->memory }} MB</td>
<td class="text-center hidden-sm hidden-xs"><span data-action="memory">--</span> / {{ $server->memory === 0 ? '' : $server->memory }} MB</td>
<td class="text-center hidden-sm hidden-xs"><span data-action="cpu" data-cpumax="{{ $server->cpu }}">--</span> %</td>
<td class="text-center">
@if($server->user->id === Auth::user()->id)

View File

@ -146,7 +146,7 @@
</a>
</li>
@endcan
@can('list-tasks', $server)
@can('list-schedules', $server)
<li
@if(starts_with(Route::currentRouteName(), 'server.schedules'))
class="active"

View File

@ -87,7 +87,7 @@
@if($overLimit)
<div class="box-body">
<div class="alert alert-danger no-margin">
You are currently using <strong>{{ count($databases) }}</strong> of your <strong>{{ $server->database_limit ?? '&infin;' }}</strong> allowed databases.
You are currently using <strong>{{ count($databases) }}</strong> of your <strong>{{ $server->database_limit ?? '' }}</strong> allowed databases.
</div>
</div>
@else

View File

@ -44,7 +44,7 @@
{!! Theme::js('vendor/lodash/lodash.js') !!}
{!! Theme::js('vendor/siofu/client.min.js') !!}
@if(App::environment('production'))
{!! Theme::js('js/frontend/files/filemanager.min.js') !!}
{!! Theme::js('js/frontend/files/filemanager.min.js?updated-cancel-buttons') !!}
@else
{!! Theme::js('js/frontend/files/src/index.js') !!}
{!! Theme::js('js/frontend/files/src/contextmenu.js') !!}

View File

@ -10,7 +10,7 @@
namespace Tests\Unit\Commands\Schedule;
use Mockery as m;
use Carbon\Carbon;
use Cake\Chronos\Chronos;
use Pterodactyl\Models\Task;
use Pterodactyl\Models\Schedule;
use Tests\Unit\Commands\CommandTestCase;
@ -20,11 +20,6 @@ use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface;
class ProcessRunnableCommandTest extends CommandTestCase
{
/**
* @var \Carbon\Carbon
*/
protected $carbon;
/**
* @var \Pterodactyl\Console\Commands\Schedule\ProcessRunnableCommand
*/
@ -47,11 +42,12 @@ class ProcessRunnableCommandTest extends CommandTestCase
{
parent::setUp();
$this->carbon = m::mock(Carbon::class);
Chronos::setTestNow(Chronos::now());
$this->processScheduleService = m::mock(ProcessScheduleService::class);
$this->repository = m::mock(ScheduleRepositoryInterface::class);
$this->command = new ProcessRunnableCommand($this->carbon, $this->processScheduleService, $this->repository);
$this->command = new ProcessRunnableCommand($this->processScheduleService, $this->repository);
}
/**
@ -62,9 +58,7 @@ class ProcessRunnableCommandTest extends CommandTestCase
$schedule = factory(Schedule::class)->make();
$schedule->tasks = collect([factory(Task::class)->make()]);
$this->carbon->shouldReceive('now')->withNoArgs()->once()->andReturnSelf()
->shouldReceive('toAtomString')->withNoArgs()->once()->andReturn('00:00:00');
$this->repository->shouldReceive('getSchedulesToProcess')->with('00:00:00')->once()->andReturn(collect([$schedule]));
$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);
@ -84,9 +78,7 @@ class ProcessRunnableCommandTest extends CommandTestCase
$schedule = factory(Schedule::class)->make();
$schedule->tasks = collect([]);
$this->carbon->shouldReceive('now')->withNoArgs()->once()->andReturnSelf()
->shouldReceive('toAtomString')->withNoArgs()->once()->andReturn('00:00:00');
$this->repository->shouldReceive('getSchedulesToProcess')->with('00:00:00')->once()->andReturn(collect([$schedule]));
$this->repository->shouldReceive('getSchedulesToProcess')->with(Chronos::now()->toAtomString())->once()->andReturn(collect([$schedule]));
$display = $this->runCommand($this->command);
@ -104,9 +96,7 @@ class ProcessRunnableCommandTest extends CommandTestCase
{
$schedule = factory(Schedule::class)->make(['tasks' => null]);
$this->carbon->shouldReceive('now')->withNoArgs()->once()->andReturnSelf()
->shouldReceive('toAtomString')->withNoArgs()->once()->andReturn('00:00:00');
$this->repository->shouldReceive('getSchedulesToProcess')->with('00:00:00')->once()->andReturn(collect([$schedule]));
$this->repository->shouldReceive('getSchedulesToProcess')->with(Chronos::now()->toAtomString())->once()->andReturn(collect([$schedule]));
$display = $this->runCommand($this->command);

View File

@ -3,24 +3,22 @@
* Created by PhpStorm.
* User: Stan
* Date: 26-5-2018
* Time: 21:06
* Time: 21:06.
*/
namespace Tests\Unit\Http\Controllers\Admin;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Routing\Controller;
use Mockery as m;
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
use Pterodactyl\Contracts\Repository\NodeRepositoryInterface;
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
use Pterodactyl\Http\Controllers\Admin\StatisticsController;
use Pterodactyl\Models\Node;
use Tests\Assertions\ControllerAssertionsTrait;
use Tests\Unit\Http\Controllers\ControllerTestCase;
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
use Pterodactyl\Http\Controllers\Admin\StatisticsController;
use Pterodactyl\Contracts\Repository\NodeRepositoryInterface;
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
class StatisticsControllerTest extends ControllerTestCase
{
@ -88,7 +86,7 @@ class StatisticsControllerTest extends ControllerTestCase
'disk' => [
'value' => 1024,
'max' => 512,
]
],
]);
$controller->shouldReceive('injectJavascript')->once();
@ -106,8 +104,7 @@ class StatisticsControllerTest extends ControllerTestCase
$this->eggRepository,
$this->nodeRepository,
$this->serverRepository,
$this->userRepository]
$this->userRepository, ]
);
}
}
}

View File

@ -4,6 +4,8 @@ namespace Tests\Unit\Http\Controllers\Base;
use Mockery as m;
use Pterodactyl\Models\User;
use Illuminate\Auth\AuthManager;
use Illuminate\Auth\SessionGuard;
use Prologue\Alerts\AlertsMessageBag;
use Pterodactyl\Services\Users\UserUpdateService;
use Tests\Unit\Http\Controllers\ControllerTestCase;
@ -17,6 +19,16 @@ class AccountControllerTest extends ControllerTestCase
*/
protected $alert;
/**
* @var \Illuminate\Auth\AuthManager|\Mockery\Mock
*/
protected $authManager;
/**
* @var \Illuminate\Auth\SessionGuard|\Mockery\Mock
*/
protected $sessionGuard;
/**
* @var \Pterodactyl\Services\Users\UserUpdateService|\Mockery\Mock
*/
@ -31,6 +43,10 @@ class AccountControllerTest extends ControllerTestCase
$this->alert = m::mock(AlertsMessageBag::class);
$this->updateService = m::mock(UserUpdateService::class);
$this->authManager = m::mock(AuthManager::class);
$this->sessionGuard = m::mock(SessionGuard::class);
$this->authManager->shouldReceive('guard')->once()->andReturn($this->sessionGuard);
}
/**
@ -50,13 +66,11 @@ class AccountControllerTest extends ControllerTestCase
public function testUpdateControllerForPassword()
{
$this->setRequestMockClass(AccountDataFormRequest::class);
$user = $this->generateRequestUserModel();
$this->request->shouldReceive('input')->with('do_action')->andReturn('password');
$this->request->shouldReceive('input')->with('new_password')->once()->andReturn('test-password');
$this->sessionGuard->shouldReceive('logoutOtherDevices')->once()->with('test-password')->andReturnSelf();
$this->updateService->shouldReceive('setUserLevel')->with(User::USER_LEVEL_USER)->once()->andReturnNull();
$this->updateService->shouldReceive('handle')->with($user, ['password' => 'test-password'])->once()->andReturn(collect());
$this->alert->shouldReceive('success->flash')->once()->andReturnNull();
$response = $this->getController()->update($this->request);
@ -113,6 +127,6 @@ class AccountControllerTest extends ControllerTestCase
*/
private function getController(): AccountController
{
return new AccountController($this->alert, $this->updateService);
return new AccountController($this->alert, $this->authManager, $this->updateService);
}
}

View File

@ -182,7 +182,7 @@ class SubuserControllerTest extends ControllerTestCase
$response = $controller->store($this->request);
$this->assertIsRedirectResponse($response);
$this->assertRedirectRouteEquals('server.subusers.view', $response, [
'uuid' => $server->uuid,
'uuid' => $server->uuidShort,
'id' => $subuser->hashid,
]);
}

View File

@ -8,7 +8,7 @@ use Illuminate\Contracts\Session\Session;
use Illuminate\Contracts\Config\Repository;
use Illuminate\Contracts\Routing\ResponseFactory;
use Tests\Unit\Http\Middleware\MiddlewareTestCase;
use Pterodactyl\Http\Middleware\AccessingValidServer;
use Pterodactyl\Http\Middleware\Server\AccessingValidServer;
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
class AccessingValidServerTest extends MiddlewareTestCase

View File

@ -3,8 +3,8 @@
namespace Tests\Unit\Jobs\Schedule;
use Mockery as m;
use Carbon\Carbon;
use Tests\TestCase;
use Cake\Chronos\Chronos;
use Pterodactyl\Models\Task;
use Pterodactyl\Models\User;
use GuzzleHttp\Psr7\Response;
@ -58,7 +58,7 @@ class RunTaskJobTest extends TestCase
{
parent::setUp();
Bus::fake();
Carbon::setTestNow(Carbon::now());
Chronos::setTestNow(Chronos::now());
$this->commandRepository = m::mock(CommandRepositoryInterface::class);
$this->config = m::mock(Repository::class);
@ -94,7 +94,7 @@ class RunTaskJobTest extends TestCase
$this->scheduleRepository->shouldReceive('withoutFreshModel->update')->with($schedule->id, [
'is_processing' => false,
'last_run_at' => Carbon::now()->toDateTimeString(),
'last_run_at' => Chronos::now()->toDateTimeString(),
])->once()->andReturnNull();
$this->getJobInstance($task->id, $schedule->id);
@ -124,7 +124,7 @@ class RunTaskJobTest extends TestCase
$this->scheduleRepository->shouldReceive('withoutFreshModel->update')->with($schedule->id, [
'is_processing' => false,
'last_run_at' => Carbon::now()->toDateTimeString(),
'last_run_at' => Chronos::now()->toDateTimeString(),
])->once()->andReturnNull();
$this->getJobInstance($task->id, $schedule->id);
@ -202,7 +202,7 @@ class RunTaskJobTest extends TestCase
$this->scheduleRepository->shouldReceive('withoutFreshModel->update')->with($schedule->id, [
'is_processing' => false,
'last_run_at' => Carbon::now()->toDateTimeString(),
'last_run_at' => Chronos::now()->toDateTimeString(),
])->once()->andReturn(1);
$this->taskRepository->shouldReceive('update')->with($task->id, ['is_queued' => false])->once()->andReturn(1);

View File

@ -1,62 +0,0 @@
<?php
namespace Tests\Unit\Services\Helpers;
use Mockery as m;
use Tests\TestCase;
use Tests\Traits\MocksUuids;
use Illuminate\Contracts\Hashing\Hasher;
use Illuminate\Database\ConnectionInterface;
use Pterodactyl\Services\Helpers\TemporaryPasswordService;
class TemporaryPasswordServiceTest extends TestCase
{
use MocksUuids;
/**
* @var \Illuminate\Database\ConnectionInterface|\Mockery\Mock
*/
protected $connection;
/**
* @var \Illuminate\Contracts\Hashing\Hasher|\Mockery\Mock
*/
protected $hasher;
/**
* @var \Pterodactyl\Services\Helpers\TemporaryPasswordService
*/
protected $service;
/**
* Setup tests.
*/
public function setUp()
{
parent::setUp();
$this->connection = m::mock(ConnectionInterface::class);
$this->hasher = m::mock(Hasher::class);
$this->service = new TemporaryPasswordService($this->connection, $this->hasher);
}
/**
* Test that a temporary password is stored and the token is returned.
*/
public function testTemporaryPasswordIsStored()
{
$token = hash_hmac(TemporaryPasswordService::HMAC_ALGO, $this->getKnownUuid(), config('app.key'));
$this->hasher->shouldReceive('make')->with($token)->once()->andReturn('hashed_token');
$this->connection->shouldReceive('table')->with('password_resets')->once()->andReturnSelf();
$this->connection->shouldReceive('insert')->with([
'email' => 'test@example.com',
'token' => 'hashed_token',
])->once()->andReturnNull();
$response = $this->service->handle('test@example.com');
$this->assertNotEmpty($response);
$this->assertEquals($token, $response);
}
}

View File

@ -1,11 +1,4 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* This software is licensed under the terms of the MIT license.
* https://opensource.org/licenses/MIT
*/
namespace Tests\Unit\Services\Nodes;
@ -63,17 +56,17 @@ class NodeUpdateServiceTest extends TestCase
->expects($this->once())->willReturn('random_string');
$this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
$this->repository->shouldReceive('withoutFreshModel->update')->with($model->id, [
$this->repository->shouldReceive('update')->with($model->id, [
'name' => 'NewName',
'daemonSecret' => 'random_string',
])->andReturn(true);
])->andReturn($model);
$this->configRepository->shouldReceive('setNode')->with($model)->once()->andReturnSelf()
->shouldReceive('update')->withNoArgs()->once()->andReturn(new Response);
$this->connection->shouldReceive('commit')->withNoArgs()->once()->andReturnNull();
$response = $this->getService()->returnUpdatedModel(false)->handle($model, ['name' => 'NewName', 'reset_secret' => true]);
$this->assertTrue($response);
$response = $this->getService()->handle($model, ['name' => 'NewName', 'reset_secret' => true]);
$this->assertInstanceOf(Node::class, $response);
}
/**
@ -83,37 +76,17 @@ class NodeUpdateServiceTest extends TestCase
{
$model = factory(Node::class)->make();
$this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
$this->repository->shouldReceive('withoutFreshModel->update')->with($model->id, [
'name' => 'NewName',
])->andReturn(true);
$this->configRepository->shouldReceive('setNode')->with($model)->once()->andReturnSelf()
->shouldReceive('update')->withNoArgs()->once()->andReturn(new Response);
$this->connection->shouldReceive('commit')->withNoArgs()->once()->andReturnNull();
$response = $this->getService()->returnUpdatedModel(false)->handle($model, ['name' => 'NewName']);
$this->assertTrue($response);
}
public function testUpdatedModelIsReturned()
{
$model = factory(Node::class)->make();
$updated = clone $model;
$updated->name = 'NewName';
$this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
$this->repository->shouldReceive('update')->with($model->id, [
'name' => $updated->name,
])->andReturn($updated);
'name' => 'NewName',
])->andReturn($model);
$this->configRepository->shouldReceive('setNode')->with($model)->once()->andReturnSelf()
->shouldReceive('update')->withNoArgs()->once()->andReturn(new Response);
$this->connection->shouldReceive('commit')->withNoArgs()->once()->andReturnNull();
$response = $this->getService()->returnUpdatedModel()->handle($model, ['name' => $updated->name]);
$response = $this->getService()->handle($model, ['name' => 'NewName']);
$this->assertInstanceOf(Node::class, $response);
$this->assertSame($updated, $response);
}
/**
@ -127,7 +100,7 @@ class NodeUpdateServiceTest extends TestCase
$model = factory(Node::class)->make();
$this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
$this->repository->shouldReceive('withoutFreshModel->update')->andReturn(new Response);
$this->repository->shouldReceive('update')->andReturn($model);
$this->configRepository->shouldReceive('setNode->update')->once()->andThrow($this->getExceptionMock());
$this->connection->shouldReceive('commit')->withNoArgs()->once()->andReturnNull();
@ -146,7 +119,7 @@ class NodeUpdateServiceTest extends TestCase
$model = factory(Node::class)->make();
$this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
$this->repository->shouldReceive('withoutFreshModel->update')->andReturn(new Response);
$this->repository->shouldReceive('update')->andReturn($model);
$this->configRepository->shouldReceive('setNode->update')->once()->andThrow($this->getExceptionMock());

View File

@ -3,36 +3,32 @@
namespace Tests\Unit\Services\Schedules;
use Mockery as m;
use Carbon\Carbon;
use Tests\TestCase;
use Cron\CronExpression;
use Pterodactyl\Models\Task;
use Pterodactyl\Models\Schedule;
use Pterodactyl\Services\Schedules\Tasks\RunTaskService;
use Illuminate\Contracts\Bus\Dispatcher;
use Pterodactyl\Jobs\Schedule\RunTaskJob;
use Pterodactyl\Services\Schedules\ProcessScheduleService;
use Pterodactyl\Contracts\Repository\TaskRepositoryInterface;
use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface;
class ProcessScheduleServiceTest extends TestCase
{
/**
* @var \Cron\CronExpression|\Mockery\Mock
* @var \Illuminate\Contracts\Bus\Dispatcher|\Mockery\Mock
*/
protected $cron;
private $dispatcher;
/**
* @var \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface|\Mockery\Mock
*/
protected $repository;
private $scheduleRepository;
/**
* @var \Pterodactyl\Services\Schedules\Tasks\RunTaskService|\Mockery\Mock
* @var \Pterodactyl\Contracts\Repository\TaskRepositoryInterface|\Mockery\Mock
*/
protected $runnerService;
/**
* @var \Pterodactyl\Services\Schedules\ProcessScheduleService
*/
protected $service;
private $taskRepository;
/**
* Setup tests.
@ -40,12 +36,10 @@ class ProcessScheduleServiceTest extends TestCase
public function setUp()
{
parent::setUp();
Carbon::setTestNow(Carbon::now());
$this->repository = m::mock(ScheduleRepositoryInterface::class);
$this->runnerService = m::mock(RunTaskService::class);
$this->service = new ProcessScheduleService($this->runnerService, $this->repository);
$this->dispatcher = m::mock(Dispatcher::class);
$this->scheduleRepository = m::mock(ScheduleRepositoryInterface::class);
$this->taskRepository = m::mock(TaskRepositoryInterface::class);
}
/**
@ -58,37 +52,36 @@ class ProcessScheduleServiceTest extends TestCase
'sequence_id' => 1,
])]));
$this->repository->shouldReceive('loadTasks')->with($model)->once()->andReturn($model);
$this->scheduleRepository->shouldReceive('loadTasks')->with($model)->once()->andReturn($model);
$formatted = sprintf('%s %s %s * %s', $model->cron_minute, $model->cron_hour, $model->cron_day_of_month, $model->cron_day_of_week);
$this->repository->shouldReceive('update')->with($model->id, [
$this->scheduleRepository->shouldReceive('update')->with($model->id, [
'is_processing' => true,
'next_run_at' => CronExpression::factory($formatted)->getNextRunDate(),
]);
$this->runnerService->shouldReceive('handle')->with($task)->once()->andReturnNull();
$this->taskRepository->shouldReceive('update')->with($task->id, ['is_queued' => true])->once();
$this->service->handle($model);
$this->dispatcher->shouldReceive('dispatch')->with(m::on(function ($class) use ($model, $task) {
$this->assertInstanceOf(RunTaskJob::class, $class);
$this->assertSame($task->time_offset, $class->delay);
$this->assertSame($task->id, $class->task);
$this->assertSame($model->id, $class->schedule);
return true;
}))->once();
$this->getService()->handle($model);
$this->assertTrue(true);
}
public function testScheduleRunTimeCanBeOverridden()
/**
* Return an instance of the service for testing purposes.
*
* @return \Pterodactyl\Services\Schedules\ProcessScheduleService
*/
private function getService(): ProcessScheduleService
{
$model = factory(Schedule::class)->make();
$model->setRelation('tasks', collect([$task = factory(Task::class)->make([
'sequence_id' => 1,
])]));
$this->repository->shouldReceive('loadTasks')->with($model)->once()->andReturn($model);
$this->repository->shouldReceive('update')->with($model->id, [
'is_processing' => true,
'next_run_at' => Carbon::now()->addSeconds(15)->toDateTimeString(),
]);
$this->runnerService->shouldReceive('handle')->with($task)->once()->andReturnNull();
$this->service->setRunTimeOverride(Carbon::now()->addSeconds(15))->handle($model);
$this->assertTrue(true);
return new ProcessScheduleService($this->dispatcher, $this->scheduleRepository, $this->taskRepository);
}
}

View File

@ -1,90 +0,0 @@
<?php
/*
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* This software is licensed under the terms of the MIT license.
* https://opensource.org/licenses/MIT
*/
namespace Tests\Unit\Services\Schedules\Tasks;
use Mockery as m;
use Tests\TestCase;
use Pterodactyl\Models\Task;
use Illuminate\Support\Facades\Bus;
use Pterodactyl\Jobs\Schedule\RunTaskJob;
use Pterodactyl\Services\Schedules\Tasks\RunTaskService;
use Pterodactyl\Contracts\Repository\TaskRepositoryInterface;
class RunTaskServiceTest extends TestCase
{
/**
* @var \Illuminate\Contracts\Bus\Dispatcher|\Mockery\Mock
*/
protected $dispatcher;
/**
* @var \Pterodactyl\Contracts\Repository\TaskRepositoryInterface|\Mockery\Mock
*/
protected $repository;
/**
* @var \Pterodactyl\Services\Schedules\Tasks\RunTaskService
*/
protected $service;
/**
* Setup tests.
*/
public function setUp()
{
parent::setUp();
Bus::fake();
$this->repository = m::mock(TaskRepositoryInterface::class);
$this->service = new RunTaskService($this->repository);
}
/**
* Test that a job is dispatched.
*/
public function testTaskIsDispatched()
{
$task = factory(Task::class)->make();
$this->repository->shouldReceive('update')->with($task->id, ['is_queued' => true])->once()->andReturnNull();
$this->service->handle($task);
Bus::assertDispatched(RunTaskJob::class, function ($job) use ($task) {
$this->assertEquals($task->id, $job->task, 'Assert job task matches parent task model.');
$this->assertEquals($task->schedule_id, $job->schedule, 'Assert job is linked to correct schedule.');
$this->assertEquals($task->time_offset, $job->delay, 'Assert job delay is set correctly to match task.');
return true;
});
}
/**
* Test that passing an ID in place of a model works.
*/
public function testIdCanBePassedInPlaceOfModel()
{
$task = factory(Task::class)->make();
$this->repository->shouldReceive('find')->with($task->id)->once()->andReturn($task);
$this->repository->shouldReceive('update')->with($task->id, ['is_queued' => true])->once()->andReturnNull();
$this->service->handle($task->id);
Bus::assertDispatched(RunTaskJob::class, function ($job) use ($task) {
$this->assertEquals($task->id, $job->task, 'Assert job task matches parent task model.');
$this->assertEquals($task->schedule_id, $job->schedule, 'Assert job is linked to correct schedule.');
$this->assertEquals($task->time_offset, $job->delay, 'Assert job delay is set correctly to match task.');
return true;
});
}
}

View File

@ -12,7 +12,7 @@ use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
class EnvironmentServiceTest extends TestCase
{
const CONFIG_MAPPING = 'pterodactyl.environment_mappings';
const CONFIG_MAPPING = 'pterodactyl.environment_variables';
/**
* @var \Illuminate\Contracts\Config\Repository|\Mockery\Mock

View File

@ -9,9 +9,9 @@ use Tests\Traits\MocksUuids;
use Illuminate\Contracts\Hashing\Hasher;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Support\Facades\Notification;
use Illuminate\Contracts\Auth\PasswordBroker;
use Pterodactyl\Notifications\AccountCreated;
use Pterodactyl\Services\Users\UserCreationService;
use Pterodactyl\Services\Helpers\TemporaryPasswordService;
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
class UserCreationServiceTest extends TestCase
@ -29,9 +29,9 @@ class UserCreationServiceTest extends TestCase
private $hasher;
/**
* @var \Pterodactyl\Services\Helpers\TemporaryPasswordService|\Mockery\Mock
* @var \Illuminate\Contracts\Auth\PasswordBroker|\Mockery\Mock
*/
private $passwordService;
private $passwordBroker;
/**
* @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface|\Mockery\Mock
@ -48,7 +48,7 @@ class UserCreationServiceTest extends TestCase
Notification::fake();
$this->connection = m::mock(ConnectionInterface::class);
$this->hasher = m::mock(Hasher::class);
$this->passwordService = m::mock(TemporaryPasswordService::class);
$this->passwordBroker = m::mock(PasswordBroker::class);
$this->repository = m::mock(UserRepositoryInterface::class);
}
@ -121,7 +121,7 @@ class UserCreationServiceTest extends TestCase
$this->hasher->shouldNotReceive('make');
$this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
$this->hasher->shouldReceive('make')->once()->andReturn('created-enc-password');
$this->passwordService->shouldReceive('handle')->with($user->email)->once()->andReturn('random-token');
$this->passwordBroker->shouldReceive('createToken')->with($user)->once()->andReturn('random-token');
$this->repository->shouldReceive('create')->with([
'password' => 'created-enc-password',
@ -152,6 +152,6 @@ class UserCreationServiceTest extends TestCase
*/
private function getService(): UserCreationService
{
return new UserCreationService($this->connection, $this->hasher, $this->passwordService, $this->repository);
return new UserCreationService($this->connection, $this->hasher, $this->passwordBroker, $this->repository);
}
}