diff --git a/.env.example b/.env.example index ae71e78e69..95607b2e37 100644 --- a/.env.example +++ b/.env.example @@ -4,7 +4,6 @@ APP_KEY= APP_TIMEZONE=UTC APP_URL=http://panel.test APP_LOCALE=en -APP_ENVIRONMENT_ONLY=true LOG_CHANNEL=daily LOG_STACK=single diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 92fea58bda..5e2ee88471 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -32,7 +32,6 @@ jobs: APP_KEY: ThisIsARandomStringForTests12345 APP_TIMEZONE: UTC APP_URL: http://localhost/ - APP_ENVIRONMENT_ONLY: "true" CACHE_DRIVER: array MAIL_MAILER: array SESSION_DRIVER: array @@ -106,7 +105,6 @@ jobs: APP_KEY: ThisIsARandomStringForTests12345 APP_TIMEZONE: UTC APP_URL: http://localhost/ - APP_ENVIRONMENT_ONLY: "true" CACHE_DRIVER: array MAIL_MAILER: array SESSION_DRIVER: array @@ -170,7 +168,6 @@ jobs: APP_KEY: ThisIsARandomStringForTests12345 APP_TIMEZONE: UTC APP_URL: http://localhost/ - APP_ENVIRONMENT_ONLY: "true" CACHE_DRIVER: array MAIL_MAILER: array SESSION_DRIVER: array diff --git a/app/Console/Commands/Environment/AppSettingsCommand.php b/app/Console/Commands/Environment/AppSettingsCommand.php index 4f2e931196..a480e9b6f5 100644 --- a/app/Console/Commands/Environment/AppSettingsCommand.php +++ b/app/Console/Commands/Environment/AppSettingsCommand.php @@ -38,8 +38,7 @@ class AppSettingsCommand extends Command {--queue= : The queue driver backend to use.} {--redis-host= : Redis host to use for connections.} {--redis-pass= : Password used to connect to redis.} - {--redis-port= : Port to connect to redis over.} - {--settings-ui= : Enable or disable the settings UI.}'; + {--redis-port= : Port to connect to redis over.}'; protected array $variables = []; @@ -87,12 +86,6 @@ public function handle(): int array_key_exists($selected, self::QUEUE_DRIVERS) ? $selected : null ); - if (!is_null($this->option('settings-ui'))) { - $this->variables['APP_ENVIRONMENT_ONLY'] = $this->option('settings-ui') == 'true' ? 'false' : 'true'; - } else { - $this->variables['APP_ENVIRONMENT_ONLY'] = $this->confirm(__('commands.appsettings.comment.settings_ui'), true) ? 'false' : 'true'; - } - // Make sure session cookies are set as "secure" when using HTTPS if (str_starts_with($this->variables['APP_URL'], 'https://')) { $this->variables['SESSION_SECURE_COOKIE'] = 'true'; diff --git a/app/Filament/Clusters/Settings.php b/app/Filament/Clusters/Settings.php deleted file mode 100644 index 0ac8254c82..0000000000 --- a/app/Filament/Clusters/Settings.php +++ /dev/null @@ -1,10 +0,0 @@ -form->fill(); + } + + protected function getFormSchema(): array + { + return [ + Tabs::make('Tabs') + ->columns() + ->persistTabInQueryString() + ->tabs([ + Tab::make('general') + ->label('General') + ->icon('tabler-home') + ->schema($this->generalSettings()), + Tab::make('recaptcha') + ->label('reCAPTCHA') + ->icon('tabler-shield') + ->schema($this->recaptchaSettings()), + Tab::make('mail') + ->label('Mail') + ->icon('tabler-mail') + ->schema($this->mailSettings()), + Tab::make('backup') + ->label('Backup') + ->icon('tabler-box') + ->schema($this->backupSettings()), + Tab::make('misc') + ->label('Misc') + ->icon('tabler-tool') + ->schema($this->miscSettings()), + ]), + ]; + } + + private function generalSettings(): array + { + return [ + TextInput::make('APP_NAME') + ->label('App Name') + ->required() + ->default(env('APP_NAME', 'Pelican')), + TextInput::make('APP_FAVICON') + ->label('App Favicon') + ->hintIcon('tabler-question-mark') + ->hintIconTooltip('Favicons should be placed in the public folder, located in the root panel directory.') + ->required() + ->default(env('APP_FAVICON', './pelican.ico')), + Toggle::make('APP_DEBUG') + ->label('Enable Debug Mode?') + ->inline(false) + ->onIcon('tabler-check') + ->offIcon('tabler-x') + ->onColor('success') + ->offColor('danger') + ->formatStateUsing(fn ($state): bool => (bool) $state) + ->afterStateUpdated(fn ($state, Set $set) => $set('APP_DEBUG', (bool) $state)) + ->default(env('RECAPTCHA_ENABLED', config('recaptcha.enabled'))), + ToggleButtons::make('FILAMENT_TOP_NAVIGATION') + ->label('Navigation') + ->grouped() + ->options([ + false => 'Sidebar', + true => 'Topbar', + ]) + ->formatStateUsing(fn ($state): bool => (bool) $state) + ->afterStateUpdated(fn ($state, Set $set) => $set('FILAMENT_TOP_NAVIGATION', (bool) $state)) + ->default(env('FILAMENT_TOP_NAVIGATION', config('panel.filament.top-navigation'))), + ToggleButtons::make('PANEL_USE_BINARY_PREFIX') + ->label('Unit prefix') + ->grouped() + ->options([ + false => 'Decimal Prefix (MB/ GB)', + true => 'Binary Prefix (MiB/ GiB)', + ]) + ->formatStateUsing(fn ($state): bool => (bool) $state) + ->afterStateUpdated(fn ($state, Set $set) => $set('PANEL_USE_BINARY_PREFIX', (bool) $state)) + ->default(env('PANEL_USE_BINARY_PREFIX', config('panel.use_binary_prefix'))), + ToggleButtons::make('APP_2FA_REQUIRED') + ->label('2FA Requirement') + ->grouped() + ->options([ + 0 => 'Not required', + 1 => 'Required for only Admins', + 2 => 'Required for all Users', + ]) + ->formatStateUsing(fn ($state): int => (int) $state) + ->afterStateUpdated(fn ($state, Set $set) => $set('APP_2FA_REQUIRED', (int) $state)) + ->default(env('APP_2FA_REQUIRED', config('panel.auth.2fa_required'))), + TagsInput::make('TRUSTED_PROXIES') + ->label('Trusted Proxies') + ->separator() + ->splitKeys(['Tab', ' ']) + ->placeholder('New IP or IP Range') + ->default(env('TRUSTED_PROXIES', config('trustedproxy.proxies'))) + ->hintActions([ + FormAction::make('clear') + ->label('Clear') + ->color('danger') + ->icon('tabler-trash') + ->requiresConfirmation() + ->action(fn (Set $set) => $set('TRUSTED_PROXIES', [])), + FormAction::make('cloudflare') + ->label('Set to Cloudflare IPs') + ->icon('tabler-brand-cloudflare') + ->action(fn (Set $set) => $set('TRUSTED_PROXIES', [ + '173.245.48.0/20', + '103.21.244.0/22', + '103.22.200.0/22', + '103.31.4.0/22', + '141.101.64.0/18', + '108.162.192.0/18', + '190.93.240.0/20', + '188.114.96.0/20', + '197.234.240.0/22', + '198.41.128.0/17', + '162.158.0.0/15', + '104.16.0.0/13', + '104.24.0.0/14', + '172.64.0.0/13', + '131.0.72.0/22', + ])), + ]), + ]; + } + + private function recaptchaSettings(): array + { + return [ + Toggle::make('RECAPTCHA_ENABLED') + ->label('Enable reCAPTCHA?') + ->inline(false) + ->onIcon('tabler-check') + ->offIcon('tabler-x') + ->onColor('success') + ->offColor('danger') + ->live() + ->formatStateUsing(fn ($state): bool => (bool) $state) + ->afterStateUpdated(fn ($state, Set $set) => $set('RECAPTCHA_ENABLED', (bool) $state)) + ->default(env('RECAPTCHA_ENABLED', config('recaptcha.enabled'))), + TextInput::make('RECAPTCHA_DOMAIN') + ->label('Domain') + ->required() + ->visible(fn (Get $get) => $get('RECAPTCHA_ENABLED')) + ->default(env('RECAPTCHA_DOMAIN', config('recaptcha.domain'))), + TextInput::make('RECAPTCHA_WEBSITE_KEY') + ->label('Website Key') + ->required() + ->visible(fn (Get $get) => $get('RECAPTCHA_ENABLED')) + ->default(env('RECAPTCHA_WEBSITE_KEY', config('recaptcha.website_key'))), + TextInput::make('RECAPTCHA_SECRET_KEY') + ->label('Secret Key') + ->required() + ->visible(fn (Get $get) => $get('RECAPTCHA_ENABLED')) + ->default(env('RECAPTCHA_SECRET_KEY', config('recaptcha.secret_key'))), + ]; + } + + private function mailSettings(): array + { + return [ + ToggleButtons::make('MAIL_MAILER') + ->label('Mail Driver') + ->columnSpanFull() + ->grouped() + ->options([ + 'log' => 'Print mails to Log', + 'smtp' => 'SMTP Server', + 'sendmail' => 'sendmail Binary', + 'mailgun' => 'Mailgun', + 'mandrill' => 'Mandrill', + 'postmark' => 'Postmark', + ]) + ->live() + ->default(env('MAIL_MAILER', config('mail.default'))) + ->hintAction( + FormAction::make('test') + ->label('Send Test Mail') + ->icon('tabler-send') + ->hidden(fn (Get $get) => $get('MAIL_MAILER') === 'log') + ->action(function () { + try { + MailNotification::route('mail', auth()->user()->email) + ->notify(new MailTested(auth()->user())); + + Notification::make() + ->title('Test Mail sent') + ->success() + ->send(); + } catch (Exception $exception) { + Notification::make() + ->title('Test Mail failed') + ->body($exception->getMessage()) + ->danger() + ->send(); + } + }) + ), + Section::make('"From" Settings') + ->description('Set the Address and Name used as "From" in mails.') + ->columns() + ->schema([ + TextInput::make('MAIL_FROM_ADDRESS') + ->label('From Address') + ->required() + ->email() + ->default(env('MAIL_FROM_ADDRESS', config('mail.from.address'))), + TextInput::make('MAIL_FROM_NAME') + ->label('From Name') + ->required() + ->default(env('MAIL_FROM_NAME', config('mail.from.name'))), + ]), + Section::make('SMTP Configuration') + ->columns() + ->visible(fn (Get $get) => $get('MAIL_MAILER') === 'smtp') + ->schema([ + TextInput::make('MAIL_HOST') + ->label('SMTP Host') + ->required() + ->default(env('MAIL_HOST', config('mail.mailers.smtp.host'))), + TextInput::make('MAIL_PORT') + ->label('SMTP Port') + ->required() + ->numeric() + ->minValue(1) + ->maxValue(65535) + ->default(env('MAIL_PORT', config('mail.mailers.smtp.port'))), + TextInput::make('MAIL_USERNAME') + ->label('SMTP Username') + ->required() + ->default(env('MAIL_USERNAME', config('mail.mailers.smtp.username'))), + TextInput::make('MAIL_PASSWORD') + ->label('SMTP Password') + ->password() + ->revealable() + ->default(env('MAIL_PASSWORD')), + ToggleButtons::make('MAIL_ENCRYPTION') + ->label('SMTP encryption') + ->required() + ->grouped() + ->options(['tls' => 'TLS', 'ssl' => 'SSL', '' => 'None']) + ->default(env('MAIL_ENCRYPTION', config('mail.mailers.smtp.encryption', 'tls'))), + ]), + Section::make('Mailgun Configuration') + ->columns() + ->visible(fn (Get $get) => $get('MAIL_MAILER') === 'mailgun') + ->schema([ + TextInput::make('MAILGUN_DOMAIN') + ->label('Mailgun Domain') + ->required() + ->default(env('MAILGUN_DOMAIN', config('services.mailgun.domain'))), + TextInput::make('MAILGUN_SECRET') + ->label('Mailgun Secret') + ->required() + ->default(env('MAIL_USERNAME', config('services.mailgun.secret'))), + TextInput::make('MAILGUN_ENDPOINT') + ->label('Mailgun Endpoint') + ->required() + ->default(env('MAILGUN_ENDPOINT', config('services.mailgun.endpoint'))), + ]), + ]; + } + + private function backupSettings(): array + { + return [ + ToggleButtons::make('APP_BACKUP_DRIVER') + ->label('Backup Driver') + ->columnSpanFull() + ->grouped() + ->options([ + Backup::ADAPTER_DAEMON => 'Wings', + Backup::ADAPTER_AWS_S3 => 'S3', + ]) + ->live() + ->default(env('APP_BACKUP_DRIVER', config('backups.default'))), + Section::make('Throttles') + ->description('Configure how many backups can be created in a period. Set period to 0 to disable this throttle.') + ->columns() + ->schema([ + TextInput::make('BACKUP_THROTTLE_LIMIT') + ->label('Limit') + ->required() + ->numeric() + ->minValue(1) + ->default(config('backups.throttles.limit')), + TextInput::make('BACKUP_THROTTLE_PERIOD') + ->label('Period') + ->required() + ->numeric() + ->minValue(0) + ->suffix('Seconds') + ->default(config('backups.throttles.period')), + ]), + Section::make('S3 Configuration') + ->columns() + ->visible(fn (Get $get) => $get('APP_BACKUP_DRIVER') === Backup::ADAPTER_AWS_S3) + ->schema([ + TextInput::make('AWS_DEFAULT_REGION') + ->label('Default Region') + ->required() + ->default(config('backups.disks.s3.region')), + TextInput::make('AWS_ACCESS_KEY_ID') + ->label('Access Key ID') + ->required() + ->default(config('backups.disks.s3.key')), + TextInput::make('AWS_SECRET_ACCESS_KEY') + ->label('Secret Access Key') + ->required() + ->default(config('backups.disks.s3.secret')), + TextInput::make('AWS_BACKUPS_BUCKET') + ->label('Bucket') + ->required() + ->default(config('backups.disks.s3.bucket')), + TextInput::make('AWS_ENDPOINT') + ->label('Endpoint') + ->required() + ->default(config('backups.disks.s3.endpoint')), + Toggle::make('AWS_USE_PATH_STYLE_ENDPOINT') + ->label('Use path style endpoint?') + ->inline(false) + ->onIcon('tabler-check') + ->offIcon('tabler-x') + ->onColor('success') + ->offColor('danger') + ->live() + ->formatStateUsing(fn ($state): bool => (bool) $state) + ->afterStateUpdated(fn ($state, Set $set) => $set('AWS_USE_PATH_STYLE_ENDPOINT', (bool) $state)) + ->default(env('AWS_USE_PATH_STYLE_ENDPOINT', config('backups.disks.s3.use_path_style_endpoint'))), + ]), + ]; + } + + private function miscSettings(): array + { + return [ + Section::make('Automatic Allocation Creation') + ->description('Toggle if Users can create allocations via the client area.') + ->columns() + ->collapsible() + ->collapsed() + ->schema([ + Toggle::make('PANEL_CLIENT_ALLOCATIONS_ENABLED') + ->label('Allow Users to create allocations?') + ->onIcon('tabler-check') + ->offIcon('tabler-x') + ->onColor('success') + ->offColor('danger') + ->live() + ->columnSpanFull() + ->formatStateUsing(fn ($state): bool => (bool) $state) + ->afterStateUpdated(fn ($state, Set $set) => $set('PANEL_CLIENT_ALLOCATIONS_ENABLED', (bool) $state)) + ->default(env('PANEL_CLIENT_ALLOCATIONS_ENABLED', config('panel.client_features.allocations.enabled'))), + TextInput::make('PANEL_CLIENT_ALLOCATIONS_RANGE_START') + ->label('Starting Port') + ->required() + ->numeric() + ->minValue(1024) + ->maxValue(65535) + ->visible(fn (Get $get) => $get('PANEL_CLIENT_ALLOCATIONS_ENABLED')) + ->default(env('PANEL_CLIENT_ALLOCATIONS_RANGE_START')), + TextInput::make('PANEL_CLIENT_ALLOCATIONS_RANGE_END') + ->label('Ending Port') + ->required() + ->numeric() + ->minValue(1024) + ->maxValue(65535) + ->visible(fn (Get $get) => $get('PANEL_CLIENT_ALLOCATIONS_ENABLED')) + ->default(env('PANEL_CLIENT_ALLOCATIONS_RANGE_END')), + ]), + Section::make('Mail Notifications') + ->description('Toggle which mail notifications should be sent to Users.') + ->columns() + ->collapsible() + ->collapsed() + ->schema([ + Toggle::make('PANEL_SEND_INSTALL_NOTIFICATION') + ->label('Server Installed') + ->onIcon('tabler-check') + ->offIcon('tabler-x') + ->onColor('success') + ->offColor('danger') + ->live() + ->columnSpanFull() + ->formatStateUsing(fn ($state): bool => (bool) $state) + ->afterStateUpdated(fn ($state, Set $set) => $set('PANEL_SEND_INSTALL_NOTIFICATION', (bool) $state)) + ->default(env('PANEL_SEND_INSTALL_NOTIFICATION', config('panel.email.send_install_notification'))), + Toggle::make('PANEL_SEND_REINSTALL_NOTIFICATION') + ->label('Server Reinstalled') + ->onIcon('tabler-check') + ->offIcon('tabler-x') + ->onColor('success') + ->offColor('danger') + ->live() + ->columnSpanFull() + ->formatStateUsing(fn ($state): bool => (bool) $state) + ->afterStateUpdated(fn ($state, Set $set) => $set('PANEL_SEND_REINSTALL_NOTIFICATION', (bool) $state)) + ->default(env('PANEL_SEND_REINSTALL_NOTIFICATION', config('panel.email.send_reinstall_notification'))), + ]), + Section::make('Connections') + ->description('Timeouts used when making requests.') + ->columns() + ->collapsible() + ->collapsed() + ->schema([ + TextInput::make('GUZZLE_TIMEOUT') + ->label('Request Timeout') + ->required() + ->numeric() + ->minValue(15) + ->maxValue(60) + ->suffix('Seconds') + ->default(env('GUZZLE_TIMEOUT', config('panel.guzzle.timeout'))), + TextInput::make('GUZZLE_CONNECT_TIMEOUT') + ->label('Connect Timeout') + ->required() + ->numeric() + ->minValue(5) + ->maxValue(60) + ->suffix('Seconds') + ->default(env('GUZZLE_CONNECT_TIMEOUT', config('panel.guzzle.connect_timeout'))), + ]), + Section::make('Activity Logs') + ->description('Configure how often old activity logs should be pruned and whether admin activities should be logged.') + ->columns() + ->collapsible() + ->collapsed() + ->schema([ + TextInput::make('APP_ACTIVITY_PRUNE_DAYS') + ->label('Prune age') + ->required() + ->numeric() + ->minValue(1) + ->maxValue(365) + ->suffix('Days') + ->default(env('APP_ACTIVITY_PRUNE_DAYS', config('activity.prune_days'))), + Toggle::make('APP_ACTIVITY_HIDE_ADMIN') + ->label('Hide admin activities?') + ->inline(false) + ->onIcon('tabler-check') + ->offIcon('tabler-x') + ->onColor('success') + ->offColor('danger') + ->live() + ->formatStateUsing(fn ($state): bool => (bool) $state) + ->afterStateUpdated(fn ($state, Set $set) => $set('APP_ACTIVITY_HIDE_ADMIN', (bool) $state)) + ->default(env('APP_ACTIVITY_HIDE_ADMIN', config('activity.hide_admin_activity'))), + ]), + Section::make('API') + ->description('Defines the rate limit for the number of requests per minute that can be executed.') + ->columns() + ->collapsible() + ->collapsed() + ->schema([ + TextInput::make('APP_API_CLIENT_RATELIMIT') + ->label('Client API Rate Limit') + ->required() + ->numeric() + ->minValue(1) + ->suffix('Requests Per Minute') + ->default(env('APP_API_CLIENT_RATELIMIT', config('http.rate_limit.client'))), + TextInput::make('APP_API_APPLICATION_RATELIMIT') + ->label('Application API Rate Limit') + ->required() + ->numeric() + ->minValue(1) + ->suffix('Requests Per Minute') + ->default(env('APP_API_APPLICATION_RATELIMIT', config('http.rate_limit.application'))), + ]), + ]; + } + + protected function getFormStatePath(): ?string + { + return 'data'; + } + + protected function hasUnsavedDataChangesAlert(): bool + { + return true; + } + + public function save(): void + { + try { + $data = $this->form->getState(); + + $this->writeToEnvironment($data); + + Artisan::call('config:clear'); + Artisan::call('queue:restart'); + + $this->rememberData(); + + $this->redirect($this->getUrl()); + + Notification::make() + ->title('Settings saved') + ->success() + ->send(); + } catch (Exception $exception) { + Notification::make() + ->title('Save failed') + ->body($exception->getMessage()) + ->danger() + ->send(); + } + } + + protected function getHeaderActions(): array + { + return [ + Action::make('save') + ->action('save') + ->keyBindings(['mod+s']), + ]; + + } + protected function getFormActions(): array + { + return []; + } +} diff --git a/app/Http/Controllers/Admin/Settings/AdvancedController.php b/app/Http/Controllers/Admin/Settings/AdvancedController.php deleted file mode 100644 index def9124a8b..0000000000 --- a/app/Http/Controllers/Admin/Settings/AdvancedController.php +++ /dev/null @@ -1,56 +0,0 @@ - $showRecaptchaWarning, - ]); - } - - /** - * @throws \App\Exceptions\Model\DataValidationException - */ - public function update(AdvancedSettingsFormRequest $request): RedirectResponse - { - foreach ($request->normalize() as $key => $value) { - Setting::set('settings::' . $key, $value); - } - - $this->kernel->call('queue:restart'); - $this->alert->success('Advanced settings have been updated successfully and the queue worker was restarted to apply these changes.')->flash(); - - return redirect()->route('admin.settings.advanced'); - } -} diff --git a/app/Http/Controllers/Admin/Settings/IndexController.php b/app/Http/Controllers/Admin/Settings/IndexController.php deleted file mode 100644 index 47c5674582..0000000000 --- a/app/Http/Controllers/Admin/Settings/IndexController.php +++ /dev/null @@ -1,56 +0,0 @@ - $this->versionService, - 'languages' => $this->getAvailableLanguages(), - ]); - } - - /** - * Handle settings update. - * - * @throws \App\Exceptions\Model\DataValidationException - */ - public function update(BaseSettingsFormRequest $request): RedirectResponse - { - foreach ($request->normalize() as $key => $value) { - Setting::set('settings::' . $key, $value); - } - - $this->kernel->call('queue:restart'); - $this->alert->success('Panel settings have been updated successfully and the queue worker was restarted to apply these changes.')->flash(); - - return redirect()->route('admin.settings'); - } -} diff --git a/app/Http/Controllers/Admin/Settings/MailController.php b/app/Http/Controllers/Admin/Settings/MailController.php deleted file mode 100644 index 33aa5c31f2..0000000000 --- a/app/Http/Controllers/Admin/Settings/MailController.php +++ /dev/null @@ -1,82 +0,0 @@ - config('mail.default') !== 'smtp', - ]); - } - - /** - * Handle request to update SMTP mail settings. - * - * @throws DisplayException - * @throws \App\Exceptions\Model\DataValidationException - */ - public function update(MailSettingsFormRequest $request): Response - { - if (config('mail.default') !== 'smtp') { - throw new DisplayException('This feature is only available if SMTP is the selected email driver for the Panel.'); - } - - $values = $request->normalize(); - if (array_get($values, 'mail:mailers:smtp:password') === '!e') { - $values['mail:mailers:smtp:password'] = ''; - } - - foreach ($values as $key => $value) { - if (in_array($key, SettingsServiceProvider::getEncryptedKeys()) && !empty($value)) { - $value = encrypt($value); - } - - Setting::set('settings::' . $key, $value); - } - - $this->kernel->call('queue:restart'); - - return response('', 204); - } - - /** - * Submit a request to send a test mail message. - */ - public function test(Request $request): Response - { - try { - Notification::route('mail', $request->user()->email) - ->notify(new MailTested($request->user())); - } catch (\Exception $exception) { - return response($exception->getMessage(), 500); - } - - return response('', 204); - } -} diff --git a/app/Models/Setting.php b/app/Models/Setting.php index d25bd1b5d8..9efad2b080 100644 --- a/app/Models/Setting.php +++ b/app/Models/Setting.php @@ -24,62 +24,4 @@ class Setting extends Model 'key' => 'required|string|between:1,255', 'value' => 'string', ]; - - private static array $cache = []; - - private static array $databaseMiss = []; - - /** - * Store a new persistent setting in the database. - */ - public static function set(string $key, string $value = null): void - { - // Clear item from the cache. - self::clearCache($key); - - self::query()->updateOrCreate(['key' => $key], ['value' => $value ?? '']); - - self::$cache[$key] = $value; - } - - /** - * Retrieve a persistent setting from the database. - */ - public static function get(string $key, mixed $default = null): mixed - { - // If item has already been requested return it from the cache. If - // we already know it is missing, immediately return the default value. - if (array_key_exists($key, self::$cache)) { - return self::$cache[$key]; - } elseif (array_key_exists($key, self::$databaseMiss)) { - return value($default); - } - - $instance = self::query()->where('key', $key)->first(); - if (is_null($instance)) { - self::$databaseMiss[$key] = true; - - return value($default); - } - - return self::$cache[$key] = $instance->value; - } - - /** - * Remove a key from the database cache. - */ - public static function forget(string $key) - { - self::clearCache($key); - - return self::query()->where('key', $key)->delete(); - } - - /** - * Remove a key from the cache. - */ - private static function clearCache(string $key): void - { - unset(self::$cache[$key], self::$databaseMiss[$key]); - } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 7d5ba0c44b..384e4501d1 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -87,11 +87,6 @@ public function boot(): void */ public function register(): void { - // Only load the settings service provider if the environment is configured to allow it. - if (!config('panel.load_environment_only', false) && $this->app->environment() !== 'testing') { - $this->app->register(SettingsServiceProvider::class); - } - $this->app->singleton('extensions.themes', function () { return new Theme(); }); diff --git a/app/Providers/SettingsServiceProvider.php b/app/Providers/SettingsServiceProvider.php deleted file mode 100644 index 5c74124559..0000000000 --- a/app/Providers/SettingsServiceProvider.php +++ /dev/null @@ -1,112 +0,0 @@ -keys = array_merge($this->keys, $this->emailKeys); - } - - try { - $values = Setting::all()->mapWithKeys(function ($setting) { - return [$setting->key => $setting->value]; - })->toArray(); - } catch (QueryException $exception) { - $log->notice('A query exception was encountered while trying to load settings from the database: ' . $exception->getMessage()); - - return; - } - - foreach ($this->keys as $key) { - $value = array_get($values, 'settings::' . $key, config(str_replace(':', '.', $key))); - if (in_array($key, self::$encrypted)) { - try { - $value = decrypt($value); - } catch (Exception) { - // ignore - } - } - - switch (strtolower($value)) { - case 'true': - case '(true)': - $value = true; - break; - case 'false': - case '(false)': - $value = false; - break; - case 'empty': - case '(empty)': - $value = ''; - break; - case 'null': - case '(null)': - $value = null; - } - - config()->set(str_replace(':', '.', $key), $value); - } - } - - public static function getEncryptedKeys(): array - { - return self::$encrypted; - } -} diff --git a/config/app.php b/config/app.php index e8d4deb301..f9a4acc850 100644 --- a/config/app.php +++ b/config/app.php @@ -5,6 +5,7 @@ return [ 'name' => env('APP_NAME', 'Pelican'), + 'favicon' => env('APP_FAVICON', './pelican.ico'), 'version' => 'canary', diff --git a/config/panel.php b/config/panel.php index 7b049f1459..32b11bc3a2 100644 --- a/config/panel.php +++ b/config/panel.php @@ -1,18 +1,6 @@ (bool) env('APP_ENVIRONMENT_ONLY', false), - /* |-------------------------------------------------------------------------- | Authentication diff --git a/lang/en/commands.php b/lang/en/commands.php index a42ca228b4..3b9ab9f452 100644 --- a/lang/en/commands.php +++ b/lang/en/commands.php @@ -6,7 +6,6 @@ 'author' => 'Provide the email address that eggs exported by this Panel should be from. This should be a valid email address.', 'url' => 'The application URL MUST begin with https:// or http:// depending on if you are using SSL or not. If you do not include the scheme your emails and other content will link to the wrong location.', 'timezone' => "The timezone should match one of PHP\'s supported timezones. If you are unsure, please reference https://php.net/manual/en/timezones.php.", - 'settings_ui' => 'Enable UI based settings editor?', ], 'redis' => [ 'note' => 'You\'ve selected the Redis driver for one or more options, please provide valid connection information below. In most cases you can use the defaults provided unless you have modified your setup.', diff --git a/resources/views/admin/settings/advanced.blade.php b/resources/views/admin/settings/advanced.blade.php deleted file mode 100644 index dc5543ca9c..0000000000 --- a/resources/views/admin/settings/advanced.blade.php +++ /dev/null @@ -1,127 +0,0 @@ -@extends('layouts.admin') -@include('partials/admin.settings.nav', ['activeTab' => 'advanced']) - -@section('title') - Advanced Settings -@endsection - -@section('content-header') -

