diff --git a/app/Core/Admin/Http/Views/assets/js/components/tabs.js b/app/Core/Admin/Http/Views/assets/js/components/tabs.js
index aeebee1..0adefad 100644
--- a/app/Core/Admin/Http/Views/assets/js/components/tabs.js
+++ b/app/Core/Admin/Http/Views/assets/js/components/tabs.js
@@ -69,6 +69,7 @@ class ChromeTabs {
this.contentContainers = {};
this.abortControllers = {};
this.instanceId = 0;
+ this.shouldUpdateTabs = true; // Флаг для отслеживания необходимости обновления ширины вкладок
}
init(el) {
@@ -81,6 +82,15 @@ class ChromeTabs {
this.layoutTabs();
this.setupNewTabButton();
this.setupDraggabilly();
+
+ // Добавляем обработчики событий для мыши
+ this.el.addEventListener('mouseenter', () => {
+ this.shouldUpdateTabs = false;
+ });
+ this.el.addEventListener('mouseleave', () => {
+ this.shouldUpdateTabs = true;
+ this.layoutTabs();
+ });
}
createContentContainerForTab(tabEl) {
@@ -213,12 +223,12 @@ class ChromeTabs {
let styleHTML = '';
this.tabPositions.forEach((position, i) => {
styleHTML += `
- .chrome-tabs[data-chrome-tabs-instance-id="${
- this.instanceId
- }"] .chrome-tab:nth-child(${i + 1}) {
- transform: translate3d(${position}px, 0, 0)
- }
- `;
+ .chrome-tabs[data-chrome-tabs-instance-id="${
+ this.instanceId
+ }"] .chrome-tab:nth-child(${i + 1}) {
+ transform: translate3d(${position}px, 0, 0)
+ }
+ `;
});
this.styleEl.innerHTML = styleHTML;
@@ -276,8 +286,8 @@ class ChromeTabs {
this.setCurrentTab(tabEl);
}
- this.layoutTabs();
this.cleanUpPreviouslyDraggedTabs();
+ this.layoutTabs();
this.setupDraggabilly();
this.saveTabs();
}
@@ -402,7 +412,9 @@ class ChromeTabs {
}
this.cleanUpPreviouslyDraggedTabs();
- this.layoutTabs();
+
+ if (this.shouldUpdateTabs) this.layoutTabs();
+
this.setupDraggabilly();
this.saveTabs();
}
diff --git a/app/Core/Admin/routes.php b/app/Core/Admin/routes.php
index 3073848..da108cf 100644
--- a/app/Core/Admin/routes.php
+++ b/app/Core/Admin/routes.php
@@ -52,6 +52,7 @@
use Flute\Core\Admin\Http\Controllers\Views\UsersView;
use Flute\Core\Admin\Http\Controllers\Views\NavbarView;
use Flute\Core\Admin\Http\Middlewares\HasPermissionMiddleware;
+use Flute\Core\Http\Middlewares\CSRFMiddleware;
use Flute\Core\Router\RouteGroup;
$router->group(function ($router) {
@@ -181,6 +182,8 @@
}, 'admin');
$router->group(function (RouteGroup $admin) {
+ $admin->middleware(CSRFMiddleware::class);
+
// $admin->get('/', [IndexApi::class, 'index']);
$admin->get('/createlog', [MainSettingsController::class, 'createLog']);
diff --git a/app/Core/App.php b/app/Core/App.php
index 58ff21c..d9e2077 100644
--- a/app/Core/App.php
+++ b/app/Core/App.php
@@ -47,7 +47,7 @@ final class App
*
* @var string
*/
- public const VERSION = '0.2.3.5-alpha';
+ public const VERSION = '0.2.3.6-alpha';
/**
* Set the base path of the application
diff --git a/app/Core/Page/PageManager.php b/app/Core/Page/PageManager.php
index cf4a47b..4eba20e 100644
--- a/app/Core/Page/PageManager.php
+++ b/app/Core/Page/PageManager.php
@@ -8,7 +8,9 @@
use Flute\Core\Database\Repositories\PageRepository;
use Flute\Core\Events\RoutingFinishedEvent;
use Flute\Core\Http\Controllers\PagesController;
+use Flute\Core\Http\Middlewares\CSRFMiddleware;
use Flute\Core\Router\RouteDispatcher;
+use Flute\Core\Router\RouteGroup;
use Nette\Utils\Json;
use Nette\Utils\JsonException;
@@ -241,8 +243,11 @@ protected function loadPermissions(): void
protected function loadRoutes(): void
{
if ($this->hasAccessToEdit()) {
- $this->routeDispatcher->post('page/save', [PagesController::class, 'saveEdit']);
- $this->routeDispatcher->post('page/saveimage', [PagesController::class, 'saveImage']);
+ $this->routeDispatcher->group(function (RouteGroup $routeGroup) {
+ $routeGroup->middleware(CSRFMiddleware::class);
+ $routeGroup->post('save', [PagesController::class, 'saveEdit']);
+ $routeGroup->post('saveimage', [PagesController::class, 'saveImage']);
+ }, 'page/');
}
}
diff --git a/app/Core/Payments/PaymentRoutes.php b/app/Core/Payments/PaymentRoutes.php
index 84693aa..bcddf25 100644
--- a/app/Core/Payments/PaymentRoutes.php
+++ b/app/Core/Payments/PaymentRoutes.php
@@ -1,8 +1,10 @@
router->group(function( RouteGroup $router) {
+ $this->router->group(function (RouteGroup $router) {
$router->middleware(isAuthenticatedMiddleware::class);
$router->get("", [LKViewController::class, "index"]);
@@ -28,10 +30,11 @@ public function init()
}, '/lk');
- $this->router->group(function( RouteGroup $api) {
+ $this->router->group(function (RouteGroup $api) {
$api->group(function (RouteGroup $apiGroup) {
+ $apiGroup->middleware(CSRFMiddleware::class);
$apiGroup->middleware(isAuthenticatedMiddleware::class);
-
+
$apiGroup->post('/validate-promo', [LKApiController::class, 'validatePromo']);
$apiGroup->post('/buy/{gateway}', [LKApiController::class, 'purchase']);
$apiGroup->get('/buy/{gateway}', [LKApiController::class, 'purchase']);
diff --git a/app/Core/Profile/Mods/InvoicesMode.php b/app/Core/Profile/Mods/InvoicesMode.php
index 8d94535..c789bf7 100644
--- a/app/Core/Profile/Mods/InvoicesMode.php
+++ b/app/Core/Profile/Mods/InvoicesMode.php
@@ -25,6 +25,7 @@ public function render(User $user): string
foreach ($invoices as $item) {
$item->amountWithCurrency = $item->originalAmount . ' ' . $item->currency->code;
$item->promoCode = !empty($item->promoCode) ? $item->promoCode->code : __('def.no');
+ $item->created_at = $item->created_at->format(default_date_format());
if ($item->isPaid) {
$item->paidCard = '
@@ -39,7 +40,7 @@ public function render(User $user): string
}
// Добавляем объединенную колонку
- $table->addColumn(new TableColumn('id', 'ID'));
+ $table->addColumn(new TableColumn('created_at', __('def.created_at')));
$table->addColumn(new TableColumn('gateway', __('admin.payments.adapter')));
$table->addColumn(new TableColumn('transactionId', __('admin.payments.transactionId')));
$table->addColumn(new TableColumn('amountWithCurrency', __('admin.payments.amount')));
diff --git a/app/Core/Profile/Mods/SecurityMode.php b/app/Core/Profile/Mods/SecurityMode.php
index 1296ed0..63c1edb 100644
--- a/app/Core/Profile/Mods/SecurityMode.php
+++ b/app/Core/Profile/Mods/SecurityMode.php
@@ -7,6 +7,7 @@
use Flute\Core\Exceptions\DuplicateEmailException;
use Flute\Core\Exceptions\DuplicateLoginException;
use Flute\Core\Exceptions\TooManyRequestsException;
+use Flute\Core\Http\Middlewares\CSRFMiddleware;
use Flute\Core\Http\Middlewares\isAuthenticatedMiddleware;
use Flute\Core\Support\FluteRequest;
@@ -14,7 +15,7 @@ class SecurityMode implements ProfileModInterface
{
public function __construct()
{
- router()->post('profile/edit/security', [$this, 'handleSave'], [isAuthenticatedMiddleware::class]);
+ router()->post('profile/edit/security', [$this, 'handleSave'], [isAuthenticatedMiddleware::class, CSRFMiddleware::class]);
}
public function handleSave(FluteRequest $fluteRequest)
diff --git a/app/Core/Profile/ProfileRoutes.php b/app/Core/Profile/ProfileRoutes.php
index 0cd2e35..412875f 100644
--- a/app/Core/Profile/ProfileRoutes.php
+++ b/app/Core/Profile/ProfileRoutes.php
@@ -7,6 +7,7 @@
use Flute\Core\Http\Controllers\Profile\IndexController;
use Flute\Core\Http\Controllers\Profile\ProfileRedirectController;
use Flute\Core\Http\Controllers\Profile\SocialController;
+use Flute\Core\Http\Middlewares\CSRFMiddleware;
use Flute\Core\Http\Middlewares\isAuthenticatedMiddleware;
use Flute\Core\Http\Middlewares\ProfileChangeMiddleware;
use Flute\Core\Http\Middlewares\UserExistsMiddleware;
@@ -17,6 +18,8 @@ class ProfileRoutes
public function register()
{
router()->group(function (RouteGroup $group) {
+ $group->middleware(CSRFMiddleware::class);
+
$group->group(function (RouteGroup $edit) {
$edit->middleware(isAuthenticatedMiddleware::class);
diff --git a/app/Core/Router/RouteDispatcher.php b/app/Core/Router/RouteDispatcher.php
index 3c1951b..e11ce30 100644
--- a/app/Core/Router/RouteDispatcher.php
+++ b/app/Core/Router/RouteDispatcher.php
@@ -36,8 +36,7 @@ class RouteDispatcher
protected array $defaultMiddlewares = [
MaintenanceMiddleware::class,
- BanCheckMiddleware::class,
- CSRFMiddleware::class
+ BanCheckMiddleware::class
];
/**
@@ -419,4 +418,32 @@ public function getRoutes(): array
{
return $this->routeCollection->all();
}
+
+ /**
+ * Removes a middleware from the default middlewares.
+ *
+ * @param mixed $middleware The middleware to remove.
+ *
+ * @return RouteDispatcher The current RouteDispatcher object.
+ */
+ public function removeDefaultMiddleware($middleware): self
+ {
+ $this->defaultMiddlewares = array_filter($this->defaultMiddlewares, function ($existingMiddleware) use ($middleware) {
+ if (is_callable($existingMiddleware) && is_callable($middleware)) {
+ return $existingMiddleware !== $middleware;
+ }
+
+ if (is_object($existingMiddleware) && is_object($middleware)) {
+ return get_class($existingMiddleware) !== get_class($middleware);
+ }
+
+ if (is_string($existingMiddleware) && is_string($middleware)) {
+ return $existingMiddleware !== $middleware;
+ }
+
+ return true;
+ });
+
+ return $this;
+ }
}
\ No newline at end of file
diff --git a/app/Core/Router/RouteGroup.php b/app/Core/Router/RouteGroup.php
index 0880430..167aff4 100644
--- a/app/Core/Router/RouteGroup.php
+++ b/app/Core/Router/RouteGroup.php
@@ -207,7 +207,7 @@ public function resources(array $resources): void
$this->resource($name, $controller);
}
}
-
+
/**
* Add array of apiResource routes to the route collection.
*
@@ -260,6 +260,35 @@ public function middleware($middleware): self
return $this;
}
+ /**
+ * Removes a middleware from the group.
+ *
+ * @param mixed $middleware The middleware to remove.
+ *
+ * @return RouteGroup The current RouteGroup object.
+ */
+ public function removeMiddleware($middleware): self
+ {
+ // Remove from this group's middlewares
+ $this->middlewares = array_filter($this->middlewares, function ($existingMiddleware) use ($middleware) {
+ if (is_callable($existingMiddleware) && is_callable($middleware)) {
+ return $existingMiddleware !== $middleware;
+ }
+
+ if (is_object($existingMiddleware) && is_object($middleware)) {
+ return get_class($existingMiddleware) !== get_class($middleware);
+ }
+
+ if (is_string($existingMiddleware) && is_string($middleware)) {
+ return $existingMiddleware !== $middleware;
+ }
+
+ return true;
+ });
+
+ return $this;
+ }
+
/**
* Creates a new subgroup within the current group.
*
diff --git a/app/Core/Services/AuthService.php b/app/Core/Services/AuthService.php
index f9c20f1..54e8e9f 100644
--- a/app/Core/Services/AuthService.php
+++ b/app/Core/Services/AuthService.php
@@ -16,6 +16,7 @@
use Flute\Core\Http\Controllers\Auth\AuthController;
use Flute\Core\Http\Controllers\Auth\PasswordResetController;
use Flute\Core\Http\Controllers\Auth\SocialAuthController;
+use Flute\Core\Http\Middlewares\CSRFMiddleware;
use Flute\Core\Http\Middlewares\GuestMiddleware;
use Flute\Core\Http\Middlewares\isAuthenticatedMiddleware;
use Flute\Core\Router\RouteDispatcher;
@@ -289,6 +290,8 @@ public function setRoutes()
// Post routes with CSRF protection
$routeGroup->group(function (RouteGroup $routeGroup) {
$routeGroup->middleware(GuestMiddleware::class);
+ $routeGroup->middleware(CSRFMiddleware::class);
+
if (!config('auth.only_social', false) || (config('auth.only_social') && social()->isEmpty())) {
$routeGroup->post('/register', [AuthController::class, 'postRegister']);
$routeGroup->post('/login', [AuthController::class, 'postLogin']);
diff --git a/app/Core/Widgets/WidgetManager.php b/app/Core/Widgets/WidgetManager.php
index ea49e85..9717586 100644
--- a/app/Core/Widgets/WidgetManager.php
+++ b/app/Core/Widgets/WidgetManager.php
@@ -9,6 +9,7 @@
use Flute\Core\Contracts\WidgetInterface;
use Flute\Core\Database\Entities\Widget;
use Flute\Core\Http\Controllers\WidgetController;
+use Flute\Core\Http\Middlewares\CSRFMiddleware;
use Flute\Core\Router\RouteDispatcher;
use Flute\Core\Router\RouteGroup;
use Nette\Utils\Json;
@@ -57,6 +58,8 @@ protected function getRepository(): Repository
protected function loadAllRoutes(): void
{
$this->routeDispatcher->group(function (RouteGroup $routeGroup) {
+ $routeGroup->middleware(CSRFMiddleware::class);
+
$routeGroup->post('show', [WidgetController::class, 'showWidget']);
$routeGroup->get('all', [WidgetController::class, 'getAllWidgets']);
}, 'widget/');
diff --git a/app/Themes/standard/assets/styles/pages/profile_edit/invoices.scss b/app/Themes/standard/assets/styles/pages/profile_edit/invoices.scss
index ee35bb5..317487d 100644
--- a/app/Themes/standard/assets/styles/pages/profile_edit/invoices.scss
+++ b/app/Themes/standard/assets/styles/pages/profile_edit/invoices.scss
@@ -4,6 +4,7 @@
border-radius: 5px;
color: $color-text-inverse;
font-weight: 500;
+ white-space: nowrap;
&.paid {
background-color: $color-success;