Compare commits
3 Commits
v0.9-rc54
...
c6f5b6cab8
| Author | SHA1 | Date | |
|---|---|---|---|
| c6f5b6cab8 | |||
|
|
8acc55a799 | ||
|
|
a5742a3156 |
8
AGENT.md
8
AGENT.md
@@ -620,7 +620,15 @@ All administrative actions are logged to the `audit_logs` table.
|
||||
- **USE Tailwind classes** - Only when absolutely necessary for minor adjustments
|
||||
- **MUST be responsive** - All pages must work on mobile, tablet, and desktop
|
||||
|
||||
### Warning Banners
|
||||
|
||||
- Use Filament `Section::make()` for warning banners (no raw HTML).
|
||||
- Always set `->icon('heroicon-o-exclamation-triangle')` and `->iconColor('warning')`.
|
||||
- Keep banners non-collapsible: `->collapsed(false)->collapsible(false)`.
|
||||
- Put the full message in `->description()` and keep the heading short.
|
||||
|
||||
### Allowed Components
|
||||
|
||||
Use these Filament native components exclusively:
|
||||
|
||||
| Category | Components |
|
||||
|
||||
@@ -575,7 +575,9 @@ class Security extends Page implements HasActions, HasForms, HasTable
|
||||
->visible(fn () => $this->fail2banRunning)
|
||||
->requiresConfirmation()
|
||||
->modalHeading(__('Disable Fail2ban'))
|
||||
->modalDescription(__('Fail2ban will be stopped and disabled. You can re-enable it later from this tab.'))
|
||||
->modalIcon('heroicon-o-exclamation-triangle')
|
||||
->modalIconColor('warning')
|
||||
->modalDescription(__('Warning: Fail2ban will be stopped and disabled. You can re-enable it later from this tab.'))
|
||||
->action('disableFail2ban'),
|
||||
])
|
||||
->schema([
|
||||
@@ -665,9 +667,12 @@ class Security extends Page implements HasActions, HasForms, HasTable
|
||||
->color(fn () => $this->clamavRunning ? 'warning' : 'success')
|
||||
->size('sm')
|
||||
->action(fn () => $this->clamavRunning ? $this->disableClamav() : $this->enableClamav())
|
||||
->requiresConfirmation(fn () => ! $this->clamavRunning)
|
||||
->requiresConfirmation()
|
||||
->modalHeading(fn () => $this->clamavRunning ? __('Disable ClamAV') : __('Enable ClamAV'))
|
||||
->modalIcon('heroicon-o-exclamation-triangle')
|
||||
->modalIconColor('warning')
|
||||
->modalDescription(fn () => $this->clamavRunning
|
||||
? __('ClamAV will be stopped and disabled. You can re-enable it later.')
|
||||
? __('Warning: This will stop and disable ClamAV. You can re-enable it later.')
|
||||
: __('Starting ClamAV daemon uses ~500MB RAM. Continue?')),
|
||||
FormAction::make('updateSignatures')
|
||||
->label(__('Update Signatures'))
|
||||
|
||||
@@ -692,17 +692,17 @@ class ServerSettings extends Page implements HasActions, HasForms
|
||||
protected function databaseTabContent(): array
|
||||
{
|
||||
return [
|
||||
Section::make(__('Warning: Changing database settings can impact performance or cause outages'))
|
||||
->description(__('Apply changes only if you understand their effects, and prefer doing so during maintenance windows.'))
|
||||
->icon('heroicon-o-exclamation-triangle')
|
||||
->iconColor('warning')
|
||||
->collapsed(false)
|
||||
->collapsible(false)
|
||||
->compact(),
|
||||
Section::make(__('Database Tuning'))
|
||||
->description(__('Adjust MariaDB/MySQL global variables.'))
|
||||
->icon('heroicon-o-circle-stack')
|
||||
->schema([
|
||||
Placeholder::make('database_tuning_warning')
|
||||
->content(new HtmlString(
|
||||
'<div class="rounded-lg bg-warning-500/10 p-4 text-sm text-warning-700 dark:text-warning-400">'.
|
||||
'<strong>'.__('Warning:').'</strong> '.
|
||||
__('Changing database settings can impact performance or cause outages. Apply changes only if you understand their effects, and prefer doing so during maintenance windows.').
|
||||
'</div>'
|
||||
)),
|
||||
EmbeddedTable::make(DatabaseTuningTable::class),
|
||||
]),
|
||||
];
|
||||
|
||||
@@ -208,7 +208,9 @@ class Services extends Page implements HasActions, HasForms, HasTable
|
||||
->visible(fn (array $record): bool => $record['is_active'])
|
||||
->requiresConfirmation()
|
||||
->modalHeading(__('Stop Service'))
|
||||
->modalDescription(fn (array $record): string => __('Are you sure you want to stop :service? This may affect running websites and services.', ['service' => $record['name']]))
|
||||
->modalIcon('heroicon-o-exclamation-triangle')
|
||||
->modalIconColor('warning')
|
||||
->modalDescription(fn (array $record): string => __('Warning: This will stop :service and may affect running websites and services. Are you sure you want to continue?', ['service' => $record['name']]))
|
||||
->modalSubmitActionLabel(__('Stop Service'))
|
||||
->action(fn (array $record) => $this->executeServiceAction($record['service'], 'stop')),
|
||||
Action::make('restart')
|
||||
@@ -236,7 +238,9 @@ class Services extends Page implements HasActions, HasForms, HasTable
|
||||
->visible(fn (array $record): bool => $record['is_enabled'])
|
||||
->requiresConfirmation()
|
||||
->modalHeading(__('Disable Service'))
|
||||
->modalDescription(fn (array $record): string => __("Are you sure you want to disable :service? It won't start automatically on boot.", ['service' => $record['name']]))
|
||||
->modalIcon('heroicon-o-exclamation-triangle')
|
||||
->modalIconColor('warning')
|
||||
->modalDescription(fn (array $record): string => __('Warning: This will disable :service and it will not start automatically on boot. Are you sure you want to continue?', ['service' => $record['name']]))
|
||||
->modalSubmitActionLabel(__('Disable Service'))
|
||||
->action(fn (array $record) => $this->executeServiceAction($record['service'], 'disable')),
|
||||
])
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Jabali\Pages;
|
||||
|
||||
use App\Models\AuditLog;
|
||||
use App\Filament\Jabali\Widgets\ActivityLogTable;
|
||||
use App\Services\Agent\AgentClient;
|
||||
use BackedEnum;
|
||||
use Filament\Actions\Action;
|
||||
@@ -16,6 +16,7 @@ use Filament\Notifications\Notification;
|
||||
use Filament\Pages\Page;
|
||||
use Filament\Schemas\Components\Tabs;
|
||||
use Filament\Schemas\Components\Tabs\Tab;
|
||||
use Filament\Schemas\Components\EmbeddedTable;
|
||||
use Filament\Schemas\Components\View;
|
||||
use Filament\Schemas\Schema;
|
||||
use Illuminate\Contracts\Support\Htmlable;
|
||||
@@ -119,7 +120,7 @@ class Logs extends Page implements HasActions, HasForms
|
||||
'activity' => Tab::make(__('Activity Log'))
|
||||
->icon('heroicon-o-clipboard-document-list')
|
||||
->schema([
|
||||
View::make('filament.jabali.pages.logs-tab-activity'),
|
||||
EmbeddedTable::make(ActivityLogTable::class),
|
||||
]),
|
||||
]),
|
||||
]);
|
||||
@@ -227,15 +228,6 @@ class Logs extends Page implements HasActions, HasForms
|
||||
->send();
|
||||
}
|
||||
|
||||
public function getActivityLogs()
|
||||
{
|
||||
return AuditLog::query()
|
||||
->where('user_id', Auth::id())
|
||||
->latest()
|
||||
->limit(50)
|
||||
->get();
|
||||
}
|
||||
|
||||
public function generateStats(): void
|
||||
{
|
||||
if (! $this->selectedDomain) {
|
||||
|
||||
79
app/Filament/Jabali/Widgets/ActivityLogTable.php
Normal file
79
app/Filament/Jabali/Widgets/ActivityLogTable.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Jabali\Widgets;
|
||||
|
||||
use App\Models\AuditLog;
|
||||
use Filament\Actions\Concerns\InteractsWithActions;
|
||||
use Filament\Actions\Contracts\HasActions;
|
||||
use Filament\Schemas\Concerns\InteractsWithSchemas;
|
||||
use Filament\Schemas\Contracts\HasSchemas;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Concerns\InteractsWithTable;
|
||||
use Filament\Tables\Contracts\HasTable;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Livewire\Component;
|
||||
|
||||
class ActivityLogTable extends Component implements HasTable, HasSchemas, HasActions
|
||||
{
|
||||
use InteractsWithTable;
|
||||
use InteractsWithSchemas;
|
||||
use InteractsWithActions;
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->query(
|
||||
AuditLog::query()
|
||||
->where('user_id', Auth::id())
|
||||
->latest()
|
||||
)
|
||||
->columns([
|
||||
TextColumn::make('created_at')
|
||||
->label(__('Time'))
|
||||
->dateTime('M d, H:i')
|
||||
->color('gray'),
|
||||
TextColumn::make('category')
|
||||
->label(__('Category'))
|
||||
->badge()
|
||||
->color(fn (string $state): string => match ($state) {
|
||||
'domain' => 'info',
|
||||
'email' => 'primary',
|
||||
'database' => 'warning',
|
||||
'auth' => 'gray',
|
||||
'firewall' => 'danger',
|
||||
'service' => 'success',
|
||||
default => 'gray',
|
||||
}),
|
||||
TextColumn::make('action')
|
||||
->label(__('Action'))
|
||||
->badge()
|
||||
->color(fn (string $state): string => match ($state) {
|
||||
'create', 'created' => 'success',
|
||||
'update', 'updated' => 'warning',
|
||||
'delete', 'deleted' => 'danger',
|
||||
'login' => 'info',
|
||||
default => 'gray',
|
||||
}),
|
||||
TextColumn::make('description')
|
||||
->label(__('Description'))
|
||||
->limit(60)
|
||||
->wrap(),
|
||||
TextColumn::make('ip_address')
|
||||
->label(__('IP'))
|
||||
->color('gray'),
|
||||
])
|
||||
->defaultPaginationPageOption(25)
|
||||
->striped()
|
||||
->emptyStateHeading(__('No activity recorded yet'))
|
||||
->emptyStateDescription(__('Recent actions performed in your account will appear here.'))
|
||||
->emptyStateIcon('heroicon-o-clipboard-document-list');
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return $this->getTable()->render();
|
||||
}
|
||||
}
|
||||
@@ -185,3 +185,5 @@ class BackupSchedule extends Model
|
||||
return $timezone;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"fakerphp/faker": "^1.23",
|
||||
"filament/blueprint": "^2.0",
|
||||
"filament/blueprint": "^2.1",
|
||||
"laravel/boost": "*",
|
||||
"laravel/pail": "^1.2.2",
|
||||
"laravel/pint": "^1.24",
|
||||
|
||||
8
composer.lock
generated
8
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "194d87cc129a30c6e832109fb820097a",
|
||||
"content-hash": "7083b0b087c4b503b50d3aa23cfbbfac",
|
||||
"packages": [
|
||||
{
|
||||
"name": "anourvalar/eloquent-serialize",
|
||||
@@ -8817,14 +8817,14 @@
|
||||
},
|
||||
{
|
||||
"name": "filament/blueprint",
|
||||
"version": "v2.0.1",
|
||||
"version": "v2.1.0",
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://packages.filamentphp.com/composer/10/127/download"
|
||||
"url": "https://packages.filamentphp.com/composer/10/473/download"
|
||||
},
|
||||
"require": {
|
||||
"filament/support": "^5.0",
|
||||
"laravel/boost": "^1.8"
|
||||
"laravel/boost": "^1.8|^2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"license": [
|
||||
|
||||
@@ -16,7 +16,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
if [[ -f "$SCRIPT_DIR/VERSION" ]]; then
|
||||
JABALI_VERSION="$(sed -n 's/^VERSION=//p' "$SCRIPT_DIR/VERSION")"
|
||||
fi
|
||||
JABALI_VERSION="${JABALI_VERSION:-0.9-rc54}"
|
||||
JABALI_VERSION="${JABALI_VERSION:-0.9-rc56}"
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
<x-filament::section class="mt-4" icon="heroicon-o-clipboard-document-list">
|
||||
<x-slot name="heading">{{ __('Activity Log') }}</x-slot>
|
||||
<x-slot name="description">{{ __('Recent actions performed in your account.') }}</x-slot>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-800">
|
||||
<tr>
|
||||
<th class="px-4 py-3 text-left fi-section-header-heading">{{ __('Time') }}</th>
|
||||
<th class="px-4 py-3 text-left fi-section-header-heading">{{ __('Category') }}</th>
|
||||
<th class="px-4 py-3 text-left fi-section-header-heading">{{ __('Action') }}</th>
|
||||
<th class="px-4 py-3 text-left fi-section-header-heading">{{ __('Description') }}</th>
|
||||
<th class="px-4 py-3 text-left fi-section-header-heading">{{ __('IP') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
@forelse($this->getActivityLogs() as $log)
|
||||
<tr>
|
||||
<td class="px-4 py-3 fi-section-header-description">{{ $log->created_at?->format('Y-m-d H:i') }}</td>
|
||||
<td class="px-4 py-3 fi-section-header-description">{{ $log->category }}</td>
|
||||
<td class="px-4 py-3 fi-section-header-description">{{ $log->action }}</td>
|
||||
<td class="px-4 py-3 fi-section-header-description">{{ $log->description }}</td>
|
||||
<td class="px-4 py-3 fi-section-header-description">{{ $log->ip_address }}</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="5" class="px-4 py-6 text-center fi-section-header-description">
|
||||
{{ __('No activity recorded yet.') }}
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</x-filament::section>
|
||||
Reference in New Issue
Block a user