Remove old admin area (#648)

* Remove old admin

* Remove controller test

* Remove unused exceptions

* Remove unused files

* More small tweaks

* Fix doc block

* Remove unused service

* Restore these

* Add back autoDeploy

* Revert "Add back autoDeploy"

This reverts commit 630c1e08acf8056ce8e612f376fcd00c23d90aea.

* Add these back

* Add back exception

* Remove ApiController again

---------

Co-authored-by: RMartinOscar <40749467+RMartinOscar@users.noreply.github.com>
Co-authored-by: Boy132 <mail@boy132.de>
Co-authored-by: notCharles <charles@pelican.dev>
This commit is contained in:
Lance Pioch 2024-11-13 17:05:48 -05:00 committed by GitHub
parent 9717aa4b5f
commit 6125b07afa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
243 changed files with 41 additions and 20965 deletions

View File

@ -1,7 +0,0 @@
<?php
namespace App\Exceptions;
class AccountNotFoundException extends \Exception
{
}

View File

@ -1,7 +0,0 @@
<?php
namespace App\Exceptions;
class AutoDeploymentException extends \Exception
{
}

View File

@ -10,7 +10,6 @@ use Illuminate\Http\Request;
use Psr\Log\LoggerInterface;
use Illuminate\Http\Response;
use Illuminate\Container\Container;
use Prologue\Alerts\AlertsMessageBag;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
class DisplayException extends PanelException implements HttpExceptionInterface
@ -67,9 +66,6 @@ class DisplayException extends PanelException implements HttpExceptionInterface
return response()->json(Handler::toArray($this), $this->getStatusCode(), $this->getHeaders());
}
// @phpstan-ignore-next-line
app(AlertsMessageBag::class)->danger($this->getMessage())->flash();
return redirect()->back()->withInput();
}

View File

