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

@ -32,6 +32,4 @@ QUEUE_HIGH=high
QUEUE_STANDARD=standard
QUEUE_LOW=low
SQS_KEY=aws-public
SQS_SECRET=aws-secret
SQS_QUEUE_PREFIX=aws-queue-prefix
SERVICE_AUTHOR=undefined@unknown-author.com

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']);

View file

@ -18,6 +18,13 @@
@endsection
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="alert alert-danger">
Services are a powerful feature of Pterodactyl Panel that allow for extreme flexibility and configuration. Please note that while powerful, modifing a service wrongly can very easily brick your servers and cause more problems. Please avoid editing our default services those provided by <code>support@pterodactyl.io</code> unless you are absolutely sure of what you are doing.
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="box">
@ -38,7 +45,7 @@
</tr>
@foreach($services as $service)
<tr>
<td class="middle"><a href="{{ route('admin.services.view', $service->id) }}">{{ $service->name }}</a></td>
<td class="middle"><a href="{{ route('admin.services.view', $service->id) }}" data-toggle="tooltip" data-placement="right" title="{{ $service->author }}">{{ $service->name }}</a></td>
<td class="col-xs-6 middle">{{ $service->description }}</td>
<td class="text-center middle"><code>{{ $service->options_count }}</code></td>
<td class="text-center middle"><code>{{ $service->packs_count }}</code></td>

View file

@ -46,13 +46,6 @@
<div class="col-md-6">
<div class="box">
<div class="box-body">
<div class="form-group">
<label class="control-label">Folder Name</label>
<div>
<input type="text" name="folder" class="form-control" value="{{ old('folder') }}" />
<p class="text-muted"><small>Service are downloaded by the daemon and stored in a folder using this name. The storage location is <code>/srv/daemon/services/{NAME}</code> by default.</small></p>
</div>
</div>
<div class="form-group">
<label class="control-label">Default Start Command</label>
<div>

View file

@ -33,7 +33,7 @@
<label for="pServiceId" class="form-label">Associated Service</label>
<select name="service_id" id="pServiceId">
@foreach($services as $service)
<option value="{{ $service->id }}" {{ old('service_id') != $service->id ?: 'selected' }}>{{ $service->name }}</option>
<option value="{{ $service->id }}" {{ old('service_id') != $service->id ?: 'selected' }}>{{ $service->name }} &lt;{{ $service->author }}&gt;</option>
@endforeach
</select>
</div>
@ -51,16 +51,19 @@
<div class="col-sm-6">
<div class="form-group">
<label for="pTag" class="form-label">Option Tag</label>
<input type="text" id="pTag" name="tag" value="{{ old('tag') }}" class="form-control" />
<div class="input-group">
<span class="input-group-addon">{{ config('pterodactyl.service.author') }}:</span>
<input type="text" id="pTag" name="tag" value="{{ old('tag') }}" class="form-control" />
</div>
<p class="text-muted small">This should be a unique identifer for this service option that is not used for any other service options. Must be alpha-numeric and no more than 60 characters in length.</p>
</div>
<div class="form-group">
<label for="pDockerImage" class="form-label">Docker Image</label>
<label for="pDockerImage" class="control-label">Docker Image <span class="field-optional"></span></label>
<input type="text" id="pDockerImage" name="docker_image" value="{{ old('docker_image') }}" placeholder="quay.io/pterodactyl/service" class="form-control" />
<p class="text-muted small">The default docker image that should be used for new servers under this service option. This can be left blank to use the parent service's defined image, and can also be changed per-server.</p>
</div>
<div class="form-group">
<label for="pStartup" class="form-label">Startup Command</label>
<label for="pStartup" class="control-label">Startup Command <span class="field-optional"></span></label>
<textarea id="pStartup" name="startup" class="form-control" rows="4">{{ old('startup') }}</textarea>
<p class="text-muted small">The default statup command that should be used for new servers under this service option. This can be left blank to use the parent service's startup, and can also be changed per-server.</p>
</div>
@ -136,7 +139,7 @@
data: $.map(_.get(Pterodactyl.services, $(this).val() + '.options', []), function (item) {
return {
id: item.id,
text: item.name,
text: item.name + ' <' + item.tag + '>',
};
}),
});

View file

