Files
Leantime/app/Core/Middleware/Updated.php

109 lines
3.8 KiB
PHP

<?php
namespace Leantime\Core\Middleware;
use Closure;
use Leantime\Core\Configuration\AppSettings;
use Leantime\Core\Controller\Frontcontroller;
use Leantime\Core\Events\DispatchesEvents;
use Leantime\Core\Http\IncomingRequest;
use Leantime\Domain\Setting\Repositories\Setting as SettingRepository;
use Symfony\Component\HttpFoundation\Response;
class Updated
{
use DispatchesEvents;
/**
* Check if Leantime is installed
*
* @param \Closure(IncomingRequest): Response $next
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
**/
public function handle(IncomingRequest $request, Closure $next): Response
{
// For HTMX and API requests, trust the session -- DB version can't change mid-session.
if (session('isUpdated') && ($request->isHtmxRequest() || $request->isApiOrCronRequest())) {
return $next($request);
}
$cachedDbVersion = session('dbVersion');
$dbVersion = $cachedDbVersion ?? app()->make(SettingRepository::class)->getSetting('db-version');
$settingsDbVersion = app()->make(AppSettings::class)->dbVersion;
if ($dbVersion !== false) {
// Setting dbVersion only if there is one in the db
// Otherwise leave dbVersion unset so we can recheck every time the settings db returns false.
session(['dbVersion' => $dbVersion]);
}
$dbVersionInt = $this->getVersionInt($dbVersion);
$settingsDbVersionInt = $this->getVersionInt($settingsDbVersion);
// Self-heal a stale session cache: the cached db-version survives an
// update run by ANOTHER session (an admin upgrading the install), which
// used to strand every other live session in a redirect loop (any page
// -> /install/update -> back again) until their cookies were cleared.
// Before concluding "not updated" from a CACHED value, re-read the real
// version from the database — one extra query, and only on the path
// that would otherwise redirect.
if ($dbVersionInt < $settingsDbVersionInt && $cachedDbVersion !== null) {
$freshDbVersion = app()->make(SettingRepository::class)->getSetting('db-version');
if ($freshDbVersion !== false) {
$dbVersion = $freshDbVersion;
session(['dbVersion' => $dbVersion]);
$dbVersionInt = $this->getVersionInt($dbVersion);
}
}
session(['isUpdated' => $dbVersionInt >= $settingsDbVersionInt]);
if (session('isUpdated')) {
return $next($request);
}
if (! $response = $this->redirectToUpdate()) {
return $next($request);
}
return $response;
}
/**
* Redirect to update
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
private function redirectToUpdate(): Response|false
{
$frontController = app()->make(Frontcontroller::class);
$allowedRoutes = ['install', 'install.update', 'api.i18n'];
$allowedRoutes = self::dispatchFilter('allowedRoutes', $allowedRoutes);
if (in_array($frontController::getCurrentRoute(), $allowedRoutes)) {
return false;
}
$route = BASE_URL.'/install/update';
$route = self::dispatchFilter('redirectroute', $route);
return $frontController::redirect($route);
}
private function getVersionInt($version)
{
$versionArray = explode('.', $version);
if (is_array($versionArray) && count($versionArray) == 3) {
$major = $versionArray[0];
$minor = str_pad($versionArray[1], 2, '0', STR_PAD_LEFT);
$patch = str_pad($versionArray[2], 2, '0', STR_PAD_LEFT);
$newDBVersion = $major.$minor.$patch;
return $newDBVersion;
}
return false;
}
}