@ -7,9 +7,6 @@ use GuzzleHttp\Exception\GuzzleException;
use App\Exceptions\DisplayException;
use Illuminate\Support\Facades\Context;
/**
* @method \GuzzleHttp\Exception\GuzzleException getPrevious()
*/
class DaemonConnectionException extends DisplayException
{
private int $statusCode = Response::HTTP_GATEWAY_TIMEOUT;

View File

@ -1,9 +0,0 @@
<?php
namespace App\Exceptions\Http\Server;
use App\Exceptions\DisplayException;
class FileTypeNotEditableException extends DisplayException
{
}

View File

@ -1,9 +0,0 @@
<?php
namespace App\Exceptions\Repository\Daemon;
use App\Exceptions\Repository\RepositoryException;
class InvalidPowerSignalException extends RepositoryException
{
}

View File

@ -1,9 +0,0 @@
<?php
namespace App\Exceptions\Repository;
use App\Exceptions\PanelException;
class RepositoryException extends PanelException
{
}

View File

@ -1,9 +0,0 @@
<?php
namespace App\Exceptions\Service\Allocation;
use App\Exceptions\PanelException;
class AllocationDoesNotBelongToServerException extends PanelException
{
}

View File

@ -1,9 +0,0 @@
<?php
namespace App\Exceptions\Service\Egg;
use App\Exceptions\DisplayException;
class BadJsonFormatException extends DisplayException
{
}

View File

@ -1,9 +0,0 @@
<?php
namespace App\Exceptions\Service\Egg;
use App\Exceptions\DisplayException;
class NoParentConfigurationFoundException extends DisplayException
{
}

View File

@ -1,9 +0,0 @@
<?php
namespace App\Exceptions\Service\Schedule\Task;
use App\Exceptions\DisplayException;
class TaskIntervalTooLongException extends DisplayException
{
}

View File

@ -1,9 +0,0 @@
<?php
namespace App\Exceptions\Service\Server;
use App\Exceptions\PanelException;
class RequiredVariableMissingException extends PanelException
{
}

View File

@ -1,9 +0,0 @@
<?php
namespace App\Exceptions\Transformer;
use App\Exceptions\PanelException;
class InvalidTransformerLevelException extends PanelException
{
}

View File

@ -1,13 +0,0 @@
<?php
namespace App\Extensions\Facades;
use Illuminate\Support\Facades\Facade;
class Theme extends Facade
{
protected static function getFacadeAccessor(): string
{
return 'extensions.themes';
}
}

View File

@ -1,21 +0,0 @@
<?php
namespace App\Extensions\Themes;
class Theme
{
public function js(string $path): string
{
return sprintf('<script src="%s"></script>' . PHP_EOL, $this->getUrl($path));
}
public function css(string $path): string
{
return sprintf('<link media="all" type="text/css" rel="stylesheet" href="%s"/>' . PHP_EOL, $this->getUrl($path));
}
protected function getUrl(string $path): string
{
return '/themes/panel/' . ltrim($path, '/');
}
}

View File

@ -1,14 +0,0 @@
<?php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
use App\Services\Activity\ActivityLogBatchService;
class LogBatch extends Facade
{
protected static function getFacadeAccessor(): string
{
return ActivityLogBatchService::class;
}
}

View File

@ -6,7 +6,6 @@ use App\Enums\ContainerStatus;
use App\Enums\ServerState;
use App\Filament\Resources\ServerResource;
use App\Filament\Resources\ServerResource\RelationManagers\AllocationsRelationManager;
use App\Http\Controllers\Admin\ServersController;
use App\Models\Database;
use App\Models\Egg;
use App\Models\Mount;
@ -15,8 +14,10 @@ use App\Models\ServerVariable;
use App\Services\Databases\DatabaseManagementService;
use App\Services\Databases\DatabasePasswordService;
use App\Services\Servers\RandomWordService;
use App\Services\Servers\ReinstallServerService;
use App\Services\Servers\ServerDeletionService;
use App\Services\Servers\SuspensionService;
use App\Services\Servers\ToggleInstallService;
use App\Services\Servers\TransferServerService;
use Closure;
use Exception;
@ -677,8 +678,8 @@ class EditServer extends EditRecord
Action::make('toggleInstall')
->label('Toggle Install Status')
->disabled(fn (Server $server) => $server->isSuspended())
->action(function (ServersController $serversController, Server $server) {
$serversController->toggleInstall($server);
->action(function (ToggleInstallService $service, Server $server) {
$service->handle($server);
$this->refreshFormData(['status', 'docker']);
}),
@ -764,7 +765,7 @@ class EditServer extends EditRecord
->modalHeading('Are you sure you want to reinstall this server?')
->modalDescription('!! This can result in unrecoverable data loss !!')
->disabled(fn (Server $server) => $server->isSuspended())
->action(fn (ServersController $serversController, Server $server) => $serversController->reinstallServer($server)),
->action(fn (ReinstallServerService $service, Server $server) => $service->handle($server)),
])->fullWidth(),
ToggleButtons::make('')
->hint('This will reinstall the server with the assigned egg install script.'),

View File

@ -1,20 +0,0 @@
<?php
namespace App\Helpers;
use Carbon\CarbonImmutable;
final class Time
{
/**
* Gets the time offset from the provided timezone relative to UTC as a number. This
* is used in the database configuration since we can't always rely on there being support
* for named timezones in MySQL.
*
* Returns the timezone as a string like +08:00 or -05:00 depending on the app timezone.
*/
public static function getMySQLTimezoneOffset(string $timezone): string
{
return CarbonImmutable::now($timezone)->getTimezone()->toOffsetName();
}
}

View File

@ -1,91 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
use Illuminate\View\View;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use App\Models\ApiKey;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use App\Services\Acl\Api\AdminAcl;
use App\Http\Controllers\Controller;
use App\Services\Api\KeyCreationService;
use App\Http\Requests\Admin\Api\StoreApplicationApiKeyRequest;
class ApiController extends Controller
{
/**
* ApiController constructor.
*/
public function __construct(
private AlertsMessageBag $alert,
private KeyCreationService $keyCreationService,
) {
}
/**
* Render view showing all of a user's application API keys.
*/
public function index(Request $request): View
{
$keys = $request->user()->apiKeys()
->where('key_type', ApiKey::TYPE_APPLICATION)
->get();
return view('admin.api.index', [
'keys' => $keys,
]);
}
/**
* Render view allowing an admin to create a new application API key.
*
* @throws \ReflectionException
*/
public function create(): View
{
$resources = AdminAcl::getResourceList();
sort($resources);
return view('admin.api.new', [
'resources' => $resources,
'permissions' => [
'r' => AdminAcl::READ,
'rw' => AdminAcl::READ | AdminAcl::WRITE,
'n' => AdminAcl::NONE,
],
]);
}
/**
* Store the new key and redirect the user back to the application key listing.
*
* @throws \App\Exceptions\Model\DataValidationException
*/
public function store(StoreApplicationApiKeyRequest $request): RedirectResponse
{
$this->keyCreationService->setKeyType(ApiKey::TYPE_APPLICATION)->handle([
'memo' => $request->input('memo'),
'user_id' => $request->user()->id,
'permissions' => $request->getKeyPermissions(),
]);
$this->alert->success('A new application API key has been generated for your account.')->flash();
return redirect()->route('admin.api.index');
}
/**
* Delete an application API key from the database.
*/
public function delete(Request $request, string $identifier): Response
{
$request->user()->apiKeys()
->where('key_type', ApiKey::TYPE_APPLICATION)
->where('identifier', $identifier)
->delete();
return response('', 204);
}
}

View File

@ -1,25 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
use Illuminate\View\View;
use App\Http\Controllers\Controller;
use App\Services\Helpers\SoftwareVersionService;
class BaseController extends Controller
{
/**
* BaseController constructor.
*/
public function __construct(private SoftwareVersionService $version)
{
}
/**
* Return the admin index view.
*/
public function index(): View
{
return view('admin.index', ['version' => $this->version]);
}
}

View File

@ -1,126 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Models\Node;
use Illuminate\View\View;
use App\Models\DatabaseHost;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use App\Http\Controllers\Controller;
use App\Services\Databases\Hosts\HostUpdateService;
use App\Http\Requests\Admin\DatabaseHostFormRequest;
use App\Services\Databases\Hosts\HostCreationService;
use App\Services\Databases\Hosts\HostDeletionService;
class DatabaseController extends Controller
{
/**
* DatabaseController constructor.
*/
public function __construct(
private AlertsMessageBag $alert,
private HostCreationService $creationService,
private HostDeletionService $deletionService,
private HostUpdateService $updateService,
) {
}
/**
* Display database host index.
*/
public function index(): View
{
$hosts = DatabaseHost::query()
->withCount('databases')
->with('node')
->get();
return view('admin.databases.index', [
'nodes' => Node::all(),
'hosts' => $hosts,
]);
}
/**
* Display database host to user.
*/
public function view(DatabaseHost $host): View
{
$databases = $host->databases()->with('server')->paginate(25);
return view('admin.databases.view', [
'nodes' => Node::all(),
'host' => $host,
'databases' => $databases,
]);
}
/**
* Handle request to create a new database host.
*
* @throws \Throwable
*/
public function create(DatabaseHostFormRequest $request): RedirectResponse
{
try {
$host = $this->creationService->handle($request->normalize());
} catch (\Exception $exception) {
if ($exception instanceof \PDOException || $exception->getPrevious() instanceof \PDOException) {
$this->alert->danger(
sprintf('There was an error while trying to connect to the host or while executing a query: "%s"', $exception->getMessage())
)->flash();
return redirect()->route('admin.databases')->withInput($request->validated());
} else {
throw $exception;
}
}
$this->alert->success('Successfully created a new database host on the system.')->flash();
return redirect()->route('admin.databases.view', $host->id);
}
/**
* Handle updating database host.
*
* @throws \Throwable
*/
public function update(DatabaseHostFormRequest $request, DatabaseHost $host): RedirectResponse
{
$redirect = redirect()->route('admin.databases.view', $host->id);
try {
$this->updateService->handle($host->id, $request->normalize());
$this->alert->success('Database host was updated successfully.')->flash();
} catch (\Exception $exception) {
// Catch any SQL related exceptions and display them back to the user, otherwise just
// throw the exception like normal and move on with it.
if ($exception instanceof \PDOException || $exception->getPrevious() instanceof \PDOException) {
$this->alert->danger(
sprintf('There was an error while trying to connect to the host or while executing a query: "%s"', $exception->getMessage())
)->flash();
return $redirect->withInput($request->normalize());
} else {
throw $exception;
}
}
return $redirect;
}
/**
* Handle request to delete a database host.
*
* @throws \App\Exceptions\Service\HasActiveServersException
*/
public function delete(int $host): RedirectResponse
{
$this->deletionService->handle($host);
$this->alert->success('The requested database host has been deleted from the system.')->flash();
return redirect()->route('admin.databases');
}
}

View File

@ -1,144 +0,0 @@
<?php
namespace App\Http\Controllers\Admin\Eggs;
use App\Exceptions\Service\Egg\NoParentConfigurationFoundException;
use Illuminate\View\View;
use App\Models\Egg;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use Illuminate\View\Factory as ViewFactory;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\Egg\EggFormRequest;
use Ramsey\Uuid\Uuid;
class EggController extends Controller
{
/**
* EggController constructor.
*/
public function __construct(
protected AlertsMessageBag $alert,
protected ViewFactory $view
) {
}
/**
* Render eggs listing page.
*/
public function index(): View
{
return view('admin.eggs.index', [
'eggs' => Egg::all(),
]);
}
/**
* Handle a request to display the Egg creation page.
*/
public function create(): View
{
$eggs = Egg::all();
\JavaScript::put(['eggs' => $eggs->keyBy('id')]);
return view('admin.eggs.new', ['eggs' => $eggs]);
}
/**
* Handle request to store a new Egg.
*
* @throws \App\Exceptions\Model\DataValidationException
* @throws \App\Exceptions\Service\Egg\NoParentConfigurationFoundException
*/
public function store(EggFormRequest $request): RedirectResponse
{
$data = $request->validated();
$data['docker_images'] = $this->normalizeDockerImages($data['docker_images'] ?? null);
$data['author'] = $request->user()->email;
$data['config_from'] = array_get($data, 'config_from');
if (!is_null($data['config_from'])) {
$parentEgg = Egg::query()->find(array_get($data, 'config_from'));
throw_unless($parentEgg, new NoParentConfigurationFoundException(trans('exceptions.egg.invalid_copy_id')));
}
$egg = Egg::query()->create(array_merge($data, [
'uuid' => Uuid::uuid4()->toString(),
]));
$this->alert->success(trans('admin/eggs.notices.egg_created'))->flash();
return redirect()->route('admin.eggs.view', $egg->id);
}
/**
* Handle request to view a single Egg.
*/
public function view(Egg $egg): View
{
return view('admin.eggs.view', [
'egg' => $egg,
'images' => array_map(
fn ($key, $value) => $key === $value ? $value : "$key|$value",
array_keys($egg->docker_images),
$egg->docker_images,
),
]);
}
/**
* Handle request to update an Egg.
*
* @throws \App\Exceptions\Model\DataValidationException
* @throws \App\Exceptions\Service\Egg\NoParentConfigurationFoundException
*/
public function update(EggFormRequest $request, Egg $egg): RedirectResponse
{
$data = $request->validated();
$data['docker_images'] = $this->normalizeDockerImages($data['docker_images'] ?? null);
$eggId = array_get($data, 'config_from');
$copiedFromEgg = Egg::query()->find($eggId);
throw_unless($copiedFromEgg, new NoParentConfigurationFoundException(trans('exceptions.egg.invalid_copy_id')));
$egg->update($data);
$this->alert->success(trans('admin/eggs.notices.updated'))->flash();
return redirect()->route('admin.eggs.view', $egg->id);
}
/**
* Handle request to destroy an egg.
*
* @throws \App\Exceptions\Service\Egg\HasChildrenException
* @throws \App\Exceptions\Service\HasActiveServersException
*/
public function destroy(Egg $egg): RedirectResponse
{
$egg->delete();
$this->alert->success(trans('admin/eggs.notices.deleted'))->flash();
return redirect()->route('admin.eggs.view', $egg->id);
}
/**
* Normalizes a string of docker image data into the expected egg format.
*/
protected function normalizeDockerImages(?string $input = null): array
{
$data = array_map(fn ($value) => trim($value), explode("\n", $input ?? ''));
$images = [];
// Iterate over the image data provided and convert it into a name => image
// pairing that is used to improve the display on the front-end.
foreach ($data as $value) {
$parts = explode('|', $value, 2);
$images[$parts[0]] = empty($parts[1]) ? $parts[0] : $parts[1];
}
return $images;
}
}

View File

@ -1,61 +0,0 @@
<?php
namespace App\Http\Controllers\Admin\Eggs;
use Illuminate\View\View;
use App\Models\Egg;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use Illuminate\View\Factory as ViewFactory;
use App\Http\Controllers\Controller;
use App\Services\Eggs\Scripts\InstallScriptService;
use App\Http\Requests\Admin\Egg\EggScriptFormRequest;
class EggScriptController extends Controller
{
/**
* EggScriptController constructor.
*/
public function __construct(
protected AlertsMessageBag $alert,
protected InstallScriptService $installScriptService,
protected ViewFactory $view
) {
}
/**
* Handle requests to render installation script for an Egg.
*/
public function index(int $egg): View
{
$egg = Egg::with('scriptFrom', 'configFrom')
->where('id', $egg)
->firstOrFail();
$copy = Egg::query()
->whereNull('copy_script_from')
->whereNot('id', $egg->id)
->firstOrFail();
$rely = Egg::query()->where('copy_script_from', $egg->id)->firstOrFail();
return view('admin.eggs.scripts', [
'copyFromOptions' => $copy,
'relyOnScript' => $rely,
'egg' => $egg,
]);
}
/**
* Handle a request to update the installation script for an Egg.
*
* @throws \App\Exceptions\Model\DataValidationException
*/
public function update(EggScriptFormRequest $request, Egg $egg): RedirectResponse
{
$this->installScriptService->handle($egg, $request->normalize());
$this->alert->success(trans('admin/eggs.notices.script_updated'))->flash();
return redirect()->route('admin.eggs.scripts', $egg);
}
}

View File

@ -1,67 +0,0 @@
<?php
namespace App\Http\Controllers\Admin\Eggs;
use App\Models\Egg;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use App\Http\Controllers\Controller;
use Symfony\Component\HttpFoundation\Response;
use App\Services\Eggs\Sharing\EggExporterService;
use App\Services\Eggs\Sharing\EggImporterService;
use App\Http\Requests\Admin\Egg\EggImportFormRequest;
class EggShareController extends Controller
{
/**
* EggShareController constructor.
*/
public function __construct(
protected AlertsMessageBag $alert,
protected EggExporterService $exporterService,
protected EggImporterService $importerService,
) {
}
public function export(Egg $egg): Response
{
$filename = trim(preg_replace('/\W/', '-', kebab_case($egg->name)), '-');
return response($this->exporterService->handle($egg->id), 200, [
'Content-Transfer-Encoding' => 'binary',
'Content-Description' => 'File Transfer',
'Content-Disposition' => 'attachment; filename=egg-' . $filename . '.json',
'Content-Type' => 'application/json',
]);
}
/**
* Import a new egg using an XML file.
*
* @throws \App\Exceptions\Model\DataValidationException
* @throws \App\Exceptions\Service\Egg\BadJsonFormatException
* @throws \App\Exceptions\Service\InvalidFileUploadException
*/
public function import(EggImportFormRequest $request): RedirectResponse
{
$egg = $this->importerService->fromFile($request->file('import_file'));
$this->alert->success(trans('admin/eggs.notices.imported'))->flash();
return redirect()->route('admin.eggs.view', ['egg' => $egg->id]);
}
/**
* Update an existing Egg using a new imported file.
*
* @throws \App\Exceptions\Model\DataValidationException
* @throws \App\Exceptions\Service\Egg\BadJsonFormatException
* @throws \App\Exceptions\Service\InvalidFileUploadException
*/
public function update(EggImportFormRequest $request, Egg $egg): RedirectResponse
{
$this->importerService->fromFile($request->file('import_file'), $egg);
$this->alert->success(trans('admin/eggs.notices.updated_via_import'))->flash();
return redirect()->route('admin.eggs.view', ['egg' => $egg]);
}
}

View File

@ -1,83 +0,0 @@
<?php
namespace App\Http\Controllers\Admin\Eggs;
use Illuminate\View\View;
use App\Models\Egg;
use App\Models\EggVariable;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use Illuminate\View\Factory as ViewFactory;
use App\Http\Controllers\Controller;
use App\Services\Eggs\Variables\VariableUpdateService;
use App\Http\Requests\Admin\Egg\EggVariableFormRequest;
use App\Services\Eggs\Variables\VariableCreationService;
class EggVariableController extends Controller
{
/**
* EggVariableController constructor.
*/
public function __construct(
protected AlertsMessageBag $alert,
protected VariableCreationService $creationService,
protected VariableUpdateService $updateService,
protected ViewFactory $view
) {
}
/**
* Handle request to view the variables attached to an Egg.
*/
public function view(int $egg): View
{
$egg = Egg::with('variables')->findOrFail($egg);
return view('admin.eggs.variables', ['egg' => $egg]);
}
/**
* Handle a request to create a new Egg variable.
*
* @throws \App\Exceptions\Model\DataValidationException
* @throws \App\Exceptions\Service\Egg\Variable\BadValidationRuleException
* @throws \App\Exceptions\Service\Egg\Variable\ReservedVariableNameException
*/
public function store(EggVariableFormRequest $request, Egg $egg): RedirectResponse
{
$this->creationService->handle($egg->id, $request->normalize());
$this->alert->success(trans('admin/eggs.variables.notices.variable_created'))->flash();
return redirect()->route('admin.eggs.variables', $egg->id);
}
/**
* Handle a request to update an existing Egg variable.
*
* @throws \App\Exceptions\DisplayException
* @throws \App\Exceptions\Model\DataValidationException
* @throws \App\Exceptions\Service\Egg\Variable\ReservedVariableNameException
*/
public function update(EggVariableFormRequest $request, Egg $egg, EggVariable $variable): RedirectResponse
{
$this->updateService->handle($variable, $request->normalize());
$this->alert->success(trans('admin/eggs.variables.notices.variable_updated', [
'variable' => $variable->name,
]))->flash();
return redirect()->route('admin.eggs.variables', $egg->id);
}
/**
* Handle a request to delete an existing Egg variable from the Panel.
*/
public function destroy(int $egg, EggVariable $variable): RedirectResponse
{
$variable->delete();
$this->alert->success(trans('admin/eggs.variables.notices.variable_deleted', [
'variable' => $variable->name,
]))->flash();
return redirect()->route('admin.eggs.variables', $egg);
}
}

View File

@ -1,32 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Models\Node;
use App\Http\Controllers\Controller;
use App\Services\Nodes\NodeAutoDeployService;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
class NodeAutoDeployController extends Controller
{
/**
* NodeAutoDeployController constructor.
*/
public function __construct(
private readonly NodeAutoDeployService $nodeAutoDeployService
) {
}
/**
* Handles the API request and returns the deployment command.
*
* @throws \App\Exceptions\Model\DataValidationException
*/
public function __invoke(Request $request, Node $node): JsonResponse
{
$command = $this->nodeAutoDeployService->handle($request, $node);
return new JsonResponse(['command' => $command]);
}
}

View File

@ -1,26 +0,0 @@
<?php
namespace App\Http\Controllers\Admin\Nodes;
use Illuminate\View\View;
use App\Models\Node;
use Spatie\QueryBuilder\QueryBuilder;
use App\Http\Controllers\Controller;
class NodeController extends Controller
{
/**
* Returns a listing of nodes on the system.
*/
public function index(): View
{
$nodes = QueryBuilder::for(
Node::query()->withCount('servers')
)
->allowedFilters(['uuid', 'name'])
->allowedSorts(['id'])
->paginate(25);
return view('admin.nodes.index', ['nodes' => $nodes]);
}
}

View File

@ -1,101 +0,0 @@
<?php
namespace App\Http\Controllers\Admin\Nodes;
use Illuminate\View\View;
use App\Models\Node;
use Illuminate\Support\Collection;
use App\Models\Allocation;
use App\Http\Controllers\Controller;
use App\Traits\Controllers\JavascriptInjection;
use App\Services\Helpers\SoftwareVersionService;
class NodeViewController extends Controller
{
use JavascriptInjection;
public const THRESHOLD_PERCENTAGE_LOW = 75;
public const THRESHOLD_PERCENTAGE_MEDIUM = 90;
/**
* NodeViewController constructor.
*/
public function __construct(
private SoftwareVersionService $versionService,
) {
}
/**
* Returns index view for a specific node on the system.
*/
public function index(Node $node): View
{
$node->loadCount('servers');
return view('admin.nodes.view.index', [
'node' => $node,
'version' => $this->versionService,
]);
}
/**
* Returns the settings page for a specific node.
*/
public function settings(Node $node): View
{
return view('admin.nodes.view.settings', [
'node' => $node,
]);
}
/**
* Return the node configuration page for a specific node.
*/
public function configuration(Node $node): View
{
return view('admin.nodes.view.configuration', compact('node'));
}
/**
* Return the node allocation management page.
*/
public function allocations(Node $node): View
{
$node->setRelation(
'allocations',
$node->allocations()
->orderByRaw('server_id IS NOT NULL DESC, server_id IS NULL')
->orderByRaw('INET_ATON(ip) ASC')
->orderBy('port')
->with('server:id,name')
->paginate(50)
);
$this->plainInject(['node' => Collection::wrap($node)->only(['id'])]);
return view('admin.nodes.view.allocation', [
'node' => $node,
'allocations' => Allocation::query()->where('node_id', $node->id)
->groupBy('ip')
->orderByRaw('INET_ATON(ip) ASC')
->get(['ip']),
]);
}
/**
* Return a listing of servers that exist for this specific node.
*/
public function servers(Node $node): View
{
$this->plainInject([
'node' => Collection::wrap($node->makeVisible(['daemon_token_id', 'daemon_token']))
->only(['scheme', 'fqdn', 'daemon_listen', 'daemon_token_id', 'daemon_token']),
]);
return view('admin.nodes.view.servers', [
'node' => $node,
'servers' => $node->servers()->with(['user', 'egg'])->paginate(25),
]);
}
}

View File

@ -1,40 +0,0 @@
<?php
namespace App\Http\Controllers\Admin\Nodes;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use App\Models\Node;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Repositories\Daemon\DaemonConfigurationRepository;
class SystemInformationController extends Controller
{
/**
* SystemInformationController constructor.
*/
public function __construct(private DaemonConfigurationRepository $repository)
{
}
/**
* Returns system information from the Daemon.
*
* @throws \App\Exceptions\Http\Connection\DaemonConnectionException
*/
public function __invoke(Request $request, Node $node): JsonResponse
{
$data = $this->repository->setNode($node)->getSystemInformation();
return new JsonResponse([
'version' => $data['version'] ?? '',
'system' => [
'type' => Str::title($data['os'] ?? 'Unknown'),
'arch' => $data['architecture'] ?? '--',
'release' => $data['kernel_version'] ?? '--',
'cpus' => $data['cpu_count'] ?? 0,
],
]);
}
}

View File

@ -1,165 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
use Illuminate\View\View;
use Illuminate\Http\Request;
use App\Models\Node;
use Illuminate\Http\Response;
use App\Models\Allocation;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use Illuminate\View\Factory as ViewFactory;
use App\Http\Controllers\Controller;
use App\Services\Nodes\NodeUpdateService;
use Illuminate\Cache\Repository as CacheRepository;
use App\Services\Nodes\NodeCreationService;
use App\Services\Nodes\NodeDeletionService;
use App\Services\Allocations\AssignmentService;
use App\Services\Helpers\SoftwareVersionService;
use App\Http\Requests\Admin\Node\NodeFormRequest;
use App\Http\Requests\Admin\Node\AllocationFormRequest;
use App\Http\Requests\Admin\Node\AllocationAliasFormRequest;
class NodesController extends Controller
{
/**
* NodesController constructor.
*/
public function __construct(
protected AlertsMessageBag $alert,
protected AssignmentService $assignmentService,
protected CacheRepository $cache,
protected NodeCreationService $creationService,
protected NodeDeletionService $deletionService,
protected NodeUpdateService $updateService,
protected SoftwareVersionService $versionService,
protected ViewFactory $view
) {
}
/**
* Displays create new node page.
*/
public function create(): View|RedirectResponse
{
return view('admin.nodes.new');
}
/**
* Post controller to create a new node on the system.
*
* @throws \App\Exceptions\Model\DataValidationException
*/
public function store(NodeFormRequest $request): RedirectResponse
{
$node = $this->creationService->handle($request->normalize());
$this->alert->info(trans('admin/node.notices.node_created'))->flash();
return redirect()->route('admin.nodes.view.allocation', $node->id);
}
/**
* Updates settings for a node.
*
* @throws \App\Exceptions\DisplayException
* @throws \App\Exceptions\Model\DataValidationException
*/
public function updateSettings(NodeFormRequest $request, Node $node): RedirectResponse
{
$this->updateService->handle($node, $request->normalize(), $request->input('reset_secret') === 'on');
$this->alert->success(trans('admin/node.notices.node_updated'))->flash();
return redirect()->route('admin.nodes.view.settings', $node->id)->withInput();
}
/**
* Removes a single allocation from a node.
*
* @throws \App\Exceptions\Service\Allocation\ServerUsingAllocationException
*/
public function allocationRemoveSingle(int $node, Allocation $allocation): Response
{
$allocation->delete();
return response('', 204);
}
/**
* Removes multiple individual allocations from a node.
*
* @throws \App\Exceptions\Service\Allocation\ServerUsingAllocationException
*/
public function allocationRemoveMultiple(Request $request, int $node): Response
{
$allocations = $request->input('allocations');
foreach ($allocations as $rawAllocation) {
$allocation = new Allocation();
$allocation->id = $rawAllocation['id'];
$this->allocationRemoveSingle($node, $allocation);
}
return response('', 204);
}
/**
* Remove all allocations for a specific IP at once on a node.
*/
public function allocationRemoveBlock(Request $request, int $node): RedirectResponse
{
/** @var Node $node */
$node = Node::query()->findOrFail($node);
$node->allocations()
->where('ip', $request->input('ip'))
->whereNull('server_id')
->delete();
$this->alert->success(trans('admin/node.notices.unallocated_deleted', ['ip' => $request->input('ip')]))
->flash();
return redirect()->route('admin.nodes.view.allocation', $node);
}
/**
* Sets an alias for a specific allocation on a node.
*
* @throws \App\Exceptions\Model\DataValidationException
*/
public function allocationSetAlias(AllocationAliasFormRequest $request): \Symfony\Component\HttpFoundation\Response
{
$allocation = Allocation::query()->findOrFail($request->input('allocation_id'));
$alias = (empty($request->input('alias'))) ? null : $request->input('alias');
$allocation->update(['ip_alias' => $alias]);
return response('', 204);
}
/**
* Creates new allocations on a node.
*
* @throws \App\Exceptions\Service\Allocation\CidrOutOfRangeException
* @throws \App\Exceptions\Service\Allocation\InvalidPortMappingException
* @throws \App\Exceptions\Service\Allocation\PortOutOfRangeException
* @throws \App\Exceptions\Service\Allocation\TooManyPortsInRangeException
*/
public function createAllocation(AllocationFormRequest $request, Node $node): RedirectResponse
{
$this->assignmentService->handle($node, $request->normalize());
$this->alert->success(trans('admin/node.notices.allocations_added'))->flash();
return redirect()->route('admin.nodes.view.allocation', $node->id);
}
/**
* Deletes a node from the system.
*
* @throws \App\Exceptions\DisplayException
*/
public function delete(int|Node $node): RedirectResponse
{
$this->deletionService->handle($node);
$this->alert->success(trans('admin/node.notices.node_deleted'))->flash();
return redirect()->route('admin.nodes');
}
}

View File

@ -1,72 +0,0 @@
<?php
namespace App\Http\Controllers\Admin\Servers;
use App\Models\Egg;
use Illuminate\View\View;
use App\Models\Node;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\ServerFormRequest;
use App\Services\Servers\ServerCreationService;
class CreateServerController extends Controller
{
/**
* CreateServerController constructor.
*/
public function __construct(
private AlertsMessageBag $alert,
private ServerCreationService $creationService,
) {
}
/**
* Displays the create server page.
*/
public function index(): View|RedirectResponse
{
$nodes = Node::all();
if (count($nodes) < 1) {
$this->alert->warning(trans('admin/server.alerts.node_required'))->flash();
return redirect()->route('admin.nodes');
}
$eggs = Egg::with('variables')->get();
\JavaScript::put([
'nodeData' => Node::getForServerCreation(),
'eggs' => $eggs->keyBy('id'),
]);
return view('admin.servers.new', [
'eggs' => $eggs,
'nodes' => Node::all(),
]);
}
/**
* Create a new server on the remote system.
*
* @throws \Illuminate\Validation\ValidationException
* @throws \App\Exceptions\DisplayException
* @throws \App\Exceptions\Service\Deployment\NoViableAllocationException
* @throws \Throwable
*/
public function store(ServerFormRequest $request): RedirectResponse
{
$data = $request->except(['_token']);
if (!empty($data['custom_image'])) {
$data['image'] = $data['custom_image'];
unset($data['custom_image']);
}
$server = $this->creationService->handle($data);
$this->alert->success(trans('admin/server.alerts.server_created'))->flash();
return new RedirectResponse('/admin/servers/view/' . $server->id);
}
}

View File

@ -1,29 +0,0 @@
<?php
namespace App\Http\Controllers\Admin\Servers;
use Illuminate\View\View;
use App\Models\Server;
use Spatie\QueryBuilder\QueryBuilder;
use Spatie\QueryBuilder\AllowedFilter;
use App\Http\Controllers\Controller;
use App\Models\Filters\AdminServerFilter;
class ServerController extends Controller
{
/**
* Returns all the servers that exist on the system using a paginated result set. If
* a query is passed along in the request it is also passed to the repository function.
*/
public function index(): View
{
$servers = QueryBuilder::for(Server::query()->with('node', 'user', 'allocation'))
->allowedFilters([
AllowedFilter::exact('owner_id'),
AllowedFilter::custom('*', new AdminServerFilter()),
])
->paginate(config()->get('panel.paginate.admin.servers'));
return view('admin.servers.index', ['servers' => $servers]);
}
}

View File

@ -1,44 +0,0 @@
<?php
namespace App\Http\Controllers\Admin\Servers;
use App\Http\Controllers\Controller;
use App\Models\Server;
use App\Services\Servers\TransferServerService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Prologue\Alerts\AlertsMessageBag;
class ServerTransferController extends Controller
{
/**
* ServerTransferController constructor.
*/
public function __construct(
private AlertsMessageBag $alert,
private TransferServerService $transferServerService,
) {
}
/**
* Starts a transfer of a server to a new node.
*
* @throws \Throwable
*/
public function transfer(Request $request, Server $server): RedirectResponse
{
$validatedData = $request->validate([
'node_id' => 'required|exists:nodes,id',
'allocation_id' => 'required|bail|unique:servers|exists:allocations,id',
'allocation_additional' => 'nullable',
]);
if ($this->transferServerService->handle($server, $validatedData)) {
$this->alert->success(trans('admin/server.alerts.transfer_started'))->flash();
} else {
$this->alert->danger(trans('admin/server.alerts.transfer_not_viable'))->flash();
}
return redirect()->route('admin.servers.view.manage', $server->id);
}
}

View File

@ -1,142 +0,0 @@
<?php
namespace App\Http\Controllers\Admin\Servers;
use App\Enums\ServerState;
use App\Models\DatabaseHost;
use App\Models\Egg;
use App\Models\Mount;
use App\Models\Node;
use Illuminate\View\View;
use App\Models\Server;
use App\Exceptions\DisplayException;
use App\Http\Controllers\Controller;
use App\Services\Servers\EnvironmentService;
use App\Traits\Controllers\JavascriptInjection;
class ServerViewController extends Controller
{
use JavascriptInjection;
/**
* ServerViewController constructor.
*/
public function __construct(
private readonly EnvironmentService $environmentService,
) {
}
/**
* Returns the index view for a server.
*/
public function index(Server $server): View
{
return view('admin.servers.view.index', compact('server'));
}
/**
* Returns the server details page.
*/
public function details(Server $server): View
{
return view('admin.servers.view.details', compact('server'));
}
/**
* Returns a view of server build settings.
*/
public function build(Server $server): View
{
$allocations = $server->node->allocations->toBase();
return view('admin.servers.view.build', [
'server' => $server,
'assigned' => $allocations->where('server_id', $server->id)->sortBy('port')->sortBy('ip'),
'unassigned' => $allocations->where('server_id', null)->sortBy('port')->sortBy('ip'),
]);
}
/**
* Returns the server startup management page.
*/
public function startup(Server $server): View
{
$variables = $this->environmentService->handle($server);
$eggs = Egg::all()->keyBy('id');
$this->plainInject([
'server' => $server,
'server_variables' => $variables,
'eggs' => $eggs,
]);
return view('admin.servers.view.startup', compact('server', 'eggs'));
}
/**
* Returns all the databases that exist for the server.
*/
public function database(Server $server): View
{
return view('admin.servers.view.database', [
'hosts' => DatabaseHost::all(),
'server' => $server,
]);
}
/**
* Returns all the mounts that exist for the server.
*/
public function mounts(Server $server): View
{
$server->load('mounts');
$mounts = Mount::query()
->whereHas('eggs', fn ($q) => $q->where('id', $server->egg_id))
->whereHas('nodes', fn ($q) => $q->where('id', $server->node_id))
->get();
return view('admin.servers.view.mounts', [
'mounts' => $mounts,
'server' => $server,
]);
}
/**
* Returns the base server management page, or an exception if the server
* is in a state that cannot be recovered from.
*
* @throws \App\Exceptions\DisplayException
*/
public function manage(Server $server): View
{
if ($server->status === ServerState::InstallFailed) {
throw new DisplayException('This server is in a failed install state and cannot be recovered. Please delete and re-create the server.');
}
// Check if the panel doesn't have at least 2 nodes configured.
$nodeCount = Node::query()->count();
$canTransfer = false;
if ($nodeCount >= 2) {
$canTransfer = true;
}
\JavaScript::put([
'nodeData' => Node::getForServerCreation(),
]);
return view('admin.servers.view.manage', [
'nodes' => Node::all(),
'server' => $server,
'canTransfer' => $canTransfer,
]);
}
/**
* Returns the server deletion page.
*/
public function delete(Server $server): View
{
return view('admin.servers.view.delete', compact('server'));
}
}

View File

@ -1,254 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Enums\ServerState;
use Filament\Notifications\Notification;
use Illuminate\Http\Request;
use App\Models\User;
use Illuminate\Http\Response;
use App\Models\Mount;
use App\Models\Server;
use App\Models\Database;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use App\Exceptions\DisplayException;
use App\Http\Controllers\Controller;
use Illuminate\Validation\ValidationException;
use App\Services\Servers\SuspensionService;
use App\Services\Servers\ServerDeletionService;
use App\Services\Servers\ReinstallServerService;
use App\Exceptions\Model\DataValidationException;
use App\Repositories\Daemon\DaemonServerRepository;
use App\Services\Servers\BuildModificationService;
use App\Services\Databases\DatabasePasswordService;
use App\Services\Servers\DetailsModificationService;
use App\Services\Servers\StartupModificationService;
use App\Services\Databases\DatabaseManagementService;
use App\Services\Servers\ServerConfigurationStructureService;
use App\Http\Requests\Admin\Servers\Databases\StoreServerDatabaseRequest;
class ServersController extends Controller
{
/**
* ServersController constructor.
*/
public function __construct(
protected AlertsMessageBag $alert,
protected BuildModificationService $buildModificationService,
protected DaemonServerRepository $daemonServerRepository,
protected DatabaseManagementService $databaseManagementService,
protected DatabasePasswordService $databasePasswordService,
protected ServerDeletionService $deletionService,
protected DetailsModificationService $detailsModificationService,
protected ReinstallServerService $reinstallService,
protected ServerConfigurationStructureService $serverConfigurationStructureService,
protected StartupModificationService $startupModificationService,
protected SuspensionService $suspensionService
) {
}
/**
* Update the details for a server.
*
* @throws \App\Exceptions\Model\DataValidationException
*/
public function setDetails(Request $request, Server $server): RedirectResponse
{
$this->detailsModificationService->handle($server, $request->only([
'owner_id', 'external_id', 'name', 'description',
]));
$this->alert->success(trans('admin/server.alerts.details_updated'))->flash();
return redirect()->route('admin.servers.view.details', $server->id);
}
/**
* Toggles the installation status for a server.
*
* @throws \App\Exceptions\DisplayException
* @throws \App\Exceptions\Model\DataValidationException
*/
public function toggleInstall(Server $server): void
{
if ($server->status === ServerState::InstallFailed) {
throw new DisplayException(trans('admin/server.exceptions.marked_as_failed'));
}
$server->status = $server->isInstalled() ? ServerState::Installing : null;
$server->save();
Notification::make()
->title('Success!')
->body(trans('admin/server.alerts.install_toggled'))
->success()
->send();
}
/**
* Reinstalls the server with the currently assigned service.
*
* @throws \App\Exceptions\DisplayException
* @throws \App\Exceptions\Model\DataValidationException
*/
public function reinstallServer(Server $server): void
{
$this->reinstallService->handle($server);
Notification::make()
->title('Success!')
->body(trans('admin/server.alerts.server_reinstalled'))
->success()
->send();
}
/**
* Manage the suspension status for a server.
*
* @throws \App\Exceptions\DisplayException
* @throws \App\Exceptions\Model\DataValidationException
*/
public function manageSuspension(Request $request, Server $server): RedirectResponse
{
$this->suspensionService->toggle($server, $request->input('action'));
$this->alert->success(trans('admin/server.alerts.suspension_toggled', [
'status' => $request->input('action') . 'ed',
]))->flash();
return redirect()->route('admin.servers.view.manage', $server->id);
}
/**
* Update the build configuration for a server.
*
* @throws \App\Exceptions\DisplayException
* @throws \Illuminate\Validation\ValidationException
*/
public function updateBuild(Request $request, Server $server): RedirectResponse
{
try {
$this->buildModificationService->handle($server, $request->only([
'allocation_id', 'add_allocations', 'remove_allocations',
'memory', 'swap', 'io', 'cpu', 'threads', 'disk',
'database_limit', 'allocation_limit', 'backup_limit', 'oom_killer',
]));
} catch (DataValidationException $exception) {
throw new ValidationException($exception->getValidator());
}
$this->alert->success(trans('admin/server.alerts.build_updated'))->flash();
return redirect()->route('admin.servers.view.build', $server->id);
}
/**
* Start the server deletion process.
*
* @throws \App\Exceptions\DisplayException
* @throws \Throwable
*/
public function delete(Request $request, Server $server): RedirectResponse
{
$this->deletionService->withForce($request->filled('force_delete'))->handle($server);
$this->alert->success(trans('admin/server.alerts.server_deleted'))->flash();
return redirect()->route('admin.servers');
}
/**
* Update the startup command as well as variables.
*
* @throws \Illuminate\Validation\ValidationException
*/
public function saveStartup(Request $request, Server $server): RedirectResponse
{
$data = $request->except('_token');
if (!empty($data['custom_docker_image'])) {
$data['docker_image'] = $data['custom_docker_image'];
unset($data['custom_docker_image']);
}
try {
$this->startupModificationService
->setUserLevel(User::USER_LEVEL_ADMIN)
->handle($server, $data);
} catch (DataValidationException $exception) {
throw new ValidationException($exception->getValidator());
}
$this->alert->success(trans('admin/server.alerts.startup_changed'))->flash();
return redirect()->route('admin.servers.view.startup', $server->id);
}
/**
* Creates a new database assigned to a specific server.
*
* @throws \Throwable
*/
public function newDatabase(StoreServerDatabaseRequest $request, Server $server): RedirectResponse
{
$this->databaseManagementService->create($server, [
'database' => DatabaseManagementService::generateUniqueDatabaseName($request->input('database'), $server->id),
'remote' => $request->input('remote'),
'database_host_id' => $request->input('database_host_id'),
'max_connections' => $request->input('max_connections'),
]);
return redirect()->route('admin.servers.view.database', $server->id)->withInput();
}
/**
* Resets the database password for a specific database on this server.
*
* @throws \Throwable
*/
public function resetDatabasePassword(Request $request, Server $server): Response
{
/** @var \App\Models\Database $database */
$database = $server->databases()->findOrFail($request->input('database'));
$this->databasePasswordService->handle($database);
return response('', 204);
}
/**
* Deletes a database from a server.
*
* @throws \Exception
*/
public function deleteDatabase(Server $server, Database $database): Response
{
$this->databaseManagementService->delete($database);
return response('', 204);
}
/**
* Add a mount to a server.
*
* @throws \Throwable
*/
public function addMount(Request $request, Server $server): RedirectResponse
{
$server->mounts()->attach($request->input('mount_id'));
$this->alert->success('Mount was added successfully.')->flash();
return redirect()->route('admin.servers.view.mounts', $server->id);
}
/**
* Remove a mount from a server.
*/
public function deleteMount(Server $server, Mount $mount): RedirectResponse
{
$server->mounts()->detach($mount);
$this->alert->success('Mount was removed successfully.')->flash();
return redirect()->route('admin.servers.view.mounts', $server->id);
}
}

View File

@ -1,146 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
use Illuminate\Http\JsonResponse;
use Illuminate\View\View;
use Illuminate\Http\Request;
use App\Models\User;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use Spatie\QueryBuilder\QueryBuilder;
use Illuminate\View\Factory as ViewFactory;
use App\Http\Controllers\Controller;
use Illuminate\Contracts\Translation\Translator;
use App\Services\Users\UserUpdateService;
use App\Traits\Helpers\AvailableLanguages;
use App\Services\Users\UserCreationService;
use App\Http\Requests\Admin\UserFormRequest;
use App\Http\Requests\Admin\NewUserFormRequest;
class UserController extends Controller
{
use AvailableLanguages;
/**
* UserController constructor.
*/
public function __construct(
protected AlertsMessageBag $alert,
protected UserCreationService $creationService,
protected Translator $translator,
protected UserUpdateService $updateService,
protected ViewFactory $view
) {
}
/**
* Display user index page.
*/
public function index(): View
{
$users = QueryBuilder::for(
User::query()->select('users.*')
->selectRaw('COUNT(DISTINCT(subusers.id)) as subuser_of_count')
->selectRaw('COUNT(DISTINCT(servers.id)) as servers_count')
->leftJoin('subusers', 'subusers.user_id', '=', 'users.id')
->leftJoin('servers', 'servers.owner_id', '=', 'users.id')
->groupBy('users.id')
)
->allowedFilters(['username', 'email', 'uuid'])
->allowedSorts(['id', 'uuid'])
->paginate(50);
return view('admin.users.index', ['users' => $users]);
}
/**
* Display new user page.
*/
public function create(): View
{
return view('admin.users.new', [
'languages' => $this->getAvailableLanguages(),
]);
}
/**
* Display user view page.
*/
public function view(User $user): View
{
return view('admin.users.view', [
'user' => $user,
'languages' => $this->getAvailableLanguages(),
]);
}
/**
* Delete a user from the system.
*
* @throws \Exception
* @throws \App\Exceptions\DisplayException
*/
public function delete(User $user): RedirectResponse
{
$user->delete();
return redirect()->route('admin.users');
}
/**
* Create a user.
*
* @throws \Exception
* @throws \Throwable
*/
public function store(NewUserFormRequest $request): RedirectResponse
{
$user = $this->creationService->handle($request->normalize());
$this->alert->success($this->translator->get('admin/user.notices.account_created'))->flash();
return redirect()->route('admin.users.view', $user->id);
}
/**
* Update a user on the system.
*
* @throws \App\Exceptions\Model\DataValidationException
*/
public function update(UserFormRequest $request, User $user): RedirectResponse
{
$this->updateService
->setUserLevel(User::USER_LEVEL_ADMIN)
->handle($user, $request->normalize());
$this->alert->success(trans('admin/user.notices.account_updated'))->flash();
return redirect()->route('admin.users.view', $user->id);
}
/**
* Get a JSON response of users on the system.
*/
public function json(Request $request): JsonResponse
{
// Handle single user requests | TODO: Separate this out into its own method
if ($userId = $request->query('user_id')) {
$user = User::query()->findOrFail($userId);
$user['md5'] = md5(strtolower($user->email));
return response()->json($user);
}
// Handle all users list
$userPaginator = QueryBuilder::for(User::query())->allowedFilters(['email'])->paginate(25);
/** @var User[] $users */
$users = $userPaginator->items();
return response()->json(collect($users)->map(function (User $user) {
$user['md5'] = md5(strtolower($user->email));
return $user;
}));
}
}

View File

@ -1,48 +0,0 @@
<?php
namespace App\Http\Controllers\Api\Remote;
use App\Models\Server;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Services\Servers\EnvironmentService;
class EggInstallController extends Controller
{
/**
* EggInstallController constructor.
*/
public function __construct(private EnvironmentService $environment)
{
}
/**
* Handle request to get script and installation information for a server
* that is being created on the node.
*/
public function index(Request $request, string $uuid): JsonResponse
{
$node = $request->attributes->get('node');
$server = Server::query()
->with('egg.scriptFrom')
->where('uuid', $uuid)
->where('node_id', $node->id)
->firstOrFail();
$egg = $server->egg;
return response()->json([
'scripts' => [
'install' => !$egg->copy_script_install ? null : str_replace(["\r\n", "\n", "\r"], "\n", $egg->copy_script_install),
'privileged' => $egg->script_is_privileged,
],
'config' => [
'container' => $egg->copy_script_container,
'entry' => $egg->copy_script_entry,
],
'env' => $this->environment->handle($server),
]);
}
}

View File

@ -1,31 +0,0 @@
<?php
namespace App\Http\Middleware\Admin\Servers;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use App\Models\Server;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class ServerInstalled
{
/**
* Checks that the server is installed before allowing access through the route.
*/
public function handle(Request $request, \Closure $next): mixed
{
/** @var \App\Models\Server|null $server */
$server = $request->route()->parameter('server');
if (!$server instanceof Server) {
throw new NotFoundHttpException('No server resource was located in the request parameters.');
}
if (!$server->isInstalled()) {
throw new HttpException(Response::HTTP_FORBIDDEN, 'Access to this resource is not allowed due to the current installation state.');
}
return $next($request);
}
}

View File

@ -1,23 +0,0 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
class AdminAuthenticate
{
/**
* Handle an incoming request.
*
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
*/
public function handle(Request $request, \Closure $next): mixed
{
if (!$request->user() || !$request->user()->isRootAdmin()) {
throw new AccessDeniedHttpException();
}
return $next($request);
}
}

View File

@ -4,7 +4,6 @@ namespace App\Http\Middleware;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use Prologue\Alerts\AlertsMessageBag;
use App\Exceptions\Http\TwoFactorAuthRequiredException;
class RequireTwoFactorAuthentication
@ -20,13 +19,6 @@ class RequireTwoFactorAuthentication
*/
protected string $redirectRoute = '/account';
/**
* RequireTwoFactorAuthentication constructor.
*/
public function __construct(private AlertsMessageBag $alert)
{
}
/**
* Check the user state on the incoming request to determine if they should be allowed to
* proceed or not. This checks if the Panel is configured to require 2FA on an account in
@ -62,8 +54,6 @@ class RequireTwoFactorAuthentication
throw new TwoFactorAuthRequiredException();
}
$this->alert->danger(trans('auth.2fa_must_be_enabled'))->flash();
return redirect()->to($this->redirectRoute);
}
}

View File

@ -1,35 +0,0 @@
<?php
namespace App\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
abstract class AdminFormRequest extends FormRequest
{
/**
* The rules to apply to the incoming form request.
*/
abstract public function rules(): array;
/**
* Determine if the user is an admin and has permission to access this
* form controller in the first place.
*/
public function authorize(): bool
{
if (is_null($this->user())) {
return false;
}
return $this->user()->isRootAdmin();
}
/**
* Return only the fields that we are interested in from the request.
* This will include empty fields as a null value.
*/
public function normalize(?array $only = null): array
{
return $this->only($only ?? array_keys($this->rules()));
}
}

