at 25.11-pre 8.0 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7let 8 cfg = config.services.omnom; 9 settingsFormat = pkgs.formats.yaml { }; 10 11 configFile = settingsFormat.generate "omnom-config.yml" cfg.settings; 12in 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 <omnom@127.0.0.1>"; 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}