some progress
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Domain;
|
||||
use App\Models\Notification;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Queue\Queueable;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class CheckDomainExpiry implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public Domain $domain
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
// Use WHOIS API to check domain expiry
|
||||
// We'll use the WHOIS XML API (https://www.whoisxmlapi.com/)
|
||||
// You can also use other providers like whoisfreaks.com or domainr.com
|
||||
|
||||
$apiKey = config('services.whois.api_key');
|
||||
|
||||
if (!$apiKey) {
|
||||
Log::warning('WHOIS API key not configured');
|
||||
return;
|
||||
}
|
||||
|
||||
$response = Http::timeout(30)
|
||||
->get('https://www.whoisxmlapi.com/whoisserver/WhoisService', [
|
||||
'apiKey' => $apiKey,
|
||||
'domainName' => $this->domain->domain,
|
||||
'outputFormat' => 'JSON',
|
||||
]);
|
||||
|
||||
if ($response->successful()) {
|
||||
$data = $response->json();
|
||||
|
||||
// Parse expiry date from WHOIS response
|
||||
$expiryDate = $this->parseExpiryDate($data);
|
||||
|
||||
if ($expiryDate) {
|
||||
$this->domain->update([
|
||||
'expires_at' => $expiryDate,
|
||||
'last_checked_at' => Carbon::now(),
|
||||
]);
|
||||
|
||||
// Check if domain is expiring soon (within 30 days)
|
||||
if ($this->domain->isExpiringSoon()) {
|
||||
$this->sendExpiryAlert();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log::error('WHOIS API request failed', [
|
||||
'domain' => $this->domain->domain,
|
||||
'status' => $response->status(),
|
||||
]);
|
||||
}
|
||||
|
||||
$this->domain->update([
|
||||
'last_checked_at' => Carbon::now(),
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Error checking domain expiry', [
|
||||
'domain' => $this->domain->domain,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
throw $e; // Re-throw to allow job retry
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse expiry date from WHOIS response
|
||||
*/
|
||||
protected function parseExpiryDate(array $data): ?Carbon
|
||||
{
|
||||
// Try multiple possible fields where expiry date might be
|
||||
$possibleFields = [
|
||||
'WhoisRecord.registryData.expiresDate',
|
||||
'WhoisRecord.expiresDate',
|
||||
'expiresDate',
|
||||
];
|
||||
|
||||
foreach ($possibleFields as $field) {
|
||||
$value = data_get($data, $field);
|
||||
|
||||
if ($value) {
|
||||
try {
|
||||
return Carbon::parse($value);
|
||||
} catch (\Exception $e) {
|
||||
Log::warning('Failed to parse date', [
|
||||
'field' => $field,
|
||||
'value' => $value,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send alert notification for expiring domain
|
||||
*/
|
||||
protected function sendExpiryAlert(): void
|
||||
{
|
||||
$daysUntilExpiry = $this->domain->daysUntilExpiration();
|
||||
|
||||
$message = $this->domain->isExpired()
|
||||
? "Domain '{$this->domain->domain}' has expired!"
|
||||
: "Domain '{$this->domain->domain}' expires in {$daysUntilExpiry} days (on {$this->domain->expires_at->format('Y-m-d')})";
|
||||
|
||||
Notification::create([
|
||||
'user_id' => $this->domain->site->user_id,
|
||||
'type' => 'domain_expiry',
|
||||
'message' => $message,
|
||||
'data' => json_encode([
|
||||
'domain_id' => $this->domain->id,
|
||||
'site_id' => $this->domain->site_id,
|
||||
'domain' => $this->domain->domain,
|
||||
'expires_at' => $this->domain->expires_at,
|
||||
'days_until_expiry' => $daysUntilExpiry,
|
||||
]),
|
||||
'read_at' => null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Notification;
|
||||
use App\Models\Sitemap;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Queue\Queueable;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class CheckSitemap implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public Sitemap $sitemap
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
// Fetch the sitemap URL
|
||||
$response = Http::timeout(30)
|
||||
->withHeaders([
|
||||
'User-Agent' => 'Mozilla/5.0 (compatible; SitemapMonitor/1.0)',
|
||||
])
|
||||
->get($this->sitemap->url);
|
||||
|
||||
if (!$response->successful()) {
|
||||
$this->sitemap->markAsInvalid(
|
||||
"HTTP {$response->status()}: Failed to fetch sitemap"
|
||||
);
|
||||
$this->sendAlert("Sitemap is inaccessible (HTTP {$response->status()})");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the response body
|
||||
$xmlContent = $response->body();
|
||||
|
||||
// Validate XML structure using simplexml_load_string
|
||||
libxml_use_internal_errors(true);
|
||||
$xml = simplexml_load_string($xmlContent);
|
||||
|
||||
if ($xml === false) {
|
||||
$errors = libxml_get_errors();
|
||||
$errorMessages = array_map(fn($error) => trim($error->message), $errors);
|
||||
libxml_clear_errors();
|
||||
|
||||
$this->sitemap->markAsInvalid(
|
||||
'Invalid XML: ' . implode('; ', $errorMessages)
|
||||
);
|
||||
$this->sendAlert('Sitemap contains invalid XML');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if it's a valid sitemap (has urlset or sitemapindex)
|
||||
$namespaces = $xml->getNamespaces(true);
|
||||
$isUrlset = isset($xml->url) || isset($namespaces['']);
|
||||
$isSitemapIndex = isset($xml->sitemap);
|
||||
|
||||
if (!$isUrlset && !$isSitemapIndex) {
|
||||
$this->sitemap->markAsInvalid(
|
||||
'Invalid sitemap format: Missing <urlset> or <sitemapindex> root element'
|
||||
);
|
||||
$this->sendAlert('Sitemap has invalid format');
|
||||
return;
|
||||
}
|
||||
|
||||
// Count URLs in the sitemap
|
||||
$urlCount = $this->countUrls($xml);
|
||||
|
||||
// Mark as valid
|
||||
$this->sitemap->markAsValid($urlCount);
|
||||
|
||||
Log::info('Sitemap validated successfully', [
|
||||
'sitemap_id' => $this->sitemap->id,
|
||||
'url' => $this->sitemap->url,
|
||||
'url_count' => $urlCount,
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Error checking sitemap', [
|
||||
'sitemap_id' => $this->sitemap->id,
|
||||
'url' => $this->sitemap->url,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
$this->sitemap->markAsInvalid(
|
||||
'Exception: ' . $e->getMessage()
|
||||
);
|
||||
$this->sendAlert('Sitemap check failed: ' . $e->getMessage());
|
||||
|
||||
throw $e; // Re-throw to allow job retry
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Count URLs in the sitemap
|
||||
*/
|
||||
protected function countUrls(\SimpleXMLElement $xml): int
|
||||
{
|
||||
$count = 0;
|
||||
|
||||
// Register namespaces
|
||||
$xml->registerXPathNamespace('sm', 'http://www.sitemaps.org/schemas/sitemap/0.9');
|
||||
|
||||
// Check for regular sitemap
|
||||
$urls = $xml->xpath('//sm:url') ?: $xml->xpath('//url');
|
||||
if ($urls !== false) {
|
||||
$count = count($urls);
|
||||
}
|
||||
|
||||
// If it's a sitemap index, count the sitemaps instead
|
||||
if ($count === 0) {
|
||||
$sitemaps = $xml->xpath('//sm:sitemap') ?: $xml->xpath('//sitemap');
|
||||
if ($sitemaps !== false) {
|
||||
$count = count($sitemaps);
|
||||
}
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send alert notification for invalid sitemap
|
||||
*/
|
||||
protected function sendAlert(string $message): void
|
||||
{
|
||||
Notification::create([
|
||||
'user_id' => $this->sitemap->site->user_id,
|
||||
'type' => 'sitemap_invalid',
|
||||
'message' => "{$message}: {$this->sitemap->url}",
|
||||
'data' => json_encode([
|
||||
'sitemap_id' => $this->sitemap->id,
|
||||
'site_id' => $this->sitemap->site_id,
|
||||
'url' => $this->sitemap->url,
|
||||
'error' => $this->sitemap->last_error,
|
||||
]),
|
||||
'read_at' => null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user