@ -59,17 +59,16 @@
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="pTag" class="form-label">Option Tag</label>
<input type="text" id="pTag" name="tag" value="{{ $option->tag }}" class="form-control" />
<p class="text-muted small">This should be a unique identifer for this service option that is not used for any other service options.</p>
<label class="form-label">Option Tag</label>
<input type="text" disabled value="{{ $option->tag }}" class="form-control" />
</div>
<div class="form-group">
<label for="pDockerImage" class="form-label">Docker Image</label>
<label for="pDockerImage" class="control-label">Docker Image <span class="field-optional"></label>
<input type="text" id="pDockerImage" name="docker_image" value="{{ $option->docker_image }}" class="form-control" />
<p class="text-muted small">The default docker image that should be used for new servers under this service option. This can be left blank to use the parent service's defined image, and can also be changed per-server.</p>
</div>
<div class="form-group">
<label for="pStartup" class="form-label">Startup Command</label>
<label for="pStartup" class="control-label">Startup Command <span class="field-optional"></label>
<textarea id="pStartup" name="startup" class="form-control" rows="4" placeholder="{{ $option->service->startup }}">{{ $option->startup }}</textarea>
<p class="text-muted small">The default statup command that should be used for new servers under this service option. This can be left blank to use the parent service's startup, and can also be changed per-server.</p>
</div>
@ -97,7 +96,7 @@
<select name="config_from" id="pConfigFrom" class="form-control">
<option value="0">None</option>
@foreach($option->service->options as $o)
<option value="{{ $o->id }}" {{ ($option->config_from !== $o->id) ?: 'selected' }}>{{ $o->name }}</option>
<option value="{{ $o->id }}" {{ ($option->config_from !== $o->id) ?: 'selected' }}>{{ $o->name }} &lt;{{ $option->tag }}&gt;</option>
@endforeach
</select>
<p class="text-muted small">If you would like to default to settings from another option select the option from the menu above.</p>

View file

@ -44,22 +44,18 @@
<div class="form-group">
<label class="control-label">Description</label>
<div>
<textarea name="description" class="form-control" rows="6">{{ $service->description }}</textarea>
<textarea name="description" class="form-control" rows="7">{{ $service->description }}</textarea>
</div>
</div>
</div>
<div class="box-footer">
<button id="deleteButton" type="submit" name="_method" value="DELETE" class="btn btn-sm btn-danger muted muted-hover"><i class="fa fa-trash-o"></i></button>
</div>
</div>
</div>
<div class="col-md-6">
<div class="box">
<div class="box-body">
<div class="form-group">
<label class="control-label">Folder Name</label>
<div>
<input type="text" name="folder" class="form-control" value="{{ $service->folder }}" />
<p class="text-muted"><small>Service are downloaded by the daemon and stored in a folder using this name. The storage location is <code>/srv/daemon/services/{NAME}</code> by default.</small></p>
</div>
</div>
<div class="form-group">
<label class="control-label">Default Start Command</label>
<div>
@ -67,10 +63,21 @@
<p class="text-muted"><small>The default start command to use when running options under this service. This command can be modified per-option and should include the executable to be called in the container.</small></p>
</div>
</div>
<div class="form-group">
<label class="control-label">Author</label>
<div>
<input type="text" readonly class="form-control" value="{{ $service->author }}" />
</div>
</div>
<div class="form-group">
<label class="control-label">UUID</label>
<div>
<input type="text" readonly class="form-control" value="{{ $service->uuid }}" />
</div>
</div>
</div>
<div class="box-footer">
{!! csrf_field() !!}
<button id="deleteButton" type="submit" name="_method" value="DELETE" class="btn btn-sm btn-danger muted muted-hover"><i class="fa fa-trash-o"></i></button>
<button type="submit" name="_method" value="PATCH" class="btn btn-primary btn-sm pull-right">Edit Service</button>
</div>
</div>
@ -86,7 +93,7 @@
<div class="box-body table-responsive no-padding">
<table class="table table-hover">
<tr>
<th class="col-sm-4 col-md-3">Name</th>
<th>Name</th>
<th>Description</th>
<th>Tag</th>
<th class="text-center">Servers</th>

View file

@ -197,6 +197,12 @@
});
</script>
@endif
<script>
$(function () {
$('[data-toggle="tooltip"]').tooltip()
})
</script>
@show
</body>
</html>