mirror of
https://github.com/pelican-dev/panel.git
synced 2025-07-23 14:12:16 +02:00
Merge branch 'main' into issue/68
# Conflicts: # app/Filament/Resources/ServerResource/Pages/CreateServer.php # app/Models/Node.php
This commit is contained in:
commit
4319f24f51
@ -30,3 +30,8 @@ MAIL_FROM_NAME="Pelican Admin"
|
|||||||
SESSION_ENCRYPT=false
|
SESSION_ENCRYPT=false
|
||||||
SESSION_PATH=/
|
SESSION_PATH=/
|
||||||
SESSION_DOMAIN=null
|
SESSION_DOMAIN=null
|
||||||
|
|
||||||
|
# Set this to true, and set start & end ports to auto create allocations.
|
||||||
|
PANEL_CLIENT_ALLOCATIONS_ENABLED=false
|
||||||
|
PANEL_CLIENT_ALLOCATIONS_RANGE_START=
|
||||||
|
PANEL_CLIENT_ALLOCATIONS_RANGE_END=
|
||||||
|
4
.github/workflows/ci.yaml
vendored
4
.github/workflows/ci.yaml
vendored
@ -59,7 +59,7 @@ jobs:
|
|||||||
uses: shivammathur/setup-php@v2
|
uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
php-version: ${{ matrix.php }}
|
php-version: ${{ matrix.php }}
|
||||||
extensions: bcmath, cli, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip
|
extensions: bcmath, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip
|
||||||
tools: composer:v2
|
tools: composer:v2
|
||||||
coverage: none
|
coverage: none
|
||||||
|
|
||||||
@ -119,7 +119,7 @@ jobs:
|
|||||||
uses: shivammathur/setup-php@v2
|
uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
php-version: ${{ matrix.php }}
|
php-version: ${{ matrix.php }}
|
||||||
extensions: bcmath, cli, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip
|
extensions: bcmath, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip
|
||||||
tools: composer:v2
|
tools: composer:v2
|
||||||
coverage: none
|
coverage: none
|
||||||
|
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,6 +4,7 @@
|
|||||||
/public/hot
|
/public/hot
|
||||||
/public/storage
|
/public/storage
|
||||||
/storage/*.key
|
/storage/*.key
|
||||||
|
/storage/clockwork/*
|
||||||
/vendor
|
/vendor
|
||||||
*.DS_Store*
|
*.DS_Store*
|
||||||
.env
|
.env
|
||||||
|
@ -118,7 +118,9 @@ class AppSettingsCommand extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($this->variables['QUEUE_CONNECTION'] !== 'sync') {
|
if ($this->variables['QUEUE_CONNECTION'] !== 'sync') {
|
||||||
Artisan::call('p:environment:queue-service', $redisUsed ? ['--use-redis'] : []);
|
$this->call('p:environment:queue-service', [
|
||||||
|
'--use-redis' => $redisUsed,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->info($this->console->output());
|
$this->info($this->console->output());
|
||||||
@ -127,7 +129,7 @@ class AppSettingsCommand extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request connection details and verify them.
|
* Request redis connection details and verify them.
|
||||||
*/
|
*/
|
||||||
private function requestRedisSettings(): void
|
private function requestRedisSettings(): void
|
||||||
{
|
{
|
||||||
|
@ -98,7 +98,7 @@ class DatabaseSettingsCommand extends Command
|
|||||||
} elseif ($this->variables['DB_CONNECTION'] === 'sqlite') {
|
} elseif ($this->variables['DB_CONNECTION'] === 'sqlite') {
|
||||||
$this->variables['DB_DATABASE'] = $this->option('database') ?? $this->ask(
|
$this->variables['DB_DATABASE'] = $this->option('database') ?? $this->ask(
|
||||||
'Database Path',
|
'Database Path',
|
||||||
config('database.connections.sqlite.database', database_path('database.sqlite'))
|
env('DB_DATABASE', 'database.sqlite')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,17 +19,18 @@ class QueueWorkerServiceCommand extends Command
|
|||||||
|
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
$serviceName = $this->option('service-name') ?? $this->ask('Service name', 'pelican-queue');
|
$serviceName = $this->option('service-name') ?? $this->ask('Queue worker service name', 'pelican-queue');
|
||||||
$path = '/etc/systemd/system/' . $serviceName . '.service';
|
$path = '/etc/systemd/system/' . $serviceName . '.service';
|
||||||
|
|
||||||
if (file_exists($path) && !$this->option('overwrite') && !$this->confirm('The service file already exists. Do you want to overwrite it?')) {
|
$fileExists = file_exists($path);
|
||||||
$this->line('Creation of queue worker service file aborted.');
|
if ($fileExists && !$this->option('overwrite') && !$this->confirm('The service file already exists. Do you want to overwrite it?')) {
|
||||||
|
$this->line('Creation of queue worker service file aborted because serive file already exists.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = $this->option('user') ?? $this->ask('User', 'www-data');
|
$user = $this->option('user') ?? $this->ask('Webserver User', 'www-data');
|
||||||
$group = $this->option('group') ?? $this->ask('Group', 'www-data');
|
$group = $this->option('group') ?? $this->ask('Webserver Group', 'www-data');
|
||||||
|
|
||||||
$afterRedis = $this->option('use-redis') ? '\nAfter=redis-server.service' : '';
|
$afterRedis = $this->option('use-redis') ? '\nAfter=redis-server.service' : '';
|
||||||
|
|
||||||
@ -45,7 +46,7 @@ Description=Pelican Queue Service$afterRedis
|
|||||||
User=$user
|
User=$user
|
||||||
Group=$group
|
Group=$group
|
||||||
Restart=always
|
Restart=always
|
||||||
ExecStart=/usr/bin/php $basePath/artisan queue:work --queue=high,standard,low --tries=3
|
ExecStart=/usr/bin/php $basePath/artisan queue:work --tries=3
|
||||||
StartLimitInterval=180
|
StartLimitInterval=180
|
||||||
StartLimitBurst=30
|
StartLimitBurst=30
|
||||||
RestartSec=5s
|
RestartSec=5s
|
||||||
@ -60,13 +61,24 @@ WantedBy=multi-user.target
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = Process::run("systemctl enable --now $serviceName.service");
|
if ($fileExists) {
|
||||||
if ($result->failed()) {
|
$result = Process::run("systemctl restart $serviceName.service");
|
||||||
$this->error('Error enabling service: ' . $result->errorOutput());
|
if ($result->failed()) {
|
||||||
|
$this->error('Error restarting service: ' . $result->errorOutput());
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->line('Queue worker service file updated successfully.');
|
||||||
|
} else {
|
||||||
|
$result = Process::run("systemctl enable --now $serviceName.service");
|
||||||
|
if ($result->failed()) {
|
||||||
|
$this->error('Error enabling service: ' . $result->errorOutput());
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->line('Queue worker service file created successfully.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->line('Queue worker service file created successfully.');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ class InfoCommand extends Command
|
|||||||
{
|
{
|
||||||
$this->output->title('Version Information');
|
$this->output->title('Version Information');
|
||||||
$this->table([], [
|
$this->table([], [
|
||||||
['Panel Version', config('app.version')],
|
['Panel Version', $this->versionService->versionData()['version']],
|
||||||
['Latest Version', $this->versionService->getPanel()],
|
['Latest Version', $this->versionService->getPanel()],
|
||||||
['Up-to-Date', $this->versionService->isLatestPanel() ? 'Yes' : $this->formatText('No', 'bg=red')],
|
['Up-to-Date', $this->versionService->isLatestPanel() ? 'Yes' : $this->formatText('No', 'bg=red')],
|
||||||
], 'compact');
|
], 'compact');
|
||||||
|
@ -25,6 +25,7 @@ class MakeNodeCommand extends Command
|
|||||||
{--uploadSize= : Enter the maximum upload filesize.}
|
{--uploadSize= : Enter the maximum upload filesize.}
|
||||||
{--daemonListeningPort= : Enter the daemon listening port.}
|
{--daemonListeningPort= : Enter the daemon listening port.}
|
||||||
{--daemonSFTPPort= : Enter the daemon SFTP listening port.}
|
{--daemonSFTPPort= : Enter the daemon SFTP listening port.}
|
||||||
|
{--daemonSFTPAlias= : Enter the daemon SFTP alias.}
|
||||||
{--daemonBase= : Enter the base folder.}';
|
{--daemonBase= : Enter the base folder.}';
|
||||||
|
|
||||||
protected $description = 'Creates a new node on the system via the CLI.';
|
protected $description = 'Creates a new node on the system via the CLI.';
|
||||||
@ -65,6 +66,7 @@ class MakeNodeCommand extends Command
|
|||||||
$data['upload_size'] = $this->option('uploadSize') ?? $this->ask(__('commands.make_node.upload_size'), '100');
|
$data['upload_size'] = $this->option('uploadSize') ?? $this->ask(__('commands.make_node.upload_size'), '100');
|
||||||
$data['daemon_listen'] = $this->option('daemonListeningPort') ?? $this->ask(__('commands.make_node.daemonListen'), '8080');
|
$data['daemon_listen'] = $this->option('daemonListeningPort') ?? $this->ask(__('commands.make_node.daemonListen'), '8080');
|
||||||
$data['daemon_sftp'] = $this->option('daemonSFTPPort') ?? $this->ask(__('commands.make_node.daemonSFTP'), '2022');
|
$data['daemon_sftp'] = $this->option('daemonSFTPPort') ?? $this->ask(__('commands.make_node.daemonSFTP'), '2022');
|
||||||
|
$data['daemon_sftp_alias'] = $this->option('daemonSFTPAlias') ?? $this->ask(__('commands.make_node.daemonSFTPAlias'), '');
|
||||||
$data['daemon_base'] = $this->option('daemonBase') ?? $this->ask(__('commands.make_node.daemonBase'), '/var/lib/pelican/volumes');
|
$data['daemon_base'] = $this->option('daemonBase') ?? $this->ask(__('commands.make_node.daemonBase'), '/var/lib/pelican/volumes');
|
||||||
|
|
||||||
$node = $this->creationService->handle($data);
|
$node = $this->creationService->handle($data);
|
||||||
|
@ -24,7 +24,7 @@ class ProcessRunnableCommand extends Command
|
|||||||
->whereRelation('server', fn (Builder $builder) => $builder->whereNull('status'))
|
->whereRelation('server', fn (Builder $builder) => $builder->whereNull('status'))
|
||||||
->where('is_active', true)
|
->where('is_active', true)
|
||||||
->where('is_processing', false)
|
->where('is_processing', false)
|
||||||
->whereDate('next_run_at', '<=', Carbon::now()->toDateString())
|
->whereDate('next_run_at', '<=', Carbon::now()->toDateTimeString())
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
if ($schedules->count() < 1) {
|
if ($schedules->count() < 1) {
|
||||||
|
@ -30,7 +30,7 @@ class MakeUserCommand extends Command
|
|||||||
public function handle(): int
|
public function handle(): int
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
DB::select('select 1 where 1');
|
DB::connection()->getPdo();
|
||||||
} catch (Exception $exception) {
|
} catch (Exception $exception) {
|
||||||
$this->error($exception->getMessage());
|
$this->error($exception->getMessage());
|
||||||
|
|
||||||
|
@ -6,9 +6,9 @@ use App\Exceptions\DisplayException;
|
|||||||
|
|
||||||
class TwoFactorAuthenticationTokenInvalid extends DisplayException
|
class TwoFactorAuthenticationTokenInvalid extends DisplayException
|
||||||
{
|
{
|
||||||
/**
|
public string $title = 'Invalid 2FA Code';
|
||||||
* TwoFactorAuthenticationTokenInvalid constructor.
|
public string $icon = 'tabler-2fa';
|
||||||
*/
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
parent::__construct('The provided two-factor authentication token was not valid.');
|
parent::__construct('The provided two-factor authentication token was not valid.');
|
||||||
|
@ -7,6 +7,7 @@ use App\Models\Egg;
|
|||||||
use App\Models\Node;
|
use App\Models\Node;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use App\Services\Helpers\SoftwareVersionService;
|
||||||
use Filament\Actions\CreateAction;
|
use Filament\Actions\CreateAction;
|
||||||
use Filament\Pages\Page;
|
use Filament\Pages\Page;
|
||||||
|
|
||||||
@ -29,8 +30,14 @@ class Dashboard extends Page
|
|||||||
|
|
||||||
public function getViewData(): array
|
public function getViewData(): array
|
||||||
{
|
{
|
||||||
|
/** @var SoftwareVersionService $softwareVersionService */
|
||||||
|
$softwareVersionService = app(SoftwareVersionService::class);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'inDevelopment' => config('app.version') === 'canary',
|
'inDevelopment' => config('app.version') === 'canary',
|
||||||
|
'version' => $softwareVersionService->versionData()['version'],
|
||||||
|
'latestVersion' => $softwareVersionService->getPanel(),
|
||||||
|
'isLatest' => $softwareVersionService->isLatestPanel(),
|
||||||
'eggsCount' => Egg::query()->count(),
|
'eggsCount' => Egg::query()->count(),
|
||||||
'nodesList' => ListNodes::getUrl(),
|
'nodesList' => ListNodes::getUrl(),
|
||||||
'nodesCount' => Node::query()->count(),
|
'nodesCount' => Node::query()->count(),
|
||||||
@ -43,6 +50,13 @@ class Dashboard extends Page
|
|||||||
->icon('tabler-brand-github')
|
->icon('tabler-brand-github')
|
||||||
->url('https://github.com/pelican-dev/panel/discussions', true),
|
->url('https://github.com/pelican-dev/panel/discussions', true),
|
||||||
],
|
],
|
||||||
|
'updateActions' => [
|
||||||
|
CreateAction::make()
|
||||||
|
->label('Read Documentation')
|
||||||
|
->icon('tabler-clipboard-text')
|
||||||
|
->url('https://pelican.dev/docs/panel/update', true)
|
||||||
|
->color('warning'),
|
||||||
|
],
|
||||||
'nodeActions' => [
|
'nodeActions' => [
|
||||||
CreateAction::make()
|
CreateAction::make()
|
||||||
->label(trans('dashboard/index.sections.intro-first-node.button_label'))
|
->label(trans('dashboard/index.sections.intro-first-node.button_label'))
|
||||||
@ -53,7 +67,7 @@ class Dashboard extends Page
|
|||||||
CreateAction::make()
|
CreateAction::make()
|
||||||
->label(trans('dashboard/index.sections.intro-support.button_donate'))
|
->label(trans('dashboard/index.sections.intro-support.button_donate'))
|
||||||
->icon('tabler-cash')
|
->icon('tabler-cash')
|
||||||
->url('https://pelican.dev/donate', true)
|
->url($softwareVersionService->getDonations(), true)
|
||||||
->color('success'),
|
->color('success'),
|
||||||
],
|
],
|
||||||
'helpActions' => [
|
'helpActions' => [
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace App\Filament\Resources\DatabaseHostResource\Pages;
|
namespace App\Filament\Resources\DatabaseHostResource\Pages;
|
||||||
|
|
||||||
use App\Filament\Resources\DatabaseHostResource;
|
use App\Filament\Resources\DatabaseHostResource;
|
||||||
|
use App\Models\DatabaseHost;
|
||||||
use Filament\Actions;
|
use Filament\Actions;
|
||||||
use Filament\Resources\Pages\EditRecord;
|
use Filament\Resources\Pages\EditRecord;
|
||||||
use Filament\Forms;
|
use Filament\Forms;
|
||||||
@ -71,7 +72,9 @@ class EditDatabaseHost extends EditRecord
|
|||||||
protected function getHeaderActions(): array
|
protected function getHeaderActions(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
Actions\DeleteAction::make(),
|
Actions\DeleteAction::make()
|
||||||
|
->label(fn (DatabaseHost $databaseHost) => $databaseHost->databases()->count() > 0 ? 'Database Host Has Databases' : 'Delete')
|
||||||
|
->disabled(fn (DatabaseHost $databaseHost) => $databaseHost->databases()->count() > 0),
|
||||||
$this->getSaveFormAction()->formId('form'),
|
$this->getSaveFormAction()->formId('form'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ use App\Services\Eggs\Sharing\EggImporterService;
|
|||||||
use Exception;
|
use Exception;
|
||||||
use Filament\Actions;
|
use Filament\Actions;
|
||||||
use Filament\Forms;
|
use Filament\Forms;
|
||||||
|
use Filament\Forms\Components\Tabs;
|
||||||
use Filament\Notifications\Notification;
|
use Filament\Notifications\Notification;
|
||||||
use Filament\Resources\Pages\ListRecords;
|
use Filament\Resources\Pages\ListRecords;
|
||||||
use Filament\Tables\Table;
|
use Filament\Tables\Table;
|
||||||
@ -62,21 +63,58 @@ class ListEggs extends ListRecords
|
|||||||
Actions\Action::make('import')
|
Actions\Action::make('import')
|
||||||
->label('Import')
|
->label('Import')
|
||||||
->form([
|
->form([
|
||||||
Forms\Components\FileUpload::make('egg')
|
Tabs::make('Tabs')
|
||||||
->acceptedFileTypes(['application/json'])
|
->tabs([
|
||||||
->storeFiles(false)
|
Tabs\Tab::make('From File')
|
||||||
->multiple(),
|
->icon('tabler-file-upload')
|
||||||
|
->schema([
|
||||||
|
Forms\Components\FileUpload::make('egg')
|
||||||
|
->label('Egg')
|
||||||
|
->hint('This should be the json file ( egg-minecraft.json )')
|
||||||
|
->acceptedFileTypes(['application/json'])
|
||||||
|
->storeFiles(false)
|
||||||
|
->multiple(),
|
||||||
|
]),
|
||||||
|
Tabs\Tab::make('From URL')
|
||||||
|
->icon('tabler-world-upload')
|
||||||
|
->schema([
|
||||||
|
Forms\Components\TextInput::make('url')
|
||||||
|
->label('URL')
|
||||||
|
->hint('This URL should point to a single json file')
|
||||||
|
->url(),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
->contained(false),
|
||||||
|
|
||||||
])
|
])
|
||||||
->action(function (array $data): void {
|
->action(function (array $data): void {
|
||||||
/** @var TemporaryUploadedFile $eggFile */
|
|
||||||
$eggFile = $data['egg'];
|
|
||||||
|
|
||||||
/** @var EggImporterService $eggImportService */
|
/** @var EggImporterService $eggImportService */
|
||||||
$eggImportService = resolve(EggImporterService::class);
|
$eggImportService = resolve(EggImporterService::class);
|
||||||
|
|
||||||
foreach ($eggFile as $file) {
|
if (!empty($data['egg'])) {
|
||||||
|
/** @var TemporaryUploadedFile[] $eggFile */
|
||||||
|
$eggFile = $data['egg'];
|
||||||
|
|
||||||
|
foreach ($eggFile as $file) {
|
||||||
|
try {
|
||||||
|
$eggImportService->fromFile($file);
|
||||||
|
} catch (Exception $exception) {
|
||||||
|
Notification::make()
|
||||||
|
->title('Import Failed')
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
report($exception);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($data['url'])) {
|
||||||
try {
|
try {
|
||||||
$eggImportService->handle($file);
|
$eggImportService->fromUrl($data['url']);
|
||||||
} catch (Exception $exception) {
|
} catch (Exception $exception) {
|
||||||
Notification::make()
|
Notification::make()
|
||||||
->title('Import Failed')
|
->title('Import Failed')
|
||||||
|
@ -4,8 +4,10 @@ namespace App\Filament\Resources\NodeResource\Pages;
|
|||||||
|
|
||||||
use App\Filament\Resources\NodeResource;
|
use App\Filament\Resources\NodeResource;
|
||||||
use Filament\Forms;
|
use Filament\Forms;
|
||||||
use Filament\Forms\Components\Tabs;
|
use Filament\Forms\Components\Actions\Action;
|
||||||
|
use Filament\Forms\Components\Wizard;
|
||||||
use Filament\Resources\Pages\CreateRecord;
|
use Filament\Resources\Pages\CreateRecord;
|
||||||
|
use Illuminate\Support\Facades\Blade;
|
||||||
use Illuminate\Support\HtmlString;
|
use Illuminate\Support\HtmlString;
|
||||||
|
|
||||||
class CreateNode extends CreateRecord
|
class CreateNode extends CreateRecord
|
||||||
@ -18,19 +20,19 @@ class CreateNode extends CreateRecord
|
|||||||
|
|
||||||
public function form(Forms\Form $form): Forms\Form
|
public function form(Forms\Form $form): Forms\Form
|
||||||
{
|
{
|
||||||
return $form->schema([
|
return $form
|
||||||
Tabs::make('Tabs')
|
->schema([
|
||||||
->columns([
|
Wizard::make([
|
||||||
'default' => 2,
|
Wizard\Step::make('basic')
|
||||||
'sm' => 3,
|
->label('Basic Settings')
|
||||||
'md' => 3,
|
|
||||||
'lg' => 4,
|
|
||||||
])
|
|
||||||
->persistTabInQueryString()
|
|
||||||
->columnSpanFull()
|
|
||||||
->tabs([
|
|
||||||
Tabs\Tab::make('Basic Settings')
|
|
||||||
->icon('tabler-server')
|
->icon('tabler-server')
|
||||||
|
->columnSpanFull()
|
||||||
|
->columns([
|
||||||
|
'default' => 2,
|
||||||
|
'sm' => 3,
|
||||||
|
'md' => 3,
|
||||||
|
'lg' => 4,
|
||||||
|
])
|
||||||
->schema([
|
->schema([
|
||||||
Forms\Components\TextInput::make('fqdn')
|
Forms\Components\TextInput::make('fqdn')
|
||||||
->columnSpan(2)
|
->columnSpan(2)
|
||||||
@ -55,7 +57,7 @@ class CreateNode extends CreateRecord
|
|||||||
return "
|
return "
|
||||||
This is the domain name that points to your node's IP Address.
|
This is the domain name that points to your node's IP Address.
|
||||||
If you've already set up this, you can verify it by checking the next field!
|
If you've already set up this, you can verify it by checking the next field!
|
||||||
";
|
";
|
||||||
})
|
})
|
||||||
->hintColor('danger')
|
->hintColor('danger')
|
||||||
->hint(function ($state) {
|
->hint(function ($state) {
|
||||||
@ -184,30 +186,17 @@ class CreateNode extends CreateRecord
|
|||||||
])
|
])
|
||||||
->default(fn () => request()->isSecure() ? 'https' : 'http'),
|
->default(fn () => request()->isSecure() ? 'https' : 'http'),
|
||||||
]),
|
]),
|
||||||
Tabs\Tab::make('Advanced Settings')
|
Wizard\Step::make('advanced')
|
||||||
|
->label('Advanced Settings')
|
||||||
->icon('tabler-server-cog')
|
->icon('tabler-server-cog')
|
||||||
|
->columnSpanFull()
|
||||||
|
->columns([
|
||||||
|
'default' => 2,
|
||||||
|
'sm' => 3,
|
||||||
|
'md' => 3,
|
||||||
|
'lg' => 4,
|
||||||
|
])
|
||||||
->schema([
|
->schema([
|
||||||
Forms\Components\TextInput::make('upload_size')
|
|
||||||
->label('Upload Limit')
|
|
||||||
->helperText('Enter the maximum size of files that can be uploaded through the web-based file manager.')
|
|
||||||
->columnSpan(1)
|
|
||||||
->numeric()->required()
|
|
||||||
->default(256)
|
|
||||||
->minValue(1)
|
|
||||||
->maxValue(1024)
|
|
||||||
->suffix('MiB'),
|
|
||||||
Forms\Components\ToggleButtons::make('public')
|
|
||||||
->label('Automatic Allocation')->inline()
|
|
||||||
->default(true)
|
|
||||||
->columnSpan(1)
|
|
||||||
->options([
|
|
||||||
true => 'Yes',
|
|
||||||
false => 'No',
|
|
||||||
])
|
|
||||||
->colors([
|
|
||||||
true => 'success',
|
|
||||||
false => 'danger',
|
|
||||||
]),
|
|
||||||
Forms\Components\ToggleButtons::make('maintenance_mode')
|
Forms\Components\ToggleButtons::make('maintenance_mode')
|
||||||
->label('Maintenance Mode')->inline()
|
->label('Maintenance Mode')->inline()
|
||||||
->columnSpan(1)
|
->columnSpan(1)
|
||||||
@ -222,13 +211,46 @@ class CreateNode extends CreateRecord
|
|||||||
true => 'danger',
|
true => 'danger',
|
||||||
false => 'success',
|
false => 'success',
|
||||||
]),
|
]),
|
||||||
|
Forms\Components\ToggleButtons::make('public')
|
||||||
|
->columnSpan(1)
|
||||||
|
->label('Automatic Allocation')->inline()
|
||||||
|
->options([
|
||||||
|
true => 'Yes',
|
||||||
|
false => 'No',
|
||||||
|
])
|
||||||
|
->colors([
|
||||||
|
true => 'success',
|
||||||
|
false => 'danger',
|
||||||
|
]),
|
||||||
Forms\Components\TagsInput::make('tags')
|
Forms\Components\TagsInput::make('tags')
|
||||||
->label('Tags')
|
->label('Tags')
|
||||||
->disabled()
|
->disabled()
|
||||||
->placeholder('Not Implemented')
|
->placeholder('Not Implemented')
|
||||||
->hintIcon('tabler-question-mark')
|
->hintIcon('tabler-question-mark')
|
||||||
->hintIconTooltip('Not Implemented')
|
->hintIconTooltip('Not Implemented')
|
||||||
->columnSpan(1),
|
->columnSpan(2),
|
||||||
|
|
||||||
|
Forms\Components\TextInput::make('upload_size')
|
||||||
|
->label('Upload Limit')
|
||||||
|
->helperText('Enter the maximum size of files that can be uploaded through the web-based file manager.')
|
||||||
|
->columnSpan(1)
|
||||||
|
->numeric()->required()
|
||||||
|
->default(256)
|
||||||
|
->minValue(1)
|
||||||
|
->maxValue(1024)
|
||||||
|
->suffix('MiB'),
|
||||||
|
Forms\Components\TextInput::make('daemon_sftp')
|
||||||
|
->columnSpan(1)
|
||||||
|
->label('SFTP Port')
|
||||||
|
->minValue(0)
|
||||||
|
->maxValue(65536)
|
||||||
|
->default(2022)
|
||||||
|
->required()
|
||||||
|
->integer(),
|
||||||
|
Forms\Components\TextInput::make('daemon_sftp_alias')
|
||||||
|
->columnSpan(2)
|
||||||
|
->label('SFTP Alias')
|
||||||
|
->helperText('Display alias for the SFTP address. Leave empty to use the Node FQDN.'),
|
||||||
Forms\Components\Grid::make()
|
Forms\Components\Grid::make()
|
||||||
->columns(6)
|
->columns(6)
|
||||||
->columnSpanFull()
|
->columnSpanFull()
|
||||||
@ -353,8 +375,17 @@ class CreateNode extends CreateRecord
|
|||||||
->suffix('%'),
|
->suffix('%'),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
]),
|
])->columnSpanFull()
|
||||||
]);
|
->nextAction(fn (Action $action) => $action->label('Next Step'))
|
||||||
|
->submitAction(new HtmlString(Blade::render(<<<'BLADE'
|
||||||
|
<x-filament::button
|
||||||
|
type="submit"
|
||||||
|
size="sm"
|
||||||
|
>
|
||||||
|
Create Node
|
||||||
|
</x-filament::button>
|
||||||
|
BLADE))),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getRedirectUrlParameters(): array
|
protected function getRedirectUrlParameters(): array
|
||||||
@ -363,4 +394,9 @@ class CreateNode extends CreateRecord
|
|||||||
'tab' => '-configuration-tab',
|
'tab' => '-configuration-tab',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getFormActions(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -215,6 +215,18 @@ class EditNode extends EditRecord
|
|||||||
->minValue(1)
|
->minValue(1)
|
||||||
->maxValue(1024)
|
->maxValue(1024)
|
||||||
->suffix('MiB'),
|
->suffix('MiB'),
|
||||||
|
Forms\Components\TextInput::make('daemon_sftp')
|
||||||
|
->columnSpan(['default' => 1, 'sm' => 1, 'md' => 1, 'lg' => 3])
|
||||||
|
->label('SFTP Port')
|
||||||
|
->minValue(0)
|
||||||
|
->maxValue(65536)
|
||||||
|
->default(2022)
|
||||||
|
->required()
|
||||||
|
->integer(),
|
||||||
|
Forms\Components\TextInput::make('daemon_sftp_alias')
|
||||||
|
->columnSpan(['default' => 1, 'sm' => 1, 'md' => 1, 'lg' => 3])
|
||||||
|
->label('SFTP Alias')
|
||||||
|
->helperText('Display alias for the SFTP address. Leave empty to use the Node FQDN.'),
|
||||||
Forms\Components\ToggleButtons::make('public')
|
Forms\Components\ToggleButtons::make('public')
|
||||||
->columnSpan(['default' => 1, 'sm' => 1, 'md' => 1, 'lg' => 3])
|
->columnSpan(['default' => 1, 'sm' => 1, 'md' => 1, 'lg' => 3])
|
||||||
->label('Automatic Allocation')->inline()
|
->label('Automatic Allocation')->inline()
|
||||||
|
@ -113,7 +113,7 @@ class AllocationsRelationManager extends RelationManager
|
|||||||
|
|
||||||
$start = max((int) $start, 0);
|
$start = max((int) $start, 0);
|
||||||
$end = min((int) $end, 2 ** 16 - 1);
|
$end = min((int) $end, 2 ** 16 - 1);
|
||||||
for ($i = $start; $i <= $end; $i++) {
|
foreach (range($start, $end) as $i) {
|
||||||
$ports->push($i);
|
$ports->push($i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -31,6 +31,9 @@ use Illuminate\Support\Facades\Hash;
|
|||||||
use Illuminate\Support\HtmlString;
|
use Illuminate\Support\HtmlString;
|
||||||
use Illuminate\Validation\Rules\Password;
|
use Illuminate\Validation\Rules\Password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method User getUser()
|
||||||
|
*/
|
||||||
class EditProfile extends \Filament\Pages\Auth\EditProfile
|
class EditProfile extends \Filament\Pages\Auth\EditProfile
|
||||||
{
|
{
|
||||||
protected function getForms(): array
|
protected function getForms(): array
|
||||||
@ -117,6 +120,7 @@ class EditProfile extends \Filament\Pages\Auth\EditProfile
|
|||||||
->helperText('Enter your current 2FA code to disable Two Factor Authentication'),
|
->helperText('Enter your current 2FA code to disable Two Factor Authentication'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
/** @var TwoFactorSetupService */
|
||||||
$setupService = app(TwoFactorSetupService::class);
|
$setupService = app(TwoFactorSetupService::class);
|
||||||
|
|
||||||
['image_url_data' => $url, 'secret' => $secret] = cache()->remember(
|
['image_url_data' => $url, 'secret' => $secret] = cache()->remember(
|
||||||
@ -126,6 +130,7 @@ class EditProfile extends \Filament\Pages\Auth\EditProfile
|
|||||||
|
|
||||||
$options = new QROptions([
|
$options = new QROptions([
|
||||||
'svgLogo' => public_path('pelican.svg'),
|
'svgLogo' => public_path('pelican.svg'),
|
||||||
|
'svgLogoScale' => 0.05,
|
||||||
'addLogoSpace' => true,
|
'addLogoSpace' => true,
|
||||||
'logoSpaceWidth' => 13,
|
'logoSpaceWidth' => 13,
|
||||||
'logoSpaceHeight' => 13,
|
'logoSpaceHeight' => 13,
|
||||||
@ -133,22 +138,24 @@ class EditProfile extends \Filament\Pages\Auth\EditProfile
|
|||||||
|
|
||||||
// https://github.com/chillerlan/php-qrcode/blob/main/examples/svgWithLogo.php
|
// https://github.com/chillerlan/php-qrcode/blob/main/examples/svgWithLogo.php
|
||||||
|
|
||||||
// SVG logo options (see extended class)
|
|
||||||
$options->svgLogo = public_path('pelican.svg'); // logo from: https://github.com/simple-icons/simple-icons
|
|
||||||
$options->svgLogoScale = 0.05;
|
|
||||||
// $options->svgLogoCssClass = 'dark';
|
|
||||||
|
|
||||||
// QROptions
|
// QROptions
|
||||||
|
// @phpstan-ignore property.protected
|
||||||
$options->version = Version::AUTO;
|
$options->version = Version::AUTO;
|
||||||
// $options->outputInterface = QRSvgWithLogo::class;
|
// $options->outputInterface = QRSvgWithLogo::class;
|
||||||
|
// @phpstan-ignore property.protected
|
||||||
$options->outputBase64 = false;
|
$options->outputBase64 = false;
|
||||||
|
// @phpstan-ignore property.protected
|
||||||
$options->eccLevel = EccLevel::H; // ECC level H is necessary when using logos
|
$options->eccLevel = EccLevel::H; // ECC level H is necessary when using logos
|
||||||
|
// @phpstan-ignore property.protected
|
||||||
$options->addQuietzone = true;
|
$options->addQuietzone = true;
|
||||||
// $options->drawLightModules = true;
|
// $options->drawLightModules = true;
|
||||||
|
// @phpstan-ignore property.protected
|
||||||
$options->connectPaths = true;
|
$options->connectPaths = true;
|
||||||
|
// @phpstan-ignore property.protected
|
||||||
$options->drawCircularModules = true;
|
$options->drawCircularModules = true;
|
||||||
// $options->circleRadius = 0.45;
|
// $options->circleRadius = 0.45;
|
||||||
|
|
||||||
|
// @phpstan-ignore property.protected
|
||||||
$options->svgDefs = '<linearGradient id="gradient" x1="100%" y2="100%">
|
$options->svgDefs = '<linearGradient id="gradient" x1="100%" y2="100%">
|
||||||
<stop stop-color="#7dd4fc" offset="0"/>
|
<stop stop-color="#7dd4fc" offset="0"/>
|
||||||
<stop stop-color="#38bdf8" offset="0.5"/>
|
<stop stop-color="#38bdf8" offset="0.5"/>
|
||||||
@ -196,8 +203,8 @@ class EditProfile extends \Filament\Pages\Auth\EditProfile
|
|||||||
])->headerActions([
|
])->headerActions([
|
||||||
Action::make('Create')
|
Action::make('Create')
|
||||||
->successRedirectUrl(route('filament.admin.auth.profile', ['tab' => '-api-keys-tab']))
|
->successRedirectUrl(route('filament.admin.auth.profile', ['tab' => '-api-keys-tab']))
|
||||||
->action(function (Get $get, Action $action) {
|
->action(function (Get $get, Action $action, $user) {
|
||||||
$token = auth()->user()->createToken(
|
$token = $user->createToken(
|
||||||
$get('description'),
|
$get('description'),
|
||||||
$get('allowed_ips'),
|
$get('allowed_ips'),
|
||||||
);
|
);
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace App\Filament\Resources\UserResource\Pages;
|
namespace App\Filament\Resources\UserResource\Pages;
|
||||||
|
|
||||||
use App\Filament\Resources\UserResource;
|
use App\Filament\Resources\UserResource;
|
||||||
|
use App\Services\Exceptions\FilamentExceptionHandler;
|
||||||
use Filament\Actions;
|
use Filament\Actions;
|
||||||
use Filament\Resources\Pages\EditRecord;
|
use Filament\Resources\Pages\EditRecord;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
@ -66,7 +67,9 @@ class EditUser extends EditRecord
|
|||||||
protected function getHeaderActions(): array
|
protected function getHeaderActions(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
Actions\DeleteAction::make(),
|
Actions\DeleteAction::make()
|
||||||
|
->label(fn (User $user) => auth()->user()->id === $user->id ? 'Can\'t Delete Yourself' : ($user->servers()->count() > 0 ? 'User Has Servers' : 'Delete'))
|
||||||
|
->disabled(fn (User $user) => auth()->user()->id === $user->id || $user->servers()->count() > 0),
|
||||||
$this->getSaveFormAction()->formId('form'),
|
$this->getSaveFormAction()->formId('form'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -75,4 +78,9 @@ class EditUser extends EditRecord
|
|||||||
{
|
{
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function exception($exception, $stopPropagation): void
|
||||||
|
{
|
||||||
|
(new FilamentExceptionHandler())->handle($exception, $stopPropagation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ class EggShareController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function import(EggImportFormRequest $request): RedirectResponse
|
public function import(EggImportFormRequest $request): RedirectResponse
|
||||||
{
|
{
|
||||||
$egg = $this->importerService->handle($request->file('import_file'));
|
$egg = $this->importerService->fromFile($request->file('import_file'));
|
||||||
$this->alert->success(trans('admin/eggs.notices.imported'))->flash();
|
$this->alert->success(trans('admin/eggs.notices.imported'))->flash();
|
||||||
|
|
||||||
return redirect()->route('admin.eggs.view', ['egg' => $egg->id]);
|
return redirect()->route('admin.eggs.view', ['egg' => $egg->id]);
|
||||||
@ -61,7 +61,7 @@ class EggShareController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function update(EggImportFormRequest $request, Egg $egg): RedirectResponse
|
public function update(EggImportFormRequest $request, Egg $egg): RedirectResponse
|
||||||
{
|
{
|
||||||
$this->updateImporterService->handle($egg, $request->file('import_file'));
|
$this->updateImporterService->fromFile($egg, $request->file('import_file'));
|
||||||
$this->alert->success(trans('admin/eggs.notices.updated_via_import'))->flash();
|
$this->alert->success(trans('admin/eggs.notices.updated_via_import'))->flash();
|
||||||
|
|
||||||
return redirect()->route('admin.eggs.view', ['egg' => $egg]);
|
return redirect()->route('admin.eggs.view', ['egg' => $egg]);
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
namespace App\Http\Controllers\Admin\Nodes;
|
namespace App\Http\Controllers\Admin\Nodes;
|
||||||
|
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use App\Models\Node;
|
use App\Models\Node;
|
||||||
use Spatie\QueryBuilder\QueryBuilder;
|
use Spatie\QueryBuilder\QueryBuilder;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
@ -13,7 +12,7 @@ class NodeController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Returns a listing of nodes on the system.
|
* Returns a listing of nodes on the system.
|
||||||
*/
|
*/
|
||||||
public function index(Request $request): View
|
public function index(): View
|
||||||
{
|
{
|
||||||
$nodes = QueryBuilder::for(
|
$nodes = QueryBuilder::for(
|
||||||
Node::query()->withCount('servers')
|
Node::query()->withCount('servers')
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
namespace App\Http\Controllers\Admin\Nodes;
|
namespace App\Http\Controllers\Admin\Nodes;
|
||||||
|
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use App\Models\Node;
|
use App\Models\Node;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use App\Models\Allocation;
|
use App\Models\Allocation;
|
||||||
@ -29,16 +28,10 @@ class NodeViewController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Returns index view for a specific node on the system.
|
* Returns index view for a specific node on the system.
|
||||||
*/
|
*/
|
||||||
public function index(Request $request, Node $node): View
|
public function index(Node $node): View
|
||||||
{
|
{
|
||||||
$node->loadCount('servers');
|
$node->loadCount('servers');
|
||||||
|
|
||||||
$stats = Node::query()
|
|
||||||
->selectRaw('IFNULL(SUM(servers.memory), 0) as sum_memory, IFNULL(SUM(servers.disk), 0) as sum_disk')
|
|
||||||
->join('servers', 'servers.node_id', '=', 'nodes.id')
|
|
||||||
->where('node_id', '=', $node->id)
|
|
||||||
->first();
|
|
||||||
|
|
||||||
return view('admin.nodes.view.index', [
|
return view('admin.nodes.view.index', [
|
||||||
'node' => $node,
|
'node' => $node,
|
||||||
'version' => $this->versionService,
|
'version' => $this->versionService,
|
||||||
@ -48,7 +41,7 @@ class NodeViewController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Returns the settings page for a specific node.
|
* Returns the settings page for a specific node.
|
||||||
*/
|
*/
|
||||||
public function settings(Request $request, Node $node): View
|
public function settings(Node $node): View
|
||||||
{
|
{
|
||||||
return view('admin.nodes.view.settings', [
|
return view('admin.nodes.view.settings', [
|
||||||
'node' => $node,
|
'node' => $node,
|
||||||
@ -58,7 +51,7 @@ class NodeViewController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Return the node configuration page for a specific node.
|
* Return the node configuration page for a specific node.
|
||||||
*/
|
*/
|
||||||
public function configuration(Request $request, Node $node): View
|
public function configuration(Node $node): View
|
||||||
{
|
{
|
||||||
return view('admin.nodes.view.configuration', compact('node'));
|
return view('admin.nodes.view.configuration', compact('node'));
|
||||||
}
|
}
|
||||||
@ -66,7 +59,7 @@ class NodeViewController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Return the node allocation management page.
|
* Return the node allocation management page.
|
||||||
*/
|
*/
|
||||||
public function allocations(Request $request, Node $node): View
|
public function allocations(Node $node): View
|
||||||
{
|
{
|
||||||
$node->setRelation(
|
$node->setRelation(
|
||||||
'allocations',
|
'allocations',
|
||||||
@ -92,7 +85,7 @@ class NodeViewController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Return a listing of servers that exist for this specific node.
|
* Return a listing of servers that exist for this specific node.
|
||||||
*/
|
*/
|
||||||
public function servers(Request $request, Node $node): View
|
public function servers(Node $node): View
|
||||||
{
|
{
|
||||||
$this->plainInject([
|
$this->plainInject([
|
||||||
'node' => Collection::wrap($node->makeVisible(['daemon_token_id', 'daemon_token']))
|
'node' => Collection::wrap($node->makeVisible(['daemon_token_id', 'daemon_token']))
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
namespace App\Http\Controllers\Admin\Servers;
|
namespace App\Http\Controllers\Admin\Servers;
|
||||||
|
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Spatie\QueryBuilder\QueryBuilder;
|
use Spatie\QueryBuilder\QueryBuilder;
|
||||||
use Spatie\QueryBuilder\AllowedFilter;
|
use Spatie\QueryBuilder\AllowedFilter;
|
||||||
@ -16,7 +15,7 @@ class ServerController extends Controller
|
|||||||
* Returns all the servers that exist on the system using a paginated result set. If
|
* 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.
|
* a query is passed along in the request it is also passed to the repository function.
|
||||||
*/
|
*/
|
||||||
public function index(Request $request): View
|
public function index(): View
|
||||||
{
|
{
|
||||||
$servers = QueryBuilder::for(Server::query()->with('node', 'user', 'allocation'))
|
$servers = QueryBuilder::for(Server::query()->with('node', 'user', 'allocation'))
|
||||||
->allowedFilters([
|
->allowedFilters([
|
||||||
|
@ -3,13 +3,13 @@
|
|||||||
namespace App\Http\Controllers\Admin;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use App\Enums\ServerState;
|
use App\Enums\ServerState;
|
||||||
|
use Filament\Notifications\Notification;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Http\Response;
|
use Illuminate\Http\Response;
|
||||||
use App\Models\Mount;
|
use App\Models\Mount;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\Database;
|
use App\Models\Database;
|
||||||
use App\Models\MountServer;
|
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Prologue\Alerts\AlertsMessageBag;
|
use Prologue\Alerts\AlertsMessageBag;
|
||||||
use App\Exceptions\DisplayException;
|
use App\Exceptions\DisplayException;
|
||||||
@ -70,7 +70,7 @@ class ServersController extends Controller
|
|||||||
* @throws \App\Exceptions\DisplayException
|
* @throws \App\Exceptions\DisplayException
|
||||||
* @throws \App\Exceptions\Model\DataValidationException
|
* @throws \App\Exceptions\Model\DataValidationException
|
||||||
*/
|
*/
|
||||||
public function toggleInstall(Server $server): RedirectResponse
|
public function toggleInstall(Server $server)
|
||||||
{
|
{
|
||||||
if ($server->status === ServerState::InstallFailed) {
|
if ($server->status === ServerState::InstallFailed) {
|
||||||
throw new DisplayException(trans('admin/server.exceptions.marked_as_failed'));
|
throw new DisplayException(trans('admin/server.exceptions.marked_as_failed'));
|
||||||
@ -79,9 +79,13 @@ class ServersController extends Controller
|
|||||||
$server->status = $server->isInstalled() ? ServerState::Installing : null;
|
$server->status = $server->isInstalled() ? ServerState::Installing : null;
|
||||||
$server->save();
|
$server->save();
|
||||||
|
|
||||||
$this->alert->success(trans('admin/server.alerts.install_toggled'))->flash();
|
Notification::make()
|
||||||
|
->title('Success!')
|
||||||
|
->body(trans('admin/server.alerts.install_toggled'))
|
||||||
|
->success()
|
||||||
|
->send();
|
||||||
|
|
||||||
return redirect()->route('admin.servers.view.manage', $server->id);
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -90,12 +94,15 @@ class ServersController extends Controller
|
|||||||
* @throws \App\Exceptions\DisplayException
|
* @throws \App\Exceptions\DisplayException
|
||||||
* @throws \App\Exceptions\Model\DataValidationException
|
* @throws \App\Exceptions\Model\DataValidationException
|
||||||
*/
|
*/
|
||||||
public function reinstallServer(Server $server): RedirectResponse
|
public function reinstallServer(Server $server)
|
||||||
{
|
{
|
||||||
$this->reinstallService->handle($server);
|
$this->reinstallService->handle($server);
|
||||||
$this->alert->success(trans('admin/server.alerts.server_reinstalled'))->flash();
|
|
||||||
|
|
||||||
return redirect()->route('admin.servers.view.manage', $server->id);
|
Notification::make()
|
||||||
|
->title('Success!')
|
||||||
|
->body(trans('admin/server.alerts.server_reinstalled'))
|
||||||
|
->success()
|
||||||
|
->send();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -228,12 +235,7 @@ class ServersController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function addMount(Request $request, Server $server): RedirectResponse
|
public function addMount(Request $request, Server $server): RedirectResponse
|
||||||
{
|
{
|
||||||
$mountServer = (new MountServer())->forceFill([
|
$server->mounts()->attach($request->input('mount_id'));
|
||||||
'mount_id' => $request->input('mount_id'),
|
|
||||||
'server_id' => $server->id,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$mountServer->saveOrFail();
|
|
||||||
|
|
||||||
$this->alert->success('Mount was added successfully.')->flash();
|
$this->alert->success('Mount was added successfully.')->flash();
|
||||||
|
|
||||||
@ -245,7 +247,7 @@ class ServersController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function deleteMount(Server $server, Mount $mount): RedirectResponse
|
public function deleteMount(Server $server, Mount $mount): RedirectResponse
|
||||||
{
|
{
|
||||||
MountServer::where('mount_id', $mount->id)->where('server_id', $server->id)->delete();
|
$server->mounts()->detach($mount);
|
||||||
|
|
||||||
$this->alert->success('Mount was removed successfully.')->flash();
|
$this->alert->success('Mount was removed successfully.')->flash();
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ class UserController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Display user index page.
|
* Display user index page.
|
||||||
*/
|
*/
|
||||||
public function index(Request $request): View
|
public function index(): View
|
||||||
{
|
{
|
||||||
$users = QueryBuilder::for(
|
$users = QueryBuilder::for(
|
||||||
User::query()->select('users.*')
|
User::query()->select('users.*')
|
||||||
|
@ -33,10 +33,9 @@ class StoreNodeRequest extends ApplicationApiRequest
|
|||||||
'upload_size',
|
'upload_size',
|
||||||
'daemon_listen',
|
'daemon_listen',
|
||||||
'daemon_sftp',
|
'daemon_sftp',
|
||||||
|
'daemon_sftp_alias',
|
||||||
'daemon_base',
|
'daemon_base',
|
||||||
])->mapWithKeys(function ($value, $key) {
|
])->mapWithKeys(function ($value, $key) {
|
||||||
$key = ($key === 'daemon_sftp') ? 'daemon_sftp' : $key;
|
|
||||||
|
|
||||||
return [snake_case($key) => $value];
|
return [snake_case($key) => $value];
|
||||||
})->toArray();
|
})->toArray();
|
||||||
}
|
}
|
||||||
@ -60,12 +59,8 @@ class StoreNodeRequest extends ApplicationApiRequest
|
|||||||
public function validated($key = null, $default = null): array
|
public function validated($key = null, $default = null): array
|
||||||
{
|
{
|
||||||
$response = parent::validated();
|
$response = parent::validated();
|
||||||
$response['daemon_listen'] = $response['daemon_listen'];
|
|
||||||
$response['daemon_sftp'] = $response['daemon_sftp'];
|
|
||||||
$response['daemon_base'] = $response['daemon_base'] ?? (new Node())->getAttribute('daemon_base');
|
$response['daemon_base'] = $response['daemon_base'] ?? (new Node())->getAttribute('daemon_base');
|
||||||
|
|
||||||
unset($response['daemon_base'], $response['daemon_listen'], $response['daemon_sftp']);
|
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Models;
|
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
|
|
||||||
class MountServer extends Model
|
|
||||||
{
|
|
||||||
protected $table = 'mount_server';
|
|
||||||
|
|
||||||
public $timestamps = false;
|
|
||||||
|
|
||||||
protected $primaryKey = null;
|
|
||||||
|
|
||||||
public $incrementing = false;
|
|
||||||
}
|
|
@ -44,7 +44,7 @@ class Node extends Model
|
|||||||
'memory', 'memory_overallocate', 'disk',
|
'memory', 'memory_overallocate', 'disk',
|
||||||
'disk_overallocate', 'cpu', 'cpu_overallocate',
|
'disk_overallocate', 'cpu', 'cpu_overallocate',
|
||||||
'upload_size', 'daemon_base',
|
'upload_size', 'daemon_base',
|
||||||
'daemon_sftp', 'daemon_listen',
|
'daemon_sftp', 'daemon_sftp_alias', 'daemon_listen',
|
||||||
'description', 'maintenance_mode',
|
'description', 'maintenance_mode',
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -63,6 +63,7 @@ class Node extends Model
|
|||||||
'cpu_overallocate' => 'required|numeric|min:-1',
|
'cpu_overallocate' => 'required|numeric|min:-1',
|
||||||
'daemon_base' => 'sometimes|required|regex:/^([\/][\d\w.\-\/]+)$/',
|
'daemon_base' => 'sometimes|required|regex:/^([\/][\d\w.\-\/]+)$/',
|
||||||
'daemon_sftp' => 'required|numeric|between:1,65535',
|
'daemon_sftp' => 'required|numeric|between:1,65535',
|
||||||
|
'daemon_sftp_alias' => 'nullable|string',
|
||||||
'daemon_listen' => 'required|numeric|between:1,65535',
|
'daemon_listen' => 'required|numeric|between:1,65535',
|
||||||
'maintenance_mode' => 'boolean',
|
'maintenance_mode' => 'boolean',
|
||||||
'upload_size' => 'int|between:1,1024',
|
'upload_size' => 'int|between:1,1024',
|
||||||
@ -103,6 +104,10 @@ class Node extends Model
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int $servers_sum_memory = 0;
|
||||||
|
public int $servers_sum_disk = 0;
|
||||||
|
public int $servers_sum_cpu = 0;
|
||||||
|
|
||||||
public function getRouteKeyName(): string
|
public function getRouteKeyName(): string
|
||||||
{
|
{
|
||||||
return 'id';
|
return 'id';
|
||||||
|
@ -5,6 +5,7 @@ namespace App\Models;
|
|||||||
use App\Enums\ServerState;
|
use App\Enums\ServerState;
|
||||||
use App\Exceptions\Http\Connection\DaemonConnectionException;
|
use App\Exceptions\Http\Connection\DaemonConnectionException;
|
||||||
use GuzzleHttp\Exception\GuzzleException;
|
use GuzzleHttp\Exception\GuzzleException;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
use Illuminate\Database\Query\JoinClause;
|
use Illuminate\Database\Query\JoinClause;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
@ -13,7 +14,6 @@ use Illuminate\Database\Eloquent\Relations\HasOne;
|
|||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\Relations\MorphToMany;
|
use Illuminate\Database\Eloquent\Relations\MorphToMany;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
|
|
||||||
use App\Exceptions\Http\Server\ServerStateConflictException;
|
use App\Exceptions\Http\Server\ServerStateConflictException;
|
||||||
|
|
||||||
class Server extends Model
|
class Server extends Model
|
||||||
@ -224,12 +224,9 @@ class Server extends Model
|
|||||||
return $this->hasMany(Backup::class);
|
return $this->hasMany(Backup::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function mounts(): BelongsToMany
|
||||||
* Returns all mounts that have this server has mounted.
|
|
||||||
*/
|
|
||||||
public function mounts(): HasManyThrough
|
|
||||||
{
|
{
|
||||||
return $this->hasManyThrough(Mount::class, MountServer::class, 'server_id', 'id', 'id', 'mount_id');
|
return $this->belongsToMany(Mount::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -6,6 +6,7 @@ use App\Extensions\Themes\Theme;
|
|||||||
use App\Models;
|
use App\Models;
|
||||||
use App\Models\ApiKey;
|
use App\Models\ApiKey;
|
||||||
use App\Models\Node;
|
use App\Models\Node;
|
||||||
|
use App\Services\Helpers\SoftwareVersionService;
|
||||||
use Dedoc\Scramble\Scramble;
|
use Dedoc\Scramble\Scramble;
|
||||||
use Dedoc\Scramble\Support\Generator\OpenApi;
|
use Dedoc\Scramble\Support\Generator\OpenApi;
|
||||||
use Dedoc\Scramble\Support\Generator\SecurityScheme;
|
use Dedoc\Scramble\Support\Generator\SecurityScheme;
|
||||||
@ -30,8 +31,9 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
{
|
{
|
||||||
Schema::defaultStringLength(191);
|
Schema::defaultStringLength(191);
|
||||||
|
|
||||||
View::share('appVersion', $this->versionData()['version'] ?? 'undefined');
|
$versionData = app(SoftwareVersionService::class)->versionData();
|
||||||
View::share('appIsGit', $this->versionData()['is_git'] ?? false);
|
View::share('appVersion', $versionData['version'] ?? 'undefined');
|
||||||
|
View::share('appIsGit', $versionData['is_git'] ?? false);
|
||||||
|
|
||||||
Paginator::useBootstrap();
|
Paginator::useBootstrap();
|
||||||
|
|
||||||
@ -96,34 +98,6 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
Scramble::ignoreDefaultRoutes();
|
Scramble::ignoreDefaultRoutes();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return version information for the footer.
|
|
||||||
*/
|
|
||||||
protected function versionData(): array
|
|
||||||
{
|
|
||||||
return cache()->remember('git-version', 5, function () {
|
|
||||||
if (file_exists(base_path('.git/HEAD'))) {
|
|
||||||
$head = explode(' ', file_get_contents(base_path('.git/HEAD')));
|
|
||||||
|
|
||||||
if (array_key_exists(1, $head)) {
|
|
||||||
$path = base_path('.git/' . trim($head[1]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($path) && file_exists($path)) {
|
|
||||||
return [
|
|
||||||
'version' => substr(file_get_contents($path), 0, 8),
|
|
||||||
'is_git' => true,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'version' => config('app.version'),
|
|
||||||
'is_git' => false,
|
|
||||||
];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public function bootAuth(): void
|
public function bootAuth(): void
|
||||||
{
|
{
|
||||||
Sanctum::usePersonalAccessTokenModel(ApiKey::class);
|
Sanctum::usePersonalAccessTokenModel(ApiKey::class);
|
||||||
|
@ -124,38 +124,6 @@ class EggConfigurationService
|
|||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Replaces the legacy modifies from eggs with their new counterpart. The legacy Daemon would
|
|
||||||
* set SERVER_MEMORY, SERVER_IP, and SERVER_PORT with their respective values on the Daemon
|
|
||||||
* side. Ensure that anything referencing those properly replaces them with the matching config
|
|
||||||
* value.
|
|
||||||
*/
|
|
||||||
protected function replaceLegacyModifiers(string $key, string $value): string
|
|
||||||
{
|
|
||||||
switch ($key) {
|
|
||||||
case 'config.docker.interface':
|
|
||||||
$replace = 'config.docker.network.interface';
|
|
||||||
break;
|
|
||||||
case 'server.build.env.SERVER_MEMORY':
|
|
||||||
case 'env.SERVER_MEMORY':
|
|
||||||
$replace = 'server.build.memory';
|
|
||||||
break;
|
|
||||||
case 'server.build.env.SERVER_IP':
|
|
||||||
case 'env.SERVER_IP':
|
|
||||||
$replace = 'server.build.default.ip';
|
|
||||||
break;
|
|
||||||
case 'server.build.env.SERVER_PORT':
|
|
||||||
case 'env.SERVER_PORT':
|
|
||||||
$replace = 'server.build.default.port';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// By default, we don't need to change anything, only if we ended up matching a specific legacy item.
|
|
||||||
$replace = $key;
|
|
||||||
}
|
|
||||||
|
|
||||||
return str_replace("{{{$key}}}", "{{{$replace}}}", $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function matchAndReplaceKeys(mixed $value, array $structure): mixed
|
protected function matchAndReplaceKeys(mixed $value, array $structure): mixed
|
||||||
{
|
{
|
||||||
preg_match_all('/{{(?<key>[\w.-]*)}}/', $value, $matches);
|
preg_match_all('/{{(?<key>[\w.-]*)}}/', $value, $matches);
|
||||||
@ -175,8 +143,6 @@ class EggConfigurationService
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$value = $this->replaceLegacyModifiers($key, $value);
|
|
||||||
|
|
||||||
// We don't want to do anything with config keys since the Daemon will need to handle
|
// We don't want to do anything with config keys since the Daemon will need to handle
|
||||||
// that. For example, the Spigot egg uses "config.docker.interface" to identify the Docker
|
// that. For example, the Spigot egg uses "config.docker.interface" to identify the Docker
|
||||||
// interface to proxy through, but the Panel would be unaware of that.
|
// interface to proxy through, but the Panel would be unaware of that.
|
||||||
@ -198,7 +164,7 @@ class EggConfigurationService
|
|||||||
// variable from the server configuration.
|
// variable from the server configuration.
|
||||||
$plucked = Arr::get(
|
$plucked = Arr::get(
|
||||||
$structure,
|
$structure,
|
||||||
preg_replace('/^env\./', 'build.env.', $key),
|
preg_replace('/^env\./', 'build.environment.', $key),
|
||||||
''
|
''
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -10,6 +10,17 @@ use App\Exceptions\Service\InvalidFileUploadException;
|
|||||||
|
|
||||||
class EggParserService
|
class EggParserService
|
||||||
{
|
{
|
||||||
|
public const UPGRADE_VARIABLES = [
|
||||||
|
'server.build.env.SERVER_IP' => 'server.allocations.default.ip',
|
||||||
|
'server.build.default.ip' => 'server.allocations.default.ip',
|
||||||
|
'server.build.env.SERVER_PORT' => 'server.allocations.default.port',
|
||||||
|
'server.build.default.port' => 'server.allocations.default.port',
|
||||||
|
'server.build.env.SERVER_MEMORY' => 'server.build.memory_limit',
|
||||||
|
'server.build.memory' => 'server.build.memory_limit',
|
||||||
|
'server.build.env.' => 'server.environment.',
|
||||||
|
'server.build.environment.' => 'server.environment.',
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes an uploaded file and parses out the egg configuration from within.
|
* Takes an uploaded file and parses out the egg configuration from within.
|
||||||
*
|
*
|
||||||
@ -26,11 +37,20 @@ class EggParserService
|
|||||||
|
|
||||||
$version = $parsed['meta']['version'] ?? '';
|
$version = $parsed['meta']['version'] ?? '';
|
||||||
|
|
||||||
return match ($version) {
|
$parsed = match ($version) {
|
||||||
'PTDL_v1' => $this->convertToV2($parsed),
|
'PTDL_v1' => $this->convertToV2($parsed),
|
||||||
'PTDL_v2' => $parsed,
|
'PTDL_v2' => $parsed,
|
||||||
default => throw new InvalidFileUploadException('The JSON file provided is not in a format that can be recognized.')
|
default => throw new InvalidFileUploadException('The JSON file provided is not in a format that can be recognized.')
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Make sure we only use recent variable format from now on
|
||||||
|
$parsed['config']['files'] = str_replace(
|
||||||
|
array_keys(self::UPGRADE_VARIABLES),
|
||||||
|
array_values(self::UPGRADE_VARIABLES),
|
||||||
|
$parsed['config']['files'] ?? '',
|
||||||
|
);
|
||||||
|
|
||||||
|
return $parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -9,6 +9,7 @@ use Illuminate\Http\UploadedFile;
|
|||||||
use App\Models\EggVariable;
|
use App\Models\EggVariable;
|
||||||
use Illuminate\Database\ConnectionInterface;
|
use Illuminate\Database\ConnectionInterface;
|
||||||
use App\Services\Eggs\EggParserService;
|
use App\Services\Eggs\EggParserService;
|
||||||
|
use Spatie\TemporaryDirectory\TemporaryDirectory;
|
||||||
|
|
||||||
class EggImporterService
|
class EggImporterService
|
||||||
{
|
{
|
||||||
@ -21,7 +22,7 @@ class EggImporterService
|
|||||||
*
|
*
|
||||||
* @throws \App\Exceptions\Service\InvalidFileUploadException|\Throwable
|
* @throws \App\Exceptions\Service\InvalidFileUploadException|\Throwable
|
||||||
*/
|
*/
|
||||||
public function handle(UploadedFile $file): Egg
|
public function fromFile(UploadedFile $file): Egg
|
||||||
{
|
{
|
||||||
$parsed = $this->parser->handle($file);
|
$parsed = $this->parser->handle($file);
|
||||||
|
|
||||||
@ -45,4 +46,20 @@ class EggImporterService
|
|||||||
return $egg;
|
return $egg;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Take an url and parse it into a new egg.
|
||||||
|
*
|
||||||
|
* @throws \App\Exceptions\Service\InvalidFileUploadException|\Throwable
|
||||||
|
*/
|
||||||
|
public function fromUrl(string $url): Egg
|
||||||
|
{
|
||||||
|
$info = pathinfo($url);
|
||||||
|
$tmpDir = TemporaryDirectory::make()->deleteWhenDestroyed();
|
||||||
|
$tmpPath = $tmpDir->path($info['basename']);
|
||||||
|
|
||||||
|
file_put_contents($tmpPath, file_get_contents($url));
|
||||||
|
|
||||||
|
return $this->fromFile(new UploadedFile($tmpPath, $info['basename'], 'application/json'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ use Illuminate\Support\Collection;
|
|||||||
use App\Models\EggVariable;
|
use App\Models\EggVariable;
|
||||||
use Illuminate\Database\ConnectionInterface;
|
use Illuminate\Database\ConnectionInterface;
|
||||||
use App\Services\Eggs\EggParserService;
|
use App\Services\Eggs\EggParserService;
|
||||||
|
use Spatie\TemporaryDirectory\TemporaryDirectory;
|
||||||
|
|
||||||
class EggUpdateImporterService
|
class EggUpdateImporterService
|
||||||
{
|
{
|
||||||
@ -23,7 +24,7 @@ class EggUpdateImporterService
|
|||||||
*
|
*
|
||||||
* @throws \App\Exceptions\Service\InvalidFileUploadException|\Throwable
|
* @throws \App\Exceptions\Service\InvalidFileUploadException|\Throwable
|
||||||
*/
|
*/
|
||||||
public function handle(Egg $egg, UploadedFile $file): Egg
|
public function fromFile(Egg $egg, UploadedFile $file): Egg
|
||||||
{
|
{
|
||||||
$parsed = $this->parser->handle($file);
|
$parsed = $this->parser->handle($file);
|
||||||
|
|
||||||
@ -47,4 +48,20 @@ class EggUpdateImporterService
|
|||||||
return $egg->refresh();
|
return $egg->refresh();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update an existing Egg using an url.
|
||||||
|
*
|
||||||
|
* @throws \App\Exceptions\Service\InvalidFileUploadException|\Throwable
|
||||||
|
*/
|
||||||
|
public function fromUrl(Egg $egg, string $url): Egg
|
||||||
|
{
|
||||||
|
$info = pathinfo($url);
|
||||||
|
$tmpDir = TemporaryDirectory::make()->deleteWhenDestroyed();
|
||||||
|
$tmpPath = $tmpDir->path($info['basename']);
|
||||||
|
|
||||||
|
file_put_contents($tmpPath, file_get_contents($url));
|
||||||
|
|
||||||
|
return $this->fromFile($egg, new UploadedFile($tmpPath, $info['basename'], 'application/json'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
24
app/Services/Exceptions/FilamentExceptionHandler.php
Normal file
24
app/Services/Exceptions/FilamentExceptionHandler.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -49,6 +49,14 @@ class SoftwareVersionService
|
|||||||
return Arr::get(self::$result, 'discord') ?? 'https://pelican.dev/discord';
|
return Arr::get(self::$result, 'discord') ?? 'https://pelican.dev/discord';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the donation URL.
|
||||||
|
*/
|
||||||
|
public function getDonations(): string
|
||||||
|
{
|
||||||
|
return Arr::get(self::$result, 'donate') ?? 'https://pelican.dev/donate';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the current version of the panel is the latest.
|
* Determine if the current version of the panel is the latest.
|
||||||
*/
|
*/
|
||||||
@ -93,8 +101,28 @@ class SoftwareVersionService
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDonations(): string
|
public function versionData(): array
|
||||||
{
|
{
|
||||||
return 'https://github.com';
|
return cache()->remember('git-version', 5, function () {
|
||||||
|
if (file_exists(base_path('.git/HEAD'))) {
|
||||||
|
$head = explode(' ', file_get_contents(base_path('.git/HEAD')));
|
||||||
|
|
||||||
|
if (array_key_exists(1, $head)) {
|
||||||
|
$path = base_path('.git/' . trim($head[1]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($path) && file_exists($path)) {
|
||||||
|
return [
|
||||||
|
'version' => 'canary (' . substr(file_get_contents($path), 0, 8) . ')',
|
||||||
|
'is_git' => true,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'version' => config('app.version'),
|
||||||
|
'is_git' => false,
|
||||||
|
];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,8 @@ class NodeUpdateService
|
|||||||
*/
|
*/
|
||||||
public function handle(Node $node, array $data, bool $resetToken = false): Node
|
public function handle(Node $node, array $data, bool $resetToken = false): Node
|
||||||
{
|
{
|
||||||
|
$data['id'] = $node->id;
|
||||||
|
|
||||||
if ($resetToken) {
|
if ($resetToken) {
|
||||||
$data['daemon_token'] = Str::random(Node::DAEMON_TOKEN_LENGTH);
|
$data['daemon_token'] = Str::random(Node::DAEMON_TOKEN_LENGTH);
|
||||||
$data['daemon_token_id'] = Str::random(Node::DAEMON_TOKEN_ID_LENGTH);
|
$data['daemon_token_id'] = Str::random(Node::DAEMON_TOKEN_ID_LENGTH);
|
||||||
@ -35,15 +37,9 @@ class NodeUpdateService
|
|||||||
[$updated, $exception] = $this->connection->transaction(function () use ($data, $node) {
|
[$updated, $exception] = $this->connection->transaction(function () use ($data, $node) {
|
||||||
/** @var \App\Models\Node $updated */
|
/** @var \App\Models\Node $updated */
|
||||||
$updated = $node->replicate();
|
$updated = $node->replicate();
|
||||||
|
$updated->exists = true;
|
||||||
$updated->forceFill($data)->save();
|
$updated->forceFill($data)->save();
|
||||||
try {
|
try {
|
||||||
// If we're changing the FQDN for the node, use the newly provided FQDN for the connection
|
|
||||||
// address. This should alleviate issues where the node gets pointed to a "valid" FQDN that
|
|
||||||
// isn't actually running the daemon software, and therefore you can't actually change it
|
|
||||||
// back.
|
|
||||||
//
|
|
||||||
// This makes more sense anyways, because only the Panel uses the FQDN for connecting, the
|
|
||||||
// node doesn't actually care about this.
|
|
||||||
$node->fqdn = $updated->fqdn;
|
$node->fqdn = $updated->fqdn;
|
||||||
|
|
||||||
$this->configurationRepository->setNode($node)->update($updated);
|
$this->configurationRepository->setNode($node)->update($updated);
|
||||||
|
@ -40,7 +40,7 @@ class ServerConfigurationStructureService
|
|||||||
*/
|
*/
|
||||||
protected function returnFormat(Server $server): array
|
protected function returnFormat(Server $server): array
|
||||||
{
|
{
|
||||||
return [
|
$response = [
|
||||||
'uuid' => $server->uuid,
|
'uuid' => $server->uuid,
|
||||||
'meta' => [
|
'meta' => [
|
||||||
'name' => $server->name,
|
'name' => $server->name,
|
||||||
@ -50,7 +50,6 @@ class ServerConfigurationStructureService
|
|||||||
'environment' => $this->environment->handle($server),
|
'environment' => $this->environment->handle($server),
|
||||||
'invocation' => $server->startup,
|
'invocation' => $server->startup,
|
||||||
'skip_egg_scripts' => $server->skip_scripts,
|
'skip_egg_scripts' => $server->skip_scripts,
|
||||||
'labels' => $server->docker_labels,
|
|
||||||
'build' => [
|
'build' => [
|
||||||
'memory_limit' => $server->memory,
|
'memory_limit' => $server->memory,
|
||||||
'swap' => $server->swap,
|
'swap' => $server->swap,
|
||||||
@ -72,17 +71,27 @@ class ServerConfigurationStructureService
|
|||||||
],
|
],
|
||||||
'mappings' => $server->getAllocationMappings(),
|
'mappings' => $server->getAllocationMappings(),
|
||||||
],
|
],
|
||||||
'mounts' => $server->mounts->map(function (Mount $mount) {
|
|
||||||
return [
|
|
||||||
'source' => $mount->source,
|
|
||||||
'target' => $mount->target,
|
|
||||||
'read_only' => $mount->read_only,
|
|
||||||
];
|
|
||||||
}),
|
|
||||||
'egg' => [
|
'egg' => [
|
||||||
'id' => $server->egg->uuid,
|
'id' => $server->egg->uuid,
|
||||||
'file_denylist' => $server->egg->inherit_file_denylist,
|
'file_denylist' => $server->egg->inherit_file_denylist,
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (!empty($server->docker_labels)) {
|
||||||
|
$response['labels'] = $server->docker_labels;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($server->mounts->isNotEmpty()) {
|
||||||
|
$response['mounts'] = $server->mounts->map(function (Mount $mount) {
|
||||||
|
return [
|
||||||
|
'source' => $mount->source,
|
||||||
|
'target' => $mount->target,
|
||||||
|
'read_only' => $mount->read_only,
|
||||||
|
];
|
||||||
|
})->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace App\Services\Servers;
|
namespace App\Services\Servers;
|
||||||
|
|
||||||
use App\Enums\ServerState;
|
use App\Enums\ServerState;
|
||||||
|
use Filament\Notifications\Notification;
|
||||||
use Webmozart\Assert\Assert;
|
use Webmozart\Assert\Assert;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Repositories\Daemon\DaemonServerRepository;
|
use App\Repositories\Daemon\DaemonServerRepository;
|
||||||
@ -26,7 +27,7 @@ class SuspensionService
|
|||||||
*
|
*
|
||||||
* @throws \Throwable
|
* @throws \Throwable
|
||||||
*/
|
*/
|
||||||
public function toggle(Server $server, string $action = self::ACTION_SUSPEND): void
|
public function toggle(Server $server, string $action = self::ACTION_SUSPEND)
|
||||||
{
|
{
|
||||||
Assert::oneOf($action, [self::ACTION_SUSPEND, self::ACTION_UNSUSPEND]);
|
Assert::oneOf($action, [self::ACTION_SUSPEND, self::ACTION_UNSUSPEND]);
|
||||||
|
|
||||||
@ -35,11 +36,12 @@ class SuspensionService
|
|||||||
// suspended in the database. Additionally, nothing needs to happen if the server
|
// suspended in the database. Additionally, nothing needs to happen if the server
|
||||||
// is not suspended, and we try to un-suspend the instance.
|
// is not suspended, and we try to un-suspend the instance.
|
||||||
if ($isSuspending === $server->isSuspended()) {
|
if ($isSuspending === $server->isSuspended()) {
|
||||||
return;
|
return Notification::make()->danger()->title('Failed!')->body('Server is already suspended!')->send();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the server is currently being transferred.
|
// Check if the server is currently being transferred.
|
||||||
if (!is_null($server->transfer)) {
|
if (!is_null($server->transfer)) {
|
||||||
|
Notification::make()->danger()->title('Failed!')->body('Server is currently being transferred.')->send();
|
||||||
throw new ConflictHttpException('Cannot toggle suspension status on a server that is currently being transferred.');
|
throw new ConflictHttpException('Cannot toggle suspension status on a server that is currently being transferred.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ class TransferServerService
|
|||||||
{
|
{
|
||||||
$node_id = $data['node_id'];
|
$node_id = $data['node_id'];
|
||||||
$allocation_id = intval($data['allocation_id']);
|
$allocation_id = intval($data['allocation_id']);
|
||||||
$additional_allocations = array_map('intval', $data['allocation_additional'] ?? []);
|
$additional_allocations = array_map(intval(...), $data['allocation_additional'] ?? []);
|
||||||
|
|
||||||
// Check if the node is viable for the transfer.
|
// Check if the node is viable for the transfer.
|
||||||
$node = Node::query()
|
$node = Node::query()
|
||||||
|
@ -46,6 +46,7 @@ class ServerTransformer extends BaseClientTransformer
|
|||||||
'is_node_under_maintenance' => $server->node->isUnderMaintenance(),
|
'is_node_under_maintenance' => $server->node->isUnderMaintenance(),
|
||||||
'sftp_details' => [
|
'sftp_details' => [
|
||||||
'ip' => $server->node->fqdn,
|
'ip' => $server->node->fqdn,
|
||||||
|
'alias' => $server->node->daemon_sftp_alias,
|
||||||
'port' => $server->node->daemon_sftp,
|
'port' => $server->node->daemon_sftp,
|
||||||
],
|
],
|
||||||
'description' => $server->description,
|
'description' => $server->description,
|
||||||
|
@ -9,10 +9,7 @@ return Application::configure(basePath: dirname(__DIR__))
|
|||||||
\Prologue\Alerts\AlertsServiceProvider::class,
|
\Prologue\Alerts\AlertsServiceProvider::class,
|
||||||
])
|
])
|
||||||
->withRouting(
|
->withRouting(
|
||||||
web: __DIR__.'/../routes/web.php',
|
|
||||||
// api: __DIR__.'/../routes/api.php',
|
|
||||||
commands: __DIR__.'/../routes/console.php',
|
commands: __DIR__.'/../routes/console.php',
|
||||||
// channels: __DIR__.'/../routes/channels.php',
|
|
||||||
health: '/up',
|
health: '/up',
|
||||||
)
|
)
|
||||||
->withMiddleware(function (Middleware $middleware) {
|
->withMiddleware(function (Middleware $middleware) {
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
"s1lentium/iptools": "~1.2.0",
|
"s1lentium/iptools": "~1.2.0",
|
||||||
"spatie/laravel-fractal": "^6.2",
|
"spatie/laravel-fractal": "^6.2",
|
||||||
"spatie/laravel-query-builder": "^5.8.1",
|
"spatie/laravel-query-builder": "^5.8.1",
|
||||||
|
"spatie/temporary-directory": "^2.2",
|
||||||
"symfony/http-client": "^7.1",
|
"symfony/http-client": "^7.1",
|
||||||
"symfony/mailgun-mailer": "^7.1",
|
"symfony/mailgun-mailer": "^7.1",
|
||||||
"symfony/postmark-mailer": "^7.0.7",
|
"symfony/postmark-mailer": "^7.0.7",
|
||||||
|
739
composer.lock
generated
739
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -75,10 +75,10 @@ class EggSeeder extends Seeder
|
|||||||
->first();
|
->first();
|
||||||
|
|
||||||
if ($egg instanceof Egg) {
|
if ($egg instanceof Egg) {
|
||||||
$this->updateImporterService->handle($egg, $file);
|
$this->updateImporterService->fromFile($egg, $file);
|
||||||
$this->command->info('Updated ' . $decoded['name']);
|
$this->command->info('Updated ' . $decoded['name']);
|
||||||
} else {
|
} else {
|
||||||
$this->importerService->handle($file);
|
$this->importerService->fromFile($file);
|
||||||
$this->command->comment('Created ' . $decoded['name']);
|
$this->command->comment('Created ' . $decoded['name']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,10 @@
|
|||||||
"version": "PTDL_v2",
|
"version": "PTDL_v2",
|
||||||
"update_url": null
|
"update_url": null
|
||||||
},
|
},
|
||||||
"exported_at": "2024-05-08T11:47:32+02:00",
|
"exported_at": "2024-06-04T22:51:49+00:00",
|
||||||
"name": "Bungeecord",
|
"name": "Bungeecord",
|
||||||
"author": "panel@example.com",
|
"author": "panel@example.com",
|
||||||
|
"uuid": "9e6b409e-4028-4947-aea8-50a2c404c271",
|
||||||
"description": "For a long time, Minecraft server owners have had a dream that encompasses a free, easy, and reliable way to connect multiple Minecraft servers together. BungeeCord is the answer to said dream. Whether you are a small server wishing to string multiple game-modes together, or the owner of the ShotBow Network, BungeeCord is the ideal solution for you. With the help of BungeeCord, you will be able to unlock your community's full potential.",
|
"description": "For a long time, Minecraft server owners have had a dream that encompasses a free, easy, and reliable way to connect multiple Minecraft servers together. BungeeCord is the answer to said dream. Whether you are a small server wishing to string multiple game-modes together, or the owner of the ShotBow Network, BungeeCord is the ideal solution for you. With the help of BungeeCord, you will be able to unlock your community's full potential.",
|
||||||
"features": [
|
"features": [
|
||||||
"eula",
|
"eula",
|
||||||
@ -23,7 +24,7 @@
|
|||||||
"file_denylist": [],
|
"file_denylist": [],
|
||||||
"startup": "java -Xms128M -XX:MaxRAMPercentage=95.0 -jar {{SERVER_JARFILE}}",
|
"startup": "java -Xms128M -XX:MaxRAMPercentage=95.0 -jar {{SERVER_JARFILE}}",
|
||||||
"config": {
|
"config": {
|
||||||
"files": "{\r\n \"config.yml\": {\r\n \"parser\": \"yaml\",\r\n \"find\": {\r\n \"listeners[0].query_port\": \"{{server.build.default.port}}\",\r\n \"listeners[0].host\": \"0.0.0.0:{{server.build.default.port}}\",\r\n \"servers.*.address\": {\r\n \"regex:^(127\\\\.0\\\\.0\\\\.1|localhost)(:\\\\d{1,5})?$\": \"{{config.docker.interface}}$2\"\r\n }\r\n }\r\n }\r\n}",
|
"files": "{\r\n \"config.yml\": {\r\n \"parser\": \"yaml\",\r\n \"find\": {\r\n \"listeners[0].query_port\": \"{{server.allocations.default.port}}\",\r\n \"listeners[0].host\": \"0.0.0.0:{{server.allocations.default.port}}\",\r\n \"servers.*.address\": {\r\n \"regex:^(127\\\\.0\\\\.0\\\\.1|localhost)(:\\\\d{1,5})?$\": \"{{config.docker.interface}}$2\"\r\n }\r\n }\r\n }\r\n}",
|
||||||
"startup": "{\r\n \"done\": \"Listening on \"\r\n}",
|
"startup": "{\r\n \"done\": \"Listening on \"\r\n}",
|
||||||
"logs": "{}",
|
"logs": "{}",
|
||||||
"stop": "end"
|
"stop": "end"
|
||||||
@ -44,6 +45,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|alpha_num|between:1,6",
|
"rules": "required|alpha_num|between:1,6",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -54,6 +56,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/",
|
"rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -4,9 +4,10 @@
|
|||||||
"version": "PTDL_v2",
|
"version": "PTDL_v2",
|
||||||
"update_url": null
|
"update_url": null
|
||||||
},
|
},
|
||||||
"exported_at": "2024-05-08T11:47:58+02:00",
|
"exported_at": "2024-06-04T22:51:58+00:00",
|
||||||
"name": "Forge Minecraft",
|
"name": "Forge Minecraft",
|
||||||
"author": "panel@example.com",
|
"author": "panel@example.com",
|
||||||
|
"uuid": "ed072427-f209-4603-875c-f540c6dd5a65",
|
||||||
"description": "Minecraft Forge Server. Minecraft Forge is a modding API (Application Programming Interface), which makes it easier to create mods, and also make sure mods are compatible with each other.",
|
"description": "Minecraft Forge Server. Minecraft Forge is a modding API (Application Programming Interface), which makes it easier to create mods, and also make sure mods are compatible with each other.",
|
||||||
"features": [
|
"features": [
|
||||||
"eula",
|
"eula",
|
||||||
@ -23,7 +24,7 @@
|
|||||||
"file_denylist": [],
|
"file_denylist": [],
|
||||||
"startup": "java -Xms128M -XX:MaxRAMPercentage=95.0 -Dterminal.jline=false -Dterminal.ansi=true $( [[ ! -f unix_args.txt ]] && printf %s \"-jar {{SERVER_JARFILE}}\" || printf %s \"@unix_args.txt\" )",
|
"startup": "java -Xms128M -XX:MaxRAMPercentage=95.0 -Dterminal.jline=false -Dterminal.ansi=true $( [[ ! -f unix_args.txt ]] && printf %s \"-jar {{SERVER_JARFILE}}\" || printf %s \"@unix_args.txt\" )",
|
||||||
"config": {
|
"config": {
|
||||||
"files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"server-port\": \"{{server.build.default.port}}\",\r\n \"query.port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}",
|
"files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"server-port\": \"{{server.allocations.default.port}}\",\r\n \"query.port\": \"{{server.allocations.default.port}}\"\r\n }\r\n }\r\n}",
|
||||||
"startup": "{\r\n \"done\": \")! For help, type \"\r\n}",
|
"startup": "{\r\n \"done\": \")! For help, type \"\r\n}",
|
||||||
"logs": "{}",
|
"logs": "{}",
|
||||||
"stop": "stop"
|
"stop": "stop"
|
||||||
@ -44,6 +45,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/",
|
"rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -54,6 +56,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|string|max:9",
|
"rules": "required|string|max:9",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -64,6 +67,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|string|in:recommended,latest",
|
"rules": "required|string|in:recommended,latest",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -74,6 +78,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "nullable|regex:\/^[0-9\\.\\-]+$\/",
|
"rules": "nullable|regex:\/^[0-9\\.\\-]+$\/",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -4,9 +4,10 @@
|
|||||||
"version": "PTDL_v2",
|
"version": "PTDL_v2",
|
||||||
"update_url": null
|
"update_url": null
|
||||||
},
|
},
|
||||||
"exported_at": "2024-05-08T11:48:34+02:00",
|
"exported_at": "2024-06-04T22:51:57+00:00",
|
||||||
"name": "Paper",
|
"name": "Paper",
|
||||||
"author": "parker@example.com",
|
"author": "parker@example.com",
|
||||||
|
"uuid": "5da37ef6-58da-4169-90a6-e683e1721247",
|
||||||
"description": "High performance Spigot fork that aims to fix gameplay and mechanics inconsistencies.",
|
"description": "High performance Spigot fork that aims to fix gameplay and mechanics inconsistencies.",
|
||||||
"features": [
|
"features": [
|
||||||
"eula",
|
"eula",
|
||||||
@ -23,7 +24,7 @@
|
|||||||
"file_denylist": [],
|
"file_denylist": [],
|
||||||
"startup": "java -Xms128M -XX:MaxRAMPercentage=95.0 -Dterminal.jline=false -Dterminal.ansi=true -jar {{SERVER_JARFILE}}",
|
"startup": "java -Xms128M -XX:MaxRAMPercentage=95.0 -Dterminal.jline=false -Dterminal.ansi=true -jar {{SERVER_JARFILE}}",
|
||||||
"config": {
|
"config": {
|
||||||
"files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"server-port\": \"{{server.build.default.port}}\",\r\n \"query.port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}",
|
"files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"server-port\": \"{{server.allocations.default.port}}\",\r\n \"query.port\": \"{{server.allocations.default.port}}\"\r\n }\r\n }\r\n}",
|
||||||
"startup": "{\r\n \"done\": \")! For help, type \"\r\n}",
|
"startup": "{\r\n \"done\": \")! For help, type \"\r\n}",
|
||||||
"logs": "{}",
|
"logs": "{}",
|
||||||
"stop": "stop"
|
"stop": "stop"
|
||||||
@ -44,6 +45,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "nullable|string|max:20",
|
"rules": "nullable|string|max:20",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -54,6 +56,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/",
|
"rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -64,6 +67,7 @@
|
|||||||
"user_viewable": false,
|
"user_viewable": false,
|
||||||
"user_editable": false,
|
"user_editable": false,
|
||||||
"rules": "nullable|string",
|
"rules": "nullable|string",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -74,6 +78,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|string|max:20",
|
"rules": "required|string|max:20",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -4,9 +4,10 @@
|
|||||||
"version": "PTDL_v2",
|
"version": "PTDL_v2",
|
||||||
"update_url": null
|
"update_url": null
|
||||||
},
|
},
|
||||||
"exported_at": "2024-05-08T11:49:03+02:00",
|
"exported_at": "2024-06-04T22:50:55+00:00",
|
||||||
"name": "Sponge (SpongeVanilla)",
|
"name": "Sponge (SpongeVanilla)",
|
||||||
"author": "panel@example.com",
|
"author": "panel@example.com",
|
||||||
|
"uuid": "f0d2f88f-1ff3-42a0-b03f-ac44c5571e6d",
|
||||||
"description": "SpongeVanilla is the SpongeAPI implementation for Vanilla Minecraft.",
|
"description": "SpongeVanilla is the SpongeAPI implementation for Vanilla Minecraft.",
|
||||||
"features": [
|
"features": [
|
||||||
"eula",
|
"eula",
|
||||||
@ -23,7 +24,7 @@
|
|||||||
"file_denylist": [],
|
"file_denylist": [],
|
||||||
"startup": "java -Xms128M -XX:MaxRAMPercentage=95.0 -jar {{SERVER_JARFILE}}",
|
"startup": "java -Xms128M -XX:MaxRAMPercentage=95.0 -jar {{SERVER_JARFILE}}",
|
||||||
"config": {
|
"config": {
|
||||||
"files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"server-port\": \"{{server.build.default.port}}\",\r\n \"query.port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}",
|
"files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"server-port\": \"{{server.allocations.default.port}}\",\r\n \"query.port\": \"{{server.allocations.default.port}}\"\r\n }\r\n }\r\n}",
|
||||||
"startup": "{\r\n \"done\": \")! For help, type \"\r\n}",
|
"startup": "{\r\n \"done\": \")! For help, type \"\r\n}",
|
||||||
"logs": "{}",
|
"logs": "{}",
|
||||||
"stop": "stop"
|
"stop": "stop"
|
||||||
@ -44,6 +45,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|regex:\/^([a-zA-Z0-9.\\-_]+)$\/",
|
"rules": "required|regex:\/^([a-zA-Z0-9.\\-_]+)$\/",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -54,7 +56,8 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/",
|
"rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,10 @@
|
|||||||
"version": "PTDL_v2",
|
"version": "PTDL_v2",
|
||||||
"update_url": null
|
"update_url": null
|
||||||
},
|
},
|
||||||
"exported_at": "2024-05-08T11:49:32+02:00",
|
"exported_at": "2024-06-04T22:51:16+00:00",
|
||||||
"name": "Vanilla Minecraft",
|
"name": "Vanilla Minecraft",
|
||||||
"author": "panel@example.com",
|
"author": "panel@example.com",
|
||||||
|
"uuid": "9ac39f3d-0c34-4d93-8174-c52ab9e6c57b",
|
||||||
"description": "Minecraft is a game about placing blocks and going on adventures. Explore randomly generated worlds and build amazing things from the simplest of homes to the grandest of castles. Play in Creative Mode with unlimited resources or mine deep in Survival Mode, crafting weapons and armor to fend off dangerous mobs. Do all this alone or with friends.",
|
"description": "Minecraft is a game about placing blocks and going on adventures. Explore randomly generated worlds and build amazing things from the simplest of homes to the grandest of castles. Play in Creative Mode with unlimited resources or mine deep in Survival Mode, crafting weapons and armor to fend off dangerous mobs. Do all this alone or with friends.",
|
||||||
"features": [
|
"features": [
|
||||||
"eula",
|
"eula",
|
||||||
@ -23,7 +24,7 @@
|
|||||||
"file_denylist": [],
|
"file_denylist": [],
|
||||||
"startup": "java -Xms128M -XX:MaxRAMPercentage=95.0 -jar {{SERVER_JARFILE}}",
|
"startup": "java -Xms128M -XX:MaxRAMPercentage=95.0 -jar {{SERVER_JARFILE}}",
|
||||||
"config": {
|
"config": {
|
||||||
"files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"server-port\": \"{{server.build.default.port}}\",\r\n \"query.port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}",
|
"files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"server-port\": \"{{server.allocations.default.port}}\",\r\n \"query.port\": \"{{server.allocations.default.port}}\"\r\n }\r\n }\r\n}",
|
||||||
"startup": "{\r\n \"done\": \")! For help, type \"\r\n}",
|
"startup": "{\r\n \"done\": \")! For help, type \"\r\n}",
|
||||||
"logs": "{}",
|
"logs": "{}",
|
||||||
"stop": "stop"
|
"stop": "stop"
|
||||||
@ -44,6 +45,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/",
|
"rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -54,6 +56,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|string|between:3,15",
|
"rules": "required|string|between:3,15",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -4,9 +4,10 @@
|
|||||||
"version": "PTDL_v2",
|
"version": "PTDL_v2",
|
||||||
"update_url": null
|
"update_url": null
|
||||||
},
|
},
|
||||||
"exported_at": "2024-05-08T11:57:56+02:00",
|
"exported_at": "2024-06-02T20:42:09+00:00",
|
||||||
"name": "Rust",
|
"name": "Rust",
|
||||||
"author": "panel@example.com",
|
"author": "panel@example.com",
|
||||||
|
"uuid": "bace2dfb-209c-452a-9459-7d6f340b07ae",
|
||||||
"description": "The only aim in Rust is to survive. To do this you will need to overcome struggles such as hunger, thirst and cold. Build a fire. Build a shelter. Kill animals for meat. Protect yourself from other players, and kill them for meat. Create alliances with other players and form a town. Do whatever it takes to survive.",
|
"description": "The only aim in Rust is to survive. To do this you will need to overcome struggles such as hunger, thirst and cold. Build a fire. Build a shelter. Kill animals for meat. Protect yourself from other players, and kill them for meat. Create alliances with other players and form a town. Do whatever it takes to survive.",
|
||||||
"features": [
|
"features": [
|
||||||
"steam_disk_space"
|
"steam_disk_space"
|
||||||
@ -38,6 +39,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|string|max:60",
|
"rules": "required|string|max:60",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -48,6 +50,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|in:vanilla,oxide,carbon",
|
"rules": "required|in:vanilla,oxide,carbon",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -58,6 +61,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|string|max:20",
|
"rules": "required|string|max:20",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -68,6 +72,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|string",
|
"rules": "required|string",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -78,6 +83,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "nullable|url",
|
"rules": "nullable|url",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -88,6 +94,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|integer",
|
"rules": "required|integer",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -98,6 +105,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "nullable|string",
|
"rules": "nullable|string",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -108,6 +116,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|integer",
|
"rules": "required|integer",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -118,6 +127,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "nullable|url",
|
"rules": "nullable|url",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -128,6 +138,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": false,
|
"user_editable": false,
|
||||||
"rules": "required|integer",
|
"rules": "required|integer",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -138,6 +149,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": false,
|
"user_editable": false,
|
||||||
"rules": "required|integer",
|
"rules": "required|integer",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -148,6 +160,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|regex:\/^[\\w.-]*$\/|max:64",
|
"rules": "required|regex:\/^[\\w.-]*$\/|max:64",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -158,6 +171,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|integer",
|
"rules": "required|integer",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -168,6 +182,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "nullable|string",
|
"rules": "nullable|string",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -178,6 +193,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": false,
|
"user_editable": false,
|
||||||
"rules": "required|integer",
|
"rules": "required|integer",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -188,6 +204,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "nullable|url",
|
"rules": "nullable|url",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -198,6 +215,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "nullable|url",
|
"rules": "nullable|url",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -208,6 +226,7 @@
|
|||||||
"user_viewable": false,
|
"user_viewable": false,
|
||||||
"user_editable": false,
|
"user_editable": false,
|
||||||
"rules": "required|string|in:258550",
|
"rules": "required|string|in:258550",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -4,9 +4,10 @@
|
|||||||
"version": "PTDL_v2",
|
"version": "PTDL_v2",
|
||||||
"update_url": null
|
"update_url": null
|
||||||
},
|
},
|
||||||
"exported_at": "2024-05-08T12:03:19+02:00",
|
"exported_at": "2024-06-02T20:42:04+00:00",
|
||||||
"name": "Counter-Strike: Global Offensive",
|
"name": "Counter-Strike: Global Offensive",
|
||||||
"author": "panel@example.com",
|
"author": "panel@example.com",
|
||||||
|
"uuid": "437c367d-06be-498f-a604-fdad135504d7",
|
||||||
"description": "Counter-Strike: Global Offensive is a multiplayer first-person shooter video game developed by Hidden Path Entertainment and Valve Corporation.",
|
"description": "Counter-Strike: Global Offensive is a multiplayer first-person shooter video game developed by Hidden Path Entertainment and Valve Corporation.",
|
||||||
"features": [
|
"features": [
|
||||||
"gsl_token",
|
"gsl_token",
|
||||||
@ -39,6 +40,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|string|alpha_dash",
|
"rules": "required|string|alpha_dash",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -49,6 +51,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|string|alpha_num|size:32",
|
"rules": "required|string|alpha_num|size:32",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -59,6 +62,7 @@
|
|||||||
"user_viewable": false,
|
"user_viewable": false,
|
||||||
"user_editable": false,
|
"user_editable": false,
|
||||||
"rules": "required|string|max:20",
|
"rules": "required|string|max:20",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -4,9 +4,10 @@
|
|||||||
"version": "PTDL_v2",
|
"version": "PTDL_v2",
|
||||||
"update_url": null
|
"update_url": null
|
||||||
},
|
},
|
||||||
"exported_at": "2024-05-08T12:02:53+02:00",
|
"exported_at": "2024-06-02T20:42:04+00:00",
|
||||||
"name": "Custom Source Engine Game",
|
"name": "Custom Source Engine Game",
|
||||||
"author": "panel@example.com",
|
"author": "panel@example.com",
|
||||||
|
"uuid": "2a42d0c2-c0ba-4067-9a0a-9b95d77a3490",
|
||||||
"description": "This option allows modifying the startup arguments and other details to run a custom SRCDS based game on the panel.",
|
"description": "This option allows modifying the startup arguments and other details to run a custom SRCDS based game on the panel.",
|
||||||
"features": [
|
"features": [
|
||||||
"steam_disk_space"
|
"steam_disk_space"
|
||||||
@ -38,6 +39,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": false,
|
"user_editable": false,
|
||||||
"rules": "required|numeric|digits_between:1,6",
|
"rules": "required|numeric|digits_between:1,6",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -48,6 +50,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": false,
|
"user_editable": false,
|
||||||
"rules": "required|alpha_dash|between:1,100",
|
"rules": "required|alpha_dash|between:1,100",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -58,6 +61,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|string|alpha_dash",
|
"rules": "required|string|alpha_dash",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -68,6 +72,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "nullable|string",
|
"rules": "nullable|string",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -78,6 +83,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "nullable|string",
|
"rules": "nullable|string",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -88,6 +94,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "nullable|string",
|
"rules": "nullable|string",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -4,9 +4,10 @@
|
|||||||
"version": "PTDL_v2",
|
"version": "PTDL_v2",
|
||||||
"update_url": null
|
"update_url": null
|
||||||
},
|
},
|
||||||
"exported_at": "2024-05-08T12:02:26+02:00",
|
"exported_at": "2024-06-02T20:42:05+00:00",
|
||||||
"name": "Garrys Mod",
|
"name": "Garrys Mod",
|
||||||
"author": "panel@example.com",
|
"author": "panel@example.com",
|
||||||
|
"uuid": "60ef81d4-30a2-4d98-ab64-f59c69e2f915",
|
||||||
"description": "Garrys Mod, is a sandbox physics game created by Garry Newman, and developed by his company, Facepunch Studios.",
|
"description": "Garrys Mod, is a sandbox physics game created by Garry Newman, and developed by his company, Facepunch Studios.",
|
||||||
"features": [
|
"features": [
|
||||||
"gsl_token",
|
"gsl_token",
|
||||||
@ -39,6 +40,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|string|alpha_dash",
|
"rules": "required|string|alpha_dash",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -49,6 +51,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "nullable|string|alpha_num|size:32",
|
"rules": "nullable|string|alpha_num|size:32",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -59,6 +62,7 @@
|
|||||||
"user_viewable": false,
|
"user_viewable": false,
|
||||||
"user_editable": false,
|
"user_editable": false,
|
||||||
"rules": "required|string|max:20",
|
"rules": "required|string|max:20",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -69,6 +73,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "nullable|integer",
|
"rules": "nullable|integer",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -79,6 +84,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|string",
|
"rules": "required|string",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -89,6 +95,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|integer|max:128",
|
"rules": "required|integer|max:128",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -99,6 +106,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|integer|max:100",
|
"rules": "required|integer|max:100",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -109,6 +117,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|boolean",
|
"rules": "required|boolean",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -4,9 +4,10 @@
|
|||||||
"version": "PTDL_v2",
|
"version": "PTDL_v2",
|
||||||
"update_url": null
|
"update_url": null
|
||||||
},
|
},
|
||||||
"exported_at": "2024-05-08T12:01:57+02:00",
|
"exported_at": "2024-06-02T20:42:06+00:00",
|
||||||
"name": "Insurgency",
|
"name": "Insurgency",
|
||||||
"author": "panel@example.com",
|
"author": "panel@example.com",
|
||||||
|
"uuid": "a5702286-655b-4069-bf1e-925c7300b61a",
|
||||||
"description": "Take to the streets for intense close quarters combat, where a team's survival depends upon securing crucial strongholds and destroying enemy supply in this multiplayer and cooperative Source Engine based experience.",
|
"description": "Take to the streets for intense close quarters combat, where a team's survival depends upon securing crucial strongholds and destroying enemy supply in this multiplayer and cooperative Source Engine based experience.",
|
||||||
"features": [
|
"features": [
|
||||||
"steam_disk_space"
|
"steam_disk_space"
|
||||||
@ -38,6 +39,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": false,
|
"user_editable": false,
|
||||||
"rules": "required|regex:\/^(237410)$\/",
|
"rules": "required|regex:\/^(237410)$\/",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -48,6 +50,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|regex:\/^(\\w{1,20})$\/",
|
"rules": "required|regex:\/^(\\w{1,20})$\/",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -4,9 +4,10 @@
|
|||||||
"version": "PTDL_v2",
|
"version": "PTDL_v2",
|
||||||
"update_url": null
|
"update_url": null
|
||||||
},
|
},
|
||||||
"exported_at": "2024-05-08T11:59:56+02:00",
|
"exported_at": "2024-06-02T20:42:07+00:00",
|
||||||
"name": "Team Fortress 2",
|
"name": "Team Fortress 2",
|
||||||
"author": "panel@example.com",
|
"author": "panel@example.com",
|
||||||
|
"uuid": "7f8eb681-b2c8-4bf8-b9f4-d79ff70b6e5d",
|
||||||
"description": "Team Fortress 2 is a team-based first-person shooter multiplayer video game developed and published by Valve Corporation. It is the sequel to the 1996 mod Team Fortress for Quake and its 1999 remake.",
|
"description": "Team Fortress 2 is a team-based first-person shooter multiplayer video game developed and published by Valve Corporation. It is the sequel to the 1996 mod Team Fortress for Quake and its 1999 remake.",
|
||||||
"features": [
|
"features": [
|
||||||
"gsl_token",
|
"gsl_token",
|
||||||
@ -39,6 +40,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": false,
|
"user_editable": false,
|
||||||
"rules": "required|in:232250",
|
"rules": "required|in:232250",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -49,6 +51,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|regex:\/^(\\w{1,20})$\/",
|
"rules": "required|regex:\/^(\\w{1,20})$\/",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -59,6 +62,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|string|alpha_num|size:32",
|
"rules": "required|string|alpha_num|size:32",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -4,9 +4,10 @@
|
|||||||
"version": "PTDL_v2",
|
"version": "PTDL_v2",
|
||||||
"update_url": null
|
"update_url": null
|
||||||
},
|
},
|
||||||
"exported_at": "2024-05-21T21:40:24-04:00",
|
"exported_at": "2024-06-04T22:53:03+00:00",
|
||||||
"name": "Mumble Server",
|
"name": "Mumble Server",
|
||||||
"author": "panel@example.com",
|
"author": "panel@example.com",
|
||||||
|
"uuid": "727ee758-7fb2-4979-972b-d3eba4e1e9f0",
|
||||||
"description": "Mumble is an open source, low-latency, high quality voice chat software primarily intended for use while gaming.",
|
"description": "Mumble is an open source, low-latency, high quality voice chat software primarily intended for use while gaming.",
|
||||||
"features": null,
|
"features": null,
|
||||||
"docker_images": {
|
"docker_images": {
|
||||||
@ -15,7 +16,7 @@
|
|||||||
"file_denylist": [],
|
"file_denylist": [],
|
||||||
"startup": "mumble-server -fg -ini murmur.ini",
|
"startup": "mumble-server -fg -ini murmur.ini",
|
||||||
"config": {
|
"config": {
|
||||||
"files": "{\r\n \"murmur.ini\": {\r\n \"parser\": \"ini\",\r\n \"find\": {\r\n \"database\": \"\/home\/container\/murmur.sqlite\",\r\n \"logfile\": \"\/home\/container\/murmur.log\",\r\n \"port\": \"{{server.build.default.port}}\",\r\n \"host\": \"\",\r\n \"users\": \"{{server.build.env.MAX_USERS}}\"\r\n }\r\n }\r\n}",
|
"files": "{\r\n \"murmur.ini\": {\r\n \"parser\": \"ini\",\r\n \"find\": {\r\n \"database\": \"\/home\/container\/murmur.sqlite\",\r\n \"logfile\": \"\/home\/container\/murmur.log\",\r\n \"port\": \"{{server.allocations.default.port}}\",\r\n \"host\": \"\",\r\n \"users\": \"{{server.environment.MAX_USERS}}\"\r\n }\r\n }\r\n}",
|
||||||
"startup": "{\r\n \"done\": \"Server listening on\"\r\n}",
|
"startup": "{\r\n \"done\": \"Server listening on\"\r\n}",
|
||||||
"logs": "{}",
|
"logs": "{}",
|
||||||
"stop": "^C"
|
"stop": "^C"
|
||||||
@ -36,7 +37,8 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": false,
|
"user_editable": false,
|
||||||
"rules": "required|numeric|digits_between:1,5",
|
"rules": "required|numeric|digits_between:1,5",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -4,9 +4,10 @@
|
|||||||
"version": "PTDL_v2",
|
"version": "PTDL_v2",
|
||||||
"update_url": null
|
"update_url": null
|
||||||
},
|
},
|
||||||
"exported_at": "2024-05-08T11:53:32+02:00",
|
"exported_at": "2024-06-02T20:42:08+00:00",
|
||||||
"name": "Teamspeak3 Server",
|
"name": "Teamspeak3 Server",
|
||||||
"author": "panel@example.com",
|
"author": "panel@example.com",
|
||||||
|
"uuid": "983b1fac-d322-4d5f-a636-436127326b37",
|
||||||
"description": "VoIP software designed with security in mind, featuring crystal clear voice quality, endless customization options, and scalabilty up to thousands of simultaneous users.",
|
"description": "VoIP software designed with security in mind, featuring crystal clear voice quality, endless customization options, and scalabilty up to thousands of simultaneous users.",
|
||||||
"features": null,
|
"features": null,
|
||||||
"docker_images": {
|
"docker_images": {
|
||||||
@ -36,6 +37,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|string|max:6",
|
"rules": "required|string|max:6",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -46,6 +48,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": false,
|
"user_editable": false,
|
||||||
"rules": "required|integer|between:1025,65535",
|
"rules": "required|integer|between:1025,65535",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -56,6 +59,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": false,
|
"user_editable": false,
|
||||||
"rules": "required|integer|between:1025,65535",
|
"rules": "required|integer|between:1025,65535",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -66,6 +70,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": true,
|
"user_editable": true,
|
||||||
"rules": "required|string|max:12",
|
"rules": "required|string|max:12",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -76,6 +81,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": false,
|
"user_editable": false,
|
||||||
"rules": "required|integer|between:1025,65535",
|
"rules": "required|integer|between:1025,65535",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -86,6 +92,7 @@
|
|||||||
"user_viewable": true,
|
"user_viewable": true,
|
||||||
"user_editable": false,
|
"user_editable": false,
|
||||||
"rules": "required|integer|between:1025,65535",
|
"rules": "required|integer|between:1025,65535",
|
||||||
|
"sort": null,
|
||||||
"field_type": "text"
|
"field_type": "text"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
$nameToUuidMapping = [
|
||||||
|
'Bungeecord' => '9e6b409e-4028-4947-aea8-50a2c404c271',
|
||||||
|
'Forge Minecraft' => 'ed072427-f209-4603-875c-f540c6dd5a65',
|
||||||
|
'Paper' => '5da37ef6-58da-4169-90a6-e683e1721247',
|
||||||
|
'Sponge (SpongeVanilla)' => 'f0d2f88f-1ff3-42a0-b03f-ac44c5571e6d',
|
||||||
|
'Vanilla Minecraft' => '9ac39f3d-0c34-4d93-8174-c52ab9e6c57b',
|
||||||
|
'Counter-Strike: Global Offensive' => '437c367d-06be-498f-a604-fdad135504d7',
|
||||||
|
'Custom Source Engine Game' => '2a42d0c2-c0ba-4067-9a0a-9b95d77a3490',
|
||||||
|
'Garrys Mod' => '60ef81d4-30a2-4d98-ab64-f59c69e2f915',
|
||||||
|
'Insurgency' => 'a5702286-655b-4069-bf1e-925c7300b61a',
|
||||||
|
'Team Fortress 2' => '7f8eb681-b2c8-4bf8-b9f4-d79ff70b6e5d',
|
||||||
|
'Mumble Server' => '727ee758-7fb2-4979-972b-d3eba4e1e9f0',
|
||||||
|
'Teamspeak3 Server' => '983b1fac-d322-4d5f-a636-436127326b37',
|
||||||
|
'Rust' => 'bace2dfb-209c-452a-9459-7d6f340b07ae',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($nameToUuidMapping as $name => $uuid) {
|
||||||
|
DB::table('eggs')
|
||||||
|
->where('name', $name)
|
||||||
|
->update(['uuid' => $uuid]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('nodes', function (Blueprint $table) {
|
||||||
|
$table->string('daemon_sftp_alias')->nullable()->after('daemon_sftp');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('nodes', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('daemon_sftp_alias');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,92 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
$eggs = DB::table('eggs')->get();
|
||||||
|
|
||||||
|
foreach ($eggs as $egg) {
|
||||||
|
$updatedPort = str_replace(
|
||||||
|
'server.build.default.port',
|
||||||
|
'server.allocations.default.port',
|
||||||
|
$egg->config_files
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($updatedPort !== $egg->config_files) {
|
||||||
|
$egg->config_files = $updatedPort;
|
||||||
|
echo "Processed Port update with ID: {$egg->name}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$updatedIp = str_replace(
|
||||||
|
'server.build.default.ip',
|
||||||
|
'server.allocations.default.ip',
|
||||||
|
$egg->config_files
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($updatedIp !== $egg->config_files) {
|
||||||
|
$egg->config_files = $updatedIp;
|
||||||
|
echo "Processed IP update with ID: {$egg->name}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$updatedEnv = str_replace(
|
||||||
|
'server.build.env.',
|
||||||
|
'server.environment.',
|
||||||
|
$egg->config_files
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($updatedEnv !== $egg->config_files) {
|
||||||
|
$egg->config_files = $updatedEnv;
|
||||||
|
echo "Processed ENV update with ID: {$egg->name}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
DB::table('eggs')
|
||||||
|
->where('id', $egg->id)
|
||||||
|
->update(['config_files' => $egg->config_files]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
$eggs = DB::table('eggs')->get();
|
||||||
|
|
||||||
|
foreach ($eggs as $egg) {
|
||||||
|
$revertedEnv = str_replace(
|
||||||
|
'server.environment.',
|
||||||
|
'server.build.env.',
|
||||||
|
$egg->config_files
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($revertedEnv !== $egg->config_files) {
|
||||||
|
$egg->config_files = $revertedEnv;
|
||||||
|
}
|
||||||
|
|
||||||
|
$revertedIp = str_replace(
|
||||||
|
'server.allocations.default.ip',
|
||||||
|
'server.build.default.ip',
|
||||||
|
$egg->config_files
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($revertedIp !== $egg->config_files) {
|
||||||
|
$egg->config_files = $revertedIp;
|
||||||
|
}
|
||||||
|
|
||||||
|
$revertedPort = str_replace(
|
||||||
|
'server.allocations.default.port',
|
||||||
|
'server.build.default.port',
|
||||||
|
$egg->config_files
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($revertedPort !== $egg->config_files) {
|
||||||
|
$egg->config_files = $revertedPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
DB::table('eggs')
|
||||||
|
->where('id', $egg->id)
|
||||||
|
->update(['config_files' => $egg->config_files]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
$eggs = DB::table('eggs')->get();
|
||||||
|
|
||||||
|
foreach ($eggs as $egg) {
|
||||||
|
$updatedEnv = str_replace(
|
||||||
|
'server.build.environment.',
|
||||||
|
'server.environment.',
|
||||||
|
$egg->config_files
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($updatedEnv !== $egg->config_files) {
|
||||||
|
$egg->config_files = $updatedEnv;
|
||||||
|
echo "Processed ENV update with ID: {$egg->name}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
DB::table('eggs')
|
||||||
|
->where('id', $egg->id)
|
||||||
|
->update(['config_files' => $egg->config_files]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
// We shouldn't revert this...
|
||||||
|
}
|
||||||
|
};
|
@ -37,6 +37,7 @@ return [
|
|||||||
'upload_size' => "'Enter the maximum filesize upload",
|
'upload_size' => "'Enter the maximum filesize upload",
|
||||||
'daemonListen' => 'Enter the daemon listening port',
|
'daemonListen' => 'Enter the daemon listening port',
|
||||||
'daemonSFTP' => 'Enter the daemon SFTP listening port',
|
'daemonSFTP' => 'Enter the daemon SFTP listening port',
|
||||||
|
'daemonSFTPAlias' => 'Enter the daemon SFTP alias (can be empty)',
|
||||||
'daemonBase' => 'Enter the base folder',
|
'daemonBase' => 'Enter the base folder',
|
||||||
'succes1' => 'Successfully created a new node with the name: ',
|
'succes1' => 'Successfully created a new node with the name: ',
|
||||||
'succes2' => 'and has an id of: ',
|
'succes2' => 'and has an id of: ',
|
||||||
|
@ -19,6 +19,10 @@ return [
|
|||||||
'button_issues' => 'Create Issue',
|
'button_issues' => 'Create Issue',
|
||||||
'button_features' => 'Discuss Features',
|
'button_features' => 'Discuss Features',
|
||||||
],
|
],
|
||||||
|
'intro-update' => [
|
||||||
|
'heading' => 'Update available',
|
||||||
|
'content' => ':latestVersion is available! Read our documentation to update your Panel.',
|
||||||
|
],
|
||||||
'intro-first-node' => [
|
'intro-first-node' => [
|
||||||
'heading' => 'No Nodes Detected',
|
'heading' => 'No Nodes Detected',
|
||||||
'content' => "It looks like you don't have any Nodes set up yet, but don't worry because you click the action button to create your first one!",
|
'content' => "It looks like you don't have any Nodes set up yet, but don't worry because you click the action button to create your first one!",
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
|||||||
function r({state:i}){return{state:i,rows:[],shouldUpdateRows:!0,init:function(){this.updateRows(),this.rows.length<=0?this.rows.push({key:"",value:""}):this.updateState(),this.$watch("state",(t,e)=>{let s=o=>o===null?0:Array.isArray(o)?o.length:typeof o!="object"?0:Object.keys(o).length;s(t)===0&&s(e)===0||this.updateRows()})},addRow:function(){this.rows.push({key:"",value:""}),this.updateState()},deleteRow:function(t){this.rows.splice(t,1),this.rows.length<=0&&this.addRow(),this.updateState()},reorderRows:function(t){let e=Alpine.raw(this.rows),s=e.splice(t.oldIndex,1)[0];e.splice(t.newIndex,0,s),this.rows=e,this.updateState()},updateRows:function(){if(!this.shouldUpdateRows){this.shouldUpdateRows=!0;return}let t=[];for(let[e,s]of Object.entries(this.state??{}))t.push({key:e,value:s});this.rows=t},updateState:function(){let t={};this.rows.forEach(e=>{e.key===""||e.key===null||(t[e.key]=e.value)}),this.shouldUpdateRows=!1,this.state=t}}}export{r as default};
|
function r({state:o}){return{state:o,rows:[],shouldUpdateRows:!0,init:function(){this.updateRows(),this.rows.length<=0?this.rows.push({key:"",value:""}):this.updateState(),this.$watch("state",(t,e)=>{let s=i=>i===null?0:Array.isArray(i)?i.length:typeof i!="object"?0:Object.keys(i).length;s(t)===0&&s(e)===0||this.updateRows()})},addRow:function(){this.rows.push({key:"",value:""}),this.updateState()},deleteRow:function(t){this.rows.splice(t,1),this.rows.length<=0&&this.addRow(),this.updateState()},reorderRows:function(t){let e=Alpine.raw(this.rows);this.rows=[];let s=e.splice(t.oldIndex,1)[0];e.splice(t.newIndex,0,s),this.$nextTick(()=>{this.rows=e,this.updateState()})},updateRows:function(){if(!this.shouldUpdateRows){this.shouldUpdateRows=!0;return}let t=[];for(let[e,s]of Object.entries(this.state??{}))t.push({key:e,value:s});this.rows=t},updateState:function(){let t={};this.rows.forEach(e=>{e.key===""||e.key===null||(t[e.key]=e.value)}),this.shouldUpdateRows=!1,this.state=t}}}export{r as default};
|
||||||
|
File diff suppressed because one or more lines are too long
58
readme.md
58
readme.md
@ -26,46 +26,20 @@ This gives you the power to run game servers without bloating machines with a ho
|
|||||||
|
|
||||||
Some of our popular eggs include but are not limited to:
|
Some of our popular eggs include but are not limited to:
|
||||||
|
|
||||||
* [Minecraft](https://github.com/pelican-eggs/minecraft)
|
| Category | Eggs | | | |
|
||||||
* Paper
|
|----------------------------------------------------------------------|-----------------|---------------|--------------------|----------------|
|
||||||
* Sponge
|
| [Minecraft](https://github.com/pelican-eggs/minecraft) | Paper | Sponge | Bungeecord | Waterfall |
|
||||||
* Bungeecord
|
| [SteamCMD](https://github.com/pelican-eggs/steamcmd) | 7 Days to Die | ARK: Survival | Arma 3 | Counter Strike |
|
||||||
* Waterfall
|
| | DayZ | Enshrouded | Left 4 Dead | Palworld |
|
||||||
* [SteamCMD](https://github.com/pelican-eggs/steamcmd)
|
| | Project Zomboid | Satisfactory | Sons of the Forest | Starbound |
|
||||||
* 7 Days to Die
|
| [Standalone Games](https://github.com/pelican-eggs/games-standalone) | Among Us | Factorio | FTL | GTA |
|
||||||
* ARK: Survival
|
| | Kerbal Space | Mindustry | Rimworld | Terraria |
|
||||||
* ARMA
|
| [Discord Bots](https://github.com/pelican-eggs/chatbots) | Redbot | JMusicBot | JMusicBot | Dynamica |
|
||||||
* Counter Strike
|
| [Voice Servers](https://github.com/pelican-eggs/voice) | Mumble | Teamspeak | Lavalink | |
|
||||||
* DayZ
|
| [Software](https://github.com/pelican-eggs/software) | Elasticsearch | Gitea | Grafana | RabbitMQ |
|
||||||
* Enshrouded
|
| [Programming](https://github.com/pelican-eggs/generic) | Node.js | Python | Java | C# |
|
||||||
* Left 4 Dead
|
| [Databases](https://github.com/pelican-eggs/database) | Redis | MariaDB | PostgreSQL | MongoDB |
|
||||||
* Palworld
|
| [Storage](https://github.com/pelican-eggs/storage) | S3 | SFTP Share | | |
|
||||||
* Project Zomboid
|
| [Monitoring](https://github.com/pelican-eggs/monitoring) | Prometheus | Loki | | |
|
||||||
* Sons of the Forest
|
|
||||||
* [Other Games](https://github.com/pelican-eggs/games)
|
|
||||||
* Among Us
|
|
||||||
* Factorio
|
|
||||||
* GTA
|
|
||||||
* Rimworld
|
|
||||||
* Terraria
|
|
||||||
* [Discord Bots](https://github.com/pelican-eggs/chatbots)
|
|
||||||
* Redbot
|
|
||||||
* JMusicBot
|
|
||||||
* SinusBot
|
|
||||||
* Dynamica
|
|
||||||
* [Software](https://github.com/pelican-eggs/software)
|
|
||||||
* [Programming Languages](https://github.com/pelican-eggs/generic)
|
|
||||||
* C#
|
|
||||||
* Java
|
|
||||||
* Lua
|
|
||||||
* Node.js
|
|
||||||
* Python
|
|
||||||
* [Database](https://github.com/pelican-eggs/database)
|
|
||||||
* Redis
|
|
||||||
* MariaDB
|
|
||||||
* PostgreSQL
|
|
||||||
* [Voice Servers](https://github.com/pelican-eggs/voice)
|
|
||||||
* [Storage](https://github.com/pelican-eggs/storage)
|
|
||||||
* [Monitoring](https://github.com/pelican-eggs/monitoring)
|
|
||||||
|
|
||||||
Copyright Pelican® 2024
|
*Copyright Pelican® 2024*
|
||||||
|
@ -21,6 +21,7 @@ export interface Server {
|
|||||||
status: ServerStatus;
|
status: ServerStatus;
|
||||||
sftpDetails: {
|
sftpDetails: {
|
||||||
ip: string;
|
ip: string;
|
||||||
|
alias: string;
|
||||||
port: number;
|
port: number;
|
||||||
};
|
};
|
||||||
invocation: string;
|
invocation: string;
|
||||||
@ -57,6 +58,7 @@ export const rawDataToServerObject = ({ attributes: data }: FractalResponseData)
|
|||||||
dockerImage: data.docker_image,
|
dockerImage: data.docker_image,
|
||||||
sftpDetails: {
|
sftpDetails: {
|
||||||
ip: data.sftp_details.ip,
|
ip: data.sftp_details.ip,
|
||||||
|
alias: data.sftp_details.alias,
|
||||||
port: data.sftp_details.port,
|
port: data.sftp_details.port,
|
||||||
},
|
},
|
||||||
description: data.description ? (data.description.length > 0 ? data.description : null) : null,
|
description: data.description ? (data.description.length > 0 ? data.description : null) : null,
|
||||||
|
@ -40,7 +40,7 @@ const ModalContent = ({ ...props }: RequiredModalProps) => {
|
|||||||
label={'Ignored Files & Directories'}
|
label={'Ignored Files & Directories'}
|
||||||
description={`
|
description={`
|
||||||
Enter the files or folders to ignore while generating this backup. Leave blank to use
|
Enter the files or folders to ignore while generating this backup. Leave blank to use
|
||||||
the contents of the .panelignore file in the root of the server directory if present.
|
the contents of the .pelicanignore file in the root of the server directory if present.
|
||||||
Wildcard matching of files and folders is supported in addition to negating a rule by
|
Wildcard matching of files and folders is supported in addition to negating a rule by
|
||||||
prefixing the path with an exclamation point.
|
prefixing the path with an exclamation point.
|
||||||
`}
|
`}
|
||||||
|
@ -91,13 +91,14 @@ export default () => {
|
|||||||
<FileManagerBreadcrumbs withinFileEditor isNewFile={action !== 'edit'} />
|
<FileManagerBreadcrumbs withinFileEditor isNewFile={action !== 'edit'} />
|
||||||
</div>
|
</div>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
{hash.replace(/^#/, '').endsWith('.panelignore') && (
|
{hash.replace(/^#/, '').endsWith('.pelicanignore') && (
|
||||||
<div css={tw`mb-4 p-4 border-l-4 bg-neutral-900 rounded border-cyan-400`}>
|
<div css={tw`mb-4 p-4 border-l-4 bg-neutral-900 rounded border-cyan-400`}>
|
||||||
<p css={tw`text-neutral-300 text-sm`}>
|
<p css={tw`text-neutral-300 text-sm`}>
|
||||||
You're editing a <code css={tw`font-mono bg-black rounded py-px px-1`}>.panelignore</code>{' '}
|
You're editing a{' '}
|
||||||
file. Any files or directories listed in here will be excluded from backups. Wildcards are
|
<code css={tw`font-mono bg-black rounded py-px px-1`}>.pelicanignore</code> directories listed
|
||||||
supported by using an asterisk (<code css={tw`font-mono bg-black rounded py-px px-1`}>*</code>).
|
in here will be excluded from backups. Wildcards are supported by using an supported by using an
|
||||||
You can negate a prior rule by prepending an exclamation point (
|
asterisk (<code css={tw`font-mono bg-black rounded py-px px-1`}>*</code>). You can negate a
|
||||||
|
prior rule by prepending an exclamation point (
|
||||||
<code css={tw`font-mono bg-black rounded py-px px-1`}>!</code>).
|
<code css={tw`font-mono bg-black rounded py-px px-1`}>!</code>).
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -168,7 +168,7 @@ const TaskDetailsModal = ({ schedule, task }: Props) => {
|
|||||||
<FormikFieldWrapper
|
<FormikFieldWrapper
|
||||||
name={'payload'}
|
name={'payload'}
|
||||||
description={
|
description={
|
||||||
'Optional. Include the files and folders to be excluded in this backup. By default, the contents of your .panelignore file will be used. If you have reached your backup limit, the oldest backup will be rotated.'
|
'Optional. Include the files and folders to be excluded in this backup. By default, the contents of your .pelicanignore file will be used. If you have reached your backup limit, the oldest backup will be rotated.'
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<FormikField as={Textarea} name={'payload'} rows={6} />
|
<FormikField as={Textarea} name={'payload'} rows={6} />
|
||||||
|
@ -31,8 +31,12 @@ export default () => {
|
|||||||
<TitledGreyBox title={'SFTP Details'} css={tw`mb-6 md:mb-10`}>
|
<TitledGreyBox title={'SFTP Details'} css={tw`mb-6 md:mb-10`}>
|
||||||
<div>
|
<div>
|
||||||
<Label>Server Address</Label>
|
<Label>Server Address</Label>
|
||||||
<CopyOnClick text={`sftp://${ip(sftp.ip)}:${sftp.port}`}>
|
<CopyOnClick text={`sftp://${sftp.alias ? sftp.alias : ip(sftp.ip)}:${sftp.port}`}>
|
||||||
<Input type={'text'} value={`sftp://${ip(sftp.ip)}:${sftp.port}`} readOnly />
|
<Input
|
||||||
|
type={'text'}
|
||||||
|
value={`sftp://${sftp.alias ? sftp.alias : ip(sftp.ip)}:${sftp.port}`}
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
</CopyOnClick>
|
</CopyOnClick>
|
||||||
</div>
|
</div>
|
||||||
<div css={tw`mt-6`}>
|
<div css={tw`mt-6`}>
|
||||||
@ -50,7 +54,10 @@ export default () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div css={tw`ml-4`}>
|
<div css={tw`ml-4`}>
|
||||||
<a href={`sftp://${username}.${id}@${ip(sftp.ip)}:${sftp.port}`}>
|
<a
|
||||||
|
href={`sftp://${username}.${id}@${sftp.alias ? sftp.alias : ip(sftp.ip)}:${sftp.port
|
||||||
|
}`}
|
||||||
|
>
|
||||||
<Button.Text variant={Button.Variants.Secondary}>Launch SFTP</Button.Text>
|
<Button.Text variant={Button.Variants.Secondary}>Launch SFTP</Button.Text>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
:actions="$this->getCachedHeaderActions()"
|
:actions="$this->getCachedHeaderActions()"
|
||||||
:breadcrumbs="filament()->hasBreadcrumbs() ? $this->getBreadcrumbs() : []"
|
:breadcrumbs="filament()->hasBreadcrumbs() ? $this->getBreadcrumbs() : []"
|
||||||
:heading=" trans('dashboard/index.heading')"
|
:heading=" trans('dashboard/index.heading')"
|
||||||
:subheading="trans('strings.version', ['version' => config('app.version')])"
|
:subheading="trans('strings.version', ['version' => $version])"
|
||||||
></x-filament-panels::header>
|
></x-filament-panels::header>
|
||||||
|
|
||||||
<p>{{ trans('dashboard/index.expand_sections') }}</p>
|
<p>{{ trans('dashboard/index.expand_sections') }}</p>
|
||||||
@ -30,6 +30,22 @@
|
|||||||
</x-filament::section>
|
</x-filament::section>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
@if (!$isLatest)
|
||||||
|
<x-filament::section
|
||||||
|
icon="tabler-info-circle"
|
||||||
|
icon-color="warning"
|
||||||
|
id="intro-update"
|
||||||
|
collapsible
|
||||||
|
persist-collapsed
|
||||||
|
:header-actions="$updateActions"
|
||||||
|
>
|
||||||
|
<x-slot name="heading">{{ trans('dashboard/index.sections.intro-update.heading') }}</x-slot>
|
||||||
|
|
||||||
|
<p>{{ trans('dashboard/index.sections.intro-update.content', ['latestVersion' => $latestVersion]) }}</p>
|
||||||
|
|
||||||
|
</x-filament::section>
|
||||||
|
@endif
|
||||||
|
|
||||||
{{-- No Nodes Created --}}
|
{{-- No Nodes Created --}}
|
||||||
@if ($nodesCount <= 0)
|
@if ($nodesCount <= 0)
|
||||||
<x-filament::section
|
<x-filament::section
|
||||||
|
Loading…
x
Reference in New Issue
Block a user