14 changed files with 502 additions and 0 deletions
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
MIT License |
||||
|
||||
Copyright (c) 2020 Dcat Admin |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
of this software and associated documentation files (the "Software"), to deal |
||||
in the Software without restriction, including without limitation the rights |
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
copies of the Software, and to permit persons to whom the Software is |
||||
furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included in all |
||||
copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
SOFTWARE. |
@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
|
||||
<div align="center"> |
||||
<img src="http://www.dcatadmin.com/assets/img/logo-text.png" height="80"> |
||||
</div> |
||||
<br> |
||||
|
||||
|
||||
## Dcat Admin 操作日志扩展 |
||||
|
||||
### 安装 |
||||
|
||||
下载`zip`压缩包,打开扩展管理页面,点击`本地安装`按钮选择提交,然后找到`form-step`行点击`启用`按钮。 |
||||
|
@ -0,0 +1,31 @@
@@ -0,0 +1,31 @@
|
||||
{ |
||||
"name": "dcat-admin/operation-log", |
||||
"description": "Dcat Admin 操作日志扩展", |
||||
"type": "library", |
||||
"keywords": ["dcat-admin", "extension"], |
||||
"homepage": "https://github.com/dcat-admin/operation-log", |
||||
"license": "MIT", |
||||
"authors": [ |
||||
{ |
||||
"name": "Jiangqh", |
||||
"email": "841324345@qq.com" |
||||
} |
||||
], |
||||
"require": { |
||||
"php": ">=7.1.0", |
||||
"dcat/laravel-admin": "~2.0" |
||||
}, |
||||
"autoload": { |
||||
"psr-4": { |
||||
"Dcat\\Admin\\OperationLog\\": "src/" |
||||
} |
||||
}, |
||||
"extra": { |
||||
"dcat-admin": "Dcat\\Admin\\OperationLog\\OperationLogServiceProvider", |
||||
"laravel": { |
||||
"providers": [ |
||||
"Dcat\\Admin\\OperationLog\\OperationLogServiceProvider" |
||||
] |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
<?php |
||||
|
||||
return [ |
||||
'title' => 'Operation Log', |
||||
'setting_title' => 'Operation Log', |
||||
]; |
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
<?php |
||||
|
||||
return [ |
||||
'title' => '操作日志', |
||||
'setting_title' => '操作日志', |
||||
]; |
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
<?php |
||||
|
||||
return [ |
||||
'title' => '操作日志', |
||||
'setting_title' => '操作日志', |
||||
]; |
@ -0,0 +1,109 @@
@@ -0,0 +1,109 @@
|
||||
<?php |
||||
|
||||
namespace Dcat\Admin\OperationLog\Http\Controllers; |
||||
|
||||
use Dcat\Admin\Grid; |
||||
use Dcat\Admin\Http\JsonResponse; |
||||
use Dcat\Admin\Layout\Content; |
||||
use Dcat\Admin\OperationLog\Models\OperationLog; |
||||
use Dcat\Admin\OperationLog\OperationLogServiceProvider; |
||||
use Dcat\Admin\Support\Helper; |
||||
use Illuminate\Support\Arr; |
||||
|
||||
class LogController |
||||
{ |
||||
public function index(Content $content) |
||||
{ |
||||
return $content |
||||
->title(OperationLogServiceProvider::trans('log.title')) |
||||
->description(trans('admin.list')) |
||||
->body($this->grid()); |
||||
} |
||||
|
||||
protected function grid() |
||||
{ |
||||
return new Grid(OperationLog::with('user'), function (Grid $grid) { |
||||
$grid->column('id', 'ID')->sortable(); |
||||
$grid->column('user', trans('admin.user')) |
||||
->display(function ($user) { |
||||
if (! $user) { |
||||
return; |
||||
} |
||||
|
||||
$user = Helper::array($user); |
||||
|
||||
return $user['name'] ?? ($user['username'] ?? $user['id']); |
||||
}) |
||||
->link(function () { |
||||
if ($this->user) { |
||||
return admin_url('auth/users/'.$this->user['id']); |
||||
} |
||||
}); |
||||
|
||||
$grid->column('method', trans('admin.method')) |
||||
->label(OperationLog::$methodColors) |
||||
->filterByValue(); |
||||
|
||||
$grid->column('path', trans('admin.uri'))->display(function ($v) { |
||||
return "<code>$v</code>"; |
||||
})->filterByValue(); |
||||
|
||||
$grid->column('ip', 'IP')->filterByValue(); |
||||
|
||||
$grid->column('input')->display(function ($input) { |
||||
$input = json_decode($input, true); |
||||
|
||||
if (empty($input)) { |
||||
return; |
||||
} |
||||
|
||||
$input = Arr::except($input, ['_pjax', '_token', '_method', '_previous_']); |
||||
|
||||
if (empty($input)) { |
||||
return; |
||||
} |
||||
|
||||
return '<pre class="dump" style="max-width: 500px">'.json_encode($input, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE).'</pre>'; |
||||
}); |
||||
|
||||
$grid->column('created_at', trans('admin.created_at')); |
||||
|
||||
$grid->model()->orderBy('id', 'DESC'); |
||||
|
||||
$grid->disableCreateButton(); |
||||
$grid->disableQuickEditButton(); |
||||
$grid->disableEditButton(); |
||||
$grid->disableViewButton(); |
||||
$grid->showColumnSelector(); |
||||
$grid->setActionClass(Grid\Displayers\Actions::class); |
||||
|
||||
$grid->filter(function (Grid\Filter $filter) { |
||||
$userModel = config('admin.database.users_model'); |
||||
|
||||
$filter->in('user_id', trans('admin.user')) |
||||
->multipleSelect($userModel::pluck('name', 'id')); |
||||
|
||||
$filter->equal('method', trans('admin.method')) |
||||
->select( |
||||
array_combine(OperationLog::$methods, OperationLog::$methods) |
||||
); |
||||
|
||||
$filter->like('path', trans('admin.uri')); |
||||
$filter->equal('ip', 'IP'); |
||||
$filter->between('created_at')->datetime(); |
||||
}); |
||||
}); |
||||
} |
||||
|
||||
public function destroy($id) |
||||
{ |
||||
$ids = explode(',', $id); |
||||
|
||||
OperationLog::destroy(array_filter($ids)); |
||||
|
||||
return JsonResponse::make() |
||||
->success(trans('admin.delete_succeeded')) |
||||
->refresh() |
||||
->send(); |
||||
} |
||||
} |
@ -0,0 +1,160 @@
@@ -0,0 +1,160 @@
|
||||
<?php |
||||
|
||||
namespace Dcat\Admin\OperationLog\Http\Middleware; |
||||
|
||||
use Dcat\Admin\Admin; |
||||
use Dcat\Admin\OperationLog\Models\OperationLog as OperationLogModel; |
||||
use Dcat\Admin\OperationLog\OperationLogServiceProvider; |
||||
use Dcat\Admin\Support\Helper; |
||||
use Illuminate\Http\Request; |
||||
use Illuminate\Support\Str; |
||||
|
||||
class LogOperation |
||||
{ |
||||
protected $secretFields = [ |
||||
'password', |
||||
'password_confirmation', |
||||
]; |
||||
|
||||
protected $except = [ |
||||
'dcat-admin.operation-log.*', |
||||
]; |
||||
|
||||
protected $defaultAllowedMethods = ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT', 'OPTIONS', 'TRACE', 'PATCH']; |
||||
|
||||
/** |
||||
* Handle an incoming request. |
||||
* |
||||
* @param \Illuminate\Http\Request $request |
||||
* @param \Closure $next |
||||
* |
||||
* @return mixed |
||||
*/ |
||||
public function handle(Request $request, \Closure $next) |
||||
{ |
||||
if ($this->shouldLogOperation($request)) { |
||||
$user = Admin::user(); |
||||
|
||||
$log = [ |
||||
'user_id' => $user ? $user->id : 0, |
||||
'path' => substr($request->path(), 0, 255), |
||||
'method' => $request->method(), |
||||
'ip' => $request->getClientIp(), |
||||
'input' => $this->formatInput($request->input()), |
||||
]; |
||||
|
||||
try { |
||||
OperationLogModel::create($log); |
||||
} catch (\Exception $exception) { |
||||
// pass |
||||
} |
||||
} |
||||
|
||||
return $next($request); |
||||
} |
||||
|
||||
/** |
||||
* @param array $input |
||||
* |
||||
* @return string |
||||
*/ |
||||
protected function formatInput(array $input) |
||||
{ |
||||
foreach ($this->getSecretFields() as $field) { |
||||
if ($field && ! empty($input[$field])) { |
||||
$input[$field] = Str::limit($input[$field], 3, '******'); |
||||
} |
||||
} |
||||
|
||||
return json_encode($input, JSON_UNESCAPED_UNICODE); |
||||
} |
||||
|
||||
/** |
||||
* @param string $key |
||||
* @param mixed $default |
||||
* |
||||
* @return mixed |
||||
*/ |
||||
protected function setting($key, $default = null) |
||||
{ |
||||
return OperationLogServiceProvider::setting($key, $default); |
||||
} |
||||
|
||||
/** |
||||
* @param Request $request |
||||
* |
||||
* @return bool |
||||
*/ |
||||
protected function shouldLogOperation(Request $request) |
||||
{ |
||||
return ! $this->inExceptArray($request) |
||||
&& $this->inAllowedMethods($request->method()); |
||||
} |
||||
|
||||
/** |
||||
* Whether requests using this method are allowed to be logged. |
||||
* |
||||
* @param string $method |
||||
* |
||||
* @return bool |
||||
*/ |
||||
protected function inAllowedMethods($method) |
||||
{ |
||||
$allowedMethods = collect($this->getAllowedMethods())->filter(); |
||||
|
||||
if ($allowedMethods->isEmpty()) { |
||||
return true; |
||||
} |
||||
|
||||
return $allowedMethods->map(function ($method) { |
||||
return strtoupper($method); |
||||
})->contains($method); |
||||
} |
||||
|
||||
/** |
||||
* Determine if the request has a URI that should pass through CSRF verification. |
||||
* |
||||
* @param \Illuminate\Http\Request $request |
||||
* |
||||
* @return bool |
||||
*/ |
||||
protected function inExceptArray($request) |
||||
{ |
||||
if ($request->routeIs(admin_api_route_name('value'))) { |
||||
return true; |
||||
} |
||||
|
||||
foreach ($this->except() as $except) { |
||||
if ($request->routeIs($except)) { |
||||
return true; |
||||
} |
||||
|
||||
$except = admin_base_path($except); |
||||
|
||||
if ($except !== '/') { |
||||
$except = trim($except, '/'); |
||||
} |
||||
|
||||
if (Helper::matchRequestPath($except)) { |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
protected function except() |
||||
{ |
||||
return array_merge((array) $this->setting('except'), $this->except); |
||||
} |
||||
|
||||
protected function getSecretFields() |
||||
{ |
||||
return array_merge((array) $this->setting('secret_fields'), $this->secretFields); |
||||
} |
||||
|
||||
protected function getAllowedMethods() |
||||
{ |
||||
return (array) ($this->setting('allowed_methods') ?: $this->defaultAllowedMethods); |
||||
} |
||||
} |
@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
|
||||
<?php |
||||
|
||||
use Dcat\Admin\OperationLog\Http\Controllers; |
||||
use Illuminate\Support\Facades\Route; |
||||
|
||||
Route::get('auth/operation-logs', Controllers\LogController::class.'@index')->name('dcat-admin.operation-log.index'); |
||||
Route::delete('auth/operation-logs/{id}', Controllers\LogController::class.'@destroy')->name('dcat-admin.operation-log.destroy'); |
@ -0,0 +1,44 @@
@@ -0,0 +1,44 @@
|
||||
<?php |
||||
|
||||
namespace Dcat\Admin\OperationLog\Models; |
||||
|
||||
use Dcat\Admin\Traits\HasDateTimeFormatter; |
||||
use Illuminate\Database\Eloquent\Model; |
||||
|
||||
class OperationLog extends Model |
||||
{ |
||||
use HasDateTimeFormatter; |
||||
|
||||
protected $table = 'admin_operation_log'; |
||||
|
||||
protected $fillable = ['user_id', 'path', 'method', 'ip', 'input']; |
||||
|
||||
public static $methodColors = [ |
||||
'GET' => 'primary', |
||||
'POST' => 'success', |
||||
'PUT' => 'blue', |
||||
'DELETE' => 'danger', |
||||
]; |
||||
|
||||
public static $methods = [ |
||||
'GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'PATCH', |
||||
'LINK', 'UNLINK', 'COPY', 'HEAD', 'PURGE', |
||||
]; |
||||
|
||||
public function __construct(array $attributes = []) |
||||
{ |
||||
$this->connection = config('database.connection') ?: config('database.default'); |
||||
|
||||
parent::__construct($attributes); |
||||
} |
||||
|
||||
/** |
||||
* Log belongs to users. |
||||
* |
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo |
||||
*/ |
||||
public function user() |
||||
{ |
||||
return $this->belongsTo(config('admin.database.users_model')); |
||||
} |
||||
} |
@ -0,0 +1,27 @@
@@ -0,0 +1,27 @@
|
||||
<?php |
||||
|
||||
namespace Dcat\Admin\OperationLog; |
||||
|
||||
use Dcat\Admin\Extend\ServiceProvider; |
||||
use Dcat\Admin\OperationLog\Http\Middleware\LogOperation; |
||||
|
||||
class OperationLogServiceProvider extends ServiceProvider |
||||
{ |
||||
protected $middleware = [ |
||||
'middle' => [ |
||||
LogOperation::class, |
||||
], |
||||
]; |
||||
|
||||
protected $menu = [ |
||||
[ |
||||
'title' => 'Operation Log', |
||||
'uri' => 'auth/operation-logs', |
||||
], |
||||
]; |
||||
|
||||
public function settingForm() |
||||
{ |
||||
return new Setting($this); |
||||
} |
||||
} |
@ -0,0 +1,31 @@
@@ -0,0 +1,31 @@
|
||||
<?php |
||||
|
||||
namespace Dcat\Admin\OperationLog; |
||||
|
||||
use Dcat\Admin\Extend\Setting as Form; |
||||
use Dcat\Admin\OperationLog\Models\OperationLog; |
||||
use Dcat\Admin\Support\Helper; |
||||
|
||||
class Setting extends Form |
||||
{ |
||||
public function title() |
||||
{ |
||||
return $this->trans('log.title'); |
||||
} |
||||
|
||||
protected function formatInput(array $input) |
||||
{ |
||||
$input['except'] = Helper::array($input['except']); |
||||
$input['allowed_methods'] = Helper::array($input['allowed_methods']); |
||||
|
||||
return $input; |
||||
} |
||||
|
||||
public function form() |
||||
{ |
||||
$this->tags('except'); |
||||
$this->multipleSelect('allowed_methods') |
||||
->options(array_combine(OperationLog::$methods, OperationLog::$methods)); |
||||
$this->tags('secret_fields'); |
||||
} |
||||
} |
@ -0,0 +1,34 @@
@@ -0,0 +1,34 @@
|
||||
<?php |
||||
|
||||
use Illuminate\Database\Migrations\Migration; |
||||
use Illuminate\Database\Schema\Blueprint; |
||||
use Illuminate\Support\Facades\Schema; |
||||
|
||||
class CreateOperationLogTable extends Migration |
||||
{ |
||||
public function getConnection() |
||||
{ |
||||
return config('database.connection') ?: config('database.default'); |
||||
} |
||||
|
||||
public function up() |
||||
{ |
||||
if (! Schema::hasTable('admin_operation_log')) { |
||||
Schema::create('admin_operation_log', function (Blueprint $table) { |
||||
$table->bigIncrements('id')->unsigned(); |
||||
$table->bigInteger('user_id'); |
||||
$table->string('path'); |
||||
$table->string('method', 10); |
||||
$table->string('ip'); |
||||
$table->text('input'); |
||||
$table->index('user_id'); |
||||
$table->timestamps(); |
||||
}); |
||||
} |
||||
} |
||||
|
||||
public function down() |
||||
{ |
||||
Schema::dropIfExists('admin_operation_log'); |
||||
} |
||||
} |
Loading…
Reference in new issue