add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks - Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists - Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services - Add Microsoft OIDC SSO, API token management, and user lifecycle features - Add swagger-ui vendor integration and OpenAPI spec - Add production Docker setup and bin/ scripts - Update composer dependencies, config, templates, and frontend assets throughout Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@ use MintyPHP\Http\Request;
|
||||
use MintyPHP\Http\LocaleResolver;
|
||||
use MintyPHP\Http\AccessControl;
|
||||
use MintyPHP\Http\AssetDetector;
|
||||
use MintyPHP\Support\Flash;
|
||||
|
||||
// Change directory to project root
|
||||
chdir(__DIR__ . '/..');
|
||||
@@ -34,10 +35,8 @@ Session::start();
|
||||
$defaultLocale = appDefaultLocale();
|
||||
if ($defaultLocale !== null) {
|
||||
I18n::$defaultLocale = $defaultLocale;
|
||||
if (!defined('APP_LOCALES') || in_array($defaultLocale, APP_LOCALES, true)) {
|
||||
if (I18n::$locale === '' || !in_array(I18n::$locale, APP_LOCALES, true)) {
|
||||
I18n::$locale = $defaultLocale;
|
||||
}
|
||||
if (I18n::$locale === '') {
|
||||
I18n::$locale = $defaultLocale;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,15 +71,43 @@ I18n::$locale = $localeResolver->resolveLocale(
|
||||
);
|
||||
|
||||
$isAssetRequest = AssetDetector::isAssetRequest($pathWithoutLocale);
|
||||
$isApiRequest = str_starts_with($pathWithoutLocale, 'api/');
|
||||
|
||||
// Refresh tenant context if stale or missing (skip for assets)
|
||||
if (!empty($_SESSION['user']['id']) && !$isAssetRequest) {
|
||||
if ($isApiRequest) {
|
||||
if (!defined('MINTY_API_REQUEST')) {
|
||||
define('MINTY_API_REQUEST', true);
|
||||
}
|
||||
|
||||
// Minty router enforces CSRF for non-GET requests before action execution.
|
||||
// Our API is Bearer-token based and does not use form-CSRF, so mark only
|
||||
// API write requests as AJAX to bypass the router-level form CSRF gate.
|
||||
$requestMethod = strtoupper((string) ($_SERVER['REQUEST_METHOD'] ?? 'GET'));
|
||||
$isApiWriteMethod = !in_array($requestMethod, ['GET', 'HEAD', 'OPTIONS'], true);
|
||||
if ($isApiWriteMethod && empty($_SERVER['HTTP_X_REQUESTED_WITH'])) {
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest';
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh tenant context if stale or missing (skip for assets and API requests)
|
||||
if (!empty($_SESSION['user']['id']) && !$isAssetRequest && !$isApiRequest) {
|
||||
$userId = (int) ($_SESSION['user']['id'] ?? 0);
|
||||
if ($userId > 0) {
|
||||
$ttl = defined('TENANT_SNAPSHOT_TTL') ? (int) TENANT_SNAPSHOT_TTL : 120;
|
||||
$authState = \MintyPHP\Service\Auth\AuthService::refreshSessionAuthState($userId);
|
||||
if (!empty($authState['logout_required'])) {
|
||||
\MintyPHP\Service\Auth\AuthService::logout();
|
||||
$reason = (string) ($authState['reason'] ?? '');
|
||||
if ($reason === 'inactive') {
|
||||
Flash::error(t('Account is inactive'), 'login', 'login_inactive');
|
||||
} else {
|
||||
Flash::error(t('No active tenant assigned'), 'login', 'no_active_tenant');
|
||||
}
|
||||
Router::redirect('login');
|
||||
}
|
||||
|
||||
$ttl = 120;
|
||||
$lastRefresh = (int) ($_SESSION['tenant_snapshot_at'] ?? 0);
|
||||
$needsRefresh = empty($_SESSION['current_tenant']) || empty($_SESSION['available_tenants']);
|
||||
if ($ttl > 0 && (time() - $lastRefresh) >= $ttl) {
|
||||
if ((time() - $lastRefresh) >= $ttl) {
|
||||
$needsRefresh = true;
|
||||
}
|
||||
if ($needsRefresh) {
|
||||
@@ -90,7 +117,7 @@ if (!empty($_SESSION['user']['id']) && !$isAssetRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
if ($requestedLocale === '' && !$isAssetRequest) {
|
||||
if ($requestedLocale === '' && !$isAssetRequest && !$isApiRequest) {
|
||||
$method = strtoupper((string) ($_SERVER['REQUEST_METHOD'] ?? 'GET'));
|
||||
if (in_array($method, ['GET', 'HEAD'], true)) {
|
||||
$target = Request::withLocale($pathWithoutLocale, I18n::$locale ?? I18n::$defaultLocale);
|
||||
|
||||
Reference in New Issue
Block a user