Files
shopPRO/autoload/admin/App.php
Jacek Pyziak de11afb003 ver. 0.294: Code review complete — 96/96 classes, 27 fixes across all layers
Full codebase review of autoload/ directory (96 classes, ~1144 methods).
Fixes: null safety (query/find guards), redundant DI bypass, undefined
variables, missing globals, and Imagick WebP mime type bug in Helpers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 15:26:07 +01:00

445 lines
14 KiB
PHP

<?php
namespace admin;
class App
{
const APP_SECRET_KEY = 'c3cb2537d25c0efc9e573d059d79c3b8';
/**
* Mapa nowych kontrolerów: module => fabryka kontrolera (DI)
*/
private static $newControllers = [];
public static function finalize_admin_login( array $user, string $domain, string $cookie_name, bool $remember = false )
{
\Shared\Helpers\Helpers::set_session( 'user', $user );
\Shared\Helpers\Helpers::delete_session( 'twofa_pending' );
if ( $remember ) {
$payloadArr = [
'login' => $user['login'],
'ts' => time()
];
$json = json_encode( $payloadArr, JSON_UNESCAPED_SLASHES );
$sig = hash_hmac( 'sha256', $json, self::APP_SECRET_KEY );
$payload = base64_encode( $json . '.' . $sig );
setcookie( $cookie_name, $payload, [
'expires' => time() + ( 86400 * 14 ),
'path' => '/',
'domain' => $domain,
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
] );
}
}
public static function special_actions()
{
global $mdb;
$sa = \Shared\Helpers\Helpers::get( 's-action' );
if ( !$sa ) return;
$domain = preg_replace( '/^www\./', '', $_SERVER['SERVER_NAME'] );
$cookie_name = 'admin_remember_' . str_replace( '.', '-', $domain );
$users = new \Domain\User\UserRepository( $mdb );
switch ( $sa )
{
case 'user-logon':
$login = \Shared\Helpers\Helpers::get( 'login' );
$pass = \Shared\Helpers\Helpers::get( 'password' );
$result = $users->logon( $login, $pass );
if ( $result == 1 )
{
$user = $users->details( $login );
if ( !$user ) {
\Shared\Helpers\Helpers::alert( 'Błąd logowania.' );
header( 'Location: /admin/' );
exit;
}
if ( $user['twofa_enabled'] == 1 )
{
\Shared\Helpers\Helpers::set_session( 'twofa_pending', [
'uid' => (int) $user['id'],
'login' => $login,
'remember' => (bool) \Shared\Helpers\Helpers::get( 'remember' ),
'started' => time(),
] );
if ( !$users->sendTwofaCode( (int) $user['id'] ) )
{
\Shared\Helpers\Helpers::alert( 'Nie udało się wysłać kodu 2FA. Spróbuj ponownie.' );
\Shared\Helpers\Helpers::delete_session( 'twofa_pending' );
header( 'Location: /admin/' );
exit;
}
header( 'Location: /admin/user/twofa/' );
exit;
}
self::finalize_admin_login( $user, $domain, $cookie_name, (bool) \Shared\Helpers\Helpers::get( 'remember' ) );
header( 'Location: /admin/articles/list/' );
exit;
}
if ( $result == -1 )
\Shared\Helpers\Helpers::alert( 'Z powodu 5 nieudanych prób Twoje konto zostało zablokowane.' );
else
\Shared\Helpers\Helpers::alert( 'Podane hasło jest nieprawidłowe lub użytkownik nie istnieje.' );
header( 'Location: /admin/' );
exit;
case 'user-2fa-verify':
$pending = \Shared\Helpers\Helpers::get_session( 'twofa_pending' );
if ( !$pending || empty( $pending['uid'] ) ) {
\Shared\Helpers\Helpers::alert( 'Sesja 2FA wygasła. Zaloguj się ponownie.' );
header( 'Location: /admin/' );
exit;
}
$code = trim( (string) \Shared\Helpers\Helpers::get( 'twofa' ) );
if ( !preg_match( '/^\d{6}$/', $code ) )
{
\Shared\Helpers\Helpers::alert( 'Nieprawidłowy format kodu.' );
header( 'Location: /admin/user/twofa/' );
exit;
}
if ( !$users->verifyTwofaCode( (int) $pending['uid'], $code ) )
{
\Shared\Helpers\Helpers::alert( 'Błędny lub wygasły kod.' );
header( 'Location: /admin/user/twofa/' );
exit;
}
$user = $users->details( $pending['login'] );
if ( !$user ) {
\Shared\Helpers\Helpers::delete_session( 'twofa_pending' );
\Shared\Helpers\Helpers::alert( 'Sesja wygasła. Zaloguj się ponownie.' );
header( 'Location: /admin/' );
exit;
}
self::finalize_admin_login( $user, $domain, $cookie_name, !empty( $pending['remember'] ) );
header( 'Location: /admin/articles/list/' );
exit;
case 'user-2fa-resend':
$pending = \Shared\Helpers\Helpers::get_session( 'twofa_pending' );
if ( !$pending || empty( $pending['uid'] ) )
{
\Shared\Helpers\Helpers::alert( 'Sesja 2FA wygasła. Zaloguj się ponownie.' );
header( 'Location: /admin/' );
exit;
}
if ( !$users->sendTwofaCode( (int) $pending['uid'], true ) )
\Shared\Helpers\Helpers::alert( 'Kod można wysłać ponownie po krótkiej przerwie.' );
else
\Shared\Helpers\Helpers::alert( 'Nowy kod został wysłany.' );
header( 'Location: /admin/user/twofa/' );
exit;
case 'user-logout':
setcookie( $cookie_name, '', [
'expires' => time() - 86400,
'path' => '/',
'domain' => $domain,
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
] );
\Shared\Helpers\Helpers::delete_session( 'twofa_pending' );
session_destroy();
header( 'Location: /admin/' );
exit;
}
}
/**
* Entry point — auth check + layout rendering.
*/
public static function render(): string
{
global $user;
if ( \Shared\Helpers\Helpers::get( 'module' ) === 'user' && \Shared\Helpers\Helpers::get( 'action' ) === 'twofa' ) {
$controller = self::createController( 'Users' );
return $controller->twofa();
}
if ( !$user || !$user['admin'] )
{
$controller = self::createController( 'Users' );
return $controller->login_form();
}
$tpl = new \Shared\Tpl\Tpl;
$tpl->content = self::route();
return $tpl->render( 'site/main-layout' );
}
/**
* Routing — buduje nazwę modułu z URL i wywołuje akcję kontrolera.
*/
public static function route()
{
$_SESSION['admin'] = true;
if ( \Shared\Helpers\Helpers::get( 'p' ) )
\Shared\Helpers\Helpers::set_session( 'p', \Shared\Helpers\Helpers::get( 'p' ) );
// Budowanie nazwy modułu: shop_product → ShopProduct
$moduleName = '';
$parts = explode( '_', (string) \Shared\Helpers\Helpers::get( 'module' ) );
foreach ( $parts as $part )
$moduleName .= ucfirst( $part );
$action = \Shared\Helpers\Helpers::get( 'action' );
$controller = self::createController( $moduleName );
if ( $controller && method_exists( $controller, $action ) )
return $controller->$action();
\Shared\Helpers\Helpers::alert( 'Nieprawidłowy adres url.' );
return false;
}
/**
* Tworzy instancję kontrolera z Dependency Injection.
*/
private static function createController( string $moduleName )
{
$factories = self::getControllerFactories();
if ( !isset( $factories[$moduleName] ) )
return null;
$factory = $factories[$moduleName];
return is_callable( $factory ) ? $factory() : null;
}
/**
* Zwraca mapę fabryk kontrolerów (lazy init).
*/
private static function getControllerFactories(): array
{
if ( !empty( self::$newControllers ) )
return self::$newControllers;
self::$newControllers = [
'Dashboard' => function() {
global $mdb;
return new \admin\Controllers\DashboardController(
new \Domain\Dashboard\DashboardRepository( $mdb ),
new \Domain\ShopStatus\ShopStatusRepository( $mdb )
);
},
'Articles' => function() {
global $mdb;
return new \admin\Controllers\ArticlesController(
new \Domain\Article\ArticleRepository( $mdb ),
new \Domain\Languages\LanguagesRepository( $mdb ),
new \Domain\Layouts\LayoutsRepository( $mdb ),
new \Domain\Pages\PagesRepository( $mdb )
);
},
'ArticlesArchive' => function() {
global $mdb;
return new \admin\Controllers\ArticlesArchiveController(
new \Domain\Article\ArticleRepository( $mdb )
);
},
'Banners' => function() {
global $mdb;
return new \admin\Controllers\BannerController(
new \Domain\Banner\BannerRepository( $mdb ),
new \Domain\Languages\LanguagesRepository( $mdb )
);
},
'Settings' => function() {
global $mdb;
return new \admin\Controllers\SettingsController(
new \Domain\Settings\SettingsRepository( $mdb ),
new \Domain\Languages\LanguagesRepository( $mdb )
);
},
'ProductArchive' => function() {
global $mdb;
return new \admin\Controllers\ProductArchiveController(
new \Domain\Product\ProductRepository( $mdb )
);
},
'Archive' => function() {
global $mdb;
return new \admin\Controllers\ProductArchiveController(
new \Domain\Product\ProductRepository( $mdb )
);
},
'Dictionaries' => function() {
global $mdb;
return new \admin\Controllers\DictionariesController(
new \Domain\Dictionaries\DictionariesRepository( $mdb ),
new \Domain\Languages\LanguagesRepository( $mdb )
);
},
'Filemanager' => function() {
return new \admin\Controllers\FilemanagerController();
},
'Users' => function() {
global $mdb;
return new \admin\Controllers\UsersController(
new \Domain\User\UserRepository( $mdb )
);
},
'Languages' => function() {
global $mdb;
return new \admin\Controllers\LanguagesController(
new \Domain\Languages\LanguagesRepository( $mdb )
);
},
'Layouts' => function() {
global $mdb;
return new \admin\Controllers\LayoutsController(
new \Domain\Layouts\LayoutsRepository( $mdb ),
new \Domain\Languages\LanguagesRepository( $mdb )
);
},
'Newsletter' => function() {
global $mdb;
return new \admin\Controllers\NewsletterController(
new \Domain\Newsletter\NewsletterRepository(
$mdb,
new \Domain\Settings\SettingsRepository( $mdb )
),
new \Domain\Newsletter\NewsletterPreviewRenderer()
);
},
'Scontainers' => function() {
global $mdb;
return new \admin\Controllers\ScontainersController(
new \Domain\Scontainers\ScontainersRepository( $mdb ),
new \Domain\Languages\LanguagesRepository( $mdb )
);
},
'ShopPromotion' => function() {
global $mdb;
return new \admin\Controllers\ShopPromotionController(
new \Domain\Promotion\PromotionRepository( $mdb )
);
},
'ShopCoupon' => function() {
global $mdb;
return new \admin\Controllers\ShopCouponController(
new \Domain\Coupon\CouponRepository( $mdb )
);
},
'ShopAttribute' => function() {
global $mdb;
return new \admin\Controllers\ShopAttributeController(
new \Domain\Attribute\AttributeRepository( $mdb ),
new \Domain\Languages\LanguagesRepository( $mdb )
);
},
'ShopPaymentMethod' => function() {
global $mdb;
return new \admin\Controllers\ShopPaymentMethodController(
new \Domain\PaymentMethod\PaymentMethodRepository( $mdb )
);
},
'ShopTransport' => function() {
global $mdb;
return new \admin\Controllers\ShopTransportController(
new \Domain\Transport\TransportRepository( $mdb ),
new \Domain\PaymentMethod\PaymentMethodRepository( $mdb )
);
},
'Pages' => function() {
global $mdb;
return new \admin\Controllers\PagesController(
new \Domain\Pages\PagesRepository( $mdb ),
new \Domain\Languages\LanguagesRepository( $mdb ),
new \Domain\Layouts\LayoutsRepository( $mdb )
);
},
'Integrations' => function() {
global $mdb;
return new \admin\Controllers\IntegrationsController(
new \Domain\Integrations\IntegrationsRepository( $mdb )
);
},
'ShopStatuses' => function() {
global $mdb;
return new \admin\Controllers\ShopStatusesController(
new \Domain\ShopStatus\ShopStatusRepository( $mdb )
);
},
'ShopProductSets' => function() {
global $mdb;
return new \admin\Controllers\ShopProductSetsController(
new \Domain\ProductSet\ProductSetRepository( $mdb )
);
},
'ShopProducer' => function() {
global $mdb;
return new \admin\Controllers\ShopProducerController(
new \Domain\Producer\ProducerRepository( $mdb ),
new \Domain\Languages\LanguagesRepository( $mdb )
);
},
'ShopCategory' => function() {
global $mdb;
return new \admin\Controllers\ShopCategoryController(
new \Domain\Category\CategoryRepository( $mdb ),
new \Domain\Languages\LanguagesRepository( $mdb )
);
},
'ShopProduct' => function() {
global $mdb;
return new \admin\Controllers\ShopProductController(
new \Domain\Product\ProductRepository( $mdb ),
new \Domain\Integrations\IntegrationsRepository( $mdb ),
new \Domain\Languages\LanguagesRepository( $mdb )
);
},
'ShopClients' => function() {
global $mdb;
return new \admin\Controllers\ShopClientsController(
new \Domain\Client\ClientRepository( $mdb )
);
},
'ShopOrder' => function() {
global $mdb;
return new \admin\Controllers\ShopOrderController(
new \Domain\Order\OrderAdminService(
new \Domain\Order\OrderRepository( $mdb )
)
);
},
'Update' => function() {
global $mdb;
return new \admin\Controllers\UpdateController(
new \Domain\Update\UpdateRepository( $mdb )
);
},
];
return self::$newControllers;
}
public static function update()
{
global $mdb;
$repository = new \Domain\Update\UpdateRepository( $mdb );
$repository->runPendingMigrations();
}
}