mirror of
https://github.com/pelican-dev/panel.git
synced 2026-07-15 19:53:59 +03:00
wip
This commit is contained in:
@@ -3,9 +3,10 @@
|
||||
namespace App\Eloquent;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @template TModel of \Illuminate\Database\Eloquent\Model
|
||||
* @template TModel of Model
|
||||
*
|
||||
* @extends Builder<TModel>
|
||||
*/
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Contracts\Validatable;
|
||||
use App\Exceptions\Service\Egg\HasChildrenException;
|
||||
use App\Exceptions\Service\HasActiveServersException;
|
||||
use App\Models\Traits\HasIcon;
|
||||
use App\Traits\HasAdminActivityLogging;
|
||||
use App\Traits\HasValidation;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
@@ -93,6 +94,7 @@ use Illuminate\Support\Str;
|
||||
*/
|
||||
class Egg extends Model implements Validatable
|
||||
{
|
||||
use HasAdminActivityLogging;
|
||||
use HasFactory;
|
||||
use HasIcon;
|
||||
use HasValidation;
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Models;
|
||||
use App\Contracts\Validatable;
|
||||
use App\Exceptions\Service\HasActiveServersException;
|
||||
use App\Repositories\Daemon\DaemonSystemRepository;
|
||||
use App\Traits\HasAdminActivityLogging;
|
||||
use App\Traits\HasValidation;
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
@@ -95,6 +96,7 @@ use Symfony\Component\Yaml\Yaml;
|
||||
*/
|
||||
class Node extends Model implements Validatable
|
||||
{
|
||||
use HasAdminActivityLogging;
|
||||
use HasFactory;
|
||||
use HasValidation;
|
||||
use Notifiable;
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Models;
|
||||
use App\Enums\RolePermissionModels;
|
||||
use App\Enums\RolePermissionPrefixes;
|
||||
use App\Enums\TablerIcon;
|
||||
use App\Traits\HasAdminActivityLogging;
|
||||
use BackedEnum;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
@@ -41,6 +42,7 @@ use Spatie\Permission\Models\Role as BaseRole;
|
||||
*/
|
||||
class Role extends BaseRole
|
||||
{
|
||||
use HasAdminActivityLogging;
|
||||
use HasFactory;
|
||||
|
||||
public const RESOURCE_NAME = 'role';
|
||||
|
||||
@@ -10,6 +10,7 @@ use App\Exceptions\Http\Server\ServerStateConflictException;
|
||||
use App\Models\Traits\HasIcon;
|
||||
use App\Repositories\Daemon\DaemonServerRepository;
|
||||
use App\Services\Subusers\SubuserDeletionService;
|
||||
use App\Traits\HasAdminActivityLogging;
|
||||
use App\Traits\HasValidation;
|
||||
use Carbon\CarbonInterface;
|
||||
use Exception;
|
||||
@@ -129,6 +130,7 @@ use Psr\Http\Message\ResponseInterface;
|
||||
*/
|
||||
class Server extends Model implements HasAvatar, Validatable
|
||||
{
|
||||
use HasAdminActivityLogging;
|
||||
use HasFactory;
|
||||
use HasIcon;
|
||||
use HasValidation;
|
||||
|
||||
@@ -9,6 +9,7 @@ use App\Events\User\Deleting;
|
||||
use App\Exceptions\DisplayException;
|
||||
use App\Extensions\Avatar\AvatarService;
|
||||
use App\Models\Traits\HasAccessTokens;
|
||||
use App\Traits\HasAdminActivityLogging;
|
||||
use App\Traits\HasValidation;
|
||||
use BackedEnum;
|
||||
use DateTimeZone;
|
||||
@@ -119,6 +120,7 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
|
||||
use Authorizable { can as protected canned; }
|
||||
use CanResetPassword;
|
||||
use HasAccessTokens;
|
||||
use HasAdminActivityLogging;
|
||||
use HasFactory;
|
||||
use HasRoles;
|
||||
use HasValidation { getRules as getValidationRules; }
|
||||
@@ -538,4 +540,9 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
|
||||
{
|
||||
$this->update(['mfa_email_enabled' => $condition]);
|
||||
}
|
||||
|
||||
public function getAdminActivityName(): string
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,9 @@
|
||||
namespace App\Observers;
|
||||
|
||||
use App\Facades\Activity;
|
||||
use App\Models\Egg;
|
||||
use App\Models\Node;
|
||||
use App\Models\Role;
|
||||
use App\Models\Server;
|
||||
use App\Models\User;
|
||||
use Filament\Facades\Filament;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class AdminActivityObserver
|
||||
{
|
||||
@@ -49,7 +45,7 @@ class AdminActivityObserver
|
||||
}
|
||||
|
||||
// Deduplicate identical events for the same record within a single request.
|
||||
$key = $event . ':' . get_class($model) . ':' . $model->getKey();
|
||||
$key = $event . ':' . $model::class . ':' . $model->getKey();
|
||||
if (isset(self::$logged[$key])) {
|
||||
return;
|
||||
}
|
||||
@@ -65,135 +61,66 @@ class AdminActivityObserver
|
||||
|
||||
$log->log();
|
||||
}
|
||||
// -------------------------------------------------------------------------
|
||||
// User events
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public function userCreated(User $user): void
|
||||
public function created(Model $model): void
|
||||
{
|
||||
$this->log('admin:user.create', $user, ['name' => $user->username]);
|
||||
$this->log($this->eventFor($model, 'create'), $model, [
|
||||
'name' => $this->displayNameFor($model),
|
||||
]);
|
||||
}
|
||||
|
||||
public function userUpdated(User $user): void
|
||||
public function updated(Model $model): void
|
||||
{
|
||||
$changedFields = $this->changedFieldsFor($user);
|
||||
$changedFields = $this->changedFieldsFor($model);
|
||||
$name = $this->displayNameFor($model);
|
||||
|
||||
$this->log('admin:user.update', $user, [
|
||||
'name' => empty($changedFields) ? $user->username : sprintf('%s (%s)', $user->username, implode(', ', $changedFields)),
|
||||
$this->log($this->eventFor($model, 'update'), $model, [
|
||||
'name' => empty($changedFields) ? $name : sprintf('%s (%s)', $name, implode(', ', $changedFields)),
|
||||
'count' => count($changedFields),
|
||||
'changes' => implode(', ', $changedFields),
|
||||
]);
|
||||
}
|
||||
|
||||
public function userDeleted(User $user): void
|
||||
public function deleted(Model $model): void
|
||||
{
|
||||
$this->log('admin:user.delete', $user, ['name' => $user->username]);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Server events
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public function serverCreated(Server $server): void
|
||||
{
|
||||
$this->log('admin:server.create', $server, ['name' => $server->name]);
|
||||
}
|
||||
|
||||
public function serverUpdated(Server $server): void
|
||||
{
|
||||
$changedFields = $this->changedFieldsFor($server);
|
||||
|
||||
$this->log('admin:server.update', $server, [
|
||||
'name' => empty($changedFields) ? $server->name : sprintf('%s (%s)', $server->name, implode(', ', $changedFields)),
|
||||
'count' => count($changedFields),
|
||||
'changes' => implode(', ', $changedFields),
|
||||
$this->log($this->eventFor($model, 'delete'), $model, [
|
||||
'name' => $this->displayNameFor($model),
|
||||
]);
|
||||
}
|
||||
|
||||
public function serverDeleted(Server $server): void
|
||||
{
|
||||
$this->log('admin:server.delete', $server, ['name' => $server->name]);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Node events
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public function nodeCreated(Node $node): void
|
||||
{
|
||||
$this->log('admin:node.create', $node, ['name' => $node->name]);
|
||||
}
|
||||
|
||||
public function nodeUpdated(Node $node): void
|
||||
{
|
||||
$changedFields = $this->changedFieldsFor($node);
|
||||
|
||||
$this->log('admin:node.update', $node, [
|
||||
'name' => empty($changedFields) ? $node->name : sprintf('%s (%s)', $node->name, implode(', ', $changedFields)),
|
||||
'count' => count($changedFields),
|
||||
'changes' => implode(', ', $changedFields),
|
||||
]);
|
||||
}
|
||||
|
||||
public function nodeDeleted(Node $node): void
|
||||
{
|
||||
$this->log('admin:node.delete', $node, ['name' => $node->name]);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Egg events
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public function eggCreated(Egg $egg): void
|
||||
{
|
||||
$this->log('admin:egg.create', $egg, ['name' => $egg->name]);
|
||||
}
|
||||
|
||||
public function eggUpdated(Egg $egg): void
|
||||
{
|
||||
$changedFields = $this->changedFieldsFor($egg);
|
||||
|
||||
$this->log('admin:egg.update', $egg, [
|
||||
'name' => empty($changedFields) ? $egg->name : sprintf('%s (%s)', $egg->name, implode(', ', $changedFields)),
|
||||
'count' => count($changedFields),
|
||||
'changes' => implode(', ', $changedFields),
|
||||
]);
|
||||
}
|
||||
|
||||
public function eggDeleted(Egg $egg): void
|
||||
{
|
||||
$this->log('admin:egg.delete', $egg, ['name' => $egg->name]);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Role events
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public function roleCreated(Role $role): void
|
||||
{
|
||||
$this->log('admin:role.create', $role, ['name' => $role->name]);
|
||||
}
|
||||
|
||||
public function roleUpdated(Role $role): void
|
||||
{
|
||||
$changedFields = $this->changedFieldsFor($role);
|
||||
|
||||
$this->log('admin:role.update', $role, [
|
||||
'name' => empty($changedFields) ? $role->name : sprintf('%s (%s)', $role->name, implode(', ', $changedFields)),
|
||||
'count' => count($changedFields),
|
||||
'changes' => implode(', ', $changedFields),
|
||||
]);
|
||||
}
|
||||
|
||||
public function roleDeleted(Role $role): void
|
||||
{
|
||||
$this->log('admin:role.delete', $role, ['name' => $role->name]);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private function eventFor(Model $model, string $action): string
|
||||
{
|
||||
return sprintf('admin:%s.%s', $this->resourceNameFor($model), $action);
|
||||
}
|
||||
|
||||
private function resourceNameFor(Model $model): string
|
||||
{
|
||||
$constant = $model::class . '::RESOURCE_NAME';
|
||||
|
||||
if (defined($constant)) {
|
||||
$value = constant($constant);
|
||||
|
||||
if (is_string($value) && $value !== '') {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
return Str::of(class_basename($model))->snake()->toString();
|
||||
}
|
||||
|
||||
private function displayNameFor(Model $model): string
|
||||
{
|
||||
if (method_exists($model, 'getAdminActivityName')) {
|
||||
return $model->getAdminActivityName();
|
||||
}
|
||||
|
||||
return (string) $model->getKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sorted list of attribute names that changed on the given model,
|
||||
* excluding internal timestamps.
|
||||
|
||||
@@ -18,13 +18,11 @@ use App\Models\Database;
|
||||
use App\Models\Egg;
|
||||
use App\Models\EggVariable;
|
||||
use App\Models\Node;
|
||||
use App\Models\Role;
|
||||
use App\Models\Schedule;
|
||||
use App\Models\Server;
|
||||
use App\Models\Task;
|
||||
use App\Models\User;
|
||||
use App\Models\UserSSHKey;
|
||||
use App\Observers\AdminActivityObserver;
|
||||
use App\Services\Helpers\PluginService;
|
||||
use App\Services\Helpers\SoftwareVersionService;
|
||||
use Dedoc\Scramble\Scramble;
|
||||
@@ -111,23 +109,6 @@ class AppServiceProvider extends ServiceProvider
|
||||
]);
|
||||
}
|
||||
|
||||
$observer = new AdminActivityObserver();
|
||||
User::created(fn (User $user) => $observer->userCreated($user));
|
||||
User::updated(fn (User $user) => $observer->userUpdated($user));
|
||||
User::deleted(fn (User $user) => $observer->userDeleted($user));
|
||||
Server::created(fn (Server $server) => $observer->serverCreated($server));
|
||||
Server::updated(fn (Server $server) => $observer->serverUpdated($server));
|
||||
Server::deleted(fn (Server $server) => $observer->serverDeleted($server));
|
||||
Node::created(fn (Node $node) => $observer->nodeCreated($node));
|
||||
Node::updated(fn (Node $node) => $observer->nodeUpdated($node));
|
||||
Node::deleted(fn (Node $node) => $observer->nodeDeleted($node));
|
||||
Egg::created(fn (Egg $egg) => $observer->eggCreated($egg));
|
||||
Egg::updated(fn (Egg $egg) => $observer->eggUpdated($egg));
|
||||
Egg::deleted(fn (Egg $egg) => $observer->eggDeleted($egg));
|
||||
Role::created(fn (Role $role) => $observer->roleCreated($role));
|
||||
Role::updated(fn (Role $role) => $observer->roleUpdated($role));
|
||||
Role::deleted(fn (Role $role) => $observer->roleDeleted($role));
|
||||
|
||||
Gate::before(fn (User $user, $ability) => $user->isRootAdmin() ? true : null);
|
||||
|
||||
AboutCommand::add('Pelican', [
|
||||
|
||||
26
app/Traits/HasAdminActivityLogging.php
Normal file
26
app/Traits/HasAdminActivityLogging.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Traits;
|
||||
|
||||
use App\Observers\AdminActivityObserver;
|
||||
|
||||
trait HasAdminActivityLogging
|
||||
{
|
||||
public static function bootHasAdminActivityLogging(): void
|
||||
{
|
||||
$observer = new AdminActivityObserver();
|
||||
|
||||
static::created(fn ($model) => $observer->created($model));
|
||||
static::updated(fn ($model) => $observer->updated($model));
|
||||
static::deleted(fn ($model) => $observer->deleted($model));
|
||||
}
|
||||
|
||||
public function getAdminActivityName(): string
|
||||
{
|
||||
if (isset($this->attributes['name'])) {
|
||||
return (string) $this->attributes['name'];
|
||||
}
|
||||
|
||||
return (string) $this->getKey();
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
return [
|
||||
'empty_table' => 'Yay! No Errors!',
|
||||
'empty_audit_log' => 'No admin actions have been logged yet.',
|
||||
'model_label' => 'Admin Audit Log',
|
||||
'model_label_plural' => 'Admin Audit Logs',
|
||||
'total_logs' => 'Total Logs',
|
||||
'error' => 'Error',
|
||||
'warning' => 'Warning',
|
||||
@@ -10,6 +13,15 @@ return [
|
||||
'debug' => 'Debug',
|
||||
'navigation' => [
|
||||
'panel_logs' => 'Panel Logs',
|
||||
'admin_audit_log' => 'Admin Audit',
|
||||
],
|
||||
'table' => [
|
||||
'actor' => 'Actor',
|
||||
'event' => 'Event',
|
||||
'description' => 'Description',
|
||||
'ip' => 'IP Address',
|
||||
'timestamp' => 'Time',
|
||||
'system' => 'System',
|
||||
],
|
||||
'actions' => [
|
||||
'upload_logs' => 'Upload Logs?',
|
||||
|
||||
Reference in New Issue
Block a user