7f7a51ceb5
Default client_max_body_size of 1M caused 413s on typical photo uploads.
137 lines
4.0 KiB
Nix
137 lines
4.0 KiB
Nix
self:
|
|
{ config, lib, pkgs, ... }:
|
|
|
|
let
|
|
cfg = config.services.photogallery;
|
|
pkg = self.packages.${pkgs.system}.default;
|
|
in {
|
|
|
|
options.services.photogallery = {
|
|
|
|
enable = lib.mkEnableOption "photogallery static photo gallery";
|
|
|
|
contentDir = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "/var/lib/photogallery/content";
|
|
description = "Directory containing images and .toml sidecars.";
|
|
};
|
|
|
|
outputDir = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "/var/lib/photogallery/public";
|
|
description = "Directory for the generated static site.";
|
|
};
|
|
|
|
baseURL = lib.mkOption {
|
|
type = lib.types.str;
|
|
example = "https://photos.example.com";
|
|
description = "Public base URL, no trailing slash.";
|
|
};
|
|
|
|
port = lib.mkOption {
|
|
type = lib.types.port;
|
|
default = 8080;
|
|
description = "Upload API port. Always bound to 127.0.0.1.";
|
|
};
|
|
|
|
nginx = {
|
|
enable = lib.mkEnableOption "nginx virtual host for photogallery";
|
|
|
|
domain = lib.mkOption {
|
|
type = lib.types.str;
|
|
example = "photos.example.com";
|
|
description = "Domain for the nginx virtual host.";
|
|
};
|
|
|
|
htpasswdFile = lib.mkOption {
|
|
type = lib.types.path;
|
|
example = "/etc/nginx/.htpasswd-gallery";
|
|
description = "Path to htpasswd file protecting the /upload endpoint.";
|
|
};
|
|
|
|
enableACME = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = true;
|
|
description = "Enable ACME/Let's Encrypt for TLS.";
|
|
};
|
|
};
|
|
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable (lib.mkMerge [
|
|
|
|
# ── Core service ──────────────────────────────────────────────────────
|
|
|
|
{
|
|
users.users.photogallery = {
|
|
isSystemUser = true;
|
|
group = "photogallery";
|
|
home = "/var/lib/photogallery";
|
|
createHome = true;
|
|
description = "photogallery service user";
|
|
};
|
|
users.groups.photogallery = {};
|
|
|
|
systemd.tmpfiles.rules = [
|
|
"d ${cfg.contentDir} 0755 photogallery photogallery -"
|
|
"d ${cfg.outputDir} 0755 photogallery photogallery -"
|
|
];
|
|
|
|
systemd.services.photogallery = {
|
|
description = "photogallery static site generator";
|
|
after = [ "network.target" ];
|
|
wantedBy = [ "multi-user.target" ];
|
|
|
|
serviceConfig = {
|
|
User = "photogallery";
|
|
Group = "photogallery";
|
|
|
|
ExecStart = lib.escapeShellArgs [
|
|
"${pkg}/bin/photogallery"
|
|
"--content" cfg.contentDir
|
|
"--output" cfg.outputDir
|
|
"--base-url" cfg.baseURL
|
|
"--port" (toString cfg.port)
|
|
];
|
|
|
|
Restart = "on-failure";
|
|
RestartSec = "5s";
|
|
|
|
# Hardening
|
|
NoNewPrivileges = true;
|
|
PrivateTmp = true;
|
|
PrivateDevices = true;
|
|
ProtectSystem = "strict";
|
|
ProtectHome = true;
|
|
ReadWritePaths = [ cfg.contentDir cfg.outputDir ];
|
|
};
|
|
};
|
|
}
|
|
|
|
# ── Optional nginx vhost ──────────────────────────────────────────────
|
|
|
|
(lib.mkIf cfg.nginx.enable {
|
|
services.nginx = {
|
|
enable = true;
|
|
virtualHosts.${cfg.nginx.domain} = {
|
|
enableACME = cfg.nginx.enableACME;
|
|
forceSSL = cfg.nginx.enableACME;
|
|
root = cfg.outputDir;
|
|
|
|
locations."/" = {
|
|
tryFiles = "$uri $uri/ =404";
|
|
};
|
|
|
|
locations."/upload" = {
|
|
proxyPass = "http://127.0.0.1:${toString cfg.port}";
|
|
basicAuth = "gallery";
|
|
basicAuthFile = cfg.nginx.htpasswdFile;
|
|
extraConfig = "client_max_body_size 20M;";
|
|
};
|
|
};
|
|
};
|
|
})
|
|
|
|
]);
|
|
}
|