Advanced SettingsConfigure advanced settings for Panel.

- -@endsection - -@section('content') - @yield('settings::nav') -
-
-
-
-
-

reCAPTCHA

-
-
-
-
- -
- -

If enabled, login forms and password reset forms will do a silent captcha check and display a visible captcha if needed.

-
-
-
- -
- -
-
-
- -
- -

Used for communication between your site and Google. Be sure to keep it a secret.

-
-
-
- @if($showRecaptchaWarning) -
-
-
- You are currently using reCAPTCHA keys that were shipped with this Panel. For improved security it is recommended to generate new invisible reCAPTCHA keys that tied specifically to your website. -
-
-
- @endif -
-
-
-
-

HTTP Connections

-
-
-
-
- -
- -

The amount of time in seconds to wait for a connection to be opened before throwing an error.

-
-
-
- -
- -

The amount of time in seconds to wait for a request to be completed before throwing an error.

-
-
-
-
-
-
-
-

Automatic Allocation Creation

-
-
-
-
- -
- -

If enabled users will have the option to automatically create new allocations for their server via the frontend.

-
-
-
- -
- -

The starting port in the range that can be automatically allocated.

-
-
-
- -
- -

The ending port in the range that can be automatically allocated.

-
-
-
-
-
-
- -
-
-
-
-@endsection diff --git a/resources/views/admin/settings/index.blade.php b/resources/views/admin/settings/index.blade.php deleted file mode 100644 index 19356e8b8b..0000000000 --- a/resources/views/admin/settings/index.blade.php +++ /dev/null @@ -1,75 +0,0 @@ -@extends('layouts.admin') -@include('partials/admin.settings.nav', ['activeTab' => 'basic']) - -@section('title') - Settings -@endsection - -@section('content-header') -

