RewriteEngine On

# mod_php strips the Authorization header from $_SERVER by default on some
# configs -- public/api/claim_link_lookup.php's bearer-token auth would
# silently see no header at all without this, even though the client sent
# one. A no-op everywhere else.
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

# Themed, admin-customizable stylesheets: /assets/css/{key}.css -> css.php?key=...
# Existing <link href="/assets/css/tokens.css"> style references keep working
# unchanged; this just routes them through the DB-backed override system.
RewriteRule ^assets/css/([a-z-]+)\.css$ css.php?key=$1 [L,QSA]

# /claim/{token}/{product-slug}  ->  product.php?token=...&slug=...
RewriteRule ^claim/([a-f0-9]{32})/([a-z0-9-]+)/?$ product.php?token=$1&slug=$2 [L,QSA]

# /claim/{token}  ->  claim.php?token=...  (overview page, always shown first)
RewriteRule ^claim/([a-f0-9]{32})/?$ claim.php?token=$1 [L,QSA]

# Allow any PHP entry point, including nested ones under admin/, to be
# requested without the .php extension, e.g. /setup instead of /setup.php,
# or /admin/login instead of /admin/login.php. Only kicks in for requests
# the rules above didn't already claim, and only when a matching .php file
# exists — never a bare 404 just because the extension was left off.
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{DOCUMENT_ROOT}/$1.php -f
RewriteRule ^([a-zA-Z0-9_/-]+)/?$ $1.php [L,QSA]

# Bare /assets/ (or /assets, no trailing slash) has no index file and
# directory listing is disabled (Options -Indexes below), which Apache
# would otherwise answer with 403 Forbidden -- same "dead end" treatment
# as everything else here, so it redirects too instead of a bare 403.
RewriteRule ^assets/?$ /welcome [R=302,L]

# Anything left over at this point matched no real file, no route above,
# and no extensionless .php file either -- a stale/mistyped/dead URL.
# Send it to the welcome page instead of a bare Apache 404. uploads/ is
# still excluded -- unlike assets/ (shipped, static, safe to treat like
# any other page), it holds customer-uploaded content (product images,
# screenshots) that's directly linked from real pages, where redirecting
# a missing file away from the page displaying it would be worse than
# just a broken image.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^/uploads/
RewriteCond %{REQUEST_URI} !^/welcome(\.php)?$
RewriteRule ^ /welcome [R=302,L]

# Never execute PHP inside the uploads directory, even if something ends up there.
# (Only <FilesMatch>/<If> here — <Directory> is not permitted inside .htaccess,
# only in server/VirtualHost config; using it here breaks Apache with a 500.)
<FilesMatch "\.ph(p[3457]?|t|tml)$">
    <If "%{REQUEST_URI} =~ m#^/uploads/#">
        Require all denied
    </If>
</FilesMatch>

Options -Indexes

# Raises PHP's own per-request upload limits above what most distros ship
# by default (Debian/Ubuntu: upload_max_filesize=2M, post_max_size=8M) --
# without this, a single screenshot near src/ImageUpload.php's own 8 MB cap
# gets silently truncated by PHP itself before that check (or any app code)
# ever runs -- confirmed live with a 4-5 MB screenshot rejected outright.
# The product editor can also submit several images in one request (cover +
# multiple screenshots + background), so the total body needs more headroom
# than any single file. Works via .htaccess because both are PHP_INI_PERDIR
# settings and the vhost has AllowOverride All (see install/INSTALL.md) --
# ships with the app instead of requiring every customer to hand-edit
# php.ini over SSH. "php_module" is the Apache module name mod_php has
# registered as since PHP 7 (no version suffix), matching this project's
# PHP 8.1+ requirement.
<IfModule php_module>
    php_value upload_max_filesize 16M
    php_value post_max_size 48M
</IfModule>