View File

@ -1,39 +0,0 @@
<?php
namespace App\Http\Requests\Admin\Api;
use App\Models\ApiKey;
use App\Http\Requests\Admin\AdminFormRequest;
class StoreApplicationApiKeyRequest extends AdminFormRequest
{
/**
* @throws \ReflectionException
* @throws \ReflectionException
*/
public function rules(): array
{
$modelRules = ApiKey::getRules();
$rules = [
'memo' => $modelRules['memo'],
'permissions' => $modelRules['permissions'],
];
return $rules;
}
public function attributes(): array
{
return [
'memo' => 'Description',
];
}
public function getKeyPermissions(): array
{
$data = $this->validated();
return array_keys($data['permissions']);
}
}

View File

@ -1,13 +0,0 @@
<?php
namespace App\Http\Requests\Admin;
class BaseFormRequest extends AdminFormRequest
{
public function rules(): array
{
return [
'company' => 'required|between:1,256',
];
}
}

View File

@ -1,30 +0,0 @@
<?php
namespace App\Http\Requests\Admin;
use App\Models\DatabaseHost;
use Illuminate\Contracts\Validation\Validator;
class DatabaseHostFormRequest extends AdminFormRequest
{
public function rules(): array
{
if ($this->method() !== 'POST') {
return DatabaseHost::getRulesForUpdate($this->route()->parameter('host'));
}
return DatabaseHost::getRules();
}
/**
* Modify submitted data before it is passed off to the validator.
*/
protected function getValidatorInstance(): Validator
{
if (!$this->filled('node_id')) {
$this->merge(['node_id' => null]);
}
return parent::getValidatorInstance();
}
}

