Deploy Ladill Hosting / deploy (push) Failing after 17s
Shared web hosting extracted from the platform monolith, with CI deploy to /var/www/ladill-hosting matching Bird/Domains/Email. Co-authored-by: Cursor <cursoragent@cursor.com>
231 lines
6.2 KiB
PHP
231 lines
6.2 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Facades\Schema;
|
|
|
|
/**
|
|
* Domain pricing overrides.
|
|
*
|
|
* When a TLD has an entry here with is_override=true, the manually set GHS price
|
|
* will be used instead of the live-converted Dynadot price.
|
|
*
|
|
* Prices are stored in pesewas (minor units).
|
|
*/
|
|
class DomainPricing extends Model
|
|
{
|
|
/**
|
|
* @var array<string, bool>
|
|
*/
|
|
private static array $columnExists = [];
|
|
|
|
protected $fillable = [
|
|
'tld',
|
|
'register_price',
|
|
'renew_price',
|
|
'transfer_price',
|
|
'currency',
|
|
'is_override',
|
|
'is_featured',
|
|
'is_active',
|
|
'sort_order',
|
|
];
|
|
|
|
protected $casts = [
|
|
'register_price' => 'integer',
|
|
'renew_price' => 'integer',
|
|
'transfer_price' => 'integer',
|
|
'is_override' => 'boolean',
|
|
'is_featured' => 'boolean',
|
|
'is_active' => 'boolean',
|
|
'sort_order' => 'integer',
|
|
];
|
|
|
|
/**
|
|
* Get active pricing override for a TLD.
|
|
*/
|
|
public static function forTld(string $tld): ?self
|
|
{
|
|
$tld = ltrim(strtolower(trim($tld)), '.');
|
|
|
|
return Cache::remember(
|
|
"domain_pricing:{$tld}",
|
|
now()->addHours(1),
|
|
function () use ($tld) {
|
|
$query = static::where('tld', $tld);
|
|
|
|
if (static::hasColumn('is_active')) {
|
|
$query->where('is_active', true);
|
|
}
|
|
|
|
return $query->first();
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get all active price overrides.
|
|
*
|
|
* @return array<string, array{register: int, renew: int, transfer: int, is_override: bool}>
|
|
*/
|
|
public static function getOverrides(): array
|
|
{
|
|
return Cache::remember(
|
|
'domain_pricing:overrides',
|
|
now()->addHours(1),
|
|
function () {
|
|
$query = static::query();
|
|
|
|
if (static::hasColumn('is_active')) {
|
|
$query->where('is_active', true);
|
|
}
|
|
|
|
// Older installs may have used this table only for manual overrides.
|
|
if (static::hasColumn('is_override')) {
|
|
$query->where('is_override', true);
|
|
}
|
|
|
|
return $query->get()
|
|
->keyBy('tld')
|
|
->map(fn ($p) => [
|
|
'register' => $p->register_price,
|
|
'renew' => $p->renew_price,
|
|
'transfer' => $p->transfer_price,
|
|
'is_override' => static::hasColumn('is_override') ? (bool) $p->is_override : true,
|
|
])
|
|
->all();
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get all active TLDs with pricing.
|
|
*/
|
|
public static function activeTlds(): array
|
|
{
|
|
return Cache::remember(
|
|
'domain_pricing:active_tlds',
|
|
now()->addHours(1),
|
|
function () {
|
|
$query = static::query();
|
|
|
|
if (static::hasColumn('is_active')) {
|
|
$query->where('is_active', true);
|
|
}
|
|
|
|
if (static::hasColumn('sort_order')) {
|
|
$query->orderBy('sort_order');
|
|
}
|
|
|
|
return $query->orderBy('tld')->pluck('tld')->all();
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get featured TLDs for search.
|
|
*/
|
|
public static function featuredTlds(): array
|
|
{
|
|
return Cache::remember(
|
|
'domain_pricing:featured_tlds',
|
|
now()->addHours(1),
|
|
function () {
|
|
$query = static::query();
|
|
|
|
if (static::hasColumn('is_active')) {
|
|
$query->where('is_active', true);
|
|
}
|
|
|
|
if (static::hasColumn('is_featured')) {
|
|
$query->where('is_featured', true);
|
|
}
|
|
|
|
if (static::hasColumn('sort_order')) {
|
|
$query->orderBy('sort_order');
|
|
}
|
|
|
|
$tlds = $query->orderBy('tld')->pluck('tld')->all();
|
|
|
|
return $tlds !== [] ? $tlds : static::activeTlds();
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get pricing map for multiple TLDs.
|
|
*
|
|
* @return array<string, array{register: int, renew: int, transfer: int, currency: string}>
|
|
*/
|
|
public static function pricingMap(array $tlds = []): array
|
|
{
|
|
$query = static::query();
|
|
|
|
if (static::hasColumn('is_active')) {
|
|
$query->where('is_active', true);
|
|
}
|
|
|
|
if (! empty($tlds)) {
|
|
$query->whereIn('tld', array_map(fn ($t) => ltrim(strtolower($t), '.'), $tlds));
|
|
}
|
|
|
|
return $query->get()->keyBy('tld')->map(fn ($p) => [
|
|
'register' => $p->register_price,
|
|
'renew' => $p->renew_price,
|
|
'transfer' => $p->transfer_price,
|
|
'currency' => $p->currency,
|
|
])->all();
|
|
}
|
|
|
|
/**
|
|
* Clear pricing cache.
|
|
*/
|
|
public static function clearCache(): void
|
|
{
|
|
Cache::forget('domain_pricing:active_tlds');
|
|
Cache::forget('domain_pricing:featured_tlds');
|
|
Cache::forget('domain_pricing:overrides');
|
|
|
|
foreach (static::pluck('tld') as $tld) {
|
|
Cache::forget("domain_pricing:{$tld}");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Format price for display.
|
|
*/
|
|
public function formatRegisterPrice(): string
|
|
{
|
|
return $this->formatPrice($this->register_price);
|
|
}
|
|
|
|
public function formatRenewPrice(): string
|
|
{
|
|
return $this->formatPrice($this->renew_price);
|
|
}
|
|
|
|
public function formatTransferPrice(): string
|
|
{
|
|
return $this->formatPrice($this->transfer_price);
|
|
}
|
|
|
|
private function formatPrice(int $amountMinor): string
|
|
{
|
|
$symbol = $this->currency === 'GHS' ? 'GH₵' : $this->currency;
|
|
return $symbol . ' ' . number_format($amountMinor / 100, 2);
|
|
}
|
|
|
|
private static function hasColumn(string $column): bool
|
|
{
|
|
$cacheKey = static::class . ':' . $column;
|
|
|
|
if (! array_key_exists($cacheKey, self::$columnExists)) {
|
|
self::$columnExists[$cacheKey] = Schema::hasColumn((new static)->getTable(), $column);
|
|
}
|
|
|
|
return self::$columnExists[$cacheKey];
|
|
}
|
|
}
|