Files
ladill-hosting/app/Models/HostingOrder.php
T
isaaccladandCursor d532f5b206
Deploy Ladill Hosting / deploy (push) Successful in 23s
Fix legacy RC hosting orders not appearing after app extraction.
Import RC service orders in hosting:import, map platform_id on legacy models, and wire legacy order manage links to hosting.legacy.show.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-06 20:31:29 +00:00

439 lines
11 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;
}
}
private function metaValue(array $keys, mixed $default = null): mixed
{
$meta = $this->meta ?? [];
$sources = [];
if (is_array($meta)) {
$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;
}
}