Redirect legacy /q/* URLs to canonical ladl.link addresses.
Deploy Ladill Events / deploy (push) Successful in 27s
Deploy Ladill Events / deploy (push) Successful in 27s
External /q requests 301/307 to ladl.link; internal X-Ladill-Internal requests still serve content for platform proxying. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use App\Support\LadillLink;
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
|
class RedirectLegacyQrToLadillLink
|
||||||
|
{
|
||||||
|
public function handle(Request $request, Closure $next): Response
|
||||||
|
{
|
||||||
|
if (! preg_match('#^q/[a-z0-9]#i', ltrim($request->path(), '/'))) {
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LadillLink::isInternalRequest($request)) {
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
return LadillLink::legacyRedirect($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ use App\Services\Billing\BillingClient;
|
|||||||
use App\Services\Billing\PaystackService;
|
use App\Services\Billing\PaystackService;
|
||||||
use App\Services\Billing\SmsService;
|
use App\Services\Billing\SmsService;
|
||||||
use App\Services\Pay\PayClient;
|
use App\Services\Pay\PayClient;
|
||||||
|
use App\Support\LadillLink;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
|
|
||||||
@@ -125,7 +126,7 @@ class EventRegistrationService
|
|||||||
'fee_tier' => $feeTier,
|
'fee_tier' => $feeTier,
|
||||||
'source_service' => 'events',
|
'source_service' => 'events',
|
||||||
'source_ref' => (string) $qrCode->id,
|
'source_ref' => (string) $qrCode->id,
|
||||||
'callback_url' => route('qr.public.event.callback', ['shortCode' => $qrCode->short_code]),
|
'callback_url' => LadillLink::path($qrCode->short_code, 'register/callback'),
|
||||||
'customer_name' => $registration->attendee_name,
|
'customer_name' => $registration->attendee_name,
|
||||||
'customer_email' => $email,
|
'customer_email' => $email,
|
||||||
'customer_phone' => $registration->attendee_phone,
|
'customer_phone' => $registration->attendee_phone,
|
||||||
|
|||||||
@@ -2,11 +2,16 @@
|
|||||||
|
|
||||||
namespace App\Support;
|
namespace App\Support;
|
||||||
|
|
||||||
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Canonical short-link URLs for the Ladill platform (ladl.link).
|
* Canonical short-link URLs for the Ladill platform (ladl.link).
|
||||||
*/
|
*/
|
||||||
final class LadillLink
|
final class LadillLink
|
||||||
{
|
{
|
||||||
|
public const INTERNAL_HEADER = 'X-Ladill-Internal';
|
||||||
|
|
||||||
public static function baseUrl(): string
|
public static function baseUrl(): string
|
||||||
{
|
{
|
||||||
$domain = (string) config('link.public_domain', 'ladl.link');
|
$domain = (string) config('link.public_domain', 'ladl.link');
|
||||||
@@ -23,4 +28,33 @@ final class LadillLink
|
|||||||
{
|
{
|
||||||
return self::url($slug).'/'.ltrim($suffix, '/');
|
return self::url($slug).'/'.ltrim($suffix, '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function isInternalRequest(Request $request): bool
|
||||||
|
{
|
||||||
|
return $request->header(self::INTERNAL_HEADER) === '1';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function legacyRedirect(Request $request): RedirectResponse
|
||||||
|
{
|
||||||
|
$shortCode = $request->route('shortCode');
|
||||||
|
if (! is_string($shortCode) || $shortCode === '') {
|
||||||
|
abort(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
$path = ltrim($request->path(), '/');
|
||||||
|
$prefix = 'q/'.$shortCode;
|
||||||
|
$suffix = '';
|
||||||
|
if ($path !== $prefix && str_starts_with($path, $prefix.'/')) {
|
||||||
|
$suffix = substr($path, strlen($prefix) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$target = $suffix === '' ? self::url($shortCode) : self::path($shortCode, $suffix);
|
||||||
|
if ($qs = $request->getQueryString()) {
|
||||||
|
$target .= '?'.$qs;
|
||||||
|
}
|
||||||
|
|
||||||
|
$status = in_array($request->method(), ['GET', 'HEAD'], true) ? 301 : 307;
|
||||||
|
|
||||||
|
return redirect()->away($target, $status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ return Application::configure(basePath: dirname(__DIR__))
|
|||||||
]);
|
]);
|
||||||
$middleware->alias([
|
$middleware->alias([
|
||||||
'platform.session' => \App\Http\Middleware\EnsurePlatformSession::class,
|
'platform.session' => \App\Http\Middleware\EnsurePlatformSession::class,
|
||||||
|
'redirect.legacy.qr' => \App\Http\Middleware\RedirectLegacyQrToLadillLink::class,
|
||||||
]);
|
]);
|
||||||
})
|
})
|
||||||
->withExceptions(function (Exceptions $exceptions): void {
|
->withExceptions(function (Exceptions $exceptions): void {
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ Route::get('/sso/logout-frontchannel', [SsoLoginController::class, 'frontchannel
|
|||||||
Route::get('/sso/platform-signed-out', [SsoLoginController::class, 'platformSignedOut'])->name('sso.platform-signed-out');
|
Route::get('/sso/platform-signed-out', [SsoLoginController::class, 'platformSignedOut'])->name('sso.platform-signed-out');
|
||||||
Route::get('/signed-out', fn () => auth()->check() ? redirect()->route('events.dashboard') : view('events.signed-out'))->name('events.signed-out');
|
Route::get('/signed-out', fn () => auth()->check() ? redirect()->route('events.dashboard') : view('events.signed-out'))->name('events.signed-out');
|
||||||
|
|
||||||
|
Route::middleware('redirect.legacy.qr')->group(function () {
|
||||||
Route::get('/q/{shortCode}', [QrScanController::class, 'resolve'])->name('qr.public.resolve');
|
Route::get('/q/{shortCode}', [QrScanController::class, 'resolve'])->name('qr.public.resolve');
|
||||||
Route::get('/q/{shortCode}/view', [QrScanController::class, 'view'])->name('qr.public.view');
|
Route::get('/q/{shortCode}/view', [QrScanController::class, 'view'])->name('qr.public.view');
|
||||||
Route::get('/q/{shortCode}/event-logo', [QrScanController::class, 'eventLogo'])->name('qr.public.event.logo');
|
Route::get('/q/{shortCode}/event-logo', [QrScanController::class, 'eventLogo'])->name('qr.public.event.logo');
|
||||||
@@ -47,6 +48,7 @@ Route::get('/q/{shortCode}/itinerary-cover', [QrScanController::class, 'itinerar
|
|||||||
Route::post('/q/{shortCode}/register', [EventRegistrationController::class, 'register'])->name('qr.public.event.register');
|
Route::post('/q/{shortCode}/register', [EventRegistrationController::class, 'register'])->name('qr.public.event.register');
|
||||||
Route::get('/q/{shortCode}/register/callback', [EventRegistrationController::class, 'callback'])->name('qr.public.event.callback');
|
Route::get('/q/{shortCode}/register/callback', [EventRegistrationController::class, 'callback'])->name('qr.public.event.callback');
|
||||||
Route::get('/q/{shortCode}/registered/{ref}', [EventRegistrationController::class, 'confirmed'])->name('qr.public.event.confirmed');
|
Route::get('/q/{shortCode}/registered/{ref}', [EventRegistrationController::class, 'confirmed'])->name('qr.public.event.confirmed');
|
||||||
|
});
|
||||||
|
|
||||||
Route::middleware(['auth', 'platform.session'])->group(function () {
|
Route::middleware(['auth', 'platform.session'])->group(function () {
|
||||||
Route::get('/wallet/balance', [WalletBalanceController::class, 'balance'])->name('wallet.balance');
|
Route::get('/wallet/balance', [WalletBalanceController::class, 'balance'])->name('wallet.balance');
|
||||||
|
|||||||
Reference in New Issue
Block a user