Add Ladill Link URL shortener on ladl.link.
Management UI at link.ladill.com with GHS 0.05 per link wallet billing, click analytics, and legacy fallback to ladill.com/q for QR ecosystem codes. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Link;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\ShortLink;
|
||||
use App\Services\Link\LinkBillingService;
|
||||
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,
|
||||
) {}
|
||||
|
||||
public function index(Request $request): View
|
||||
{
|
||||
$account = ladill_account();
|
||||
$links = ShortLink::query()
|
||||
->where('user_id', $account->id)
|
||||
->latest()
|
||||
->paginate(20);
|
||||
|
||||
return view('links.index', [
|
||||
'links' => $links,
|
||||
'canCreate' => $this->billing->canCreate($account),
|
||||
'pricePerLink' => \App\Models\LinkWallet::pricePerLink(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function create(): View
|
||||
{
|
||||
$account = ladill_account();
|
||||
|
||||
return view('links.create', [
|
||||
'canCreate' => $this->billing->canCreate($account),
|
||||
'pricePerLink' => \App\Models\LinkWallet::pricePerLink(),
|
||||
]);
|
||||
}
|
||||
|
||||
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,
|
||||
'recentClicks' => $link->clicks()->latest('clicked_at')->limit(20)->get(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(Request $request, ShortLink $link): RedirectResponse
|
||||
{
|
||||
$this->authorizeLink($link);
|
||||
|
||||
$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);
|
||||
$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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user