Compare commits
3
Commits
d88f6f312d
...
8ec061bede
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ec061bede | ||
|
|
3f5a120338 | ||
|
|
e2ac57d9c6 |
@@ -41,6 +41,15 @@ let
|
||||
in if builtins.length parts > 1 then lib.concatStringsSep "/" (lib.init parts) else null)
|
||||
writableDirs));
|
||||
|
||||
# Escape a value for interpolation into a single-quoted SQL string literal
|
||||
# (used for the admin-seed INSERT below). Doubling backslashes first, then
|
||||
# quotes, matches MySQL/MariaDB's default (backslash-escapes-active)
|
||||
# string literal rules -- without this, an ordinary value like "O'Brien"
|
||||
# breaks the INSERT's SQL syntax outright (failing hortusfox-setup on
|
||||
# first boot, since the script runs under `set -euo pipefail`), and a
|
||||
# crafted value could inject arbitrary SQL.
|
||||
sqlEscape = s: lib.replaceStrings [ "\\" "'" ] [ "\\\\" "''" ] s;
|
||||
|
||||
# Render one KEY=VALUE .env line. Booleans/ints/floats are left bare so
|
||||
# Asatru's datatype auto-detection picks them up as such rather than string.
|
||||
mkEnvLine = name: value:
|
||||
@@ -112,6 +121,20 @@ let
|
||||
lib.concatStringsSep "\n" (lib.mapAttrsToList mkEnvLine envVars) + "\n"
|
||||
);
|
||||
|
||||
# Cron jobs curl the app's own loopback address rather than its
|
||||
# externally-reachable appUrl, on the port nginx/caddy actually listen on
|
||||
# (80, or 443 when proxy.ssl -- never cfg.port, which per its own option
|
||||
# doc doesn't change that). Otherwise cron would depend on the host being
|
||||
# able to route back to its own public domain (hairpin NAT, not
|
||||
# guaranteed) and, whenever cfg.port is set to an externally-forwarded
|
||||
# port nginx/caddy don't themselves listen on (e.g. the QEMU test VM's
|
||||
# hostfwd), would curl a port nothing is listening on at all. curl's
|
||||
# --resolve pins the connection to 127.0.0.1 while keeping the
|
||||
# Host header/SNI as cfg.domain, so vhost routing and cert validation
|
||||
# still work normally.
|
||||
cronPort = if cfg.proxy.ssl then 443 else 80;
|
||||
cronUrl = path: "${scheme}://${cfg.domain}:${toString cronPort}${path}";
|
||||
|
||||
# curl'd cron endpoints -> (name, path, calendar spec)
|
||||
cronjobs = {
|
||||
"tasks-overdue" = { path = "/cronjob/tasks/overdue"; when = "*-*-* 06,18:00:00"; };
|
||||
@@ -264,6 +287,20 @@ in {
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
assertions = [
|
||||
{
|
||||
assertion = !(cfg.proxy.ssl && cfg.domain == "localhost");
|
||||
message = ''
|
||||
services.hortusfox-web: proxy.ssl = true (the default) requests an
|
||||
ACME certificate for services.hortusfox-web.domain, but domain is
|
||||
left at its own default of "localhost" -- Let's Encrypt can never
|
||||
issue a certificate for that, so this would only fail at runtime.
|
||||
Set domain to a real, publicly resolvable FQDN, or set
|
||||
proxy.ssl = false for a local/test deployment.
|
||||
'';
|
||||
}
|
||||
];
|
||||
|
||||
users.users.${cfg.user} = {
|
||||
isSystemUser = true;
|
||||
group = cfg.group;
|
||||
@@ -384,7 +421,7 @@ in {
|
||||
# this point.
|
||||
${lib.optionalString (cfg.adminAccount.passwordFile != null) ''
|
||||
admin_hash="$(php -r 'echo password_hash($argv[1], PASSWORD_BCRYPT);' "$(cat "${cfg.adminAccount.passwordFile}")")"
|
||||
mariadb "${cfg.database.name}" -e "INSERT INTO UserModel (name, email, password, admin) VALUES ('${cfg.adminAccount.username}', '${cfg.adminAccount.email}', '$admin_hash', 1);"
|
||||
mariadb "${cfg.database.name}" -e "INSERT INTO UserModel (name, email, password, admin) VALUES ('${sqlEscape cfg.adminAccount.username}', '${sqlEscape cfg.adminAccount.email}', '$admin_hash', 1);"
|
||||
''}
|
||||
|
||||
touch "${stateDir}/.installed"
|
||||
@@ -401,12 +438,13 @@ in {
|
||||
} // (lib.mapAttrs' (name: job:
|
||||
lib.nameValuePair "hortusfox-cron-${name}" {
|
||||
description = "HortusFox cronjob: ${name}";
|
||||
after = [ "nginx.service" ];
|
||||
after = [ "${cfg.proxy.server}.service" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
User = cfg.user;
|
||||
ExecStart = "${pkgs.curl}/bin/curl -fsS --max-time 300 "
|
||||
+ "${appUrl}${job.path}";
|
||||
+ "--resolve ${cfg.domain}:${toString cronPort}:127.0.0.1 "
|
||||
+ "${cronUrl job.path}";
|
||||
};
|
||||
}) cronjobs);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user