Begin updating UI

This commit is contained in:
Dane Everitt 2017-10-02 22:51:13 -05:00
parent 493c5888a3
commit ae671e6b19
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
22 changed files with 182 additions and 102 deletions

View file

@ -9,7 +9,6 @@
namespace Pterodactyl\Console\Commands\Environment;
use Ramsey\Uuid\Uuid;
use Illuminate\Console\Command;
use Illuminate\Contracts\Console\Kernel;
use Pterodactyl\Traits\Commands\EnvironmentWriterTrait;
@ -38,6 +37,7 @@ class AppSettingsCommand extends Command
* @var string
*/
protected $signature = 'p:environment:setup
{--author= : The email that services created on this instance should be linked to.}
{--url= : The URL that this Panel is running on.}
{--timezone= : The timezone to use for Panel times.}
{--cache= : The cache driver backend to use.}
@ -72,9 +72,10 @@ class AppSettingsCommand extends Command
*/
public function handle()
{
if (is_null($this->config->get('pterodactyl.service.author'))) {
$this->variables['SERVICE_AUTHOR'] = Uuid::uuid4()->toString();
}
$this->output->comment(trans('command/messages.environment.app.author_help'));
$this->variables['SERVICE_AUTHOR'] = $this->option('author') ?? $this->ask(
trans('command/messages.environment.app.author'), $this->config->get('pterodactyl.service.author', 'undefined@unknown-author.com')
);
$this->output->comment(trans('command/messages.environment.app.app_url_help'));
$this->variables['APP_URL'] = $this->option('url') ?? $this->ask(

View file

@ -9,6 +9,9 @@
namespace Pterodactyl\Contracts\Repository;
use Pterodactyl\Models\Service;
use Illuminate\Support\Collection;
interface ServiceRepositoryInterface extends RepositoryInterface
{
/**
@ -17,7 +20,15 @@ interface ServiceRepositoryInterface extends RepositoryInterface
* @param int $id
* @return \Illuminate\Support\Collection
*/
public function getWithOptions($id = null);
public function getWithOptions(int $id = null): Collection;
/**
* Return a service or all services and the count of options, packs, and servers for that service.
*
* @param int|null $id
* @return \Illuminate\Support\Collection
*/
public function getWithCounts(int $id = null): Collection;
/**
* Return a service along with its associated options and the servers relation on those options.
@ -25,5 +36,5 @@ interface ServiceRepositoryInterface extends RepositoryInterface
* @param int $id
* @return mixed
*/
public function getWithOptionServers($id);
public function getWithOptionServers(int $id): Service;
}

View file

@ -9,7 +9,9 @@
namespace Pterodactyl\Http\Controllers\Admin;
use Illuminate\View\View;
use Pterodactyl\Models\Service;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Services\Services\ServiceUpdateService;
@ -46,6 +48,15 @@ class ServiceController extends Controller
*/
protected $updateService;
/**
* ServiceController constructor.
*
* @param \Prologue\Alerts\AlertsMessageBag $alert
* @param \Pterodactyl\Services\Services\ServiceCreationService $creationService
* @param \Pterodactyl\Services\Services\ServiceDeletionService $deletionService
* @param \Pterodactyl\Contracts\Repository\ServiceRepositoryInterface $repository
* @param \Pterodactyl\Services\Services\ServiceUpdateService $updateService
*/
public function __construct(
AlertsMessageBag $alert,
ServiceCreationService $creationService,
@ -65,10 +76,10 @@ class ServiceController extends Controller
*
* @return \Illuminate\View\View
*/
public function index()
public function index(): View
{
return view('admin.services.index', [
'services' => $this->repository->getWithOptions(),
'services' => $this->repository->getWithCounts(),
]);
}
@ -77,7 +88,7 @@ class ServiceController extends Controller
*
* @return \Illuminate\View\View
*/
public function create()
public function create(): View
{
return view('admin.services.new');
}
@ -88,7 +99,7 @@ class ServiceController extends Controller
* @param int $service
* @return \Illuminate\View\View
*/
public function view($service)
public function view(int $service): View
{
return view('admin.services.view', [
'service' => $this->repository->getWithOptionServers($service),
@ -101,7 +112,7 @@ class ServiceController extends Controller
* @param \Pterodactyl\Models\Service $service
* @return \Illuminate\View\View
*/
public function viewFunctions(Service $service)
public function viewFunctions(Service $service): View
{
return view('admin.services.functions', ['service' => $service]);
}
@ -114,7 +125,7 @@ class ServiceController extends Controller
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
*/
public function store(ServiceFormRequest $request)
public function store(ServiceFormRequest $request): RedirectResponse
{
$service = $this->creationService->handle($request->normalize());
$this->alert->success(trans('admin/services.notices.service_created', ['name' => $service->name]))->flash();
@ -132,7 +143,7 @@ class ServiceController extends Controller
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function update(ServiceFormRequest $request, Service $service)
public function update(ServiceFormRequest $request, Service $service): RedirectResponse
{
$this->updateService->handle($service->id, $request->normalize());
$this->alert->success(trans('admin/services.notices.service_updated'))->flash();
@ -150,7 +161,7 @@ class ServiceController extends Controller
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function updateFunctions(ServiceFunctionsFormRequest $request, Service $service)
public function updateFunctions(ServiceFunctionsFormRequest $request, Service $service): RedirectResponse
{
$this->updateService->handle($service->id, $request->normalize());
$this->alert->success(trans('admin/services.notices.functions_updated'))->flash();
@ -166,7 +177,7 @@ class ServiceController extends Controller
*
* @throws \Pterodactyl\Exceptions\Service\HasActiveServersException
*/
public function destroy(Service $service)
public function destroy(Service $service): RedirectResponse
{
$this->deletionService->handle($service->id);
$this->alert->success(trans('admin/services.notices.service_deleted'))->flash();

View file

@ -13,7 +13,12 @@ use Illuminate\Foundation\Http\FormRequest;
abstract class AdminFormRequest extends FormRequest
{
abstract public function rules();
/**
* The rules to apply to the incoming form request.
*
* @return array
*/
abstract public function rules(): array;
/**
* Determine if the user is an admin and has permission to access this
@ -21,7 +26,7 @@ abstract class AdminFormRequest extends FormRequest
*
* @return bool
*/
public function authorize()
public function authorize(): bool
{
if (is_null($this->user())) {
return false;
@ -37,7 +42,7 @@ abstract class AdminFormRequest extends FormRequest
* @param array $only
* @return array
*/
public function normalize($only = [])
public function normalize($only = []): array
{
return array_merge(
$this->only($only),

View file

@ -16,22 +16,12 @@ class ServiceFormRequest extends AdminFormRequest
/**
* @return array
*/
public function rules()
public function rules(): array
{
$rules = [
return [
'name' => 'required|string|min:1|max:255',
'description' => 'required|nullable|string',
'folder' => 'required|regex:/^[\w.-]{1,50}$/|unique:services,folder',
'startup' => 'required|nullable|string',
];
if ($this->method() === 'PATCH') {
$service = $this->route()->parameter('service');
$rules['folder'] = $rules['folder'] . ',' . $service->id;
return $rules;
}
return $rules;
}
}

View file

@ -31,7 +31,12 @@ class Service extends Model implements CleansAttributes, ValidableContract
*
* @var array
*/
protected $fillable = ['name', 'author', 'description', 'folder', 'startup', 'index_file'];
protected $fillable = [
'name',
'description',
'startup',
'index_file',
];
/**
* @var array
@ -40,7 +45,6 @@ class Service extends Model implements CleansAttributes, ValidableContract
'author' => 'required',
'name' => 'required',
'description' => 'sometimes',
'folder' => 'required',
'startup' => 'sometimes',
'index_file' => 'required',
];
@ -49,10 +53,9 @@ class Service extends Model implements CleansAttributes, ValidableContract
* @var array
*/
protected static $dataIntegrityRules = [
'author' => 'string|size:36',
'author' => 'email',
'name' => 'string|max:255',
'description' => 'nullable|string',
'folder' => 'string|max:255|regex:/^[\w.-]{1,50}$/|unique:services,folder',
'startup' => 'nullable|string',
'index_file' => 'string',
];
@ -74,12 +77,7 @@ class Service extends Model implements CleansAttributes, ValidableContract
*/
public function packs()
{
return $this->hasManyThrough(
Pack::class,
ServiceOption::class,
'service_id',
'option_id'
);
return $this->hasManyThrough(Pack::class, ServiceOption::class, 'service_id', 'option_id');
}
/**

View file

@ -31,7 +31,22 @@ class ServiceOption extends Model implements CleansAttributes, ValidableContract
*
* @var array
*/
protected $guarded = ['id', 'created_at', 'updated_at'];
protected $fillable = [
'name',
'description',
'docker_image',
'config_files',
'config_startup',
'config_logs',
'config_stop',
'config_from',
'startup',
'script_is_privileged',
'script_install',
'script_entry',
'script_container',
'copy_script_from',
];
/**
* Cast values to correct type.
@ -40,7 +55,9 @@ class ServiceOption extends Model implements CleansAttributes, ValidableContract
*/
protected $casts = [
'service_id' => 'integer',
'config_from' => 'integer',
'script_is_privileged' => 'boolean',
'copy_script_from' => 'integer',
];
/**
@ -48,6 +65,7 @@ class ServiceOption extends Model implements CleansAttributes, ValidableContract
*/
protected static $applicationRules = [
'service_id' => 'required',
'author' => 'required',
'name' => 'required',
'description' => 'required',
'tag' => 'required',
@ -64,13 +82,14 @@ class ServiceOption extends Model implements CleansAttributes, ValidableContract
* @var array
*/
protected static $dataIntegrityRules = [
'service_id' => 'numeric|exists:services,id',
'service_id' => 'bail|numeric|exists:services,id',
'author' => 'email',
'name' => 'string|max:255',
'description' => 'string',
'tag' => 'alpha_num|max:60|unique:service_options,tag',
'tag' => 'bail|alpha_num|max:60|unique:service_options,tag',
'docker_image' => 'string|max:255',
'startup' => 'nullable|string',
'config_from' => 'nullable|numeric|exists:service_options,id',
'config_from' => 'bail|nullable|numeric|exists:service_options,id',
'config_stop' => 'nullable|string|max:255',
'config_startup' => 'nullable|json',
'config_logs' => 'nullable|json',

View file

@ -9,8 +9,8 @@
namespace Pterodactyl\Repositories\Eloquent;
use Webmozart\Assert\Assert;
use Pterodactyl\Models\Service;
use Illuminate\Support\Collection;
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
use Pterodactyl\Contracts\Repository\ServiceRepositoryInterface;
@ -27,16 +27,14 @@ class ServiceRepository extends EloquentRepository implements ServiceRepositoryI
/**
* {@inheritdoc}
*/
public function getWithOptions($id = null)
public function getWithOptions(int $id = null): Collection
{
Assert::nullOrNumeric($id, 'First argument passed to getWithOptions must be null or numeric, received %s.');
$instance = $this->getBuilder()->with('options.packs', 'options.variables');
if (! is_null($id)) {
$instance = $instance->find($id, $this->getColumns());
if (! $instance) {
throw new RecordNotFoundException();
throw new RecordNotFoundException;
}
return $instance;
@ -48,15 +46,33 @@ class ServiceRepository extends EloquentRepository implements ServiceRepositoryI
/**
* {@inheritdoc}
*/
public function getWithOptionServers($id)
public function getWithCounts(int $id = null): Collection
{
Assert::numeric($id, 'First argument passed to getWithOptionServers must be numeric, received %s.');
$instance = $this->getBuilder()->withCount(['options', 'packs', 'servers']);
$instance = $this->getBuilder()->with('options.servers')->find($id, $this->getColumns());
if (! $instance) {
throw new RecordNotFoundException();
if (! is_null($id)) {
$instance = $instance->find($id, $this->getColumns());
if (! $instance) {
throw new RecordNotFoundException;
}
return $instance;
}
return $instance->get($this->getColumns());
}
/**
* {@inheritdoc}
*/
public function getWithOptionServers(int $id): Service
{
$instance = $this->getBuilder()->with('options.servers')->find($id, $this->getColumns());
if (! $instance) {
throw new RecordNotFoundException;
}
/* @var Service $instance */
return $instance;
}
}

View file

@ -40,7 +40,7 @@ class InstallScriptUpdateService
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Pterodactyl\Exceptions\Service\ServiceOption\InvalidCopyFromException
*/
public function handle($option, array $data)
public function handle($option, array $data): void
{
if (! $option instanceof ServiceOption) {
$option = $this->repository->find($option);

View file

@ -9,11 +9,19 @@
namespace Pterodactyl\Services\Services\Options;
use Ramsey\Uuid\Uuid;
use Pterodactyl\Models\ServiceOption;
use Illuminate\Contracts\Config\Repository as ConfigRepository;
use Pterodactyl\Contracts\Repository\ServiceOptionRepositoryInterface;
use Pterodactyl\Exceptions\Service\ServiceOption\NoParentConfigurationFoundException;
class OptionCreationService
{
/**
* @var \Illuminate\Contracts\Config\Repository
*/
protected $config;
/**
* @var \Pterodactyl\Contracts\Repository\ServiceOptionRepositoryInterface
*/
@ -22,10 +30,12 @@ class OptionCreationService
/**
* CreationService constructor.
*
* @param \Illuminate\Contracts\Config\Repository $config
* @param \Pterodactyl\Contracts\Repository\ServiceOptionRepositoryInterface $repository
*/
public function __construct(ServiceOptionRepositoryInterface $repository)
public function __construct(ConfigRepository $config, ServiceOptionRepositoryInterface $repository)
{
$this->config = $config;
$this->repository = $repository;
}
@ -38,7 +48,7 @@ class OptionCreationService
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Service\ServiceOption\NoParentConfigurationFoundException
*/
public function handle(array $data)
public function handle(array $data): ServiceOption
{
if (! is_null(array_get($data, 'config_from'))) {
$results = $this->repository->findCountWhere([
@ -53,6 +63,14 @@ class OptionCreationService
$data['config_from'] = null;
}
return $this->repository->create($data);
if (count($parts = explode(':', array_get($data, 'tag'))) > 1) {
$data['tag'] = $this->config->get('pterodactyl.service.author') . ':' . trim(array_pop($parts));
} else {
$data['tag'] = $this->config->get('pterodactyl.service.author') . ':' . trim(array_get($data, 'tag'));
}
return $this->repository->create(array_merge($data, [
'uuid' => Uuid::uuid4()->toString(),
]), true, true);
}
}

View file

@ -9,7 +9,6 @@
namespace Pterodactyl\Services\Services\Options;
use Webmozart\Assert\Assert;
use Pterodactyl\Exceptions\Service\HasActiveServersException;
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
use Pterodactyl\Contracts\Repository\ServiceOptionRepositoryInterface;
@ -50,10 +49,8 @@ class OptionDeletionService
* @throws \Pterodactyl\Exceptions\Service\HasActiveServersException
* @throws \Pterodactyl\Exceptions\Service\ServiceOption\HasChildrenException
*/
public function handle($option)
public function handle(int $option): int
{
Assert::integerish($option, 'First argument passed to handle must be integer, received %s.');
$servers = $this->serverRepository->findCountWhere([['option_id', '=', $option]]);
if ($servers > 0) {
throw new HasActiveServersException(trans('exceptions.service.options.delete_has_servers'));

View file

@ -40,7 +40,7 @@ class OptionUpdateService
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Pterodactyl\Exceptions\Service\ServiceOption\NoParentConfigurationFoundException
*/
public function handle($option, array $data)
public function handle($option, array $data): void
{
if (! $option instanceof ServiceOption) {
$option = $this->repository->find($option);

View file

@ -9,6 +9,8 @@
namespace Pterodactyl\Services\Services;
use Ramsey\Uuid\Uuid;
use Pterodactyl\Models\Service;
use Pterodactyl\Traits\Services\CreatesServiceIndex;
use Illuminate\Contracts\Config\Repository as ConfigRepository;
use Pterodactyl\Contracts\Repository\ServiceRepositoryInterface;
@ -49,16 +51,15 @@ class ServiceCreationService
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
*/
public function handle(array $data)
public function handle(array $data): Service
{
return $this->repository->create(array_merge([
return $this->repository->create([
'uuid' => Uuid::uuid4()->toString(),
'author' => $this->config->get('pterodactyl.service.author'),
], [
'name' => array_get($data, 'name'),
'description' => array_get($data, 'description'),
'folder' => array_get($data, 'folder'),
'startup' => array_get($data, 'startup'),
'index_file' => $this->getIndexScript(),
]));
], true, true);
}
}

View file

@ -47,7 +47,7 @@ class ServiceDeletionService
*
* @throws \Pterodactyl\Exceptions\Service\HasActiveServersException
*/
public function handle($service)
public function handle(int $service): int
{
$count = $this->serverRepository->findCountWhere([['service_id', '=', $service]]);
if ($count > 0) {

View file

@ -36,7 +36,7 @@ class ServiceUpdateService
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function handle($service, array $data)
public function handle(int $service, array $data): void
{
if (! is_null(array_get($data, 'author'))) {
unset($data['author']);