Aggregate ladl.link slugs from all platform apps in the link list.
Deploy Ladill Link / deploy (push) Successful in 29s
Deploy Ladill Link / deploy (push) Successful in 29s
Sync QR and storefront short codes from sibling apps via a platform catalog API so My Links shows every ladl.link URL in one place. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Link;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\ShortLink;
|
||||
use App\Services\Link\LinkBillingService;
|
||||
use App\Services\Link\LinkCatalogSyncService;
|
||||
use App\Services\Link\LinkManagerService;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -16,11 +17,14 @@ class LinkController extends Controller
|
||||
public function __construct(
|
||||
private LinkManagerService $links,
|
||||
private LinkBillingService $billing,
|
||||
private LinkCatalogSyncService $catalogSync,
|
||||
) {}
|
||||
|
||||
public function index(Request $request): View
|
||||
{
|
||||
$account = ladill_account();
|
||||
$this->catalogSync->syncForUser($account);
|
||||
|
||||
$links = ShortLink::query()
|
||||
->where('user_id', $account->id)
|
||||
->latest()
|
||||
@@ -77,6 +81,10 @@ class LinkController extends Controller
|
||||
{
|
||||
$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'],
|
||||
@@ -96,6 +104,11 @@ class LinkController extends Controller
|
||||
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.');
|
||||
|
||||
@@ -4,13 +4,19 @@ namespace App\Http\Controllers\Link;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\ShortLink;
|
||||
use App\Services\Link\LinkCatalogSyncService;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class OverviewController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private LinkCatalogSyncService $catalogSync,
|
||||
) {}
|
||||
|
||||
public function index(): View
|
||||
{
|
||||
$account = ladill_account();
|
||||
$this->catalogSync->syncForUser($account);
|
||||
$wallet = $account->getOrCreateLinkWallet();
|
||||
|
||||
$recentLinks = ShortLink::query()
|
||||
|
||||
@@ -10,9 +10,14 @@ class ShortLink extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'source_app',
|
||||
'source_kind',
|
||||
'source_ref',
|
||||
'slug',
|
||||
'label',
|
||||
'destination_url',
|
||||
'manage_url',
|
||||
'is_managed_here',
|
||||
'is_active',
|
||||
'clicks_total',
|
||||
'unique_clicks_total',
|
||||
@@ -21,6 +26,7 @@ class ShortLink extends Model
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'is_managed_here' => 'boolean',
|
||||
'is_active' => 'boolean',
|
||||
'clicks_total' => 'integer',
|
||||
'unique_clicks_total' => 'integer',
|
||||
@@ -57,4 +63,19 @@ class ShortLink extends Model
|
||||
{
|
||||
return $this->expires_at !== null && $this->expires_at->isPast();
|
||||
}
|
||||
|
||||
public function sourceAppLabel(): string
|
||||
{
|
||||
return match ($this->source_app) {
|
||||
'link' => 'Ladill Link',
|
||||
'qrplus' => 'QR Plus',
|
||||
'merchant' => 'Merchant',
|
||||
'events' => 'Events',
|
||||
'mini' => 'Mini',
|
||||
'give' => 'Give',
|
||||
'transfer' => 'Transfer',
|
||||
'platform' => 'Ladill',
|
||||
default => ucfirst((string) $this->source_app),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Link;
|
||||
|
||||
use App\Models\ShortLink;
|
||||
use App\Models\User;
|
||||
use App\Support\LadillLink;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* Mirrors platform-wide ladl.link slugs into the local catalog for unified listing.
|
||||
*/
|
||||
class LinkCatalogSyncService
|
||||
{
|
||||
public function __construct(
|
||||
private PlatformLinkCatalogClient $catalog,
|
||||
) {}
|
||||
|
||||
public function syncForUser(User $user): void
|
||||
{
|
||||
$remote = $this->catalog->linksForUser((string) $user->public_id);
|
||||
$remoteSlugs = collect($remote)->pluck('slug')->filter()->all();
|
||||
|
||||
DB::transaction(function () use ($user, $remote, $remoteSlugs): void {
|
||||
foreach ($remote as $row) {
|
||||
$slug = (string) ($row['slug'] ?? '');
|
||||
if ($slug === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ShortLink::query()
|
||||
->where('slug', $slug)
|
||||
->where('is_managed_here', true)
|
||||
->exists()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ShortLink::query()->updateOrCreate(
|
||||
['slug' => $slug],
|
||||
[
|
||||
'user_id' => $user->id,
|
||||
'label' => $row['label'] ?? null,
|
||||
'destination_url' => (string) ($row['destination_url'] ?? LadillLink::url($slug)),
|
||||
'source_app' => (string) ($row['source_app'] ?? 'platform'),
|
||||
'source_kind' => (string) ($row['source_kind'] ?? 'qr'),
|
||||
'source_ref' => isset($row['source_ref']) ? (string) $row['source_ref'] : null,
|
||||
'manage_url' => $row['manage_url'] ?? null,
|
||||
'is_managed_here' => false,
|
||||
'is_active' => (bool) ($row['is_active'] ?? true),
|
||||
'clicks_total' => (int) ($row['clicks_total'] ?? 0),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
if ($remoteSlugs !== []) {
|
||||
ShortLink::query()
|
||||
->where('user_id', $user->id)
|
||||
->where('is_managed_here', false)
|
||||
->whereNotIn('slug', $remoteSlugs)
|
||||
->delete();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,9 @@ class LinkManagerService
|
||||
|
||||
$link = ShortLink::create([
|
||||
'user_id' => $user->id,
|
||||
'source_app' => 'link',
|
||||
'source_kind' => 'redirect',
|
||||
'is_managed_here' => true,
|
||||
'slug' => $slug,
|
||||
'label' => $data['label'] ?? null,
|
||||
'destination_url' => $this->normalizeUrl($data['destination_url']),
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Link;
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
/**
|
||||
* Fetches ladl.link slugs created in other Ladill apps from the platform catalog API.
|
||||
*/
|
||||
class PlatformLinkCatalogClient
|
||||
{
|
||||
private function base(): string
|
||||
{
|
||||
$platform = rtrim((string) config('app.platform_url', 'https://ladill.com'), '/');
|
||||
|
||||
return $platform.'/api/link-catalog';
|
||||
}
|
||||
|
||||
private function token(): string
|
||||
{
|
||||
return (string) (config('billing.api_key') ?? '');
|
||||
}
|
||||
|
||||
/** @return list<array<string, mixed>> */
|
||||
public function linksForUser(string $publicId): array
|
||||
{
|
||||
if ($this->token() === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
$response = Http::withToken($this->token())
|
||||
->acceptJson()
|
||||
->timeout(12)
|
||||
->get($this->base().'/links', ['user' => $publicId]);
|
||||
|
||||
if (! $response->successful()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_values(array_filter(
|
||||
(array) ($response->json('links') ?? []),
|
||||
fn ($row) => is_array($row) && filled($row['slug'] ?? null),
|
||||
));
|
||||
} catch (\Throwable) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user