Cron previously curled appUrl (the externally-reachable domain, with
cfg.port appended when set). That meant every cron tick depended on
the host being able to route back to its own public domain (hairpin
NAT, not guaranteed), and whenever cfg.port was set to an
externally-forwarded port nginx/caddy don't themselves listen on (e.g.
the QEMU test VM's hostfwd), cron would curl a port nothing is
listening on at all and fail outright.
curl --resolve now pins the connection to 127.0.0.1 on the port
nginx/caddy actually listen on (80, or 443 under proxy.ssl), while
keeping the Host header/SNI as cfg.domain so vhost routing and cert
validation still work normally. Also fixes the cron services'
"after" dependency, which was hardcoded to nginx.service even when
proxy.server = "caddy".
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
proxy.ssl = true (the default) requests a real ACME certificate for
services.hortusfox-web.domain. Left at its own default of "localhost",
that request can never succeed -- Let's Encrypt won't issue for it --
but previously this only surfaced as an ACME failure at activation
time. Catch it at eval time instead with a clear assertion message.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Both fields were spliced directly into a single-quoted SQL string. An
ordinary value containing an apostrophe (e.g. adminAccount.username =
"O'Brien") broke the INSERT's SQL syntax outright, failing
hortusfox-setup on first boot under set -euo pipefail; a crafted value
could inject arbitrary SQL. Escape backslashes and quotes to match
MySQL/MariaDB's default string literal rules.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The seeded admin account's UserModel "name" was hardcoded to "admin".
Make it a separate, overridable option (still defaulting to "admin"
for compatibility) -- it's purely a display name, since HortusFox logs
in by email rather than this field.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The seeded admin account's email was hardcoded to admin@<domain>, which
isn't necessarily a mailbox the deployer can actually receive mail at --
notably breaking HortusFox's own "Restore password" flow, which emails
that address. Make it a separate, overridable option (still defaulting
to admin@<domain> for compatibility).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Asatru's own url()/asset() helpers build absolute URLs from
$_SERVER['SERVER_PORT'] rather than APP_URL, and nginx/caddy populate
that from the port they're actually listening on -- not an externally
forwarded one (e.g. the QEMU test VM's hostfwd to 8080). That silently
dropped the port from every generated asset/redirect URL, breaking all
static assets when accessed through a forwarded port. Override
SERVER_PORT at the fastcgi layer to cfg.port when set, for both
backends.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Packages HortusFox from source via php.buildComposerProject and provides
a NixOS module (services.hortusfox-web) to deploy it as a full service
(mariadb + phpfpm + nginx/caddy + ACME + cron timers).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>