mirror of
https://github.com/pelican-dev/panel.git
synced 2026-05-04 18:00:48 +03:00
Compare commits
23 Commits
v1.0.0-bet
...
v1.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
242a75bf3d | ||
|
|
2ab4c81e2a | ||
|
|
5a47948a93 | ||
|
|
9d1e7f510f | ||
|
|
be55e75109 | ||
|
|
8b5f33ee71 | ||
|
|
014e866d0e | ||
|
|
4a1ecb1adc | ||
|
|
e2529ab436 | ||
|
|
cd3f3a97ac | ||
|
|
2f5790b121 | ||
|
|
59f0fe1959 | ||
|
|
fdd9faaaa3 | ||
|
|
9449d78144 | ||
|
|
a391d21043 | ||
|
|
b13fcfd644 | ||
|
|
760aaf9bfb | ||
|
|
1ab4ddb07c | ||
|
|
f278041bc0 | ||
|
|
cdc928a15b | ||
|
|
3939c409c1 | ||
|
|
091ca5447a | ||
|
|
57c4172c74 |
@@ -2,7 +2,7 @@
|
||||
# Pelican Production Dockerfile
|
||||
|
||||
##
|
||||
# If you want to build this locally you want to run `docker build -f Dockerfile.dev`
|
||||
# If you want to build this locally you want to run `docker build -f Dockerfile.dev .`
|
||||
##
|
||||
|
||||
# ================================
|
||||
@@ -76,13 +76,14 @@ RUN chown root:www-data ./ \
|
||||
# Files should not have execute set, but directories need it
|
||||
&& find ./ -type d -exec chmod 750 {} \; \
|
||||
# Create necessary directories
|
||||
&& mkdir -p /pelican-data/storage /var/www/html/storage/app/public /var/run/supervisord /etc/supercronic \
|
||||
# Symlinks for env, database, and avatars
|
||||
&& mkdir -p /pelican-data/storage /pelican-data/plugins /var/www/html/storage/app/public /var/run/supervisord /etc/supercronic \
|
||||
# Symlinks for env, database, storage, and plugins
|
||||
&& ln -s /pelican-data/.env ./.env \
|
||||
&& ln -s /pelican-data/database/database.sqlite ./database/database.sqlite \
|
||||
&& ln -sf /var/www/html/storage/app/public /var/www/html/public/storage \
|
||||
&& ln -s /pelican-data/storage/avatars /var/www/html/storage/app/public/avatars \
|
||||
&& ln -s /pelican-data/storage/fonts /var/www/html/storage/app/public/fonts \
|
||||
&& ln -s /pelican-data/plugins /var/www/html/plugins \
|
||||
# Allow www-data write permissions where necessary
|
||||
&& chown -R www-data:www-data /pelican-data ./storage ./bootstrap/cache /var/run/supervisord /var/www/html/public/storage \
|
||||
&& chmod -R u+rwX,g+rwX,o-rwx /pelican-data ./storage ./bootstrap/cache /var/run/supervisord \
|
||||
|
||||
@@ -80,13 +80,14 @@ RUN chown root:www-data ./ \
|
||||
# Files should not have execute set, but directories need it
|
||||
&& find ./ -type d -exec chmod 750 {} \; \
|
||||
# Create necessary directories
|
||||
&& mkdir -p /pelican-data/storage /var/www/html/storage/app/public /var/run/supervisord /etc/supercronic \
|
||||
# Symlinks for env, database, and avatars
|
||||
&& mkdir -p /pelican-data/storage /pelican-data/plugins /var/www/html/storage/app/public /var/run/supervisord /etc/supercronic \
|
||||
# Symlinks for env, database, storage, and plugins
|
||||
&& ln -s /pelican-data/.env ./.env \
|
||||
&& ln -s /pelican-data/database/database.sqlite ./database/database.sqlite \
|
||||
&& ln -sf /var/www/html/storage/app/public /var/www/html/public/storage \
|
||||
&& ln -s /pelican-data/storage/avatars /var/www/html/storage/app/public/avatars \
|
||||
&& ln -s /pelican-data/storage/fonts /var/www/html/storage/app/public/fonts \
|
||||
&& ln -s /pelican-data/plugins /var/www/html/plugins \
|
||||
# Allow www-data write permissions where necessary
|
||||
&& chown -R www-data:www-data /pelican-data ./storage ./bootstrap/cache /var/run/supervisord /var/www/html/public/storage \
|
||||
&& chmod -R u+rwX,g+rwX,o-rwx /pelican-data ./storage ./bootstrap/cache /var/run/supervisord \
|
||||
|
||||
25
app/Console/Commands/Plugin/ComposerPluginsCommand.php
Normal file
25
app/Console/Commands/Plugin/ComposerPluginsCommand.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands\Plugin;
|
||||
|
||||
use App\Services\Helpers\PluginService;
|
||||
use Exception;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class ComposerPluginsCommand extends Command
|
||||
{
|
||||
protected $signature = 'p:plugin:composer';
|
||||
|
||||
protected $description = 'Makes sure the needed composer packages for all installed plugins are available.';
|
||||
|
||||
public function handle(PluginService $pluginService): void
|
||||
{
|
||||
try {
|
||||
$pluginService->manageComposerPackages();
|
||||
} catch (Exception $exception) {
|
||||
report($exception);
|
||||
|
||||
$this->error($exception->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
37
app/Console/Commands/Plugin/DisablePluginCommand.php
Normal file
37
app/Console/Commands/Plugin/DisablePluginCommand.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands\Plugin;
|
||||
|
||||
use App\Models\Plugin;
|
||||
use App\Services\Helpers\PluginService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class DisablePluginCommand extends Command
|
||||
{
|
||||
protected $signature = 'p:plugin:disable {id?}';
|
||||
|
||||
protected $description = 'Disables a plugin';
|
||||
|
||||
public function handle(PluginService $pluginService): void
|
||||
{
|
||||
$id = $this->argument('id') ?? $this->choice('Plugin', Plugin::pluck('name', 'id')->toArray());
|
||||
|
||||
$plugin = Plugin::find($id);
|
||||
|
||||
if (!$plugin) {
|
||||
$this->error('Plugin does not exist!');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$plugin->canDisable()) {
|
||||
$this->error("Plugin can't be disabled!");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$pluginService->disablePlugin($plugin);
|
||||
|
||||
$this->info('Plugin disabled.');
|
||||
}
|
||||
}
|
||||
38
app/Console/Commands/Plugin/InstallPluginCommand.php
Normal file
38
app/Console/Commands/Plugin/InstallPluginCommand.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands\Plugin;
|
||||
|
||||
use App\Enums\PluginStatus;
|
||||
use App\Models\Plugin;
|
||||
use App\Services\Helpers\PluginService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class InstallPluginCommand extends Command
|
||||
{
|
||||
protected $signature = 'p:plugin:install {id?}';
|
||||
|
||||
protected $description = 'Installs a plugin';
|
||||
|
||||
public function handle(PluginService $pluginService): void
|
||||
{
|
||||
$id = $this->argument('id') ?? $this->choice('Plugin', Plugin::pluck('name', 'id')->toArray());
|
||||
|
||||
$plugin = Plugin::find($id);
|
||||
|
||||
if (!$plugin) {
|
||||
$this->error('Plugin does not exist!');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($plugin->status !== PluginStatus::NotInstalled) {
|
||||
$this->error('Plugin is already installed!');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$pluginService->installPlugin($plugin);
|
||||
|
||||
$this->info('Plugin installed and enabled.');
|
||||
}
|
||||
}
|
||||
28
app/Console/Commands/Plugin/ListPluginsCommand.php
Normal file
28
app/Console/Commands/Plugin/ListPluginsCommand.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands\Plugin;
|
||||
|
||||
use App\Models\Plugin;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class ListPluginsCommand extends Command
|
||||
{
|
||||
protected $signature = 'p:plugin:list';
|
||||
|
||||
protected $description = 'List all installed plugins';
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
$plugins = Plugin::query()->get(['name', 'author', 'status', 'version', 'panels', 'category']);
|
||||
|
||||
if (count($plugins) < 1) {
|
||||
$this->warn('No plugins installed');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->table(['Name', 'Author', 'Status', 'Version', 'Panels', 'Category'], $plugins->toArray());
|
||||
|
||||
$this->output->newLine();
|
||||
}
|
||||
}
|
||||
135
app/Console/Commands/Plugin/MakePluginCommand.php
Normal file
135
app/Console/Commands/Plugin/MakePluginCommand.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands\Plugin;
|
||||
|
||||
use App\Enums\PluginCategory;
|
||||
use App\Enums\PluginStatus;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class MakePluginCommand extends Command
|
||||
{
|
||||
protected $signature = 'p:plugin:make
|
||||
{--name=}
|
||||
{--author=}
|
||||
{--description=}
|
||||
{--category=}
|
||||
{--url=}
|
||||
{--updateUrl=}
|
||||
{--panels=}
|
||||
{--panelVersion=}';
|
||||
|
||||
protected $description = 'Create a new plugin';
|
||||
|
||||
public function __construct(private Filesystem $filesystem)
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
$name = $this->option('name') ?? $this->ask('Name');
|
||||
$name = preg_replace('/[^A-Za-z0-9 ]/', '', Str::ascii($name));
|
||||
|
||||
$id = Str::slug($name);
|
||||
|
||||
if ($this->filesystem->exists(plugin_path($id))) {
|
||||
$this->error('Plugin with that name already exists!');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$author = $this->option('author') ?? $this->ask('Author', cache('plugin.author'));
|
||||
$author = preg_replace('/[^A-Za-z0-9 ]/', '', Str::ascii($author));
|
||||
cache()->forever('plugin.author', $author);
|
||||
|
||||
$namespace = Str::studly($author) . '\\' . Str::studly($name);
|
||||
$class = Str::studly($name . 'Plugin');
|
||||
|
||||
if (class_exists('\\' . $namespace . '\\' . $class)) {
|
||||
$this->error('Plugin class with that name already exists!');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->info('Creating Plugin "' . $name . '" (' . $id . ') by ' . $author);
|
||||
|
||||
$description = $this->option('description') ?? $this->ask('Description (can be empty)');
|
||||
|
||||
$category = $this->option('category') ?? $this->choice('Category', collect(PluginCategory::cases())->mapWithKeys(fn (PluginCategory $category) => [$category->value => $category->getLabel()])->toArray(), PluginCategory::Plugin->value);
|
||||
|
||||
if (!PluginCategory::tryFrom($category)) {
|
||||
$this->error('Unknown plugin category!');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$url = $this->option('url') ?? $this->ask('URL (can be empty)');
|
||||
$updateUrl = $this->option('updateUrl') ?? $this->ask('Update URL (can be empty)');
|
||||
|
||||
$panels = $this->option('panels');
|
||||
if (!$panels) {
|
||||
if ($this->confirm('Should the plugin be available on all panels?', true)) {
|
||||
$panels = null;
|
||||
} else {
|
||||
$panels = $this->choice('Panels (comma separated list)', [
|
||||
'admin' => 'Admin Area',
|
||||
'server' => 'Client Area',
|
||||
'app' => 'Server List',
|
||||
], multiple: true);
|
||||
}
|
||||
}
|
||||
$panels = is_string($panels) ? explode(',', $panels) : $panels;
|
||||
|
||||
$panelVersion = $this->option('panelVersion');
|
||||
if (!$panelVersion) {
|
||||
$panelVersion = $this->ask('Required panel version (leave empty for no constraint)', config('app.version') === 'canary' ? null : config('app.version'));
|
||||
|
||||
if ($panelVersion && $this->confirm("Should the version constraint be minimal instead of strict? ($panelVersion or higher instead of only $panelVersion)")) {
|
||||
$panelVersion = "^$panelVersion";
|
||||
}
|
||||
}
|
||||
|
||||
$composerPackages = null;
|
||||
// TODO: ask for composer packages?
|
||||
|
||||
// Create base directory
|
||||
$this->filesystem->makeDirectory(plugin_path($id));
|
||||
|
||||
// Write plugin.json
|
||||
$this->filesystem->put(plugin_path($id, 'plugin.json'), json_encode([
|
||||
'id' => $id,
|
||||
'name' => $name,
|
||||
'author' => $author,
|
||||
'version' => '1.0.0',
|
||||
'description' => $description,
|
||||
'category' => $category,
|
||||
'url' => $url,
|
||||
'update_url' => $updateUrl,
|
||||
'namespace' => $namespace,
|
||||
'class' => $class,
|
||||
'panels' => $panels,
|
||||
'panel_version' => $panelVersion,
|
||||
'composer_packages' => $composerPackages,
|
||||
'meta' => [
|
||||
'status' => PluginStatus::Enabled,
|
||||
'status_message' => null,
|
||||
],
|
||||
], JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
||||
|
||||
// Create src directory and create main class
|
||||
$this->filesystem->makeDirectory(plugin_path($id, 'src'));
|
||||
$this->filesystem->put(plugin_path($id, 'src', $class . '.php'), Str::replace(['$namespace$', '$class$', '$id$'], [$namespace, $class, $id], file_get_contents(__DIR__ . '/Plugin.stub')));
|
||||
|
||||
// Create Providers directory and create service provider
|
||||
$this->filesystem->makeDirectory(plugin_path($id, 'src', 'Providers'));
|
||||
$this->filesystem->put(plugin_path($id, 'src', 'Providers', $class . 'Provider.php'), Str::replace(['$namespace$', '$class$'], [$namespace, $class], file_get_contents(__DIR__ . '/PluginProvider.stub')));
|
||||
|
||||
// Create config directory and create config file
|
||||
$this->filesystem->makeDirectory(plugin_path($id, 'config'));
|
||||
$this->filesystem->put(plugin_path($id, 'config', $id . '.php'), Str::replace(['$name$'], [$name], file_get_contents(__DIR__ . '/PluginConfig.stub')));
|
||||
|
||||
$this->info('Plugin created.');
|
||||
}
|
||||
}
|
||||
25
app/Console/Commands/Plugin/Plugin.stub
Normal file
25
app/Console/Commands/Plugin/Plugin.stub
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace $namespace$;
|
||||
|
||||
use Filament\Contracts\Plugin;
|
||||
use Filament\Panel;
|
||||
|
||||
class $class$ implements Plugin
|
||||
{
|
||||
public function getId(): string
|
||||
{
|
||||
return '$id$';
|
||||
}
|
||||
|
||||
public function register(Panel $panel): void
|
||||
{
|
||||
// Allows you to use any configuration option that is available to the panel.
|
||||
// This includes registering resources, custom pages, themes, render hooks and more.
|
||||
}
|
||||
|
||||
public function boot(Panel $panel): void
|
||||
{
|
||||
// Is run only when the panel that the plugin is being registered to is actually in-use. It is executed by a middleware class.
|
||||
}
|
||||
}
|
||||
5
app/Console/Commands/Plugin/PluginConfig.stub
Normal file
5
app/Console/Commands/Plugin/PluginConfig.stub
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
// Config values for $name$
|
||||
];
|
||||
18
app/Console/Commands/Plugin/PluginProvider.stub
Normal file
18
app/Console/Commands/Plugin/PluginProvider.stub
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace $namespace$\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class $class$Provider extends ServiceProvider
|
||||
{
|
||||
public function register(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function boot(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
43
app/Console/Commands/Plugin/UninstallPluginCommand.php
Normal file
43
app/Console/Commands/Plugin/UninstallPluginCommand.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands\Plugin;
|
||||
|
||||
use App\Enums\PluginStatus;
|
||||
use App\Models\Plugin;
|
||||
use App\Services\Helpers\PluginService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class UninstallPluginCommand extends Command
|
||||
{
|
||||
protected $signature = 'p:plugin:uninstall {id?} {--delete : Delete the plugin files}';
|
||||
|
||||
protected $description = 'Uninstalls a plugin';
|
||||
|
||||
public function handle(PluginService $pluginService): void
|
||||
{
|
||||
$id = $this->argument('id') ?? $this->choice('Plugin', Plugin::pluck('name', 'id')->toArray());
|
||||
|
||||
$plugin = Plugin::find($id);
|
||||
|
||||
if (!$plugin) {
|
||||
$this->error('Plugin does not exist!');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($plugin->status === PluginStatus::NotInstalled) {
|
||||
$this->error('Plugin is not installed!');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$deleteFiles = $this->option('delete');
|
||||
if ($this->input->isInteractive() && !$deleteFiles) {
|
||||
$deleteFiles = $this->confirm('Do you also want to delete the plugin files?');
|
||||
}
|
||||
|
||||
$pluginService->uninstallPlugin($plugin, $deleteFiles);
|
||||
|
||||
$this->info('Plugin uninstalled' . ($deleteFiles ? ' and files deleted' : '') . '.');
|
||||
}
|
||||
}
|
||||
37
app/Console/Commands/Plugin/UpdatePluginCommand.php
Normal file
37
app/Console/Commands/Plugin/UpdatePluginCommand.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands\Plugin;
|
||||
|
||||
use App\Models\Plugin;
|
||||
use App\Services\Helpers\PluginService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class UpdatePluginCommand extends Command
|
||||
{
|
||||
protected $signature = 'p:plugin:update {id?}';
|
||||
|
||||
protected $description = 'Updates a plugin';
|
||||
|
||||
public function handle(PluginService $pluginService): void
|
||||
{
|
||||
$id = $this->argument('id') ?? $this->choice('Plugin', Plugin::pluck('name', 'id')->toArray());
|
||||
|
||||
$plugin = Plugin::find($id);
|
||||
|
||||
if (!$plugin) {
|
||||
$this->error('Plugin does not exist!');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$plugin->isUpdateAvailable()) {
|
||||
$this->error("Plugin doesn't need updating!");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$pluginService->updatePlugin($plugin);
|
||||
|
||||
$this->info('Plugin updated.');
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,13 @@
|
||||
|
||||
namespace App\Contracts\Http;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
|
||||
interface ClientPermissionsRequest
|
||||
{
|
||||
/**
|
||||
* Returns the permissions string indicating which permission should be used to
|
||||
* validate that the authenticated user has permission to perform this action against
|
||||
* the given resource (server).
|
||||
* Returns the permission used to validate that the authenticated user may perform
|
||||
* this action against the given resource (server).
|
||||
*/
|
||||
public function permission(): string;
|
||||
public function permission(): SubuserPermission|string;
|
||||
}
|
||||
|
||||
18
app/Contracts/Plugins/HasPluginSettings.php
Normal file
18
app/Contracts/Plugins/HasPluginSettings.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Contracts\Plugins;
|
||||
|
||||
use Filament\Schemas\Components\Component;
|
||||
|
||||
interface HasPluginSettings
|
||||
{
|
||||
/**
|
||||
* @return Component[]
|
||||
*/
|
||||
public function getSettingsForm(): array;
|
||||
|
||||
/**
|
||||
* @param array<mixed, mixed> $data
|
||||
*/
|
||||
public function saveSettings(array $data): void;
|
||||
}
|
||||
141
app/Enums/EditorLanguages.php
Normal file
141
app/Enums/EditorLanguages.php
Normal file
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use Filament\Support\Contracts\HasLabel;
|
||||
|
||||
enum EditorLanguages: string implements HasLabel
|
||||
{
|
||||
case plaintext = 'plaintext';
|
||||
case abap = 'abap';
|
||||
case apex = 'apex';
|
||||
case azcali = 'azcali';
|
||||
case bat = 'bat';
|
||||
case bicep = 'bicep';
|
||||
case cameligo = 'cameligo';
|
||||
case clojure = 'clojure';
|
||||
case coffeescript = 'coffeescript';
|
||||
case c = 'c';
|
||||
case cpp = 'cpp';
|
||||
case csharp = 'csharp';
|
||||
case csp = 'csp';
|
||||
case css = 'css';
|
||||
case cypher = 'cypher';
|
||||
case dart = 'dart';
|
||||
case dockerfile = 'dockerfile';
|
||||
case ecl = 'ecl';
|
||||
case elixir = 'elixir';
|
||||
case flow9 = 'flow9';
|
||||
case fsharp = 'fsharp';
|
||||
case go = 'go';
|
||||
case graphql = 'graphql';
|
||||
case handlebars = 'handlebars';
|
||||
case hcl = 'hcl';
|
||||
case html = 'html';
|
||||
case ini = 'ini';
|
||||
case java = 'java';
|
||||
case javascript = 'javascript';
|
||||
case julia = 'julia';
|
||||
case json = 'json';
|
||||
case kotlin = 'kotlin';
|
||||
case less = 'less';
|
||||
case lexon = 'lexon';
|
||||
case lua = 'lua';
|
||||
case liquid = 'liquid';
|
||||
case m3 = 'm3';
|
||||
case markdown = 'markdown';
|
||||
case mdx = 'mdx';
|
||||
case mips = 'mips';
|
||||
case msdax = 'msdax';
|
||||
case mysql = 'mysql';
|
||||
case objectivec = 'objective-c';
|
||||
case pascal = 'pascal';
|
||||
case pascaligo = 'pascaligo';
|
||||
case perl = 'perl';
|
||||
case pgsql = 'pgsql';
|
||||
case php = 'php';
|
||||
case pla = 'pla';
|
||||
case postiats = 'postiats';
|
||||
case powerquery = 'powerquery';
|
||||
case powershell = 'powershell';
|
||||
case proto = 'proto';
|
||||
case pug = 'pug';
|
||||
case python = 'python';
|
||||
case qsharp = 'qsharp';
|
||||
case r = 'r';
|
||||
case razor = 'razor';
|
||||
case redis = 'redis';
|
||||
case redshift = 'redshift';
|
||||
case restructuredtext = 'restructuredtext';
|
||||
case ruby = 'ruby';
|
||||
case rust = 'rust';
|
||||
case sb = 'sb';
|
||||
case scala = 'scala';
|
||||
case scheme = 'scheme';
|
||||
case scss = 'scss';
|
||||
case shell = 'shell';
|
||||
case sol = 'sol';
|
||||
case aes = 'aes';
|
||||
case sparql = 'sparql';
|
||||
case sql = 'sql';
|
||||
case st = 'st';
|
||||
case swift = 'swift';
|
||||
case systemverilog = 'systemverilog';
|
||||
case verilog = 'verilog';
|
||||
case tcl = 'tcl';
|
||||
case twig = 'twig';
|
||||
case typescript = 'typescript';
|
||||
case typespec = 'typespec';
|
||||
case vb = 'vb';
|
||||
case wgsl = 'wgsl';
|
||||
case xml = 'xml';
|
||||
case yaml = 'yaml';
|
||||
|
||||
public static function fromWithAlias(string $match): self
|
||||
{
|
||||
return match ($match) {
|
||||
'h' => self::c,
|
||||
|
||||
'cc', 'hpp' => self::cpp,
|
||||
|
||||
'cs' => self::csharp,
|
||||
|
||||
'class' => self::java,
|
||||
|
||||
'htm' => self::html,
|
||||
|
||||
'js', 'mjs', 'cjs' => self::javascript,
|
||||
|
||||
'kt', 'kts' => self::kotlin,
|
||||
|
||||
'md' => self::markdown,
|
||||
|
||||
'm' => self::objectivec,
|
||||
|
||||
'pl', 'pm' => self::perl,
|
||||
|
||||
'php3', 'php4', 'php5', 'phtml' => self::php,
|
||||
|
||||
'py', 'pyc', 'pyo', 'pyi' => self::python,
|
||||
|
||||
'rdata', 'rds' => self::r,
|
||||
|
||||
'rb', 'erb' => self::ruby,
|
||||
|
||||
'sc' => self::scala,
|
||||
|
||||
'sh', 'zsh' => self::shell,
|
||||
|
||||
'ts', 'tsx' => self::typescript,
|
||||
|
||||
'yml' => self::yaml,
|
||||
|
||||
default => self::tryFrom($match) ?? self::plaintext,
|
||||
};
|
||||
}
|
||||
|
||||
public function getLabel(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
27
app/Enums/PluginCategory.php
Normal file
27
app/Enums/PluginCategory.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use Filament\Support\Contracts\HasIcon;
|
||||
use Filament\Support\Contracts\HasLabel;
|
||||
|
||||
enum PluginCategory: string implements HasIcon, HasLabel
|
||||
{
|
||||
case Plugin = 'plugin';
|
||||
case Theme = 'theme';
|
||||
case Language = 'language';
|
||||
|
||||
public function getIcon(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::Plugin => 'tabler-package',
|
||||
self::Theme => 'tabler-palette',
|
||||
self::Language => 'tabler-language',
|
||||
};
|
||||
}
|
||||
|
||||
public function getLabel(): string
|
||||
{
|
||||
return trans('admin/plugin.category_enum.' . $this->value);
|
||||
}
|
||||
}
|
||||
43
app/Enums/PluginStatus.php
Normal file
43
app/Enums/PluginStatus.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use Filament\Support\Contracts\HasColor;
|
||||
use Filament\Support\Contracts\HasIcon;
|
||||
use Filament\Support\Contracts\HasLabel;
|
||||
|
||||
enum PluginStatus: string implements HasColor, HasIcon, HasLabel
|
||||
{
|
||||
case NotInstalled = 'not_installed';
|
||||
case Disabled = 'disabled';
|
||||
case Enabled = 'enabled';
|
||||
case Errored = 'errored';
|
||||
case Incompatible = 'incompatible';
|
||||
|
||||
public function getIcon(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::NotInstalled => 'tabler-heart-off',
|
||||
self::Disabled => 'tabler-heart-x',
|
||||
self::Enabled => 'tabler-heart-check',
|
||||
self::Errored => 'tabler-heart-broken',
|
||||
self::Incompatible => 'tabler-heart-cancel',
|
||||
};
|
||||
}
|
||||
|
||||
public function getColor(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::NotInstalled => 'gray',
|
||||
self::Disabled => 'warning',
|
||||
self::Enabled => 'success',
|
||||
self::Errored => 'danger',
|
||||
self::Incompatible => 'danger',
|
||||
};
|
||||
}
|
||||
|
||||
public function getLabel(): string
|
||||
{
|
||||
return trans('admin/plugin.status_enum.' . $this->value);
|
||||
}
|
||||
}
|
||||
88
app/Enums/SubuserPermission.php
Normal file
88
app/Enums/SubuserPermission.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum SubuserPermission: string
|
||||
{
|
||||
case WebsocketConnect = 'websocket.connect';
|
||||
|
||||
case ControlConsole = 'control.console';
|
||||
case ControlStart = 'control.start';
|
||||
case ControlStop = 'control.stop';
|
||||
case ControlRestart = 'control.restart';
|
||||
|
||||
case FileRead = 'file.read';
|
||||
case FileReadContent = 'file.read-content';
|
||||
case FileCreate = 'file.create';
|
||||
case FileUpdate = 'file.update';
|
||||
case FileDelete = 'file.delete';
|
||||
case FileArchive = 'file.archive';
|
||||
case FileSftp = 'file.sftp';
|
||||
|
||||
case BackupRead = 'backup.read';
|
||||
case BackupCreate = 'backup.create';
|
||||
case BackupDelete = 'backup.delete';
|
||||
case BackupDownload = 'backup.download';
|
||||
case BackupRestore = 'backup.restore';
|
||||
|
||||
case ScheduleRead = 'schedule.read';
|
||||
case ScheduleCreate = 'schedule.create';
|
||||
case ScheduleUpdate = 'schedule.update';
|
||||
case ScheduleDelete = 'schedule.delete';
|
||||
|
||||
case UserRead = 'user.read';
|
||||
case UserCreate = 'user.create';
|
||||
case UserUpdate = 'user.update';
|
||||
case UserDelete = 'user.delete';
|
||||
|
||||
case DatabaseRead = 'database.read';
|
||||
case DatabaseCreate = 'database.create';
|
||||
case DatabaseUpdate = 'database.update';
|
||||
case DatabaseDelete = 'database.delete';
|
||||
case DatabaseViewPassword = 'database.view-password';
|
||||
|
||||
case AllocationRead = 'allocation.read';
|
||||
case AllocationCreate = 'allocation.create';
|
||||
case AllocationUpdate = 'allocation.update';
|
||||
case AllocationDelete = 'allocation.delete';
|
||||
|
||||
case ActivityRead = 'activity.read';
|
||||
|
||||
case StartupRead = 'startup.read';
|
||||
case StartupUpdate = 'startup.update';
|
||||
case StartupDockerImage = 'startup.docker-image';
|
||||
|
||||
case SettingsRename = 'settings.rename';
|
||||
case SettingsDescription = 'settings.description';
|
||||
case SettingsReinstall = 'settings.reinstall';
|
||||
|
||||
/** @return string[] */
|
||||
public function split(): array
|
||||
{
|
||||
return explode('.', $this->value, 2);
|
||||
}
|
||||
|
||||
public function isHidden(): bool
|
||||
{
|
||||
return $this === self::WebsocketConnect;
|
||||
}
|
||||
|
||||
public function getIcon(): ?string
|
||||
{
|
||||
[$group, $permission] = $this->split();
|
||||
|
||||
return match ($group) {
|
||||
'control' => 'tabler-terminal-2',
|
||||
'user' => 'tabler-users',
|
||||
'file' => 'tabler-files',
|
||||
'backup' => 'tabler-file-zip',
|
||||
'allocation' => 'tabler-network',
|
||||
'startup' => 'tabler-player-play',
|
||||
'database' => 'tabler-database',
|
||||
'schedule' => 'tabler-clock',
|
||||
'settings' => 'tabler-settings',
|
||||
'activity' => 'tabler-stack',
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
namespace App\Extensions\Features\Schemas;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Extensions\Features\FeatureSchemaInterface;
|
||||
use App\Facades\Activity;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServerVariable;
|
||||
use App\Repositories\Daemon\DaemonServerRepository;
|
||||
@@ -54,7 +54,7 @@ class GSLTokenSchema implements FeatureSchemaInterface
|
||||
->modalHeading('Invalid GSL token')
|
||||
->modalDescription('It seems like your Gameserver Login Token (GSL token) is invalid or has expired.')
|
||||
->modalSubmitActionLabel('Update GSL Token')
|
||||
->disabledSchema(fn () => !user()?->can(Permission::ACTION_STARTUP_UPDATE, $server))
|
||||
->disabledSchema(fn () => !user()?->can(SubuserPermission::StartupUpdate, $server))
|
||||
->schema([
|
||||
TextEntry::make('info')
|
||||
->label(new HtmlString(Blade::render('You can either <x-filament::link href="https://steamcommunity.com/dev/managegameservers" target="_blank">generate a new one</x-filament::link> and enter it below or leave the field blank to remove it completely.'))),
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
namespace App\Extensions\Features\Schemas;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Extensions\Features\FeatureSchemaInterface;
|
||||
use App\Facades\Activity;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Server;
|
||||
use App\Repositories\Daemon\DaemonServerRepository;
|
||||
use Exception;
|
||||
@@ -44,7 +44,7 @@ class JavaVersionSchema implements FeatureSchemaInterface
|
||||
->modalHeading('Unsupported Java Version')
|
||||
->modalDescription('This server is currently running an unsupported version of Java and cannot be started.')
|
||||
->modalSubmitActionLabel('Update Docker Image')
|
||||
->disabledSchema(fn () => !user()?->can(Permission::ACTION_STARTUP_DOCKER_IMAGE, $server))
|
||||
->disabledSchema(fn () => !user()?->can(SubuserPermission::StartupDockerImage, $server))
|
||||
->schema([
|
||||
TextEntry::make('java')
|
||||
->label('Please select a supported version from the list below to continue starting the server.'),
|
||||
|
||||
@@ -123,7 +123,7 @@ class ListLogs extends BaseListLogs
|
||||
}
|
||||
}),
|
||||
DeleteAction::make()
|
||||
->icon('tabler-trash')->iconSize(IconSize::Medium)->iconButton(),
|
||||
->iconSize(IconSize::Medium)->iconButton(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ class ViewLogs extends BaseViewLog
|
||||
BackAction::make()
|
||||
->icon('tabler-arrow-left')->iconSize(IconSize::ExtraLarge)->iconButton(),
|
||||
DeleteAction::make(withTooltip: true)
|
||||
->icon('tabler-trash')->iconSize(IconSize::ExtraLarge)->iconButton(),
|
||||
->iconSize(IconSize::ExtraLarge)->iconButton(),
|
||||
DownloadAction::make(withTooltip: true)
|
||||
->icon('tabler-file-download')->iconSize(IconSize::ExtraLarge)->iconButton(),
|
||||
Action::make('uploadLogs')
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
|
||||
namespace App\Filament\Admin\Resources\Eggs\Pages;
|
||||
|
||||
use App\Enums\EditorLanguages;
|
||||
use App\Filament\Admin\Resources\Eggs\EggResource;
|
||||
use App\Filament\Components\Forms\Fields\CopyFrom;
|
||||
use App\Filament\Components\Forms\Fields\MonacoEditor;
|
||||
use App\Models\EggVariable;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
@@ -11,7 +13,6 @@ use Exception;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Forms\Components\Checkbox;
|
||||
use Filament\Forms\Components\CodeEditor;
|
||||
use Filament\Forms\Components\Hidden;
|
||||
use Filament\Forms\Components\KeyValue;
|
||||
use Filament\Forms\Components\Repeater;
|
||||
@@ -268,8 +269,9 @@ class CreateEgg extends CreateRecord
|
||||
'/bin/bash' => '/bin/bash',
|
||||
])
|
||||
->required(),
|
||||
CodeEditor::make('script_install')
|
||||
MonacoEditor::make('script_install')
|
||||
->label(trans('admin/egg.script_install'))
|
||||
->language(EditorLanguages::shell)
|
||||
->columnSpanFull()
|
||||
->lazy(),
|
||||
]),
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
|
||||
namespace App\Filament\Admin\Resources\Eggs\Pages;
|
||||
|
||||
use App\Enums\EditorLanguages;
|
||||
use App\Filament\Admin\Resources\Eggs\EggResource;
|
||||
use App\Filament\Components\Actions\ExportEggAction;
|
||||
use App\Filament\Components\Actions\ImportEggAction;
|
||||
use App\Filament\Components\Forms\Fields\CopyFrom;
|
||||
use App\Filament\Components\Forms\Fields\MonacoEditor;
|
||||
use App\Models\Egg;
|
||||
use App\Models\EggVariable;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
@@ -15,7 +17,6 @@ use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Actions\DeleteAction;
|
||||
use Filament\Forms\Components\Checkbox;
|
||||
use Filament\Forms\Components\CodeEditor;
|
||||
use Filament\Forms\Components\FileUpload;
|
||||
use Filament\Forms\Components\Hidden;
|
||||
use Filament\Forms\Components\KeyValue;
|
||||
@@ -38,7 +39,9 @@ use Filament\Schemas\Components\Utilities\Get;
|
||||
use Filament\Schemas\Components\Utilities\Set;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Support\Enums\IconSize;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Validation\Rules\Unique;
|
||||
use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
|
||||
|
||||
class EditEgg extends EditRecord
|
||||
{
|
||||
@@ -84,7 +87,8 @@ class EditEgg extends EditRecord
|
||||
->tabs([
|
||||
Tab::make(trans('admin/egg.import.url'))
|
||||
->schema([
|
||||
Hidden::make('base64Image'),
|
||||
Hidden::make('imageUrl'),
|
||||
Hidden::make('imageExtension'),
|
||||
TextInput::make('image_url')
|
||||
->label(trans('admin/egg.import.image_url'))
|
||||
->reactive()
|
||||
@@ -93,28 +97,21 @@ class EditEgg extends EditRecord
|
||||
->afterStateUpdated(function ($state, Set $set) {
|
||||
if (!$state) {
|
||||
$set('image_url_error', null);
|
||||
$set('imageUrl', null);
|
||||
$set('imageExtension', null);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!filter_var($state, FILTER_VALIDATE_URL)) {
|
||||
throw new \Exception(trans('admin/egg.import.invalid_url'));
|
||||
throw new Exception(trans('admin/egg.import.invalid_url'));
|
||||
}
|
||||
|
||||
$allowedExtensions = [
|
||||
'png' => 'image/png',
|
||||
'jpg' => 'image/jpeg',
|
||||
'jpeg' => 'image/jpeg',
|
||||
'gif' => 'image/gif',
|
||||
'webp' => 'image/webp',
|
||||
'svg' => 'image/svg+xml',
|
||||
];
|
||||
|
||||
$extension = strtolower(pathinfo(parse_url($state, PHP_URL_PATH), PATHINFO_EXTENSION));
|
||||
|
||||
if (!array_key_exists($extension, $allowedExtensions)) {
|
||||
throw new \Exception(trans('admin/egg.import.unsupported_format', ['format' => implode(', ', $allowedExtensions)]));
|
||||
if (!array_key_exists($extension, Egg::IMAGE_FORMATS)) {
|
||||
throw new Exception(trans('admin/egg.import.unsupported_format', ['format' => implode(', ', array_keys(Egg::IMAGE_FORMATS))]));
|
||||
}
|
||||
|
||||
$host = parse_url($state, PHP_URL_HOST);
|
||||
@@ -123,37 +120,17 @@ class EditEgg extends EditRecord
|
||||
if (
|
||||
filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false
|
||||
) {
|
||||
throw new \Exception(trans('admin/egg.import.no_local_ip'));
|
||||
throw new Exception(trans('admin/egg.import.no_local_ip'));
|
||||
}
|
||||
|
||||
$context = stream_context_create([
|
||||
'http' => ['timeout' => 3],
|
||||
'https' => [
|
||||
'timeout' => 3,
|
||||
'verify_peer' => true,
|
||||
'verify_peer_name' => true,
|
||||
],
|
||||
]);
|
||||
|
||||
$imageContent = @file_get_contents($state, false, $context, 0, 1048576); // 1024KB
|
||||
|
||||
if (!$imageContent) {
|
||||
throw new \Exception(trans('admin/egg.import.image_error'));
|
||||
}
|
||||
|
||||
if (strlen($imageContent) >= 1048576) {
|
||||
throw new \Exception(trans('admin/egg.import.image_too_large'));
|
||||
}
|
||||
|
||||
$mimeType = $allowedExtensions[$extension];
|
||||
$base64 = 'data:' . $mimeType . ';base64,' . base64_encode($imageContent);
|
||||
|
||||
$set('base64Image', $base64);
|
||||
$set('imageUrl', $state);
|
||||
$set('imageExtension', $extension);
|
||||
$set('image_url_error', null);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
} catch (Exception $e) {
|
||||
$set('image_url_error', $e->getMessage());
|
||||
$set('base64Image', null);
|
||||
$set('imageUrl', null);
|
||||
$set('imageExtension', null);
|
||||
}
|
||||
}),
|
||||
TextEntry::make('image_url_error')
|
||||
@@ -172,57 +149,68 @@ class EditEgg extends EditRecord
|
||||
->previewable()
|
||||
->openable(false)
|
||||
->downloadable(false)
|
||||
->maxSize(1024)
|
||||
->maxSize(256)
|
||||
->maxFiles(1)
|
||||
->columnSpanFull()
|
||||
->alignCenter()
|
||||
->imageEditor()
|
||||
->saveUploadedFileUsing(function ($file, Set $set) {
|
||||
$base64 = "data:{$file->getMimeType()};base64,". base64_encode(file_get_contents($file->getRealPath()));
|
||||
$set('base64Image', $base64);
|
||||
|
||||
return $base64;
|
||||
->image()
|
||||
->disk('public')
|
||||
->directory(Egg::ICON_STORAGE_PATH)
|
||||
->acceptedFileTypes([
|
||||
'image/png',
|
||||
'image/jpeg',
|
||||
'image/webp',
|
||||
'image/svg+xml',
|
||||
])
|
||||
->getUploadedFileNameForStorageUsing(function (TemporaryUploadedFile $file, $record) {
|
||||
return $record->uuid . '.' . $file->getClientOriginalExtension();
|
||||
}),
|
||||
]),
|
||||
]),
|
||||
])
|
||||
->action(function (array $data, $record): void {
|
||||
$base64 = $data['base64Image'] ?? null;
|
||||
|
||||
if (empty($base64) && !empty($data['image'])) {
|
||||
$base64 = $data['image'];
|
||||
}
|
||||
|
||||
if (!empty($base64)) {
|
||||
$record->update([
|
||||
'image' => $base64,
|
||||
]);
|
||||
if (!empty($data['imageUrl']) && !empty($data['imageExtension'])) {
|
||||
$this->saveImageFromUrl($data['imageUrl'], $data['imageExtension'], $record);
|
||||
|
||||
Notification::make()
|
||||
->title(trans('admin/egg.import.image_updated'))
|
||||
->success()
|
||||
->send();
|
||||
|
||||
$record->refresh();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!empty($data['image'])) {
|
||||
Notification::make()
|
||||
->title(trans('admin/egg.import.image_updated'))
|
||||
->success()
|
||||
->send();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($data['imageUrl']) && empty($data['image'])) {
|
||||
Notification::make()
|
||||
->title(trans('admin/egg.import.no_image'))
|
||||
->warning()
|
||||
->send();
|
||||
}
|
||||
}),
|
||||
Action::make('deleteImage')
|
||||
Action::make('delete_image')
|
||||
->visible(fn ($record) => $record->image)
|
||||
->label('')
|
||||
->hiddenLabel()
|
||||
->icon('tabler-trash')
|
||||
->iconButton()
|
||||
->iconSize(IconSize::Large)
|
||||
->color('danger')
|
||||
->action(function ($record) {
|
||||
|
||||
$record->update([
|
||||
'image' => null,
|
||||
]);
|
||||
foreach (array_keys(Egg::IMAGE_FORMATS) as $ext) {
|
||||
$path = Egg::ICON_STORAGE_PATH . "/$record->uuid.$ext";
|
||||
if (Storage::disk('public')->exists($path)) {
|
||||
Storage::disk('public')->delete($path);
|
||||
}
|
||||
}
|
||||
|
||||
Notification::make()
|
||||
->title(trans('admin/egg.import.image_deleted'))
|
||||
@@ -437,8 +425,9 @@ class EditEgg extends EditRecord
|
||||
'/bin/bash' => '/bin/bash',
|
||||
])
|
||||
->required(),
|
||||
CodeEditor::make('script_install')
|
||||
MonacoEditor::make('script_install')
|
||||
->hiddenLabel()
|
||||
->language(EditorLanguages::shell)
|
||||
->columnSpanFull(),
|
||||
]),
|
||||
])->columnSpanFull()->persistTabInQueryString(),
|
||||
@@ -467,6 +456,37 @@ class EditEgg extends EditRecord
|
||||
$this->fillForm();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save an image from URL download to a file.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
private function saveImageFromUrl(string $imageUrl, string $extension, Egg $egg): void
|
||||
{
|
||||
$context = stream_context_create([
|
||||
'http' => ['timeout' => 3],
|
||||
'https' => [
|
||||
'timeout' => 3,
|
||||
'verify_peer' => true,
|
||||
'verify_peer_name' => true,
|
||||
],
|
||||
]);
|
||||
|
||||
$data = @file_get_contents($imageUrl, false, $context, 0, 1048576); // 1024KB
|
||||
|
||||
if (empty($data)) {
|
||||
throw new Exception(trans('admin/egg.import.invalid_url'));
|
||||
}
|
||||
|
||||
$normalizedExtension = match ($extension) {
|
||||
'svg+xml' => 'svg',
|
||||
'jpeg' => 'jpg',
|
||||
default => $extension,
|
||||
};
|
||||
|
||||
Storage::disk('public')->put(Egg::ICON_STORAGE_PATH . "/$egg->uuid.$normalizedExtension", $data);
|
||||
}
|
||||
|
||||
protected function getFormActions(): array
|
||||
{
|
||||
return [];
|
||||
|
||||
43
app/Filament/Admin/Resources/Plugins/Pages/ListPlugins.php
Normal file
43
app/Filament/Admin/Resources/Plugins/Pages/ListPlugins.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\Plugins\Pages;
|
||||
|
||||
use App\Enums\PluginCategory;
|
||||
use App\Filament\Admin\Resources\Plugins\PluginResource;
|
||||
use App\Models\Plugin;
|
||||
use App\Services\Helpers\PluginService;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
use Filament\Schemas\Components\Tabs\Tab;
|
||||
|
||||
class ListPlugins extends ListRecords
|
||||
{
|
||||
protected static string $resource = PluginResource::class;
|
||||
|
||||
public function reorderTable(array $order, int|string|null $draggedRecordKey = null): void
|
||||
{
|
||||
/** @var PluginService $pluginService */
|
||||
$pluginService = app(PluginService::class); // @phpstan-ignore myCustomRules.forbiddenGlobalFunctions
|
||||
|
||||
$pluginService->updateLoadOrder($order);
|
||||
}
|
||||
|
||||
public function getTabs(): array
|
||||
{
|
||||
$tabs = [
|
||||
'all' => Tab::make('all')
|
||||
->label(trans('admin/plugin.all'))
|
||||
->badge(Plugin::count()),
|
||||
];
|
||||
|
||||
foreach (PluginCategory::cases() as $category) {
|
||||
$query = Plugin::whereCategory($category->value);
|
||||
$tabs[$category->value] = Tab::make($category->value)
|
||||
->label($category->getLabel())
|
||||
->icon($category->getIcon())
|
||||
->badge($query->count())
|
||||
->modifyQueryUsing(fn () => $query);
|
||||
}
|
||||
|
||||
return $tabs;
|
||||
}
|
||||
}
|
||||
313
app/Filament/Admin/Resources/Plugins/PluginResource.php
Normal file
313
app/Filament/Admin/Resources/Plugins/PluginResource.php
Normal file
@@ -0,0 +1,313 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\Plugins;
|
||||
|
||||
use App\Enums\PluginStatus;
|
||||
use App\Filament\Admin\Resources\Plugins\Pages\ListPlugins;
|
||||
use App\Models\Plugin;
|
||||
use App\Services\Helpers\PluginService;
|
||||
use Exception;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Forms\Components\FileUpload;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Infolists\Components\TextEntry;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Support\Enums\IconSize;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
|
||||
class PluginResource extends Resource
|
||||
{
|
||||
protected static ?string $model = Plugin::class;
|
||||
|
||||
protected static string|\BackedEnum|null $navigationIcon = 'tabler-packages';
|
||||
|
||||
protected static ?string $recordTitleAttribute = 'name';
|
||||
|
||||
public static function getNavigationLabel(): string
|
||||
{
|
||||
return trans('admin/plugin.nav_title');
|
||||
}
|
||||
|
||||
public static function getModelLabel(): string
|
||||
{
|
||||
return trans('admin/plugin.model_label');
|
||||
}
|
||||
|
||||
public static function getPluralModelLabel(): string
|
||||
{
|
||||
return trans('admin/plugin.model_label_plural');
|
||||
}
|
||||
|
||||
public static function getNavigationBadge(): ?string
|
||||
{
|
||||
return (string) static::getEloquentQuery()->count() ?: null;
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->openRecordUrlInNewTab()
|
||||
->reorderable('load_order')
|
||||
->authorizeReorder(fn () => user()?->can('update plugin'))
|
||||
->reorderRecordsTriggerAction(fn (Action $action, bool $isReordering) => $action->hiddenLabel()->tooltip($isReordering ? trans('admin/plugin.apply_load_order') : trans('admin/plugin.change_load_order')))
|
||||
->defaultSort('load_order')
|
||||
->columns([
|
||||
TextColumn::make('name')
|
||||
->label(trans('admin/plugin.name'))
|
||||
->description(fn (Plugin $plugin) => (strlen($plugin->description) > 80) ? substr($plugin->description, 0, 80).'...' : $plugin->description)
|
||||
->icon(fn (Plugin $plugin) => $plugin->isUpdateAvailable() ? 'tabler-versions-off' : 'tabler-versions')
|
||||
->iconColor(fn (Plugin $plugin) => $plugin->isUpdateAvailable() ? 'danger' : 'success')
|
||||
->tooltip(fn (Plugin $plugin) => $plugin->isUpdateAvailable() ? trans('admin/plugin.update_available') : null)
|
||||
->sortable()
|
||||
->searchable(),
|
||||
TextColumn::make('author')
|
||||
->label(trans('admin/plugin.author'))
|
||||
->sortable(),
|
||||
TextColumn::make('version')
|
||||
->label(trans('admin/plugin.version'))
|
||||
->sortable(),
|
||||
TextColumn::make('category')
|
||||
->label(trans('admin/plugin.category'))
|
||||
->badge()
|
||||
->sortable()
|
||||
->visible(fn ($livewire) => $livewire->activeTab === 'all'),
|
||||
TextColumn::make('status')
|
||||
->label(trans('admin/plugin.status'))
|
||||
->badge()
|
||||
->tooltip(fn (Plugin $plugin) => $plugin->status_message)
|
||||
->sortable(),
|
||||
])
|
||||
->recordActions([
|
||||
Action::make('view')
|
||||
->label(trans('filament-actions::view.single.label'))
|
||||
->icon(fn (Plugin $plugin) => $plugin->getReadme() ? 'tabler-eye' : 'tabler-eye-share')
|
||||
->color('gray')
|
||||
->visible(fn (Plugin $plugin) => $plugin->getReadme() || $plugin->url)
|
||||
->url(fn (Plugin $plugin) => !$plugin->getReadme() ? $plugin->url : null, true)
|
||||
->slideOver(true)
|
||||
->modalHeading('Readme')
|
||||
->modalSubmitAction(fn (Plugin $plugin) => Action::make('visit_website')
|
||||
->label(trans('admin/plugin.visit_website'))
|
||||
->visible(!is_null($plugin->url))
|
||||
->url($plugin->url, true)
|
||||
)
|
||||
->modalCancelActionLabel(trans('filament::components/modal.actions.close.label'))
|
||||
->schema(fn (Plugin $plugin) => $plugin->getReadme() ? [
|
||||
TextEntry::make('readme')
|
||||
->hiddenLabel()
|
||||
->markdown()
|
||||
->state(fn (Plugin $plugin) => $plugin->getReadme()),
|
||||
] : null),
|
||||
Action::make('settings')
|
||||
->label(trans('admin/plugin.settings'))
|
||||
->authorize(fn (Plugin $plugin) => user()?->can('update', $plugin))
|
||||
->icon('tabler-settings')
|
||||
->color('primary')
|
||||
->visible(fn (Plugin $plugin) => $plugin->status === PluginStatus::Enabled && $plugin->hasSettings())
|
||||
->schema(fn (Plugin $plugin) => $plugin->getSettingsForm())
|
||||
->action(fn (array $data, Plugin $plugin) => $plugin->saveSettings($data))
|
||||
->slideOver(),
|
||||
ActionGroup::make([
|
||||
Action::make('install')
|
||||
->label(trans('admin/plugin.install'))
|
||||
->authorize(fn (Plugin $plugin) => user()?->can('update', $plugin))
|
||||
->icon('tabler-terminal')
|
||||
->color('success')
|
||||
->hidden(fn (Plugin $plugin) => $plugin->status !== PluginStatus::NotInstalled)
|
||||
->action(function (Plugin $plugin, $livewire, PluginService $pluginService) {
|
||||
$pluginService->installPlugin($plugin, !$plugin->isTheme() || !$pluginService->hasThemePluginEnabled());
|
||||
|
||||
redirect(ListPlugins::getUrl(['tab' => $livewire->activeTab]));
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title(trans('admin/plugin.notifications.installed'))
|
||||
->send();
|
||||
}),
|
||||
Action::make('update')
|
||||
->label(trans('admin/plugin.update'))
|
||||
->authorize(fn (Plugin $plugin) => user()?->can('update', $plugin))
|
||||
->icon('tabler-download')
|
||||
->color('success')
|
||||
->visible(fn (Plugin $plugin) => $plugin->status !== PluginStatus::NotInstalled && $plugin->isUpdateAvailable())
|
||||
->action(function (Plugin $plugin, $livewire, PluginService $pluginService) {
|
||||
$pluginService->updatePlugin($plugin);
|
||||
|
||||
redirect(ListPlugins::getUrl(['tab' => $livewire->activeTab]));
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title(trans('admin/plugin.notifications.updated'))
|
||||
->send();
|
||||
}),
|
||||
Action::make('enable')
|
||||
->label(trans('admin/plugin.enable'))
|
||||
->authorize(fn (Plugin $plugin) => user()?->can('update', $plugin))
|
||||
->icon('tabler-check')
|
||||
->color('success')
|
||||
->visible(fn (Plugin $plugin) => $plugin->canEnable())
|
||||
->requiresConfirmation(fn (Plugin $plugin, PluginService $pluginService) => $plugin->isTheme() && $pluginService->hasThemePluginEnabled())
|
||||
->modalHeading(fn (Plugin $plugin, PluginService $pluginService) => $plugin->isTheme() && $pluginService->hasThemePluginEnabled() ? trans('admin/plugin.enable_theme_modal.heading') : null)
|
||||
->modalDescription(fn (Plugin $plugin, PluginService $pluginService) => $plugin->isTheme() && $pluginService->hasThemePluginEnabled() ? trans('admin/plugin.enable_theme_modal.description') : null)
|
||||
->action(function (Plugin $plugin, $livewire, PluginService $pluginService) {
|
||||
$pluginService->enablePlugin($plugin);
|
||||
|
||||
redirect(ListPlugins::getUrl(['tab' => $livewire->activeTab]));
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title(trans('admin/plugin.notifications.updated'))
|
||||
->send();
|
||||
}),
|
||||
Action::make('disable')
|
||||
->label(trans('admin/plugin.disable'))
|
||||
->authorize(fn (Plugin $plugin) => user()?->can('update', $plugin))
|
||||
->icon('tabler-x')
|
||||
->color('warning')
|
||||
->visible(fn (Plugin $plugin) => $plugin->canDisable())
|
||||
->action(function (Plugin $plugin, $livewire, PluginService $pluginService) {
|
||||
$pluginService->disablePlugin($plugin);
|
||||
|
||||
redirect(ListPlugins::getUrl(['tab' => $livewire->activeTab]));
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title(trans('admin/plugin.notifications.disabled'))
|
||||
->send();
|
||||
}),
|
||||
Action::make('delete')
|
||||
->label(trans('filament-actions::delete.single.label'))
|
||||
->authorize(fn (Plugin $plugin) => user()?->can('delete', $plugin))
|
||||
->icon('tabler-trash')
|
||||
->color('danger')
|
||||
->requiresConfirmation()
|
||||
->visible(fn (Plugin $plugin) => $plugin->status === PluginStatus::NotInstalled)
|
||||
->action(function (Plugin $plugin, $livewire, PluginService $pluginService) {
|
||||
$pluginService->deletePlugin($plugin);
|
||||
|
||||
redirect(ListPlugins::getUrl(['tab' => $livewire->activeTab]));
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title(trans('admin/plugin.notifications.deleted'))
|
||||
->send();
|
||||
}),
|
||||
Action::make('uninstall')
|
||||
->label(trans('admin/plugin.uninstall'))
|
||||
->authorize(fn (Plugin $plugin) => user()?->can('update', $plugin))
|
||||
->icon('tabler-terminal')
|
||||
->color('danger')
|
||||
->requiresConfirmation()
|
||||
->hidden(fn (Plugin $plugin) => $plugin->status === PluginStatus::NotInstalled)
|
||||
->action(function (Plugin $plugin, $livewire, PluginService $pluginService) {
|
||||
$pluginService->uninstallPlugin($plugin);
|
||||
|
||||
redirect(ListPlugins::getUrl(['tab' => $livewire->activeTab]));
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title(trans('admin/plugin.notifications.uninstalled'))
|
||||
->send();
|
||||
}),
|
||||
]),
|
||||
])
|
||||
->headerActions([
|
||||
Action::make('import_from_file')
|
||||
->label(trans('admin/plugin.import_from_file'))
|
||||
->authorize(fn () => user()?->can('create', Plugin::class))
|
||||
->icon('tabler-file-download')
|
||||
->iconButton()
|
||||
->iconSize(IconSize::ExtraLarge)
|
||||
->schema([
|
||||
// TODO: switch to new file upload
|
||||
FileUpload::make('file')
|
||||
->required()
|
||||
->acceptedFileTypes(['application/zip', 'application/zip-compressed', 'application/x-zip-compressed'])
|
||||
->preserveFilenames()
|
||||
->previewable(false)
|
||||
->storeFiles(false),
|
||||
])
|
||||
->action(function ($data, $livewire, PluginService $pluginService) {
|
||||
try {
|
||||
/** @var UploadedFile $file */
|
||||
$file = $data['file'];
|
||||
|
||||
$pluginName = str($file->getClientOriginalName())->before('.zip')->toString();
|
||||
|
||||
if (Plugin::where('id', $pluginName)->exists()) {
|
||||
throw new Exception(trans('admin/plugin.notifications.import_exists'));
|
||||
}
|
||||
|
||||
$pluginService->downloadPluginFromFile($file);
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title(trans('admin/plugin.notifications.imported'))
|
||||
->send();
|
||||
|
||||
redirect(ListPlugins::getUrl(['tab' => $livewire->activeTab]));
|
||||
} catch (Exception $exception) {
|
||||
report($exception);
|
||||
|
||||
Notification::make()
|
||||
->danger()
|
||||
->title(trans('admin/plugin.notifications.import_failed'))
|
||||
->body($exception->getMessage())
|
||||
->send();
|
||||
}
|
||||
}),
|
||||
Action::make('import_from_url')
|
||||
->label(trans('admin/plugin.import_from_url'))
|
||||
->authorize(fn () => user()?->can('create', Plugin::class))
|
||||
->icon('tabler-world-download')
|
||||
->iconButton()
|
||||
->iconSize(IconSize::ExtraLarge)
|
||||
->schema([
|
||||
TextInput::make('url')
|
||||
->required()
|
||||
->url()
|
||||
->endsWith('.zip'),
|
||||
])
|
||||
->action(function ($data, $livewire, PluginService $pluginService) {
|
||||
try {
|
||||
$pluginName = str($data['url'])->before('.zip')->explode('/')->last();
|
||||
|
||||
if (Plugin::where('id', $pluginName)->exists()) {
|
||||
throw new Exception(trans('admin/plugin.notifications.import_exists'));
|
||||
}
|
||||
|
||||
$pluginService->downloadPluginFromUrl($data['url']);
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title(trans('admin/plugin.notifications.imported'))
|
||||
->send();
|
||||
|
||||
redirect(ListPlugins::getUrl(['tab' => $livewire->activeTab]));
|
||||
} catch (Exception $exception) {
|
||||
report($exception);
|
||||
|
||||
Notification::make()
|
||||
->danger()
|
||||
->title(trans('admin/plugin.notifications.import_failed'))
|
||||
->body($exception->getMessage())
|
||||
->send();
|
||||
}
|
||||
}),
|
||||
])
|
||||
->emptyStateIcon('tabler-packages')
|
||||
->emptyStateDescription('')
|
||||
->emptyStateHeading(trans('admin/plugin.no_plugins'));
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => ListPlugins::route('/'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,9 @@ use App\Enums\SuspendAction;
|
||||
use App\Filament\Admin\Resources\Servers\RelationManagers\AllocationsRelationManager;
|
||||
use App\Filament\Admin\Resources\Servers\RelationManagers\DatabasesRelationManager;
|
||||
use App\Filament\Admin\Resources\Servers\ServerResource;
|
||||
use App\Filament\Components\Actions\DeleteServerIcon;
|
||||
use App\Filament\Components\Actions\PreviewStartupAction;
|
||||
use App\Filament\Components\Forms\Fields\MonacoEditor;
|
||||
use App\Filament\Components\Forms\Fields\StartupVariable;
|
||||
use App\Filament\Components\StateCasts\ServerConditionStateCast;
|
||||
use App\Filament\Server\Pages\Console;
|
||||
@@ -27,7 +29,6 @@ use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Exception;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Forms\Components\CodeEditor;
|
||||
use Filament\Forms\Components\FileUpload;
|
||||
use Filament\Forms\Components\Hidden;
|
||||
use Filament\Forms\Components\KeyValue;
|
||||
@@ -57,7 +58,9 @@ use Filament\Support\Enums\IconSize;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Http\Client\ConnectionException;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\HtmlString;
|
||||
use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
|
||||
use LogicException;
|
||||
use Random\RandomException;
|
||||
|
||||
@@ -113,155 +116,125 @@ class EditServer extends EditRecord
|
||||
->modal()
|
||||
->modalSubmitActionLabel(trans('server/setting.server_info.icon.upload'))
|
||||
->schema([
|
||||
Tabs::make()->tabs([
|
||||
Tab::make(trans('admin/egg.import.url'))
|
||||
->schema([
|
||||
Hidden::make('base64Image'),
|
||||
TextInput::make('image_url')
|
||||
->label(trans('admin/egg.import.image_url'))
|
||||
->reactive()
|
||||
->autocomplete(false)
|
||||
->debounce(500)
|
||||
->afterStateUpdated(function ($state, Set $set) {
|
||||
if (!$state) {
|
||||
$set('image_url_error', null);
|
||||
Tabs::make()
|
||||
->contained(false)
|
||||
->tabs([
|
||||
Tab::make(trans('admin/egg.import.url'))
|
||||
->schema([
|
||||
Hidden::make('imageUrl'),
|
||||
Hidden::make('imageExtension'),
|
||||
TextInput::make('image_url')
|
||||
->label(trans('admin/egg.import.image_url'))
|
||||
->reactive()
|
||||
->autocomplete(false)
|
||||
->debounce(500)
|
||||
->afterStateUpdated(function ($state, Set $set) {
|
||||
if (!$state) {
|
||||
$set('image_url_error', null);
|
||||
$set('imageUrl', null);
|
||||
$set('imageExtension', null);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!in_array(parse_url($state, PHP_URL_SCHEME), ['http', 'https'], true)) {
|
||||
throw new \Exception(trans('admin/egg.import.invalid_url'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!filter_var($state, FILTER_VALIDATE_URL)) {
|
||||
throw new \Exception(trans('admin/egg.import.invalid_url'));
|
||||
try {
|
||||
if (!in_array(parse_url($state, PHP_URL_SCHEME), ['http', 'https'], true)) {
|
||||
throw new \Exception(trans('admin/egg.import.invalid_url'));
|
||||
}
|
||||
|
||||
if (!filter_var($state, FILTER_VALIDATE_URL)) {
|
||||
throw new \Exception(trans('admin/egg.import.invalid_url'));
|
||||
}
|
||||
|
||||
$extension = strtolower(pathinfo(parse_url($state, PHP_URL_PATH), PATHINFO_EXTENSION));
|
||||
|
||||
if (!array_key_exists($extension, Server::IMAGE_FORMATS)) {
|
||||
throw new \Exception(trans('admin/egg.import.unsupported_format', ['format' => implode(', ', array_keys(Server::IMAGE_FORMATS))]));
|
||||
}
|
||||
|
||||
$host = parse_url($state, PHP_URL_HOST);
|
||||
$ip = gethostbyname($host);
|
||||
|
||||
if (
|
||||
filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false
|
||||
) {
|
||||
throw new \Exception(trans('admin/egg.import.no_local_ip'));
|
||||
}
|
||||
|
||||
$set('imageUrl', $state);
|
||||
$set('imageExtension', $extension);
|
||||
$set('image_url_error', null);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$set('image_url_error', $e->getMessage());
|
||||
$set('imageUrl', null);
|
||||
$set('imageExtension', null);
|
||||
}
|
||||
|
||||
$allowedExtensions = [
|
||||
'png' => 'image/png',
|
||||
'jpg' => 'image/jpeg',
|
||||
'jpeg' => 'image/jpeg',
|
||||
'gif' => 'image/gif',
|
||||
'webp' => 'image/webp',
|
||||
'svg' => 'image/svg+xml',
|
||||
];
|
||||
|
||||
$extension = strtolower(pathinfo(parse_url($state, PHP_URL_PATH), PATHINFO_EXTENSION));
|
||||
|
||||
if (!array_key_exists($extension, $allowedExtensions)) {
|
||||
throw new \Exception(trans('admin/egg.import.unsupported_format', ['format' => implode(', ', array_keys($allowedExtensions))]));
|
||||
}
|
||||
|
||||
$host = parse_url($state, PHP_URL_HOST);
|
||||
$ip = gethostbyname($host);
|
||||
|
||||
if (
|
||||
filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false
|
||||
) {
|
||||
throw new \Exception(trans('admin/egg.import.no_local_ip'));
|
||||
}
|
||||
|
||||
$context = stream_context_create([
|
||||
'http' => ['timeout' => 3],
|
||||
'https' => [
|
||||
'timeout' => 3,
|
||||
'verify_peer' => true,
|
||||
'verify_peer_name' => true,
|
||||
],
|
||||
]);
|
||||
|
||||
$imageContent = @file_get_contents($state, false, $context, 0, 262144); //256KB
|
||||
|
||||
if (!$imageContent) {
|
||||
throw new \Exception(trans('admin/egg.import.image_error'));
|
||||
}
|
||||
|
||||
$mimeType = $allowedExtensions[$extension];
|
||||
$base64 = 'data:' . $mimeType . ';base64,' . base64_encode($imageContent);
|
||||
|
||||
$set('base64Image', $base64);
|
||||
$set('image_url_error', null);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$set('image_url_error', $e->getMessage());
|
||||
$set('base64Image', null);
|
||||
}
|
||||
}),
|
||||
TextEntry::make('image_url_error')
|
||||
->hiddenLabel()
|
||||
->visible(fn (Get $get) => $get('image_url_error') !== null)
|
||||
->afterStateHydrated(fn (Get $get) => $get('image_url_error')),
|
||||
Image::make(fn (Get $get) => $get('image_url'), '')
|
||||
->imageSize(150)
|
||||
->visible(fn (Get $get) => $get('image_url') && !$get('image_url_error'))
|
||||
->alignCenter(),
|
||||
]),
|
||||
Tab::make(trans('admin/egg.import.file'))
|
||||
->schema([
|
||||
FileUpload::make('image')
|
||||
->hiddenLabel()
|
||||
->previewable()
|
||||
->openable(false)
|
||||
->downloadable(false)
|
||||
->maxSize(256)
|
||||
->maxFiles(1)
|
||||
->columnSpanFull()
|
||||
->alignCenter()
|
||||
->imageEditor()
|
||||
->image()
|
||||
->saveUploadedFileUsing(function ($file, Set $set) {
|
||||
$base64 = "data:{$file->getMimeType()};base64,". base64_encode(file_get_contents($file->getRealPath()));
|
||||
$set('base64Image', $base64);
|
||||
|
||||
return $base64;
|
||||
}),
|
||||
]),
|
||||
]),
|
||||
}),
|
||||
TextEntry::make('image_url_error')
|
||||
->hiddenLabel()
|
||||
->visible(fn (Get $get) => $get('image_url_error') !== null)
|
||||
->afterStateHydrated(fn (Get $get) => $get('image_url_error')),
|
||||
Image::make(fn (Get $get) => $get('image_url'), '')
|
||||
->imageSize(150)
|
||||
->visible(fn (Get $get) => $get('image_url') && !$get('image_url_error'))
|
||||
->alignCenter(),
|
||||
]),
|
||||
Tab::make(trans('admin/egg.import.file'))
|
||||
->schema([
|
||||
FileUpload::make('image')
|
||||
->hiddenLabel()
|
||||
->previewable()
|
||||
->openable(false)
|
||||
->downloadable(false)
|
||||
->maxSize(256)
|
||||
->maxFiles(1)
|
||||
->columnSpanFull()
|
||||
->alignCenter()
|
||||
->imageEditor()
|
||||
->image()
|
||||
->disk('public')
|
||||
->directory(Server::ICON_STORAGE_PATH)
|
||||
->acceptedFileTypes([
|
||||
'image/png',
|
||||
'image/jpeg',
|
||||
'image/webp',
|
||||
'image/svg+xml',
|
||||
])
|
||||
->getUploadedFileNameForStorageUsing(function (TemporaryUploadedFile $file, $record) {
|
||||
return $record->uuid . '.' . $file->getClientOriginalExtension();
|
||||
}),
|
||||
]),
|
||||
]),
|
||||
])
|
||||
->action(function (array $data, $record): void {
|
||||
$base64 = $data['base64Image'] ?? null;
|
||||
|
||||
if (empty($base64) && !empty($data['image'])) {
|
||||
$base64 = $data['image'];
|
||||
}
|
||||
|
||||
if (!empty($base64)) {
|
||||
$record->update([
|
||||
'icon' => $base64,
|
||||
]);
|
||||
|
||||
if (!empty($data['imageUrl']) && !empty($data['imageExtension'])) {
|
||||
$this->saveIconFromUrl($data['imageUrl'], $data['imageExtension'], $record);
|
||||
Notification::make()
|
||||
->title(trans('server/setting.server_info.icon.updated'))
|
||||
->success()
|
||||
->send();
|
||||
|
||||
$record->refresh();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!empty($data['image'])) {
|
||||
Notification::make()
|
||||
->title(trans('server/setting.server_info.icon.updated'))
|
||||
->success()
|
||||
->send();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($data['imageUrl']) && empty($data['image'])) {
|
||||
Notification::make()
|
||||
->title(trans('admin/egg.import.no_image'))
|
||||
->warning()
|
||||
->send();
|
||||
}
|
||||
}),
|
||||
Action::make('deleteIcon')
|
||||
->visible(fn ($record) => $record->icon)
|
||||
->label('')
|
||||
->icon('tabler-trash')
|
||||
->iconButton()->iconSize(IconSize::Large)
|
||||
->color('danger')
|
||||
->action(function ($record) {
|
||||
$record->update([
|
||||
'icon' => null,
|
||||
]);
|
||||
|
||||
Notification::make()
|
||||
->title(trans('server/setting.server_info.icon.deleted'))
|
||||
->success()
|
||||
->send();
|
||||
|
||||
$record->refresh();
|
||||
}),
|
||||
DeleteServerIcon::make(),
|
||||
]),
|
||||
Grid::make()
|
||||
->columns(3)
|
||||
@@ -330,13 +303,13 @@ class EditServer extends EditRecord
|
||||
->modalFooterActionsAlignment(Alignment::Right)
|
||||
->modalCancelActionLabel(trans('filament::components/modal.actions.close.label'))
|
||||
->schema([
|
||||
CodeEditor::make('logs')
|
||||
MonacoEditor::make('logs')
|
||||
->hiddenLabel()
|
||||
->formatStateUsing(function (Server $server, DaemonServerRepository $serverRepository) {
|
||||
try {
|
||||
$logs = $serverRepository->setServer($server)->getInstallLogs();
|
||||
|
||||
return mb_convert_encoding($logs, 'UTF-8', ['UTF-8', 'UTF-16', 'ISO-8859-1', 'Windows-1252', 'ASCII']);
|
||||
return mb_convert_encoding($logs, 'UTF-8', ['UTF-8', 'UTF-16', 'ISO-8859-1', 'ASCII']);
|
||||
} catch (ConnectionException) {
|
||||
Notification::make()
|
||||
->title(trans('admin/server.notifications.error_connecting', ['node' => $server->node->name]))
|
||||
@@ -798,37 +771,17 @@ class EditServer extends EditRecord
|
||||
->label(trans('admin/server.startup_cmd'))
|
||||
->required()
|
||||
->live()
|
||||
->afterStateUpdated(function (Set $set, $state) {
|
||||
$set('startup', $state);
|
||||
$set('previewing', false);
|
||||
})
|
||||
->options(function ($state, Get $get, Set $set) {
|
||||
->options(function (Get $get) {
|
||||
$egg = Egg::find($get('egg_id'));
|
||||
$startups = $egg->startup_commands ?? [];
|
||||
|
||||
$currentStartup = $get('startup');
|
||||
if (!$currentStartup && $startups) {
|
||||
$currentStartup = collect($startups)->first();
|
||||
$set('startup', $currentStartup);
|
||||
$set('select_startup', $currentStartup);
|
||||
}
|
||||
|
||||
return array_flip($startups) + ['custom' => 'Custom Startup'];
|
||||
return array_flip($egg->startup_commands ?? []) + ['custom' => 'Custom Startup'];
|
||||
})
|
||||
->formatStateUsing(function (Server $server) {
|
||||
$startups = $server->egg->startup_commands;
|
||||
|
||||
$currentStartup = $server->startup;
|
||||
$matchingStartup = collect($startups)
|
||||
->filter(fn ($value, $key) => $value === $currentStartup)
|
||||
->keys()
|
||||
->first();
|
||||
|
||||
if (!$matchingStartup) {
|
||||
return 'custom';
|
||||
->formatStateUsing(fn (Server $server) => in_array($server->startup, $server->egg->startup_commands) ? $server->startup : 'custom')
|
||||
->afterStateUpdated(function (Set $set, string $state) {
|
||||
if ($state !== 'custom') {
|
||||
$set('startup', $state);
|
||||
}
|
||||
|
||||
return $matchingStartup;
|
||||
$set('previewing', false);
|
||||
})
|
||||
->selectablePlaceholder(false)
|
||||
->columnSpanFull()
|
||||
@@ -1214,6 +1167,37 @@ class EditServer extends EditRecord
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save an icon from URL download to a file.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
private function saveIconFromUrl(string $imageUrl, string $extension, Server $server): void
|
||||
{
|
||||
$context = stream_context_create([
|
||||
'http' => ['timeout' => 3],
|
||||
'https' => [
|
||||
'timeout' => 3,
|
||||
'verify_peer' => true,
|
||||
'verify_peer_name' => true,
|
||||
],
|
||||
]);
|
||||
|
||||
$data = @file_get_contents($imageUrl, false, $context, 0, 262144); //256KB
|
||||
|
||||
if (empty($data)) {
|
||||
throw new \Exception(trans('admin/egg.import.invalid_url'));
|
||||
}
|
||||
|
||||
$normalizedExtension = match ($extension) {
|
||||
'svg+xml' => 'svg',
|
||||
'jpeg' => 'jpg',
|
||||
default => $extension,
|
||||
};
|
||||
|
||||
Storage::disk('public')->put(Server::ICON_STORAGE_PATH . "/$server->uuid.$normalizedExtension", $data);
|
||||
}
|
||||
|
||||
public function getRelationManagers(): array
|
||||
{
|
||||
return [
|
||||
|
||||
@@ -33,6 +33,7 @@ use Filament\Forms\Components\FileUpload;
|
||||
use Filament\Forms\Components\Repeater;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Toggle;
|
||||
use Filament\Infolists\Components\TextEntry;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Resources\Pages\PageRegistration;
|
||||
@@ -224,65 +225,71 @@ class UserResource extends Resource
|
||||
'md' => 1,
|
||||
'lg' => 1,
|
||||
]),
|
||||
Select::make('timezone')
|
||||
->label(trans('profile.timezone'))
|
||||
Toggle::make('is_managed_externally')
|
||||
->label(trans('admin/user.is_managed_externally'))
|
||||
->hintIcon('tabler-question-mark', trans('admin/user.is_managed_externally_helper'))
|
||||
->inline(false)
|
||||
->columnSpan([
|
||||
'default' => 1,
|
||||
'md' => 1,
|
||||
'lg' => 1,
|
||||
])
|
||||
->required()
|
||||
->prefixIcon('tabler-clock-pin')
|
||||
->default(fn () => config('app.timezone', 'UTC'))
|
||||
->selectablePlaceholder(false)
|
||||
->options(fn () => collect(DateTimeZone::listIdentifiers())->mapWithKeys(fn ($tz) => [$tz => $tz]))
|
||||
->searchable(),
|
||||
Select::make('language')
|
||||
->label(trans('profile.language'))
|
||||
->columnSpan([
|
||||
'default' => 1,
|
||||
'md' => 1,
|
||||
'lg' => 1,
|
||||
])
|
||||
->required()
|
||||
->prefixIcon('tabler-flag')
|
||||
->live()
|
||||
->default('en')
|
||||
->searchable()
|
||||
->selectablePlaceholder(false)
|
||||
->options(fn (LanguageService $languageService) => $languageService->getAvailableLanguages()),
|
||||
FileUpload::make('avatar')
|
||||
->visible(fn (?User $user, FileUpload $fileUpload) => $user ? $fileUpload->getDisk()->exists($fileUpload->getDirectory() . '/' . $user->id . '.png') : false)
|
||||
->avatar()
|
||||
->directory('avatars')
|
||||
->disk('public')
|
||||
->formatStateUsing(function (FileUpload $fileUpload, ?User $user) {
|
||||
if (!$user) {
|
||||
return null;
|
||||
}
|
||||
$path = $fileUpload->getDirectory() . '/' . $user->id . '.png';
|
||||
if ($fileUpload->getDisk()->exists($path)) {
|
||||
return $path;
|
||||
}
|
||||
})
|
||||
->deleteUploadedFileUsing(function (FileUpload $fileUpload, $file) {
|
||||
if ($file instanceof TemporaryUploadedFile) {
|
||||
return $file->delete();
|
||||
}
|
||||
]),
|
||||
Section::make(trans('profile.tabs.customization'))
|
||||
->collapsible()
|
||||
->columnSpanFull()
|
||||
->columns(2)
|
||||
->schema([
|
||||
Select::make('timezone')
|
||||
->label(trans('profile.timezone'))
|
||||
->required()
|
||||
->prefixIcon('tabler-clock-pin')
|
||||
->default(fn () => config('app.timezone', 'UTC'))
|
||||
->selectablePlaceholder(false)
|
||||
->options(fn () => collect(DateTimeZone::listIdentifiers())->mapWithKeys(fn ($tz) => [$tz => $tz]))
|
||||
->searchable(),
|
||||
Select::make('language')
|
||||
->label(trans('profile.language'))
|
||||
->required()
|
||||
->prefixIcon('tabler-flag')
|
||||
->live()
|
||||
->default('en')
|
||||
->searchable()
|
||||
->selectablePlaceholder(false)
|
||||
->options(fn (LanguageService $languageService) => $languageService->getAvailableLanguages()),
|
||||
FileUpload::make('avatar')
|
||||
->visible(fn (?User $user, FileUpload $fileUpload) => $user ? $fileUpload->getDisk()->exists($fileUpload->getDirectory() . '/' . $user->id . '.png') : false)
|
||||
->columnSpanFull()
|
||||
->avatar()
|
||||
->directory('avatars')
|
||||
->disk('public')
|
||||
->formatStateUsing(function (FileUpload $fileUpload, ?User $user) {
|
||||
if (!$user) {
|
||||
return null;
|
||||
}
|
||||
$path = $fileUpload->getDirectory() . '/' . $user->id . '.png';
|
||||
if ($fileUpload->getDisk()->exists($path)) {
|
||||
return $path;
|
||||
}
|
||||
})
|
||||
->deleteUploadedFileUsing(function (FileUpload $fileUpload, $file) {
|
||||
if ($file instanceof TemporaryUploadedFile) {
|
||||
return $file->delete();
|
||||
}
|
||||
|
||||
if ($fileUpload->getDisk()->exists($file)) {
|
||||
return $fileUpload->getDisk()->delete($file);
|
||||
}
|
||||
}),
|
||||
if ($fileUpload->getDisk()->exists($file)) {
|
||||
return $fileUpload->getDisk()->delete($file);
|
||||
}
|
||||
}),
|
||||
]),
|
||||
Section::make(trans('profile.tabs.oauth'))
|
||||
->visible(fn (?User $user) => $user)
|
||||
->collapsible()
|
||||
->columnSpanFull()
|
||||
->schema(function (OAuthService $oauthService, ?User $user) {
|
||||
|
||||
if (!$user) {
|
||||
return;
|
||||
}
|
||||
|
||||
$actions = [];
|
||||
foreach ($user->oauth ?? [] as $schema => $_) {
|
||||
$schema = $oauthService->get($schema);
|
||||
|
||||
@@ -25,9 +25,11 @@ class CreateWebhookConfiguration extends CreateRecord
|
||||
{
|
||||
return [
|
||||
$this->getCancelFormAction()->formId('form')
|
||||
->iconButton()->iconSize(IconSize::ExtraLarge),
|
||||
->iconButton()->iconSize(IconSize::ExtraLarge)
|
||||
->icon('tabler-arrow-left'),
|
||||
$this->getCreateFormAction()->formId('form')
|
||||
->iconButton()->iconSize(IconSize::ExtraLarge),
|
||||
->iconButton()->iconSize(IconSize::ExtraLarge)
|
||||
->icon('tabler-file-plus'),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -4,11 +4,11 @@ namespace App\Filament\App\Resources\Servers\Pages;
|
||||
|
||||
use App\Enums\CustomizationKey;
|
||||
use App\Enums\ServerResourceType;
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Filament\App\Resources\Servers\ServerResource;
|
||||
use App\Filament\Components\Tables\Columns\ProgressBarColumn;
|
||||
use App\Filament\Components\Tables\Columns\ServerEntryColumn;
|
||||
use App\Filament\Server\Pages\Console;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Server;
|
||||
use App\Repositories\Daemon\DaemonServerRepository;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
@@ -91,7 +91,7 @@ class ListServers extends ListRecords
|
||||
->label('')
|
||||
->warningThresholdPercent(static::WARNING_THRESHOLD)
|
||||
->dangerThresholdPercent(static::DANGER_THRESHOLD)
|
||||
->maxValue(fn (Server $server) => ServerResourceType::CPULimit->getResourceAmount($server) === 0 ? ($server->node->systemInformation()['cpu_count'] ?? 0 * 100) : ServerResourceType::CPULimit->getResourceAmount($server))
|
||||
->maxValue(fn (Server $server) => ServerResourceType::CPULimit->getResourceAmount($server) === 0 ? (($server->node->systemInformation()['cpu_count'] ?? 0) * 100) : ServerResourceType::CPULimit->getResourceAmount($server))
|
||||
->state(fn (Server $server) => $server->retrieveResources()['cpu_absolute'] ?? 0)
|
||||
->helperLabel(fn (Server $server) => $server->formatResource(ServerResourceType::CPU, 0) . ' / ' . $server->formatResource(ServerResourceType::CPULimit, 0)),
|
||||
ProgressBarColumn::make('memoryUsage')
|
||||
@@ -244,21 +244,21 @@ class ListServers extends ListRecords
|
||||
->label(trans('server/console.power_actions.start'))
|
||||
->color('primary')
|
||||
->icon('tabler-player-play-filled')
|
||||
->authorize(fn (Server $server) => user()?->can(Permission::ACTION_CONTROL_START, $server))
|
||||
->authorize(fn (Server $server) => user()?->can(SubuserPermission::ControlStart, $server))
|
||||
->visible(fn (Server $server) => $server->retrieveStatus()->isStartable())
|
||||
->dispatch('powerAction', fn (Server $server) => ['server' => $server, 'action' => 'start']),
|
||||
Action::make('restart')
|
||||
->label(trans('server/console.power_actions.restart'))
|
||||
->color('gray')
|
||||
->icon('tabler-reload')
|
||||
->authorize(fn (Server $server) => user()?->can(Permission::ACTION_CONTROL_RESTART, $server))
|
||||
->authorize(fn (Server $server) => user()?->can(SubuserPermission::ControlRestart, $server))
|
||||
->visible(fn (Server $server) => $server->retrieveStatus()->isRestartable())
|
||||
->dispatch('powerAction', fn (Server $server) => ['server' => $server, 'action' => 'restart']),
|
||||
Action::make('stop')
|
||||
->label(trans('server/console.power_actions.stop'))
|
||||
->color('danger')
|
||||
->icon('tabler-player-stop-filled')
|
||||
->authorize(fn (Server $server) => user()?->can(Permission::ACTION_CONTROL_STOP, $server))
|
||||
->authorize(fn (Server $server) => user()?->can(SubuserPermission::ControlStop, $server))
|
||||
->visible(fn (Server $server) => $server->retrieveStatus()->isStoppable() && !$server->retrieveStatus()->isKillable())
|
||||
->dispatch('powerAction', fn (Server $server) => ['server' => $server, 'action' => 'stop']),
|
||||
Action::make('kill')
|
||||
@@ -266,7 +266,7 @@ class ListServers extends ListRecords
|
||||
->color('danger')
|
||||
->icon('tabler-alert-square')
|
||||
->tooltip(trans('server/console.power_actions.kill_tooltip'))
|
||||
->authorize(fn (Server $server) => user()?->can(Permission::ACTION_CONTROL_STOP, $server))
|
||||
->authorize(fn (Server $server) => user()?->can(SubuserPermission::ControlStop, $server))
|
||||
->visible(fn (Server $server) => $server->retrieveStatus()->isKillable())
|
||||
->dispatch('powerAction', fn (Server $server) => ['server' => $server, 'action' => 'kill']),
|
||||
])
|
||||
|
||||
50
app/Filament/Components/Actions/DeleteServerIcon.php
Normal file
50
app/Filament/Components/Actions/DeleteServerIcon.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Components\Actions;
|
||||
|
||||
use App\Models\Server;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Support\Enums\IconSize;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class DeleteServerIcon extends Action
|
||||
{
|
||||
public static function getDefaultName(): ?string
|
||||
{
|
||||
return 'delete_icon';
|
||||
}
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->visible(fn ($record) => $record->icon);
|
||||
|
||||
$this->hiddenLabel();
|
||||
|
||||
$this->icon('tabler-trash');
|
||||
|
||||
$this->iconButton();
|
||||
|
||||
$this->iconSize(IconSize::Large);
|
||||
|
||||
$this->color('danger');
|
||||
|
||||
$this->action(function ($record) {
|
||||
foreach (array_keys(Server::IMAGE_FORMATS) as $ext) {
|
||||
$path = Server::ICON_STORAGE_PATH . "/$record->uuid.$ext";
|
||||
if (Storage::disk('public')->exists($path)) {
|
||||
Storage::disk('public')->delete($path);
|
||||
}
|
||||
}
|
||||
|
||||
Notification::make()
|
||||
->title(trans('server/setting.server_info.icon.deleted'))
|
||||
->success()
|
||||
->send();
|
||||
|
||||
$record->refresh();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ class ExportEggAction extends Action
|
||||
|
||||
$this->iconButton();
|
||||
|
||||
$this->icon('tabler-file-export');
|
||||
$this->icon('tabler-download');
|
||||
|
||||
$this->tableIcon('tabler-download');
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace App\Filament\Components\Actions;
|
||||
|
||||
use App\Models\Permission;
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Models\Schedule;
|
||||
use App\Models\Server;
|
||||
use App\Services\Schedules\Sharing\ScheduleExporterService;
|
||||
@@ -36,7 +36,7 @@ class ExportScheduleAction extends Action
|
||||
|
||||
$this->label(trans('filament-actions::export.modal.actions.export.label'));
|
||||
|
||||
$this->authorize(fn () => user()?->can(Permission::ACTION_SCHEDULE_READ, $server));
|
||||
$this->authorize(fn () => user()?->can(SubuserPermission::ScheduleRead, $server));
|
||||
|
||||
$this->action(fn (ScheduleExporterService $service, Schedule $schedule) => response()->streamDownload(function () use ($service, $schedule) {
|
||||
echo $service->handle($schedule);
|
||||
|
||||
@@ -120,7 +120,7 @@ class ImportEggAction extends Action
|
||||
FileUpload::make('files')
|
||||
->label(trans('admin/egg.model_label'))
|
||||
->hint(trans('admin/egg.import.egg_help'))
|
||||
->acceptedFileTypes(['application/json', 'application/yaml', 'application/x-yaml', 'text/yaml'])
|
||||
->acceptedFileTypes(['application/json', 'application/x-yaml', 'text/yaml', '.yaml', '.yml'])
|
||||
->preserveFilenames()
|
||||
->previewable(false)
|
||||
->storeFiles(false)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace App\Filament\Components\Actions;
|
||||
|
||||
use App\Models\Permission;
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Models\Server;
|
||||
use App\Services\Schedules\Sharing\ScheduleImporterService;
|
||||
use Exception;
|
||||
@@ -33,7 +33,7 @@ class ImportScheduleAction extends Action
|
||||
|
||||
$this->label(trans('filament-actions::import.modal.actions.import.label'));
|
||||
|
||||
$this->authorize(fn () => user()?->can(Permission::ACTION_SCHEDULE_CREATE, $server));
|
||||
$this->authorize(fn () => user()?->can(SubuserPermission::ScheduleCreate, $server));
|
||||
|
||||
$this->schema([
|
||||
Tabs::make('Tabs')
|
||||
|
||||
124
app/Filament/Components/Forms/Fields/MonacoEditor.php
Normal file
124
app/Filament/Components/Forms/Fields/MonacoEditor.php
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Components\Forms\Fields;
|
||||
|
||||
use App\Enums\EditorLanguages;
|
||||
use Closure;
|
||||
use Exception;
|
||||
use Filament\Forms\Components\Field;
|
||||
|
||||
class MonacoEditor extends Field
|
||||
{
|
||||
public bool|Closure $showLoader = true;
|
||||
|
||||
public bool|Closure $automaticLayout = true;
|
||||
|
||||
public int|Closure $lineNumbersMinChars = 3;
|
||||
|
||||
public string|Closure $fontSize = '15px';
|
||||
|
||||
public EditorLanguages|Closure $language = EditorLanguages::html;
|
||||
|
||||
public string|Closure $theme = 'blackboard';
|
||||
|
||||
protected string $view = 'filament.components.monaco-editor';
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->showLoader = config('monaco-editor.general.show-loader');
|
||||
$this->fontSize = config('monaco-editor.general.font-size');
|
||||
$this->lineNumbersMinChars = config('monaco-editor.general.line-numbers-min-chars');
|
||||
$this->automaticLayout = config('monaco-editor.general.automatic-layout');
|
||||
$this->theme = config('monaco-editor.general.default-theme');
|
||||
}
|
||||
|
||||
public function editorTheme(): string
|
||||
{
|
||||
$theme = $this->evaluate($this->theme);
|
||||
|
||||
if (!isset(config('monaco-editor.themes')[$theme])) {
|
||||
throw new Exception("Theme {$theme} not found in config file.");
|
||||
}
|
||||
|
||||
return json_encode([
|
||||
'base' => config("monaco-editor.themes.{$theme}.base"),
|
||||
'inherit' => config("monaco-editor.themes.{$theme}.inherit"),
|
||||
'rules' => config("monaco-editor.themes.{$theme}.rules"),
|
||||
'colors' => config("monaco-editor.themes.{$theme}.colors"),
|
||||
], JSON_THROW_ON_ERROR);
|
||||
}
|
||||
|
||||
public function language(EditorLanguages|Closure $lang = EditorLanguages::html): static
|
||||
{
|
||||
$this->language = $lang;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function showLoader(bool|Closure $condition = true): static
|
||||
{
|
||||
$this->showLoader = $condition;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hideLoader(): static
|
||||
{
|
||||
$this->showLoader = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function fontSize(string|Closure $size = '15px'): static
|
||||
{
|
||||
$this->fontSize = $size;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function lineNumbersMinChars(int|Closure $value = 3): static
|
||||
{
|
||||
$this->lineNumbersMinChars = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function automaticLayout(bool|Closure $condition = true): static
|
||||
{
|
||||
$this->automaticLayout = $condition;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function theme(string|Closure $name = 'blackboard'): static
|
||||
{
|
||||
$this->theme = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLanguage(): EditorLanguages
|
||||
{
|
||||
return $this->evaluate($this->language);
|
||||
}
|
||||
|
||||
public function getShowLoader(): bool
|
||||
{
|
||||
return (bool) $this->evaluate($this->showLoader);
|
||||
}
|
||||
|
||||
public function getFontSize(): string
|
||||
{
|
||||
return $this->evaluate($this->fontSize);
|
||||
}
|
||||
|
||||
public function getLineNumbersMinChars(): int
|
||||
{
|
||||
return (int) $this->evaluate($this->lineNumbersMinChars);
|
||||
}
|
||||
|
||||
public function getAutomaticLayout(): bool
|
||||
{
|
||||
return (bool) $this->evaluate($this->automaticLayout);
|
||||
}
|
||||
}
|
||||
@@ -93,12 +93,14 @@ class EditProfile extends BaseEditProfile
|
||||
->icon('tabler-user-cog')
|
||||
->schema([
|
||||
TextInput::make('username')
|
||||
->disabled(fn (User $user) => $user->is_managed_externally)
|
||||
->prefixIcon('tabler-user')
|
||||
->label(trans('profile.username'))
|
||||
->required()
|
||||
->maxLength(255)
|
||||
->unique(),
|
||||
TextInput::make('email')
|
||||
->disabled(fn (User $user) => $user->is_managed_externally)
|
||||
->prefixIcon('tabler-mail')
|
||||
->label(trans('profile.email'))
|
||||
->email()
|
||||
@@ -106,6 +108,7 @@ class EditProfile extends BaseEditProfile
|
||||
->maxLength(255)
|
||||
->unique(),
|
||||
TextInput::make('password')
|
||||
->hidden(fn (User $user) => $user->is_managed_externally)
|
||||
->label(trans('profile.password'))
|
||||
->password()
|
||||
->prefixIcon('tabler-password')
|
||||
@@ -145,6 +148,7 @@ class EditProfile extends BaseEditProfile
|
||||
FileUpload::make('avatar')
|
||||
->visible(fn () => config('panel.filament.uploadable-avatars'))
|
||||
->avatar()
|
||||
->imageEditor()
|
||||
->acceptedFileTypes(['image/png'])
|
||||
->directory('avatars')
|
||||
->disk('public')
|
||||
@@ -534,7 +538,6 @@ class EditProfile extends BaseEditProfile
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
|
||||
])
|
||||
->operation('edit')
|
||||
->model($this->getUser())
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Filament\Server\Pages;
|
||||
|
||||
use App\Enums\ConsoleWidgetPosition;
|
||||
use App\Enums\ContainerStatus;
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Exceptions\Http\Server\ServerStateConflictException;
|
||||
use App\Extensions\Features\FeatureService;
|
||||
use App\Filament\Server\Widgets\ServerConsole;
|
||||
@@ -12,7 +13,6 @@ use App\Filament\Server\Widgets\ServerMemoryChart;
|
||||
use App\Filament\Server\Widgets\ServerNetworkChart;
|
||||
use App\Filament\Server\Widgets\ServerOverview;
|
||||
use App\Livewire\AlertBanner;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Server;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use Filament\Actions\Action;
|
||||
@@ -164,7 +164,7 @@ class Console extends Page
|
||||
->label(trans('server/console.power_actions.start'))
|
||||
->color('primary')
|
||||
->icon('tabler-player-play-filled')
|
||||
->authorize(fn (Server $server) => user()?->can(Permission::ACTION_CONTROL_START, $server))
|
||||
->authorize(fn (Server $server) => user()?->can(SubuserPermission::ControlStart, $server))
|
||||
->disabled(fn (Server $server) => $server->isInConflictState() || !$this->status->isStartable())
|
||||
->action(fn (Server $server) => $this->dispatch('setServerState', uuid: $server->uuid, state: 'start'))
|
||||
->size(Size::ExtraLarge),
|
||||
@@ -172,7 +172,7 @@ class Console extends Page
|
||||
->label(trans('server/console.power_actions.restart'))
|
||||
->color('gray')
|
||||
->icon('tabler-reload')
|
||||
->authorize(fn (Server $server) => user()?->can(Permission::ACTION_CONTROL_RESTART, $server))
|
||||
->authorize(fn (Server $server) => user()?->can(SubuserPermission::ControlRestart, $server))
|
||||
->disabled(fn (Server $server) => $server->isInConflictState() || !$this->status->isRestartable())
|
||||
->action(fn (Server $server) => $this->dispatch('setServerState', uuid: $server->uuid, state: 'restart'))
|
||||
->size(Size::ExtraLarge),
|
||||
@@ -180,7 +180,7 @@ class Console extends Page
|
||||
->label(trans('server/console.power_actions.stop'))
|
||||
->color('danger')
|
||||
->icon('tabler-player-stop-filled')
|
||||
->authorize(fn (Server $server) => user()?->can(Permission::ACTION_CONTROL_STOP, $server))
|
||||
->authorize(fn (Server $server) => user()?->can(SubuserPermission::ControlStop, $server))
|
||||
->visible(fn () => !$this->status->isKillable())
|
||||
->disabled(fn (Server $server) => $server->isInConflictState() || !$this->status->isStoppable())
|
||||
->action(fn (Server $server) => $this->dispatch('setServerState', uuid: $server->uuid, state: 'stop'))
|
||||
@@ -191,7 +191,7 @@ class Console extends Page
|
||||
->icon('tabler-alert-square')
|
||||
->tooltip(trans('server/console.power_actions.kill_tooltip'))
|
||||
->requiresConfirmation()
|
||||
->authorize(fn (Server $server) => user()?->can(Permission::ACTION_CONTROL_STOP, $server))
|
||||
->authorize(fn (Server $server) => user()?->can(SubuserPermission::ControlStop, $server))
|
||||
->visible(fn () => $this->status->isKillable())
|
||||
->disabled(fn (Server $server) => $server->isInConflictState() || !$this->status->isKillable())
|
||||
->action(fn (Server $server) => $this->dispatch('setServerState', uuid: $server->uuid, state: 'kill'))
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
namespace App\Filament\Server\Pages;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Facades\Activity;
|
||||
use App\Models\Permission;
|
||||
use App\Filament\Components\Actions\DeleteServerIcon;
|
||||
use App\Models\Server;
|
||||
use App\Services\Servers\ReinstallServerService;
|
||||
use Exception;
|
||||
@@ -25,6 +26,8 @@ use Filament\Schemas\Components\Utilities\Set;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Support\Enums\Alignment;
|
||||
use Filament\Support\Enums\IconSize;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
|
||||
|
||||
class Settings extends ServerFormPage
|
||||
{
|
||||
@@ -60,7 +63,7 @@ class Settings extends ServerFormPage
|
||||
->columnStart(1)
|
||||
->columnSpanFull()
|
||||
->label(trans('server/setting.server_info.name'))
|
||||
->disabled(fn (Server $server) => !user()?->can(Permission::ACTION_SETTINGS_RENAME, $server))
|
||||
->disabled(fn (Server $server) => !user()?->can(SubuserPermission::SettingsRename, $server))
|
||||
->required()
|
||||
->live(onBlur: true)
|
||||
->afterStateUpdated(fn ($state, Server $server) => $this->updateName($state, $server)),
|
||||
@@ -69,7 +72,7 @@ class Settings extends ServerFormPage
|
||||
->columnSpanFull()
|
||||
->label(trans('server/setting.server_info.description'))
|
||||
->hidden(!config('panel.editable_server_descriptions'))
|
||||
->disabled(fn (Server $server) => !user()?->can(Permission::ACTION_SETTINGS_DESCRIPTION, $server))
|
||||
->disabled(fn (Server $server) => !user()?->can(SubuserPermission::SettingsDescription, $server))
|
||||
->autosize()
|
||||
->live(onBlur: true)
|
||||
->afterStateUpdated(fn ($state, Server $server) => $this->updateDescription($state ?? '', $server)),
|
||||
@@ -90,155 +93,124 @@ class Settings extends ServerFormPage
|
||||
->modal()
|
||||
->modalSubmitActionLabel(trans('server/setting.server_info.icon.upload'))
|
||||
->schema([
|
||||
Tabs::make()->tabs([
|
||||
Tab::make(trans('admin/egg.import.url'))
|
||||
->schema([
|
||||
Hidden::make('base64Image'),
|
||||
TextInput::make('image_url')
|
||||
->label(trans('admin/egg.import.image_url'))
|
||||
->reactive()
|
||||
->autocomplete(false)
|
||||
->debounce(500)
|
||||
->afterStateUpdated(function ($state, Set $set) {
|
||||
if (!$state) {
|
||||
$set('image_url_error', null);
|
||||
Tabs::make()
|
||||
->contained(false)
|
||||
->tabs([
|
||||
Tab::make(trans('admin/egg.import.url'))
|
||||
->schema([
|
||||
Hidden::make('imageUrl'),
|
||||
Hidden::make('imageExtension'),
|
||||
TextInput::make('image_url')
|
||||
->label(trans('admin/egg.import.image_url'))
|
||||
->reactive()
|
||||
->autocomplete(false)
|
||||
->debounce(500)
|
||||
->afterStateUpdated(function ($state, Set $set) {
|
||||
if (!$state) {
|
||||
$set('image_url_error', null);
|
||||
$set('imageUrl', null);
|
||||
$set('imageExtension', null);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!in_array(parse_url($state, PHP_URL_SCHEME), ['http', 'https'], true)) {
|
||||
throw new \Exception(trans('admin/egg.import.invalid_url'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!filter_var($state, FILTER_VALIDATE_URL)) {
|
||||
throw new \Exception(trans('admin/egg.import.invalid_url'));
|
||||
try {
|
||||
if (!in_array(parse_url($state, PHP_URL_SCHEME), ['http', 'https'], true)) {
|
||||
throw new \Exception(trans('admin/egg.import.invalid_url'));
|
||||
}
|
||||
|
||||
if (!filter_var($state, FILTER_VALIDATE_URL)) {
|
||||
throw new \Exception(trans('admin/egg.import.invalid_url'));
|
||||
}
|
||||
|
||||
$extension = strtolower(pathinfo(parse_url($state, PHP_URL_PATH), PATHINFO_EXTENSION));
|
||||
|
||||
if (!array_key_exists($extension, Server::IMAGE_FORMATS)) {
|
||||
throw new \Exception(trans('admin/egg.import.unsupported_format', ['format' => implode(', ', array_keys(Server::IMAGE_FORMATS))]));
|
||||
}
|
||||
|
||||
$host = parse_url($state, PHP_URL_HOST);
|
||||
$ip = gethostbyname($host);
|
||||
|
||||
if (
|
||||
filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false
|
||||
) {
|
||||
throw new \Exception(trans('admin/egg.import.no_local_ip'));
|
||||
}
|
||||
|
||||
$set('imageUrl', $state);
|
||||
$set('imageExtension', $extension);
|
||||
$set('image_url_error', null);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$set('image_url_error', $e->getMessage());
|
||||
$set('imageUrl', null);
|
||||
$set('imageExtension', null);
|
||||
}
|
||||
|
||||
$allowedExtensions = [
|
||||
'png' => 'image/png',
|
||||
'jpg' => 'image/jpeg',
|
||||
'jpeg' => 'image/jpeg',
|
||||
'gif' => 'image/gif',
|
||||
'webp' => 'image/webp',
|
||||
'svg' => 'image/svg+xml',
|
||||
];
|
||||
|
||||
$extension = strtolower(pathinfo(parse_url($state, PHP_URL_PATH), PATHINFO_EXTENSION));
|
||||
|
||||
if (!array_key_exists($extension, $allowedExtensions)) {
|
||||
throw new \Exception(trans('admin/egg.import.unsupported_format', ['format' => implode(', ', array_keys($allowedExtensions))]));
|
||||
}
|
||||
|
||||
$host = parse_url($state, PHP_URL_HOST);
|
||||
$ip = gethostbyname($host);
|
||||
|
||||
if (
|
||||
filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false
|
||||
) {
|
||||
throw new \Exception(trans('admin/egg.import.no_local_ip'));
|
||||
}
|
||||
|
||||
$context = stream_context_create([
|
||||
'http' => ['timeout' => 3],
|
||||
'https' => [
|
||||
'timeout' => 3,
|
||||
'verify_peer' => true,
|
||||
'verify_peer_name' => true,
|
||||
],
|
||||
]);
|
||||
|
||||
$imageContent = @file_get_contents($state, false, $context, 0, 262144); //256KB
|
||||
|
||||
if (!$imageContent) {
|
||||
throw new \Exception(trans('admin/egg.import.image_error'));
|
||||
}
|
||||
|
||||
$mimeType = $allowedExtensions[$extension];
|
||||
$base64 = 'data:' . $mimeType . ';base64,' . base64_encode($imageContent);
|
||||
|
||||
$set('base64Image', $base64);
|
||||
$set('image_url_error', null);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$set('image_url_error', $e->getMessage());
|
||||
$set('base64Image', null);
|
||||
}
|
||||
}),
|
||||
TextEntry::make('image_url_error')
|
||||
->hiddenLabel()
|
||||
->visible(fn (Get $get) => $get('image_url_error') !== null)
|
||||
->afterStateHydrated(fn (Get $get) => $get('image_url_error')),
|
||||
Image::make(fn (Get $get) => $get('image_url'), '')
|
||||
->imageSize(150)
|
||||
->visible(fn (Get $get) => $get('image_url') && !$get('image_url_error'))
|
||||
->alignCenter(),
|
||||
]),
|
||||
Tab::make(trans('admin/egg.import.file'))
|
||||
->schema([
|
||||
FileUpload::make('image')
|
||||
->hiddenLabel()
|
||||
->previewable()
|
||||
->openable(false)
|
||||
->downloadable(false)
|
||||
->maxSize(256)
|
||||
->maxFiles(1)
|
||||
->columnSpanFull()
|
||||
->alignCenter()
|
||||
->imageEditor()
|
||||
->image()
|
||||
->saveUploadedFileUsing(function ($file, Set $set) {
|
||||
$base64 = "data:{$file->getMimeType()};base64,". base64_encode(file_get_contents($file->getRealPath()));
|
||||
$set('base64Image', $base64);
|
||||
|
||||
return $base64;
|
||||
}),
|
||||
]),
|
||||
]),
|
||||
}),
|
||||
TextEntry::make('image_url_error')
|
||||
->hiddenLabel()
|
||||
->visible(fn (Get $get) => $get('image_url_error') !== null)
|
||||
->afterStateHydrated(fn (Get $get) => $get('image_url_error')),
|
||||
Image::make(fn (Get $get) => $get('image_url'), '')
|
||||
->imageSize(150)
|
||||
->visible(fn (Get $get) => $get('image_url') && !$get('image_url_error'))
|
||||
->alignCenter(),
|
||||
]),
|
||||
Tab::make(trans('admin/egg.import.file'))
|
||||
->schema([
|
||||
FileUpload::make('image')
|
||||
->hiddenLabel()
|
||||
->previewable()
|
||||
->openable(false)
|
||||
->downloadable(false)
|
||||
->maxSize(256)
|
||||
->maxFiles(1)
|
||||
->columnSpanFull()
|
||||
->alignCenter()
|
||||
->imageEditor()
|
||||
->image()
|
||||
->disk('public')
|
||||
->directory(Server::ICON_STORAGE_PATH)
|
||||
->acceptedFileTypes([
|
||||
'image/png',
|
||||
'image/jpeg',
|
||||
'image/webp',
|
||||
'image/svg+xml',
|
||||
])
|
||||
->getUploadedFileNameForStorageUsing(function (TemporaryUploadedFile $file, $record) {
|
||||
return $record->uuid . '.' . $file->getClientOriginalExtension();
|
||||
}),
|
||||
]),
|
||||
]),
|
||||
])
|
||||
->action(function (array $data, $record): void {
|
||||
$base64 = $data['base64Image'] ?? null;
|
||||
|
||||
if (empty($base64) && !empty($data['image'])) {
|
||||
$base64 = $data['image'];
|
||||
}
|
||||
|
||||
if (!empty($base64)) {
|
||||
$record->update([
|
||||
'icon' => $base64,
|
||||
]);
|
||||
|
||||
if (!empty($data['imageUrl']) && !empty($data['imageExtension'])) {
|
||||
$this->saveIconFromUrl($data['imageUrl'], $data['imageExtension'], $record);
|
||||
Notification::make()
|
||||
->title(trans('server/setting.server_info.icon.updated'))
|
||||
->success()
|
||||
->send();
|
||||
|
||||
$record->refresh();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!empty($data['image'])) {
|
||||
Notification::make()
|
||||
->title(trans('server/setting.server_info.icon.updated'))
|
||||
->success()
|
||||
->send();
|
||||
}
|
||||
|
||||
if (empty($data['imageUrl']) && empty($data['image'])) {
|
||||
Notification::make()
|
||||
->title(trans('admin/egg.import.no_image'))
|
||||
->warning()
|
||||
->send();
|
||||
}
|
||||
}),
|
||||
Action::make('deleteIcon')
|
||||
->visible(fn ($record) => $record->icon)
|
||||
->label('')
|
||||
->icon('tabler-trash')
|
||||
->iconButton()->iconSize(IconSize::Large)
|
||||
->color('danger')
|
||||
->action(function ($record) {
|
||||
$record->update([
|
||||
'icon' => null,
|
||||
]);
|
||||
|
||||
Notification::make()
|
||||
->title(trans('server/setting.server_info.icon.deleted'))
|
||||
->success()
|
||||
->send();
|
||||
|
||||
$record->refresh();
|
||||
}),
|
||||
DeleteServerIcon::make(),
|
||||
]),
|
||||
TextInput::make('uuid')
|
||||
->label(trans('server/setting.server_info.uuid'))
|
||||
@@ -319,7 +291,7 @@ class Settings extends ServerFormPage
|
||||
]),
|
||||
Fieldset::make(trans('server/setting.server_info.sftp.title'))
|
||||
->columnSpanFull()
|
||||
->hidden(fn (Server $server) => !user()?->can(Permission::ACTION_FILE_SFTP, $server))
|
||||
->hidden(fn (Server $server) => !user()?->can(SubuserPermission::FileSftp, $server))
|
||||
->columns([
|
||||
'default' => 1,
|
||||
'sm' => 1,
|
||||
@@ -361,19 +333,19 @@ class Settings extends ServerFormPage
|
||||
]),
|
||||
]),
|
||||
Section::make(trans('server/setting.reinstall.title'))
|
||||
->hidden(fn (Server $server) => !user()?->can(Permission::ACTION_SETTINGS_REINSTALL, $server))
|
||||
->hidden(fn (Server $server) => !user()?->can(SubuserPermission::SettingsReinstall, $server))
|
||||
->columnSpanFull()
|
||||
->footerActions([
|
||||
Action::make('reinstall')
|
||||
->label(trans('server/setting.reinstall.action'))
|
||||
->color('danger')
|
||||
->disabled(fn (Server $server) => !user()?->can(Permission::ACTION_SETTINGS_REINSTALL, $server))
|
||||
->disabled(fn (Server $server) => !user()?->can(SubuserPermission::SettingsReinstall, $server))
|
||||
->requiresConfirmation()
|
||||
->modalHeading(trans('server/setting.reinstall.modal'))
|
||||
->modalDescription(trans('server/setting.reinstall.modal_description'))
|
||||
->modalSubmitActionLabel(trans('server/setting.reinstall.yes'))
|
||||
->action(function (Server $server, ReinstallServerService $reinstallService) {
|
||||
abort_unless(user()?->can(Permission::ACTION_SETTINGS_REINSTALL, $server), 403);
|
||||
abort_unless(user()?->can(SubuserPermission::SettingsReinstall, $server), 403);
|
||||
|
||||
try {
|
||||
$reinstallService->handle($server);
|
||||
@@ -412,7 +384,7 @@ class Settings extends ServerFormPage
|
||||
|
||||
public function updateName(string $name, Server $server): void
|
||||
{
|
||||
abort_unless(user()?->can(Permission::ACTION_SETTINGS_RENAME, $server), 403);
|
||||
abort_unless(user()?->can(SubuserPermission::SettingsRename, $server), 403);
|
||||
|
||||
$original = $server->name;
|
||||
|
||||
@@ -443,7 +415,7 @@ class Settings extends ServerFormPage
|
||||
|
||||
public function updateDescription(string $description, Server $server): void
|
||||
{
|
||||
abort_unless(user()?->can(Permission::ACTION_SETTINGS_DESCRIPTION, $server) && config('panel.editable_server_descriptions'), 403);
|
||||
abort_unless(user()?->can(SubuserPermission::SettingsDescription, $server) && config('panel.editable_server_descriptions'), 403);
|
||||
|
||||
$original = $server->description;
|
||||
|
||||
@@ -472,6 +444,37 @@ class Settings extends ServerFormPage
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save an icon from URL download to a file.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
private function saveIconFromUrl(string $imageUrl, string $extension, Server $server): void
|
||||
{
|
||||
$context = stream_context_create([
|
||||
'http' => ['timeout' => 3],
|
||||
'https' => [
|
||||
'timeout' => 3,
|
||||
'verify_peer' => true,
|
||||
'verify_peer_name' => true,
|
||||
],
|
||||
]);
|
||||
|
||||
$data = @file_get_contents($imageUrl, false, $context, 0, 262144); //256KB
|
||||
|
||||
if (empty($data)) {
|
||||
throw new \Exception(trans('admin/egg.import.invalid_url'));
|
||||
}
|
||||
|
||||
$normalizedExtension = match ($extension) {
|
||||
'svg+xml' => 'svg',
|
||||
'jpeg' => 'jpg',
|
||||
default => $extension,
|
||||
};
|
||||
|
||||
Storage::disk('public')->put(Server::ICON_STORAGE_PATH . "/$server->uuid.$normalizedExtension", $data);
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return trans('server/setting.title');
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
namespace App\Filament\Server\Pages;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Facades\Activity;
|
||||
use App\Filament\Components\Actions\PreviewStartupAction;
|
||||
use App\Filament\Components\Forms\Fields\StartupVariable;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServerVariable;
|
||||
use Exception;
|
||||
@@ -51,7 +51,7 @@ class Startup extends ServerFormPage
|
||||
->label(trans('server/startup.command'))
|
||||
->live()
|
||||
->visible(fn (Server $server) => in_array($server->startup, $server->egg->startup_commands))
|
||||
->disabled(fn (Server $server) => !user()?->can(Permission::ACTION_STARTUP_UPDATE, $server))
|
||||
->disabled(fn (Server $server) => !user()?->can(SubuserPermission::StartupUpdate, $server))
|
||||
->formatStateUsing(fn (Server $server) => $server->startup)
|
||||
->afterStateUpdated(function ($state, Server $server, Set $set) {
|
||||
$original = $server->startup;
|
||||
@@ -85,7 +85,7 @@ class Startup extends ServerFormPage
|
||||
->label(trans('server/startup.docker_image'))
|
||||
->live()
|
||||
->visible(fn (Server $server) => in_array($server->image, $server->egg->docker_images))
|
||||
->disabled(fn (Server $server) => !user()?->can(Permission::ACTION_STARTUP_DOCKER_IMAGE, $server))
|
||||
->disabled(fn (Server $server) => !user()?->can(SubuserPermission::StartupDockerImage, $server))
|
||||
->afterStateUpdated(function ($state, Server $server) {
|
||||
$original = $server->image;
|
||||
$server->forceFill(['image' => $state])->saveOrFail();
|
||||
@@ -123,7 +123,7 @@ class Startup extends ServerFormPage
|
||||
return $query->where('egg_variables.user_viewable', true)->orderByPowerJoins('variable.sort');
|
||||
})
|
||||
->grid()
|
||||
->disabled(fn (Server $server) => !user()?->can(Permission::ACTION_STARTUP_UPDATE, $server))
|
||||
->disabled(fn (Server $server) => !user()?->can(SubuserPermission::StartupUpdate, $server))
|
||||
->reorderable(false)->addable(false)->deletable(false)
|
||||
->schema([
|
||||
StartupVariable::make('variable_value')
|
||||
@@ -139,12 +139,12 @@ class Startup extends ServerFormPage
|
||||
|
||||
protected function authorizeAccess(): void
|
||||
{
|
||||
abort_unless(user()?->can(Permission::ACTION_STARTUP_READ, Filament::getTenant()), 403);
|
||||
abort_unless(user()?->can(SubuserPermission::StartupRead, Filament::getTenant()), 403);
|
||||
}
|
||||
|
||||
public static function canAccess(): bool
|
||||
{
|
||||
return parent::canAccess() && user()?->can(Permission::ACTION_STARTUP_READ, Filament::getTenant());
|
||||
return parent::canAccess() && user()?->can(SubuserPermission::StartupRead, Filament::getTenant());
|
||||
}
|
||||
|
||||
public function update(?string $state, ServerVariable $serverVariable): null
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
namespace App\Filament\Server\Resources\Allocations;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Facades\Activity;
|
||||
use App\Filament\Server\Resources\Allocations\Pages\ListAllocations;
|
||||
use App\Models\Allocation;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Server;
|
||||
use App\Services\Allocations\FindAssignableAllocationService;
|
||||
use App\Traits\Filament\BlockAccessInConflict;
|
||||
@@ -57,7 +57,7 @@ class AllocationResource extends Resource
|
||||
TextInputColumn::make('notes')
|
||||
->label(trans('server/network.notes'))
|
||||
->visibleFrom('sm')
|
||||
->disabled(fn () => !user()?->can(Permission::ACTION_ALLOCATION_UPDATE, $server))
|
||||
->disabled(fn () => !user()?->can(SubuserPermission::AllocationUpdate, $server))
|
||||
->placeholder(trans('server/network.no_notes')),
|
||||
IconColumn::make('primary')
|
||||
->icon(fn ($state) => match ($state) {
|
||||
@@ -69,7 +69,7 @@ class AllocationResource extends Resource
|
||||
default => 'gray',
|
||||
})
|
||||
->tooltip(fn (Allocation $allocation) => $allocation->id === $server->allocation_id ? trans('server/network.primary') : trans('server/network.make_primary'))
|
||||
->action(fn (Allocation $allocation) => user()?->can(PERMISSION::ACTION_ALLOCATION_UPDATE, $server) && $server->update(['allocation_id' => $allocation->id]))
|
||||
->action(fn (Allocation $allocation) => user()?->can(SubuserPermission::AllocationUpdate, $server) && $server->update(['allocation_id' => $allocation->id]))
|
||||
->default(fn (Allocation $allocation) => $allocation->id === $server->allocation_id)
|
||||
->label(trans('server/network.primary')),
|
||||
IconColumn::make('is_locked')
|
||||
@@ -81,7 +81,7 @@ class AllocationResource extends Resource
|
||||
->recordActions([
|
||||
DetachAction::make()
|
||||
->visible(fn (Allocation $allocation) => !$allocation->is_locked || user()?->can('update', $allocation->node))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_ALLOCATION_DELETE, $server))
|
||||
->authorize(fn () => user()?->can(SubuserPermission::AllocationDelete, $server))
|
||||
->label(trans('server/network.delete'))
|
||||
->action(function (Allocation $allocation) {
|
||||
Allocation::where('id', $allocation->id)->update([
|
||||
@@ -101,7 +101,7 @@ class AllocationResource extends Resource
|
||||
Action::make('add_allocation')
|
||||
->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge)
|
||||
->icon(fn () => $server->allocations()->count() >= $server->allocation_limit ? 'tabler-network-off' : 'tabler-network')
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_ALLOCATION_CREATE, $server))
|
||||
->authorize(fn () => user()?->can(SubuserPermission::AllocationCreate, $server))
|
||||
->tooltip(fn () => $server->allocations()->count() >= $server->allocation_limit ? trans('server/network.limit') : trans('server/network.add'))
|
||||
->hidden(fn () => !config('panel.client_features.allocations.enabled') || $server->allocation === null)
|
||||
->disabled(fn () => $server->allocations()->count() >= $server->allocation_limit)
|
||||
|
||||
@@ -4,13 +4,13 @@ namespace App\Filament\Server\Resources\Backups;
|
||||
|
||||
use App\Enums\BackupStatus;
|
||||
use App\Enums\ServerState;
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Facades\Activity;
|
||||
use App\Filament\Components\Tables\Columns\BytesColumn;
|
||||
use App\Filament\Components\Tables\Columns\DateTimeColumn;
|
||||
use App\Filament\Server\Resources\Backups\Pages\ListBackups;
|
||||
use App\Http\Controllers\Api\Client\Servers\BackupController;
|
||||
use App\Models\Backup;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Server;
|
||||
use App\Repositories\Daemon\DaemonBackupRepository;
|
||||
use App\Services\Backups\DeleteBackupService;
|
||||
@@ -128,7 +128,7 @@ class BackupResource extends Resource
|
||||
ActionGroup::make([
|
||||
Action::make('rename')
|
||||
->icon('tabler-pencil')
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_BACKUP_DELETE, $server))
|
||||
->authorize(fn () => user()?->can(SubuserPermission::BackupDelete, $server))
|
||||
->label(trans('server/backup.actions.rename.title'))
|
||||
->schema([
|
||||
TextInput::make('name')
|
||||
@@ -159,7 +159,7 @@ class BackupResource extends Resource
|
||||
Action::make('lock')
|
||||
->iconSize(IconSize::Large)
|
||||
->icon(fn (Backup $backup) => !$backup->is_locked ? 'tabler-lock' : 'tabler-lock-open')
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_BACKUP_DELETE, $server))
|
||||
->authorize(fn () => user()?->can(SubuserPermission::BackupDelete, $server))
|
||||
->label(fn (Backup $backup) => !$backup->is_locked ? trans('server/backup.actions.lock.lock') : trans('server/backup.actions.lock.unlock'))
|
||||
->action(fn (BackupController $backupController, Backup $backup, Request $request) => $backupController->toggleLock($request, $server, $backup))
|
||||
->visible(fn (Backup $backup) => $backup->status === BackupStatus::Successful),
|
||||
@@ -168,7 +168,7 @@ class BackupResource extends Resource
|
||||
->iconSize(IconSize::Large)
|
||||
->color('primary')
|
||||
->icon('tabler-download')
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_BACKUP_DOWNLOAD, $server))
|
||||
->authorize(fn () => user()?->can(SubuserPermission::BackupDownload, $server))
|
||||
->url(fn (DownloadLinkService $downloadLinkService, Backup $backup, Request $request) => $downloadLinkService->handle($backup, $request->user()), true)
|
||||
->visible(fn (Backup $backup) => $backup->status === BackupStatus::Successful),
|
||||
Action::make('restore')
|
||||
@@ -176,7 +176,7 @@ class BackupResource extends Resource
|
||||
->iconSize(IconSize::Large)
|
||||
->color('success')
|
||||
->icon('tabler-folder-up')
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_BACKUP_RESTORE, $server))
|
||||
->authorize(fn () => user()?->can(SubuserPermission::BackupRestore, $server))
|
||||
->schema([
|
||||
TextEntry::make('stop_info')
|
||||
->hiddenLabel()
|
||||
@@ -258,7 +258,7 @@ class BackupResource extends Resource
|
||||
])
|
||||
->toolbarActions([
|
||||
CreateAction::make()
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_BACKUP_CREATE, $server))
|
||||
->authorize(fn () => user()?->can(SubuserPermission::BackupCreate, $server))
|
||||
->icon('tabler-file-zip')
|
||||
->tooltip(fn () => $server->backups()->count() >= $server->backup_limit ? trans('server/backup.actions.create.limit') : trans('server/backup.actions.create.title'))
|
||||
->disabled(fn () => $server->backups()->count() >= $server->backup_limit)
|
||||
@@ -269,7 +269,7 @@ class BackupResource extends Resource
|
||||
->action(function (InitiateBackupService $initiateBackupService, $data) use ($server) {
|
||||
$action = $initiateBackupService->setIgnoredFiles(explode(PHP_EOL, $data['ignored'] ?? ''));
|
||||
|
||||
if (user()?->can(Permission::ACTION_BACKUP_DELETE, $server)) {
|
||||
if (user()?->can(SubuserPermission::BackupDelete, $server)) {
|
||||
$action->setIsLocked((bool) $data['is_locked']);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
namespace App\Filament\Server\Resources\Databases;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Filament\Components\Actions\RotateDatabasePasswordAction;
|
||||
use App\Filament\Components\Tables\Columns\DateTimeColumn;
|
||||
use App\Filament\Server\Resources\Databases\Pages\ListDatabases;
|
||||
use App\Models\Database;
|
||||
use App\Models\DatabaseHost;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Server;
|
||||
use App\Services\Databases\DatabaseManagementService;
|
||||
use App\Traits\Filament\BlockAccessInConflict;
|
||||
@@ -87,10 +87,10 @@ class DatabaseResource extends Resource
|
||||
TextInput::make('password')
|
||||
->label(trans('server/database.password'))
|
||||
->password()->revealable()
|
||||
->hidden(fn () => !user()?->can(Permission::ACTION_DATABASE_VIEW_PASSWORD, $server))
|
||||
->hidden(fn () => !user()?->can(SubuserPermission::DatabaseViewPassword, $server))
|
||||
->hintAction(
|
||||
RotateDatabasePasswordAction::make()
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_DATABASE_UPDATE, $server))
|
||||
->authorize(fn () => user()?->can(SubuserPermission::DatabaseUpdate, $server))
|
||||
)
|
||||
->copyable()
|
||||
->formatStateUsing(fn (Database $database) => $database->password),
|
||||
@@ -102,7 +102,7 @@ class DatabaseResource extends Resource
|
||||
TextInput::make('jdbc')
|
||||
->label(trans('server/database.jdbc'))
|
||||
->password()->revealable()
|
||||
->hidden(!user()?->can(Permission::ACTION_DATABASE_VIEW_PASSWORD, $server))
|
||||
->hidden(!user()?->can(SubuserPermission::DatabaseViewPassword, $server))
|
||||
->copyable()
|
||||
->columnSpanFull()
|
||||
->formatStateUsing(fn (Database $database) => $database->jdbc),
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
namespace App\Filament\Server\Resources\Files\Pages;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Facades\Activity;
|
||||
use App\Filament\Server\Resources\Files\FileResource;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Server;
|
||||
use App\Services\Nodes\NodeJWTService;
|
||||
use Carbon\CarbonImmutable;
|
||||
@@ -55,7 +55,7 @@ class DownloadFiles extends Page
|
||||
|
||||
protected function authorizeAccess(): void
|
||||
{
|
||||
abort_unless(user()?->can(Permission::ACTION_FILE_READ_CONTENT, Filament::getTenant()), 403);
|
||||
abort_unless(user()?->can(SubuserPermission::FileReadContent, Filament::getTenant()), 403);
|
||||
}
|
||||
|
||||
public static function route(string $path): PageRegistration
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
|
||||
namespace App\Filament\Server\Resources\Files\Pages;
|
||||
|
||||
use App\Enums\EditorLanguages;
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Exceptions\Http\Server\FileSizeTooLargeException;
|
||||
use App\Exceptions\Repository\FileNotEditableException;
|
||||
use App\Facades\Activity;
|
||||
use App\Filament\Components\Forms\Fields\MonacoEditor;
|
||||
use App\Filament\Server\Resources\Files\FileResource;
|
||||
use App\Livewire\AlertBanner;
|
||||
use App\Models\File;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Server;
|
||||
use App\Repositories\Daemon\DaemonFileRepository;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
@@ -16,8 +18,6 @@ use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Closure;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Forms\Components\CodeEditor;
|
||||
use Filament\Forms\Components\CodeEditor\Enums\Language;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Concerns\InteractsWithForms;
|
||||
use Filament\Notifications\Notification;
|
||||
@@ -83,7 +83,7 @@ class EditFiles extends Page
|
||||
->footerActions([
|
||||
Action::make('save_and_close')
|
||||
->label(trans('server/file.actions.edit.save_close'))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_UPDATE, $server))
|
||||
->authorize(fn () => user()?->can(SubuserPermission::FileUpdate, $server))
|
||||
->icon('tabler-device-floppy')
|
||||
->keyBindings('mod+shift+s')
|
||||
->action(function () {
|
||||
@@ -103,7 +103,7 @@ class EditFiles extends Page
|
||||
}),
|
||||
Action::make('save')
|
||||
->label(trans('server/file.actions.edit.save'))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_UPDATE, $server))
|
||||
->authorize(fn () => user()?->can(SubuserPermission::FileUpdate, $server))
|
||||
->icon('tabler-device-floppy')
|
||||
->keyBindings('mod+s')
|
||||
->action(function () {
|
||||
@@ -137,43 +137,18 @@ class EditFiles extends Page
|
||||
->label(trans('server/file.actions.new_file.syntax'))
|
||||
->searchable()
|
||||
->live()
|
||||
->options(Language::class)
|
||||
->options(EditorLanguages::class)
|
||||
->selectablePlaceholder(false)
|
||||
->default(fn () => match (pathinfo($this->path, PATHINFO_EXTENSION)) {
|
||||
'cc', 'hpp' => Language::Cpp,
|
||||
|
||||
'css', 'scss' => Language::Css,
|
||||
|
||||
'go' => Language::Go,
|
||||
|
||||
'html' => Language::Html,
|
||||
|
||||
'class', 'kt', 'kts' => Language::Java,
|
||||
|
||||
'js', 'mjs', 'cjs', 'ts', 'tsx' => Language::JavaScript,
|
||||
|
||||
'json', 'json5' => Language::Json,
|
||||
|
||||
'md' => Language::Markdown,
|
||||
|
||||
'php3', 'php4', 'php5', 'phtml', 'php' => Language::Php,
|
||||
|
||||
'py', 'pyc', 'pyo', 'pyi' => Language::Python,
|
||||
|
||||
'xml' => Language::Xml,
|
||||
|
||||
'yml', 'yaml' => Language::Yaml,
|
||||
|
||||
default => null,
|
||||
}),
|
||||
CodeEditor::make('editor')
|
||||
->afterStateUpdated(fn ($state) => $this->dispatch('setLanguage', lang: $state))
|
||||
->default(fn () => EditorLanguages::fromWithAlias(pathinfo($this->path, PATHINFO_EXTENSION))),
|
||||
MonacoEditor::make('editor')
|
||||
->hiddenLabel()
|
||||
->language(fn (Get $get) => $get('lang'))
|
||||
->default(function () {
|
||||
try {
|
||||
$contents = $this->getDaemonFileRepository()->getContent($this->path, config('panel.files.max_edit_size'));
|
||||
|
||||
return mb_convert_encoding($contents, 'UTF-8', ['UTF-8', 'UTF-16', 'ISO-8859-1', 'Windows-1252', 'ASCII']);
|
||||
return mb_convert_encoding($contents, 'UTF-8', ['UTF-8', 'UTF-16', 'ISO-8859-1', 'ASCII']);
|
||||
} catch (FileSizeTooLargeException) {
|
||||
AlertBanner::make('file_too_large')
|
||||
->title(trans('server/file.alerts.file_too_large.title', ['name' => basename($this->path)]))
|
||||
@@ -196,6 +171,7 @@ class EditFiles extends Page
|
||||
} catch (ConnectionException) {
|
||||
// Alert banner for this one will be handled by ListFiles
|
||||
}
|
||||
|
||||
$this->redirectToList();
|
||||
}),
|
||||
])
|
||||
@@ -233,7 +209,7 @@ class EditFiles extends Page
|
||||
|
||||
protected function authorizeAccess(): void
|
||||
{
|
||||
abort_unless(user()?->can(Permission::ACTION_FILE_READ_CONTENT, Filament::getTenant()), 403);
|
||||
abort_unless(user()?->can(SubuserPermission::FileReadContent, Filament::getTenant()), 403);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,14 +2,16 @@
|
||||
|
||||
namespace App\Filament\Server\Resources\Files\Pages;
|
||||
|
||||
use App\Enums\EditorLanguages;
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Exceptions\Repository\FileExistsException;
|
||||
use App\Facades\Activity;
|
||||
use App\Filament\Components\Forms\Fields\MonacoEditor;
|
||||
use App\Filament\Components\Tables\Columns\BytesColumn;
|
||||
use App\Filament\Components\Tables\Columns\DateTimeColumn;
|
||||
use App\Filament\Server\Resources\Files\FileResource;
|
||||
use App\Livewire\AlertBanner;
|
||||
use App\Models\File;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Server;
|
||||
use App\Repositories\Daemon\DaemonFileRepository;
|
||||
use App\Services\Nodes\NodeJWTService;
|
||||
@@ -26,7 +28,6 @@ use Filament\Actions\DeleteBulkAction;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Forms\Components\CheckboxList;
|
||||
use Filament\Forms\Components\CodeEditor;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Infolists\Components\TextEntry;
|
||||
@@ -122,7 +123,7 @@ class ListFiles extends ListRecords
|
||||
return self::getUrl(['path' => encode_path(join_paths($this->path, $file->name))]);
|
||||
}
|
||||
|
||||
if (!user()?->can(Permission::ACTION_FILE_READ_CONTENT, $server)) {
|
||||
if (!user()?->can(SubuserPermission::FileReadContent, $server)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -130,18 +131,18 @@ class ListFiles extends ListRecords
|
||||
})
|
||||
->recordActions([
|
||||
Action::make('view')
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_READ, $server))
|
||||
->authorize(fn () => user()?->can(SubuserPermission::FileRead, $server))
|
||||
->label(trans('server/file.actions.open'))
|
||||
->icon('tabler-eye')->iconSize(IconSize::Large)
|
||||
->visible(fn (File $file) => $file->is_directory)
|
||||
->url(fn (File $file) => self::getUrl(['path' => encode_path(join_paths($this->path, $file->name))])),
|
||||
EditAction::make('edit')
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_READ_CONTENT, $server))
|
||||
->authorize(fn () => user()?->can(SubuserPermission::FileReadContent, $server))
|
||||
->visible(fn (File $file) => $file->canEdit())
|
||||
->url(fn (File $file) => EditFiles::getUrl(['path' => encode_path(join_paths($this->path, $file->name))])),
|
||||
ActionGroup::make([
|
||||
Action::make('rename')
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_UPDATE, $server))
|
||||
->authorize(fn () => user()?->can(SubuserPermission::FileUpdate, $server))
|
||||
->label(trans('server/file.actions.rename.title'))
|
||||
->icon('tabler-forms')->iconSize(IconSize::Large)
|
||||
->schema([
|
||||
@@ -171,7 +172,7 @@ class ListFiles extends ListRecords
|
||||
$this->refreshPage();
|
||||
}),
|
||||
Action::make('copy')
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_CREATE, $server))
|
||||
->authorize(fn () => user()?->can(SubuserPermission::FileCreate, $server))
|
||||
->label(trans('server/file.actions.copy.title'))
|
||||
->icon('tabler-copy')->iconSize(IconSize::Large)
|
||||
->visible(fn (File $file) => $file->is_file)
|
||||
@@ -190,13 +191,13 @@ class ListFiles extends ListRecords
|
||||
$this->refreshPage();
|
||||
}),
|
||||
Action::make('download')
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_READ_CONTENT, $server))
|
||||
->authorize(fn () => user()?->can(SubuserPermission::FileReadContent, $server))
|
||||
->label(trans('server/file.actions.download'))
|
||||
->icon('tabler-download')->iconSize(IconSize::Large)
|
||||
->visible(fn (File $file) => $file->is_file)
|
||||
->url(fn (File $file) => DownloadFiles::getUrl(['path' => encode_path(join_paths($this->path, $file->name))]), true),
|
||||
Action::make('move')
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_UPDATE, $server))
|
||||
->authorize(fn () => user()?->can(SubuserPermission::FileUpdate, $server))
|
||||
->label(trans('server/file.actions.move.title'))
|
||||
->icon('tabler-replace')->iconSize(IconSize::Large)
|
||||
->schema([
|
||||
@@ -233,7 +234,7 @@ class ListFiles extends ListRecords
|
||||
$this->refreshPage();
|
||||
}),
|
||||
Action::make('permissions')
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_UPDATE, $server))
|
||||
->authorize(fn () => user()?->can(SubuserPermission::FileUpdate, $server))
|
||||
->label(trans('server/file.actions.permissions.title'))
|
||||
->icon('tabler-license')->iconSize(IconSize::Large)
|
||||
->schema([
|
||||
@@ -295,7 +296,7 @@ class ListFiles extends ListRecords
|
||||
->send();
|
||||
}),
|
||||
Action::make('archive')
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_ARCHIVE, $server))
|
||||
->authorize(fn () => user()?->can(SubuserPermission::FileArchive, $server))
|
||||
->label(trans('server/file.actions.archive.title'))
|
||||
->icon('tabler-archive')->iconSize(IconSize::Large)
|
||||
->schema([
|
||||
@@ -335,7 +336,7 @@ class ListFiles extends ListRecords
|
||||
$this->refreshPage();
|
||||
}),
|
||||
Action::make('unarchive')
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_ARCHIVE, $server))
|
||||
->authorize(fn () => user()?->can(SubuserPermission::FileArchive, $server))
|
||||
->label(trans('server/file.actions.unarchive.title'))
|
||||
->icon('tabler-archive')->iconSize(IconSize::Large)
|
||||
->visible(fn (File $file) => $file->isArchive())
|
||||
@@ -356,7 +357,7 @@ class ListFiles extends ListRecords
|
||||
}),
|
||||
])->iconSize(IconSize::Large),
|
||||
DeleteAction::make()
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_DELETE, $server))
|
||||
->authorize(fn () => user()?->can(SubuserPermission::FileDelete, $server))
|
||||
->hiddenLabel()
|
||||
->iconSize(IconSize::Large)
|
||||
->requiresConfirmation()
|
||||
@@ -376,7 +377,7 @@ class ListFiles extends ListRecords
|
||||
->toolbarActions([
|
||||
BulkActionGroup::make([
|
||||
BulkAction::make('move')
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_UPDATE, $server))
|
||||
->authorize(fn () => user()?->can(SubuserPermission::FileUpdate, $server))
|
||||
->schema([
|
||||
TextInput::make('location')
|
||||
->label(trans('server/file.actions.move.directory'))
|
||||
@@ -405,7 +406,7 @@ class ListFiles extends ListRecords
|
||||
$this->refreshPage();
|
||||
}),
|
||||
BulkAction::make('archive')
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_ARCHIVE, $server))
|
||||
->authorize(fn () => user()?->can(SubuserPermission::FileArchive, $server))
|
||||
->schema([
|
||||
Grid::make(3)
|
||||
->schema([
|
||||
@@ -446,7 +447,7 @@ class ListFiles extends ListRecords
|
||||
}),
|
||||
DeleteBulkAction::make()
|
||||
->successNotificationTitle(null)
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_DELETE, $server))
|
||||
->authorize(fn () => user()?->can(SubuserPermission::FileDelete, $server))
|
||||
->action(function (Collection $files) {
|
||||
$files = $files->map(fn ($file) => $file['name'])->toArray();
|
||||
$this->getDaemonFileRepository()->deleteFiles($this->path, $files);
|
||||
@@ -466,7 +467,7 @@ class ListFiles extends ListRecords
|
||||
]),
|
||||
|
||||
Action::make('new_file')
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_CREATE, $server))
|
||||
->authorize(fn () => user()?->can(SubuserPermission::FileCreate, $server))
|
||||
->tooltip(trans('server/file.actions.new_file.title'))
|
||||
->hiddenLabel()->icon('tabler-file-plus')->iconButton()->iconSize(IconSize::ExtraLarge)
|
||||
->color('primary')
|
||||
@@ -495,11 +496,20 @@ class ListFiles extends ListRecords
|
||||
TextInput::make('name')
|
||||
->label(trans('server/file.actions.new_file.file_name'))
|
||||
->required(),
|
||||
CodeEditor::make('editor')
|
||||
->hiddenLabel(),
|
||||
Select::make('lang')
|
||||
->label(trans('server/file.actions.new_file.syntax'))
|
||||
->searchable()
|
||||
->live()
|
||||
->options(EditorLanguages::class)
|
||||
->selectablePlaceholder(false)
|
||||
->afterStateUpdated(fn ($state) => $this->dispatch('setLanguage', lang: $state))
|
||||
->default(EditorLanguages::plaintext->value),
|
||||
MonacoEditor::make('editor')
|
||||
->hiddenLabel()
|
||||
->language(fn (Get $get) => $get('lang') ?? 'plaintext'),
|
||||
]),
|
||||
Action::make('new_folder')
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_CREATE, $server))
|
||||
->authorize(fn () => user()?->can(SubuserPermission::FileCreate, $server))
|
||||
->hiddenLabel()->icon('tabler-folder-plus')->iconButton()->iconSize(IconSize::ExtraLarge)
|
||||
->tooltip(trans('server/file.actions.new_folder.title'))
|
||||
->color('primary')
|
||||
@@ -530,10 +540,10 @@ class ListFiles extends ListRecords
|
||||
->required(),
|
||||
]),
|
||||
Action::make('uploadFile')
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_CREATE, $server))
|
||||
->authorize(fn () => user()?->can(SubuserPermission::FileCreate, $server))
|
||||
->view('filament.server.pages.file-upload'),
|
||||
Action::make('uploadURL')
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_CREATE, $server))
|
||||
->authorize(fn () => user()?->can(SubuserPermission::FileCreate, $server))
|
||||
->hiddenLabel()->icon('tabler-download')->iconButton()->iconSize(IconSize::ExtraLarge)
|
||||
->tooltip(trans('server/file.actions.upload.from_url'))
|
||||
->modalHeading(trans('server/file.actions.upload.from_url'))
|
||||
@@ -555,17 +565,17 @@ class ListFiles extends ListRecords
|
||||
->url(),
|
||||
]),
|
||||
Action::make('search')
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_READ, $server))
|
||||
->authorize(fn () => user()?->can(SubuserPermission::FileRead, $server))
|
||||
->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge)
|
||||
->tooltip(trans('server/file.actions.global_search.title'))
|
||||
->tooltip(trans('server/file.actions.nested_search.title'))
|
||||
->color('primary')
|
||||
->icon('tabler-world-search')
|
||||
->modalHeading(trans('server/file.actions.global_search.title'))
|
||||
->modalSubmitActionLabel(trans('server/file.actions.global_search.search'))
|
||||
->modalHeading(trans('server/file.actions.nested_search.title'))
|
||||
->modalSubmitActionLabel(trans('server/file.actions.nested_search.search'))
|
||||
->schema([
|
||||
TextInput::make('searchTerm')
|
||||
->label(trans('server/file.actions.global_search.search_term'))
|
||||
->placeholder(trans('server/file.actions.global_search.search_term_placeholder'))
|
||||
->label(trans('server/file.actions.nested_search.search_term'))
|
||||
->placeholder(trans('server/file.actions.nested_search.search_term_placeholder'))
|
||||
->required()
|
||||
->regex('/^[^*]*\*?[^*]*$/')
|
||||
->minValue(3),
|
||||
@@ -605,7 +615,7 @@ class ListFiles extends ListRecords
|
||||
/** @var Server $server */
|
||||
$server = Filament::getTenant();
|
||||
|
||||
if (!user()?->can(Permission::ACTION_FILE_CREATE, $server)) {
|
||||
if (!user()?->can(SubuserPermission::FileCreate, $server)) {
|
||||
abort(403, 'You do not have permission to upload files.');
|
||||
}
|
||||
|
||||
@@ -640,7 +650,7 @@ class ListFiles extends ListRecords
|
||||
/** @var Server $server */
|
||||
$server = Filament::getTenant();
|
||||
|
||||
if (!user()?->can(Permission::ACTION_FILE_CREATE, $server)) {
|
||||
if (!user()?->can(SubuserPermission::FileCreate, $server)) {
|
||||
abort(403, 'You do not have permission to create folders.');
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ class SearchFiles extends ListRecords
|
||||
|
||||
return [
|
||||
$resource::getUrl() => $resource::getBreadcrumb(),
|
||||
self::getUrl(['searchTerm' => $this->searchTerm]) => trans('server/file.actions.global_search.search_for_term', ['term' => ' "' . $this->searchTerm . '"']),
|
||||
self::getUrl(['searchTerm' => $this->searchTerm]) => trans('server/file.actions.nested_search.search_for_term', ['term' => ' "' . $this->searchTerm . '"']),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -67,15 +67,15 @@ class SearchFiles extends ListRecords
|
||||
])
|
||||
->recordUrl(function (File $file) {
|
||||
if ($file->is_directory) {
|
||||
return ListFiles::getUrl(['path' => join_paths($this->path, $file->name)]);
|
||||
return ListFiles::getUrl(['path' => $file->name]);
|
||||
}
|
||||
|
||||
return $file->canEdit() ? EditFiles::getUrl(['path' => join_paths($this->path, $file->name)]) : null;
|
||||
return $file->canEdit() ? EditFiles::getUrl(['path' => $file->name]) : null;
|
||||
});
|
||||
}
|
||||
|
||||
public function getTitle(): string|Htmlable
|
||||
{
|
||||
return trans('server/file.actions.global_search.title');
|
||||
return trans('server/file.actions.nested_search.title');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
|
||||
namespace App\Filament\Server\Resources\Schedules\Pages;
|
||||
|
||||
use App\Enums\ScheduleStatus;
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Facades\Activity;
|
||||
use App\Filament\Components\Actions\ExportScheduleAction;
|
||||
use App\Filament\Server\Resources\Schedules\ScheduleResource;
|
||||
use App\Models\Schedule;
|
||||
use App\Services\Schedules\ProcessScheduleService;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Actions\DeleteAction;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
use Filament\Support\Enums\IconSize;
|
||||
|
||||
@@ -49,22 +53,36 @@ class EditSchedule extends EditRecord
|
||||
{
|
||||
return [
|
||||
DeleteAction::make()
|
||||
->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge)
|
||||
->hiddenLabel()
|
||||
->iconButton()->iconSize(IconSize::ExtraLarge)
|
||||
->tooltip(trans('server/schedule.delete'))
|
||||
->after(function ($record) {
|
||||
Activity::event('server:schedule.delete')
|
||||
->property('name', $record->name)
|
||||
->log();
|
||||
}),
|
||||
Action::make('run_now')
|
||||
->iconButton()->iconSize(IconSize::ExtraLarge)
|
||||
->icon('tabler-run')
|
||||
->authorize(fn () => user()?->can(SubuserPermission::ScheduleUpdate, Filament::getTenant()))
|
||||
->tooltip(fn (Schedule $schedule) => $schedule->tasks->count() === 0 ? trans('server/schedule.no_tasks') : ($schedule->status === ScheduleStatus::Processing ? ScheduleStatus::Processing->getLabel() : trans('server/schedule.run_now')))
|
||||
->color(fn (Schedule $schedule) => $schedule->tasks->count() === 0 || $schedule->status === ScheduleStatus::Processing ? 'warning' : 'primary')
|
||||
->disabled(fn (Schedule $schedule) => $schedule->tasks->count() === 0 || $schedule->status === ScheduleStatus::Processing)
|
||||
->action(function (ProcessScheduleService $service, Schedule $schedule) {
|
||||
$service->handle($schedule, true);
|
||||
|
||||
Activity::event('server:schedule.execute')
|
||||
->subject($schedule)
|
||||
->property('name', $schedule->name)
|
||||
->log();
|
||||
|
||||
$this->fillForm();
|
||||
}),
|
||||
ExportScheduleAction::make(),
|
||||
$this->getSaveFormAction()->formId('form')
|
||||
->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge)
|
||||
->icon('tabler-device-floppy')
|
||||
->tooltip(trans('server/schedule.save')),
|
||||
$this->getCancelFormAction()->formId('form')
|
||||
->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge)
|
||||
->icon('tabler-arrow-left')
|
||||
->tooltip(trans('server/schedule.cancel')),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -2,18 +2,12 @@
|
||||
|
||||
namespace App\Filament\Server\Resources\Schedules\Pages;
|
||||
|
||||
use App\Enums\ScheduleStatus;
|
||||
use App\Facades\Activity;
|
||||
use App\Filament\Server\Resources\Schedules\ScheduleResource;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Schedule;
|
||||
use App\Services\Schedules\ProcessScheduleService;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Resources\Pages\ViewRecord;
|
||||
use Filament\Support\Enums\IconSize;
|
||||
|
||||
@@ -28,21 +22,6 @@ class ViewSchedule extends ViewRecord
|
||||
protected function getDefaultHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Action::make('run_now')
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_SCHEDULE_UPDATE, Filament::getTenant()))
|
||||
->label(fn (Schedule $schedule) => $schedule->tasks->count() === 0 ? trans('server/schedule.no_tasks') : ($schedule->status === ScheduleStatus::Processing ? ScheduleStatus::Processing->getLabel() : trans('server/schedule.run_now')))
|
||||
->color(fn (Schedule $schedule) => $schedule->tasks->count() === 0 || $schedule->status === ScheduleStatus::Processing ? 'warning' : 'primary')
|
||||
->disabled(fn (Schedule $schedule) => $schedule->tasks->count() === 0 || $schedule->status === ScheduleStatus::Processing)
|
||||
->action(function (ProcessScheduleService $service, Schedule $schedule) {
|
||||
$service->handle($schedule, true);
|
||||
|
||||
Activity::event('server:schedule.execute')
|
||||
->subject($schedule)
|
||||
->property('name', $schedule->name)
|
||||
->log();
|
||||
|
||||
$this->fillForm();
|
||||
}),
|
||||
EditAction::make()
|
||||
->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge)
|
||||
->icon('tabler-calendar-code')
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Server\Resources\Users\Pages;
|
||||
namespace App\Filament\Server\Resources\Subusers\Pages;
|
||||
|
||||
use App\Filament\Server\Resources\Users\UserResource;
|
||||
use App\Filament\Server\Resources\Subusers\SubuserResource;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Actions\Action;
|
||||
@@ -10,12 +10,12 @@ use Filament\Actions\ActionGroup;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
use Illuminate\Contracts\Support\Htmlable;
|
||||
|
||||
class ListUsers extends ListRecords
|
||||
class ListSubusers extends ListRecords
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = UserResource::class;
|
||||
protected static string $resource = SubuserResource::class;
|
||||
|
||||
/** @return array<Action|ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
@@ -1,12 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Server\Resources\Users;
|
||||
namespace App\Filament\Server\Resources\Subusers;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Facades\Activity;
|
||||
use App\Filament\Server\Resources\Users\Pages\ListUsers;
|
||||
use App\Models\Permission;
|
||||
use App\Filament\Server\Resources\Subusers\Pages\ListSubusers;
|
||||
use App\Models\Server;
|
||||
use App\Models\User;
|
||||
use App\Models\Subuser;
|
||||
use App\Services\Subusers\SubuserCreationService;
|
||||
use App\Services\Subusers\SubuserDeletionService;
|
||||
use App\Services\Subusers\SubuserUpdateService;
|
||||
@@ -38,7 +38,7 @@ use Filament\Tables\Columns\ImageColumn;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
class UserResource extends Resource
|
||||
class SubuserResource extends Resource
|
||||
{
|
||||
use BlockAccessInConflict;
|
||||
use CanCustomizePages;
|
||||
@@ -46,14 +46,12 @@ class UserResource extends Resource
|
||||
use CanModifyTable;
|
||||
use HasLimitBadge;
|
||||
|
||||
protected static ?string $model = User::class;
|
||||
protected static ?string $model = Subuser::class;
|
||||
|
||||
protected static ?int $navigationSort = 5;
|
||||
|
||||
protected static string|\BackedEnum|null $navigationIcon = 'tabler-users';
|
||||
|
||||
protected static ?string $tenantOwnershipRelationshipName = 'subServers';
|
||||
|
||||
protected static function getBadgeCount(): int
|
||||
{
|
||||
/** @var Server $server */
|
||||
@@ -70,7 +68,11 @@ class UserResource extends Resource
|
||||
$tabs = [];
|
||||
$permissionsArray = [];
|
||||
|
||||
foreach (Permission::permissionData() as $data) {
|
||||
foreach (Subuser::allPermissionData() as $data) {
|
||||
if ($data['hidden']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$options = [];
|
||||
$descriptions = [];
|
||||
|
||||
@@ -86,6 +88,7 @@ class UserResource extends Resource
|
||||
Section::make()
|
||||
->description(trans('server/user.permissions.' . $data['name'] . '_desc'))
|
||||
->icon($data['icon'])
|
||||
->contained(false)
|
||||
->schema([
|
||||
CheckboxList::make($data['name'])
|
||||
->hiddenLabel()
|
||||
@@ -104,24 +107,26 @@ class UserResource extends Resource
|
||||
->visibleFrom('lg')
|
||||
->label('')
|
||||
->alignCenter()->circular()
|
||||
->defaultImageUrl(fn (User $user) => Filament::getUserAvatarUrl($user)),
|
||||
TextColumn::make('username')
|
||||
->defaultImageUrl(fn (Subuser $subuser) => Filament::getUserAvatarUrl($subuser->user)),
|
||||
TextColumn::make('user.username')
|
||||
->label(trans('server/user.username'))
|
||||
->searchable(),
|
||||
TextColumn::make('email')
|
||||
TextColumn::make('user.email')
|
||||
->label(trans('server/user.email'))
|
||||
->searchable(),
|
||||
TextColumn::make('permissions')
|
||||
TextColumn::make('permissions_count')
|
||||
->label(trans('server/user.permissions.title'))
|
||||
->state(fn (User $user) => count($server->subusers->where('user_id', $user->id)->first()->permissions)),
|
||||
->state(fn (Subuser $subuser) => collect($subuser->permissions)
|
||||
->reject(fn (string $permission) => SubuserPermission::tryFrom($permission)?->isHidden() ?? false)
|
||||
->count()
|
||||
),
|
||||
])
|
||||
->recordActions([
|
||||
DeleteAction::make()
|
||||
->label(trans('server/user.delete'))
|
||||
->hidden(fn (User $user) => user()?->id === $user->id)
|
||||
->hidden(fn (Subuser $subuser) => user()?->id === $subuser->user->id)
|
||||
->successNotificationTitle(null)
|
||||
->action(function (User $user, SubuserDeletionService $subuserDeletionService) use ($server) {
|
||||
$subuser = $server->subusers->where('user_id', $user->id)->first();
|
||||
->action(function (Subuser $subuser, SubuserDeletionService $subuserDeletionService) use ($server) {
|
||||
$subuserDeletionService->handle($subuser, $server);
|
||||
|
||||
Notification::make()
|
||||
@@ -131,17 +136,15 @@ class UserResource extends Resource
|
||||
}),
|
||||
EditAction::make()
|
||||
->label(trans('server/user.edit'))
|
||||
->hidden(fn (User $user) => user()?->id === $user->id)
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_USER_UPDATE, $server))
|
||||
->modalHeading(fn (User $user) => trans('server/user.editing', ['user' => $user->email]))
|
||||
->hidden(fn (Subuser $subuser) => user()?->id === $subuser->user->id)
|
||||
->authorize(fn () => user()?->can(SubuserPermission::UserUpdate, $server))
|
||||
->modalHeading(fn (Subuser $subuser) => trans('server/user.editing', ['user' => $subuser->user->email]))
|
||||
->successNotificationTitle(null)
|
||||
->action(function (array $data, SubuserUpdateService $subuserUpdateService, User $user) use ($server) {
|
||||
$subuser = $server->subusers->where('user_id', $user->id)->first();
|
||||
|
||||
->action(function (array $data, SubuserUpdateService $subuserUpdateService, Subuser $subuser) use ($server) {
|
||||
$permissions = collect($data)
|
||||
->forget('email')
|
||||
->flatMap(fn ($permissions, $key) => collect($permissions)->map(fn ($permission) => "$key.$permission"))
|
||||
->push(Permission::ACTION_WEBSOCKET_CONNECT)
|
||||
->push(SubuserPermission::WebsocketConnect->value)
|
||||
->unique()
|
||||
->all();
|
||||
|
||||
@@ -172,7 +175,8 @@ class UserResource extends Resource
|
||||
'sm' => 1,
|
||||
'md' => 4,
|
||||
'lg' => 5,
|
||||
]),
|
||||
])
|
||||
->formatStateUsing(fn (Subuser $subuser) => $subuser->user->email),
|
||||
Actions::make([
|
||||
Action::make('assignAll')
|
||||
->label(trans('server/user.assign_all'))
|
||||
@@ -195,12 +199,10 @@ class UserResource extends Resource
|
||||
->schema($tabs),
|
||||
]),
|
||||
])
|
||||
->mutateRecordDataUsing(function ($data, User $user) use ($server) {
|
||||
$permissionsArray = $server->subusers->where('user_id', $user->id)->first()->permissions;
|
||||
|
||||
->mutateRecordDataUsing(function ($data, Subuser $subuser) {
|
||||
$transformedPermissions = [];
|
||||
|
||||
foreach ($permissionsArray as $permission) {
|
||||
foreach ($subuser->permissions as $permission) {
|
||||
[$group, $action] = explode('.', $permission, 2);
|
||||
$transformedPermissions[$group][] = $action;
|
||||
}
|
||||
@@ -218,7 +220,7 @@ class UserResource extends Resource
|
||||
->icon('tabler-user-plus')
|
||||
->tooltip(trans('server/user.invite_user'))
|
||||
->createAnother(false)
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_USER_CREATE, $server))
|
||||
->authorize(fn () => user()?->can(SubuserPermission::UserCreate, $server))
|
||||
->schema([
|
||||
Grid::make()
|
||||
->columnSpanFull()
|
||||
@@ -272,7 +274,7 @@ class UserResource extends Resource
|
||||
$permissions = collect($data)
|
||||
->forget('email')
|
||||
->flatMap(fn ($permissions, $key) => collect($permissions)->map(fn ($permission) => "$key.$permission"))
|
||||
->push(Permission::ACTION_WEBSOCKET_CONNECT)
|
||||
->push(SubuserPermission::WebsocketConnect->value)
|
||||
->unique()
|
||||
->all();
|
||||
|
||||
@@ -312,7 +314,7 @@ class UserResource extends Resource
|
||||
public static function getDefaultPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => ListUsers::route('/'),
|
||||
'index' => ListSubusers::route('/'),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
namespace App\Filament\Server\Widgets;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Exceptions\Http\HttpForbiddenException;
|
||||
use App\Livewire\AlertBanner;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Server;
|
||||
use App\Models\User;
|
||||
use App\Services\Nodes\NodeJWTService;
|
||||
@@ -46,7 +46,7 @@ class ServerConsole extends Widget
|
||||
|
||||
protected function getToken(): string
|
||||
{
|
||||
if (!$this->user || !$this->server || $this->user->cannot(Permission::ACTION_WEBSOCKET_CONNECT, $this->server)) {
|
||||
if (!$this->user || !$this->server || $this->user->cannot(SubuserPermission::WebsocketConnect, $this->server)) {
|
||||
throw new HttpForbiddenException('You do not have permission to connect to this server\'s websocket.');
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ class ServerConsole extends Widget
|
||||
|
||||
protected function authorizeSendCommand(): bool
|
||||
{
|
||||
return $this->user->can(Permission::ACTION_CONTROL_CONSOLE, $this->server);
|
||||
return $this->user->can(SubuserPermission::ControlConsole, $this->server);
|
||||
}
|
||||
|
||||
protected function canSendCommand(): bool
|
||||
|
||||
@@ -7,15 +7,22 @@ use App\Http\Controllers\Api\Application\ApplicationApiController;
|
||||
use App\Http\Requests\Api\Application\Eggs\ExportEggRequest;
|
||||
use App\Http\Requests\Api\Application\Eggs\GetEggRequest;
|
||||
use App\Http\Requests\Api\Application\Eggs\GetEggsRequest;
|
||||
use App\Http\Requests\Api\Application\Eggs\ImportEggRequest;
|
||||
use App\Models\Egg;
|
||||
use App\Services\Eggs\Sharing\EggExporterService;
|
||||
use App\Services\Eggs\Sharing\EggImporterService;
|
||||
use App\Transformers\Api\Application\EggTransformer;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Response;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use Throwable;
|
||||
|
||||
class EggController extends ApplicationApiController
|
||||
{
|
||||
public function __construct(
|
||||
private EggExporterService $exporterService,
|
||||
private EggImporterService $importService
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
@@ -48,6 +55,20 @@ class EggController extends ApplicationApiController
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete egg
|
||||
*
|
||||
* Delete an egg from the Panel.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function delete(GetEggRequest $request, Egg $egg): Response
|
||||
{
|
||||
$egg->delete();
|
||||
|
||||
return $this->returnNoContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Export egg
|
||||
*
|
||||
@@ -63,4 +84,22 @@ class EggController extends ApplicationApiController
|
||||
'Content-Type' => 'application/' . $format->value,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import egg
|
||||
*
|
||||
* Create a new egg on the Panel. Returns the created egg and an HTTP/201 status response on success
|
||||
* If no uuid is supplied a new one will be generated
|
||||
* If an uuid is supplied, and it already exists the old configuration get overwritten
|
||||
*
|
||||
* @throws Exception|Throwable
|
||||
*/
|
||||
public function import(ImportEggRequest $request): JsonResponse
|
||||
{
|
||||
$egg = $this->importService->fromContent($request->getContent());
|
||||
|
||||
return $this->fractal->item($egg)
|
||||
->transformWith($this->getTransformer(EggTransformer::class))
|
||||
->respond(201);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,12 @@ namespace App\Http\Controllers\Api\Client;
|
||||
|
||||
use App\Http\Requests\Api\Client\GetServersRequest;
|
||||
use App\Models\Filters\MultiFieldServerFilter;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Server;
|
||||
use App\Models\Subuser;
|
||||
use App\Transformers\Api\Client\ServerTransformer;
|
||||
use Dedoc\Scramble\Attributes\Group;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Collection;
|
||||
use Spatie\QueryBuilder\AllowedFilter;
|
||||
use Spatie\QueryBuilder\QueryBuilder;
|
||||
|
||||
@@ -81,14 +80,14 @@ class ClientController extends ClientApiController
|
||||
*
|
||||
* Returns all the subuser permissions available on the system.
|
||||
*
|
||||
* @return array{object: string, attributes: array{permissions: Collection}}
|
||||
* @return array{object: string, attributes: array{permissions: string[]}}
|
||||
*/
|
||||
public function permissions(): array
|
||||
{
|
||||
return [
|
||||
'object' => 'system_permissions',
|
||||
'attributes' => [
|
||||
'permissions' => Permission::permissions(),
|
||||
'permissions' => Subuser::allPermissionKeys(),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
namespace App\Http\Controllers\Api\Client\Servers;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Controllers\Api\Client\ClientApiController;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\ActivityLog;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Role;
|
||||
use App\Models\Server;
|
||||
use App\Models\User;
|
||||
@@ -29,7 +29,7 @@ class ActivityLogController extends ClientApiController
|
||||
*/
|
||||
public function __invoke(ClientApiRequest $request, Server $server): array
|
||||
{
|
||||
Gate::authorize(Permission::ACTION_ACTIVITY_READ, $server);
|
||||
Gate::authorize(SubuserPermission::ActivityRead, $server);
|
||||
|
||||
$activity = QueryBuilder::for($server->activity())
|
||||
->allowedSorts(['timestamp'])
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
namespace App\Http\Controllers\Api\Client\Servers;
|
||||
|
||||
use App\Enums\ServerState;
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Facades\Activity;
|
||||
use App\Http\Controllers\Api\Client\ClientApiController;
|
||||
use App\Http\Requests\Api\Client\Servers\Backups\RenameBackupRequest;
|
||||
use App\Http\Requests\Api\Client\Servers\Backups\RestoreBackupRequest;
|
||||
use App\Http\Requests\Api\Client\Servers\Backups\StoreBackupRequest;
|
||||
use App\Models\Backup;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Server;
|
||||
use App\Repositories\Daemon\DaemonBackupRepository;
|
||||
use App\Services\Backups\DeleteBackupService;
|
||||
@@ -48,7 +48,7 @@ class BackupController extends ClientApiController
|
||||
*/
|
||||
public function index(Request $request, Server $server): array
|
||||
{
|
||||
if (!$request->user()->can(Permission::ACTION_BACKUP_READ, $server)) {
|
||||
if (!$request->user()->can(SubuserPermission::BackupRead, $server)) {
|
||||
throw new AuthorizationException();
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ class BackupController extends ClientApiController
|
||||
// otherwise ignore this status. This gets a little funky since it isn't clear
|
||||
// how best to allow a user to create a backup that is locked without also preventing
|
||||
// them from just filling up a server with backups that can never be deleted?
|
||||
if ($request->user()->can(Permission::ACTION_BACKUP_DELETE, $server)) {
|
||||
if ($request->user()->can(SubuserPermission::BackupDelete, $server)) {
|
||||
$action->setIsLocked((bool) $request->input('is_locked'));
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ class BackupController extends ClientApiController
|
||||
*/
|
||||
public function toggleLock(Request $request, Server $server, Backup $backup): array
|
||||
{
|
||||
if (!$request->user()->can(Permission::ACTION_BACKUP_DELETE, $server)) {
|
||||
if (!$request->user()->can(SubuserPermission::BackupDelete, $server)) {
|
||||
throw new AuthorizationException();
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ class BackupController extends ClientApiController
|
||||
*/
|
||||
public function view(Request $request, Server $server, Backup $backup): array
|
||||
{
|
||||
if (!$request->user()->can(Permission::ACTION_BACKUP_READ, $server)) {
|
||||
if (!$request->user()->can(SubuserPermission::BackupRead, $server)) {
|
||||
throw new AuthorizationException();
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ class BackupController extends ClientApiController
|
||||
*/
|
||||
public function delete(Request $request, Server $server, Backup $backup): JsonResponse
|
||||
{
|
||||
if (!$request->user()->can(Permission::ACTION_BACKUP_DELETE, $server)) {
|
||||
if (!$request->user()->can(SubuserPermission::BackupDelete, $server)) {
|
||||
throw new AuthorizationException();
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ class BackupController extends ClientApiController
|
||||
*/
|
||||
public function download(Request $request, Server $server, Backup $backup): JsonResponse
|
||||
{
|
||||
if (!$request->user()->can(Permission::ACTION_BACKUP_DOWNLOAD, $server)) {
|
||||
if (!$request->user()->can(SubuserPermission::BackupDownload, $server)) {
|
||||
throw new AuthorizationException();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Controllers\Api\Client\Servers;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Exceptions\Http\HttpForbiddenException;
|
||||
use App\Exceptions\Model\DataValidationException;
|
||||
use App\Exceptions\Service\ServiceLimitExceededException;
|
||||
@@ -9,7 +10,6 @@ use App\Facades\Activity;
|
||||
use App\Http\Controllers\Api\Client\ClientApiController;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Http\Requests\Api\Client\Servers\Schedules\StoreTaskRequest;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Schedule;
|
||||
use App\Models\Server;
|
||||
use App\Models\Task;
|
||||
@@ -170,7 +170,7 @@ class ScheduleTaskController extends ClientApiController
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
if (!$request->user()->can(Permission::ACTION_SCHEDULE_DELETE, $server)) {
|
||||
if (!$request->user()->can(SubuserPermission::ScheduleDelete, $server)) {
|
||||
throw new HttpForbiddenException('You do not have permission to perform this action.');
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Controllers\Api\Client\Servers;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Exceptions\Model\DataValidationException;
|
||||
use App\Exceptions\Service\Subuser\ServerSubuserExistsException;
|
||||
use App\Exceptions\Service\Subuser\UserIsServerOwnerException;
|
||||
@@ -11,7 +12,6 @@ use App\Http\Requests\Api\Client\Servers\Subusers\DeleteSubuserRequest;
|
||||
use App\Http\Requests\Api\Client\Servers\Subusers\GetSubuserRequest;
|
||||
use App\Http\Requests\Api\Client\Servers\Subusers\StoreSubuserRequest;
|
||||
use App\Http\Requests\Api\Client\Servers\Subusers\UpdateSubuserRequest;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Server;
|
||||
use App\Models\Subuser;
|
||||
use App\Models\User;
|
||||
@@ -82,18 +82,17 @@ class SubuserController extends ClientApiController
|
||||
*/
|
||||
public function store(StoreSubuserRequest $request, Server $server): array
|
||||
{
|
||||
$response = $this->creationService->handle(
|
||||
$server,
|
||||
$request->input('email'),
|
||||
$this->getDefaultPermissions($request)
|
||||
);
|
||||
$email = $request->input('email');
|
||||
$permissions = $this->getCleanedPermissions($request);
|
||||
|
||||
$subuser = $this->creationService->handle($server, $email, $permissions);
|
||||
|
||||
Activity::event('server:subuser.create')
|
||||
->subject($response->user)
|
||||
->property(['email' => $request->input('email'), 'permissions' => $this->getDefaultPermissions($request)])
|
||||
->subject($subuser->user)
|
||||
->property(['email' => $email, 'permissions' => $subuser->permissions])
|
||||
->log();
|
||||
|
||||
return $this->fractal->item($response)
|
||||
return $this->fractal->item($subuser)
|
||||
->transformWith($this->getTransformer(SubuserTransformer::class))
|
||||
->toArray();
|
||||
}
|
||||
@@ -112,7 +111,7 @@ class SubuserController extends ClientApiController
|
||||
/** @var Subuser $subuser */
|
||||
$subuser = $request->attributes->get('subuser');
|
||||
|
||||
$this->updateService->handle($subuser, $server, $this->getDefaultPermissions($request));
|
||||
$this->updateService->handle($subuser, $server, $this->getCleanedPermissions($request));
|
||||
|
||||
return $this->fractal->item($subuser->refresh())
|
||||
->transformWith($this->getTransformer(SubuserTransformer::class))
|
||||
@@ -135,17 +134,19 @@ class SubuserController extends ClientApiController
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default permissions for subusers and parses out any permissions
|
||||
* Returns the "cleaned" permissions for subusers and parses out any permissions
|
||||
* that were passed that do not also exist in the internally tracked list of
|
||||
* permissions.
|
||||
*
|
||||
* @return array<array-key, mixed>
|
||||
* @return string[]
|
||||
*/
|
||||
protected function getDefaultPermissions(Request $request): array
|
||||
protected function getCleanedPermissions(Request $request): array
|
||||
{
|
||||
$allowed = Permission::permissionKeys()->all();
|
||||
$cleaned = array_intersect($request->input('permissions') ?? [], $allowed);
|
||||
|
||||
return array_unique(array_merge($cleaned, [Permission::ACTION_WEBSOCKET_CONNECT]));
|
||||
return collect($request->input('permissions') ?? [])
|
||||
->intersect(Subuser::allPermissionKeys())
|
||||
->push(SubuserPermission::WebsocketConnect->value)
|
||||
->unique()
|
||||
->values()
|
||||
->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
namespace App\Http\Controllers\Api\Client\Servers;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Exceptions\Http\HttpForbiddenException;
|
||||
use App\Http\Controllers\Api\Client\ClientApiController;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Server;
|
||||
use App\Services\Nodes\NodeJWTService;
|
||||
use App\Services\Servers\GetUserPermissionsService;
|
||||
@@ -37,7 +37,7 @@ class WebsocketController extends ClientApiController
|
||||
public function __invoke(ClientApiRequest $request, Server $server): JsonResponse
|
||||
{
|
||||
$user = $request->user();
|
||||
if ($user->cannot(Permission::ACTION_WEBSOCKET_CONNECT, $server)) {
|
||||
if ($user->cannot(SubuserPermission::WebsocketConnect, $server)) {
|
||||
throw new HttpForbiddenException('You do not have permission to connect to this server\'s websocket.');
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
namespace App\Http\Controllers\Api\Remote;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Exceptions\Http\HttpForbiddenException;
|
||||
use App\Facades\Activity;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Api\Remote\SftpAuthenticationFormRequest;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Server;
|
||||
use App\Models\User;
|
||||
use App\Services\Servers\GetUserPermissionsService;
|
||||
@@ -141,7 +141,7 @@ class SftpAuthenticationController extends Controller
|
||||
if ($user->cannot('update server', $server) && $server->owner_id !== $user->id) {
|
||||
$permissions = $this->permissions->handle($server, $user);
|
||||
|
||||
if (!in_array(Permission::ACTION_FILE_SFTP, $permissions)) {
|
||||
if (!in_array(SubuserPermission::FileSftp->value, $permissions)) {
|
||||
Activity::event('server:sftp.denied')->actor($user)->subject($server)->log();
|
||||
|
||||
throw new HttpForbiddenException('You do not have permission to access SFTP for this server.');
|
||||
|
||||
14
app/Http/Requests/Api/Application/Eggs/ImportEggRequest.php
Normal file
14
app/Http/Requests/Api/Application/Eggs/ImportEggRequest.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Api\Application\Eggs;
|
||||
|
||||
use App\Http\Requests\Api\Application\ApplicationApiRequest;
|
||||
use App\Models\Egg;
|
||||
use App\Services\Acl\Api\AdminAcl;
|
||||
|
||||
class ImportEggRequest extends ApplicationApiRequest
|
||||
{
|
||||
protected ?string $resource = Egg::RESOURCE_NAME;
|
||||
|
||||
protected int $permission = AdminAcl::WRITE;
|
||||
}
|
||||
@@ -22,6 +22,7 @@ class StoreUserRequest extends ApplicationApiRequest
|
||||
|
||||
return collect($rules)->only([
|
||||
'external_id',
|
||||
'is_managed_externally',
|
||||
'email',
|
||||
'username',
|
||||
'password',
|
||||
@@ -39,6 +40,7 @@ class StoreUserRequest extends ApplicationApiRequest
|
||||
{
|
||||
return [
|
||||
'external_id' => 'Third Party Identifier',
|
||||
'is_managed_externally' => 'Is managed by Third Party?',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ class UpdateEmailRequest extends ClientApiRequest
|
||||
throw new InvalidPasswordProvidedException(trans('validation.internal.invalid_password'));
|
||||
}
|
||||
|
||||
return true;
|
||||
return !$this->user()->is_managed_externally;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
|
||||
@@ -25,7 +25,7 @@ class UpdatePasswordRequest extends ClientApiRequest
|
||||
throw new InvalidPasswordProvidedException(trans('validation.internal.invalid_password'));
|
||||
}
|
||||
|
||||
return true;
|
||||
return !$this->user()->is_managed_externally;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
|
||||
@@ -26,7 +26,7 @@ class UpdateUsernameRequest extends ClientApiRequest
|
||||
throw new InvalidPasswordProvidedException(trans('validation.internal.invalid_password'));
|
||||
}
|
||||
|
||||
return true;
|
||||
return !$this->user()->is_managed_externally;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
namespace App\Http\Requests\Api\Client\Servers\Backups;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
|
||||
class RenameBackupRequest extends ClientApiRequest
|
||||
{
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_BACKUP_DELETE;
|
||||
return SubuserPermission::BackupDelete;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
namespace App\Http\Requests\Api\Client\Servers\Backups;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
|
||||
class RestoreBackupRequest extends ClientApiRequest
|
||||
{
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_BACKUP_RESTORE;
|
||||
return SubuserPermission::BackupRestore;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
namespace App\Http\Requests\Api\Client\Servers\Backups;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
|
||||
class StoreBackupRequest extends ClientApiRequest
|
||||
{
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_BACKUP_CREATE;
|
||||
return SubuserPermission::BackupCreate;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
namespace App\Http\Requests\Api\Client\Servers\Databases;
|
||||
|
||||
use App\Contracts\Http\ClientPermissionsRequest;
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
|
||||
class DeleteDatabaseRequest extends ClientApiRequest implements ClientPermissionsRequest
|
||||
{
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_DATABASE_DELETE;
|
||||
return SubuserPermission::DatabaseDelete;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
namespace App\Http\Requests\Api\Client\Servers\Databases;
|
||||
|
||||
use App\Contracts\Http\ClientPermissionsRequest;
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
|
||||
class GetDatabasesRequest extends ClientApiRequest implements ClientPermissionsRequest
|
||||
{
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_DATABASE_READ;
|
||||
return SubuserPermission::DatabaseRead;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
|
||||
namespace App\Http\Requests\Api\Client\Servers\Databases;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
|
||||
class RotatePasswordRequest extends ClientApiRequest
|
||||
{
|
||||
/**
|
||||
* Check that the user has permission to rotate the password.
|
||||
*/
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_DATABASE_UPDATE;
|
||||
return SubuserPermission::DatabaseUpdate;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
namespace App\Http\Requests\Api\Client\Servers\Databases;
|
||||
|
||||
use App\Contracts\Http\ClientPermissionsRequest;
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Database;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Server;
|
||||
use App\Services\Databases\DatabaseManagementService;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
@@ -14,9 +14,9 @@ use Webmozart\Assert\Assert;
|
||||
|
||||
class StoreDatabaseRequest extends ClientApiRequest implements ClientPermissionsRequest
|
||||
{
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_DATABASE_CREATE;
|
||||
return SubuserPermission::DatabaseCreate;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
namespace App\Http\Requests\Api\Client\Servers\Files;
|
||||
|
||||
use App\Contracts\Http\ClientPermissionsRequest;
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
|
||||
class ChmodFilesRequest extends ClientApiRequest implements ClientPermissionsRequest
|
||||
{
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_FILE_UPDATE;
|
||||
return SubuserPermission::FileUpdate;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
|
||||
namespace App\Http\Requests\Api\Client\Servers\Files;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
|
||||
class CompressFilesRequest extends ClientApiRequest
|
||||
{
|
||||
/**
|
||||
* Checks that the authenticated user is allowed to create archives for this server.
|
||||
*/
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_FILE_ARCHIVE;
|
||||
return SubuserPermission::FileArchive;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
namespace App\Http\Requests\Api\Client\Servers\Files;
|
||||
|
||||
use App\Contracts\Http\ClientPermissionsRequest;
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
|
||||
class CopyFileRequest extends ClientApiRequest implements ClientPermissionsRequest
|
||||
{
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_FILE_CREATE;
|
||||
return SubuserPermission::FileCreate;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
|
||||
namespace App\Http\Requests\Api\Client\Servers\Files;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
|
||||
class CreateFolderRequest extends ClientApiRequest
|
||||
{
|
||||
/**
|
||||
* Checks that the authenticated user is allowed to create files on the server.
|
||||
*/
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_FILE_CREATE;
|
||||
return SubuserPermission::FileCreate;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
namespace App\Http\Requests\Api\Client\Servers\Files;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
|
||||
class DecompressFilesRequest extends ClientApiRequest
|
||||
{
|
||||
@@ -12,9 +12,9 @@ class DecompressFilesRequest extends ClientApiRequest
|
||||
* rely on the archive permission here as it makes more sense to make sure the user can create
|
||||
* additional files rather than make an archive.
|
||||
*/
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_FILE_CREATE;
|
||||
return SubuserPermission::FileCreate;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
namespace App\Http\Requests\Api\Client\Servers\Files;
|
||||
|
||||
use App\Contracts\Http\ClientPermissionsRequest;
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
|
||||
class DeleteFileRequest extends ClientApiRequest implements ClientPermissionsRequest
|
||||
{
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_FILE_DELETE;
|
||||
return SubuserPermission::FileDelete;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
namespace App\Http\Requests\Api\Client\Servers\Files;
|
||||
|
||||
use App\Contracts\Http\ClientPermissionsRequest;
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
|
||||
class GetFileContentsRequest extends ClientApiRequest implements ClientPermissionsRequest
|
||||
{
|
||||
@@ -13,9 +13,9 @@ class GetFileContentsRequest extends ClientApiRequest implements ClientPermissio
|
||||
* validate that the authenticated user has permission to perform this action aganist
|
||||
* the given resource (server).
|
||||
*/
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_FILE_READ_CONTENT;
|
||||
return SubuserPermission::FileReadContent;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
namespace App\Http\Requests\Api\Client\Servers\Files;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
|
||||
class ListFilesRequest extends ClientApiRequest
|
||||
{
|
||||
@@ -11,9 +11,9 @@ class ListFilesRequest extends ClientApiRequest
|
||||
* Check that the user making this request to the API is authorized to list all
|
||||
* the files that exist for a given server.
|
||||
*/
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_FILE_READ;
|
||||
return SubuserPermission::FileRead;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
namespace App\Http\Requests\Api\Client\Servers\Files;
|
||||
|
||||
use App\Contracts\Http\ClientPermissionsRequest;
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
|
||||
class PullFileRequest extends ClientApiRequest implements ClientPermissionsRequest
|
||||
{
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_FILE_CREATE;
|
||||
return SubuserPermission::FileCreate;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
namespace App\Http\Requests\Api\Client\Servers\Files;
|
||||
|
||||
use App\Contracts\Http\ClientPermissionsRequest;
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
|
||||
class RenameFileRequest extends ClientApiRequest implements ClientPermissionsRequest
|
||||
{
|
||||
@@ -12,9 +12,9 @@ class RenameFileRequest extends ClientApiRequest implements ClientPermissionsReq
|
||||
* The permission the user is required to have in order to perform this
|
||||
* request action.
|
||||
*/
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_FILE_UPDATE;
|
||||
return SubuserPermission::FileUpdate;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
namespace App\Http\Requests\Api\Client\Servers\Files;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
|
||||
class UploadFileRequest extends ClientApiRequest
|
||||
{
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_FILE_CREATE;
|
||||
return SubuserPermission::FileCreate;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
namespace App\Http\Requests\Api\Client\Servers\Files;
|
||||
|
||||
use App\Contracts\Http\ClientPermissionsRequest;
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
|
||||
class WriteFileContentRequest extends ClientApiRequest implements ClientPermissionsRequest
|
||||
{
|
||||
@@ -13,9 +13,9 @@ class WriteFileContentRequest extends ClientApiRequest implements ClientPermissi
|
||||
* validate that the authenticated user has permission to perform this action aganist
|
||||
* the given resource (server).
|
||||
*/
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_FILE_CREATE;
|
||||
return SubuserPermission::FileCreate;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
namespace App\Http\Requests\Api\Client\Servers\Network;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
|
||||
class DeleteAllocationRequest extends ClientApiRequest
|
||||
{
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_ALLOCATION_DELETE;
|
||||
return SubuserPermission::AllocationDelete;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
namespace App\Http\Requests\Api\Client\Servers\Network;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
|
||||
class GetNetworkRequest extends ClientApiRequest
|
||||
{
|
||||
@@ -11,8 +11,8 @@ class GetNetworkRequest extends ClientApiRequest
|
||||
* Check that the user has permission to view the allocations for
|
||||
* this server.
|
||||
*/
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_ALLOCATION_READ;
|
||||
return SubuserPermission::AllocationRead;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
namespace App\Http\Requests\Api\Client\Servers\Network;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
|
||||
class NewAllocationRequest extends ClientApiRequest
|
||||
{
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_ALLOCATION_CREATE;
|
||||
return SubuserPermission::AllocationCreate;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
|
||||
namespace App\Http\Requests\Api\Client\Servers\Network;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Allocation;
|
||||
use App\Models\Permission;
|
||||
|
||||
class UpdateAllocationRequest extends ClientApiRequest
|
||||
{
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_ALLOCATION_UPDATE;
|
||||
return SubuserPermission::AllocationUpdate;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
namespace App\Http\Requests\Api\Client\Servers\Schedules;
|
||||
|
||||
use App\Models\Permission;
|
||||
use App\Enums\SubuserPermission;
|
||||
|
||||
class DeleteScheduleRequest extends ViewScheduleRequest
|
||||
{
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_SCHEDULE_DELETE;
|
||||
return SubuserPermission::ScheduleDelete;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
namespace App\Http\Requests\Api\Client\Servers\Schedules;
|
||||
|
||||
use App\Models\Permission;
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Models\Schedule;
|
||||
|
||||
class StoreScheduleRequest extends ViewScheduleRequest
|
||||
{
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_SCHEDULE_CREATE;
|
||||
return SubuserPermission::ScheduleCreate;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace App\Http\Requests\Api\Client\Servers\Schedules;
|
||||
|
||||
use App\Models\Permission;
|
||||
use App\Enums\SubuserPermission;
|
||||
|
||||
class StoreTaskRequest extends ViewScheduleRequest
|
||||
{
|
||||
@@ -11,9 +11,9 @@ class StoreTaskRequest extends ViewScheduleRequest
|
||||
* check if they can modify a schedule to determine if they're able to do this. There
|
||||
* are no task specific permissions.
|
||||
*/
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_SCHEDULE_UPDATE;
|
||||
return SubuserPermission::ScheduleUpdate;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
namespace App\Http\Requests\Api\Client\Servers\Schedules;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
|
||||
class TriggerScheduleRequest extends ClientApiRequest
|
||||
{
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_SCHEDULE_UPDATE;
|
||||
return SubuserPermission::ScheduleUpdate;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
namespace App\Http\Requests\Api\Client\Servers\Schedules;
|
||||
|
||||
use App\Models\Permission;
|
||||
use App\Enums\SubuserPermission;
|
||||
|
||||
class UpdateScheduleRequest extends StoreScheduleRequest
|
||||
{
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_SCHEDULE_UPDATE;
|
||||
return SubuserPermission::ScheduleUpdate;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
namespace App\Http\Requests\Api\Client\Servers\Schedules;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Schedule;
|
||||
use App\Models\Server;
|
||||
use App\Models\Task;
|
||||
@@ -36,8 +36,8 @@ class ViewScheduleRequest extends ClientApiRequest
|
||||
return true;
|
||||
}
|
||||
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_SCHEDULE_READ;
|
||||
return SubuserPermission::ScheduleRead;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
|
||||
namespace App\Http\Requests\Api\Client\Servers;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
|
||||
class SendCommandRequest extends ClientApiRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the API user has permission to perform this action.
|
||||
*/
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_CONTROL_CONSOLE;
|
||||
return SubuserPermission::ControlConsole;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,28 +2,28 @@
|
||||
|
||||
namespace App\Http\Requests\Api\Client\Servers;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
|
||||
class SendPowerRequest extends ClientApiRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user has permission to send a power command to a server.
|
||||
*/
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
switch ($this->input('signal')) {
|
||||
case 'start':
|
||||
return Permission::ACTION_CONTROL_START;
|
||||
return SubuserPermission::ControlStart;
|
||||
case 'stop':
|
||||
case 'kill':
|
||||
return Permission::ACTION_CONTROL_STOP;
|
||||
return SubuserPermission::ControlStop;
|
||||
case 'restart':
|
||||
return Permission::ACTION_CONTROL_RESTART;
|
||||
return SubuserPermission::ControlRestart;
|
||||
}
|
||||
|
||||
// Fallback for invalid signals
|
||||
return Permission::ACTION_WEBSOCKET_CONNECT;
|
||||
return SubuserPermission::WebsocketConnect;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
namespace App\Http\Requests\Api\Client\Servers\Settings;
|
||||
|
||||
use App\Contracts\Http\ClientPermissionsRequest;
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
|
||||
class DescriptionServerRequest extends ClientApiRequest implements ClientPermissionsRequest
|
||||
{
|
||||
@@ -13,9 +13,9 @@ class DescriptionServerRequest extends ClientApiRequest implements ClientPermiss
|
||||
* validate that the authenticated user has permission to perform this action against
|
||||
* the given resource (server).
|
||||
*/
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_SETTINGS_DESCRIPTION;
|
||||
return SubuserPermission::SettingsDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
namespace App\Http\Requests\Api\Client\Servers\Settings;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
|
||||
class ReinstallServerRequest extends ClientApiRequest
|
||||
{
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_SETTINGS_REINSTALL;
|
||||
return SubuserPermission::SettingsReinstall;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
namespace App\Http\Requests\Api\Client\Servers\Settings;
|
||||
|
||||
use App\Contracts\Http\ClientPermissionsRequest;
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Server;
|
||||
|
||||
class RenameServerRequest extends ClientApiRequest implements ClientPermissionsRequest
|
||||
@@ -14,9 +14,9 @@ class RenameServerRequest extends ClientApiRequest implements ClientPermissionsR
|
||||
* validate that the authenticated user has permission to perform this action against
|
||||
* the given resource (server).
|
||||
*/
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_SETTINGS_RENAME;
|
||||
return SubuserPermission::SettingsRename;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,17 +3,17 @@
|
||||
namespace App\Http\Requests\Api\Client\Servers\Settings;
|
||||
|
||||
use App\Contracts\Http\ClientPermissionsRequest;
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
class SetDockerImageRequest extends ClientApiRequest implements ClientPermissionsRequest
|
||||
{
|
||||
public function permission(): string
|
||||
public function permission(): SubuserPermission
|
||||
{
|
||||
return Permission::ACTION_STARTUP_DOCKER_IMAGE;
|
||||
return SubuserPermission::StartupDockerImage;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user