Files
ladill-link/app/Http/Controllers/Link/LinkController.php
T
isaacclad d45f4b5c58
Deploy Ladill Link / deploy (push) Successful in 1m55s
Make Ladill Link short URLs fully dynamic at click time.
Forward query strings and path suffixes to the live destination, resolve public hosts from config/custom domains instead of hardcoding ladl.link, and keep proxy Location rewrites on the visitor host.
2026-07-16 20:05:43 +00:00

150 lines
5.1 KiB
PHP

<?php
namespace App\Http\Controllers\Link;
use App\Http\Controllers\Controller;
use App\Models\ShortLink;
use App\Services\Link\LinkAnalyticsService;
use App\Services\Link\LinkBillingService;
use App\Services\Link\LinkCatalogSyncService;
use App\Services\Link\LinkManagerService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
use RuntimeException;
class LinkController extends Controller
{
public function __construct(
private LinkManagerService $links,
private LinkBillingService $billing,
private LinkCatalogSyncService $catalogSync,
private LinkAnalyticsService $analytics,
) {}
public function index(Request $request): View
{
$account = ladill_account();
$this->catalogSync->syncForUser($account);
$links = ShortLink::query()
->where('user_id', $account->id)
->latest()
->paginate(20);
$wallet = $account->getOrCreateLinkWallet();
return view('links.index', [
'links' => $links,
'wallet' => $wallet,
'balance' => $this->billing->balanceCedis($account),
'canCreate' => $this->billing->canCreate($account),
'pricePerLink' => \App\Models\LinkWallet::pricePerLink(),
'topupUrl' => ladill_account_url('wallet'),
'publicHost' => parse_url($account->publicLinkBaseUrl(), PHP_URL_HOST) ?: ShortLink::publicHost(),
'publicBaseUrl' => $account->publicLinkBaseUrl(),
]);
}
public function create(): View
{
$account = ladill_account();
return view('links.create', [
'canCreate' => $this->billing->canCreate($account),
'pricePerLink' => \App\Models\LinkWallet::pricePerLink(),
'publicHost' => parse_url($account->publicLinkBaseUrl(), PHP_URL_HOST) ?: ShortLink::publicHost(),
'publicBaseUrl' => $account->publicLinkBaseUrl(),
]);
}
public function store(Request $request): RedirectResponse
{
$validated = $request->validate([
'destination_url' => ['required', 'string', 'max:2048'],
'label' => ['nullable', 'string', 'max:120'],
'custom_slug' => ['nullable', 'string', 'max:20', 'regex:/^[a-z0-9][a-z0-9-]{1,18}[a-z0-9]$/'],
'expires_at' => ['nullable', 'date', 'after:now'],
]);
try {
$link = $this->links->create(ladill_account(), $validated);
} catch (RuntimeException $e) {
return back()->withInput()->withErrors(['balance' => $e->getMessage()]);
}
return redirect()
->route('user.links.show', $link)
->with('success', 'Short link created.');
}
public function show(ShortLink $link): View
{
$this->authorizeLink($link);
return view('links.show', [
'link' => $link,
'summary' => $this->analytics->summaryForLink($link),
'dailyClicks' => $this->analytics->dailyClicksForLink($link, 30),
'devices' => $this->analytics->breakdownForLink($link, 'device_type'),
'browsers' => $this->analytics->breakdownForLink($link, 'browser'),
'platforms' => $this->analytics->breakdownForLink($link, 'os'),
'countries' => $this->analytics->breakdownForLink($link, 'country'),
'referrers' => $this->analytics->referrersForLink($link),
'recentClicks' => $this->analytics->recentClicksForLink($link),
]);
}
public function update(Request $request, ShortLink $link): RedirectResponse
{
$this->authorizeLink($link);
if (! $link->is_managed_here) {
return back()->withErrors(['link' => 'This link is managed in '.$link->sourceAppLabel().'.']);
}
$validated = $request->validate([
'destination_url' => ['sometimes', 'required', 'string', 'max:2048'],
'label' => ['nullable', 'string', 'max:120'],
'is_active' => ['sometimes', 'boolean'],
'expires_at' => ['nullable', 'date'],
]);
try {
$this->links->update($link, $validated);
} catch (RuntimeException $e) {
return back()->withInput()->withErrors(['destination_url' => $e->getMessage()]);
}
return back()->with('success', 'Link updated.');
}
public function destroy(ShortLink $link): RedirectResponse
{
$this->authorizeLink($link);
if (! $link->is_managed_here) {
return back()->withErrors(['link' => 'This link is managed in '.$link->sourceAppLabel().'.']);
}
$link->delete();
return redirect()->route('user.links.index')->with('success', 'Link deleted.');
}
public function checkSlug(Request $request): \Illuminate\Http\JsonResponse
{
$slug = strtolower((string) $request->query('slug', ''));
$taken = $slug !== '' && ShortLink::where('slug', $slug)->exists();
return response()->json(['available' => ! $taken]);
}
private function authorizeLink(ShortLink $link): void
{
if ($link->user_id !== ladill_account()->id) {
abort(403);
}
}
}