mirror of
https://github.com/alexjustesen/speedtest-tracker.git
synced 2026-06-23 02:20:09 +00:00
feat: Add Prometheus (#2440)
Co-authored-by: Alex Justesen <alexjustesen@users.noreply.github.com>
This commit is contained in:
@@ -7,15 +7,18 @@ use App\Jobs\Influxdb\v2\TestConnectionJob;
|
||||
use App\Settings\DataIntegrationSettings;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Forms\Components\Checkbox;
|
||||
use Filament\Forms\Components\TagsInput;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Toggle;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Pages\SettingsPage;
|
||||
use Filament\Schemas\Components\Actions;
|
||||
use Filament\Schemas\Components\Grid;
|
||||
use Filament\Schemas\Components\Section;
|
||||
use Filament\Schemas\Components\Tabs;
|
||||
use Filament\Schemas\Components\Tabs\Tab;
|
||||
use Filament\Schemas\Components\Utilities\Get;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Support\Icons\Heroicon;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class DataIntegration extends SettingsPage
|
||||
@@ -52,16 +55,14 @@ class DataIntegration extends SettingsPage
|
||||
{
|
||||
return $schema
|
||||
->components([
|
||||
Grid::make([
|
||||
'default' => 1,
|
||||
'md' => 3,
|
||||
])
|
||||
Tabs::make()
|
||||
->schema([
|
||||
Section::make(__('settings/data_integration.influxdb_v2'))
|
||||
->description(__('settings/data_integration.influxdb_v2_description'))
|
||||
Tab::make(__('settings/data_integration.influxdb_v2'))
|
||||
->icon(Heroicon::OutlinedCircleStack)
|
||||
->schema([
|
||||
Toggle::make('influxdb_v2_enabled')
|
||||
->label(__('settings/data_integration.influxdb_v2_enabled'))
|
||||
->helpertext(__('settings/data_integration.influxdb_v2_description'))
|
||||
->reactive()
|
||||
->columnSpanFull(),
|
||||
Grid::make(['default' => 1, 'md' => 3])
|
||||
@@ -127,7 +128,26 @@ class DataIntegration extends SettingsPage
|
||||
]),
|
||||
]),
|
||||
])
|
||||
->compact()
|
||||
->columnSpanFull(),
|
||||
Tab::make(__('settings/data_integration.prometheus'))
|
||||
->icon(Heroicon::OutlinedChartBar)
|
||||
->schema([
|
||||
Toggle::make('prometheus_enabled')
|
||||
->label(__('settings/data_integration.prometheus_enabled'))
|
||||
->helperText(__('settings/data_integration.influxdb_v2_description'))
|
||||
->reactive()
|
||||
->columnSpanFull(),
|
||||
Grid::make(['default' => 1, 'md' => 3])
|
||||
->hidden(fn (Get $get) => $get('prometheus_enabled') !== true)
|
||||
->schema([
|
||||
TagsInput::make('prometheus_allowed_ips')
|
||||
->label(__('settings/data_integration.prometheus_allowed_ips'))
|
||||
->helperText(__('settings/data_integration.prometheus_allowed_ips_helper'))
|
||||
->placeholder('192.168.1.100')
|
||||
->splitKeys(['Tab', ',', ' '])
|
||||
->columnSpanFull(),
|
||||
]),
|
||||
])
|
||||
->columnSpanFull(),
|
||||
])
|
||||
->columnSpanFull(),
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Services\PrometheusMetricsService;
|
||||
use App\Settings\DataIntegrationSettings;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
class MetricsController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
protected PrometheusMetricsService $metricsService,
|
||||
protected DataIntegrationSettings $settings
|
||||
) {}
|
||||
|
||||
public function __invoke(): Response
|
||||
{
|
||||
if (! $this->settings->prometheus_enabled) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
$metrics = $this->metricsService->generateMetrics();
|
||||
|
||||
return response($metrics, 200, [
|
||||
'Content-Type' => 'text/plain; version=0.0.4; charset=utf-8',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Helpers\Network;
|
||||
use App\Settings\DataIntegrationSettings;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class PrometheusAllowedIpMiddleware
|
||||
{
|
||||
public function __construct(
|
||||
protected DataIntegrationSettings $settings
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param Closure(Request):Response $next
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
if (blank($this->settings->prometheus_allowed_ips)) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
$clientIp = $request->ip();
|
||||
$allowedIps = $this->settings->prometheus_allowed_ips;
|
||||
|
||||
foreach ($allowedIps as $allowedIp) {
|
||||
if (str_contains($allowedIp, '/')) {
|
||||
if (Network::ipInRange($clientIp, $allowedIp)) {
|
||||
return $next($request);
|
||||
}
|
||||
} elseif ($clientIp === $allowedIp) {
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
||||
abort(403);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ use App\Events\SpeedtestCompleted;
|
||||
use App\Events\SpeedtestFailed;
|
||||
use App\Jobs\Influxdb\v2\WriteResult;
|
||||
use App\Settings\DataIntegrationSettings;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class ProcessSpeedtestDataIntegrations
|
||||
{
|
||||
@@ -24,5 +25,9 @@ class ProcessSpeedtestDataIntegrations
|
||||
if ($this->settings->influxdb_v2_enabled) {
|
||||
WriteResult::dispatch($event->result);
|
||||
}
|
||||
|
||||
if ($this->settings->prometheus_enabled) {
|
||||
Cache::forever('prometheus:latest_result', $event->result->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Result;
|
||||
use App\Settings\DataIntegrationSettings;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Prometheus\CollectorRegistry;
|
||||
use Prometheus\RenderTextFormat;
|
||||
use Prometheus\Storage\InMemory;
|
||||
|
||||
class PrometheusMetricsService
|
||||
{
|
||||
public function __construct(
|
||||
protected DataIntegrationSettings $settings
|
||||
) {}
|
||||
|
||||
public function generateMetrics(): string
|
||||
{
|
||||
$registry = new CollectorRegistry(new InMemory);
|
||||
|
||||
$resultId = Cache::get('prometheus:latest_result');
|
||||
|
||||
if (! $resultId) {
|
||||
return $this->emptyMetrics();
|
||||
}
|
||||
|
||||
$lastResult = Result::find($resultId);
|
||||
|
||||
if (! $lastResult) {
|
||||
return $this->emptyMetrics();
|
||||
}
|
||||
|
||||
$this->registerMetrics($registry, $lastResult);
|
||||
|
||||
$renderer = new RenderTextFormat;
|
||||
|
||||
return $renderer->render($registry->getMetricFamilySamples());
|
||||
}
|
||||
|
||||
protected function registerMetrics(CollectorRegistry $registry, Result $result): void
|
||||
{
|
||||
$labels = $this->buildLabels($result);
|
||||
$labelNames = array_keys($labels);
|
||||
$labelValues = array_values($labels);
|
||||
|
||||
// Download speed in bytes
|
||||
$downloadBytesGauge = $registry->getOrRegisterGauge(
|
||||
'speedtest_tracker',
|
||||
'download_bytes',
|
||||
'Download speed in bytes per second',
|
||||
$labelNames
|
||||
);
|
||||
$downloadBytesGauge->set($result->download, $labelValues);
|
||||
|
||||
// Upload speed in bytes
|
||||
$uploadBytesGauge = $registry->getOrRegisterGauge(
|
||||
'speedtest_tracker',
|
||||
'upload_bytes',
|
||||
'Upload speed in bytes per second',
|
||||
$labelNames
|
||||
);
|
||||
$uploadBytesGauge->set($result->upload, $labelValues);
|
||||
|
||||
// Download speed in bits per second
|
||||
$downloadBitsGauge = $registry->getOrRegisterGauge(
|
||||
'speedtest_tracker',
|
||||
'download_bits',
|
||||
'Download speed in bits per second',
|
||||
$labelNames
|
||||
);
|
||||
$downloadBitsGauge->set(toBits($result->download), $labelValues);
|
||||
|
||||
// Upload speed in bits per second
|
||||
$uploadBitsGauge = $registry->getOrRegisterGauge(
|
||||
'speedtest_tracker',
|
||||
'upload_bits',
|
||||
'Upload speed in bits per second',
|
||||
$labelNames
|
||||
);
|
||||
$uploadBitsGauge->set(toBits($result->upload), $labelValues);
|
||||
|
||||
// Ping latency in milliseconds
|
||||
$pingGauge = $registry->getOrRegisterGauge(
|
||||
'speedtest_tracker',
|
||||
'ping_ms',
|
||||
'Ping latency in milliseconds',
|
||||
$labelNames
|
||||
);
|
||||
$pingGauge->set($result->ping, $labelValues);
|
||||
|
||||
// Ping jitter
|
||||
$pingJitterGauge = $registry->getOrRegisterGauge(
|
||||
'speedtest_tracker',
|
||||
'ping_jitter_ms',
|
||||
'Ping jitter in milliseconds',
|
||||
$labelNames
|
||||
);
|
||||
$pingJitterGauge->set($result->ping_jitter ?? 0, $labelValues);
|
||||
|
||||
// Download jitter
|
||||
$downloadJitterGauge = $registry->getOrRegisterGauge(
|
||||
'speedtest_tracker',
|
||||
'download_jitter_ms',
|
||||
'Download jitter in milliseconds',
|
||||
$labelNames
|
||||
);
|
||||
$downloadJitterGauge->set($result->download_jitter ?? 0, $labelValues);
|
||||
|
||||
// Upload jitter
|
||||
$uploadJitterGauge = $registry->getOrRegisterGauge(
|
||||
'speedtest_tracker',
|
||||
'upload_jitter_ms',
|
||||
'Upload jitter in milliseconds',
|
||||
$labelNames
|
||||
);
|
||||
$uploadJitterGauge->set($result->upload_jitter ?? 0, $labelValues);
|
||||
|
||||
// Packet loss
|
||||
$packetLossGauge = $registry->getOrRegisterGauge(
|
||||
'speedtest_tracker',
|
||||
'packet_loss_percent',
|
||||
'Packet loss percentage',
|
||||
$labelNames
|
||||
);
|
||||
$packetLossGauge->set($result->packet_loss ?? 0, $labelValues);
|
||||
|
||||
// Ping latency low/high
|
||||
$pingLowGauge = $registry->getOrRegisterGauge(
|
||||
'speedtest_tracker',
|
||||
'ping_low_ms',
|
||||
'Ping low latency in milliseconds',
|
||||
$labelNames
|
||||
);
|
||||
$pingLowGauge->set($result->ping_low ?? 0, $labelValues);
|
||||
|
||||
$pingHighGauge = $registry->getOrRegisterGauge(
|
||||
'speedtest_tracker',
|
||||
'ping_high_ms',
|
||||
'Ping high latency in milliseconds',
|
||||
$labelNames
|
||||
);
|
||||
$pingHighGauge->set($result->ping_high ?? 0, $labelValues);
|
||||
|
||||
// Download latency metrics (IQM = Interquartile Mean - more reliable than average)
|
||||
$downloadLatencyIqmGauge = $registry->getOrRegisterGauge(
|
||||
'speedtest_tracker',
|
||||
'download_latency_iqm_ms',
|
||||
'Download latency interquartile mean in milliseconds',
|
||||
$labelNames
|
||||
);
|
||||
$downloadLatencyIqmGauge->set($result->downloadlatencyiqm ?? 0, $labelValues);
|
||||
|
||||
$downloadLatencyLowGauge = $registry->getOrRegisterGauge(
|
||||
'speedtest_tracker',
|
||||
'download_latency_low_ms',
|
||||
'Download latency low in milliseconds',
|
||||
$labelNames
|
||||
);
|
||||
$downloadLatencyLowGauge->set($result->downloadlatency_low ?? 0, $labelValues);
|
||||
|
||||
$downloadLatencyHighGauge = $registry->getOrRegisterGauge(
|
||||
'speedtest_tracker',
|
||||
'download_latency_high_ms',
|
||||
'Download latency high in milliseconds',
|
||||
$labelNames
|
||||
);
|
||||
$downloadLatencyHighGauge->set($result->downloadlatency_high ?? 0, $labelValues);
|
||||
|
||||
// Upload latency metrics
|
||||
$uploadLatencyIqmGauge = $registry->getOrRegisterGauge(
|
||||
'speedtest_tracker',
|
||||
'upload_latency_iqm_ms',
|
||||
'Upload latency interquartile mean in milliseconds',
|
||||
$labelNames
|
||||
);
|
||||
$uploadLatencyIqmGauge->set($result->uploadlatencyiqm ?? 0, $labelValues);
|
||||
|
||||
$uploadLatencyLowGauge = $registry->getOrRegisterGauge(
|
||||
'speedtest_tracker',
|
||||
'upload_latency_low_ms',
|
||||
'Upload latency low in milliseconds',
|
||||
$labelNames
|
||||
);
|
||||
$uploadLatencyLowGauge->set($result->uploadlatency_low ?? 0, $labelValues);
|
||||
|
||||
$uploadLatencyHighGauge = $registry->getOrRegisterGauge(
|
||||
'speedtest_tracker',
|
||||
'upload_latency_high_ms',
|
||||
'Upload latency high in milliseconds',
|
||||
$labelNames
|
||||
);
|
||||
$uploadLatencyHighGauge->set($result->uploadlatency_high ?? 0, $labelValues);
|
||||
|
||||
// Bytes transferred during test
|
||||
$downloadedBytesGauge = $registry->getOrRegisterGauge(
|
||||
'speedtest_tracker',
|
||||
'downloaded_bytes',
|
||||
'Total bytes downloaded during test',
|
||||
$labelNames
|
||||
);
|
||||
$downloadedBytesGauge->set($result->downloaded_bytes ?? 0, $labelValues);
|
||||
|
||||
$uploadedBytesGauge = $registry->getOrRegisterGauge(
|
||||
'speedtest_tracker',
|
||||
'uploaded_bytes',
|
||||
'Total bytes uploaded during test',
|
||||
$labelNames
|
||||
);
|
||||
$uploadedBytesGauge->set($result->uploaded_bytes ?? 0, $labelValues);
|
||||
|
||||
// Test duration
|
||||
$downloadElapsedGauge = $registry->getOrRegisterGauge(
|
||||
'speedtest_tracker',
|
||||
'download_elapsed_ms',
|
||||
'Download test duration in milliseconds',
|
||||
$labelNames
|
||||
);
|
||||
$downloadElapsedGauge->set($result->download_elapsed ?? 0, $labelValues);
|
||||
|
||||
$uploadElapsedGauge = $registry->getOrRegisterGauge(
|
||||
'speedtest_tracker',
|
||||
'upload_elapsed_ms',
|
||||
'Upload test duration in milliseconds',
|
||||
$labelNames
|
||||
);
|
||||
$uploadElapsedGauge->set($result->upload_elapsed ?? 0, $labelValues);
|
||||
}
|
||||
|
||||
protected function buildLabels(Result $result): array
|
||||
{
|
||||
return [
|
||||
'server_id' => (string) ($result->server_id ?? ''),
|
||||
'server_name' => $result->server_name ?? '',
|
||||
'server_country' => $result->server_country ?? '',
|
||||
'server_location' => $result->server_location ?? '',
|
||||
'isp' => $result->isp ?? '',
|
||||
'scheduled' => $result->scheduled ? 'true' : 'false',
|
||||
'healthy' => $result->healthy ? 'true' : 'false',
|
||||
'status' => $result->status->value,
|
||||
'app_name' => config('app.name', 'Speedtest Tracker'),
|
||||
];
|
||||
}
|
||||
|
||||
protected function emptyMetrics(): string
|
||||
{
|
||||
return "# no data available\n";
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,10 @@ class DataIntegrationSettings extends Settings
|
||||
|
||||
public bool $influxdb_v2_verify_ssl;
|
||||
|
||||
public bool $prometheus_enabled;
|
||||
|
||||
public array $prometheus_allowed_ips = [];
|
||||
|
||||
public static function group(): string
|
||||
{
|
||||
return 'dataintegration';
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"livewire/livewire": "^3.6.4",
|
||||
"lorisleiva/laravel-actions": "^2.9.1",
|
||||
"maennchen/zipstream-php": "^2.4",
|
||||
"promphp/prometheus_client_php": "^2.14",
|
||||
"saloonphp/laravel-plugin": "^3.0",
|
||||
"secondnetwork/blade-tabler-icons": "^3.35.0",
|
||||
"spatie/laravel-json-api-paginate": "^1.16.3",
|
||||
|
||||
Generated
+68
@@ -5462,6 +5462,74 @@
|
||||
},
|
||||
"time": "2025-09-19T23:02:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "promphp/prometheus_client_php",
|
||||
"version": "v2.14.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PromPHP/prometheus_client_php.git",
|
||||
"reference": "a283aea8269287dc35313a0055480d950c59ac1f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PromPHP/prometheus_client_php/zipball/a283aea8269287dc35313a0055480d950c59ac1f",
|
||||
"reference": "a283aea8269287dc35313a0055480d950c59ac1f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"php": "^7.4|^8.0"
|
||||
},
|
||||
"replace": {
|
||||
"endclothing/prometheus_client_php": "*",
|
||||
"jimdo/prometheus_client_php": "*",
|
||||
"lkaemmerling/prometheus_client_php": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"guzzlehttp/guzzle": "^6.3|^7.0",
|
||||
"phpstan/extension-installer": "^1.0",
|
||||
"phpstan/phpstan": "^1.5.4",
|
||||
"phpstan/phpstan-phpunit": "^1.1.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.1.0",
|
||||
"phpunit/phpunit": "^9.4",
|
||||
"squizlabs/php_codesniffer": "^3.6",
|
||||
"symfony/polyfill-apcu": "^1.6"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-apc": "Required if using APCu.",
|
||||
"ext-pdo": "Required if using PDO.",
|
||||
"ext-redis": "Required if using Redis.",
|
||||
"promphp/prometheus_push_gateway_php": "An easy client for using Prometheus PushGateway.",
|
||||
"symfony/polyfill-apcu": "Required if you use APCu on PHP8.0+"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Prometheus\\": "src/Prometheus/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"Apache-2.0"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Lukas Kämmerling",
|
||||
"email": "kontakt@lukas-kaemmerling.de"
|
||||
}
|
||||
],
|
||||
"description": "Prometheus instrumentation library for PHP applications.",
|
||||
"support": {
|
||||
"issues": "https://github.com/PromPHP/prometheus_client_php/issues",
|
||||
"source": "https://github.com/PromPHP/prometheus_client_php/tree/v2.14.1"
|
||||
},
|
||||
"time": "2025-04-14T07:59:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/clock",
|
||||
"version": "1.0.0",
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
use Spatie\LaravelSettings\Migrations\SettingsMigration;
|
||||
|
||||
class CreatePrometheusSettings extends SettingsMigration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
$this->migrator->add('dataintegration.prometheus_enabled', false);
|
||||
$this->migrator->add('dataintegration.prometheus_allowed_ips', []);
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,13 @@ return [
|
||||
'influxdb_bulk_write_success' => 'Finished bulk data load to Influxdb.',
|
||||
'influxdb_bulk_write_success_body' => 'Data has been sent to InfluxDB, check if the data was received.',
|
||||
|
||||
// Prometheus
|
||||
'prometheus' => 'Prometheus',
|
||||
'prometheus_enabled' => 'Enable',
|
||||
'prometheus_enabled_helper_text' => 'When enabled, metrics for each new speedtest will be available at the /prometheus endpoint.',
|
||||
'prometheus_allowed_ips' => 'Allowed IP Addresses',
|
||||
'prometheus_allowed_ips_helper' => 'List of IP addresses or CIDR ranges (e.g., 192.168.1.0/24) allowed to access the metrics endpoint. Leave empty to allow all IPs.',
|
||||
|
||||
// Common labels
|
||||
'org' => 'Org',
|
||||
'bucket' => 'Bucket',
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<?php
|
||||
|
||||
use App\Http\Controllers\HomeController;
|
||||
use App\Http\Controllers\MetricsController;
|
||||
use App\Http\Middleware\PrometheusAllowedIpMiddleware;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
/*
|
||||
@@ -18,6 +20,10 @@ Route::get('/', HomeController::class)
|
||||
->middleware(['getting-started', 'public-dashboard'])
|
||||
->name('home');
|
||||
|
||||
Route::get('/prometheus', MetricsController::class)
|
||||
->middleware(PrometheusAllowedIpMiddleware::class)
|
||||
->name('prometheus');
|
||||
|
||||
Route::view('/getting-started', 'getting-started')
|
||||
->name('getting-started');
|
||||
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Result;
|
||||
use App\Settings\DataIntegrationSettings;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
beforeEach(function () {
|
||||
Cache::flush();
|
||||
});
|
||||
|
||||
describe('metrics endpoint', function () {
|
||||
test('returns 404 when prometheus is disabled', function () {
|
||||
app(DataIntegrationSettings::class)->fill(['prometheus_enabled' => false])->save();
|
||||
|
||||
$response = $this->get('/prometheus');
|
||||
|
||||
$response->assertNotFound();
|
||||
});
|
||||
|
||||
test('returns metrics when prometheus is enabled and no IP restrictions', function () {
|
||||
app(DataIntegrationSettings::class)->fill([
|
||||
'prometheus_enabled' => true,
|
||||
'prometheus_allowed_ips' => [],
|
||||
])->save();
|
||||
|
||||
Result::factory()->create();
|
||||
|
||||
$response = $this->get('/prometheus');
|
||||
|
||||
$response->assertSuccessful();
|
||||
$response->assertHeader('Content-Type', 'text/plain; version=0.0.4; charset=utf-8');
|
||||
});
|
||||
|
||||
test('returns 403 when IP is not in allowed list', function () {
|
||||
app(DataIntegrationSettings::class)->fill([
|
||||
'prometheus_enabled' => true,
|
||||
'prometheus_allowed_ips' => ['192.168.1.100', '10.0.0.1'],
|
||||
])->save();
|
||||
|
||||
$response = $this->get('/prometheus', [
|
||||
'REMOTE_ADDR' => '192.168.1.50',
|
||||
]);
|
||||
|
||||
$response->assertForbidden();
|
||||
});
|
||||
|
||||
test('returns metrics when IP is in allowed list', function () {
|
||||
app(DataIntegrationSettings::class)->fill([
|
||||
'prometheus_enabled' => true,
|
||||
'prometheus_allowed_ips' => ['192.168.1.100', '10.0.0.1'],
|
||||
])->save();
|
||||
|
||||
Result::factory()->create();
|
||||
|
||||
$response = $this->get('/prometheus', [
|
||||
'REMOTE_ADDR' => '192.168.1.100',
|
||||
]);
|
||||
|
||||
$response->assertSuccessful();
|
||||
$response->assertHeader('Content-Type', 'text/plain; version=0.0.4; charset=utf-8');
|
||||
});
|
||||
|
||||
test('allows access with empty array', function () {
|
||||
app(DataIntegrationSettings::class)->fill([
|
||||
'prometheus_enabled' => true,
|
||||
'prometheus_allowed_ips' => [],
|
||||
])->save();
|
||||
|
||||
Result::factory()->create();
|
||||
|
||||
$response = $this->get('/prometheus', [
|
||||
'REMOTE_ADDR' => '10.0.0.1',
|
||||
]);
|
||||
|
||||
$response->assertSuccessful();
|
||||
});
|
||||
|
||||
test('allows access when IP is in CIDR range', function () {
|
||||
app(DataIntegrationSettings::class)->fill([
|
||||
'prometheus_enabled' => true,
|
||||
'prometheus_allowed_ips' => ['192.168.1.0/24'],
|
||||
])->save();
|
||||
|
||||
Result::factory()->create();
|
||||
|
||||
$response = $this->get('/prometheus', [
|
||||
'REMOTE_ADDR' => '192.168.1.150',
|
||||
]);
|
||||
|
||||
$response->assertSuccessful();
|
||||
});
|
||||
|
||||
test('denies access when IP is not in CIDR range', function () {
|
||||
app(DataIntegrationSettings::class)->fill([
|
||||
'prometheus_enabled' => true,
|
||||
'prometheus_allowed_ips' => ['192.168.1.0/24'],
|
||||
])->save();
|
||||
|
||||
$response = $this->get('/prometheus', [
|
||||
'REMOTE_ADDR' => '192.168.2.1',
|
||||
]);
|
||||
|
||||
$response->assertForbidden();
|
||||
});
|
||||
|
||||
test('supports mixed IP addresses and CIDR ranges', function () {
|
||||
app(DataIntegrationSettings::class)->fill([
|
||||
'prometheus_enabled' => true,
|
||||
'prometheus_allowed_ips' => ['10.0.0.1', '192.168.1.0/24'],
|
||||
])->save();
|
||||
|
||||
Result::factory()->create();
|
||||
|
||||
$response = $this->get('/prometheus', [
|
||||
'REMOTE_ADDR' => '192.168.1.50',
|
||||
]);
|
||||
|
||||
$response->assertSuccessful();
|
||||
|
||||
$response = $this->get('/prometheus', [
|
||||
'REMOTE_ADDR' => '10.0.0.1',
|
||||
]);
|
||||
|
||||
$response->assertSuccessful();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user