GG.deals clone: CodeIgniter 4 + MongoDB game price tracker

- Full gg.deals-style UI: deal feed with filters, game pages with price
  history charts, rankings, vouchers, news, prepaids, subscriptions,
  bundles, franchises, wishlist, API docs
- Real data crawlers:
  * gg:crawl (Steam Store API + GOG Catalog + Epic free games)
  * gg:catalog (252K game slugs from gg.deals sitemaps)
  * gg:ggdeals (per-game prices via FlareSolverr session, JSON-LD parsing)
  * gg:seed / gg:crawl-covers (demo seed + Steam CDN cover art)
- 24/7 self-updating scheduler (scripts/scheduler_loop.php, lockfile-protected)
- Tailwind v4 dark/light theme, Alpine.js, Chart.js
This commit is contained in:
deepseek
2026-08-23 07:06:02 +00:00
commit fff7a4b9b9
146 changed files with 15691 additions and 0 deletions
+75
View File
@@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
namespace App\Controllers;
use App\Models\GameModel;
use App\Models\StoreModel;
use CodeIgniter\Controller;
class Games extends Controller
{
public function index(string $genre = ''): string
{
$model = new GameModel();
$request = service('request');
// genre map (slug => name)
$genres = [];
foreach ($model->genres() as $g) {
$slug = url_title((string) $g['_id'], '-', true);
$genres[$slug] = ['name' => $g['_id'], 'count' => $g['count']];
}
ksort($genres);
$genreName = '';
if ($genre !== '') {
if (! isset($genres[$genre])) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
}
$genreName = $genres[$genre]['name'];
}
// ---- filters (same shape as the deal feed) ----
$filters = [
'stores' => array_values(array_filter(array_map('strval', (array) $request->getGet('stores')))),
'kind' => (string) ($request->getGet('kind') ?? ''),
'min_price' => $request->getGet('min_price') !== null ? (string) $request->getGet('min_price') : '',
'max_price' => $request->getGet('max_price') !== null ? (string) $request->getGet('max_price') : '',
'min_cut' => (int) ($request->getGet('min_cut') ?? 0),
'hl_only' => (bool) $request->getGet('hl_only'),
'sort' => (string) ($request->getGet('sort') ?? 'trending'),
'q' => trim((string) ($request->getGet('q') ?? '')),
'price_tier' => (string) ($request->getGet('price_tier') ?? ''),
'discount_tier' => (string) ($request->getGet('discount_tier') ?? ''),
'deal_rating' => (string) ($request->getGet('deal_rating') ?? ''),
'release_range' => (string) ($request->getGet('release_range') ?? ''),
'time_range' => (string) ($request->getGet('time_range') ?? ''),
'genre' => $genreName,
'genres' => array_values(array_filter(array_map('strval', (array) $request->getGet('genres')))),
];
$page = max(1, (int) $request->getGet('page'));
$perPage = 20;
$feed = $model->feed($filters, $page, $perPage);
$data = [
'pageTitle' => ($genreName !== '' ? ucwords($genreName) . ' games — ' : '') . 'GG.deals',
'filters' => $filters,
'feed' => $feed['rows'],
'total' => $feed['total'],
'pages' => $feed['pages'],
'page' => $page,
'perPage' => $perPage,
'genres' => $genres,
'activeGenre' => $genre,
'activeGenreName' => $genreName,
'stores' => (new StoreModel())->all(),
'stats' => $model->stats(),
];
return view('games', $data);
}
}