#591 - monitoring

This commit is contained in:
Saeed Vaziry
2025-05-31 00:18:04 +02:00
parent 857319025f
commit c09c7a63fa
32 changed files with 1692 additions and 117 deletions

View File

@ -7,6 +7,7 @@
use Illuminate\Contracts\Database\Query\Expression;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
class GetMetrics
@ -17,8 +18,13 @@ class GetMetrics
*/
public function filter(Server $server, array $input): Collection
{
if (isset($input['from']) && isset($input['to']) && $input['from'] === $input['to']) {
Validator::make($input, self::rules($input))->validate();
if (isset($input['from'])) {
$input['from'] = Carbon::parse($input['from'])->format('Y-m-d').' 00:00:00';
}
if (isset($input['to'])) {
$input['to'] = Carbon::parse($input['to'])->format('Y-m-d').' 23:59:59';
}
@ -145,8 +151,8 @@ public static function rules(array $input): array
];
if (isset($input['period']) && $input['period'] === 'custom') {
$rules['from'] = ['required', 'date', 'before:to'];
$rules['to'] = ['required', 'date', 'after:from'];
$rules['from'] = ['required', 'date', 'before_or_equal:to'];
$rules['to'] = ['required', 'date', 'after_or_equal:from'];
}
return $rules;

View File

@ -5,6 +5,7 @@
use App\Models\Server;
use App\Models\Service;
use App\SSH\Services\ServiceInterface;
use Illuminate\Support\Facades\Validator;
class UpdateMetricSettings
{
@ -13,6 +14,8 @@ class UpdateMetricSettings
*/
public function update(Server $server, array $input): void
{
Validator::make($input, self::rules())->validate();
/** @var Service $service */
$service = $server->monitoring();
/** @var ServiceInterface $handler */

View File

@ -0,0 +1,95 @@
<?php
namespace App\Http\Controllers;
use App\Actions\Monitoring\GetMetrics;
use App\Actions\Monitoring\UpdateMetricSettings;
use App\Enums\ServiceStatus;
use App\Models\Metric;
use App\Models\Server;
use App\Models\Service;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Inertia\Inertia;
use Inertia\Response;
use Spatie\RouteAttributes\Attributes\Delete;
use Spatie\RouteAttributes\Attributes\Get;
use Spatie\RouteAttributes\Attributes\Middleware;
use Spatie\RouteAttributes\Attributes\Patch;
use Spatie\RouteAttributes\Attributes\Prefix;
#[Prefix('servers/{server}/monitoring')]
#[Middleware(['auth', 'has-project'])]
class MonitoringController extends Controller
{
#[Get('/', name: 'monitoring')]
public function index(Server $server): Response
{
$this->authorize('viewAny', [Metric::class, $server]);
return Inertia::render('monitoring/index', [
'lastMetric' => $server->metrics()->latest()->first(),
'dataRetention' => $server->monitoring()?->type_data['data_retention'] ?? 30,
'hasMonitoringService' => $server->monitoring()?->status === ServiceStatus::READY,
]);
}
#[Get('/json', name: 'monitoring.json')]
public function json(Request $request, Server $server): JsonResponse
{
$this->authorize('viewAny', [Metric::class, $server]);
$metrics = app(GetMetrics::class)->filter($server, $request->input());
return response()->json($metrics);
}
#[Get('/{metric}', name: 'monitoring.show')]
public function show(Server $server, string $metric): Response
{
if (! in_array($metric, ['load', 'memory', 'disk'])) {
abort(404);
}
$this->authorize('viewAny', [Metric::class, $server]);
return Inertia::render('monitoring/show', [
'metric' => $metric,
]);
}
#[Patch('/update', name: 'monitoring.update')]
public function update(Request $request, Server $server): RedirectResponse
{
/** @var ?Service $monitoring */
$monitoring = $server->monitoring();
if (! $monitoring) {
abort(404);
}
$this->authorize('update', $monitoring);
app(UpdateMetricSettings::class)->update($server, $request->input());
return back()->with('success', 'Settings updated!');
}
#[Delete('/reset', name: 'monitoring.destroy')]
public function destroy(Server $server): RedirectResponse
{
/** @var ?Service $monitoring */
$monitoring = $server->monitoring();
if (! $monitoring) {
abort(404);
}
$this->authorize('update', $monitoring);
$server->metrics()->delete();
return back()->with('success', 'All metrics deleted!');
}
}

View File

@ -3,6 +3,7 @@
namespace App\Models;
use Carbon\Carbon;
use Database\Factories\MetricFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -29,7 +30,7 @@
*/
class Metric extends Model
{
/** @use HasFactory<\Database\Factories\MetricFactory> */
/** @use HasFactory<MetricFactory> */
use HasFactory;
protected $fillable = [

View File

@ -2,6 +2,7 @@
namespace App\Models;
use Database\Factories\ServerLogFactory;
use Exception;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
@ -25,7 +26,7 @@
*/
class ServerLog extends AbstractModel
{
/** @use HasFactory<\Database\Factories\ServerLogFactory> */
/** @use HasFactory<ServerLogFactory> */
use HasFactory;
protected $fillable = [
@ -103,6 +104,10 @@ public function download(): StreamedResponse
return Storage::disk('local')->download($tmpName, str($this->name)->afterLast('/'));
}
if (! Storage::disk($this->disk)->exists($this->name)) {
abort(404, "Log file doesn't exist or is empty!");
}
return Storage::disk($this->disk)->download($this->name);
}
@ -114,7 +119,7 @@ public static function getRemote(Builder $query, bool $active = true, ?Site $sit
{
$query->where('is_remote', $active);
if ($site instanceof \App\Models\Site) {
if ($site instanceof Site) {
$query->where('name', 'like', $site->path.'%');
}

View File

@ -10,6 +10,7 @@
use App\SSH\Services\ProcessManager\ProcessManager;
use App\SSH\Services\ServiceInterface;
use App\SSH\Services\Webserver\Webserver;
use Database\Factories\ServiceFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Str;
@ -28,7 +29,7 @@
*/
class Service extends AbstractModel
{
/** @use HasFactory<\Database\Factories\ServiceFactory> */
/** @use HasFactory<ServiceFactory> */
use HasFactory;
protected $fillable = [

View File

@ -14,7 +14,6 @@ class MetricPolicy
public function viewAny(User $user, Server $server): bool
{
return ($user->isAdmin() || $server->project->users->contains($user)) &&
$server->service('monitoring') &&
$server->isReady();
}
@ -22,28 +21,24 @@ public function view(User $user, Metric $metric): bool
{
return ($user->isAdmin() || $metric->server->project->users->contains($user)) &&
$metric->server->service('monitoring') &&
$metric->server->isReady();
}
public function create(User $user, Server $server): bool
{
return ($user->isAdmin() || $server->project->users->contains($user)) &&
$server->service('monitoring') &&
$server->isReady();
}
public function update(User $user, Metric $metric): bool
{
return ($user->isAdmin() || $metric->server->project->users->contains($user)) &&
$metric->server->service('monitoring') &&
$metric->server->isReady();
}
public function delete(User $user, Metric $metric): bool
{
return ($user->isAdmin() || $metric->server->project->users->contains($user)) &&
$metric->server->service('monitoring') &&
$metric->server->isReady();
}
}

View File

@ -122,4 +122,13 @@ protected function addUfw(): void
'version' => 'latest',
]);
}
protected function addMonitoring(): void
{
$this->server->services()->create([
'type' => 'monitoring',
'name' => 'remote-monitor',
'version' => 'latest',
]);
}
}

View File

@ -39,5 +39,6 @@ public function createServices(array $input): void
$this->addSupervisor();
$this->addRedis();
$this->addUfw();
$this->addMonitoring();
}
}