Move services onto new services system, includes tests
This commit is contained in:
parent
e91079d128
commit
90bbe57148
26 changed files with 899 additions and 272 deletions
|
@ -12,7 +12,6 @@ services:
|
|||
before_install:
|
||||
- mysql -e 'CREATE DATABASE IF NOT EXISTS travis;'
|
||||
before_script:
|
||||
# - phpenv config-rm xdebug.ini
|
||||
- cp .env.travis .env
|
||||
- composer install --no-interaction --prefer-dist --no-suggest --verbose
|
||||
- php artisan migrate --seed -v
|
||||
|
|
|
@ -33,4 +33,12 @@ interface ServiceRepositoryInterface extends RepositoryInterface
|
|||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
public function getWithOptions($id = null);
|
||||
|
||||
/**
|
||||
* Return a service along with its associated options and the servers relation on those options.
|
||||
*
|
||||
* @param int $id
|
||||
* @return mixed
|
||||
*/
|
||||
public function getWithOptionServers($id);
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace Pterodactyl\Exceptions\Services\ServiceOption;
|
||||
namespace Pterodactyl\Exceptions\Services;
|
||||
|
||||
class HasActiveServersException extends \Exception
|
||||
{
|
|
@ -38,7 +38,7 @@ use Pterodactyl\Http\Requests\Admin\Service\ServiceOptionFormRequest;
|
|||
use Pterodactyl\Services\Services\Options\InstallScriptUpdateService;
|
||||
use Pterodactyl\Contracts\Repository\ServiceOptionRepositoryInterface;
|
||||
use Pterodactyl\Exceptions\Services\ServiceOption\InvalidCopyFromException;
|
||||
use Pterodactyl\Exceptions\Services\ServiceOption\HasActiveServersException;
|
||||
use Pterodactyl\Exceptions\Services\HasActiveServersException;
|
||||
use Pterodactyl\Exceptions\Services\ServiceOption\NoParentConfigurationFoundException;
|
||||
|
||||
class OptionController extends Controller
|
||||
|
@ -148,11 +148,11 @@ class OptionController extends Controller
|
|||
* @param \Pterodactyl\Models\ServiceOption $option
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function delete(ServiceOption $option)
|
||||
public function destroy(ServiceOption $option)
|
||||
{
|
||||
try {
|
||||
$this->optionDeletionService->handle($option->id);
|
||||
$this->alert->success()->flash();
|
||||
$this->alert->success(trans('admin/services.options.notices.option_deleted'))->flash();
|
||||
} catch (HasActiveServersException $exception) {
|
||||
$this->alert->danger($exception->getMessage())->flash();
|
||||
|
||||
|
@ -229,6 +229,7 @@ class OptionController extends Controller
|
|||
* @return \Illuminate\Http\RedirectResponse
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function updateScripts(EditOptionScript $request, ServiceOption $option)
|
||||
{
|
||||
|
|
|
@ -24,37 +24,76 @@
|
|||
|
||||
namespace Pterodactyl\Http\Controllers\Admin;
|
||||
|
||||
use Log;
|
||||
use Alert;
|
||||
use Pterodactyl\Models;
|
||||
use Illuminate\Http\Request;
|
||||
use Pterodactyl\Exceptions\DisplayException;
|
||||
use Pterodactyl\Models\Service;
|
||||
use Prologue\Alerts\AlertsMessageBag;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Pterodactyl\Repositories\ServiceRepository;
|
||||
use Pterodactyl\Exceptions\DisplayValidationException;
|
||||
use Pterodactyl\Services\Services\ServiceUpdateService;
|
||||
use Pterodactyl\Services\Services\ServiceCreationService;
|
||||
use Pterodactyl\Services\Services\ServiceDeletionService;
|
||||
use Pterodactyl\Exceptions\Services\HasActiveServersException;
|
||||
use Pterodactyl\Http\Requests\Admin\Service\ServiceFormRequest;
|
||||
use Pterodactyl\Contracts\Repository\ServiceRepositoryInterface;
|
||||
use Pterodactyl\Http\Requests\Admin\Service\ServiceFunctionsFormRequest;
|
||||
|
||||
class ServiceController extends Controller
|
||||
{
|
||||
/**
|
||||
* @var \Prologue\Alerts\AlertsMessageBag
|
||||
*/
|
||||
protected $alert;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Services\ServiceCreationService
|
||||
*/
|
||||
protected $creationService;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Services\ServiceDeletionService
|
||||
*/
|
||||
protected $deletionService;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\ServiceRepositoryInterface
|
||||
*/
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Services\ServiceUpdateService
|
||||
*/
|
||||
protected $updateService;
|
||||
|
||||
public function __construct(
|
||||
AlertsMessageBag $alert,
|
||||
ServiceCreationService $creationService,
|
||||
ServiceDeletionService $deletionService,
|
||||
ServiceRepositoryInterface $repository,
|
||||
ServiceUpdateService $updateService
|
||||
) {
|
||||
$this->alert = $alert;
|
||||
$this->creationService = $creationService;
|
||||
$this->deletionService = $deletionService;
|
||||
$this->repository = $repository;
|
||||
$this->updateService = $updateService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display service overview page.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function index(Request $request)
|
||||
public function index()
|
||||
{
|
||||
return view('admin.services.index', [
|
||||
'services' => Models\Service::withCount('servers', 'options', 'packs')->get(),
|
||||
'services' => $this->repository->getWithOptions(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display create service page.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function create(Request $request)
|
||||
public function create()
|
||||
{
|
||||
return view('admin.services.new');
|
||||
}
|
||||
|
@ -62,91 +101,96 @@ class ServiceController extends Controller
|
|||
/**
|
||||
* Return base view for a service.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param int $id
|
||||
* @param int $service
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function view(Request $request, $id)
|
||||
public function view($service)
|
||||
{
|
||||
return view('admin.services.view', [
|
||||
'service' => Models\Service::with('options', 'options.servers')->findOrFail($id),
|
||||
'service' => $this->repository->getWithOptionServers($service),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return function editing view for a service.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param int $id
|
||||
* @param \Pterodactyl\Models\Service $service
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function viewFunctions(Request $request, $id)
|
||||
public function viewFunctions(Service $service)
|
||||
{
|
||||
return view('admin.services.functions', ['service' => Models\Service::findOrFail($id)]);
|
||||
return view('admin.services.functions', ['service' => $service]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle post action for new service.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Pterodactyl\Http\Requests\Admin\Service\ServiceFormRequest $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
*/
|
||||
public function store(Request $request)
|
||||
public function store(ServiceFormRequest $request)
|
||||
{
|
||||
$repo = new ServiceRepository;
|
||||
|
||||
try {
|
||||
$service = $repo->create($request->intersect([
|
||||
'name', 'description', 'folder', 'startup',
|
||||
]));
|
||||
Alert::success('Successfully created new service!')->flash();
|
||||
$service = $this->creationService->handle($request->normalize());
|
||||
$this->alert->success(trans('admin/services.notices.service_created', ['name' => $service->name]))->flash();
|
||||
|
||||
return redirect()->route('admin.services.view', $service->id);
|
||||
} catch (DisplayValidationException $ex) {
|
||||
return redirect()->route('admin.services.new')->withErrors(json_decode($ex->getMessage()))->withInput();
|
||||
} catch (DisplayException $ex) {
|
||||
Alert::danger($ex->getMessage())->flash();
|
||||
} catch (\Exception $ex) {
|
||||
Log::error($ex);
|
||||
Alert::danger('An error occured while attempting to add a new service. This error has been logged.')->flash();
|
||||
}
|
||||
|
||||
return redirect()->route('admin.services.new')->withInput();
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits configuration for a specific service.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param int $id
|
||||
* @param \Pterodactyl\Http\Requests\Admin\Service\ServiceFormRequest $request
|
||||
* @param \Pterodactyl\Models\Service $service
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function update(ServiceFormRequest $request, Service $service)
|
||||
{
|
||||
$this->updateService->handle($service->id, $request->normalize());
|
||||
$this->alert->success(trans('admin/services.notices.service_updated'))->flash();
|
||||
|
||||
return redirect()->route('admin.services.view', $service);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the functions file for a service.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Admin\Service\ServiceFunctionsFormRequest $request
|
||||
* @param \Pterodactyl\Models\Service $service
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function updateFunctions(ServiceFunctionsFormRequest $request, Service $service)
|
||||
{
|
||||
$this->updateService->handle($service->id, $request->normalize());
|
||||
$this->alert->success(trans('admin/services.notices.functions_updated'))->flash();
|
||||
|
||||
return redirect()->route('admin.services.view.functions', $service->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a service from the panel.
|
||||
*
|
||||
* @param \Pterodactyl\Models\Service $service
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function edit(Request $request, $id)
|
||||
public function destroy(Service $service)
|
||||
{
|
||||
$repo = new ServiceRepository;
|
||||
$redirectTo = ($request->input('redirect_to')) ? 'admin.services.view.functions' : 'admin.services.view';
|
||||
|
||||
try {
|
||||
if ($request->input('action') !== 'delete') {
|
||||
$repo->update($id, $request->intersect([
|
||||
'name', 'description', 'folder', 'startup', 'index_file',
|
||||
]));
|
||||
Alert::success('Service has been updated successfully.')->flash();
|
||||
} else {
|
||||
$repo->delete($id);
|
||||
Alert::success('Successfully deleted service from the system.')->flash();
|
||||
$this->deletionService->handle($service->id);
|
||||
$this->alert->success(trans('admin/services.notices.service_deleted'))->flash();
|
||||
} catch (HasActiveServersException $exception) {
|
||||
$this->alert->danger($exception->getMessage())->flash();
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
return redirect()->route('admin.services');
|
||||
}
|
||||
} catch (DisplayValidationException $ex) {
|
||||
return redirect()->route($redirectTo, $id)->withErrors(json_decode($ex->getMessage()))->withInput();
|
||||
} catch (DisplayException $ex) {
|
||||
Alert::danger($ex->getMessage())->flash();
|
||||
} catch (\Exception $ex) {
|
||||
Log::error($ex);
|
||||
Alert::danger('An error occurred while attempting to update this service. This error has been logged.')->flash();
|
||||
}
|
||||
|
||||
return redirect()->route($redirectTo, $id);
|
||||
}
|
||||
}
|
||||
|
|
52
app/Http/Requests/Admin/Service/ServiceFormRequest.php
Normal file
52
app/Http/Requests/Admin/Service/ServiceFormRequest.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
/*
|
||||
* Pterodactyl - Panel
|
||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace Pterodactyl\Http\Requests\Admin\Service;
|
||||
|
||||
use Pterodactyl\Http\Requests\Admin\AdminFormRequest;
|
||||
|
||||
class ServiceFormRequest extends AdminFormRequest
|
||||
{
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
$rules = [
|
||||
'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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
/*
|
||||
* Pterodactyl - Panel
|
||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace Pterodactyl\Http\Requests\Admin\Service;
|
||||
|
||||
use Pterodactyl\Http\Requests\Admin\AdminFormRequest;
|
||||
|
||||
class ServiceFunctionsFormRequest extends AdminFormRequest
|
||||
{
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'index_file' => 'required|nullable|string',
|
||||
];
|
||||
}
|
||||
}
|
|
@ -24,10 +24,16 @@
|
|||
|
||||
namespace Pterodactyl\Models;
|
||||
|
||||
use Sofa\Eloquence\Eloquence;
|
||||
use Sofa\Eloquence\Validable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Sofa\Eloquence\Contracts\CleansAttributes;
|
||||
use Sofa\Eloquence\Contracts\Validable as ValidableContract;
|
||||
|
||||
class Service extends Model
|
||||
class Service extends Model implements CleansAttributes, ValidableContract
|
||||
{
|
||||
use Eloquence, Validable;
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
|
@ -40,52 +46,31 @@ class Service extends Model
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name', 'description', 'folder', 'startup', 'index_file',
|
||||
protected $fillable = ['name', 'author', 'description', 'folder', 'startup', 'index_file'];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $applicationRules = [
|
||||
'author' => 'required',
|
||||
'name' => 'required',
|
||||
'description' => 'sometimes',
|
||||
'folder' => 'required',
|
||||
'startup' => 'sometimes',
|
||||
'index_file' => 'required',
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns the default contents of the index.js file for a service.
|
||||
*
|
||||
* @return string
|
||||
* @var array
|
||||
*/
|
||||
public static function defaultIndexFile()
|
||||
{
|
||||
return <<<'EOF'
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Pterodactyl - Daemon
|
||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
const rfr = require('rfr');
|
||||
const _ = require('lodash');
|
||||
|
||||
const Core = rfr('src/services/index.js');
|
||||
|
||||
class Service extends Core {}
|
||||
|
||||
module.exports = Service;
|
||||
EOF;
|
||||
}
|
||||
protected static $dataIntegrityRules = [
|
||||
'author' => 'string|size:36',
|
||||
'name' => 'string|max:255',
|
||||
'description' => 'nullable|string',
|
||||
'folder' => 'string|max:255|regex:/^[\w.-]{1,50}$/|unique:services,folder',
|
||||
'startup' => 'nullable|string',
|
||||
'index_file' => 'string',
|
||||
];
|
||||
|
||||
/**
|
||||
* Gets all service options associated with this service.
|
||||
|
|
|
@ -49,8 +49,8 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf
|
|||
*/
|
||||
public function create(array $fields, $validate = true, $force = false)
|
||||
{
|
||||
Assert::boolean($validate, 'Second argument passed to create should be boolean, recieved %s.');
|
||||
Assert::boolean($force, 'Third argument passed to create should be boolean, received %s.');
|
||||
Assert::boolean($validate, 'Second argument passed to create must be boolean, recieved %s.');
|
||||
Assert::boolean($force, 'Third argument passed to create must be boolean, received %s.');
|
||||
|
||||
$instance = $this->getBuilder()->newModelInstance();
|
||||
|
||||
|
@ -77,7 +77,7 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf
|
|||
*/
|
||||
public function find($id)
|
||||
{
|
||||
Assert::integer($id, 'First argument passed to find should be integer, received %s.');
|
||||
Assert::numeric($id, 'First argument passed to find must be numeric, received %s.');
|
||||
|
||||
$instance = $this->getBuilder()->find($id, $this->getColumns());
|
||||
|
||||
|
@ -125,8 +125,8 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf
|
|||
*/
|
||||
public function delete($id, $destroy = false)
|
||||
{
|
||||
Assert::integer($id, 'First argument passed to delete should be integer, received %s.');
|
||||
Assert::boolean($destroy, 'Second argument passed to delete should be boolean, received %s.');
|
||||
Assert::numeric($id, 'First argument passed to delete must be numeric, received %s.');
|
||||
Assert::boolean($destroy, 'Second argument passed to delete must be boolean, received %s.');
|
||||
|
||||
$instance = $this->getBuilder()->where($this->getModel()->getKeyName(), $id);
|
||||
|
||||
|
@ -138,7 +138,7 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf
|
|||
*/
|
||||
public function deleteWhere(array $attributes, $force = false)
|
||||
{
|
||||
Assert::boolean($force, 'Second argument passed to deleteWhere should be boolean, received %s.');
|
||||
Assert::boolean($force, 'Second argument passed to deleteWhere must be boolean, received %s.');
|
||||
|
||||
$instance = $this->getBuilder()->where($attributes);
|
||||
|
||||
|
@ -150,9 +150,9 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf
|
|||
*/
|
||||
public function update($id, array $fields, $validate = true, $force = false)
|
||||
{
|
||||
Assert::integer($id, 'First argument passed to update expected to be integer, received %s.');
|
||||
Assert::boolean($validate, 'Third argument passed to update should be boolean, received %s.');
|
||||
Assert::boolean($force, 'Fourth argument passed to update should be boolean, received %s.');
|
||||
Assert::numeric($id, 'First argument passed to update must be numeric, received %s.');
|
||||
Assert::boolean($validate, 'Third argument passed to update must be boolean, received %s.');
|
||||
Assert::boolean($force, 'Fourth argument passed to update must be boolean, received %s.');
|
||||
|
||||
$instance = $this->getBuilder()->where('id', $id)->first();
|
||||
|
||||
|
@ -182,7 +182,7 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf
|
|||
*/
|
||||
public function updateWhereIn($column, array $values, array $fields)
|
||||
{
|
||||
Assert::stringNotEmpty($column, 'First argument passed to updateWhereIn expected to be a string, received %s.');
|
||||
Assert::stringNotEmpty($column, 'First argument passed to updateWhereIn must be a non-empty string, received %s.');
|
||||
|
||||
return $this->getBuilder()->whereIn($column, $values)->update($fields);
|
||||
}
|
||||
|
@ -255,8 +255,8 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf
|
|||
*/
|
||||
public function updateOrCreate(array $where, array $fields, $validate = true, $force = false)
|
||||
{
|
||||
Assert::boolean($validate, 'Third argument passed to updateOrCreate should be boolean, received %s.');
|
||||
Assert::boolean($force, 'Fourth argument passed to updateOrCreate should be boolean, received %s.');
|
||||
Assert::boolean($validate, 'Third argument passed to updateOrCreate must be boolean, received %s.');
|
||||
Assert::boolean($force, 'Fourth argument passed to updateOrCreate must be boolean, received %s.');
|
||||
|
||||
$instance = $this->withColumns('id')->findWhere($where)->first();
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
namespace Pterodactyl\Repositories\Eloquent;
|
||||
|
||||
use Webmozart\Assert\Assert;
|
||||
use Pterodactyl\Models\Service;
|
||||
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
|
||||
use Pterodactyl\Contracts\Repository\ServiceRepositoryInterface;
|
||||
|
@ -43,11 +44,12 @@ class ServiceRepository extends EloquentRepository implements ServiceRepositoryI
|
|||
*/
|
||||
public function getWithOptions($id = null)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
@ -57,4 +59,19 @@ class ServiceRepository extends EloquentRepository implements ServiceRepositoryI
|
|||
|
||||
return $instance->get($this->getColumns());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getWithOptionServers($id)
|
||||
{
|
||||
Assert::numeric($id, 'First argument passed to getWithOptionServers must be numeric, received %s.');
|
||||
|
||||
$instance = $this->getBuilder()->with('options.servers')->find($id, $this->getColumns());
|
||||
if (! $instance) {
|
||||
throw new RecordNotFoundException();
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,135 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Pterodactyl - Panel
|
||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace Pterodactyl\Repositories;
|
||||
|
||||
use DB;
|
||||
use Validator;
|
||||
use Pterodactyl\Models\Service;
|
||||
use Pterodactyl\Exceptions\DisplayException;
|
||||
use Pterodactyl\Exceptions\DisplayValidationException;
|
||||
|
||||
class ServiceRepository
|
||||
{
|
||||
/**
|
||||
* Creates a new service on the system.
|
||||
*
|
||||
* @param array $data
|
||||
* @return \Pterodactyl\Models\Service
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\DisplayValidationException
|
||||
*/
|
||||
public function create(array $data)
|
||||
{
|
||||
$validator = Validator::make($data, [
|
||||
'name' => 'required|string|min:1|max:255',
|
||||
'description' => 'required|nullable|string',
|
||||
'folder' => 'required|unique:services,folder|regex:/^[\w.-]{1,50}$/',
|
||||
'startup' => 'required|nullable|string',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
throw new DisplayValidationException(json_encode($validator->errors()));
|
||||
}
|
||||
|
||||
return DB::transaction(function () use ($data) {
|
||||
$service = new Service;
|
||||
$service->author = config('pterodactyl.service.author');
|
||||
$service->fill([
|
||||
'name' => $data['name'],
|
||||
'description' => (isset($data['description'])) ? $data['description'] : null,
|
||||
'folder' => $data['folder'],
|
||||
'startup' => (isset($data['startup'])) ? $data['startup'] : null,
|
||||
'index_file' => Service::defaultIndexFile(),
|
||||
])->save();
|
||||
|
||||
// It is possible for an event to return false or throw an exception
|
||||
// which won't necessarily be detected by this transaction.
|
||||
//
|
||||
// This check ensures the model was actually saved.
|
||||
if (! $service->exists) {
|
||||
throw new \Exception('Service model was created however the response appears to be invalid. Did an event fire wrongly?');
|
||||
}
|
||||
|
||||
return $service;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a service.
|
||||
*
|
||||
* @param int $id
|
||||
* @param array $data
|
||||
* @return \Pterodactyl\Models\Service
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\DisplayValidationException
|
||||
*/
|
||||
public function update($id, array $data)
|
||||
{
|
||||
$service = Service::findOrFail($id);
|
||||
|
||||
$validator = Validator::make($data, [
|
||||
'name' => 'sometimes|required|string|min:1|max:255',
|
||||
'description' => 'sometimes|required|nullable|string',
|
||||
'folder' => 'sometimes|required|regex:/^[\w.-]{1,50}$/',
|
||||
'startup' => 'sometimes|required|nullable|string',
|
||||
'index_file' => 'sometimes|required|string',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
throw new DisplayValidationException(json_encode($validator->errors()));
|
||||
}
|
||||
|
||||
return DB::transaction(function () use ($data, $service) {
|
||||
$service->fill($data)->save();
|
||||
|
||||
return $service;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a service and associated files and options.
|
||||
*
|
||||
* @param int $id
|
||||
* @return void
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\DisplayException
|
||||
*/
|
||||
public function delete($id)
|
||||
{
|
||||
$service = Service::withCount('servers')->with('options')->findOrFail($id);
|
||||
|
||||
if ($service->servers_count > 0) {
|
||||
throw new DisplayException('You cannot delete a service that has servers associated with it.');
|
||||
}
|
||||
|
||||
DB::transaction(function () use ($service) {
|
||||
foreach ($service->options as $option) {
|
||||
(new OptionRepository)->delete($option->id);
|
||||
}
|
||||
|
||||
$service->delete();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -26,7 +26,7 @@ namespace Pterodactyl\Services\Services\Options;
|
|||
|
||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
||||
use Pterodactyl\Contracts\Repository\ServiceOptionRepositoryInterface;
|
||||
use Pterodactyl\Exceptions\Services\ServiceOption\HasActiveServersException;
|
||||
use Pterodactyl\Exceptions\Services\HasActiveServersException;
|
||||
|
||||
class OptionDeletionService
|
||||
{
|
||||
|
@ -60,7 +60,7 @@ class OptionDeletionService
|
|||
* @param int $option
|
||||
* @return int
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Services\ServiceOption\HasActiveServersException
|
||||
* @throws \Pterodactyl\Exceptions\Services\HasActiveServersException
|
||||
*/
|
||||
public function handle($option)
|
||||
{
|
||||
|
|
79
app/Services/Services/ServiceCreationService.php
Normal file
79
app/Services/Services/ServiceCreationService.php
Normal file
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
/*
|
||||
* Pterodactyl - Panel
|
||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace Pterodactyl\Services\Services;
|
||||
|
||||
use Pterodactyl\Traits\Services\CreatesServiceIndex;
|
||||
use Illuminate\Contracts\Config\Repository as ConfigRepository;
|
||||
use Pterodactyl\Contracts\Repository\ServiceRepositoryInterface;
|
||||
|
||||
class ServiceCreationService
|
||||
{
|
||||
use CreatesServiceIndex;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Config\Repository
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\ServiceRepositoryInterface
|
||||
*/
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* ServiceCreationService constructor.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Config\Repository $config
|
||||
* @param \Pterodactyl\Contracts\Repository\ServiceRepositoryInterface $repository
|
||||
*/
|
||||
public function __construct(
|
||||
ConfigRepository $config,
|
||||
ServiceRepositoryInterface $repository
|
||||
) {
|
||||
$this->config = $config;
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new service on the system.
|
||||
*
|
||||
* @param array $data
|
||||
* @return \Pterodactyl\Models\Service
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
*/
|
||||
public function handle(array $data)
|
||||
{
|
||||
return $this->repository->create(array_merge([
|
||||
'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(),
|
||||
]));
|
||||
}
|
||||
}
|
74
app/Services/Services/ServiceDeletionService.php
Normal file
74
app/Services/Services/ServiceDeletionService.php
Normal file
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
/*
|
||||
* Pterodactyl - Panel
|
||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace Pterodactyl\Services\Services;
|
||||
|
||||
use Pterodactyl\Exceptions\Services\HasActiveServersException;
|
||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
||||
use Pterodactyl\Contracts\Repository\ServiceRepositoryInterface;
|
||||
|
||||
class ServiceDeletionService
|
||||
{
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
|
||||
*/
|
||||
protected $serverRepository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\ServiceRepositoryInterface
|
||||
*/
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* ServiceDeletionService constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $serverRepository
|
||||
* @param \Pterodactyl\Contracts\Repository\ServiceRepositoryInterface $repository
|
||||
*/
|
||||
public function __construct(
|
||||
ServerRepositoryInterface $serverRepository,
|
||||
ServiceRepositoryInterface $repository
|
||||
) {
|
||||
$this->serverRepository = $serverRepository;
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a service from the system only if there are no servers attached to it.
|
||||
*
|
||||
* @param int $service
|
||||
* @return int
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Services\HasActiveServersException
|
||||
*/
|
||||
public function handle($service)
|
||||
{
|
||||
$count = $this->serverRepository->findCountWhere([['service_id', '=', $service]]);
|
||||
if ($count > 0) {
|
||||
throw new HasActiveServersException(trans('admin/exceptions.service.delete_has_servers'));
|
||||
}
|
||||
|
||||
return $this->repository->delete($service);
|
||||
}
|
||||
}
|
62
app/Services/Services/ServiceUpdateService.php
Normal file
62
app/Services/Services/ServiceUpdateService.php
Normal file
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
/*
|
||||
* Pterodactyl - Panel
|
||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace Pterodactyl\Services\Services;
|
||||
|
||||
use Pterodactyl\Contracts\Repository\ServiceRepositoryInterface;
|
||||
|
||||
class ServiceUpdateService
|
||||
{
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\ServiceRepositoryInterface
|
||||
*/
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* ServiceUpdateService constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Contracts\Repository\ServiceRepositoryInterface $repository
|
||||
*/
|
||||
public function __construct(ServiceRepositoryInterface $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a service and prevent changing the author once it is set.
|
||||
*
|
||||
* @param int $service
|
||||
* @param array $data
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function handle($service, array $data)
|
||||
{
|
||||
if (! is_null(array_get($data, 'author'))) {
|
||||
unset($data['author']);
|
||||
}
|
||||
|
||||
$this->repository->withoutFresh()->update($service, $data);
|
||||
}
|
||||
}
|
71
app/Traits/Services/CreatesServiceIndex.php
Normal file
71
app/Traits/Services/CreatesServiceIndex.php
Normal file
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
/*
|
||||
* Pterodactyl - Panel
|
||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace Pterodactyl\Traits\Services;
|
||||
|
||||
trait CreatesServiceIndex
|
||||
{
|
||||
/**
|
||||
* Returns the default index.js file that is used for services on the daemon.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIndexScript()
|
||||
{
|
||||
return <<<'EOF'
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Pterodactyl - Daemon
|
||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
const rfr = require('rfr');
|
||||
const _ = require('lodash');
|
||||
|
||||
const Core = rfr('src/services/index.js');
|
||||
|
||||
class Service extends Core {}
|
||||
|
||||
module.exports = Service;
|
||||
EOF;
|
||||
}
|
||||
}
|
|
@ -88,6 +88,17 @@ $factory->define(Pterodactyl\Models\Node::class, function (Faker\Generator $fake
|
|||
];
|
||||
});
|
||||
|
||||
$factory->define(Pterodactyl\Models\Service::class, function (Faker\Generator $faker) {
|
||||
return [
|
||||
'author' => $faker->unique()->uuid,
|
||||
'name' => $faker->word,
|
||||
'description' => null,
|
||||
'folder' => strtolower($faker->unique()->word),
|
||||
'startup' => 'java -jar test.jar',
|
||||
'index_file' => 'indexjs',
|
||||
];
|
||||
});
|
||||
|
||||
$factory->define(Pterodactyl\Models\ServiceOption::class, function (Faker\Generator $faker) {
|
||||
return [
|
||||
'id' => $faker->unique()->randomNumber(),
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CascadeDeletionWhenAParentServiceIsDeleted extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('service_options', function (Blueprint $table) {
|
||||
$table->dropForeign(['service_id']);
|
||||
|
||||
$table->foreign('service_id')->references('id')->on('services')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('service_options', function (Blueprint $table) {
|
||||
$table->dropForeign(['service_id']);
|
||||
|
||||
$table->foreign('service_id')->references('id')->on('services');
|
||||
});
|
||||
}
|
||||
}
|
|
@ -34,6 +34,7 @@ return [
|
|||
'cidr_out_of_range' => 'CIDR notation only allows masks between /25 and /32.',
|
||||
],
|
||||
'service' => [
|
||||
'delete_has_servers' => 'A service with active servers attached to it cannot be deleted from the Panel.',
|
||||
'options' => [
|
||||
'delete_has_servers' => 'A service option with active servers attached to it cannot be deleted from the Panel.',
|
||||
'invalid_copy_id' => 'The service option selected for copying a script from either does not exist, or is copying a script itself.',
|
||||
|
|
|
@ -23,8 +23,15 @@
|
|||
*/
|
||||
|
||||
return [
|
||||
'notices' => [
|
||||
'service_created' => 'A new service, :name, has been successfully created.',
|
||||
'service_deleted' => 'Successfully deleted the requested service from the Panel.',
|
||||
'service_updated' => 'Successfully updated the service configuration options.',
|
||||
'functions_updated' => 'The service functions file has been updated. You will need to reboot your Nodes in order for these changes to be applied.',
|
||||
],
|
||||
'options' => [
|
||||
'notices' => [
|
||||
'option_deleted' => 'Successfully deleted the requested service option from the Panel.',
|
||||
'option_updated' => 'Service option configuration has been updated successfully.',
|
||||
'script_updated' => 'Service option install script has been updated and will run whenever servers are installed.',
|
||||
'option_created' => 'New service option was created successfully. You will need to restart any running daemons to apply this new service.',
|
||||
|
|
|
@ -50,15 +50,14 @@
|
|||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Functions Control</h3>
|
||||
</div>
|
||||
<form action="{{ route('admin.services.view', $service->id) }}" method="POST">
|
||||
<form action="{{ route('admin.services.view.functions', $service->id) }}" method="POST">
|
||||
<div class="box-body no-padding">
|
||||
<div id="editor_index"style="height:500px">{{ $service->index_file }}</div>
|
||||
<textarea name="index_file" class="hidden"></textarea>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{!! csrf_field() !!}
|
||||
<input type="hidden" name="redirect_to" value="functions" />
|
||||
<button type="submit" name="action" value="edit" class="btn btn-sm btn-success pull-right">Save File</button>
|
||||
<button type="submit" name="_method" value="PATCH" class="btn btn-sm btn-success pull-right">Save File</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -166,8 +166,8 @@ Route::group(['prefix' => 'nodes'], function () {
|
|||
Route::group(['prefix' => 'services'], function () {
|
||||
Route::get('/', 'ServiceController@index')->name('admin.services');
|
||||
Route::get('/new', 'ServiceController@create')->name('admin.services.new');
|
||||
Route::get('/view/{id}', 'ServiceController@view')->name('admin.services.view');
|
||||
Route::get('/view/{id}/functions', 'ServiceController@viewFunctions')->name('admin.services.view.functions');
|
||||
Route::get('/view/{service}', 'ServiceController@view')->name('admin.services.view');
|
||||
Route::get('/view/{service}/functions', 'ServiceController@viewFunctions')->name('admin.services.view.functions');
|
||||
Route::get('/option/new', 'OptionController@create')->name('admin.services.option.new');
|
||||
Route::get('/option/{option}', 'OptionController@viewConfiguration')->name('admin.services.option.view');
|
||||
Route::get('/option/{option}/variables', 'VariableController@view')->name('admin.services.option.variables');
|
||||
|
@ -177,13 +177,14 @@ Route::group(['prefix' => 'services'], function () {
|
|||
Route::post('/option/new', 'OptionController@store');
|
||||
Route::post('/option/{option}/variables', 'VariableController@store');
|
||||
|
||||
Route::patch('/view/{option}', 'ServiceController@edit');
|
||||
Route::patch('/view/{service}', 'ServiceController@update');
|
||||
Route::patch('/view/{service}/functions', 'ServiceController@updateFunctions');
|
||||
Route::patch('/option/{option}', 'OptionController@editConfiguration');
|
||||
Route::patch('/option/{option}/scripts', 'OptionController@updateScripts');
|
||||
Route::patch('/option/{option}/variables/{variable}', 'VariableController@update')->name('admin.services.option.variables.edit');
|
||||
|
||||
Route::delete('/view/{id}', 'ServiceController@delete');
|
||||
Route::delete('/option/{option}', 'OptionController@delete');
|
||||
Route::delete('/view/{service}', 'ServiceController@destroy');
|
||||
Route::delete('/option/{option}', 'OptionController@destroy');
|
||||
Route::delete('/option/{option}/variables/{variable}', 'VariableController@delete');
|
||||
});
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace Tests\Unit\Services\Services\Options;
|
|||
use Mockery as m;
|
||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
||||
use Pterodactyl\Contracts\Repository\ServiceOptionRepositoryInterface;
|
||||
use Pterodactyl\Exceptions\Services\ServiceOption\HasActiveServersException;
|
||||
use Pterodactyl\Exceptions\Services\HasActiveServersException;
|
||||
use Pterodactyl\Services\Services\Options\OptionDeletionService;
|
||||
use Tests\TestCase;
|
||||
|
||||
|
|
94
tests/Unit/Services/Services/ServiceCreationServiceTest.php
Normal file
94
tests/Unit/Services/Services/ServiceCreationServiceTest.php
Normal file
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
/**
|
||||
* Pterodactyl - Panel
|
||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace Tests\Unit\Services\Services;
|
||||
|
||||
use Mockery as m;
|
||||
use Pterodactyl\Contracts\Repository\ServiceRepositoryInterface;
|
||||
use Pterodactyl\Models\Service;
|
||||
use Pterodactyl\Services\Services\ServiceCreationService;
|
||||
use Pterodactyl\Traits\Services\CreatesServiceIndex;
|
||||
use Tests\TestCase;
|
||||
use Illuminate\Contracts\Config\Repository;
|
||||
|
||||
class ServiceCreationServiceTest extends TestCase
|
||||
{
|
||||
use CreatesServiceIndex;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Config\Repository
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\ServiceRepositoryInterface
|
||||
*/
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Services\ServiceCreationService
|
||||
*/
|
||||
protected $service;
|
||||
|
||||
/**
|
||||
* Setup tests.
|
||||
*/
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->config = m::mock(Repository::class);
|
||||
$this->repository = m::mock(ServiceRepositoryInterface::class);
|
||||
|
||||
$this->service = new ServiceCreationService($this->config, $this->repository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a new service can be created using the correct data.
|
||||
*/
|
||||
public function testCreateNewService()
|
||||
{
|
||||
$model = factory(Service::class)->make();
|
||||
$data = [
|
||||
'name' => $model->name,
|
||||
'description' => $model->description,
|
||||
'folder' => $model->folder,
|
||||
'startup' => $model->startup,
|
||||
];
|
||||
|
||||
$this->config->shouldReceive('get')->with('pterodactyl.service.author')->once()->andReturn('0000-author');
|
||||
$this->repository->shouldReceive('create')->with([
|
||||
'author' => '0000-author',
|
||||
'name' => $data['name'],
|
||||
'description' => $data['description'],
|
||||
'folder' => $data['folder'],
|
||||
'startup' => $data['startup'],
|
||||
'index_file' => $this->getIndexScript(),
|
||||
])->once()->andReturn($model);
|
||||
|
||||
$response = $this->service->handle($data);
|
||||
$this->assertInstanceOf(Service::class, $response);
|
||||
$this->assertEquals($model, $response);
|
||||
}
|
||||
}
|
104
tests/Unit/Services/Services/ServiceDeletionServiceTest.php
Normal file
104
tests/Unit/Services/Services/ServiceDeletionServiceTest.php
Normal file
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
/**
|
||||
* Pterodactyl - Panel
|
||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace Tests\Unit\Services\Services;
|
||||
|
||||
use Exception;
|
||||
use Mockery as m;
|
||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
||||
use Pterodactyl\Contracts\Repository\ServiceRepositoryInterface;
|
||||
use Pterodactyl\Exceptions\Services\HasActiveServersException;
|
||||
use Pterodactyl\Services\Services\ServiceDeletionService;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ServiceDeletionServiceTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
|
||||
*/
|
||||
protected $serverRepository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\ServiceRepositoryInterface
|
||||
*/
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Services\ServiceDeletionService
|
||||
*/
|
||||
protected $service;
|
||||
|
||||
/**
|
||||
* Setup tests.
|
||||
*/
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->serverRepository = m::mock(ServerRepositoryInterface::class);
|
||||
$this->repository = m::mock(ServiceRepositoryInterface::class);
|
||||
|
||||
$this->service = new ServiceDeletionService($this->serverRepository, $this->repository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a service is deleted when there are no servers attached to a service.
|
||||
*/
|
||||
public function testServiceIsDeleted()
|
||||
{
|
||||
$this->serverRepository->shouldReceive('findCountWhere')->with([['service_id', '=', 1]])->once()->andReturn(0);
|
||||
$this->repository->shouldReceive('delete')->with(1)->once()->andReturn(1);
|
||||
|
||||
$this->assertEquals(1, $this->service->handle(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that an exception is thrown when there are servers attached to a service.
|
||||
*
|
||||
* @dataProvider serverCountProvider
|
||||
*/
|
||||
public function testExceptionIsThrownIfServersAreAttached($count)
|
||||
{
|
||||
$this->serverRepository->shouldReceive('findCountWhere')->with([['service_id', '=', 1]])->once()->andReturn($count);
|
||||
|
||||
try {
|
||||
$this->service->handle(1);
|
||||
} catch (Exception $exception) {
|
||||
$this->assertInstanceOf(HasActiveServersException::class, $exception);
|
||||
$this->assertEquals(trans('admin/exceptions.service.delete_has_servers'), $exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide assorted server counts to ensure that an exception is always thrown when more than 0 servers are found.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function serverCountProvider()
|
||||
{
|
||||
return [
|
||||
[1], [2], [5], [10],
|
||||
];
|
||||
}
|
||||
}
|
77
tests/Unit/Services/Services/ServiceUpdateServiceTest.php
Normal file
77
tests/Unit/Services/Services/ServiceUpdateServiceTest.php
Normal file
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
/**
|
||||
* Pterodactyl - Panel
|
||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace Tests\Unit\Services\Services;
|
||||
|
||||
use Mockery as m;
|
||||
use Pterodactyl\Contracts\Repository\ServiceRepositoryInterface;
|
||||
use Pterodactyl\Services\Services\ServiceUpdateService;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ServiceUpdateServiceTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\ServiceRepositoryInterface
|
||||
*/
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Services\ServiceUpdateService
|
||||
*/
|
||||
protected $service;
|
||||
|
||||
/**
|
||||
* Setup tests.
|
||||
*/
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->repository = m::mock(ServiceRepositoryInterface::class);
|
||||
|
||||
$this->service = new ServiceUpdateService($this->repository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the author key is removed from the data array before updating the record.
|
||||
*/
|
||||
public function testAuthorArrayKeyIsRemovedIfPassed()
|
||||
{
|
||||
$this->repository->shouldReceive('withoutFresh')->withNoArgs()->once()->andReturnSelf()
|
||||
->shouldReceive('update')->with(1, ['otherfield' => 'value'])->once()->andReturnNull();
|
||||
|
||||
$this->service->handle(1, ['author' => 'author1', 'otherfield' => 'value']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the function continues to work when no author key is passed.
|
||||
*/
|
||||
public function testServiceIsUpdatedWhenNoAuthorKeyIsPassed()
|
||||
{
|
||||
$this->repository->shouldReceive('withoutFresh')->withNoArgs()->once()->andReturnSelf()
|
||||
->shouldReceive('update')->with(1, ['otherfield' => 'value'])->once()->andReturnNull();
|
||||
|
||||
$this->service->handle(1, ['otherfield' => 'value']);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue