Skip to content

Commit

Permalink
nixos/omnom: init module
Browse files Browse the repository at this point in the history
  • Loading branch information
eljamm committed Nov 23, 2024
1 parent 4fa019d commit 0a745b6
Show file tree
Hide file tree
Showing 2 changed files with 282 additions and 0 deletions.
1 change: 1 addition & 0 deletions nixos/modules/module-list.nix
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,7 @@
./services/misc/octoprint.nix
./services/misc/ollama.nix
./services/misc/ombi.nix
./services/misc/omnom.nix
./services/misc/open-webui.nix
./services/misc/osrm.nix
./services/misc/owncast.nix
Expand Down
281 changes: 281 additions & 0 deletions nixos/modules/services/misc/omnom.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.omnom;
settingsFormat = pkgs.formats.yaml { };

configFile = settingsFormat.generate "omnom-config.yml" cfg.settings;
in
{
options = {
services.omnom = {
enable = lib.mkEnableOption "Omnom, a webpage bookmarking and snapshotting service";
package = lib.mkPackageOption pkgs "omnom" { };

dataDir = lib.mkOption {
type = lib.types.path;
default = "/var/lib/omnom";
description = "The directory where Omnom stores its data files.";
};

port = lib.mkOption {
type = lib.types.port;
default = 7331;
description = "The Omnom service port.";
};

openFirewall = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to open ports in the firewall.";
};

user = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "omnom";
description = "The Omnom service user.";
};

group = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "omnom";
description = "The Omnom service group.";
};

secretsFile = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
description = ''
YAML file containing all secrets. This needs to be in the same structure as the configuration.
This must contain the SMTP username and password.
Example:
```yaml
smtp:
username: "test"
password: "test"
```
'';
};

settings = lib.mkOption {
description = ''
Configuration options for the /etc/omnom/config.yml file.
'';
type = lib.types.submodule {
freeformType = settingsFormat.type;
options = {
app = {
debug = lib.mkEnableOption "debug mode";
disable_signup = lib.mkEnableOption "restricting user creation";
results_per_page = lib.mkOption {
type = lib.types.int;
default = 20;
description = "Number of results per page.";
};
};
db = {
connection = lib.mkOption {
type = lib.types.str;
default = "${cfg.dataDir}/db.sqlite3";
description = "Database connection URI.";
defaultText = lib.literalExpression ''
"''${config.services.omnom.dataDir}/db.sqlite3"
'';
};
type = lib.mkOption {
type = lib.types.enum [ "sqlite" ];
default = "sqlite";
description = "Database type.";
};
};
server = {
address = lib.mkOption {
type = lib.types.str;
default = "127.0.0.1:${toString cfg.port}";
description = "Server address.";
defaultText = lib.literalExpression ''
"127.0.0.1:''${config.services.omnom.port}"
'';
};
secure_cookie = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to limit cookies to a secure channel.";
};
};
storage = {
type = lib.mkOption {
type = lib.types.str;
default = "fs";
description = "Storage type.";
};
root = lib.mkOption {
type = lib.types.path;
default = "${cfg.dataDir}/static/data";
defaultText = lib.literalExpression ''
"''${config.services.omnom.dataDir}/static/data"
'';
description = "Where the snapshots are saved.";
};
};
smtp = {
tls = lib.mkEnableOption "Whether TLS encryption should be used.";
tls_allow_insecure = lib.mkEnableOption "Whether to allow insecure TLS.";
host = lib.mkOption {
type = lib.types.str;
default = "";
description = "SMTP server hostname.";
};
port = lib.mkOption {
type = lib.types.port;
default = 25;
description = "SMTP server port address.";
};
sender = lib.mkOption {
type = lib.types.str;
default = "Omnom <[email protected]>";
description = "Omnom sender e-mail.";
};
send_timeout = lib.mkOption {
type = lib.types.int;
default = 10;
description = "Send timeout duration in seconds.";
};
connection_timeout = lib.mkOption {
type = lib.types.int;
default = 5;
description = "Connection timeout duration in seconds.";
};
};
};
};
default = { };
};
};
};

config = lib.mkIf cfg.enable {
assertions = [
{
assertion = !lib.hasAttr "username" cfg.settings.smtp;
message = ''
`services.omnom.settings.smtp.username` must be defined in `services.omnom.secretsFile`.
'';
}
{
assertion = !lib.hasAttr "password" cfg.settings.smtp;
message = ''
`services.omnom.settings.smtp.password` must be defined in `services.omnom.secretsFile`.
'';
}
{
assertion = !(cfg.settings.storage.root != "${cfg.dataDir}/static/data");
message = ''
For Omnom to access the snapshots, it needs the storage root
directory to be inside the service's working directory.
As such, `services.omnom.settings.storage.root` must be the same as
`''${services.omnom.dataDir}/static/data`.
'';
}
];

environment.etc."omnom/config.yml".source = configFile;

systemd.services.omnom = {
path = with pkgs; [
yq-go # needed by startup script
];

serviceConfig = {
User = cfg.user;
Group = cfg.group;
StateDirectory = "omnom";
WorkingDirectory = cfg.dataDir;
Restart = "on-failure";
RestartSec = "10s";
LoadCredential = lib.mkIf (cfg.secretsFile != null) [
"SECRETS_FILE:${cfg.secretsFile}"
];
};
script = ''
export CONFIG_FILE=${configFile}
${lib.optionalString (cfg.secretsFile != null) ''
# merge secrets into main config
yq eval-all "select(fileIndex == 0) * select(fileIndex == 1)" \
"${configFile}" "$CREDENTIALS_DIRECTORY/SECRETS_FILE" > "${cfg.dataDir}/config.yml"
CONFIG_FILE="${cfg.dataDir}/config.yml"
''}
${lib.getExe cfg.package} listen --config "$CONFIG_FILE"
'';
after = [
"network.target"
"systemd-tmpfiles-setup.service"
];
wantedBy = [ "multi-user.target" ];
};

# TODO: The program needs to run from the dataDir for it the work, which
# is difficult to do with a DynamicUser.
# After this has been fixed upstream, remove this and use DynamicUser, instead.
users = {
users = lib.mkIf (cfg.user == "omnom") {
omnom = {
group = cfg.group;
home = cfg.dataDir;
isSystemUser = true;
};
};
groups = lib.mkIf (cfg.group == "omnom") { omnom = { }; };
};

systemd.tmpfiles.settings."10-omnom" =
let
settings = {
inherit (cfg) user group;
};
in
{
"${cfg.dataDir}"."d" = settings;
"${cfg.dataDir}/templates"."L+" = settings // {
argument = "${cfg.package}/share/templates";
};
"${cfg.settings.storage.root}"."d" = settings;
}
// lib.optionalAttrs (cfg.settings.db.type == "sqlite") {
"${cfg.dataDir}/db.sqlite3"."f" = settings;
}
// lib.optionalAttrs (cfg.secretsFile != null) {
"${cfg.dataDir}/config.yml"."f" = settings // {
mode = "0600";
};
};

networking.firewall = lib.mkIf cfg.openFirewall {
allowedTCPPorts = [ cfg.port ];
};

environment.systemPackages =
let
omnom-wrapped = pkgs.writeScriptBin "omnom" ''
#! ${pkgs.runtimeShell}
cd ${cfg.dataDir}
sudo=exec
if [[ "$USER" != ${cfg.user} ]]; then
sudo='exec /run/wrappers/bin/sudo -u ${cfg.user}'
fi
$sudo ${lib.getExe cfg.package} "$@"
'';
in
[ omnom-wrapped ];
};
}

0 comments on commit 0a745b6

Please sign in to comment.