# Zenton CMS — корень установленного проекта одновременно document root и
# runtime root (см. index.php / bootstrap.php::ABS_PATH). Раньше эту роль
# играл только public/, поэтому здесь явно закрыты от прямого HTTP-доступа
# служебные директории и файлы, которые теперь физически лежат рядом с
# document root, а не вне его.

# Служебные top-level директории приложения. Ничего из них не должно
# отдаваться веб-сервером напрямую — это PHP-исходники/шаблоны (app,
# database, framework, installer, release, vendor), служебное хранилище
# (storage/cloud, storage/platform — machine-креды и служебное состояние),
# либо dev/CI-инструментарий (tests, docs, runtime, node_modules, .git,
# .github, .claude, .idea, .vscode). release/ — весь релиз-сабсистем,
# включая собранные архивы в release/dist/. extensions/ — исходники
# собственных плагинов Zenton (zenton/commerce), ставятся из ZIP, в релиз
# CMS не входят. uploads/ и packages/ — намеренно
# НЕ в этом списке: это настоящие публичные директории (загруженные медиа и
# опубликованные ассеты пакетов, см. LocalMediaStorage/PackageAssetPublisher),
# отдаются веб-сервером напрямую как обычная статика.
<IfModule mod_rewrite.c>
RewriteEngine On

# REST API (/api/v1): Apache с PHP-FPM/CGI не передаёт PHP заголовок
# Authorization — без этой строки токен интеграции до приложения не дойдёт.
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

# console/ — особый случай: помимо PHP/Twig-исходников (которые тоже не
# должны отдаваться напрямую), это имя — реальный префикс маршрутов
# (/console/... — админка, /console/https/console.php). Раньше (public/ как
# webroot) коллизии не было — console/ физически лежал вне document root.
# Теперь это реальный каталог рядом с index.php, поэтому простой запрет по
# имени каталога сломал бы саму админку. Вместо этого каждый запрос под этим
# префиксом принудительно уходит в index.php — дальше решает сам роутер:
# зарегистрированный маршрут отработает, всё остальное (включая прямые пути
# к .twig/.php внутри console/) получит обычный 404 через ExceptionHandler,
# а не отдачу сырого файла.
#
# /packages/assets/... (ассеты plugins/themes, PackageAssetController /
# app/https/app.php) не нуждается в отдельной строчки здесь — packages/ не
# физическая директория (plugins/themes теперь под app/), поэтому эти URL
# уже не совпадают ни с одним реальным путём на диске и падают в общий
# catch-all ниже точно так же, как любой другой маршрут.
RewriteRule ^console(/|$) /index.php [L]

# /console — тоже реальный каталог, и без этой строки mod_dir отвечает на него
# своим 301 на /console/ раньше index.php. Location этого 301 Apache собирает
# по своей схеме: за TLS-прокси (nginx → Apache по http) вход по https уводил
# на http://…/console/, где Secure-cookie сессии нет — после входа снова форма.
<IfModule mod_dir.c>
DirectorySlash Off
</IfModule>

# Здесь коллизий с реальными маршрутами нет — прямой запрос всегда запрещён.
RewriteRule ^(app|config|database|docs|extensions|framework|installer|node_modules|release|runtime|storage|tests|vendor)(/|$) - [F,L]
RewriteRule ^\.(git|github|idea|vscode|claude)(/|$) - [F,L]
</IfModule>

# Чувствительные файлы в корне: креды/окружение, состояние установки,
# Composer-метаданные (могут раскрывать версии зависимостей), сам
# entry point рантайма (bootstrap.php выполняется только через index.php,
# прямой HTTP-запрос к нему не нужен) и dev-тулинг верхнего уровня.
<FilesMatch "^(\.env(\.example)?|\.installed|\.maintenance|\.gitignore|\.htaccess|composer\.(json|lock|phar)|package(-lock)?\.json|phpunit\.xml|gulpfile\.js|bootstrap\.php|router\.php|zenton-release\.json|CHANGELOG\.md|README\.md|LICENSE|CLAUDE\.md)$">
    <IfModule mod_authz_core.c>
        Require all denied
    </IfModule>
    <IfModule !mod_authz_core.c>
        Order allow,deny
        Deny from all
    </IfModule>
</FilesMatch>

<IfModule mod_rewrite.c>
RewriteRule (.+)/$ /$1 [L,R=301]
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . /index.php [L]
</IfModule>