View File

@ -1,44 +0,0 @@
<?php
namespace App\Http\Requests\Admin\Egg;
use App\Http\Requests\Admin\AdminFormRequest;
use Illuminate\Validation\Validator;
class EggFormRequest extends AdminFormRequest
{
public function rules(): array
{
$rules = [
'name' => 'required|string|max:255',
'description' => 'nullable|string',
'docker_images' => 'required|string',
'force_outgoing_ip' => 'sometimes|boolean',
'file_denylist' => 'array',
'startup' => 'required|string',
'config_from' => 'sometimes|bail|nullable|numeric',
'config_stop' => 'required_without:config_from|nullable|string|max:255',
'config_startup' => 'required_without:config_from|nullable|json',
'config_logs' => 'required_without:config_from|nullable|json',
'config_files' => 'required_without:config_from|nullable|json',
];
return $rules;
}
public function withValidator(Validator $validator): void
{
$validator->sometimes('config_from', 'exists:eggs,id', function () {
return (int) $this->input('config_from') !== 0;
});
}
public function validated($key = null, $default = null): array
{
$data = parent::validated();
return array_merge($data, [
'force_outgoing_ip' => array_get($data, 'force_outgoing_ip', false),
]);
}
}

View File

@ -1,15 +0,0 @@
<?php
namespace App\Http\Requests\Admin\Egg;
use App\Http\Requests\Admin\AdminFormRequest;
class EggImportFormRequest extends AdminFormRequest
{
public function rules(): array
{
return [
'import_file' => 'bail|required|file|max:1000|mimetypes:application/json,text/plain',
];
}
}

