selfhostblocks/modules/services/pinchflat.nix
2025-10-15 01:20:00 +02:00

194 lines
5.2 KiB
Nix

{ config, lib, pkgs, ... }:
let
cfg = config.shb.pinchflat;
inherit (lib) types;
contracts = pkgs.callPackage ../contracts {};
in
{
imports = [
../blocks/nginx.nix
];
options.shb.pinchflat = {
enable = lib.mkEnableOption "the Pinchflat service.";
subdomain = lib.mkOption {
type = lib.types.str;
description = "Subdomain under which Pinchflat will be served.";
default = "pinchflat";
};
domain = lib.mkOption {
type = lib.types.str;
description = "domain under which Pinchflat will be served.";
example = "mydomain.com";
};
ssl = lib.mkOption {
description = "Path to SSL files";
type = lib.types.nullOr contracts.ssl.certs;
default = null;
};
port = lib.mkOption {
type = lib.types.port;
description = "Port Pinchflat listens to incoming requests.";
default = 8945;
};
secretKeyBase = lib.mkOption {
description = ''
Used to sign/encrypt cookies and other secrets.
Make sure the secret is at least 64 characters long.
'';
type = types.submodule {
options = contracts.secret.mkRequester {
restartUnits = [ "pinchflat.service" ];
};
};
};
mediaDir = lib.mkOption {
description = "Path where videos are stored.";
type = lib.types.str;
};
timeZone = lib.mkOption {
type = lib.types.oneOf [ lib.types.str lib.shb.secretFileType ];
description = "Timezone of this instance.";
example = "America/Los_Angeles";
};
ldap = lib.mkOption {
description = ''
Setup LDAP integration.
'';
default = {};
type = types.submodule {
options = {
enable = lib.mkEnableOption "LDAP integration." // {
default = cfg.sso.enable;
};
userGroup = lib.mkOption {
type = types.str;
description = "Group users must belong to be able to login.";
default = "pinchflat_user";
};
};
};
};
sso = lib.mkOption {
description = ''
Setup SSO integration.
'';
default = {};
type = types.submodule {
options = {
enable = lib.mkEnableOption "SSO integration.";
authEndpoint = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = "Endpoint to the SSO provider. Leave null to not have SSO configured.";
example = "https://authelia.example.com";
};
authorization_policy = lib.mkOption {
type = types.enum [ "one_factor" "two_factor" ];
description = "Require one factor (password) or two factor (device) authentication.";
default = "one_factor";
};
};
};
};
backup = lib.mkOption {
description = ''
Backup media directory `shb.mediaDir`.
'';
default = {};
type = lib.types.submodule {
options = contracts.backup.mkRequester {
user = "pinchflat";
sourceDirectories = [
cfg.mediaDir
];
sourceDirectoriesText = "[ config.shb.pinchflat.mediaDir ]";
};
};
};
};
config = lib.mkIf cfg.enable {
systemd.tmpfiles.rules = [
"d '/run/pinchflat' 0750 root root - -"
];
# Pinchflat relies on the global value so for now this is the only way to pass the option in.
time.timeZone = lib.mkDefault cfg.timeZone;
services.pinchflat = {
inherit (cfg) enable port mediaDir;
secretsFile = "/run/pinchflat/secrets.env";
extraConfig = {
ENABLE_PROMETHEUS = true;
# TZ = "as"; # I consider where you live to be sensible so it should be passed as a secret.
};
};
# This should be using a contract instead of setting the option directly.
shb.lldap = lib.mkIf config.shb.lldap.enable {
ensureGroups = { ${cfg.ldap.userGroup} = {}; };
};
systemd.services.pinchflat-pre = {
script = lib.shb.replaceSecrets {
userConfig = {
SECRET_KEY_BASE.source = cfg.secretKeyBase.result.path;
# TZ = cfg.secretKeyBase.result.path; # Uncomment when PR is merged.
};
resultPath = "/run/pinchflat/secrets.env";
generator = lib.shb.toEnvVar;
};
serviceConfig.Type = "oneshot";
wantedBy = [ "multi-user.target" ];
before = [ "pinchflat.service" ];
requiredBy = [ "pinchflat.service" ];
};
shb.nginx.vhosts = [
{
inherit (cfg) subdomain domain ssl;
inherit (cfg.sso) authEndpoint;
upstream = "http://127.0.0.1:${toString cfg.port}";
autheliaRules = lib.optionals (cfg.sso.enable) [
{
domain = "${cfg.subdomain}.${cfg.domain}";
policy = cfg.sso.authorization_policy;
subject = [ "group:${cfg.ldap.userGroup}" ];
}
];
}
];
services.prometheus.scrapeConfigs = [
{
job_name = "pinchflat";
static_configs = [
{
targets = ["127.0.0.1:${toString cfg.port}"];
labels = {
"hostname" = config.networking.hostName;
"domain" = cfg.domain;
};
}
];
}
];
};
}