Files
isaaccladandCursor e3fd235139
Deploy Ladill Hosting / deploy (push) Successful in 26s
Unify SSO and surface legacy ResellerClub hosting orders on the dashboard.
Add silent OAuth and session keepalive, and show imported RC orders alongside
native hosting accounts.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-07 06:33:16 +00:00

450 lines
12 KiB
PHP

<?php
namespace App\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Arr;
class HostingOrder extends Model
{
use HasFactory;
public const STATUS_PENDING = 'pending';
public const STATUS_ACTIVE = 'active';
public const STATUS_SUSPENDED = 'suspended';
public const STATUS_CANCELLED = 'cancelled';
public const STATUS_EXPIRED = 'expired';
public const STATUS_FAILED = 'failed';
public const TYPE_SINGLE_DOMAIN = 'single_domain';
public const TYPE_MULTI_DOMAIN = 'multi_domain';
public const TYPE_RESELLER = 'reseller';
protected $fillable = [
'platform_id',
'user_id',
'domain_id',
'domain_name',
'rc_order_id',
'rc_customer_id',
'plan_name',
'hosting_type',
'status',
'ip_address',
'cpanel_url',
'cpanel_username',
'bandwidth_mb',
'disk_space_mb',
'expires_at',
'provisioned_at',
'meta',
'is_legacy_rc',
];
protected $casts = [
'expires_at' => 'datetime',
'provisioned_at' => 'datetime',
'meta' => 'array',
];
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function domain(): BelongsTo
{
return $this->belongsTo(Domain::class);
}
public function isActive(): bool
{
return $this->status === self::STATUS_ACTIVE;
}
public function isExpired(): bool
{
return $this->status === self::STATUS_EXPIRED
|| ($this->expires_at && $this->expires_at->isPast());
}
public function getDomainNameAttribute($value): string
{
return $value ?: $this->metaValue([
'domain_name',
'domainname',
'domain-name',
'domain',
'hostname',
'description',
], '');
}
public function getPlanNameAttribute($value): ?string
{
$resolved = $value ?: $this->metaValue([
'plan_name',
'planname',
'productcategory',
'productkey',
'description',
]);
return $resolved !== '' ? $resolved : null;
}
public function getPlanIdAttribute(): ?string
{
$resolved = $this->metaValue([
'plan_id',
'planid',
'plan-id',
'productkey',
'plan.name',
'entitytype.entitytypekey',
]);
return $resolved !== null && $resolved !== '' ? (string) $resolved : null;
}
public function getRcOrderIdAttribute($value): ?string
{
$resolved = $value ?: $this->metaValue([
'rc_order_id',
'order_id',
'orderid',
'order-id',
'entityid',
'entity-id',
]);
return $resolved !== '' ? (string) $resolved : null;
}
public function getRcCustomerIdAttribute($value): ?string
{
$resolved = $value ?: $this->metaValue([
'rc_customer_id',
'customer_id',
'customerid',
'customer-id',
]);
return $resolved !== '' ? (string) $resolved : null;
}
public function getIpAddressAttribute($value): ?string
{
$resolved = $value ?: $this->metaValue([
'ip_address',
'ipaddress',
'ip',
]);
return $resolved !== '' ? $resolved : null;
}
public function getCpanelUrlAttribute($value): ?string
{
$resolved = $value ?: $this->metaValue([
'cpanel_url',
'cpanelurl',
'controlpanelurl',
'access_url',
'url',
]);
if ($this->looksLikeTemporaryHostingUrl($resolved)) {
$resolved = null;
}
if (! $resolved && $preferredDomain = $this->preferredCpanelDomain()) {
return 'https://'.$preferredDomain.'/cpanel';
}
if (! $resolved && $this->ip_address) {
return 'https://'.$this->ip_address.':2083';
}
return $resolved !== '' ? $resolved : null;
}
public function getCpanelUsernameAttribute($value): ?string
{
$resolved = $value ?: $this->metaValue([
'cpanel_username',
'cpanelusername',
'access_username',
'username',
'siteadminusername',
]);
return $resolved !== '' ? $resolved : null;
}
/**
* @return array<int, string>
*/
public function getNameserversAttribute($value): array
{
$resolved = [];
if (is_array($value)) {
$resolved = $value;
} else {
$candidate = $this->metaValue([
'nameservers',
'nameserver',
'name_servers',
'ns',
'dns.nameservers',
'server.nameservers',
]);
if (is_array($candidate)) {
$resolved = $candidate;
} else {
$resolved = array_filter([
$this->metaValue(['ns1', 'nameserver1', 'name_server_1', 'server.ns1']),
$this->metaValue(['ns2', 'nameserver2', 'name_server_2', 'server.ns2']),
$this->metaValue(['ns3', 'nameserver3', 'name_server_3', 'server.ns3']),
$this->metaValue(['ns4', 'nameserver4', 'name_server_4', 'server.ns4']),
]);
}
}
return collect($resolved)
->map(fn ($item) => strtolower(trim((string) $item, " \t\n\r\0\x0B.")))
->filter()
->unique()
->values()
->all();
}
public function getWebsiteUrlAttribute(): ?string
{
$domain = strtolower(trim((string) $this->domain_name));
if ($domain === '' || filter_var($domain, FILTER_VALIDATE_IP)) {
return null;
}
return 'https://'.$domain;
}
public function getDirectUrlAttribute(): ?string
{
$resolved = $this->metaValue([
'tempurl',
'temp_url',
'temporary_url',
'server.tempurl',
'server.temporary_url',
]);
return $resolved !== '' ? (string) $resolved : null;
}
public function getDiskSpaceMbAttribute($value): ?int
{
$resolved = $value ?? $this->metaValue([
'disk_space_mb',
'diskspace',
'disk_space',
]);
return is_numeric($resolved) && (int) $resolved >= 0 ? (int) $resolved : null;
}
public function getBandwidthMbAttribute($value): ?int
{
$resolved = $value ?? $this->metaValue([
'bandwidth_mb',
'bandwidth',
]);
return is_numeric($resolved) && (int) $resolved >= 0 ? (int) $resolved : null;
}
public function getStatusAttribute($value): string
{
$candidate = $value;
if ($candidate === null || $candidate === '' || $candidate === self::STATUS_PENDING) {
$candidate = $this->metaValue([
'status',
'currentstatus',
'orderstatus',
], $candidate ?: self::STATUS_PENDING);
}
return $this->normalizeStatus((string) $candidate);
}
public function getProvisionedAtAttribute($value): ?Carbon
{
if ($value instanceof Carbon) {
return $value;
}
if ($value) {
return Carbon::parse($value);
}
$timestamp = $this->metaValue([
'provisioned_at',
'creation_time',
'creationtime',
'creation-date',
]);
if ($timestamp === null || $timestamp === '') {
return null;
}
try {
return is_numeric($timestamp)
? Carbon::createFromTimestamp((int) $timestamp)
: Carbon::parse((string) $timestamp);
} catch (\Throwable) {
return null;
}
}
public function getExpiresAtAttribute($value): ?Carbon
{
if ($value instanceof Carbon) {
return $value;
}
if ($value) {
return Carbon::parse($value);
}
$timestamp = $this->metaValue([
'expires_at',
'end_time',
'endtime',
'expirytime',
'expiry-date',
]);
if ($timestamp === null || $timestamp === '') {
return null;
}
try {
return is_numeric($timestamp)
? Carbon::createFromTimestamp((int) $timestamp)
: Carbon::parse((string) $timestamp);
} catch (\Throwable) {
return null;
}
}
/**
* @return array<string, mixed>
*/
private function normalizedMeta(): array
{
$meta = $this->getAttributes()['meta'] ?? $this->meta ?? [];
while (is_string($meta)) {
$decoded = json_decode($meta, true);
$meta = json_last_error() === JSON_ERROR_NONE ? $decoded : [];
}
return is_array($meta) ? $meta : [];
}
private function metaValue(array $keys, mixed $default = null): mixed
{
$meta = $this->normalizedMeta();
$sources = [$meta];
if (is_array($meta['raw'] ?? null)) {
$sources[] = $meta['raw'];
}
foreach ($sources as $source) {
foreach ($keys as $key) {
$nested = Arr::get($source, $key);
if ($nested !== null && $nested !== '') {
return $nested;
}
if (array_key_exists($key, $source) && $source[$key] !== null && $source[$key] !== '') {
return $source[$key];
}
}
}
return $default;
}
private function normalizeStatus(string $status): string
{
return match (strtolower(trim($status))) {
'active', 'active (paid)' => self::STATUS_ACTIVE,
'suspended', 'inactive' => self::STATUS_SUSPENDED,
'deleted', 'cancelled' => self::STATUS_CANCELLED,
'expired' => self::STATUS_EXPIRED,
'failed' => self::STATUS_FAILED,
default => self::STATUS_PENDING,
};
}
private function looksLikeTemporaryHostingUrl(?string $value): bool
{
$normalized = strtolower(trim((string) $value));
if ($normalized === '') {
return false;
}
return str_contains($normalized, 'tempwebhost.net')
|| str_contains($normalized, '/~');
}
private function preferredCpanelDomain(): ?string
{
$domain = strtolower(trim((string) $this->domain_name));
if ($domain === '' || filter_var($domain, FILTER_VALIDATE_IP)) {
return null;
}
if ($this->relationLoaded('domain') && $this->domain) {
return $this->domainIsActive($this->domain) ? $domain : null;
}
if ($this->domain_id) {
$linkedDomain = $this->domain()->first();
if ($linkedDomain) {
return $this->domainIsActive($linkedDomain) ? $domain : null;
}
}
$matchingDomain = Domain::query()
->where('host', $domain)
->latest('id')
->first();
if ($matchingDomain) {
return $this->domainIsActive($matchingDomain) ? $domain : null;
}
return $domain;
}
private function domainIsActive(Domain $domain): bool
{
return (string) $domain->status === 'verified'
&& (string) $domain->onboarding_state === Domain::STATE_ACTIVE;
}
}