View File

@ -1,22 +0,0 @@
<?php
namespace App\Http\Requests\Admin\Egg;
use App\Http\Requests\Admin\AdminFormRequest;
class EggScriptFormRequest extends AdminFormRequest
{
/**
* Return the rules to be used when validating the data sent in the request.
*/
public function rules(): array
{
return [
'script_install' => 'sometimes|nullable|string',
'script_is_privileged' => 'sometimes|required|boolean',
'script_entry' => 'sometimes|required|string',
'script_container' => 'sometimes|required|string',
'copy_script_from' => 'sometimes|nullable|numeric',
];
}
}

View File

@ -1,24 +0,0 @@
<?php
namespace App\Http\Requests\Admin\Egg;
use App\Models\EggVariable;
use App\Http\Requests\Admin\AdminFormRequest;
class EggVariableFormRequest extends AdminFormRequest
{
/**
* Define rules for validation of this request.
*/
public function rules(): array
{
return [
'name' => 'required|string|min:1|max:255',
'description' => 'sometimes|nullable|string',
'env_variable' => 'required|regex:/^[\w]{1,255}$/|notIn:' . EggVariable::RESERVED_ENV_NAMES,
'options' => 'sometimes|required|array',
'rules' => 'bail|required|string',
'default_value' => 'present',
];
}
}

