feat: static sites

This commit is contained in:
Lewis Wynne 2026-04-07 16:48:29 +01:00
parent cc562fdede
commit 196440e90b
2 changed files with 44 additions and 23 deletions

View file

@ -2,7 +2,9 @@
# #
# domain — primary domain (required) # domain — primary domain (required)
# repo — git repository URL (required) # repo — git repository URL (required)
# port — Node.js server port (required) # static — serve build output as static files, no Node server (default: false)
# port — Node.js server port (required when static = false)
# buildOutputDir — build output directory relative to repo root (default: "dist")
# redirectDomains — domains that 301 to the primary domain (default: []) # redirectDomains — domains that 301 to the primary domain (default: [])
# branch — git branch to track (default: "main") # branch — git branch to track (default: "main")
# packageManager — "npm" or "pnpm" (default: "pnpm") # packageManager — "npm" or "pnpm" (default: "pnpm")
@ -36,6 +38,6 @@ in
services.site.penfield = { services.site.penfield = {
domain = "penfield2.ily.rs"; domain = "penfield2.ily.rs";
repo = "https://git.ily.rs/lew/penfield"; repo = "https://git.ily.rs/lew/penfield";
port = 4324; static = true;
}; };
} }

View file

@ -26,9 +26,22 @@ let
default = "main"; default = "main";
}; };
static = mkOption {
type = types.bool;
default = false;
description = "Serve build output as static files instead of running a Node.js server.";
};
port = mkOption { port = mkOption {
type = types.port; type = types.nullOr types.port;
description = "Port the Node.js server listens on."; default = null;
description = "Port the Node.js server listens on. Required when static = false.";
};
buildOutputDir = mkOption {
type = types.str;
default = "dist";
description = "Build output directory relative to repo root (used for static sites).";
}; };
packageManager = mkOption { packageManager = mkOption {
@ -100,7 +113,12 @@ in
config = { config = {
services.caddy.virtualHosts = mkMerge (mapAttrsToList (name: site: services.caddy.virtualHosts = mkMerge (mapAttrsToList (name: site:
{ {
${site.domain}.extraConfig = '' ${site.domain}.extraConfig = if site.static then ''
root * ${site.dataDir}/repo/${site.buildOutputDir}
encode zstd gzip
try_files {path} /index.html
file_server
'' else ''
reverse_proxy localhost:${toString site.port} reverse_proxy localhost:${toString site.port}
encode zstd gzip encode zstd gzip
''; '';
@ -114,23 +132,6 @@ in
systemd.services = mkMerge ((mapAttrsToList (name: site: systemd.services = mkMerge ((mapAttrsToList (name: site:
let h = siteHelpers name site; in { let h = siteHelpers name site; in {
${name} = {
description = site.domain;
environment = {
HOST = "127.0.0.1";
PORT = toString site.port;
} // site.environment;
serviceConfig = {
Type = "simple";
WorkingDirectory = "${h.dataDir}/repo";
ExecStart = "${pkgs.nodejs}/bin/node ${site.entryPoint}";
Restart = "on-failure";
User = name;
Group = name;
ReadWritePaths = site.readWritePaths;
};
};
"${name}-rebuild" = { "${name}-rebuild" = {
description = "Clone/pull and build ${site.domain}"; description = "Clone/pull and build ${site.domain}";
after = [ "network-online.target" ] ++ site.afterServices; after = [ "network-online.target" ] ++ site.afterServices;
@ -157,11 +158,29 @@ in
${h.installCmd} ${h.installCmd}
${h.pmBin} run build ${h.pmBin} run build
''; '';
ExecStartPost = "+/run/current-system/sw/bin/systemctl restart ${name}"; ExecStartPost = lib.mkIf (!site.static)
"+/run/current-system/sw/bin/systemctl restart ${name}";
User = name; User = name;
Group = name; Group = name;
}; };
}; };
} // lib.optionalAttrs (!site.static) {
${name} = {
description = site.domain;
environment = {
HOST = "127.0.0.1";
PORT = toString site.port;
} // site.environment;
serviceConfig = {
Type = "simple";
WorkingDirectory = "${h.dataDir}/repo";
ExecStart = "${pkgs.nodejs}/bin/node ${site.entryPoint}";
Restart = "on-failure";
User = name;
Group = name;
ReadWritePaths = site.readWritePaths;
};
};
} }
) cfg) ++ [{ ) cfg) ++ [{
site-webhook = mkIf (cfg != {}) { site-webhook = mkIf (cfg != {}) {