Skip to content

Commit b9da4f2

Browse files
authored
nixos/omnom: init module (#357830)
1 parent cd2aa80 commit b9da4f2

File tree

2 files changed

+252
-0
lines changed

2 files changed

+252
-0
lines changed

nixos/modules/module-list.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,7 @@
813813
./services/misc/octoprint.nix
814814
./services/misc/ollama.nix
815815
./services/misc/ombi.nix
816+
./services/misc/omnom.nix
816817
./services/misc/open-webui.nix
817818
./services/misc/osrm.nix
818819
./services/misc/owncast.nix
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
{
2+
config,
3+
lib,
4+
pkgs,
5+
...
6+
}:
7+
let
8+
cfg = config.services.omnom;
9+
settingsFormat = pkgs.formats.yaml { };
10+
11+
configFile = settingsFormat.generate "omnom-config.yml" cfg.settings;
12+
in
13+
{
14+
options = {
15+
services.omnom = {
16+
enable = lib.mkEnableOption "Omnom, a webpage bookmarking and snapshotting service";
17+
package = lib.mkPackageOption pkgs "omnom" { };
18+
19+
dataDir = lib.mkOption {
20+
type = lib.types.path;
21+
default = "/var/lib/omnom";
22+
description = "The directory where Omnom stores its data files.";
23+
};
24+
25+
port = lib.mkOption {
26+
type = lib.types.port;
27+
default = 7331;
28+
description = "The Omnom service port.";
29+
};
30+
31+
openFirewall = lib.mkOption {
32+
type = lib.types.bool;
33+
default = false;
34+
description = "Whether to open ports in the firewall.";
35+
};
36+
37+
user = lib.mkOption {
38+
type = lib.types.nonEmptyStr;
39+
default = "omnom";
40+
description = "The Omnom service user.";
41+
};
42+
43+
group = lib.mkOption {
44+
type = lib.types.nonEmptyStr;
45+
default = "omnom";
46+
description = "The Omnom service group.";
47+
};
48+
49+
passwordFile = lib.mkOption {
50+
type = lib.types.nullOr lib.types.path;
51+
default = null;
52+
description = "File containing the password for the SMTP user.";
53+
};
54+
55+
settings = lib.mkOption {
56+
description = ''
57+
Configuration options for the /etc/omnom/config.yml file.
58+
'';
59+
type = lib.types.submodule {
60+
freeformType = settingsFormat.type;
61+
options = {
62+
app = {
63+
debug = lib.mkEnableOption "debug mode";
64+
disable_signup = lib.mkEnableOption "restricting user creation";
65+
results_per_page = lib.mkOption {
66+
type = lib.types.int;
67+
default = 20;
68+
description = "Number of results per page.";
69+
};
70+
};
71+
db = {
72+
connection = lib.mkOption {
73+
type = lib.types.str;
74+
default = "${cfg.dataDir}/db.sqlite3";
75+
description = "Database connection URI.";
76+
defaultText = lib.literalExpression ''
77+
"''${config.services.omnom.dataDir}/db.sqlite3"
78+
'';
79+
};
80+
type = lib.mkOption {
81+
type = lib.types.enum [ "sqlite" ];
82+
default = "sqlite";
83+
description = "Database type.";
84+
};
85+
};
86+
server = {
87+
address = lib.mkOption {
88+
type = lib.types.str;
89+
default = "127.0.0.1:${toString cfg.port}";
90+
description = "Server address.";
91+
defaultText = lib.literalExpression ''
92+
"127.0.0.1:''${config.services.omnom.port}"
93+
'';
94+
};
95+
secure_cookie = lib.mkOption {
96+
type = lib.types.bool;
97+
default = true;
98+
description = "Whether to limit cookies to a secure channel.";
99+
};
100+
};
101+
storage = {
102+
type = lib.mkOption {
103+
type = lib.types.str;
104+
default = "fs";
105+
description = "Storage type.";
106+
};
107+
root = lib.mkOption {
108+
type = lib.types.path;
109+
default = "${cfg.dataDir}/static/data";
110+
defaultText = lib.literalExpression ''
111+
"''${config.services.omnom.dataDir}/static/data"
112+
'';
113+
description = "Where the snapshots are saved.";
114+
};
115+
};
116+
smtp = {
117+
tls = lib.mkEnableOption "Whether TLS encryption should be used.";
118+
tls_allow_insecure = lib.mkEnableOption "Whether to allow insecure TLS.";
119+
host = lib.mkOption {
120+
type = lib.types.str;
121+
default = "";
122+
description = "SMTP server hostname.";
123+
};
124+
port = lib.mkOption {
125+
type = lib.types.port;
126+
default = 25;
127+
description = "SMTP server port address.";
128+
};
129+
sender = lib.mkOption {
130+
type = lib.types.str;
131+
default = "Omnom <[email protected]>";
132+
description = "Omnom sender e-mail.";
133+
};
134+
send_timeout = lib.mkOption {
135+
type = lib.types.int;
136+
default = 10;
137+
description = "Send timeout duration in seconds.";
138+
};
139+
connection_timeout = lib.mkOption {
140+
type = lib.types.int;
141+
default = 5;
142+
description = "Connection timeout duration in seconds.";
143+
};
144+
};
145+
};
146+
};
147+
default = { };
148+
};
149+
};
150+
};
151+
152+
config = lib.mkIf cfg.enable {
153+
assertions = [
154+
{
155+
assertion = !lib.hasAttr "password" cfg.settings.smtp;
156+
message = ''
157+
`services.omnom.settings.smtp.password` must be defined in `services.omnom.passwordFile`.
158+
'';
159+
}
160+
{
161+
assertion = !(cfg.settings.storage.root != "${cfg.dataDir}/static/data");
162+
message = ''
163+
For Omnom to access the snapshots, it needs the storage root
164+
directory to be inside the service's working directory.
165+
166+
As such, `services.omnom.settings.storage.root` must be the same as
167+
`''${services.omnom.dataDir}/static/data`.
168+
'';
169+
}
170+
];
171+
172+
systemd.services.omnom = {
173+
path = with pkgs; [
174+
yq-go # needed by startup script
175+
];
176+
177+
serviceConfig = {
178+
User = cfg.user;
179+
Group = cfg.group;
180+
StateDirectory = "omnom";
181+
WorkingDirectory = cfg.dataDir;
182+
Restart = "on-failure";
183+
RestartSec = "10s";
184+
LoadCredential = lib.optional (cfg.passwordFile != null) "PASSWORD_FILE:${cfg.passwordFile}";
185+
};
186+
script = ''
187+
install -m 600 ${configFile} $STATE_DIRECTORY/config.yml
188+
189+
${lib.optionalString (cfg.passwordFile != null) ''
190+
# merge password into main config
191+
yq -i '.smtp.password = load(env(CREDENTIALS_DIRECTORY) + "/PASSWORD_FILE")' \
192+
"$STATE_DIRECTORY/config.yml"
193+
''}
194+
195+
${lib.getExe cfg.package} listen --config "$STATE_DIRECTORY/config.yml"
196+
'';
197+
after = [
198+
"network.target"
199+
"systemd-tmpfiles-setup.service"
200+
];
201+
wantedBy = [ "multi-user.target" ];
202+
};
203+
204+
# TODO: The program needs to run from the dataDir for it the work, which
205+
# is difficult to do with a DynamicUser.
206+
# After this has been fixed upstream, remove this and use DynamicUser, instead.
207+
# See: https://github.com/asciimoo/omnom/issues/21
208+
users = {
209+
users = lib.mkIf (cfg.user == "omnom") {
210+
omnom = {
211+
group = cfg.group;
212+
home = cfg.dataDir;
213+
isSystemUser = true;
214+
};
215+
};
216+
groups = lib.mkIf (cfg.group == "omnom") { omnom = { }; };
217+
};
218+
219+
systemd.tmpfiles.settings."10-omnom" =
220+
let
221+
settings = {
222+
inherit (cfg) user group;
223+
};
224+
in
225+
{
226+
"${cfg.dataDir}"."d" = settings;
227+
"${cfg.dataDir}/templates"."L+" = settings // {
228+
argument = "${cfg.package}/share/templates";
229+
};
230+
"${cfg.settings.storage.root}"."d" = settings;
231+
};
232+
233+
networking.firewall = lib.mkIf cfg.openFirewall {
234+
allowedTCPPorts = [ cfg.port ];
235+
};
236+
237+
environment.systemPackages =
238+
let
239+
omnom-wrapped = pkgs.writeScriptBin "omnom" ''
240+
#! ${pkgs.runtimeShell}
241+
cd ${cfg.dataDir}
242+
sudo=exec
243+
if [[ "$USER" != ${cfg.user} ]]; then
244+
sudo='exec /run/wrappers/bin/sudo -u ${cfg.user}'
245+
fi
246+
$sudo ${lib.getExe cfg.package} "$@"
247+
'';
248+
in
249+
[ omnom-wrapped ];
250+
};
251+
}

0 commit comments

Comments
 (0)