Panel SettingsConfigure Panel to your liking.

- -@endsection - -@section('content') - @yield('settings::nav') -
-
-
-
-

Panel Settings

-
-
-
-
-
- -
- -

This is the name that is used throughout the panel and in emails sent to clients.

-
-
-
- -
-
- @php - $level = old('panel:auth:2fa_required', config('panel.auth.2fa_required')); - @endphp - - - -
-

If enabled, any account falling into the selected grouping will be required to have 2-Factor authentication enabled to use the Panel.

-
-
-
- -
- -

The default language to use when rendering UI components.

-
-
-
-
- -
-
-
-
-@endsection diff --git a/resources/views/admin/settings/mail.blade.php b/resources/views/admin/settings/mail.blade.php deleted file mode 100644 index 0488d1c7a0..0000000000 --- a/resources/views/admin/settings/mail.blade.php +++ /dev/null @@ -1,202 +0,0 @@ -@extends('layouts.admin') -@include('partials/admin.settings.nav', ['activeTab' => 'mail']) - -@section('title') - Mail Settings -@endsection - -@section('content-header') -

Mail SettingsConfigure how email sending should be handled.

- -@endsection - -@section('content') - @yield('settings::nav') -
-
-
-
-

Email Settings

-
- @if($disabled) -
-
-
-
- This interface is limited to instances using SMTP as the mail driver. Please either use php artisan p:environment:mail command to update your email settings, or set MAIL_DRIVER=smtp in your environment file. -
-
-
-
- @else -
-
-
-
- -
- -