View File

@ -1,27 +0,0 @@
<?php
namespace App\Http\Requests\Admin;
use App\Models\User;
use Illuminate\Support\Collection;
class NewUserFormRequest extends AdminFormRequest
{
/**
* Rules to apply to requests for updating or creating a user
* in the Admin CP.
*/
public function rules(): array
{
return Collection::make(
User::getRules()
)->only([
'email',
'username',
'name_first',
'name_last',
'password',
'language',
])->toArray();
}
}

View File

@ -1,16 +0,0 @@
<?php
namespace App\Http\Requests\Admin\Node;
use App\Http\Requests\Admin\AdminFormRequest;
class AllocationAliasFormRequest extends AdminFormRequest
{
public function rules(): array
{
return [
'alias' => 'present|nullable|string',
'allocation_id' => 'required|numeric|exists:allocations,id',
];
}
}

View File

@ -1,17 +0,0 @@
<?php
namespace App\Http\Requests\Admin\Node;
use App\Http\Requests\Admin\AdminFormRequest;
class AllocationFormRequest extends AdminFormRequest
{
public function rules(): array
{
return [
'allocation_ip' => 'required|string',
'allocation_alias' => 'sometimes|nullable|string|max:255',
'allocation_ports' => 'required|array',
];
}
}

View File

@ -1,21 +0,0 @@
<?php
namespace App\Http\Requests\Admin\Node;
use App\Models\Node;
use App\Http\Requests\Admin\AdminFormRequest;
class NodeFormRequest extends AdminFormRequest
{
/**
* Get rules to apply to data in this request.
*/
public function rules(): array
{
if ($this->method() === 'PATCH') {
return Node::getRulesForUpdate($this->route()->parameter('node'));
}
return Node::getRules();
}
}

View File

@ -1,58 +0,0 @@
<?php
namespace App\Http\Requests\Admin;
use App\Models\Server;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Validator;
class ServerFormRequest extends AdminFormRequest
{
/**
* Rules to be applied to this request.
*/
public function rules(): array
{
$rules = Server::getRules();
$rules['description'][] = 'nullable';
$rules['custom_image'] = 'sometimes|nullable|string';
return $rules;
}
/**
* Run validation after the rules above have been applied.
*/
public function withValidator(Validator $validator): void
{
$validator->after(function ($validator) {
$validator->sometimes('node_id', 'required|numeric|bail|exists:nodes,id', function ($input) {
return !$input->auto_deploy;
});
$validator->sometimes('allocation_id', [
'required',
'numeric',
'bail',
Rule::exists('allocations', 'id')->where(function ($query) {
$query->where('node_id', $this->input('node_id'));
$query->whereNull('server_id');
}),
], function ($input) {
return !$input->auto_deploy;
});
$validator->sometimes('allocation_additional.*', [
'sometimes',
'required',
'numeric',
Rule::exists('allocations', 'id')->where(function ($query) {
$query->where('node_id', $this->input('node_id'));
$query->whereNull('server_id');
}),
], function ($input) {
return !$input->auto_deploy;
});
});
}
}

View File

@ -1,31 +0,0 @@
<?php
namespace App\Http\Requests\Admin\Servers\Databases;
use Illuminate\Validation\Rule;
use Illuminate\Database\Query\Builder;
use App\Http\Requests\Admin\AdminFormRequest;
class StoreServerDatabaseRequest extends AdminFormRequest
{
/**
* Validation rules for database creation.
*/
public function rules(): array
{
return [
'database' => [
'required',
'string',
'min:1',
'max:24',
Rule::unique('databases')->where(function (Builder $query) {
$query->where('database_host_id', $this->input('database_host_id') ?? 0);
}),
],
'max_connections' => 'nullable',
'remote' => 'required|string|regex:/^[0-9%.]{1,15}$/',
'database_host_id' => 'required|integer|exists:database_hosts,id',
];
}
}

View File

@ -1,50 +0,0 @@
<?php
namespace App\Http\Requests\Admin\Settings;
use App\Http\Requests\Admin\AdminFormRequest;
class AdvancedSettingsFormRequest extends AdminFormRequest
{
/**
* Return all the rules to apply to this request's data.
*/
public function rules(): array
{
return [
'recaptcha:enabled' => 'required|in:true,false',
'recaptcha:secret_key' => 'required|string|max:255',
'recaptcha:website_key' => 'required|string|max:255',
'panel:guzzle:timeout' => 'required|integer|between:1,60',
'panel:guzzle:connect_timeout' => 'required|integer|between:1,60',
'panel:client_features:allocations:enabled' => 'required|in:true,false',
'panel:client_features:allocations:range_start' => [
'nullable',
'required_if:panel:client_features:allocations:enabled,true',
'integer',
'between:1024,65535',
],
'panel:client_features:allocations:range_end' => [
'nullable',
'required_if:panel:client_features:allocations:enabled,true',
'integer',
'between:1024,65535',
'gt:panel:client_features:allocations:range_start',
],
];
}
public function attributes(): array
{
return [
'recaptcha:enabled' => 'reCAPTCHA Enabled',
'recaptcha:secret_key' => 'reCAPTCHA Secret Key',
'recaptcha:website_key' => 'reCAPTCHA Website Key',
'panel:guzzle:timeout' => 'HTTP Request Timeout',
'panel:guzzle:connect_timeout' => 'HTTP Connection Timeout',
'panel:client_features:allocations:enabled' => 'Auto Create Allocations Enabled',
'panel:client_features:allocations:range_start' => 'Starting Port',
'panel:client_features:allocations:range_end' => 'Ending Port',
];
}
}

View File

@ -1,30 +0,0 @@
<?php
namespace App\Http\Requests\Admin\Settings;
use Illuminate\Validation\Rule;
use App\Traits\Helpers\AvailableLanguages;
use App\Http\Requests\Admin\AdminFormRequest;
class BaseSettingsFormRequest extends AdminFormRequest
{
use AvailableLanguages;
public function rules(): array
{
return [
'app:name' => 'required|string|max:255',
'panel:auth:2fa_required' => 'required|integer|in:0,1,2',
'app:locale' => ['required', 'string', Rule::in(array_keys($this->getAvailableLanguages()))],
];
}
public function attributes(): array
{
return [
'app:name' => 'Company Name',
'panel:auth:2fa_required' => 'Require 2-Factor Authentication',
'app:locale' => 'Default Language',
];
}
}

View File

@ -1,40 +0,0 @@
<?php
namespace App\Http\Requests\Admin\Settings;
use Illuminate\Validation\Rule;
use App\Http\Requests\Admin\AdminFormRequest;
class MailSettingsFormRequest extends AdminFormRequest
{
/**
* Return rules to validate mail settings POST data against.
*/
public function rules(): array
{
return [
'mail:mailers:smtp:host' => 'required|string',
'mail:mailers:smtp:port' => 'required|integer|between:1,65535',
'mail:mailers:smtp:encryption' => ['present', Rule::in([null, 'tls', 'ssl'])],
'mail:mailers:smtp:username' => 'nullable|string|max:255',
'mail:mailers:smtp:password' => 'nullable|string|max:255',
'mail:from:address' => 'required|string|email',
'mail:from:name' => 'nullable|string|max:255',
];
}
/**
* Override the default normalization function for this type of request
* as we need to accept empty values on the keys.
*/
public function normalize(?array $only = null): array
{
$keys = array_flip(array_keys($this->rules()));
if (empty($this->input('mail:mailers:smtp:password'))) {
unset($keys['mail:mailers:smtp:password']);
}
return $this->only(array_flip($keys));
}
}

