PHPackages                             cranleighschool/domain-expiry - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. cranleighschool/domain-expiry

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

cranleighschool/domain-expiry
=============================

WHOIS-based domain expiry checker and dashboard for Laravel 12+

v0.0.4(1mo ago)05MITPHPPHP ^8.2CI passing

Since Apr 24Pushed 1mo agoCompare

[ Source](https://github.com/cranleighschool/laravel-domain-expiry)[ Packagist](https://packagist.org/packages/cranleighschool/domain-expiry)[ RSS](/packages/cranleighschool-domain-expiry/feed)WikiDiscussions main Synced 3w ago

READMEChangelog (4)Dependencies (7)Versions (5)Used By (0)

cranleighschool/domain-expiry
=============================

[](#cranleighschooldomain-expiry)

A Laravel 12+ package for monitoring domain expiry dates via direct WHOIS queries.

Features
--------

[](#features)

- Direct TCP WHOIS queries — no external API keys or services required
- 70+ TLDs supported out of the box, including two-part TLDs (`co.uk`, `com.au`, etc.)
- Built-in web dashboard at `/domain-expiry`
- JSON API endpoint for integration with other dashboards
- Artisan command for CLI checks and cron-based alerting
- Laravel Cache integration with configurable TTL
- Per-domain refresh via the dashboard UI
- Zero production dependencies beyond the Laravel framework

---

Installation
------------

[](#installation)

```
composer require cranleighschool/domain-expiry
```

Publish the config file:

```
php artisan vendor:publish --tag=domain-expiry-config
```

---

Configuration
-------------

[](#configuration)

Open `config/domain-expiry.php` and add your domains:

```
'domains' => [
    'cranleighschool.org',
    'example.co.uk',
    'myproject.io',
],
```

### Protect the dashboard

[](#protect-the-dashboard)

By default the dashboard had the middleware `web` and `auth` attached. If you want to amend the middelware you can do that in the config file. Removing `auth` for example will make the dashboard accessible to all.

```
'dashboard' => [
    'enabled'    => true,
    'uri'        => '/domain-expiry',
    'middleware' => ['web', 'auth'],
],
```

### Urgency thresholds

[](#urgency-thresholds)

```
'thresholds' => [
    'critical' => 14,   // < 14 days  → red
    'warning'  => 30,   // < 30 days  → amber
    'notice'   => 60,   // < 60 days  → blue
],
```

### Cache settings

[](#cache-settings)

```
'cache_store' => 'redis',  // null = default cache driver
'cache_ttl'   => 3600,     // 1 hour
```

### Add custom WHOIS servers

[](#add-custom-whois-servers)

```
'extra_whois_servers' => [
    'sch.uk' => 'whois.nic.uk',
],
```

---

Usage
-----

[](#usage)

### Web Dashboard

[](#web-dashboard)

Visit `/domain-expiry` (or your configured URI) to see the dashboard. Each row shows:

- Urgency badge (OK / NOTICE / WARNING / CRITICAL / EXPIRED / UNKNOWN)
- Days remaining with a progress bar
- Expiry date and time (UTC)
- WHOIS server used
- Per-domain refresh button

### JSON API

[](#json-api)

```
GET /domain-expiry/json

```

Returns:

```
{
    "generated_at": "2025-10-01T09:00:00+00:00",
    "summary": { "ok": 3, "notice": 1, "warning": 0, "critical": 0, "expired": 0, "unknown": 0 },
    "domains": [
        {
            "domain": "example.com",
            "tld": "com",
            "server": "whois.verisign-grs.com",
            "expiry_date": "2026-05-12T00:00:00+00:00",
            "days": 223,
            "urgency": "ok",
            "error": null
        }
    ]
}
```

### Facade

[](#facade)

```
use CranleighSchool\DomainExpiry\Facades\DomainExpiry;

// Check a single domain
$result = DomainExpiry::check('example.com');
echo $result->daysUntilExpiry();    // 223
echo $result->urgencyLevel()->value; // "ok"

// Check many (sorted by soonest expiry)
$results = DomainExpiry::checkMany(['example.com', 'example.co.uk']);

// Only domains expiring within 30 days
$urgent = DomainExpiry::expiringSoon(['example.com', 'example.co.uk'], withinDays: 30);

// Force a fresh WHOIS query, bypassing the cache
$fresh = DomainExpiry::refresh('example.com');
```

### Importing domains from registrars

[](#importing-domains-from-registrars)

Pull all domains from your configured registrars and upsert them into the database:

```
php artisan domain-expiry:registrar-import
```

The package ships with [Gandi](https://www.gandi.net) and [Porkbun](https://porkbun.com) support. Set your credentials in `.env`:

```
GANDI_ORGANISATION_API_KEY=your-key-here
PORKBUN_API_KEY=your-key-here
PORKBUN_SECRET_API_KEY=your-secret-here
```

#### Adding a custom registrar

[](#adding-a-custom-registrar)

Implement `RegistrarInterface` and tag it in your service provider — the import command picks it up automatically alongside the built-in registrars.

**1. Implement the interface**

```
namespace App\Registrars;

use CranleighSchool\DomainExpiry\RegistrarInterface;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Http;

class Namecheap implements RegistrarInterface
{
    /**
     *  Must return a collection of domain names (string), without any schema/protcol or extra data — just the domain name itself.)
     * eg: ["example.com", "mydomain.co.uk", "anotherdomain.io"]
     */
    public function getDomains(): Collection
    {
        return Http::get('https://api.namecheap.com/...')
            ->collect()
            ->pluck('Name');
    }
}
```

**2. Tag it in your `AppServiceProvider`**

```
use App\Registrars\Namecheap;

public function register(): void
{
    $this->app->tag([Namecheap::class], 'domain-expiry.registrars');
}
```

---

### Artisan command

[](#artisan-command)

```
# Check all domains from config
php artisan domain-expiry:check

# Check specific domains
php artisan domain-expiry:check --domain=example.com --domain=example.co.uk

# Only show domains that need attention
php artisan domain-expiry:check --warn-only

# Output JSON
php artisan domain-expiry:check --json

# Bypass cache (force fresh WHOIS queries)
php artisan domain-expiry:check --refresh
```

The command exits with code `1` if any domain is CRITICAL or EXPIRED — useful for CI or monitoring scripts.

### Scheduled alerting

[](#scheduled-alerting)

In `routes/console.php`:

```
use Illuminate\Support\Facades\Schedule;

// Check daily at 08:00 and email if anything is expiring soon
Schedule::command('domain-expiry:check --warn-only')
    ->dailyAt('08:00')
    ->emailOutputOnFailure('admin@example.com');
```

---

Testing
-------

[](#testing)

```
composer test
```

---

Publishing views
----------------

[](#publishing-views)

To customise the dashboard blade template:

```
php artisan vendor:publish --tag=domain-expiry-views
```

The view will be published to `resources/views/vendor/domain-expiry/dashboard.blade.php`.

---

Limitations
-----------

[](#limitations)

- Some registrars (Cloudflare, GoDaddy proxy registration) redact WHOIS data. These will show as `UNKNOWN`.
- WHOIS servers rate-limit aggressively — do not set `cache_ttl` too low in production.
- The `sch.uk` and other specialist TLDs may need adding via `extra_whois_servers`.

---

Licence
-------

[](#licence)

MIT

###  Health Score

36

—

LowBetter than 79% of packages

Maintenance88

Actively maintained with recent releases

Popularity4

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity39

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~11 days

Total

4

Last Release

59d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/1639226?v=4)[Fred Bradley](/maintainers/fredbradley)[@fredbradley](https://github.com/fredbradley)

---

Top Contributors

[![fredbradley](https://avatars.githubusercontent.com/u/1639226?v=4)](https://github.com/fredbradley "fredbradley (8 commits)")

---

Tags

laraveldnsdomainwhoisexpiry

###  Code Quality

TestsPest

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/cranleighschool-domain-expiry/health.svg)

```
[![Health](https://phpackages.com/badges/cranleighschool-domain-expiry/health.svg)](https://phpackages.com/packages/cranleighschool-domain-expiry)
```

###  Alternatives

[livewire/flux

The official UI component library for Livewire.

9577.8M138](/packages/livewire-flux)[psalm/plugin-laravel

Psalm plugin for Laravel

3345.3M347](/packages/psalm-plugin-laravel)[tallstackui/tallstackui

TallStackUI is a powerful suite of Blade components that elevate your workflow of Livewire applications.

728176.2k14](/packages/tallstackui-tallstackui)[venturedrake/laravel-crm

A free open source CRM built as a package for laravel projects

44611.3k](/packages/venturedrake-laravel-crm)[tomshaw/electricgrid

A feature-rich Livewire package designed for projects that require dynamic, interactive data tables.

119.4k](/packages/tomshaw-electricgrid)[revolution/self-ordering

Self Ordering System

2313.2k](/packages/revolution-self-ordering)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
