Deploy Ladill Hosting / deploy (push) Successful in 23s
Consolidate the inline HostingAccountMember lookups (Overview/Search/Hosting Product/Afia controllers) into HostingAccessResolver, which can source hosting developer-access from the monolith's central /identity/hosting/developer-access (correlating cPanel usernames to local account ids). Shadow mode by default (HOSTING_CENTRAL_DEV_ACCESS=false): local hosting_account_members stays authoritative and divergence from central is logged; flip the flag to cut over. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
144 lines
5.6 KiB
PHP
144 lines
5.6 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Hosting;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\CustomerHostingOrder;
|
|
use App\Models\HostedSite;
|
|
use App\Models\HostingAccount;
|
|
use App\Models\HostingOrder;
|
|
use App\Models\HostingProduct;
|
|
use App\Models\User;
|
|
use App\Services\Hosting\HostingAccessResolver;
|
|
use Illuminate\Database\Eloquent\Collection;
|
|
use Illuminate\Support\Collection as SupportCollection;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\View\View;
|
|
|
|
class OverviewController extends Controller
|
|
{
|
|
/** Authenticated hosting dashboard (overview hub). */
|
|
public function index(Request $request): View
|
|
{
|
|
$user = $request->user();
|
|
$accounts = $this->sharedHostingAccounts($user);
|
|
$legacyOrders = $this->legacySharedHostingOrders($user);
|
|
$accountIds = $accounts->pluck('id');
|
|
|
|
$pendingOrders = CustomerHostingOrder::query()
|
|
->forUser($user->id)
|
|
->whereHas('product', fn ($q) => $q->whereIn('type', $this->sharedTypes()))
|
|
->whereIn('status', [
|
|
CustomerHostingOrder::STATUS_PENDING_PAYMENT,
|
|
CustomerHostingOrder::STATUS_PENDING_APPROVAL,
|
|
CustomerHostingOrder::STATUS_APPROVED,
|
|
CustomerHostingOrder::STATUS_PROVISIONING,
|
|
])
|
|
->with('product')
|
|
->latest()
|
|
->take(5)
|
|
->get();
|
|
|
|
$linkedDomains = ($accountIds->isEmpty()
|
|
? 0
|
|
: HostedSite::query()->whereIn('hosting_account_id', $accountIds)->count())
|
|
+ $legacyOrders->filter(fn (HostingOrder $order) => filled($order->domain_name))->count();
|
|
|
|
return view('hosting.dashboard', [
|
|
'accounts' => $accounts,
|
|
'legacyOrders' => $legacyOrders,
|
|
'activeCount' => $accounts->where('status', HostingAccount::STATUS_ACTIVE)->count()
|
|
+ $legacyOrders->where('status', HostingOrder::STATUS_ACTIVE)->count(),
|
|
'expiringCount' => $accounts
|
|
->where('status', HostingAccount::STATUS_ACTIVE)
|
|
->filter(fn (HostingAccount $account) => $account->expires_at !== null && $account->expires_at->between(now(), now()->addDays(30)))
|
|
->count()
|
|
+ $legacyOrders
|
|
->where('status', HostingOrder::STATUS_ACTIVE)
|
|
->filter(fn (HostingOrder $order) => $order->expires_at !== null && $order->expires_at->between(now(), now()->addDays(30)))
|
|
->count(),
|
|
'pendingOrderCount' => CustomerHostingOrder::query()
|
|
->forUser($user->id)
|
|
->whereHas('product', fn ($q) => $q->whereIn('type', $this->sharedTypes()))
|
|
->whereIn('status', [
|
|
CustomerHostingOrder::STATUS_PENDING_PAYMENT,
|
|
CustomerHostingOrder::STATUS_PENDING_APPROVAL,
|
|
CustomerHostingOrder::STATUS_APPROVED,
|
|
CustomerHostingOrder::STATUS_PROVISIONING,
|
|
])
|
|
->count(),
|
|
'linkedDomains' => $linkedDomains,
|
|
'recent' => $accounts->take(5),
|
|
'recentLegacyOrders' => $legacyOrders->take(5),
|
|
'pendingOrders' => $pendingOrders,
|
|
]);
|
|
}
|
|
|
|
public function accounts(Request $request): View
|
|
{
|
|
$user = $request->user();
|
|
|
|
return view('hosting.accounts-list', [
|
|
'title' => 'Your Hosting Accounts',
|
|
'accounts' => $this->sharedHostingAccounts($user),
|
|
'legacyOrders' => $this->legacySharedHostingOrders($user),
|
|
]);
|
|
}
|
|
|
|
/** @return list<string> */
|
|
private function sharedTypes(): array
|
|
{
|
|
return [
|
|
HostingProduct::TYPE_SINGLE_DOMAIN,
|
|
HostingProduct::TYPE_MULTI_DOMAIN,
|
|
HostingProduct::TYPE_WORDPRESS,
|
|
];
|
|
}
|
|
|
|
/** @return Collection<int, HostingAccount> */
|
|
private function sharedHostingAccounts(User $user): Collection
|
|
{
|
|
$sharedTypes = $this->sharedTypes();
|
|
|
|
$sharedAccountIds = app(HostingAccessResolver::class)->sharedAccountIds($user);
|
|
|
|
$hostingAccounts = HostingAccount::query()
|
|
->where(function ($query) use ($user, $sharedAccountIds) {
|
|
$query->where('user_id', $user->id);
|
|
if ($sharedAccountIds->isNotEmpty()) {
|
|
$query->orWhereIn('id', $sharedAccountIds);
|
|
}
|
|
})
|
|
->whereHas('product', fn ($q) => $q->whereIn('type', $sharedTypes))
|
|
->with(['product', 'sites'])
|
|
->latest()
|
|
->get();
|
|
|
|
$orderAccounts = CustomerHostingOrder::query()
|
|
->forUser($user->id)
|
|
->whereHas('product', fn ($q) => $q->whereIn('type', $sharedTypes))
|
|
->with(['hostingAccount.product', 'hostingAccount.sites'])
|
|
->latest()
|
|
->get()
|
|
->filter(fn (CustomerHostingOrder $order) => $order->hostingAccount !== null)
|
|
->map(fn (CustomerHostingOrder $order) => $order->hostingAccount);
|
|
|
|
return $hostingAccounts->merge($orderAccounts)->unique('id')->values();
|
|
}
|
|
|
|
/** @return SupportCollection<int, HostingOrder> */
|
|
private function legacySharedHostingOrders(User $user): SupportCollection
|
|
{
|
|
return HostingOrder::query()
|
|
->where('user_id', $user->id)
|
|
->whereIn('hosting_type', [
|
|
HostingOrder::TYPE_SINGLE_DOMAIN,
|
|
HostingOrder::TYPE_MULTI_DOMAIN,
|
|
HostingOrder::TYPE_RESELLER,
|
|
])
|
|
->whereNotIn('status', [HostingOrder::STATUS_CANCELLED])
|
|
->latest()
|
|
->get();
|
|
}
|
|
}
|