View File

@ -1,27 +0,0 @@
<?php
namespace App\Http\Requests\Admin;
use App\Models\User;
use Illuminate\Support\Collection;
class UserFormRequest extends AdminFormRequest
{
/**
* Rules to apply to requests for updating or creating a user
* in the Admin CP.
*/
public function rules(): array
{
return Collection::make(
User::getRulesForUpdate($this->route()->parameter('user'))
)->only([
'email',
'username',
'name_first',
'name_last',
'password',
'language',
])->toArray();
}
}

View File

@ -1,18 +0,0 @@
<?php
namespace App\Http\Requests\Api\Client\Servers\Files;
use App\Models\Server;
use App\Http\Requests\Api\Client\ClientApiRequest;
class DownloadFileRequest extends ClientApiRequest
{
/**
* Ensure that the user making this request has permission to download files
* from this server.
*/
public function authorize(): bool
{
return $this->user()->can('file.read', $this->parameter('server', Server::class));
}
}

View File

@ -1,20 +0,0 @@
<?php
namespace App\Http\Requests\Api\Remote;
use Illuminate\Foundation\Http\FormRequest;
class AuthenticateWebsocketDetailsRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'server_uuid' => 'required|string',
];
}
}

View File

@ -1,21 +0,0 @@
<?php
namespace App\Http\Requests\Auth;
use Illuminate\Foundation\Http\FormRequest;
class LoginRequest extends FormRequest
{
public function authorized(): bool
{
return true;
}
public function rules(): array
{
return [
'user' => 'required|string|min:1',
'password' => 'required|string',
];
}
}

View File

@ -1,29 +0,0 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
abstract class FrontendUserFormRequest extends FormRequest
{
abstract public function rules(): array;
/**
* Determine if a user is authorized to access this endpoint.
*/
public function authorize(): bool
{
return !is_null($this->user());
}
/**
* Return only the fields that we are interested in from the request.
* This will include empty fields as a null value.
*/
public function normalize(): array
{
return $this->only(
array_keys($this->rules())
);
}
}

View File

