├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── Dockerfile ├── README.md ├── client ├── react-url-mapper │ ├── .env │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── robots.txt │ ├── src │ │ ├── App.css │ │ ├── App.test.tsx │ │ ├── App.tsx │ │ ├── Settings.tsx │ │ ├── api │ │ │ └── url_map.ts │ │ ├── index.css │ │ ├── index.tsx │ │ ├── react-app-env.d.ts │ │ ├── reportWebVitals.ts │ │ ├── setupTests.ts │ │ └── url_maps │ │ │ ├── URLMapEdit.tsx │ │ │ ├── URLMapIndex.tsx │ │ │ └── URLMapNew.tsx │ └── tsconfig.json └── tera │ ├── index.html │ ├── index.js │ ├── style.css │ └── url_maps │ ├── edit.html │ ├── index.html │ └── new.html ├── config └── default.json ├── docker-compose.yml ├── k8s ├── postgres.yml └── url-mapper-rs.yml ├── migrations └── 20210615064800_create_url_maps.sql └── src ├── config.rs ├── db ├── db.rs ├── manager.rs └── mod.rs ├── macros.rs ├── main.rs └── server ├── mod.rs ├── routes ├── admin │ ├── mod.rs │ └── url_maps │ │ ├── handlers.rs │ │ └── mod.rs ├── api │ ├── mod.rs │ └── url_maps │ │ ├── handlers.rs │ │ └── mod.rs └── mod.rs ├── server.rs └── state.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | 3 | config/*.json 4 | !config/default.json 5 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "ahash" 5 | version = "0.7.4" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "43bb833f0bf979d8475d38fbf09ed3b8a55e1885fe93ad3f93239fc6a4f17b98" 8 | dependencies = [ 9 | "getrandom", 10 | "once_cell", 11 | "version_check", 12 | ] 13 | 14 | [[package]] 15 | name = "aho-corasick" 16 | version = "0.7.18" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 19 | dependencies = [ 20 | "memchr", 21 | ] 22 | 23 | [[package]] 24 | name = "ansi_term" 25 | version = "0.12.1" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" 28 | dependencies = [ 29 | "winapi", 30 | ] 31 | 32 | [[package]] 33 | name = "anyhow" 34 | version = "1.0.41" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "15af2628f6890fe2609a3b91bef4c83450512802e59489f9c1cb1fa5df064a61" 37 | 38 | [[package]] 39 | name = "arrayvec" 40 | version = "0.5.2" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" 43 | 44 | [[package]] 45 | name = "atoi" 46 | version = "0.4.0" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "616896e05fc0e2649463a93a15183c6a16bf03413a7af88ef1285ddedfa9cda5" 49 | dependencies = [ 50 | "num-traits 0.2.14", 51 | ] 52 | 53 | [[package]] 54 | name = "autocfg" 55 | version = "1.0.1" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 58 | 59 | [[package]] 60 | name = "base64" 61 | version = "0.13.0" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 64 | 65 | [[package]] 66 | name = "bitflags" 67 | version = "1.2.1" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 70 | 71 | [[package]] 72 | name = "bitvec" 73 | version = "0.19.5" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321" 76 | dependencies = [ 77 | "funty", 78 | "radium", 79 | "tap", 80 | "wyz", 81 | ] 82 | 83 | [[package]] 84 | name = "block-buffer" 85 | version = "0.7.3" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" 88 | dependencies = [ 89 | "block-padding", 90 | "byte-tools", 91 | "byteorder", 92 | "generic-array 0.12.4", 93 | ] 94 | 95 | [[package]] 96 | name = "block-buffer" 97 | version = "0.9.0" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" 100 | dependencies = [ 101 | "generic-array 0.14.4", 102 | ] 103 | 104 | [[package]] 105 | name = "block-padding" 106 | version = "0.1.5" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" 109 | dependencies = [ 110 | "byte-tools", 111 | ] 112 | 113 | [[package]] 114 | name = "bstr" 115 | version = "0.2.16" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "90682c8d613ad3373e66de8c6411e0ae2ab2571e879d2efbf73558cc66f21279" 118 | dependencies = [ 119 | "memchr", 120 | ] 121 | 122 | [[package]] 123 | name = "build_const" 124 | version = "0.2.2" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "b4ae4235e6dac0694637c763029ecea1a2ec9e4e06ec2729bd21ba4d9c863eb7" 127 | 128 | [[package]] 129 | name = "bumpalo" 130 | version = "3.7.0" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" 133 | 134 | [[package]] 135 | name = "byte-tools" 136 | version = "0.3.1" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" 139 | 140 | [[package]] 141 | name = "byteorder" 142 | version = "1.4.3" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 145 | 146 | [[package]] 147 | name = "bytes" 148 | version = "1.0.1" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" 151 | 152 | [[package]] 153 | name = "cc" 154 | version = "1.0.68" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787" 157 | 158 | [[package]] 159 | name = "cfg-if" 160 | version = "1.0.0" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 163 | 164 | [[package]] 165 | name = "chrono" 166 | version = "0.4.19" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" 169 | dependencies = [ 170 | "libc", 171 | "num-integer", 172 | "num-traits 0.2.14", 173 | "winapi", 174 | ] 175 | 176 | [[package]] 177 | name = "chrono-tz" 178 | version = "0.5.3" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "2554a3155fec064362507487171dcc4edc3df60cb10f3a1fb10ed8094822b120" 181 | dependencies = [ 182 | "chrono", 183 | "parse-zoneinfo", 184 | ] 185 | 186 | [[package]] 187 | name = "config" 188 | version = "0.11.0" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "1b1b9d958c2b1368a663f05538fc1b5975adce1e19f435acceae987aceeeb369" 191 | dependencies = [ 192 | "lazy_static", 193 | "nom 5.1.2", 194 | "rust-ini", 195 | "serde 1.0.126", 196 | "serde-hjson", 197 | "serde_json", 198 | "toml", 199 | "yaml-rust", 200 | ] 201 | 202 | [[package]] 203 | name = "cpufeatures" 204 | version = "0.1.4" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "ed00c67cb5d0a7d64a44f6ad2668db7e7530311dd53ea79bcd4fb022c64911c8" 207 | dependencies = [ 208 | "libc", 209 | ] 210 | 211 | [[package]] 212 | name = "crc" 213 | version = "1.8.1" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" 216 | dependencies = [ 217 | "build_const", 218 | ] 219 | 220 | [[package]] 221 | name = "crossbeam-channel" 222 | version = "0.5.1" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" 225 | dependencies = [ 226 | "cfg-if", 227 | "crossbeam-utils", 228 | ] 229 | 230 | [[package]] 231 | name = "crossbeam-queue" 232 | version = "0.3.2" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9" 235 | dependencies = [ 236 | "cfg-if", 237 | "crossbeam-utils", 238 | ] 239 | 240 | [[package]] 241 | name = "crossbeam-utils" 242 | version = "0.8.5" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" 245 | dependencies = [ 246 | "cfg-if", 247 | "lazy_static", 248 | ] 249 | 250 | [[package]] 251 | name = "crypto-mac" 252 | version = "0.10.0" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | checksum = "4857fd85a0c34b3c3297875b747c1e02e06b6a0ea32dd892d8192b9ce0813ea6" 255 | dependencies = [ 256 | "generic-array 0.14.4", 257 | "subtle", 258 | ] 259 | 260 | [[package]] 261 | name = "deunicode" 262 | version = "0.4.3" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "850878694b7933ca4c9569d30a34b55031b9b139ee1fc7b94a527c4ef960d690" 265 | 266 | [[package]] 267 | name = "digest" 268 | version = "0.8.1" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" 271 | dependencies = [ 272 | "generic-array 0.12.4", 273 | ] 274 | 275 | [[package]] 276 | name = "digest" 277 | version = "0.9.0" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" 280 | dependencies = [ 281 | "generic-array 0.14.4", 282 | ] 283 | 284 | [[package]] 285 | name = "dirs" 286 | version = "3.0.2" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" 289 | dependencies = [ 290 | "dirs-sys", 291 | ] 292 | 293 | [[package]] 294 | name = "dirs-sys" 295 | version = "0.3.6" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" 298 | dependencies = [ 299 | "libc", 300 | "redox_users", 301 | "winapi", 302 | ] 303 | 304 | [[package]] 305 | name = "dotenv" 306 | version = "0.15.0" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" 309 | 310 | [[package]] 311 | name = "either" 312 | version = "1.6.1" 313 | source = "registry+https://github.com/rust-lang/crates.io-index" 314 | checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" 315 | 316 | [[package]] 317 | name = "fake-simd" 318 | version = "0.1.2" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" 321 | 322 | [[package]] 323 | name = "fnv" 324 | version = "1.0.7" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 327 | 328 | [[package]] 329 | name = "form_urlencoded" 330 | version = "1.0.1" 331 | source = "registry+https://github.com/rust-lang/crates.io-index" 332 | checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" 333 | dependencies = [ 334 | "matches", 335 | "percent-encoding", 336 | ] 337 | 338 | [[package]] 339 | name = "funty" 340 | version = "1.1.0" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" 343 | 344 | [[package]] 345 | name = "futures" 346 | version = "0.3.15" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "0e7e43a803dae2fa37c1f6a8fe121e1f7bf9548b4dfc0522a42f34145dadfc27" 349 | dependencies = [ 350 | "futures-channel", 351 | "futures-core", 352 | "futures-executor", 353 | "futures-io", 354 | "futures-sink", 355 | "futures-task", 356 | "futures-util", 357 | ] 358 | 359 | [[package]] 360 | name = "futures-channel" 361 | version = "0.3.15" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "e682a68b29a882df0545c143dc3646daefe80ba479bcdede94d5a703de2871e2" 364 | dependencies = [ 365 | "futures-core", 366 | "futures-sink", 367 | ] 368 | 369 | [[package]] 370 | name = "futures-core" 371 | version = "0.3.15" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "0402f765d8a89a26043b889b26ce3c4679d268fa6bb22cd7c6aad98340e179d1" 374 | 375 | [[package]] 376 | name = "futures-executor" 377 | version = "0.3.15" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "badaa6a909fac9e7236d0620a2f57f7664640c56575b71a7552fbd68deafab79" 380 | dependencies = [ 381 | "futures-core", 382 | "futures-task", 383 | "futures-util", 384 | ] 385 | 386 | [[package]] 387 | name = "futures-io" 388 | version = "0.3.15" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "acc499defb3b348f8d8f3f66415835a9131856ff7714bf10dadfc4ec4bdb29a1" 391 | 392 | [[package]] 393 | name = "futures-macro" 394 | version = "0.3.15" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "a4c40298486cdf52cc00cd6d6987892ba502c7656a16a4192a9992b1ccedd121" 397 | dependencies = [ 398 | "autocfg", 399 | "proc-macro-hack", 400 | "proc-macro2", 401 | "quote", 402 | "syn", 403 | ] 404 | 405 | [[package]] 406 | name = "futures-sink" 407 | version = "0.3.15" 408 | source = "registry+https://github.com/rust-lang/crates.io-index" 409 | checksum = "a57bead0ceff0d6dde8f465ecd96c9338121bb7717d3e7b108059531870c4282" 410 | 411 | [[package]] 412 | name = "futures-task" 413 | version = "0.3.15" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | checksum = "8a16bef9fc1a4dddb5bee51c989e3fbba26569cbb0e31f5b303c184e3dd33dae" 416 | 417 | [[package]] 418 | name = "futures-util" 419 | version = "0.3.15" 420 | source = "registry+https://github.com/rust-lang/crates.io-index" 421 | checksum = "feb5c238d27e2bf94ffdfd27b2c29e3df4a68c4193bb6427384259e2bf191967" 422 | dependencies = [ 423 | "autocfg", 424 | "futures-channel", 425 | "futures-core", 426 | "futures-io", 427 | "futures-macro", 428 | "futures-sink", 429 | "futures-task", 430 | "memchr", 431 | "pin-project-lite", 432 | "pin-utils", 433 | "proc-macro-hack", 434 | "proc-macro-nested", 435 | "slab", 436 | ] 437 | 438 | [[package]] 439 | name = "generic-array" 440 | version = "0.12.4" 441 | source = "registry+https://github.com/rust-lang/crates.io-index" 442 | checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" 443 | dependencies = [ 444 | "typenum", 445 | ] 446 | 447 | [[package]] 448 | name = "generic-array" 449 | version = "0.14.4" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" 452 | dependencies = [ 453 | "typenum", 454 | "version_check", 455 | ] 456 | 457 | [[package]] 458 | name = "getrandom" 459 | version = "0.2.3" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" 462 | dependencies = [ 463 | "cfg-if", 464 | "libc", 465 | "wasi", 466 | ] 467 | 468 | [[package]] 469 | name = "globset" 470 | version = "0.4.8" 471 | source = "registry+https://github.com/rust-lang/crates.io-index" 472 | checksum = "10463d9ff00a2a068db14231982f5132edebad0d7660cd956a1c30292dbcbfbd" 473 | dependencies = [ 474 | "aho-corasick", 475 | "bstr", 476 | "fnv", 477 | "log", 478 | "regex", 479 | ] 480 | 481 | [[package]] 482 | name = "globwalk" 483 | version = "0.8.1" 484 | source = "registry+https://github.com/rust-lang/crates.io-index" 485 | checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" 486 | dependencies = [ 487 | "bitflags", 488 | "ignore", 489 | "walkdir", 490 | ] 491 | 492 | [[package]] 493 | name = "h2" 494 | version = "0.3.3" 495 | source = "registry+https://github.com/rust-lang/crates.io-index" 496 | checksum = "825343c4eef0b63f541f8903f395dc5beb362a979b5799a84062527ef1e37726" 497 | dependencies = [ 498 | "bytes", 499 | "fnv", 500 | "futures-core", 501 | "futures-sink", 502 | "futures-util", 503 | "http", 504 | "indexmap", 505 | "slab", 506 | "tokio", 507 | "tokio-util", 508 | "tracing", 509 | ] 510 | 511 | [[package]] 512 | name = "hashbrown" 513 | version = "0.9.1" 514 | source = "registry+https://github.com/rust-lang/crates.io-index" 515 | checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" 516 | 517 | [[package]] 518 | name = "hashbrown" 519 | version = "0.11.2" 520 | source = "registry+https://github.com/rust-lang/crates.io-index" 521 | checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" 522 | dependencies = [ 523 | "ahash", 524 | ] 525 | 526 | [[package]] 527 | name = "hashlink" 528 | version = "0.7.0" 529 | source = "registry+https://github.com/rust-lang/crates.io-index" 530 | checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf" 531 | dependencies = [ 532 | "hashbrown 0.11.2", 533 | ] 534 | 535 | [[package]] 536 | name = "heck" 537 | version = "0.3.3" 538 | source = "registry+https://github.com/rust-lang/crates.io-index" 539 | checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" 540 | dependencies = [ 541 | "unicode-segmentation", 542 | ] 543 | 544 | [[package]] 545 | name = "hermit-abi" 546 | version = "0.1.18" 547 | source = "registry+https://github.com/rust-lang/crates.io-index" 548 | checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" 549 | dependencies = [ 550 | "libc", 551 | ] 552 | 553 | [[package]] 554 | name = "hex" 555 | version = "0.4.3" 556 | source = "registry+https://github.com/rust-lang/crates.io-index" 557 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 558 | 559 | [[package]] 560 | name = "hmac" 561 | version = "0.10.1" 562 | source = "registry+https://github.com/rust-lang/crates.io-index" 563 | checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" 564 | dependencies = [ 565 | "crypto-mac", 566 | "digest 0.9.0", 567 | ] 568 | 569 | [[package]] 570 | name = "http" 571 | version = "0.2.4" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11" 574 | dependencies = [ 575 | "bytes", 576 | "fnv", 577 | "itoa", 578 | ] 579 | 580 | [[package]] 581 | name = "http-body" 582 | version = "0.4.2" 583 | source = "registry+https://github.com/rust-lang/crates.io-index" 584 | checksum = "60daa14be0e0786db0f03a9e57cb404c9d756eed2b6c62b9ea98ec5743ec75a9" 585 | dependencies = [ 586 | "bytes", 587 | "http", 588 | "pin-project-lite", 589 | ] 590 | 591 | [[package]] 592 | name = "httparse" 593 | version = "1.4.1" 594 | source = "registry+https://github.com/rust-lang/crates.io-index" 595 | checksum = "f3a87b616e37e93c22fb19bcd386f02f3af5ea98a25670ad0fce773de23c5e68" 596 | 597 | [[package]] 598 | name = "httpdate" 599 | version = "1.0.1" 600 | source = "registry+https://github.com/rust-lang/crates.io-index" 601 | checksum = "6456b8a6c8f33fee7d958fcd1b60d55b11940a79e63ae87013e6d22e26034440" 602 | 603 | [[package]] 604 | name = "humansize" 605 | version = "1.1.1" 606 | source = "registry+https://github.com/rust-lang/crates.io-index" 607 | checksum = "02296996cb8796d7c6e3bc2d9211b7802812d36999a51bb754123ead7d37d026" 608 | 609 | [[package]] 610 | name = "hyper" 611 | version = "0.14.9" 612 | source = "registry+https://github.com/rust-lang/crates.io-index" 613 | checksum = "07d6baa1b441335f3ce5098ac421fb6547c46dda735ca1bc6d0153c838f9dd83" 614 | dependencies = [ 615 | "bytes", 616 | "futures-channel", 617 | "futures-core", 618 | "futures-util", 619 | "h2", 620 | "http", 621 | "http-body", 622 | "httparse", 623 | "httpdate", 624 | "itoa", 625 | "pin-project-lite", 626 | "socket2", 627 | "tokio", 628 | "tower-service", 629 | "tracing", 630 | "want", 631 | ] 632 | 633 | [[package]] 634 | name = "idna" 635 | version = "0.2.3" 636 | source = "registry+https://github.com/rust-lang/crates.io-index" 637 | checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" 638 | dependencies = [ 639 | "matches", 640 | "unicode-bidi", 641 | "unicode-normalization", 642 | ] 643 | 644 | [[package]] 645 | name = "ignore" 646 | version = "0.4.18" 647 | source = "registry+https://github.com/rust-lang/crates.io-index" 648 | checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d" 649 | dependencies = [ 650 | "crossbeam-utils", 651 | "globset", 652 | "lazy_static", 653 | "log", 654 | "memchr", 655 | "regex", 656 | "same-file", 657 | "thread_local", 658 | "walkdir", 659 | "winapi-util", 660 | ] 661 | 662 | [[package]] 663 | name = "indexmap" 664 | version = "1.6.2" 665 | source = "registry+https://github.com/rust-lang/crates.io-index" 666 | checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" 667 | dependencies = [ 668 | "autocfg", 669 | "hashbrown 0.9.1", 670 | ] 671 | 672 | [[package]] 673 | name = "instant" 674 | version = "0.1.9" 675 | source = "registry+https://github.com/rust-lang/crates.io-index" 676 | checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" 677 | dependencies = [ 678 | "cfg-if", 679 | ] 680 | 681 | [[package]] 682 | name = "itoa" 683 | version = "0.4.7" 684 | source = "registry+https://github.com/rust-lang/crates.io-index" 685 | checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" 686 | 687 | [[package]] 688 | name = "js-sys" 689 | version = "0.3.51" 690 | source = "registry+https://github.com/rust-lang/crates.io-index" 691 | checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062" 692 | dependencies = [ 693 | "wasm-bindgen", 694 | ] 695 | 696 | [[package]] 697 | name = "lazy_static" 698 | version = "1.4.0" 699 | source = "registry+https://github.com/rust-lang/crates.io-index" 700 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 701 | 702 | [[package]] 703 | name = "lexical-core" 704 | version = "0.7.6" 705 | source = "registry+https://github.com/rust-lang/crates.io-index" 706 | checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" 707 | dependencies = [ 708 | "arrayvec", 709 | "bitflags", 710 | "cfg-if", 711 | "ryu", 712 | "static_assertions", 713 | ] 714 | 715 | [[package]] 716 | name = "libc" 717 | version = "0.2.97" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6" 720 | 721 | [[package]] 722 | name = "linked-hash-map" 723 | version = "0.5.4" 724 | source = "registry+https://github.com/rust-lang/crates.io-index" 725 | checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" 726 | 727 | [[package]] 728 | name = "lock_api" 729 | version = "0.4.4" 730 | source = "registry+https://github.com/rust-lang/crates.io-index" 731 | checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" 732 | dependencies = [ 733 | "scopeguard", 734 | ] 735 | 736 | [[package]] 737 | name = "log" 738 | version = "0.4.14" 739 | source = "registry+https://github.com/rust-lang/crates.io-index" 740 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 741 | dependencies = [ 742 | "cfg-if", 743 | ] 744 | 745 | [[package]] 746 | name = "maplit" 747 | version = "1.0.2" 748 | source = "registry+https://github.com/rust-lang/crates.io-index" 749 | checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" 750 | 751 | [[package]] 752 | name = "matchers" 753 | version = "0.0.1" 754 | source = "registry+https://github.com/rust-lang/crates.io-index" 755 | checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" 756 | dependencies = [ 757 | "regex-automata", 758 | ] 759 | 760 | [[package]] 761 | name = "matches" 762 | version = "0.1.8" 763 | source = "registry+https://github.com/rust-lang/crates.io-index" 764 | checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" 765 | 766 | [[package]] 767 | name = "md-5" 768 | version = "0.9.1" 769 | source = "registry+https://github.com/rust-lang/crates.io-index" 770 | checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" 771 | dependencies = [ 772 | "block-buffer 0.9.0", 773 | "digest 0.9.0", 774 | "opaque-debug 0.3.0", 775 | ] 776 | 777 | [[package]] 778 | name = "memchr" 779 | version = "2.4.0" 780 | source = "registry+https://github.com/rust-lang/crates.io-index" 781 | checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" 782 | 783 | [[package]] 784 | name = "mio" 785 | version = "0.7.13" 786 | source = "registry+https://github.com/rust-lang/crates.io-index" 787 | checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16" 788 | dependencies = [ 789 | "libc", 790 | "log", 791 | "miow", 792 | "ntapi", 793 | "winapi", 794 | ] 795 | 796 | [[package]] 797 | name = "miow" 798 | version = "0.3.7" 799 | source = "registry+https://github.com/rust-lang/crates.io-index" 800 | checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" 801 | dependencies = [ 802 | "winapi", 803 | ] 804 | 805 | [[package]] 806 | name = "nom" 807 | version = "5.1.2" 808 | source = "registry+https://github.com/rust-lang/crates.io-index" 809 | checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" 810 | dependencies = [ 811 | "lexical-core", 812 | "memchr", 813 | "version_check", 814 | ] 815 | 816 | [[package]] 817 | name = "nom" 818 | version = "6.1.2" 819 | source = "registry+https://github.com/rust-lang/crates.io-index" 820 | checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2" 821 | dependencies = [ 822 | "bitvec", 823 | "funty", 824 | "lexical-core", 825 | "memchr", 826 | "version_check", 827 | ] 828 | 829 | [[package]] 830 | name = "ntapi" 831 | version = "0.3.6" 832 | source = "registry+https://github.com/rust-lang/crates.io-index" 833 | checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" 834 | dependencies = [ 835 | "winapi", 836 | ] 837 | 838 | [[package]] 839 | name = "num-integer" 840 | version = "0.1.44" 841 | source = "registry+https://github.com/rust-lang/crates.io-index" 842 | checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" 843 | dependencies = [ 844 | "autocfg", 845 | "num-traits 0.2.14", 846 | ] 847 | 848 | [[package]] 849 | name = "num-traits" 850 | version = "0.1.43" 851 | source = "registry+https://github.com/rust-lang/crates.io-index" 852 | checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" 853 | dependencies = [ 854 | "num-traits 0.2.14", 855 | ] 856 | 857 | [[package]] 858 | name = "num-traits" 859 | version = "0.2.14" 860 | source = "registry+https://github.com/rust-lang/crates.io-index" 861 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 862 | dependencies = [ 863 | "autocfg", 864 | ] 865 | 866 | [[package]] 867 | name = "num_cpus" 868 | version = "1.13.0" 869 | source = "registry+https://github.com/rust-lang/crates.io-index" 870 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 871 | dependencies = [ 872 | "hermit-abi", 873 | "libc", 874 | ] 875 | 876 | [[package]] 877 | name = "once_cell" 878 | version = "1.8.0" 879 | source = "registry+https://github.com/rust-lang/crates.io-index" 880 | checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" 881 | 882 | [[package]] 883 | name = "opaque-debug" 884 | version = "0.2.3" 885 | source = "registry+https://github.com/rust-lang/crates.io-index" 886 | checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" 887 | 888 | [[package]] 889 | name = "opaque-debug" 890 | version = "0.3.0" 891 | source = "registry+https://github.com/rust-lang/crates.io-index" 892 | checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" 893 | 894 | [[package]] 895 | name = "parking_lot" 896 | version = "0.11.1" 897 | source = "registry+https://github.com/rust-lang/crates.io-index" 898 | checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" 899 | dependencies = [ 900 | "instant", 901 | "lock_api", 902 | "parking_lot_core", 903 | ] 904 | 905 | [[package]] 906 | name = "parking_lot_core" 907 | version = "0.8.3" 908 | source = "registry+https://github.com/rust-lang/crates.io-index" 909 | checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" 910 | dependencies = [ 911 | "cfg-if", 912 | "instant", 913 | "libc", 914 | "redox_syscall", 915 | "smallvec", 916 | "winapi", 917 | ] 918 | 919 | [[package]] 920 | name = "parse-zoneinfo" 921 | version = "0.3.0" 922 | source = "registry+https://github.com/rust-lang/crates.io-index" 923 | checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41" 924 | dependencies = [ 925 | "regex", 926 | ] 927 | 928 | [[package]] 929 | name = "percent-encoding" 930 | version = "2.1.0" 931 | source = "registry+https://github.com/rust-lang/crates.io-index" 932 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 933 | 934 | [[package]] 935 | name = "pest" 936 | version = "2.1.3" 937 | source = "registry+https://github.com/rust-lang/crates.io-index" 938 | checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" 939 | dependencies = [ 940 | "ucd-trie", 941 | ] 942 | 943 | [[package]] 944 | name = "pest_derive" 945 | version = "2.1.0" 946 | source = "registry+https://github.com/rust-lang/crates.io-index" 947 | checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" 948 | dependencies = [ 949 | "pest", 950 | "pest_generator", 951 | ] 952 | 953 | [[package]] 954 | name = "pest_generator" 955 | version = "2.1.3" 956 | source = "registry+https://github.com/rust-lang/crates.io-index" 957 | checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" 958 | dependencies = [ 959 | "pest", 960 | "pest_meta", 961 | "proc-macro2", 962 | "quote", 963 | "syn", 964 | ] 965 | 966 | [[package]] 967 | name = "pest_meta" 968 | version = "2.1.3" 969 | source = "registry+https://github.com/rust-lang/crates.io-index" 970 | checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" 971 | dependencies = [ 972 | "maplit", 973 | "pest", 974 | "sha-1 0.8.2", 975 | ] 976 | 977 | [[package]] 978 | name = "pin-project-lite" 979 | version = "0.2.6" 980 | source = "registry+https://github.com/rust-lang/crates.io-index" 981 | checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" 982 | 983 | [[package]] 984 | name = "pin-utils" 985 | version = "0.1.0" 986 | source = "registry+https://github.com/rust-lang/crates.io-index" 987 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 988 | 989 | [[package]] 990 | name = "ppv-lite86" 991 | version = "0.2.10" 992 | source = "registry+https://github.com/rust-lang/crates.io-index" 993 | checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" 994 | 995 | [[package]] 996 | name = "proc-macro-hack" 997 | version = "0.5.19" 998 | source = "registry+https://github.com/rust-lang/crates.io-index" 999 | checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" 1000 | 1001 | [[package]] 1002 | name = "proc-macro-nested" 1003 | version = "0.1.7" 1004 | source = "registry+https://github.com/rust-lang/crates.io-index" 1005 | checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" 1006 | 1007 | [[package]] 1008 | name = "proc-macro2" 1009 | version = "1.0.27" 1010 | source = "registry+https://github.com/rust-lang/crates.io-index" 1011 | checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" 1012 | dependencies = [ 1013 | "unicode-xid", 1014 | ] 1015 | 1016 | [[package]] 1017 | name = "quote" 1018 | version = "1.0.9" 1019 | source = "registry+https://github.com/rust-lang/crates.io-index" 1020 | checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" 1021 | dependencies = [ 1022 | "proc-macro2", 1023 | ] 1024 | 1025 | [[package]] 1026 | name = "radium" 1027 | version = "0.5.3" 1028 | source = "registry+https://github.com/rust-lang/crates.io-index" 1029 | checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8" 1030 | 1031 | [[package]] 1032 | name = "rand" 1033 | version = "0.8.3" 1034 | source = "registry+https://github.com/rust-lang/crates.io-index" 1035 | checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" 1036 | dependencies = [ 1037 | "libc", 1038 | "rand_chacha", 1039 | "rand_core", 1040 | "rand_hc", 1041 | ] 1042 | 1043 | [[package]] 1044 | name = "rand_chacha" 1045 | version = "0.3.1" 1046 | source = "registry+https://github.com/rust-lang/crates.io-index" 1047 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1048 | dependencies = [ 1049 | "ppv-lite86", 1050 | "rand_core", 1051 | ] 1052 | 1053 | [[package]] 1054 | name = "rand_core" 1055 | version = "0.6.2" 1056 | source = "registry+https://github.com/rust-lang/crates.io-index" 1057 | checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" 1058 | dependencies = [ 1059 | "getrandom", 1060 | ] 1061 | 1062 | [[package]] 1063 | name = "rand_hc" 1064 | version = "0.3.0" 1065 | source = "registry+https://github.com/rust-lang/crates.io-index" 1066 | checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" 1067 | dependencies = [ 1068 | "rand_core", 1069 | ] 1070 | 1071 | [[package]] 1072 | name = "redox_syscall" 1073 | version = "0.2.8" 1074 | source = "registry+https://github.com/rust-lang/crates.io-index" 1075 | checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc" 1076 | dependencies = [ 1077 | "bitflags", 1078 | ] 1079 | 1080 | [[package]] 1081 | name = "redox_users" 1082 | version = "0.4.0" 1083 | source = "registry+https://github.com/rust-lang/crates.io-index" 1084 | checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" 1085 | dependencies = [ 1086 | "getrandom", 1087 | "redox_syscall", 1088 | ] 1089 | 1090 | [[package]] 1091 | name = "regex" 1092 | version = "1.5.4" 1093 | source = "registry+https://github.com/rust-lang/crates.io-index" 1094 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" 1095 | dependencies = [ 1096 | "aho-corasick", 1097 | "memchr", 1098 | "regex-syntax", 1099 | ] 1100 | 1101 | [[package]] 1102 | name = "regex-automata" 1103 | version = "0.1.10" 1104 | source = "registry+https://github.com/rust-lang/crates.io-index" 1105 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 1106 | dependencies = [ 1107 | "regex-syntax", 1108 | ] 1109 | 1110 | [[package]] 1111 | name = "regex-syntax" 1112 | version = "0.6.25" 1113 | source = "registry+https://github.com/rust-lang/crates.io-index" 1114 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 1115 | 1116 | [[package]] 1117 | name = "ring" 1118 | version = "0.16.20" 1119 | source = "registry+https://github.com/rust-lang/crates.io-index" 1120 | checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" 1121 | dependencies = [ 1122 | "cc", 1123 | "libc", 1124 | "once_cell", 1125 | "spin", 1126 | "untrusted", 1127 | "web-sys", 1128 | "winapi", 1129 | ] 1130 | 1131 | [[package]] 1132 | name = "routerify" 1133 | version = "2.1.0" 1134 | source = "registry+https://github.com/rust-lang/crates.io-index" 1135 | checksum = "4e33e46aa5a1e4d60c193d6f734534fa0b3f8d74ae3b579fe7e1c3242ac23e94" 1136 | dependencies = [ 1137 | "http", 1138 | "hyper", 1139 | "lazy_static", 1140 | "percent-encoding", 1141 | "regex", 1142 | ] 1143 | 1144 | [[package]] 1145 | name = "rust-ini" 1146 | version = "0.13.0" 1147 | source = "registry+https://github.com/rust-lang/crates.io-index" 1148 | checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" 1149 | 1150 | [[package]] 1151 | name = "rustls" 1152 | version = "0.19.1" 1153 | source = "registry+https://github.com/rust-lang/crates.io-index" 1154 | checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" 1155 | dependencies = [ 1156 | "base64", 1157 | "log", 1158 | "ring", 1159 | "sct", 1160 | "webpki", 1161 | ] 1162 | 1163 | [[package]] 1164 | name = "ryu" 1165 | version = "1.0.5" 1166 | source = "registry+https://github.com/rust-lang/crates.io-index" 1167 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 1168 | 1169 | [[package]] 1170 | name = "same-file" 1171 | version = "1.0.6" 1172 | source = "registry+https://github.com/rust-lang/crates.io-index" 1173 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 1174 | dependencies = [ 1175 | "winapi-util", 1176 | ] 1177 | 1178 | [[package]] 1179 | name = "scopeguard" 1180 | version = "1.1.0" 1181 | source = "registry+https://github.com/rust-lang/crates.io-index" 1182 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 1183 | 1184 | [[package]] 1185 | name = "sct" 1186 | version = "0.6.1" 1187 | source = "registry+https://github.com/rust-lang/crates.io-index" 1188 | checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" 1189 | dependencies = [ 1190 | "ring", 1191 | "untrusted", 1192 | ] 1193 | 1194 | [[package]] 1195 | name = "serde" 1196 | version = "0.8.23" 1197 | source = "registry+https://github.com/rust-lang/crates.io-index" 1198 | checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" 1199 | 1200 | [[package]] 1201 | name = "serde" 1202 | version = "1.0.126" 1203 | source = "registry+https://github.com/rust-lang/crates.io-index" 1204 | checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" 1205 | dependencies = [ 1206 | "serde_derive", 1207 | ] 1208 | 1209 | [[package]] 1210 | name = "serde-hjson" 1211 | version = "0.9.1" 1212 | source = "registry+https://github.com/rust-lang/crates.io-index" 1213 | checksum = "6a3a4e0ea8a88553209f6cc6cfe8724ecad22e1acf372793c27d995290fe74f8" 1214 | dependencies = [ 1215 | "lazy_static", 1216 | "num-traits 0.1.43", 1217 | "regex", 1218 | "serde 0.8.23", 1219 | ] 1220 | 1221 | [[package]] 1222 | name = "serde_derive" 1223 | version = "1.0.126" 1224 | source = "registry+https://github.com/rust-lang/crates.io-index" 1225 | checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" 1226 | dependencies = [ 1227 | "proc-macro2", 1228 | "quote", 1229 | "syn", 1230 | ] 1231 | 1232 | [[package]] 1233 | name = "serde_json" 1234 | version = "1.0.64" 1235 | source = "registry+https://github.com/rust-lang/crates.io-index" 1236 | checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" 1237 | dependencies = [ 1238 | "itoa", 1239 | "ryu", 1240 | "serde 1.0.126", 1241 | ] 1242 | 1243 | [[package]] 1244 | name = "sha-1" 1245 | version = "0.8.2" 1246 | source = "registry+https://github.com/rust-lang/crates.io-index" 1247 | checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" 1248 | dependencies = [ 1249 | "block-buffer 0.7.3", 1250 | "digest 0.8.1", 1251 | "fake-simd", 1252 | "opaque-debug 0.2.3", 1253 | ] 1254 | 1255 | [[package]] 1256 | name = "sha-1" 1257 | version = "0.9.6" 1258 | source = "registry+https://github.com/rust-lang/crates.io-index" 1259 | checksum = "8c4cfa741c5832d0ef7fab46cabed29c2aae926db0b11bb2069edd8db5e64e16" 1260 | dependencies = [ 1261 | "block-buffer 0.9.0", 1262 | "cfg-if", 1263 | "cpufeatures", 1264 | "digest 0.9.0", 1265 | "opaque-debug 0.3.0", 1266 | ] 1267 | 1268 | [[package]] 1269 | name = "sha2" 1270 | version = "0.9.5" 1271 | source = "registry+https://github.com/rust-lang/crates.io-index" 1272 | checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12" 1273 | dependencies = [ 1274 | "block-buffer 0.9.0", 1275 | "cfg-if", 1276 | "cpufeatures", 1277 | "digest 0.9.0", 1278 | "opaque-debug 0.3.0", 1279 | ] 1280 | 1281 | [[package]] 1282 | name = "sharded-slab" 1283 | version = "0.1.1" 1284 | source = "registry+https://github.com/rust-lang/crates.io-index" 1285 | checksum = "79c719719ee05df97490f80a45acfc99e5a30ce98a1e4fb67aee422745ae14e3" 1286 | dependencies = [ 1287 | "lazy_static", 1288 | ] 1289 | 1290 | [[package]] 1291 | name = "signal-hook-registry" 1292 | version = "1.4.0" 1293 | source = "registry+https://github.com/rust-lang/crates.io-index" 1294 | checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" 1295 | dependencies = [ 1296 | "libc", 1297 | ] 1298 | 1299 | [[package]] 1300 | name = "slab" 1301 | version = "0.4.3" 1302 | source = "registry+https://github.com/rust-lang/crates.io-index" 1303 | checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" 1304 | 1305 | [[package]] 1306 | name = "slug" 1307 | version = "0.1.4" 1308 | source = "registry+https://github.com/rust-lang/crates.io-index" 1309 | checksum = "b3bc762e6a4b6c6fcaade73e77f9ebc6991b676f88bb2358bddb56560f073373" 1310 | dependencies = [ 1311 | "deunicode", 1312 | ] 1313 | 1314 | [[package]] 1315 | name = "smallvec" 1316 | version = "1.6.1" 1317 | source = "registry+https://github.com/rust-lang/crates.io-index" 1318 | checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" 1319 | 1320 | [[package]] 1321 | name = "socket2" 1322 | version = "0.4.0" 1323 | source = "registry+https://github.com/rust-lang/crates.io-index" 1324 | checksum = "9e3dfc207c526015c632472a77be09cf1b6e46866581aecae5cc38fb4235dea2" 1325 | dependencies = [ 1326 | "libc", 1327 | "winapi", 1328 | ] 1329 | 1330 | [[package]] 1331 | name = "spin" 1332 | version = "0.5.2" 1333 | source = "registry+https://github.com/rust-lang/crates.io-index" 1334 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 1335 | 1336 | [[package]] 1337 | name = "sqlformat" 1338 | version = "0.1.6" 1339 | source = "registry+https://github.com/rust-lang/crates.io-index" 1340 | checksum = "6d86e3c77ff882a828346ba401a7ef4b8e440df804491c6064fe8295765de71c" 1341 | dependencies = [ 1342 | "lazy_static", 1343 | "maplit", 1344 | "nom 6.1.2", 1345 | "regex", 1346 | "unicode_categories", 1347 | ] 1348 | 1349 | [[package]] 1350 | name = "sqlx" 1351 | version = "0.5.5" 1352 | source = "registry+https://github.com/rust-lang/crates.io-index" 1353 | checksum = "ba82f79b31f30acebf19905bcd8b978f46891b9d0723f578447361a8910b6584" 1354 | dependencies = [ 1355 | "sqlx-core", 1356 | "sqlx-macros", 1357 | ] 1358 | 1359 | [[package]] 1360 | name = "sqlx-core" 1361 | version = "0.5.5" 1362 | source = "registry+https://github.com/rust-lang/crates.io-index" 1363 | checksum = "7f23af36748ec8ea8d49ef8499839907be41b0b1178a4e82b8cb45d29f531dc9" 1364 | dependencies = [ 1365 | "ahash", 1366 | "atoi", 1367 | "base64", 1368 | "bitflags", 1369 | "byteorder", 1370 | "bytes", 1371 | "crc", 1372 | "crossbeam-channel", 1373 | "crossbeam-queue", 1374 | "crossbeam-utils", 1375 | "dirs", 1376 | "either", 1377 | "futures-channel", 1378 | "futures-core", 1379 | "futures-util", 1380 | "hashlink", 1381 | "hex", 1382 | "hmac", 1383 | "itoa", 1384 | "libc", 1385 | "log", 1386 | "md-5", 1387 | "memchr", 1388 | "once_cell", 1389 | "parking_lot", 1390 | "percent-encoding", 1391 | "rand", 1392 | "rustls", 1393 | "serde 1.0.126", 1394 | "serde_json", 1395 | "sha-1 0.9.6", 1396 | "sha2", 1397 | "smallvec", 1398 | "sqlformat", 1399 | "sqlx-rt", 1400 | "stringprep", 1401 | "thiserror", 1402 | "tokio-stream", 1403 | "url", 1404 | "webpki", 1405 | "webpki-roots", 1406 | "whoami", 1407 | ] 1408 | 1409 | [[package]] 1410 | name = "sqlx-macros" 1411 | version = "0.5.5" 1412 | source = "registry+https://github.com/rust-lang/crates.io-index" 1413 | checksum = "47e4a2349d1ffd60a03ca0de3f116ba55d7f406e55a0d84c64a5590866d94c06" 1414 | dependencies = [ 1415 | "dotenv", 1416 | "either", 1417 | "futures", 1418 | "heck", 1419 | "once_cell", 1420 | "proc-macro2", 1421 | "quote", 1422 | "sha2", 1423 | "sqlx-core", 1424 | "sqlx-rt", 1425 | "syn", 1426 | "url", 1427 | ] 1428 | 1429 | [[package]] 1430 | name = "sqlx-rt" 1431 | version = "0.5.5" 1432 | source = "registry+https://github.com/rust-lang/crates.io-index" 1433 | checksum = "8199b421ecf3493ee9ef3e7bc90c904844cfb2ea7ea2f57347a93f52bfd3e057" 1434 | dependencies = [ 1435 | "once_cell", 1436 | "tokio", 1437 | "tokio-rustls", 1438 | ] 1439 | 1440 | [[package]] 1441 | name = "static_assertions" 1442 | version = "1.1.0" 1443 | source = "registry+https://github.com/rust-lang/crates.io-index" 1444 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 1445 | 1446 | [[package]] 1447 | name = "stringprep" 1448 | version = "0.1.2" 1449 | source = "registry+https://github.com/rust-lang/crates.io-index" 1450 | checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" 1451 | dependencies = [ 1452 | "unicode-bidi", 1453 | "unicode-normalization", 1454 | ] 1455 | 1456 | [[package]] 1457 | name = "subtle" 1458 | version = "2.4.0" 1459 | source = "registry+https://github.com/rust-lang/crates.io-index" 1460 | checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2" 1461 | 1462 | [[package]] 1463 | name = "syn" 1464 | version = "1.0.73" 1465 | source = "registry+https://github.com/rust-lang/crates.io-index" 1466 | checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" 1467 | dependencies = [ 1468 | "proc-macro2", 1469 | "quote", 1470 | "unicode-xid", 1471 | ] 1472 | 1473 | [[package]] 1474 | name = "tap" 1475 | version = "1.0.1" 1476 | source = "registry+https://github.com/rust-lang/crates.io-index" 1477 | checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" 1478 | 1479 | [[package]] 1480 | name = "tera" 1481 | version = "1.12.1" 1482 | source = "registry+https://github.com/rust-lang/crates.io-index" 1483 | checksum = "bf95b0d8a46da5fe3ea119394a6c7f1e745f9de359081641c99946e2bf55d4f2" 1484 | dependencies = [ 1485 | "chrono", 1486 | "chrono-tz", 1487 | "globwalk", 1488 | "humansize", 1489 | "lazy_static", 1490 | "percent-encoding", 1491 | "pest", 1492 | "pest_derive", 1493 | "rand", 1494 | "regex", 1495 | "serde 1.0.126", 1496 | "serde_json", 1497 | "slug", 1498 | "unic-segment", 1499 | ] 1500 | 1501 | [[package]] 1502 | name = "thiserror" 1503 | version = "1.0.25" 1504 | source = "registry+https://github.com/rust-lang/crates.io-index" 1505 | checksum = "fa6f76457f59514c7eeb4e59d891395fab0b2fd1d40723ae737d64153392e9c6" 1506 | dependencies = [ 1507 | "thiserror-impl", 1508 | ] 1509 | 1510 | [[package]] 1511 | name = "thiserror-impl" 1512 | version = "1.0.25" 1513 | source = "registry+https://github.com/rust-lang/crates.io-index" 1514 | checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d" 1515 | dependencies = [ 1516 | "proc-macro2", 1517 | "quote", 1518 | "syn", 1519 | ] 1520 | 1521 | [[package]] 1522 | name = "thread_local" 1523 | version = "1.1.3" 1524 | source = "registry+https://github.com/rust-lang/crates.io-index" 1525 | checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" 1526 | dependencies = [ 1527 | "once_cell", 1528 | ] 1529 | 1530 | [[package]] 1531 | name = "tinyvec" 1532 | version = "1.2.0" 1533 | source = "registry+https://github.com/rust-lang/crates.io-index" 1534 | checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342" 1535 | dependencies = [ 1536 | "tinyvec_macros", 1537 | ] 1538 | 1539 | [[package]] 1540 | name = "tinyvec_macros" 1541 | version = "0.1.0" 1542 | source = "registry+https://github.com/rust-lang/crates.io-index" 1543 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" 1544 | 1545 | [[package]] 1546 | name = "tokio" 1547 | version = "1.6.2" 1548 | source = "registry+https://github.com/rust-lang/crates.io-index" 1549 | checksum = "aea337f72e96efe29acc234d803a5981cd9a2b6ed21655cd7fc21cfe021e8ec7" 1550 | dependencies = [ 1551 | "autocfg", 1552 | "bytes", 1553 | "libc", 1554 | "memchr", 1555 | "mio", 1556 | "num_cpus", 1557 | "once_cell", 1558 | "parking_lot", 1559 | "pin-project-lite", 1560 | "signal-hook-registry", 1561 | "tokio-macros", 1562 | "winapi", 1563 | ] 1564 | 1565 | [[package]] 1566 | name = "tokio-macros" 1567 | version = "1.2.0" 1568 | source = "registry+https://github.com/rust-lang/crates.io-index" 1569 | checksum = "c49e3df43841dafb86046472506755d8501c5615673955f6aa17181125d13c37" 1570 | dependencies = [ 1571 | "proc-macro2", 1572 | "quote", 1573 | "syn", 1574 | ] 1575 | 1576 | [[package]] 1577 | name = "tokio-rustls" 1578 | version = "0.22.0" 1579 | source = "registry+https://github.com/rust-lang/crates.io-index" 1580 | checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" 1581 | dependencies = [ 1582 | "rustls", 1583 | "tokio", 1584 | "webpki", 1585 | ] 1586 | 1587 | [[package]] 1588 | name = "tokio-stream" 1589 | version = "0.1.6" 1590 | source = "registry+https://github.com/rust-lang/crates.io-index" 1591 | checksum = "f8864d706fdb3cc0843a49647ac892720dac98a6eeb818b77190592cf4994066" 1592 | dependencies = [ 1593 | "futures-core", 1594 | "pin-project-lite", 1595 | "tokio", 1596 | ] 1597 | 1598 | [[package]] 1599 | name = "tokio-util" 1600 | version = "0.6.7" 1601 | source = "registry+https://github.com/rust-lang/crates.io-index" 1602 | checksum = "1caa0b0c8d94a049db56b5acf8cba99dc0623aab1b26d5b5f5e2d945846b3592" 1603 | dependencies = [ 1604 | "bytes", 1605 | "futures-core", 1606 | "futures-sink", 1607 | "log", 1608 | "pin-project-lite", 1609 | "tokio", 1610 | ] 1611 | 1612 | [[package]] 1613 | name = "toml" 1614 | version = "0.5.8" 1615 | source = "registry+https://github.com/rust-lang/crates.io-index" 1616 | checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" 1617 | dependencies = [ 1618 | "serde 1.0.126", 1619 | ] 1620 | 1621 | [[package]] 1622 | name = "tower-service" 1623 | version = "0.3.1" 1624 | source = "registry+https://github.com/rust-lang/crates.io-index" 1625 | checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" 1626 | 1627 | [[package]] 1628 | name = "tracing" 1629 | version = "0.1.26" 1630 | source = "registry+https://github.com/rust-lang/crates.io-index" 1631 | checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d" 1632 | dependencies = [ 1633 | "cfg-if", 1634 | "pin-project-lite", 1635 | "tracing-attributes", 1636 | "tracing-core", 1637 | ] 1638 | 1639 | [[package]] 1640 | name = "tracing-attributes" 1641 | version = "0.1.15" 1642 | source = "registry+https://github.com/rust-lang/crates.io-index" 1643 | checksum = "c42e6fa53307c8a17e4ccd4dc81cf5ec38db9209f59b222210375b54ee40d1e2" 1644 | dependencies = [ 1645 | "proc-macro2", 1646 | "quote", 1647 | "syn", 1648 | ] 1649 | 1650 | [[package]] 1651 | name = "tracing-core" 1652 | version = "0.1.18" 1653 | source = "registry+https://github.com/rust-lang/crates.io-index" 1654 | checksum = "a9ff14f98b1a4b289c6248a023c1c2fa1491062964e9fed67ab29c4e4da4a052" 1655 | dependencies = [ 1656 | "lazy_static", 1657 | ] 1658 | 1659 | [[package]] 1660 | name = "tracing-log" 1661 | version = "0.1.2" 1662 | source = "registry+https://github.com/rust-lang/crates.io-index" 1663 | checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3" 1664 | dependencies = [ 1665 | "lazy_static", 1666 | "log", 1667 | "tracing-core", 1668 | ] 1669 | 1670 | [[package]] 1671 | name = "tracing-serde" 1672 | version = "0.1.2" 1673 | source = "registry+https://github.com/rust-lang/crates.io-index" 1674 | checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b" 1675 | dependencies = [ 1676 | "serde 1.0.126", 1677 | "tracing-core", 1678 | ] 1679 | 1680 | [[package]] 1681 | name = "tracing-subscriber" 1682 | version = "0.2.18" 1683 | source = "registry+https://github.com/rust-lang/crates.io-index" 1684 | checksum = "aa5553bf0883ba7c9cbe493b085c29926bd41b66afc31ff72cf17ff4fb60dcd5" 1685 | dependencies = [ 1686 | "ansi_term", 1687 | "chrono", 1688 | "lazy_static", 1689 | "matchers", 1690 | "regex", 1691 | "serde 1.0.126", 1692 | "serde_json", 1693 | "sharded-slab", 1694 | "smallvec", 1695 | "thread_local", 1696 | "tracing", 1697 | "tracing-core", 1698 | "tracing-log", 1699 | "tracing-serde", 1700 | ] 1701 | 1702 | [[package]] 1703 | name = "try-lock" 1704 | version = "0.2.3" 1705 | source = "registry+https://github.com/rust-lang/crates.io-index" 1706 | checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" 1707 | 1708 | [[package]] 1709 | name = "typenum" 1710 | version = "1.13.0" 1711 | source = "registry+https://github.com/rust-lang/crates.io-index" 1712 | checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" 1713 | 1714 | [[package]] 1715 | name = "ucd-trie" 1716 | version = "0.1.3" 1717 | source = "registry+https://github.com/rust-lang/crates.io-index" 1718 | checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" 1719 | 1720 | [[package]] 1721 | name = "unic-char-property" 1722 | version = "0.9.0" 1723 | source = "registry+https://github.com/rust-lang/crates.io-index" 1724 | checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" 1725 | dependencies = [ 1726 | "unic-char-range", 1727 | ] 1728 | 1729 | [[package]] 1730 | name = "unic-char-range" 1731 | version = "0.9.0" 1732 | source = "registry+https://github.com/rust-lang/crates.io-index" 1733 | checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" 1734 | 1735 | [[package]] 1736 | name = "unic-common" 1737 | version = "0.9.0" 1738 | source = "registry+https://github.com/rust-lang/crates.io-index" 1739 | checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" 1740 | 1741 | [[package]] 1742 | name = "unic-segment" 1743 | version = "0.9.0" 1744 | source = "registry+https://github.com/rust-lang/crates.io-index" 1745 | checksum = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23" 1746 | dependencies = [ 1747 | "unic-ucd-segment", 1748 | ] 1749 | 1750 | [[package]] 1751 | name = "unic-ucd-segment" 1752 | version = "0.9.0" 1753 | source = "registry+https://github.com/rust-lang/crates.io-index" 1754 | checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700" 1755 | dependencies = [ 1756 | "unic-char-property", 1757 | "unic-char-range", 1758 | "unic-ucd-version", 1759 | ] 1760 | 1761 | [[package]] 1762 | name = "unic-ucd-version" 1763 | version = "0.9.0" 1764 | source = "registry+https://github.com/rust-lang/crates.io-index" 1765 | checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" 1766 | dependencies = [ 1767 | "unic-common", 1768 | ] 1769 | 1770 | [[package]] 1771 | name = "unicode-bidi" 1772 | version = "0.3.5" 1773 | source = "registry+https://github.com/rust-lang/crates.io-index" 1774 | checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0" 1775 | dependencies = [ 1776 | "matches", 1777 | ] 1778 | 1779 | [[package]] 1780 | name = "unicode-normalization" 1781 | version = "0.1.19" 1782 | source = "registry+https://github.com/rust-lang/crates.io-index" 1783 | checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" 1784 | dependencies = [ 1785 | "tinyvec", 1786 | ] 1787 | 1788 | [[package]] 1789 | name = "unicode-segmentation" 1790 | version = "1.7.1" 1791 | source = "registry+https://github.com/rust-lang/crates.io-index" 1792 | checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" 1793 | 1794 | [[package]] 1795 | name = "unicode-xid" 1796 | version = "0.2.2" 1797 | source = "registry+https://github.com/rust-lang/crates.io-index" 1798 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 1799 | 1800 | [[package]] 1801 | name = "unicode_categories" 1802 | version = "0.1.1" 1803 | source = "registry+https://github.com/rust-lang/crates.io-index" 1804 | checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" 1805 | 1806 | [[package]] 1807 | name = "untrusted" 1808 | version = "0.7.1" 1809 | source = "registry+https://github.com/rust-lang/crates.io-index" 1810 | checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" 1811 | 1812 | [[package]] 1813 | name = "url" 1814 | version = "2.2.2" 1815 | source = "registry+https://github.com/rust-lang/crates.io-index" 1816 | checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" 1817 | dependencies = [ 1818 | "form_urlencoded", 1819 | "idna", 1820 | "matches", 1821 | "percent-encoding", 1822 | ] 1823 | 1824 | [[package]] 1825 | name = "url-mapper-rs" 1826 | version = "0.1.0" 1827 | dependencies = [ 1828 | "anyhow", 1829 | "base64", 1830 | "config", 1831 | "hyper", 1832 | "lazy_static", 1833 | "routerify", 1834 | "serde 1.0.126", 1835 | "serde_json", 1836 | "sqlx", 1837 | "tera", 1838 | "tokio", 1839 | "tracing", 1840 | "tracing-subscriber", 1841 | ] 1842 | 1843 | [[package]] 1844 | name = "version_check" 1845 | version = "0.9.3" 1846 | source = "registry+https://github.com/rust-lang/crates.io-index" 1847 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" 1848 | 1849 | [[package]] 1850 | name = "walkdir" 1851 | version = "2.3.2" 1852 | source = "registry+https://github.com/rust-lang/crates.io-index" 1853 | checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" 1854 | dependencies = [ 1855 | "same-file", 1856 | "winapi", 1857 | "winapi-util", 1858 | ] 1859 | 1860 | [[package]] 1861 | name = "want" 1862 | version = "0.3.0" 1863 | source = "registry+https://github.com/rust-lang/crates.io-index" 1864 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 1865 | dependencies = [ 1866 | "log", 1867 | "try-lock", 1868 | ] 1869 | 1870 | [[package]] 1871 | name = "wasi" 1872 | version = "0.10.2+wasi-snapshot-preview1" 1873 | source = "registry+https://github.com/rust-lang/crates.io-index" 1874 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 1875 | 1876 | [[package]] 1877 | name = "wasm-bindgen" 1878 | version = "0.2.74" 1879 | source = "registry+https://github.com/rust-lang/crates.io-index" 1880 | checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd" 1881 | dependencies = [ 1882 | "cfg-if", 1883 | "wasm-bindgen-macro", 1884 | ] 1885 | 1886 | [[package]] 1887 | name = "wasm-bindgen-backend" 1888 | version = "0.2.74" 1889 | source = "registry+https://github.com/rust-lang/crates.io-index" 1890 | checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900" 1891 | dependencies = [ 1892 | "bumpalo", 1893 | "lazy_static", 1894 | "log", 1895 | "proc-macro2", 1896 | "quote", 1897 | "syn", 1898 | "wasm-bindgen-shared", 1899 | ] 1900 | 1901 | [[package]] 1902 | name = "wasm-bindgen-macro" 1903 | version = "0.2.74" 1904 | source = "registry+https://github.com/rust-lang/crates.io-index" 1905 | checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4" 1906 | dependencies = [ 1907 | "quote", 1908 | "wasm-bindgen-macro-support", 1909 | ] 1910 | 1911 | [[package]] 1912 | name = "wasm-bindgen-macro-support" 1913 | version = "0.2.74" 1914 | source = "registry+https://github.com/rust-lang/crates.io-index" 1915 | checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97" 1916 | dependencies = [ 1917 | "proc-macro2", 1918 | "quote", 1919 | "syn", 1920 | "wasm-bindgen-backend", 1921 | "wasm-bindgen-shared", 1922 | ] 1923 | 1924 | [[package]] 1925 | name = "wasm-bindgen-shared" 1926 | version = "0.2.74" 1927 | source = "registry+https://github.com/rust-lang/crates.io-index" 1928 | checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f" 1929 | 1930 | [[package]] 1931 | name = "web-sys" 1932 | version = "0.3.51" 1933 | source = "registry+https://github.com/rust-lang/crates.io-index" 1934 | checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582" 1935 | dependencies = [ 1936 | "js-sys", 1937 | "wasm-bindgen", 1938 | ] 1939 | 1940 | [[package]] 1941 | name = "webpki" 1942 | version = "0.21.4" 1943 | source = "registry+https://github.com/rust-lang/crates.io-index" 1944 | checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" 1945 | dependencies = [ 1946 | "ring", 1947 | "untrusted", 1948 | ] 1949 | 1950 | [[package]] 1951 | name = "webpki-roots" 1952 | version = "0.21.1" 1953 | source = "registry+https://github.com/rust-lang/crates.io-index" 1954 | checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" 1955 | dependencies = [ 1956 | "webpki", 1957 | ] 1958 | 1959 | [[package]] 1960 | name = "whoami" 1961 | version = "1.1.2" 1962 | source = "registry+https://github.com/rust-lang/crates.io-index" 1963 | checksum = "4abacf325c958dfeaf1046931d37f2a901b6dfe0968ee965a29e94c6766b2af6" 1964 | dependencies = [ 1965 | "wasm-bindgen", 1966 | "web-sys", 1967 | ] 1968 | 1969 | [[package]] 1970 | name = "winapi" 1971 | version = "0.3.9" 1972 | source = "registry+https://github.com/rust-lang/crates.io-index" 1973 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1974 | dependencies = [ 1975 | "winapi-i686-pc-windows-gnu", 1976 | "winapi-x86_64-pc-windows-gnu", 1977 | ] 1978 | 1979 | [[package]] 1980 | name = "winapi-i686-pc-windows-gnu" 1981 | version = "0.4.0" 1982 | source = "registry+https://github.com/rust-lang/crates.io-index" 1983 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1984 | 1985 | [[package]] 1986 | name = "winapi-util" 1987 | version = "0.1.5" 1988 | source = "registry+https://github.com/rust-lang/crates.io-index" 1989 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 1990 | dependencies = [ 1991 | "winapi", 1992 | ] 1993 | 1994 | [[package]] 1995 | name = "winapi-x86_64-pc-windows-gnu" 1996 | version = "0.4.0" 1997 | source = "registry+https://github.com/rust-lang/crates.io-index" 1998 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1999 | 2000 | [[package]] 2001 | name = "wyz" 2002 | version = "0.2.0" 2003 | source = "registry+https://github.com/rust-lang/crates.io-index" 2004 | checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" 2005 | 2006 | [[package]] 2007 | name = "yaml-rust" 2008 | version = "0.4.5" 2009 | source = "registry+https://github.com/rust-lang/crates.io-index" 2010 | checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" 2011 | dependencies = [ 2012 | "linked-hash-map", 2013 | ] 2014 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "url-mapper-rs" 3 | version = "0.1.0" 4 | authors = ["Dhruva Sagar "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | anyhow = "1.0.41" 11 | base64 = "0.13.0" 12 | config = { version = "0.11.0", features = ["json"] } 13 | hyper = "0.14.9" 14 | lazy_static = "1.4.0" 15 | routerify = "2.1.0" 16 | serde = { version = "1.0.126", features = ["derive"] } 17 | serde_json = "1.0.64" 18 | sqlx = { version = "0.5.5", features = ["runtime-tokio-rustls", "postgres", "migrate"] } 19 | tera = "1.12.1" 20 | tokio = { version = "1.6.2", features = ["full"] } 21 | tracing = "0.1.26" 22 | tracing-subscriber = "0.2.18" 23 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ekidd/rust-musl-builder:latest as builder 2 | COPY --chown=rust:rust . ./ 3 | RUN cargo build --release 4 | 5 | FROM scratch 6 | WORKDIR /url-mapper-rs 7 | COPY --from=builder /home/rust/src/target/x86_64-unknown-linux-musl/release/url-mapper-rs ./ 8 | COPY config ./config 9 | EXPOSE 3000 10 | CMD ["./url-mapper-rs"] 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # URL Mapper in Rust 2 | 3 | This is a simple URL Mapper service built in Rust as part of a youtube series, 4 | you can find the playlist [here](https://www.youtube.com/playlist?list=PLz51_WNhdOqv7S5pnycKySU_4PpCagU4Q) 5 | -------------------------------------------------------------------------------- /client/react-url-mapper/.env: -------------------------------------------------------------------------------- 1 | REACT_APP_API_HOST=http://localhost:3001 2 | -------------------------------------------------------------------------------- /client/react-url-mapper/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /client/react-url-mapper/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `npm start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 13 | 14 | The page will reload if you make edits.\ 15 | You will also see any lint errors in the console. 16 | 17 | ### `npm test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `npm run build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `npm run eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 35 | 36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 39 | 40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | -------------------------------------------------------------------------------- /client/react-url-mapper/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-url-mapper", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.14.1", 7 | "@testing-library/react": "^11.2.7", 8 | "@testing-library/user-event": "^12.8.3", 9 | "@types/jest": "^26.0.24", 10 | "@types/node": "^12.20.19", 11 | "@types/react": "^17.0.16", 12 | "@types/react-dom": "^17.0.9", 13 | "@types/react-router-dom": "^5.1.8", 14 | "react": "^17.0.2", 15 | "react-dom": "^17.0.2", 16 | "react-router-dom": "^5.2.0", 17 | "react-scripts": "4.0.3", 18 | "typescript": "^4.3.5", 19 | "web-vitals": "^1.1.2" 20 | }, 21 | "scripts": { 22 | "start": "react-scripts start", 23 | "build": "react-scripts build", 24 | "test": "react-scripts test", 25 | "eject": "react-scripts eject" 26 | }, 27 | "eslintConfig": { 28 | "extends": [ 29 | "react-app", 30 | "react-app/jest" 31 | ] 32 | }, 33 | "browserslist": { 34 | "production": [ 35 | ">0.2%", 36 | "not dead", 37 | "not op_mini all" 38 | ], 39 | "development": [ 40 | "last 1 chrome version", 41 | "last 1 firefox version", 42 | "last 1 safari version" 43 | ] 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /client/react-url-mapper/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dhruvasagar/url-mapper-rs/c870d5f7d43ce795d8ed69adf131620ee05e1298/client/react-url-mapper/public/favicon.ico -------------------------------------------------------------------------------- /client/react-url-mapper/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 32 | 33 | 34 | 35 |
36 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /client/react-url-mapper/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dhruvasagar/url-mapper-rs/c870d5f7d43ce795d8ed69adf131620ee05e1298/client/react-url-mapper/public/logo192.png -------------------------------------------------------------------------------- /client/react-url-mapper/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dhruvasagar/url-mapper-rs/c870d5f7d43ce795d8ed69adf131620ee05e1298/client/react-url-mapper/public/logo512.png -------------------------------------------------------------------------------- /client/react-url-mapper/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /client/react-url-mapper/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /client/react-url-mapper/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /client/react-url-mapper/src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render, screen } from '@testing-library/react'; 3 | import App from './App'; 4 | 5 | test('renders learn react link', () => { 6 | render(); 7 | const linkElement = screen.getByText(/learn react/i); 8 | expect(linkElement).toBeInTheDocument(); 9 | }); 10 | -------------------------------------------------------------------------------- /client/react-url-mapper/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { 3 | BrowserRouter as Router, 4 | Switch, 5 | Route, 6 | NavLink 7 | } from 'react-router-dom' 8 | import './App.css' 9 | 10 | import Settings from './Settings' 11 | import URLMapNew from './url_maps/URLMapNew' 12 | import URLMapEdit from './url_maps/URLMapEdit' 13 | import URLMapIndex from './url_maps/URLMapIndex' 14 | 15 | function App() { 16 | return ( 17 | 18 |
19 |
    20 |
  • 21 | Home 22 |
  • 23 |
  • 24 | Settings 25 |
  • 26 |
27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 |
44 | ) 45 | } 46 | 47 | export default App 48 | -------------------------------------------------------------------------------- /client/react-url-mapper/src/Settings.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react' 2 | 3 | export default function Settings() { 4 | const [authToken, setAuthToken] = useState(() => { 5 | return window.localStorage.getItem('authorization') || '' 6 | }) 7 | 8 | useEffect(() => { 9 | window.localStorage.setItem('authorization', authToken) 10 | }) 11 | 12 | return ( 13 |
14 | Authorization 15 | setAuthToken(e.target.value)} 20 | /> 21 | 22 |
23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /client/react-url-mapper/src/api/url_map.ts: -------------------------------------------------------------------------------- 1 | export const API_HOST=process.env.REACT_APP_API_HOST 2 | 3 | export interface URLMap { 4 | key: string 5 | url: string 6 | } 7 | 8 | const buildHeaders = () => { 9 | const headers = new Headers() 10 | headers.set('Content-Type', 'application/json') 11 | const authToken = window.localStorage.getItem('authorization') || '' 12 | headers.set('Authorization', authToken) 13 | return headers 14 | } 15 | 16 | export const getURLMaps = async () => { 17 | const response = await fetch(`${API_HOST}/api/url_maps`, { 18 | headers: buildHeaders() 19 | }) 20 | let urlMaps: Array = await response.json() 21 | return urlMaps 22 | } 23 | 24 | export const getURLMap = async (key: string) => { 25 | const response = await fetch(`${API_HOST}/api/url_maps/${key}`, { 26 | headers: buildHeaders() 27 | }) 28 | let urlMap: URLMap = await response.json() 29 | return urlMap 30 | } 31 | 32 | export const createURLMap = async (urlMap: URLMap) => { 33 | const response = await fetch(`${API_HOST}/api/url_maps`, { 34 | method: 'POST', 35 | headers: buildHeaders(), 36 | body: JSON.stringify(urlMap) 37 | }) 38 | let createdURLMap: URLMap = await response.json() 39 | return createdURLMap 40 | } 41 | 42 | export const updateURLMap = async (urlMap: URLMap) => { 43 | const response = await fetch(`${API_HOST}/api/url_maps/${urlMap.key}`, { 44 | method: 'PUT', 45 | headers: buildHeaders(), 46 | body: JSON.stringify(urlMap) 47 | }) 48 | let updatedURLMap: URLMap = await response.json() 49 | return updatedURLMap 50 | } 51 | 52 | export const deleteURLMap = async (key: string) => { 53 | const response = await fetch(`${API_HOST}/api/url_maps/${key}`, { 54 | method: 'DELETE', 55 | headers: buildHeaders(), 56 | }) 57 | return response.ok 58 | } 59 | -------------------------------------------------------------------------------- /client/react-url-mapper/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 5px; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | 15 | a { 16 | padding: 0 2px; 17 | } 18 | 19 | button { 20 | margin-right: 5px; 21 | } 22 | -------------------------------------------------------------------------------- /client/react-url-mapper/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /client/react-url-mapper/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /client/react-url-mapper/src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals'; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry); 7 | getFID(onPerfEntry); 8 | getFCP(onPerfEntry); 9 | getLCP(onPerfEntry); 10 | getTTFB(onPerfEntry); 11 | }); 12 | } 13 | }; 14 | 15 | export default reportWebVitals; 16 | -------------------------------------------------------------------------------- /client/react-url-mapper/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /client/react-url-mapper/src/url_maps/URLMapEdit.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react' 2 | import { useParams, useHistory } from 'react-router-dom' 3 | import { getURLMap, updateURLMap } from '../api/url_map' 4 | 5 | export default function URLMapEdit() { 6 | const { id } = useParams<{id: string}>() 7 | 8 | const history = useHistory() 9 | const [key, setKey] = useState(id) 10 | const [url, setURL] = useState('') 11 | 12 | useEffect(() => { 13 | getURLMap(key).then((urlMap) => { 14 | setKey(urlMap.key) 15 | setURL(urlMap.url) 16 | }) 17 | }, [key]) 18 | 19 | const handleSubmit = async (e: React.FormEvent) => { 20 | e.preventDefault() 21 | await updateURLMap({key: key, url: url}) 22 | history.push('/') 23 | } 24 | 25 | const handleCancel = (e: React.FormEvent) => { 26 | e.preventDefault() 27 | history.goBack() 28 | } 29 | 30 | return ( 31 |
32 | 33 | setKey(e.target.value)} /> 34 | 35 | 36 | setURL(e.target.value)} /> 37 | 38 | 39 | 40 |
41 | ) 42 | } 43 | -------------------------------------------------------------------------------- /client/react-url-mapper/src/url_maps/URLMapIndex.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react' 2 | import { Link, useHistory } from 'react-router-dom' 3 | import { API_HOST, URLMap, getURLMaps, deleteURLMap } from '../api/url_map' 4 | 5 | interface URLMapProp { 6 | keyword: string 7 | url: string 8 | } 9 | 10 | const URLMapRow = (urlMap: URLMapProp) => { 11 | const history = useHistory() 12 | const handleDelete = (e: React.FormEvent) => { 13 | if (window.confirm("Are you sure?")) { 14 | e.preventDefault() 15 | deleteURLMap(urlMap.keyword).then(() => { 16 | history.go(0) 17 | }) 18 | } 19 | } 20 | return ( 21 | 22 | {urlMap.keyword} 23 | {urlMap.url} 24 | 25 | 26 | Test 27 | 28 | Edit 29 | Delete 30 | 31 | 32 | ) 33 | } 34 | 35 | const URLMapEmptyRow = () => { 36 | return ( 37 | 38 | No URLMaps! 39 | 40 | ) 41 | } 42 | 43 | export default function URLMapIndex() { 44 | const [urlMaps, setURLMaps] = useState(Array()) 45 | 46 | useEffect(() => { 47 | getURLMaps().then(setURLMaps) 48 | }, []) 49 | 50 | let rows = urlMaps.map((urlMap) => ) 51 | if (rows.length === 0) { 52 | rows = [ 53 | 54 | ] 55 | } 56 | 57 | return ( 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | { rows } 68 | 69 |
KeyURLActions Create
70 | ) 71 | } 72 | -------------------------------------------------------------------------------- /client/react-url-mapper/src/url_maps/URLMapNew.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | import { useHistory } from 'react-router-dom' 3 | import { createURLMap } from '../api/url_map' 4 | 5 | export default function URLMapNew() { 6 | const history = useHistory() 7 | const [key, setKey] = useState('') 8 | const [url, setURL] = useState('') 9 | 10 | const handleSubmit = async (e: React.FormEvent) => { 11 | e.preventDefault() 12 | await createURLMap({key: key, url: url}) 13 | history.push('/') 14 | } 15 | 16 | const handleCancel = (e: React.FormEvent) => { 17 | e.preventDefault() 18 | history.goBack() 19 | } 20 | 21 | return ( 22 |
23 | 24 | setKey(e.target.value)} /> 25 | 26 | 27 | setURL(e.target.value)} /> 28 | 29 | 30 | 31 |
32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /client/react-url-mapper/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx" 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /client/tera/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | {% block title %}{% endblock title %} - Url Mapper in Rust 12 | 13 | 14 |
15 |
16 | Authorization 17 | 23 | 24 |
25 | {% block content %}{% endblock content %} 26 |
27 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /client/tera/index.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | const AUTH_KEY = 'authorization' 3 | 4 | document.addEventListener('DOMContentLoaded', () => { 5 | const auth_token = localStorage.getItem(AUTH_KEY) 6 | document.getElementById(AUTH_KEY).value = auth_token 7 | }) 8 | 9 | document.getElementById('save_auth_token_button') 10 | .addEventListener('click', () => { 11 | const auth_token = document.getElementById(AUTH_KEY).value 12 | localStorage.setItem(AUTH_KEY, auth_token) 13 | }) 14 | 15 | const create_form = document.querySelector('#create_url_map_form') 16 | if (create_form) { 17 | create_form.addEventListener('submit', (event) => { 18 | event.preventDefault() 19 | const formData = new FormData(event.target) 20 | 21 | fetch('/api/url_maps', { 22 | method: 'POST', 23 | headers: {'authorization': localStorage.getItem(AUTH_KEY)}, 24 | body: JSON.stringify(Object.fromEntries(formData)), 25 | }).then((response) => { 26 | if (response.status == 200) { 27 | alert('Create Url Map successfully!') 28 | window.location.href = '/admin/url_maps' 29 | } 30 | }) 31 | }) 32 | } 33 | 34 | const update_form = document.getElementById('update_url_map_form') 35 | if (update_form) { 36 | update_form.addEventListener('submit', (event) => { 37 | event.preventDefault() 38 | const formData = new FormData(event.target) 39 | const key = formData.get('key') 40 | 41 | fetch(`/api/url_maps/${key}`, { 42 | method: 'PUT', 43 | headers: {'authorization': localStorage.getItem(AUTH_KEY)}, 44 | body: JSON.stringify(Object.fromEntries(formData)), 45 | }).then((response) => { 46 | if (response.status == 200) { 47 | alert(`Updated Url Map for ${key} successfully!`) 48 | window.location.href = '/admin/url_maps' 49 | } 50 | }) 51 | }) 52 | } 53 | 54 | const delete_url_map_links = document.querySelectorAll(".delete-url-map") 55 | Array.from(delete_url_map_links).forEach(link => { 56 | link.addEventListener("click", () => { 57 | const key = link.getAttribute('data') 58 | fetch(`/api/url_maps/${key}`, { 59 | method: 'DELETE', 60 | headers: {'authorization': localStorage.getItem(AUTH_KEY)}, 61 | }).then((response) => { 62 | if (response.status == 200) { 63 | alert(`Deleted Url Map for ${key} successfully!`) 64 | window.location.reload() 65 | } 66 | }) 67 | }) 68 | }) 69 | })() 70 | -------------------------------------------------------------------------------- /client/tera/style.css: -------------------------------------------------------------------------------- 1 | #content { 2 | padding: 50px; 3 | } 4 | 5 | #footer { 6 | padding: 20px; 7 | text-align: center; 8 | } 9 | -------------------------------------------------------------------------------- /client/tera/url_maps/edit.html: -------------------------------------------------------------------------------- 1 | {% extends "index.html" %} 2 | {% block title %}Edit Url Map {{ url_map.key }}{% endblock title %} 3 | {% block content %} 4 |
5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 |
17 | {% endblock content %} 18 | -------------------------------------------------------------------------------- /client/tera/url_maps/index.html: -------------------------------------------------------------------------------- 1 | {% extends "index.html" %} 2 | {% block title %}Url Maps Index{% endblock title %} 3 | {% block content %} 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | {% for url_map in url_maps %} 14 | 15 | 16 | 17 | 26 | 27 | {% endfor %} 28 | 29 |
KeyURLActions Create
{{ url_map.key }}{{ url_map.url }} 18 | Test 19 | Edit 20 | 23 | Delete 24 | 25 |
30 | {% endblock content %} 31 | -------------------------------------------------------------------------------- /client/tera/url_maps/new.html: -------------------------------------------------------------------------------- 1 | {% extends "index.html" %} 2 | {% block title %}Create Url Map{% endblock title %} 3 | {% block content %} 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | {% endblock content %} 14 | -------------------------------------------------------------------------------- /config/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "host": "127.0.0.1", 3 | "port": 3000, 4 | "auth_token": "This is a simple auth token, but long enough to be safe enough!", 5 | "database": { 6 | "url": "postgres:///testdb?sslmode=disable", 7 | "max_connections": 16 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.9" 2 | 3 | services: 4 | db: 5 | image: postgres 6 | volumes: 7 | - pgdata:/var/lib/postgresql/data 8 | environment: 9 | - POSTGRES_DB=url_mapper_prod 10 | - POSTGRES_USER=postgres 11 | - POSTGRES_PASSWORD=admin 12 | url-mapper-rs: 13 | image: url-mapper-rs:v2 14 | build: . 15 | ports: 16 | - "3000:3000" 17 | environment: 18 | - DATABASE_URL=postgres://postgres:admin@db/url_mapper_prod?sslmode=disable 19 | depends_on: 20 | - db 21 | volumes: 22 | pgdata: 23 | -------------------------------------------------------------------------------- /k8s/postgres.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: postgres 5 | namespace: url-mapper-rs 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: postgres 11 | template: 12 | metadata: 13 | labels: 14 | app: postgres 15 | spec: 16 | containers: 17 | - name: postgres 18 | image: postgres 19 | env: 20 | - name: POSTGRES_DB 21 | value: url_mapper_prod 22 | - name: POSTGRES_USER 23 | value: postgres 24 | - name: POSTGRES_PASSWORD 25 | value: admin 26 | --- 27 | apiVersion: v1 28 | kind: Service 29 | metadata: 30 | name: postgres 31 | namespace: url-mapper-rs 32 | spec: 33 | selector: 34 | app: postgres 35 | ports: 36 | - port: 5432 37 | -------------------------------------------------------------------------------- /k8s/url-mapper-rs.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: url-mapper-rs 5 | namespace: url-mapper-rs 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: url-mapper-rs 11 | template: 12 | metadata: 13 | labels: 14 | app: url-mapper-rs 15 | spec: 16 | containers: 17 | - name: url-mapper-rs 18 | image: url-mapper-rs:v2 19 | env: 20 | - name: DATABASE_URL 21 | value: postgres://postgres:admin@postgres/url_mapper_prod?sslmode=disable 22 | --- 23 | apiVersion: v1 24 | kind: Service 25 | metadata: 26 | name: url-mapper-rs 27 | namespace: url-mapper-rs 28 | spec: 29 | selector: 30 | app: url-mapper-rs 31 | ports: 32 | - port: 3000 33 | --- 34 | apiVersion: networking.k8s.io/v1 35 | kind: Ingress 36 | metadata: 37 | name: url-mapper-rs 38 | namespace: url-mapper-rs 39 | spec: 40 | rules: 41 | - http: 42 | paths: 43 | - pathType: Prefix 44 | path: "/" 45 | backend: 46 | service: 47 | name: url-mapper-rs 48 | port: 49 | number: 3000 50 | -------------------------------------------------------------------------------- /migrations/20210615064800_create_url_maps.sql: -------------------------------------------------------------------------------- 1 | -- Add migration script here 2 | CREATE TABLE IF NOT EXISTS url_maps ( 3 | key VARCHAR(50) PRIMARY KEY, 4 | url TEXT NOT NULL 5 | ); 6 | -------------------------------------------------------------------------------- /src/config.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{Context, Result}; 2 | use config::{Config as RConfig, Environment, File}; 3 | use lazy_static::lazy_static; 4 | use serde::{Deserialize, Serialize}; 5 | use std::env; 6 | 7 | #[derive(Debug, Serialize, Deserialize)] 8 | pub struct Database { 9 | pub url: String, 10 | pub max_connections: u32, 11 | } 12 | 13 | #[derive(Debug, Serialize, Deserialize)] 14 | pub struct Config { 15 | pub env: String, 16 | pub host: String, 17 | pub port: i32, 18 | pub auth_token: String, 19 | pub database: Database, 20 | } 21 | 22 | impl Config { 23 | pub fn new() -> Result { 24 | let mut s = RConfig::default(); 25 | 26 | s.merge(File::with_name("config/default")) 27 | .context("Unable to load the default config")?; 28 | 29 | let env = env::var("ENV").unwrap_or("development".into()); 30 | s.set("env", env.clone())?; 31 | s.merge(File::with_name(&format!("config/{}", env)).required(false)) 32 | .context(format!("Unable to load config/{}.json", env))?; 33 | 34 | s.merge(Environment::new().separator("_".into()))?; 35 | 36 | s.try_into().context("Unable to instantiate Config struct") 37 | } 38 | } 39 | 40 | lazy_static! { 41 | pub static ref CONFIG: Config = Config::new().unwrap(); 42 | } 43 | -------------------------------------------------------------------------------- /src/db/db.rs: -------------------------------------------------------------------------------- 1 | use sqlx::{migrate, Pool, Postgres, postgres::PgPoolOptions, FromRow}; 2 | use serde::{Serialize, Deserialize}; 3 | use anyhow::Result; 4 | use crate::config::CONFIG; 5 | 6 | #[derive(Debug, FromRow, Clone, Serialize, Deserialize)] 7 | pub struct UrlMap { 8 | pub key: String, 9 | pub url: String, 10 | } 11 | 12 | impl UrlMap { 13 | pub fn new(key: String, url: String) -> Self { 14 | Self { key, url } 15 | } 16 | } 17 | 18 | pub struct DB { 19 | pub pool: Pool, 20 | } 21 | 22 | impl DB { 23 | pub async fn new() -> Result { 24 | let pool = PgPoolOptions::new() 25 | .max_connections(CONFIG.database.max_connections) 26 | .connect(&CONFIG.database.url) 27 | .await?; 28 | 29 | let migrator = migrate!(); 30 | migrator 31 | .run(&pool) 32 | .await?; 33 | 34 | Ok(Self { pool }) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/db/manager.rs: -------------------------------------------------------------------------------- 1 | use crate::db::{DB, UrlMap}; 2 | use sqlx::{Postgres, pool::PoolConnection}; 3 | use tokio::sync::{mpsc::Receiver, oneshot::Sender}; 4 | 5 | type Responder = Sender>; 6 | 7 | #[derive(Debug)] 8 | pub enum Message { 9 | GetUrlMaps { resp: Responder> }, 10 | GetUrlMap { key: String, resp: Responder }, 11 | CreateUrlMap { url_map: UrlMap, resp: Responder }, 12 | UpdateUrlMap { url_map: UrlMap, resp: Responder }, 13 | DeleteUrlMap { key: String, resp: Responder }, 14 | } 15 | 16 | pub struct Manager { 17 | db: DB, 18 | receiver: Receiver, 19 | } 20 | 21 | type Connection = PoolConnection; 22 | 23 | impl Manager { 24 | pub fn new(db: DB, receiver: Receiver) -> Self { 25 | Self { db, receiver } 26 | } 27 | 28 | async fn get_url_maps(conn: &mut Connection) -> Result, sqlx::Error> { 29 | sqlx::query_as::<_, UrlMap>("SELECT * FROM url_maps") 30 | .fetch_all(conn) 31 | .await 32 | } 33 | 34 | async fn get_url_map(conn: &mut Connection, key: String) -> Result { 35 | sqlx::query_as::<_, UrlMap>("SELECT * FROM url_maps WHERE key = $1") 36 | .bind(key) 37 | .fetch_one(conn) 38 | .await 39 | } 40 | 41 | async fn create_url_map(conn: &mut Connection, url_map: UrlMap) -> Result { 42 | sqlx::query_as::<_, UrlMap>("INSERT INTO url_maps (key, url) VALUES ($1, $2) RETURNING *") 43 | .bind(url_map.key) 44 | .bind(url_map.url) 45 | .fetch_one(conn) 46 | .await 47 | } 48 | 49 | async fn update_url_map(conn: &mut Connection, url_map: UrlMap) -> Result { 50 | sqlx::query_as::<_, UrlMap>("UPDATE url_maps SET url=$1 WHERE key=$2 RETURNING *") 51 | .bind(url_map.url) 52 | .bind(url_map.key) 53 | .fetch_one(conn) 54 | .await 55 | } 56 | 57 | async fn delete_url_map(conn: &mut Connection, key: String) -> Result { 58 | sqlx::query_as::<_, UrlMap>("DELETE FROM url_maps WHERE key = $1 RETURNING *") 59 | .bind(key) 60 | .fetch_one(conn) 61 | .await 62 | } 63 | 64 | pub async fn listen(&mut self) { 65 | while let Some(message) = self.receiver.recv().await { 66 | let mut connection = self.db.pool.acquire().await.unwrap(); 67 | match message { 68 | Message::GetUrlMaps { resp } => { 69 | let url_maps = Self::get_url_maps(&mut connection).await; 70 | resp_failed!(resp.send(url_maps), "GetUrlMaps"); 71 | } 72 | Message::GetUrlMap { key, resp } => { 73 | let url_map = Self::get_url_map(&mut connection, key).await; 74 | resp_failed!(resp.send(url_map), "GetUrlMap"); 75 | } 76 | Message::CreateUrlMap { url_map, resp } => { 77 | let url_map = Self::create_url_map(&mut connection, url_map).await; 78 | resp_failed!(resp.send(url_map), "CreateUrlMap"); 79 | } 80 | Message::UpdateUrlMap { url_map, resp } => { 81 | let url_map = Self::update_url_map(&mut connection, url_map).await; 82 | resp_failed!(resp.send(url_map), "UpdateUrlMap"); 83 | } 84 | Message::DeleteUrlMap { key, resp } => { 85 | let url_map = Self::delete_url_map(&mut connection, key).await; 86 | resp_failed!(resp.send(url_map), "DeleteUrlMap"); 87 | } 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/db/mod.rs: -------------------------------------------------------------------------------- 1 | mod db; 2 | mod manager; 3 | 4 | pub use db::{UrlMap, DB}; 5 | pub use manager::{Manager, Message}; 6 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! sender_failed { 3 | ($m: expr, $f: tt) => { 4 | match $m { 5 | Ok(_) => {} 6 | Err(e) => { 7 | tracing::error!("Database Manager failed to get {}! error: {}", $f, e); 8 | return Ok(Response::builder() 9 | .status(hyper::StatusCode::INTERNAL_SERVER_ERROR) 10 | .body(Body::from(format!("Something went wrong: {}", e))) 11 | .unwrap()); 12 | } 13 | } 14 | }; 15 | } 16 | 17 | #[macro_export] 18 | macro_rules! recv_failed { 19 | ($m: expr) => { 20 | match $m { 21 | Ok(d) => d, 22 | Err(e) => { 23 | tracing::error!("Database Manager returned error: {}", e); 24 | return Ok(Response::builder() 25 | .status(hyper::StatusCode::NOT_FOUND) 26 | .body(Body::from("Key does not exist")) 27 | .unwrap()); 28 | } 29 | } 30 | }; 31 | } 32 | 33 | #[macro_export] 34 | macro_rules! resp_failed { 35 | ($m: expr, $f: tt) => { 36 | match $m { 37 | Ok(_) => {} 38 | Err(e) => tracing::error!("Resp failed for {}, error: {:?}", $f, e), 39 | } 40 | }; 41 | } 42 | 43 | #[macro_export] 44 | macro_rules! json_response { 45 | (body: $body:expr) => { 46 | Response::builder() 47 | .header(hyper::header::CONTENT_TYPE, "application/json") 48 | .body(serde_json::to_string($body).unwrap().into()) 49 | .unwrap() 50 | }; 51 | (status: $status:expr, body: $body:expr) => { 52 | Response::builder() 53 | .header(hyper::header::CONTENT_TYPE, "application/json") 54 | .status($status) 55 | .body(serde_json::to_string($body).unwrap().into()) 56 | .unwrap() 57 | }; 58 | (error: $e:expr) => { 59 | json_response!( 60 | status: hyper::StatusCode::INTERNAL_SERVER_ERROR, 61 | body: &serde_json::json!({ 62 | "error": $e.to_string(), 63 | }).to_string()) 64 | }; 65 | } 66 | 67 | #[macro_export] 68 | macro_rules! sender_failed_json { 69 | ($m: expr, $f: tt) => { 70 | match $m { 71 | Ok(_) => {} 72 | Err(e) => { 73 | tracing::error!("Database Manager failed to get {}! error: {}", $f, e); 74 | return Ok(json_response!(error: e)); 75 | } 76 | } 77 | }; 78 | } 79 | 80 | #[macro_export] 81 | macro_rules! recv_failed_json { 82 | ($m: expr, $status: expr) => { 83 | match $m { 84 | Ok(d) => d, 85 | Err(e) => { 86 | tracing::error!("Database Manager returned error: {}", e); 87 | return Ok(json_response!( 88 | status: $status, 89 | body: &e.to_string())) 90 | } 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use crate::db::{DB, Manager}; 3 | use tracing::subscriber::set_global_default; 4 | use tracing_subscriber::FmtSubscriber; 5 | use server::Server; 6 | use std::process; 7 | 8 | #[macro_use] 9 | mod macros; 10 | 11 | mod config; 12 | mod db; 13 | mod server; 14 | 15 | #[tokio::main] 16 | async fn main() -> Result<()> { 17 | let subscriber = FmtSubscriber::new(); 18 | set_global_default(subscriber)?; 19 | 20 | let db = DB::new().await.unwrap(); 21 | let (db_tx, db_rx) = tokio::sync::mpsc::channel(32); 22 | tokio::spawn(async move { 23 | let mut manager = Manager::new(db, db_rx); 24 | manager.listen().await; 25 | }); 26 | 27 | tokio::spawn(async move { 28 | use tokio::signal::unix::{signal, SignalKind}; 29 | let mut hup = signal(SignalKind::hangup()).unwrap(); 30 | let mut int = signal(SignalKind::interrupt()).unwrap(); 31 | let mut quit = signal(SignalKind::quit()).unwrap(); 32 | let mut term = signal(SignalKind::terminate()).unwrap(); 33 | 34 | tokio::select! { 35 | _ = hup.recv() => tracing::info!("Recieved SIGHUP!"), 36 | _ = int.recv() => tracing::info!("Recieved SIGINT!"), 37 | _ = quit.recv() => tracing::info!("Recieved SIGQUIT!"), 38 | _ = term.recv() => tracing::info!("Recieved SIGTERM!"), 39 | } 40 | tracing::info!("Good Bye from Url Mapper in Rust!"); 41 | process::exit(0); 42 | }); 43 | 44 | Server::new(db_tx).listen().await?; 45 | 46 | Ok(()) 47 | } 48 | -------------------------------------------------------------------------------- /src/server/mod.rs: -------------------------------------------------------------------------------- 1 | mod routes; 2 | mod server; 3 | mod state; 4 | 5 | pub use server::Server; 6 | pub use state::State; 7 | -------------------------------------------------------------------------------- /src/server/routes/admin/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::config::CONFIG; 2 | use anyhow::{Result, Error}; 3 | use hyper::{Body, Request, Response}; 4 | use routerify::Router; 5 | use std::fs::read_to_string; 6 | 7 | mod url_maps; 8 | 9 | async fn css_handler(_req: Request) -> Result> { 10 | let css = match CONFIG.env.as_str() { 11 | "development" => read_to_string("client/tera/style.css")?, 12 | _ => include_str!("../../../../client/tera/style.css").to_string(), 13 | }; 14 | Ok(Response::builder() 15 | .body(Body::from(css)) 16 | .unwrap()) 17 | } 18 | 19 | async fn js_handler(_req: Request) -> Result> { 20 | let js = match CONFIG.env.as_str() { 21 | "development" => read_to_string("client/tera/index.js")?, 22 | _ => include_str!("../../../../client/tera/index.js").to_string(), 23 | }; 24 | Ok(Response::builder() 25 | .body(Body::from(js)) 26 | .unwrap()) 27 | } 28 | 29 | pub fn router() -> Router { 30 | Router::builder() 31 | .get("/index.js", js_handler) 32 | .get("/style.css", css_handler) 33 | .scope("/url_maps", url_maps::router()) 34 | .build() 35 | .unwrap() 36 | } 37 | -------------------------------------------------------------------------------- /src/server/routes/admin/url_maps/handlers.rs: -------------------------------------------------------------------------------- 1 | use crate::{db::Message, server::State}; 2 | use hyper::{Body, Request, Response}; 3 | use anyhow::Result; 4 | use routerify::ext::RequestExt; 5 | use tera::Context; 6 | 7 | pub async fn index(req: Request) -> Result> { 8 | let state = req.data::().unwrap(); 9 | let sender = state.db_sender(); 10 | let tera = state.tera(); 11 | 12 | let (tx, rx) = tokio::sync::oneshot::channel(); 13 | sender_failed!( 14 | sender 15 | .send(Message::GetUrlMaps { resp: tx }) 16 | .await, "GetUrlMaps"); 17 | let url_maps = recv_failed!(rx.await.unwrap()); 18 | 19 | let mut context = Context::new(); 20 | context.insert("url_maps", &url_maps); 21 | let index_html = tera.render("url_maps/index.html", &context)?; 22 | 23 | Ok(Response::builder() 24 | .body(Body::from(index_html)) 25 | .unwrap()) 26 | } 27 | 28 | pub async fn new(req: Request) -> Result> { 29 | let state = req.data::().unwrap(); 30 | let tera = state.tera(); 31 | 32 | let new_html = tera.render("url_maps/new.html", &Context::new())?; 33 | 34 | Ok(Response::builder() 35 | .body(Body::from(new_html)) 36 | .unwrap()) 37 | } 38 | 39 | pub async fn edit(req: Request) -> Result> { 40 | let state = req.data::().unwrap(); 41 | let sender = state.db_sender(); 42 | let tera = state.tera(); 43 | let key = req.param("key").unwrap(); 44 | 45 | let (tx, rx) = tokio::sync::oneshot::channel(); 46 | sender_failed!( 47 | sender 48 | .send(Message::GetUrlMap { key: key.into(), resp: tx }) 49 | .await, "GetUrlMap"); 50 | let url_map = recv_failed!(rx.await.unwrap()); 51 | 52 | let mut context = Context::new(); 53 | context.insert("url_map", &url_map); 54 | let edit_html = tera.render("url_maps/edit.html", &context)?; 55 | 56 | Ok(Response::builder() 57 | .body(Body::from(edit_html)) 58 | .unwrap()) 59 | } 60 | -------------------------------------------------------------------------------- /src/server/routes/admin/url_maps/mod.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Error; 2 | use hyper::Body; 3 | use routerify::Router; 4 | 5 | mod handlers; 6 | 7 | pub fn router() -> Router { 8 | Router::builder() 9 | .get("/", handlers::index) 10 | .get("/new", handlers::new) 11 | .get("/:key/edit", handlers::edit) 12 | .build() 13 | .unwrap() 14 | } 15 | -------------------------------------------------------------------------------- /src/server/routes/api/mod.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{anyhow, Error, Result}; 2 | use hyper::{Body, Request}; 3 | use routerify::{Router, Middleware}; 4 | use base64::decode; 5 | use std::str::from_utf8; 6 | use crate::config::CONFIG; 7 | 8 | mod url_maps; 9 | 10 | fn validate_token(encoded_token: &str) -> Result<()> { 11 | let auth_token_bytes = decode(&encoded_token)?; 12 | let auth_token = from_utf8(&auth_token_bytes)?; 13 | if auth_token != CONFIG.auth_token.as_str() { 14 | return Err(anyhow!("Unauthorized Access")); 15 | } 16 | Ok(()) 17 | } 18 | 19 | async fn auth_middleware(req: Request) -> Result> { 20 | if req.method() == hyper::Method::OPTIONS { 21 | return Ok(req); 22 | } 23 | 24 | let auth_token_header = req.headers().get(hyper::header::AUTHORIZATION); 25 | match auth_token_header { 26 | None => Err(anyhow!("Unauthorized Access")), 27 | Some(auth_token) => { 28 | let token = auth_token.to_str()?; 29 | validate_token(token)?; 30 | Ok(req) 31 | } 32 | } 33 | } 34 | 35 | pub fn router() -> Router { 36 | Router::builder() 37 | .middleware(Middleware::pre(auth_middleware)) 38 | .scope("/url_maps", url_maps::router()) 39 | .build() 40 | .unwrap() 41 | } 42 | -------------------------------------------------------------------------------- /src/server/routes/api/url_maps/handlers.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use serde::{Serialize, Deserialize}; 3 | use hyper::{Body, Request, Response, body::to_bytes}; 4 | use routerify::ext::RequestExt; 5 | use crate::{db::{UrlMap, Message}, server::State}; 6 | 7 | pub async fn get_url_maps(req: Request) -> Result> { 8 | let (tx, rx) = tokio::sync::oneshot::channel(); 9 | let state = req.data::().unwrap(); 10 | let sender = state.db_sender(); 11 | sender_failed_json!( 12 | sender 13 | .send(Message::GetUrlMaps { resp: tx }) 14 | .await, "GetUrlMaps"); 15 | let url_maps = recv_failed_json!(rx.await.unwrap(), hyper::StatusCode::INTERNAL_SERVER_ERROR); 16 | Ok(json_response!(body: &url_maps)) 17 | } 18 | 19 | pub async fn get_url_map(req: Request) -> Result> { 20 | let state = req.data::().unwrap(); 21 | let sender = state.db_sender(); 22 | let (tx, rx) = tokio::sync::oneshot::channel(); 23 | let key = req.param("key").unwrap(); 24 | sender_failed_json!( 25 | sender 26 | .send(Message::GetUrlMap { key: key.into(), resp: tx }) 27 | .await, "GetUrlMap"); 28 | let url_map = recv_failed_json!(rx.await.unwrap(), hyper::StatusCode::NOT_FOUND); 29 | Ok(json_response!(body: &url_map)) 30 | } 31 | 32 | pub async fn create_url_map(mut req: Request) -> Result> { 33 | let body = req.body_mut(); 34 | let url_map_bytes = to_bytes(body).await?; 35 | let url_map = serde_json::from_slice::(&url_map_bytes)?; 36 | let (tx, rx) = tokio::sync::oneshot::channel(); 37 | let state = req.data::().unwrap(); 38 | let sender = state.db_sender(); 39 | sender_failed_json!( 40 | sender 41 | .send(Message::CreateUrlMap { url_map, resp: tx }) 42 | .await, "CreateUrlMap"); 43 | let url_map = recv_failed_json!(rx.await.unwrap(), hyper::StatusCode::UNPROCESSABLE_ENTITY); 44 | Ok(json_response!(body: &url_map)) 45 | } 46 | 47 | pub async fn update_url_map(mut req: Request) -> Result> { 48 | #[derive(Debug, Serialize, Deserialize)] 49 | struct UrlMapUrl { 50 | url: String, 51 | } 52 | 53 | let body = req.body_mut(); 54 | let url_map_url_bytes = to_bytes(body).await?; 55 | let url_map_url = serde_json::from_slice::(&url_map_url_bytes)?; 56 | let key = req.param("key").unwrap(); 57 | let url_map = UrlMap::new(key.into(), url_map_url.url); 58 | let (tx, rx) = tokio::sync::oneshot::channel(); 59 | let state = req.data::().unwrap(); 60 | let sender = state.db_sender(); 61 | sender_failed_json!( 62 | sender 63 | .send(Message::UpdateUrlMap { url_map, resp: tx }) 64 | .await, "UpdateUrlMap"); 65 | let url_map = recv_failed_json!(rx.await.unwrap(), hyper::StatusCode::UNPROCESSABLE_ENTITY); 66 | Ok(json_response!(body: &url_map)) 67 | } 68 | 69 | pub async fn delete_url_map(req: Request) -> Result> { 70 | let key = req.param("key").unwrap(); 71 | let state = req.data::().unwrap(); 72 | let sender = state.db_sender(); 73 | let (tx, rx) = tokio::sync::oneshot::channel(); 74 | sender_failed_json!( 75 | sender 76 | .send(Message::DeleteUrlMap { key: key.into(), resp: tx }) 77 | .await, "DeleteUrlMap"); 78 | recv_failed_json!(rx.await.unwrap(), hyper::StatusCode::NOT_FOUND); 79 | Ok(json_response!(body: &serde_json::json!({ 80 | "ok": "true" 81 | }).to_string())) 82 | } 83 | -------------------------------------------------------------------------------- /src/server/routes/api/url_maps/mod.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Error; 2 | use hyper::Body; 3 | use routerify::Router; 4 | 5 | mod handlers; 6 | 7 | pub fn router() -> Router { 8 | Router::builder() 9 | .get("/", handlers::get_url_maps) 10 | .post("/", handlers::create_url_map) 11 | .get("/:key", handlers::get_url_map) 12 | .put("/:key", handlers::update_url_map) 13 | .delete("/:key", handlers::delete_url_map) 14 | .build() 15 | .unwrap() 16 | } 17 | -------------------------------------------------------------------------------- /src/server/routes/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::{db::Message, server::State}; 2 | use hyper::{ 3 | Body, 4 | Request, 5 | Response, 6 | header::HeaderValue 7 | }; 8 | use routerify::{ 9 | Middleware, 10 | Router, 11 | RouterBuilder, 12 | ext::RequestExt, 13 | RequestInfo 14 | }; 15 | use anyhow::{Error, Result}; 16 | use tracing::{info, error}; 17 | 18 | mod api; 19 | mod admin; 20 | 21 | async fn logger(req: Request) -> Result> { 22 | info!("{} {} {}", req.remote_addr(), req.method(), req.uri().path()); 23 | Ok(req) 24 | } 25 | 26 | async fn cors(mut res: Response) -> Result> { 27 | let headers = res.headers_mut(); 28 | headers.insert(hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN, HeaderValue::from_static("http://localhost:3000")); 29 | headers.insert(hyper::header::ACCESS_CONTROL_ALLOW_HEADERS, HeaderValue::from_static("*")); 30 | headers.insert(hyper::header::ACCESS_CONTROL_ALLOW_METHODS, HeaderValue::from_static("*")); 31 | headers.insert(hyper::header::ACCESS_CONTROL_EXPOSE_HEADERS, HeaderValue::from_static("*")); 32 | Ok(res) 33 | } 34 | 35 | async fn home_handler(_: Request) -> Result> { 36 | Ok(Response::new(Body::from("Url Mapper in Rust!"))) 37 | } 38 | 39 | async fn error_handler(err: routerify::RouteError, _: RequestInfo) -> Response { 40 | error!("{}", err); 41 | let status = match err.to_string().as_str() { 42 | "Unauthorized Access" => hyper::StatusCode::UNAUTHORIZED, 43 | _ => hyper::StatusCode::INTERNAL_SERVER_ERROR, 44 | }; 45 | Response::builder() 46 | .status(status) 47 | .body(Body::from(format!("Something went wrong: {}", err))) 48 | .unwrap() 49 | } 50 | 51 | async fn redirect_handler(req: Request) -> Result> { 52 | let state = req.data::().unwrap(); 53 | let sender = state.db_sender(); 54 | let key = req.param("key").unwrap(); 55 | let (tx, rx) = tokio::sync::oneshot::channel(); 56 | sender_failed!( 57 | sender 58 | .send(Message::GetUrlMap { key: key.clone(), resp: tx}) 59 | .await, "GetUrlMap"); 60 | let url_map = recv_failed!(rx.await.unwrap()); 61 | Ok(Response::builder() 62 | .header(hyper::header::LOCATION, url_map.url.clone()) 63 | .status(hyper::StatusCode::SEE_OTHER) 64 | .body(Body::from(format!("redirecting to url: {}", url_map.url))) 65 | .unwrap()) 66 | } 67 | 68 | pub fn router() -> RouterBuilder { 69 | Router::builder() 70 | .middleware(Middleware::pre(logger)) 71 | .middleware(Middleware::post(cors)) 72 | .get("/", home_handler) 73 | .get("/:key", redirect_handler) 74 | .scope("/api", api::router()) 75 | .scope("/admin", admin::router()) 76 | .err_handler_with_info(error_handler) 77 | } 78 | -------------------------------------------------------------------------------- /src/server/server.rs: -------------------------------------------------------------------------------- 1 | use hyper::Server as HyperServer; 2 | use crate::{db::Message, config::CONFIG}; 3 | use routerify::RouterService; 4 | use anyhow::Result; 5 | use tracing::info; 6 | use super::{State, routes}; 7 | use tokio::sync::mpsc::Sender; 8 | 9 | pub struct Server { 10 | db_sender: Sender, 11 | } 12 | 13 | impl Server { 14 | pub fn new(db_sender: Sender) -> Self { 15 | Self { db_sender } 16 | } 17 | 18 | pub async fn listen(&self) -> Result<()> { 19 | let state = State::new(self.db_sender.clone())?; 20 | let router = routes::router() 21 | .data(state) 22 | .build() 23 | .unwrap(); 24 | let service = RouterService::new(router).unwrap(); 25 | let addr = format!("{}:{}", CONFIG.host, CONFIG.port) 26 | .parse()?; 27 | let server = HyperServer::bind(&addr).serve(service); 28 | info!("Server started listening on {}", addr); 29 | server.await?; 30 | Ok(()) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/server/state.rs: -------------------------------------------------------------------------------- 1 | use crate::{config::CONFIG, db::Message}; 2 | use anyhow::Result; 3 | use tera::Tera; 4 | use tokio::sync::mpsc::Sender; 5 | 6 | #[derive(Debug)] 7 | pub struct State { 8 | db_sender: Sender, 9 | tera: Tera, 10 | } 11 | 12 | impl State { 13 | pub fn new(db_sender: Sender) -> Result { 14 | let tera = Tera::new("client/tera/**/*.html")?; 15 | Ok(Self { db_sender, tera }) 16 | } 17 | 18 | pub fn db_sender(&self) -> Sender { 19 | self.db_sender.clone() 20 | } 21 | 22 | pub fn tera(&self) -> Tera { 23 | let mut tera = self.tera.clone(); 24 | if CONFIG.env.as_str() == "development" { 25 | match tera.full_reload() { 26 | Ok(_) => tracing::info!("Tera templates reloaded successfully!"), 27 | Err(e) => tracing::error!("Failed to reload tera templates: {}", e), 28 | } 29 | } 30 | tera 31 | } 32 | } 33 | --------------------------------------------------------------------------------