Remove obsolete Livewire fuel search components and consolidate pricing tiers
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (8.3) (push) Has been cancelled
tests / ci (8.4) (push) Has been cancelled
tests / ci (8.5) (push) Has been cancelled

- Delete unused Livewire Search test and fuel type select Blade component
- Move subscription webhook listener from EventServiceProvider to AppServiceProvider
- Add FUEL_TYPES global config to app layout for client-side use
- Add Billable trait to User model and include email_verified_at in fillable
- Implement monthly/annual cadence toggle with pricing display and smart CTA routing on homepage
- Update VerifyApiKeyMiddlewareTest to use e10 instead of petrol
- Refactor PollFuelPrices to auto-refresh stale stations based on last_seen_at
- Add incremental polling with cached timestamp and effective-start-timestamp param to FuelPriceService
- Normalize amenities/fuel_types from API objects to flat arrays, skip stations missing required fields
- Log response body on API failures in ApiLogger
- Default homepage sort to 'reliable' instead of 'price'
This commit is contained in:
Ovidiu U
2026-04-20 14:12:15 +01:00
parent aec547cd86
commit 5acb99c9e3
33 changed files with 739 additions and 391 deletions

View File

@@ -1,17 +1,25 @@
<?php
use App\Enums\FuelType;
use App\Http\Controllers\Api\AuthController;
use App\Http\Controllers\Api\PredictionController;
use App\Http\Controllers\Api\StationController;
use App\Http\Controllers\Api\StatsController;
use App\Http\Controllers\Api\UserController;
use App\Http\Middleware\VerifyApiKey;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Route;
// Public endpoints (no API key required)
Route::post('/auth/register', [AuthController::class, 'register']);
Route::post('/auth/login', [AuthController::class, 'login']);
Route::get('/fuel-types', function () {
return Cache::remember('api:fuel-types', now()->addDay(), fn () => collect(FuelType::cases())
->map(fn (FuelType $case) => ['value' => $case->value, 'label' => $case->label()])
->values());
});
// Protected endpoints (API key required)
Route::middleware(['throttle:60,1', VerifyApiKey::class])->group(function (): void {
Route::get('/stations', [StationController::class, 'index']);

View File

@@ -9,14 +9,17 @@ Artisan::command('inspire', function () {
$this->comment(Inspiring::quote());
})->purpose('Display an inspiring quote');
// Poll for price changes every 15 minutes
// Poll for price changes every 30 minutes — API updates within 30 min of any
// change. The command auto-refreshes station metadata once per day on the
// first poll after midnight, and uses incremental fetch thereafter.
Schedule::command('fuel:poll')
->everyFifteenMinutes()
->everyThirtyMinutes()
->withoutOverlapping()
->onOneServer()
->runInBackground();
// Full refresh (station metadata + prices) once daily at 3am
// Safety-net full station + price refresh at 3am in case the auto-refresh
// staleness check is skipped for any reason.
Schedule::command('fuel:poll --full')
->dailyAt('03:00')
->withoutOverlapping()

View File

@@ -1,5 +1,6 @@
<?php
use App\Http\Controllers\BillingController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Route;
@@ -16,5 +17,12 @@ Route::get('/logout', function (Request $request) {
return redirect('/');
})->middleware('auth')->name('logout');
Route::middleware(['auth'])->prefix('billing')->name('billing.')->group(function () {
Route::get('/checkout/{tier}/{cadence}', [BillingController::class, 'checkout'])->name('checkout');
Route::get('/portal', [BillingController::class, 'portal'])->name('portal');
Route::get('/success', [BillingController::class, 'success'])->name('success');
Route::get('/cancel', [BillingController::class, 'cancel'])->name('cancel');
});
// SPA catch-all — must be last
Route::get('/{any?}', fn () => view('app'))->where('any', '.*')->name('home');