spatie/laravel-dashboard-oh-dear-uptime-tile
Laravel Dashboard tile that shows which monitored sites are currently down according to Oh Dear. Use it to surface uptime incidents at a glance on your dashboard.
Installation Require the package via Composer:
composer require spatie/laravel-dashboard-oh-dear-uptime-tile
Publish the configuration (if needed):
php artisan vendor:publish --provider="Spatie\Dashboard\DashboardServiceProvider" --tag="dashboard-config"
Configuration
Ensure your config/dashboard.php includes the tile in the tiles array:
'tiles' => [
// ...
\Spatie\Dashboard\Tiles\OhDearUptimeTile::class,
],
Oh Dear API Setup
.env:
OH_DEAR_API_TOKEN=your_api_token_here
config/dashboard.php (if not using Oh Dear’s default):
'oh_dear' => [
'api_url' => env('OH_DEAR_API_URL', 'https://api.ohdear.app/v1'),
],
First Use Case
Visit your Laravel Dashboard (/dashboard). The tile will automatically fetch and display down sites from Oh Dear, with:
Integration with Laravel Dashboard
\Spatie\Dashboard\Tile, so it follows the same rendering lifecycle as other dashboard tiles.php artisan vendor:publish --tag=dashboard-views).Data Fetching
getData() method in a custom tile class to modify the request:
use Spatie\Dashboard\Tiles\OhDearUptimeTile;
class CustomOhDearTile extends OhDearUptimeTile {
public function getData()
{
$response = $this->http->get('https://api.ohdear.app/v1/sites', [
'headers' => [
'Authorization' => 'Bearer ' . config('services.oh_dear.token'),
'Accept' => 'application/json',
],
]);
return $response->json();
}
}
Conditional Rendering
shouldBeDisplayed():
public function shouldBeDisplayed(): bool
{
$data = $this->getData();
return $data['down'] > 0;
}
Real-Time Updates
use Illuminate\Support\Facades\Cache;
public function getData()
{
return Cache::remember('oh_dear_sites', now()->addMinutes(5), function () {
return $this->http->get('...')->json();
});
}
Multi-Tenant Support
public function getData()
{
$tenantId = auth()->user()->tenant_id;
return $this->http->get('...', [
'query' => ['tenant_id' => $tenantId],
])->json();
}
API Rate Limiting
getData() if you encounter 429 errors:
try {
return $this->http->get('...')->json();
} catch (\GuzzleHttp\Exception\RequestException $e) {
if ($e->getCode() === 429) {
sleep(2); // Retry after 2 seconds
return $this->http->get('...')->json();
}
throw $e;
}
Token Security
.env or Laravel’s config/services.php:
OH_DEAR_API_TOKEN=your_token_here
// config/services.php
'oh_dear' => [
'token' => env('OH_DEAR_API_TOKEN'),
],
Data Parsing Issues
getData():
$data = $this->http->get('...')->json();
if (!isset($data['sites'])) {
throw new \RuntimeException('Invalid API response format');
}
Caching Stale Data
Cache::forget('oh_dear_sites') in routes/web.php for testing:
Route::get('/clear-oh-dear-cache', function () {
Cache::forget('oh_dear_sites');
return 'Cache cleared!';
});
Custom Styling
resources/views/vendor/dashboard/tiles/oh-dear.blade.php) to change colors or layout:
@if($tile->data['down'] > 0)
<div class="bg-red-100 border border-red-400 text-red-800">
<!-- Custom content -->
</div>
@endif
Notifications Integration
use Illuminate\Notifications\Notification;
public function shouldBeDisplayed(): bool
{
$data = $this->getData();
if ($data['down'] > 0 && $this->lastChecked['down'] === 0) {
Notification::send(auth()->user(), new SiteDownNotification($data));
}
$this->lastChecked = $data;
return $data['down'] > 0;
}
Environment-Specific Config
OH_DEAR_API_TOKEN_STAGING=...
OH_DEAR_API_TOKEN_PROD=...
// config/dashboard.php
'oh_dear' => [
'token' => env('OH_DEAR_API_TOKEN_' . app()->environment()),
],
Testing
$this->app->instance(\GuzzleHttp\Client::class, \Mockery::mock(\GuzzleHttp\Client::class));
$mock = $this->app->make(\GuzzleHttp\Client::class);
$mock->shouldReceive('get')
->once()
->andReturn((new \GuzzleHttp\Psr7\Response(200, [], json_encode(['down' => 1]))));
Performance Optimization
public function shouldBeDisplayed(): bool
{
return true; // Always show the tile
}
public function render()
{
$data = $this->getData(); // Load data only when rendering
return view('...', ['data' => $data]);
}
How can I help you explore Laravel packages today?