# =====================================================================
#  八藏CMS Apache 配置（伪静态 + 安全加固）
#  说明：装完就忘是本类 CMS 最常见的失陷原因。下面「安全」段的每一条
#        都对应一个真实可复现的暴露面，删任何一条之前请先想清楚后果。
# =====================================================================

<IfModule mod_rewrite.c>
  Options +FollowSymlinks -Multiviews
  RewriteEngine On

  # -------------------------------------------------------------------
  # 一、安全拦截（必须放在伪静态规则之前，先拦后放）
  # -------------------------------------------------------------------

  # 1) 已安装完成（存在 install.lock）→ 整个安装向导目录对外关闭
  RewriteCond %{DOCUMENT_ROOT}/install/install.lock -f
  RewriteRule ^install(/|$) - [F,L]

  # 2) 安装目录里的 SQL / 说明 / 锁文件永远不可下载
  #    建库脚本含完整表结构与演示数据，可下载 = 数据库结构全暴露
  RewriteRule ^install/.*\.(sql|sql\.gz|md|txt|lock|log|bak|old|json|ini|yml|yaml|zip|tar|gz)$ - [F,L]

  # 3) 源码与内部目录：app / config / tools / backups / admin-ui 一律拒绝
  #    admin-ui 是后台前端源码目录，静态资源统一走 /admin-assets/ 虚拟路径
  RewriteRule ^(app|config|tools|backups|admin-ui)(/|$) - [F,L]

  # 4) storage 只公开 uploads/ 与 download/，其余（logs / backups / cache …）
  #    logs 里有绝对路径与 SQL 片段，backups 里有数据库导出，都不可外泄
  RewriteRule ^storage/(?!uploads/|download/) - [F,L]

  # 5) 上传目录内禁止执行任何脚本（即使被绕过了扩展名白名单）
  RewriteRule ^storage/uploads/.*\.(php|phtml|php[0-9]|phps|phar|inc|pl|py|rb|cgi|asp|aspx|jsp|sh|exe)$ - [F,L]

  # 6) 隐藏文件与版本控制目录（.git / .env / .htaccess 自身）
  RewriteRule (^|/)\.(?!well-known/) - [F,L]

  # 7) 工程杂项：依赖清单、示例配置、说明文档、备份与脚本
  RewriteRule ^(composer\.(json|lock)|package(-lock)?\.json|yarn\.lock|pnpm-lock\.yaml)$ - [F,L]
  RewriteRule \.(md|sample|conf|sql|sql\.gz|lock|log|bak|old|orig|ini|yml|yaml|sh|py|dist|tgz|tar|gz)$ - [F,L]

  # -------------------------------------------------------------------
  # 二、伪静态
  # -------------------------------------------------------------------

  # 安装向导入口
  RewriteRule ^install/?$ install/index.php [L]

  # 后台入口（admin.php 与 admin.php/api/... 均由 admin.php 处理）
  RewriteRule ^admin\.php(/.*)?$ admin.php [L]

  # 后台 SPA 静态资源（虚拟路径 admin-assets → admin-ui）
  RewriteRule ^admin-assets/(.+)$ admin.php [L]

  # 存在的真实文件/目录直接访问
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d

  # robots / sitemap
  RewriteRule ^(robots\.txt|sitemap\.xml)$ index.php [L]

  # 其余全部走前台
  RewriteRule (.*) index.php [L,QSA]
</IfModule>

# ---------------------------------------------------------------------
# 三、响应头加固
#     这些头同样在 PHP 层输出（Core\Security::headers）。两边都写是有意的：
#     静态文件（图片/CSS/JS）不经过 PHP，只能靠服务器补。
# ---------------------------------------------------------------------
<IfModule mod_headers.c>
  # 不把运行时版本号告诉攻击者
  Header unset X-Powered-By
  Header unset Server

  Header always set X-Content-Type-Options "nosniff"
  Header always set X-Frame-Options "SAMEORIGIN"
  Header always set Referrer-Policy "strict-origin-when-cross-origin"
  Header always set Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=()"
  Header always set Cross-Origin-Opener-Policy "same-origin"
  Header always set X-XSS-Protection "0"

  # 站点启用 HTTPS 后请打开下面这行（HSTS 一旦下发很难回退，确认无误再开）
  # Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"

  # 静态资源长缓存：URL 已带 ?v=文件指纹，内容变了 URL 就变，可以放心长缓存
  <FilesMatch "\.(css|js|png|jpe?g|gif|webp|svg|ico|woff2?|ttf|eot)$">
    Header set Cache-Control "public, max-age=2592000, immutable"
  </FilesMatch>

  # 上传文件一律下载而不渲染（HTML/SVG 被直接打开会执行内嵌脚本）
  <FilesMatch "\.(svg|html?|xml)$">
    Header set X-Content-Type-Options "nosniff"
  </FilesMatch>
</IfModule>

# ---------------------------------------------------------------------
# 四、字符集与 MIME
# ---------------------------------------------------------------------
<IfModule mod_mime.c>
  AddDefaultCharset UTF-8
  AddType image/webp .webp
  AddType font/woff2 .woff2
</IfModule>

# 关闭目录列表（默认就应为 off，显式写出来防止上游配置被改）
Options -Indexes

# mod_php 环境下直接关掉版本号暴露（PHP-FPM 请在 php.ini 里设 expose_php=Off）
<IfModule mod_php.c>
  php_flag expose_php off
  php_flag display_errors off
</IfModule>
<IfModule mod_php7.c>
  php_flag expose_php off
  php_flag display_errors off
</IfModule>
<IfModule mod_php8.c>
  php_flag expose_php off
  php_flag display_errors off
</IfModule>