Enter the SMTP server address that mail should be sent through.

-
-
-
- -
- -

Enter the SMTP server port that mail should be sent through.

-
-
-
- -
- @php - $encryption = old('mail:mailers:smtp:encryption', config('mail.mailers.smtp.encryption')); - @endphp - -

Select the type of encryption to use when sending mail.

-
-
-
- -
- -

The username to use when connecting to the SMTP server.

-
-
-
- -
- -

The password to use in conjunction with the SMTP username. Leave blank to continue using the existing password. To set the password to an empty value enter !e into the field.

-
-
-
-
-
-
- -
- -

Enter an email address that all outgoing emails will originate from.

-
-
-
- -
- -

The name that emails should appear to come from.

-
-
-
-
- -
- @endif -
-
-
-@endsection - -@section('footer-scripts') - @parent - - -@endsection diff --git a/resources/views/filament/pages/settings.blade.php b/resources/views/filament/pages/settings.blade.php new file mode 100644 index 0000000000..9f9b3f4391 --- /dev/null +++ b/resources/views/filament/pages/settings.blade.php @@ -0,0 +1,15 @@ + + + {{ $this->form }} + + + + diff --git a/resources/views/layouts/admin.blade.php b/resources/views/layouts/admin.blade.php index d48fad64bf..24b21db199 100644 --- a/resources/views/layouts/admin.blade.php +++ b/resources/views/layouts/admin.blade.php @@ -96,11 +96,6 @@
  • OTHER
  • -
  • - - Settings - -
  • Application API diff --git a/resources/views/partials/admin/settings/nav.blade.php b/resources/views/partials/admin/settings/nav.blade.php deleted file mode 100644 index 9f1ace7f31..0000000000 --- a/resources/views/partials/admin/settings/nav.blade.php +++ /dev/null @@ -1,16 +0,0 @@ -@include('partials/admin.settings.notice') - -@section('settings::nav') - @yield('settings::notice') -
    - -
    -@endsection diff --git a/resources/views/partials/admin/settings/notice.blade.php b/resources/views/partials/admin/settings/notice.blade.php deleted file mode 100644 index 980c5ef60d..0000000000 --- a/resources/views/partials/admin/settings/notice.blade.php +++ /dev/null @@ -1,11 +0,0 @@ -@section('settings::notice') - @if(config('panel.load_environment_only', false)) -
    -
    -
    - Your Panel is currently configured to read settings from the environment only. You will need to set APP_ENVIRONMENT_ONLY=false in your environment file in order to load settings dynamically. -
    -
    -
    - @endif -@endsection diff --git a/routes/admin.php b/routes/admin.php index 3786b876a1..60d047e01f 100644 --- a/routes/admin.php +++ b/routes/admin.php @@ -40,26 +40,6 @@ Route::delete('/view/{host:id}', [Admin\DatabaseController::class, 'delete']); }); -/* -|-------------------------------------------------------------------------- -| Settings Controller Routes -|-------------------------------------------------------------------------- -| -| Endpoint: /admin/settings -| -*/ -Route::prefix('settings')->group(function () { - Route::get('/', [Admin\Settings\IndexController::class, 'index'])->name('admin.settings'); - Route::get('/mail', [Admin\Settings\MailController::class, 'index'])->name('admin.settings.mail'); - Route::get('/advanced', [Admin\Settings\AdvancedController::class, 'index'])->name('admin.settings.advanced'); - - Route::post('/mail/test', [Admin\Settings\MailController::class, 'test'])->name('admin.settings.mail.test'); - - Route::patch('/', [Admin\Settings\IndexController::class, 'update']); - Route::patch('/mail', [Admin\Settings\MailController::class, 'update']); - Route::patch('/advanced', [Admin\Settings\AdvancedController::class, 'update']); -}); - /* |-------------------------------------------------------------------------- | User Controller Routes