@ -5,21 +5,16 @@ namespace App\Http\ViewComposers;
use App\Services\Helpers\AssetHashService;
use Illuminate\View\View;
class AssetComposer
readonly class AssetComposer
{
/**
* AssetComposer constructor.
*/
public function __construct(private AssetHashService $assetHashService)
{
}
/**
* Provide access to the asset service in the views.
*/
public function compose(View $view): void
{
$view->with('asset', $this->assetHashService);
$view->with('siteConfiguration', [
'name' => config('app.name', 'Panel'),
'locale' => config('app.locale') ?? 'en',

View File

@ -1,30 +0,0 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class APILog extends Model
{
/**
* The table associated with the model.
*/
protected $table = 'api_logs';
/**
* The attributes excluded from the model's JSON form.
*/
protected $hidden = [];
/**
* Fields that are not mass assignable.
*/
protected $guarded = ['id', 'created_at', 'updated_at'];
protected function casts(): array
{
return [
'authorized' => 'boolean',
];
}
}

View File

@ -45,8 +45,6 @@ use Illuminate\Database\Eloquent\Model as IlluminateModel;
* @method static Builder|ActivityLog whereIp($value)
* @method static Builder|ActivityLog whereProperties($value)
* @method static Builder|ActivityLog whereTimestamp($value)
*
* @mixin \Eloquent
*/
class ActivityLog extends Model
{

View File

@ -20,8 +20,6 @@ use Illuminate\Database\Eloquent\SoftDeletingScope;
* @method static \Illuminate\Database\Eloquent\Builder|ActivityLogSubject newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ActivityLogSubject newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ActivityLogSubject query()
*
* @mixin \Eloquent
*/
class ActivityLogSubject extends Pivot
{

View File

@ -36,8 +36,6 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
* @method static \Illuminate\Database\Eloquent\Builder|Allocation wherePort($value)
* @method static \Illuminate\Database\Eloquent\Builder|Allocation whereServerId($value)
* @method static \Illuminate\Database\Eloquent\Builder|Allocation whereUpdatedAt($value)
*
* @mixin \Eloquent
*/
class Allocation extends Model
{

View File

@ -46,8 +46,6 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
* @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereToken($value)
* @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereUserId($value)
*
* @mixin \Eloquent
*/
class ApiKey extends Model
{

View File

@ -1,12 +0,0 @@
<?php
namespace App\Models;
class EggMount extends Model
{
protected $table = 'egg_mount';
protected $primaryKey = null;
public $incrementing = false;
}

View File

@ -1,35 +0,0 @@
<?php
namespace App\Models\Filters;
use Spatie\QueryBuilder\Filters\Filter;
use Illuminate\Database\Eloquent\Builder;
class AdminServerFilter implements Filter
{
/**
* A multi-column filter for the servers table that allows an administrative user to search
* across UUID, name, owner username, and owner email.
*
* @param string $value
*/
public function __invoke(Builder $query, $value, string $property): void
{
if ($query->getQuery()->from !== 'servers') {
throw new \BadMethodCallException('Cannot use the AdminServerFilter against a non-server model.');
}
$query
->select('servers.*')
->leftJoin('users', 'users.id', '=', 'servers.owner_id')
->where(function (Builder $builder) use ($value) {
$builder->where('servers.uuid', $value)
->orWhere('servers.uuid', 'LIKE', "$value%")
->orWhere('servers.uuid_short', $value)
->orWhere('servers.external_id', $value)
->orWhereRaw('LOWER(users.username) LIKE ?', ["%$value%"])
->orWhereRaw('LOWER(users.email) LIKE ?', ["$value%"])
->orWhereRaw('LOWER(servers.name) LIKE ?', ["%$value%"]);
})
->groupBy('servers.id');
}
}

View File

@ -6,6 +6,9 @@ use Illuminate\Support\Str;
use Spatie\QueryBuilder\Filters\Filter;
use Illuminate\Database\Eloquent\Builder;
/**
* @template-implements Filter<\Illuminate\Database\Eloquent\Model>
*/
class MultiFieldServerFilter implements Filter
{
/**

View File

@ -115,8 +115,6 @@ use App\Exceptions\Http\Server\ServerStateConflictException;
* @method static \Illuminate\Database\Eloquent\Builder|Server whereInstalledAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|Server wherePorts($value)
* @method static \Illuminate\Database\Eloquent\Builder|Server whereUuidShort($value)
*
* @mixin \Eloquent
*/
class Server extends Model
{

View File

@ -1,21 +0,0 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Session extends Model
{
/**
* The table associated with the model.
*/
protected $table = 'sessions';
protected function casts(): array
{
return [
'id' => 'string',
'user_id' => 'integer',
];
}
}

View File

@ -1,27 +0,0 @@
<?php
namespace App\Models;
/**
* App\Models\Setting.
*
* @property int $id
* @property string $key
* @property string $value
*/
class Setting extends Model
{
/**
* The table associated with the model.
*/
protected $table = 'settings';
public $timestamps = false;
protected $fillable = ['key', 'value'];
public static array $validationRules = [
'key' => 'required|string|between:1,255',
'value' => 'string',
];
}

View File

@ -1,30 +0,0 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class TaskLog extends Model
{
/**
* The table associated with the model.
*/
protected $table = 'tasks_log';
/**
* Fields that are not mass assignable.
*/
protected $guarded = ['id', 'created_at', 'updated_at'];
protected function casts(): array
{
return [
'id' => 'integer',
'task_id' => 'integer',
'run_status' => 'integer',
'run_time' => 'datetime',
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
}
}

View File

@ -85,8 +85,6 @@ use Spatie\Permission\Traits\HasRoles;
* @method static Builder|User whereUseTotp($value)
* @method static Builder|User whereUsername($value)
* @method static Builder|User whereUuid($value)
*
* @mixin \Eloquent
*/
class User extends Model implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract, FilamentUser, HasAvatar, HasName
{

View File

@ -32,9 +32,6 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
* @method static \Illuminate\Database\Eloquent\Builder|UserSSHKey whereUserId($value)
* @method static \Illuminate\Database\Query\Builder|UserSSHKey withTrashed()
* @method static \Illuminate\Database\Query\Builder|UserSSHKey withoutTrashed()
*
* @mixin \Eloquent
*
* @method static \Database\Factories\UserSSHKeyFactory factory(...$parameters)
*/
class UserSSHKey extends Model

View File

@ -2,7 +2,6 @@
namespace App\Providers;
use App\Extensions\Themes\Theme;
use App\Models;
use App\Models\ApiKey;
use App\Models\Node;
@ -14,16 +13,13 @@ use Filament\Support\Colors\Color;
use Filament\Support\Facades\FilamentColor;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Foundation\Application;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;
use Laravel\Sanctum\Sanctum;
use SocialiteProviders\Discord\Provider;
use SocialiteProviders\Manager\SocialiteWasCalled;
class AppServiceProvider extends ServiceProvider
{
@ -32,19 +28,6 @@ class AppServiceProvider extends ServiceProvider
*/
public function boot(Application $app): void
{
// TODO: remove when old admin area gets yeeted
View::share('appVersion', config('app.version'));
View::share('appIsGit', false);
Paginator::useBootstrap();
// If the APP_URL value is set with https:// make sure we force it here. Theoretically
// this should just work with the proxy logic, but there are a lot of cases where it
// doesn't, and it triggers a lot of support requests, so lets just head it off here.
if (Str::startsWith(config('app.url') ?? '', 'https://')) {
URL::forceScheme('https');
}
Relation::enforceMorphMap([
'allocation' => Models\Allocation::class,
'api_key' => Models\ApiKey::class,
@ -71,8 +54,7 @@ class AppServiceProvider extends ServiceProvider
->baseUrl($node->getConnectionAddress())
);
$this->bootAuth();
$this->bootBroadcast();
Sanctum::usePersonalAccessTokenModel(ApiKey::class);
$bearerTokens = fn (OpenApi $openApi) => $openApi->secure(SecurityScheme::http('bearer'));
Gate::define('viewApiDocs', fn () => true);
@ -80,8 +62,8 @@ class AppServiceProvider extends ServiceProvider
Scramble::registerApi('client', ['api_path' => 'api/client', 'info' => ['version' => '1.0']])->afterOpenApiGenerated($bearerTokens);
Scramble::registerApi('remote', ['api_path' => 'api/remote', 'info' => ['version' => '1.0']])->afterOpenApiGenerated($bearerTokens);
Event::listen(function (\SocialiteProviders\Manager\SocialiteWasCalled $event) {
$event->extendSocialite('discord', \SocialiteProviders\Discord\Provider::class);
Event::listen(function (SocialiteWasCalled $event) {
$event->extendSocialite('discord', Provider::class);
});
FilamentColor::register([
@ -103,28 +85,7 @@ class AppServiceProvider extends ServiceProvider
*/
public function register(): void
{
$this->app->singleton('extensions.themes', function () {
return new Theme();
});
Scramble::extendOpenApi(fn (OpenApi $openApi) => $openApi->secure(SecurityScheme::http('bearer')));
Scramble::ignoreDefaultRoutes();
}
public function bootAuth(): void
{
Sanctum::usePersonalAccessTokenModel(ApiKey::class);
}
public function bootBroadcast(): void
{
Broadcast::routes();
/*
* Authenticate the user's personal channel...
*/
Broadcast::channel('App.User.*', function ($user, $userId) {
return (int) $user->id === (int) $userId;
});
}
}

View File

@ -7,7 +7,6 @@ use Illuminate\Foundation\Http\Middleware\TrimStrings;
use Illuminate\Support\Facades\Route;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;
use App\Http\Middleware\AdminAuthenticate;
use App\Http\Middleware\RequireTwoFactorAuthentication;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
@ -37,10 +36,6 @@ class RouteServiceProvider extends ServiceProvider
Route::middleware(['auth.session', RequireTwoFactorAuthentication::class])
->group(base_path('routes/base.php'));
Route::middleware(['auth.session', RequireTwoFactorAuthentication::class, AdminAuthenticate::class])
->prefix('/legacy')
->group(base_path('routes/admin.php'));
Route::middleware('guest')->prefix('/auth')->group(base_path('routes/auth.php'));
});

View File

@ -89,7 +89,7 @@ class DatabaseManagementService
'password' => Utilities::randomStringWithSpecialCharacters(24),
]);
return $this->connection->transaction(function () use ($data, &$database) {
return $this->connection->transaction(function () use ($data) {
$database = $this->createModel($data);
$this->dynamic->set('dynamic', $data['database_host_id']);

View File

@ -1,26 +0,0 @@
<?php
namespace App\Services\Databases\Hosts;
use App\Exceptions\Service\HasActiveServersException;
use App\Models\DatabaseHost;
class HostDeletionService
{
/**
* Delete a specified host from the Panel if no databases are
* attached to it.
*
* @throws \App\Exceptions\Service\HasActiveServersException
*/
public function handle(int $host): int
{
$host = DatabaseHost::query()->findOrFail($host);
if ($host->databases()->count() > 0) {
throw new HasActiveServersException(trans('exceptions.databases.delete_has_databases'));
}
return (int) $host->delete();
}
}

View File

@ -1,22 +0,0 @@
<?php
namespace App\Services\Eggs\Scripts;
use App\Models\Egg;
class InstallScriptService
{
/**
* Modify the install script for a given Egg.
*/
public function handle(Egg $egg, array $data): void
{
$egg->update([
'script_install' => array_get($data, 'script_install'),
'script_is_privileged' => array_get($data, 'script_is_privileged', 1),
'script_entry' => array_get($data, 'script_entry'),
'script_container' => array_get($data, 'script_container'),
'copy_script_from' => array_get($data, 'copy_script_from'),
]);
}
}

View File

@ -1,24 +0,0 @@
<?php
namespace App\Services\Exceptions;
use Exception;
use Filament\Notifications\Notification;
class FilamentExceptionHandler
{
public function handle(Exception $exception, callable $stopPropagation): void
{
Notification::make()
->title($exception->title ?? null)
->body($exception->body ?? $exception->getMessage())
->color($exception->color ?? 'danger')
->icon($exception->icon ?? 'tabler-x')
->danger()
->send();
if ($this->stopPropagation ?? true) {
$stopPropagation();
}
}
}

View File

@ -115,7 +115,6 @@ class ServerCreationService
*/
private function configureDeployment(array $data, DeploymentObject $deployment): Allocation
{
/** @var Collection<\App\Models\Node> $nodes */
$nodes = $this->findViableNodesService->handle(
Arr::get($data, 'memory', 0),
Arr::get($data, 'disk', 0),

View File

@ -0,0 +1,19 @@
<?php
namespace App\Services\Servers;
use App\Enums\ServerState;
use App\Models\Server;
class ToggleInstallService
{
public function handle(Server $server): void
{
if ($server->status === ServerState::InstallFailed) {
abort(500, trans('admin/server.exceptions.marked_as_failed'));
}
$server->status = $server->isInstalled() ? ServerState::Installing : null;
$server->save();
}
}

View File

@ -1,28 +0,0 @@
<?php
namespace App\Traits\Controllers;
use Illuminate\Http\Request;
trait JavascriptInjection
{
private Request $request;
/**
* Set the request object to use when injecting JS.
*/
public function setRequest(Request $request): self
{
$this->request = $request;
return $this;
}
/**
* Injects the exact array passed in, nothing more.
*/
public function plainInject(array $args = []): string
{
return \JavaScript::put($args);
}
}

View File

@ -1,16 +0,0 @@
<?php
namespace App\Traits\Controllers;
use JavaScript;
trait PlainJavascriptInjection
{
/**
* Injects statistics into javascript.
*/
public function injectJavascript($data): void
{
\JavaScript::put($data);
}
}

View File

@ -40,8 +40,6 @@ class AllocationTransformer extends BaseTransformer
/**
* Load the node relationship onto a given transformation.
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*/
public function includeNode(Allocation $allocation): Item|NullResource
{
@ -58,8 +56,6 @@ class AllocationTransformer extends BaseTransformer
/**
* Load the server relationship onto a given transformation.
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*/
public function includeServer(Allocation $allocation): Item|NullResource
{

View File

@ -93,8 +93,6 @@ abstract class BaseTransformer extends TransformerAbstract
* @param class-string<T> $abstract
* @return T
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*
* @noinspection PhpDocSignatureInspection
*/
protected function makeTransformer(string $abstract)

View File

@ -43,8 +43,6 @@ class DatabaseHostTransformer extends BaseTransformer
/**
* Include the databases associated with this host.
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*/
public function includeDatabases(DatabaseHost $model): Collection|NullResource
{
@ -59,8 +57,6 @@ class DatabaseHostTransformer extends BaseTransformer
/**
* Include the node associated with this host.
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*/
public function includeNode(DatabaseHost $model): Item|NullResource
{

View File

@ -77,8 +77,6 @@ class EggTransformer extends BaseTransformer
/**
* Include the Servers relationship for the given Egg in the transformation.
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*/
public function includeServers(Egg $model): Collection|NullResource
{
@ -93,8 +91,6 @@ class EggTransformer extends BaseTransformer
/**
* Include the variables that are defined for this Egg.
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*/
public function includeVariables(Egg $model): Collection|NullResource
{

View File

@ -31,8 +31,6 @@ class MountTransformer extends BaseTransformer
/**
* Return the eggs associated with this mount.
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*/
public function includeEggs(Mount $mount): Collection|NullResource
{
@ -51,8 +49,6 @@ class MountTransformer extends BaseTransformer
/**
* Return the nodes associated with this mount.
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*/
public function includeNodes(Mount $mount): Collection|NullResource
{
@ -71,8 +67,6 @@ class MountTransformer extends BaseTransformer
/**
* Return the servers associated with this mount.
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*/
public function includeServers(Mount $mount): Collection|NullResource
{

View File

@ -48,8 +48,6 @@ class NodeTransformer extends BaseTransformer
/**
* Return the nodes associated with this location.
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*/
public function includeAllocations(Node $node): Collection|NullResource
{
@ -68,8 +66,6 @@ class NodeTransformer extends BaseTransformer
/**
* Return the nodes associated with this location.
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*/
public function includeServers(Node $node): Collection|NullResource
{

View File

@ -35,8 +35,6 @@ class RoleTransformer extends BaseTransformer
/**
* Include the permissions associated with this role.
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*/
public function includePermissions(Role $model): Collection|NullResource
{

View File

@ -51,8 +51,6 @@ class ServerDatabaseTransformer extends BaseTransformer
/**
* Return the database host relationship for this server database.
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*/
public function includeHost(Database $model): Item|NullResource
{

View File

@ -96,8 +96,6 @@ class ServerTransformer extends BaseTransformer
/**
* Return a generic array of allocations for this server.
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*/
public function includeAllocations(Server $server): Collection|NullResource
{
@ -112,8 +110,6 @@ class ServerTransformer extends BaseTransformer
/**
* Return a generic array of data about subusers for this server.
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*/
public function includeSubusers(Server $server): Collection|NullResource
{
@ -128,8 +124,6 @@ class ServerTransformer extends BaseTransformer
/**
* Return a generic array of data about subusers for this server.
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*/
public function includeUser(Server $server): Item|NullResource
{
@ -144,8 +138,6 @@ class ServerTransformer extends BaseTransformer
/**
* Return a generic array with egg information for this server.
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*/
public function includeEgg(Server $server): Item|NullResource
{
@ -160,8 +152,6 @@ class ServerTransformer extends BaseTransformer
/**
* Return a generic array of data about subusers for this server.
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*/
public function includeVariables(Server $server): Collection|NullResource
{
@ -176,8 +166,6 @@ class ServerTransformer extends BaseTransformer
/**
* Return a generic array with node information for this server.
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*/
public function includeNode(Server $server): Item|NullResource
{
@ -192,8 +180,6 @@ class ServerTransformer extends BaseTransformer
/**
* Return a generic array with database information for this server.
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*/
public function includeDatabases(Server $server): Collection|NullResource
{

View File

@ -32,8 +32,6 @@ class ServerVariableTransformer extends BaseTransformer
/**
* Return the parent service variable data.
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*/
public function includeParent(EggVariable $variable): Item|NullResource
{

View File

@ -40,8 +40,6 @@ class SubuserTransformer extends BaseTransformer
/**
* Return a generic item of user for this subuser.
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*/
public function includeUser(Subuser $subuser): Item|NullResource
{
@ -56,8 +54,6 @@ class SubuserTransformer extends BaseTransformer
/**
* Return a generic item of server for this subuser.
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*/
public function includeServer(Subuser $subuser): Item|NullResource
{

View File

@ -50,8 +50,6 @@ class UserTransformer extends BaseTransformer
/**
* Return the servers associated with this user.
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*/
public function includeServers(User $user): Collection|NullResource
{
@ -66,8 +64,6 @@ class UserTransformer extends BaseTransformer
/**
* Return the roles associated with this user.
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*/
public function includeRoles(User $user): Collection|NullResource
{

View File

@ -47,8 +47,6 @@ class ScheduleTransformer extends BaseClientTransformer
/**
* Allows attaching the tasks specific to the schedule in the response.
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*/
public function includeTasks(Schedule $model): Collection
{

Some files were not shown because too many files have changed in this diff Show More