at master 10 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7let 8 9 eachBlockbook = config.services.blockbook-frontend; 10 11 blockbookOpts = 12 { 13 config, 14 lib, 15 name, 16 ... 17 }: 18 { 19 20 options = { 21 22 enable = lib.mkEnableOption "blockbook-frontend application"; 23 24 package = lib.mkPackageOption pkgs "blockbook" { }; 25 26 user = lib.mkOption { 27 type = lib.types.str; 28 default = "blockbook-frontend-${name}"; 29 description = "The user as which to run blockbook-frontend-${name}."; 30 }; 31 32 group = lib.mkOption { 33 type = lib.types.str; 34 default = "${config.user}"; 35 description = "The group as which to run blockbook-frontend-${name}."; 36 }; 37 38 certFile = lib.mkOption { 39 type = lib.types.nullOr lib.types.path; 40 default = null; 41 example = "/etc/secrets/blockbook-frontend-${name}/certFile"; 42 description = '' 43 To enable SSL, specify path to the name of certificate files without extension. 44 Expecting {file}`certFile.crt` and {file}`certFile.key`. 45 ''; 46 }; 47 48 configFile = lib.mkOption { 49 type = with lib.types; nullOr path; 50 default = null; 51 example = "${config.dataDir}/config.json"; 52 description = "Location of the blockbook configuration file."; 53 }; 54 55 coinName = lib.mkOption { 56 type = lib.types.str; 57 default = "Bitcoin"; 58 description = '' 59 See <https://github.com/trezor/blockbook/blob/master/bchain/coins/blockchain.go#L61> 60 for current of coins supported in master (Note: may differ from release). 61 ''; 62 }; 63 64 cssDir = lib.mkOption { 65 type = lib.types.path; 66 default = "${config.package}/share/css/"; 67 defaultText = lib.literalExpression ''"''${package}/share/css/"''; 68 example = lib.literalExpression ''"''${dataDir}/static/css/"''; 69 description = '' 70 Location of the dir with {file}`main.css` CSS file. 71 By default, the one shipped with the package is used. 72 ''; 73 }; 74 75 dataDir = lib.mkOption { 76 type = lib.types.path; 77 default = "/var/lib/blockbook-frontend-${name}"; 78 description = "Location of blockbook-frontend-${name} data directory."; 79 }; 80 81 debug = lib.mkOption { 82 type = lib.types.bool; 83 default = false; 84 description = "Debug mode, return more verbose errors, reload templates on each request."; 85 }; 86 87 internal = lib.mkOption { 88 type = lib.types.nullOr lib.types.str; 89 default = ":9030"; 90 description = "Internal http server binding `[address]:port`."; 91 }; 92 93 messageQueueBinding = lib.mkOption { 94 type = lib.types.str; 95 default = "tcp://127.0.0.1:38330"; 96 description = "Message Queue Binding `address:port`."; 97 }; 98 99 public = lib.mkOption { 100 type = lib.types.nullOr lib.types.str; 101 default = ":9130"; 102 description = "Public http server binding `[address]:port`."; 103 }; 104 105 rpc = { 106 url = lib.mkOption { 107 type = lib.types.str; 108 default = "http://127.0.0.1"; 109 description = "URL for JSON-RPC connections."; 110 }; 111 112 port = lib.mkOption { 113 type = lib.types.port; 114 default = 8030; 115 description = "Port for JSON-RPC connections."; 116 }; 117 118 user = lib.mkOption { 119 type = lib.types.str; 120 default = "rpc"; 121 description = "Username for JSON-RPC connections."; 122 }; 123 124 password = lib.mkOption { 125 type = lib.types.str; 126 default = "rpc"; 127 description = '' 128 RPC password for JSON-RPC connections. 129 Warning: this is stored in cleartext in the Nix store!!! 130 Use `configFile` or `passwordFile` if needed. 131 ''; 132 }; 133 134 passwordFile = lib.mkOption { 135 type = lib.types.nullOr lib.types.path; 136 default = null; 137 description = '' 138 File containing password of the RPC user. 139 Note: This options is ignored when `configFile` is used. 140 ''; 141 }; 142 }; 143 144 sync = lib.mkOption { 145 type = lib.types.bool; 146 default = true; 147 description = "Synchronizes until tip, if together with zeromq, keeps index synchronized."; 148 }; 149 150 templateDir = lib.mkOption { 151 type = lib.types.path; 152 default = "${config.package}/share/templates/"; 153 defaultText = lib.literalExpression ''"''${package}/share/templates/"''; 154 example = lib.literalExpression ''"''${dataDir}/templates/static/"''; 155 description = "Location of the HTML templates. By default, ones shipped with the package are used."; 156 }; 157 158 extraConfig = lib.mkOption { 159 type = lib.types.attrs; 160 default = { }; 161 example = lib.literalExpression '' 162 { 163 "alternative_estimate_fee" = "whatthefee-disabled"; 164 "alternative_estimate_fee_params" = "{\"url\": \"https://whatthefee.io/data.json\", \"periodSeconds\": 60}"; 165 "fiat_rates" = "coingecko"; 166 "fiat_rates_params" = "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"bitcoin\", \"periodSeconds\": 60}"; 167 "coin_shortcut" = "BTC"; 168 "coin_label" = "Bitcoin"; 169 "parse" = true; 170 "subversion" = ""; 171 "address_format" = ""; 172 "xpub_magic" = 76067358; 173 "xpub_magic_segwit_p2sh" = 77429938; 174 "xpub_magic_segwit_native" = 78792518; 175 "mempool_workers" = 8; 176 "mempool_sub_workers" = 2; 177 "block_addresses_to_keep" = 300; 178 }''; 179 description = '' 180 Additional configurations to be appended to {file}`coin.conf`. 181 Overrides any already defined configuration options. 182 See <https://github.com/trezor/blockbook/tree/master/configs/coins> 183 for current configuration options supported in master (Note: may differ from release). 184 ''; 185 }; 186 187 extraCmdLineOptions = lib.mkOption { 188 type = lib.types.listOf lib.types.str; 189 default = [ ]; 190 example = [ 191 "-workers=1" 192 "-dbcache=0" 193 "-logtosderr" 194 ]; 195 description = '' 196 Extra command line options to pass to Blockbook. 197 Run blockbook --help to list all available options. 198 ''; 199 }; 200 }; 201 }; 202in 203{ 204 # interface 205 206 options = { 207 services.blockbook-frontend = lib.mkOption { 208 type = lib.types.attrsOf (lib.types.submodule blockbookOpts); 209 default = { }; 210 description = "Specification of one or more blockbook-frontend instances."; 211 }; 212 }; 213 214 # implementation 215 216 config = lib.mkIf (eachBlockbook != { }) { 217 218 systemd.services = lib.mapAttrs' ( 219 blockbookName: cfg: 220 (lib.nameValuePair "blockbook-frontend-${blockbookName}" ( 221 let 222 configFile = 223 if cfg.configFile != null then 224 cfg.configFile 225 else 226 pkgs.writeText "config.conf" ( 227 builtins.toJSON ( 228 { 229 coin_name = "${cfg.coinName}"; 230 rpc_user = "${cfg.rpc.user}"; 231 rpc_pass = "${cfg.rpc.password}"; 232 rpc_url = "${cfg.rpc.url}:${toString cfg.rpc.port}"; 233 message_queue_binding = "${cfg.messageQueueBinding}"; 234 } 235 // cfg.extraConfig 236 ) 237 ); 238 in 239 { 240 description = "blockbook-frontend-${blockbookName} daemon"; 241 after = [ "network.target" ]; 242 wantedBy = [ "multi-user.target" ]; 243 preStart = '' 244 ln -sf ${cfg.templateDir} ${cfg.dataDir}/static/ 245 ln -sf ${cfg.cssDir} ${cfg.dataDir}/static/ 246 ${lib.optionalString (cfg.rpc.passwordFile != null && cfg.configFile == null) '' 247 CONFIGTMP=$(mktemp) 248 ${pkgs.jq}/bin/jq ".rpc_pass = \"$(cat ${cfg.rpc.passwordFile})\"" ${configFile} > $CONFIGTMP 249 mv $CONFIGTMP ${cfg.dataDir}/${blockbookName}-config.json 250 ''} 251 ''; 252 serviceConfig = { 253 User = cfg.user; 254 Group = cfg.group; 255 ExecStart = '' 256 ${cfg.package}/bin/blockbook \ 257 ${ 258 if (cfg.rpc.passwordFile != null && cfg.configFile == null) then 259 "-blockchaincfg=${cfg.dataDir}/${blockbookName}-config.json" 260 else 261 "-blockchaincfg=${configFile}" 262 } \ 263 -datadir=${cfg.dataDir} \ 264 ${lib.optionalString (cfg.sync != false) "-sync"} \ 265 ${lib.optionalString (cfg.certFile != null) "-certfile=${toString cfg.certFile}"} \ 266 ${lib.optionalString (cfg.debug != false) "-debug"} \ 267 ${lib.optionalString (cfg.internal != null) "-internal=${toString cfg.internal}"} \ 268 ${lib.optionalString (cfg.public != null) "-public=${toString cfg.public}"} \ 269 ${toString cfg.extraCmdLineOptions} 270 ''; 271 Restart = "on-failure"; 272 WorkingDirectory = cfg.dataDir; 273 LimitNOFILE = 65536; 274 }; 275 } 276 )) 277 ) eachBlockbook; 278 279 systemd.tmpfiles.rules = lib.flatten ( 280 lib.mapAttrsToList (blockbookName: cfg: [ 281 "d ${cfg.dataDir} 0750 ${cfg.user} ${cfg.group} - -" 282 "d ${cfg.dataDir}/static 0750 ${cfg.user} ${cfg.group} - -" 283 ]) eachBlockbook 284 ); 285 286 users.users = lib.mapAttrs' ( 287 blockbookName: cfg: 288 (lib.nameValuePair "blockbook-frontend-${blockbookName}" { 289 name = cfg.user; 290 group = cfg.group; 291 home = cfg.dataDir; 292 isSystemUser = true; 293 }) 294 ) eachBlockbook; 295 296 users.groups = lib.mapAttrs' ( 297 instanceName: cfg: (lib.nameValuePair "${cfg.group}" { }) 298 ) eachBlockbook; 299 }; 300 301 meta.maintainers = with lib.maintainers; [ _1000101 ]; 302 303}