at master 8.2 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 }; 108 smtp = { 109 tls = lib.mkEnableOption "Whether TLS encryption should be used."; 110 tls_allow_insecure = lib.mkEnableOption "Whether to allow insecure TLS."; 111 host = lib.mkOption { 112 type = lib.types.str; 113 default = ""; 114 description = "SMTP server hostname."; 115 }; 116 port = lib.mkOption { 117 type = lib.types.port; 118 default = 25; 119 description = "SMTP server port address."; 120 }; 121 sender = lib.mkOption { 122 type = lib.types.str; 123 default = "Omnom <omnom@127.0.0.1>"; 124 description = "Omnom sender e-mail."; 125 }; 126 send_timeout = lib.mkOption { 127 type = lib.types.int; 128 default = 10; 129 description = "Send timeout duration in seconds."; 130 }; 131 connection_timeout = lib.mkOption { 132 type = lib.types.int; 133 default = 5; 134 description = "Connection timeout duration in seconds."; 135 }; 136 }; 137 activitypub = { 138 pubkey = lib.mkOption { 139 type = lib.types.path; 140 default = "${cfg.dataDir}/public.pem"; 141 defaultText = lib.literalExpression '' 142 "''${config.services.omnom.dataDir}/public.pem" 143 ''; 144 description = "ActivityPub public key. Will be generated, by default."; 145 }; 146 privkey = lib.mkOption { 147 type = lib.types.path; 148 default = "${cfg.dataDir}/private.pem"; 149 defaultText = lib.literalExpression '' 150 "''${config.services.omnom.dataDir}/private.pem" 151 ''; 152 description = "ActivityPub private key. Will be generated, by default."; 153 }; 154 }; 155 }; 156 }; 157 default = { }; 158 }; 159 }; 160 }; 161 162 config = lib.mkIf cfg.enable { 163 services.omnom = { 164 settings.app = { 165 static_dir = "${cfg.dataDir}/static"; 166 template_dir = "${cfg.package}/share/templates"; 167 }; 168 }; 169 170 assertions = [ 171 { 172 assertion = !lib.hasAttr "password" cfg.settings.smtp; 173 message = '' 174 `services.omnom.settings.smtp.password` must be defined in `services.omnom.passwordFile`. 175 ''; 176 } 177 ]; 178 179 systemd.services.omnom = { 180 path = with pkgs; [ 181 yq-go # needed by startup script 182 ]; 183 184 serviceConfig = { 185 User = cfg.user; 186 Group = cfg.group; 187 StateDirectory = "omnom"; 188 WorkingDirectory = cfg.dataDir; 189 Restart = "on-failure"; 190 RestartSec = "10s"; 191 LoadCredential = lib.optional (cfg.passwordFile != null) "PASSWORD_FILE:${cfg.passwordFile}"; 192 }; 193 script = '' 194 install -m 600 ${configFile} $STATE_DIRECTORY/config.yml 195 196 ${lib.optionalString (cfg.passwordFile != null) '' 197 # merge password into main config 198 yq -i '.smtp.password = load(env(CREDENTIALS_DIRECTORY) + "/PASSWORD_FILE")' \ 199 "$STATE_DIRECTORY/config.yml" 200 ''} 201 202 ${lib.getExe cfg.package} listen --config "$STATE_DIRECTORY/config.yml" 203 ''; 204 after = [ 205 "network.target" 206 "systemd-tmpfiles-setup.service" 207 ]; 208 wantedBy = [ "multi-user.target" ]; 209 }; 210 211 # TODO: The program needs to run from the dataDir for it the work, which 212 # is difficult to do with a DynamicUser. 213 # After this has been fixed upstream, remove this and use DynamicUser, instead. 214 # See: https://github.com/asciimoo/omnom/issues/21 215 users = { 216 users = lib.mkIf (cfg.user == "omnom") { 217 omnom = { 218 group = cfg.group; 219 home = cfg.dataDir; 220 isSystemUser = true; 221 }; 222 }; 223 groups = lib.mkIf (cfg.group == "omnom") { omnom = { }; }; 224 }; 225 226 systemd.tmpfiles.settings."10-omnom" = 227 let 228 settings = { 229 inherit (cfg) user group; 230 }; 231 in 232 { 233 "${cfg.dataDir}"."d" = settings; 234 "${cfg.settings.app.static_dir}"."C" = settings // { 235 argument = "${cfg.package}/share/static"; 236 }; 237 "${cfg.settings.app.static_dir}/data"."d" = settings; 238 }; 239 240 networking.firewall = lib.mkIf cfg.openFirewall { 241 allowedTCPPorts = [ cfg.port ]; 242 }; 243 244 environment.systemPackages = 245 let 246 omnom-wrapped = pkgs.writeScriptBin "omnom" '' 247 #! ${pkgs.runtimeShell} 248 cd ${cfg.dataDir} 249 sudo=exec 250 if [[ "$USER" != ${cfg.user} ]]; then 251 sudo='exec /run/wrappers/bin/sudo -u ${cfg.user}' 252 fi 253 $sudo ${lib.getExe cfg.package} "$@" 254 ''; 255 in 256 [ omnom-wrapped ]; 257 }; 258}