Initial working version of the seed collection service
This commit is contained in:
parent
2763ff2481
commit
d09cfd4659
6
.gitignore
vendored
6
.gitignore
vendored
@ -4,3 +4,9 @@
|
||||
/data
|
||||
/qp_raker.toml
|
||||
.*.swp
|
||||
|
||||
quickpeep_static/.parcel-cache
|
||||
quickpeep_static/dist
|
||||
quickpeep_static/node_modules
|
||||
quickpeep/testdb.sqlite
|
||||
qp_web.ron
|
590
Cargo.lock
generated
590
Cargo.lock
generated
@ -42,6 +42,17 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
||||
dependencies = [
|
||||
"getrandom 0.2.5",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.18"
|
||||
@ -57,6 +68,74 @@ version = "1.0.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27"
|
||||
|
||||
[[package]]
|
||||
name = "askama"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb98f10f371286b177db5eeb9a6e5396609555686a35e1d4f7b9a9c6d8af0139"
|
||||
dependencies = [
|
||||
"askama_derive",
|
||||
"askama_escape",
|
||||
"askama_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "askama_derive"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87bf87e6e8b47264efa9bde63d6225c6276a52e05e91bf37eaa8afd0032d6b71"
|
||||
dependencies = [
|
||||
"askama_shared",
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "askama_escape"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341"
|
||||
|
||||
[[package]]
|
||||
name = "askama_shared"
|
||||
version = "0.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf722b94118a07fcbc6640190f247334027685d4e218b794dbfe17c32bf38ed0"
|
||||
dependencies = [
|
||||
"askama_escape",
|
||||
"humansize",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"nom",
|
||||
"num-traits",
|
||||
"percent-encoding",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"syn",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.52"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atoi"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "616896e05fc0e2649463a93a15183c6a16bf03413a7af88ef1285ddedfa9cda5"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
@ -74,6 +153,50 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9f346c92c1e9a71d14fe4aaf7c2a5d9932cc4e5e48d8fb6641524416eb79ddd"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum-core",
|
||||
"bitflags",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"matchit",
|
||||
"memchr",
|
||||
"mime",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tower",
|
||||
"tower-http",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-core"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6dbcda393bef9c87572779cb8ef916f12d77750b27535dd6819fa86591627a51"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"mime",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.0"
|
||||
@ -96,6 +219,15 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.9.1"
|
||||
@ -230,6 +362,30 @@ version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49fc9a695bca7f35f5f4c15cddc84415f66a74ea78eef08e90c5024f2b540e23"
|
||||
dependencies = [
|
||||
"crc-catalog",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc-catalog"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccaeedb56da03b09f598226e25e80088cb4cd25f316e6e4df7d695f0feeb1403"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.3.2"
|
||||
@ -273,6 +429,16 @@ dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f25d8400f4a7a5778f0e4e52384a48cbd9b5c495d110786187fc750075277a2"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.7"
|
||||
@ -358,6 +524,21 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dotenv"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
|
||||
|
||||
[[package]]
|
||||
name = "dtoa"
|
||||
version = "0.4.8"
|
||||
@ -440,6 +621,18 @@ dependencies = [
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.10.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843c03199d0c0ca54bc1ea90ac0d507274c28abcc4f691ae8b4eaa375087c76a"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"pin-project",
|
||||
"spin 0.9.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
@ -498,6 +691,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -506,6 +700,28 @@ version = "0.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-intrusive"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62007592ac46aa7c2b6416f7deb9a8a8f63a01e0f1d6e1787d5630170db2b63e"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"lock_api",
|
||||
"parking_lot 0.11.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.21"
|
||||
@ -544,6 +760,7 @@ dependencies = [
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
@ -578,6 +795,16 @@ dependencies = [
|
||||
"x509-signature",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.16"
|
||||
@ -615,7 +842,7 @@ dependencies = [
|
||||
"indexmap",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tokio-util 0.6.9",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
@ -624,6 +851,18 @@ name = "hashbrown"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashlink"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
@ -649,6 +888,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "html5ever"
|
||||
version = "0.25.1"
|
||||
@ -685,6 +930,12 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-range-header"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29"
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.6.0"
|
||||
@ -697,6 +948,12 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
|
||||
|
||||
[[package]]
|
||||
name = "humansize"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02296996cb8796d7c6e3bc2d9211b7802812d36999a51bb754123ead7d37d026"
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
@ -858,6 +1115,17 @@ version = "0.2.119"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.23.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2cafc7c74096c336d9d27145f7ebd4f4b6f95ba16aa5a282387267e6925cb58"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lifeguard"
|
||||
version = "0.6.1"
|
||||
@ -1684,6 +1952,12 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
|
||||
|
||||
[[package]]
|
||||
name = "matchit"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9376a4f0340565ad675d11fc1419227faf5f60cd7ac9cb2e7185a471f30af833"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.4.1"
|
||||
@ -1705,6 +1979,22 @@ version = "0.3.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
|
||||
|
||||
[[package]]
|
||||
name = "mime_guess"
|
||||
version = "2.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
|
||||
dependencies = [
|
||||
"mime",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.4.4"
|
||||
@ -1781,6 +2071,16 @@ version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ntapi"
|
||||
version = "0.3.7"
|
||||
@ -1883,6 +2183,12 @@ version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.38"
|
||||
@ -1973,6 +2279,12 @@ dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5"
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.1.0"
|
||||
@ -2042,6 +2354,26 @@ dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.8"
|
||||
@ -2139,6 +2471,19 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "quickpeep"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"askama",
|
||||
"axum",
|
||||
"env_logger",
|
||||
"itertools",
|
||||
"log",
|
||||
"ron",
|
||||
"serde",
|
||||
"sqlx",
|
||||
"tokio",
|
||||
"tower-http",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quickpeep_densedoc"
|
||||
@ -2366,7 +2711,7 @@ dependencies = [
|
||||
"serde_urlencoded",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-util",
|
||||
"tokio-util 0.6.9",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
@ -2383,7 +2728,7 @@ dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"spin",
|
||||
"spin 0.5.2",
|
||||
"untrusted",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
@ -2421,6 +2766,17 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ron"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b861ecaade43ac97886a512b360d01d66be9f41f3c61088b42cedf92e03d678"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bitflags",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
@ -2598,6 +2954,19 @@ dependencies = [
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.9.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.1.17"
|
||||
@ -2664,6 +3033,110 @@ version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlformat"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4b7922be017ee70900be125523f38bdd644f4f06a1b16e8fa5a8ee8c34bffd4"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"nom",
|
||||
"unicode_categories",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc15591eb44ffb5816a4a70a7efd5dd87bfd3aa84c4c200401c4396140525826"
|
||||
dependencies = [
|
||||
"sqlx-core",
|
||||
"sqlx-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-core"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "195183bf6ff8328bb82c0511a83faf60aacf75840103388851db61d7a9854ae3"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"atoi",
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"crc",
|
||||
"crossbeam-queue",
|
||||
"either",
|
||||
"flume",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-intrusive",
|
||||
"futures-util",
|
||||
"hashlink",
|
||||
"hex",
|
||||
"indexmap",
|
||||
"itoa 1.0.1",
|
||||
"libc",
|
||||
"libsqlite3-sys",
|
||||
"log",
|
||||
"memchr",
|
||||
"once_cell",
|
||||
"paste",
|
||||
"percent-encoding",
|
||||
"rustls",
|
||||
"sha2",
|
||||
"smallvec",
|
||||
"sqlformat",
|
||||
"sqlx-rt",
|
||||
"stringprep",
|
||||
"thiserror",
|
||||
"tokio-stream",
|
||||
"url",
|
||||
"webpki",
|
||||
"webpki-roots",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-macros"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eee35713129561f5e55c554bba1c378e2a7e67f81257b7311183de98c50e6f94"
|
||||
dependencies = [
|
||||
"dotenv",
|
||||
"either",
|
||||
"heck 0.3.3",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"sha2",
|
||||
"sqlx-core",
|
||||
"sqlx-rt",
|
||||
"syn",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-rt"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b555e70fbbf84e269ec3858b7a6515bcfe7a166a7cc9c636dd6efd20431678b6"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
@ -2696,6 +3169,16 @@ dependencies = [
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stringprep"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1"
|
||||
dependencies = [
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
@ -2732,6 +3215,12 @@ dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sync_wrapper"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.3.0"
|
||||
@ -2876,6 +3365,17 @@ dependencies = [
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-stream"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.6.9"
|
||||
@ -2890,6 +3390,20 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64910e1b9c1901aaf5375561e35b9c057d95ff41a44ede043a03e09279eabaf1"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"log",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.8"
|
||||
@ -2899,6 +3413,54 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a89fd63ad6adf737582df5db40d286574513c69a11dac5214dc3b5603d6713e"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"pin-project",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tokio-util 0.7.0",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aba3f3efabf7fb41fae8534fc20a817013dd1c12cb45441efb6c82e6556b4cd8"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-range-header",
|
||||
"httpdate",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tokio-util 0.7.0",
|
||||
"tower",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-layer"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62"
|
||||
|
||||
[[package]]
|
||||
name = "tower-service"
|
||||
version = "0.3.1"
|
||||
@ -2912,6 +3474,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a1bdf54a7c28a2bbf701e1d2233f6c77f473486b94bee4f9678da5a148dca7f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"log",
|
||||
"pin-project-lite",
|
||||
"tracing-core",
|
||||
]
|
||||
@ -2941,12 +3504,27 @@ dependencies = [
|
||||
"unchecked-index",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
|
||||
|
||||
[[package]]
|
||||
name = "unchecked-index"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eeba86d422ce181a719445e51872fa30f1f7413b62becb52e95ec91aa262d85c"
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
|
||||
dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.7"
|
||||
@ -2974,6 +3552,12 @@ version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "unicode_categories"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.7.1"
|
||||
|
62
qp_web.sample.ron
Normal file
62
qp_web.sample.ron
Normal file
@ -0,0 +1,62 @@
|
||||
(
|
||||
seed_collection: (
|
||||
column1: [
|
||||
(
|
||||
name: "Kind",
|
||||
tags: [
|
||||
(name: "Blog", id: "blog"),
|
||||
(name: "Manual", id: "man"),
|
||||
(name: "Cheatsheets", id: "cheatsheets"),
|
||||
]
|
||||
),
|
||||
(
|
||||
name: "Personage",
|
||||
tags: [
|
||||
(name: "Personal", id: "personal"),
|
||||
(name: "Small team (personal but multiple people)", id: "multipersonal"),
|
||||
(name: "Community", id: "community"),
|
||||
(name: "Non-profit organisation", id: "nonprofit"),
|
||||
(name: "Company", id: "company"),
|
||||
]
|
||||
),
|
||||
],
|
||||
column2: [
|
||||
(
|
||||
name: "Content",
|
||||
tags: [
|
||||
(name: "Technical (broad category)", id: "technical"),
|
||||
(name: "Software", id: "software"),
|
||||
(name: "Electronics", id: "electronics"),
|
||||
(name: "Mechanical", id: "mechanical"),
|
||||
(name: "Physics", id: "physics"),
|
||||
(name: "Chemistry", id: "chemistry"),
|
||||
(name: "Biology", id: "biology"),
|
||||
(name: "Mathematics", id: "maths"),
|
||||
(name: "Non-technical (broad category)", id: "nontechnical"),
|
||||
(name: "Plants", id: "plants"),
|
||||
(name: "Animals", id: "animals"),
|
||||
(name: "Culinary", id: "culinary"),
|
||||
(name: "Visual Art", id: "gfxart"),
|
||||
(name: "Music", id: "music"),
|
||||
(name: "Other Art", id: "miscart"),
|
||||
(name: "Travel", id: "travel"),
|
||||
]
|
||||
)
|
||||
],
|
||||
column3: [
|
||||
(
|
||||
name: "Anti-tags",
|
||||
tags: [
|
||||
(name: "Outdated ('old')", id: "old"),
|
||||
(name: "Unsafe", id: "nsfw"),
|
||||
]
|
||||
)
|
||||
],
|
||||
contact: [
|
||||
("Matrix", "..."),
|
||||
("e-mail", "..."),
|
||||
]
|
||||
),
|
||||
|
||||
sqlite_db_path: "data/dev_qp_web.sqlite3"
|
||||
)
|
@ -6,3 +6,14 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.56"
|
||||
tokio = { version = "1.17.0", features = ["full"] }
|
||||
askama = "0.11.1"
|
||||
axum = "0.4.8"
|
||||
serde = { version = "1.0.136", features = ["derive"] }
|
||||
ron = "0.7.0"
|
||||
tower-http = { version = "0.2.5", features = ["fs"] }
|
||||
log = "0.4.14"
|
||||
env_logger = "0.9.0"
|
||||
sqlx = { version = "0.5.11", features = ["sqlite", "runtime-tokio-rustls"] }
|
||||
itertools = "0.10.3"
|
||||
|
3
quickpeep/askama.toml
Normal file
3
quickpeep/askama.toml
Normal file
@ -0,0 +1,3 @@
|
||||
[[escaper]]
|
||||
path = "askama::Html"
|
||||
extensions = ["askama"]
|
7
quickpeep/dev_db.sh
Executable file
7
quickpeep/dev_db.sh
Executable file
@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
dbpath="$(dirname "$0")/testdb.sqlite"
|
||||
#echo $dbpath
|
||||
sqlx db create --database-url sqlite:"$dbpath"
|
||||
sqlx migrate run --database-url sqlite:"$dbpath"
|
||||
|
21
quickpeep/migrations/20220316065143_seed_collection.sql
Normal file
21
quickpeep/migrations/20220316065143_seed_collection.sql
Normal file
@ -0,0 +1,21 @@
|
||||
-- Support the collection of seeds
|
||||
|
||||
CREATE TABLE collected_seeds (
|
||||
-- Aliases the ROWID and should therefore self-generate.
|
||||
collected_seed_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
-- sec since epoch of collection
|
||||
collected_ts INTEGER NOT NULL,
|
||||
|
||||
-- the URL that was collected
|
||||
url TEXT NOT NULL,
|
||||
|
||||
-- Comma-separated, for ease, as we don't need to do much but pull them out anyway.
|
||||
tags TEXT NOT NULL,
|
||||
|
||||
-- Extra tags (unsanitised)
|
||||
extra_tags TEXT NOT NULL,
|
||||
|
||||
-- Any remarks that were entered
|
||||
remarks_private TEXT NOT NULL
|
||||
);
|
18
quickpeep/src/config.rs
Normal file
18
quickpeep/src/config.rs
Normal file
@ -0,0 +1,18 @@
|
||||
use crate::web::seed_collector::SeedTagColumn;
|
||||
use serde::Deserialize;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct SeedCollectionConfig {
|
||||
pub column1: SeedTagColumn,
|
||||
pub column2: SeedTagColumn,
|
||||
pub column3: SeedTagColumn,
|
||||
/// Name, URL pairs
|
||||
pub contact: Vec<(String, String)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct WebConfig {
|
||||
pub seed_collection: SeedCollectionConfig,
|
||||
pub sqlite_db_path: PathBuf,
|
||||
}
|
@ -1,3 +1,91 @@
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
use crate::config::WebConfig;
|
||||
use crate::web::seed_collector::{seed_collection_root, seed_collection_root_post};
|
||||
use anyhow::{bail, Context};
|
||||
use axum::extract::Extension;
|
||||
use axum::http::StatusCode;
|
||||
use axum::routing::{get, get_service, post};
|
||||
use axum::Router;
|
||||
use env_logger::Env;
|
||||
use sqlx::sqlite::SqlitePoolOptions;
|
||||
use std::path::PathBuf;
|
||||
use tower_http::services::ServeDir;
|
||||
|
||||
mod config;
|
||||
|
||||
mod web;
|
||||
|
||||
mod webutil;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
env_logger::Builder::from_env(
|
||||
Env::default().default_filter_or("info,quickpeep=debug,sqlx=warn"),
|
||||
)
|
||||
.init();
|
||||
|
||||
let config_path =
|
||||
PathBuf::from(std::env::var("QP_WEB_CONFIG").unwrap_or_else(|_| "qp_web.ron".to_owned()));
|
||||
|
||||
if !config_path.exists() {
|
||||
bail!(
|
||||
"Config path {:?} doesn't exist. QP_WEB_CONFIG env var overrides.",
|
||||
config_path
|
||||
);
|
||||
}
|
||||
|
||||
let file_bytes = std::fs::read(&config_path).context("Failed to read web config file")?;
|
||||
let web_config: WebConfig =
|
||||
ron::de::from_bytes(&file_bytes).context("Failed to parse web config")?;
|
||||
|
||||
let pool = SqlitePoolOptions::new()
|
||||
.min_connections(1)
|
||||
.after_connect(|conn| {
|
||||
Box::pin(async move {
|
||||
// Use the WAL because it just makes more sense :)
|
||||
sqlx::query("PRAGMA journal_mode = WAL")
|
||||
.execute(&mut *conn)
|
||||
.await?;
|
||||
|
||||
// Enable foreign keys because we like them!
|
||||
sqlx::query("PRAGMA foreign_keys = ON")
|
||||
.execute(&mut *conn)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
})
|
||||
.connect(
|
||||
&web_config
|
||||
.sqlite_db_path
|
||||
.to_str()
|
||||
.context("SQLite DB path should be UTF-8")?,
|
||||
)
|
||||
.await?;
|
||||
|
||||
sqlx::migrate!().run(&pool).await?;
|
||||
|
||||
let app = Router::new()
|
||||
.route("/seeds/", get(seed_collection_root))
|
||||
.route("/seeds/", post(seed_collection_root_post))
|
||||
.layer(Extension(web_config))
|
||||
.layer(Extension(pool))
|
||||
.nest(
|
||||
"/static",
|
||||
get_service(ServeDir::new("./quickpeep_static/dist")).handle_error(
|
||||
|error: std::io::Error| async move {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Unhandled internal error: {}", error),
|
||||
)
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// run it with hyper on localhost:3000
|
||||
axum::Server::bind(&"0.0.0.0:9001".parse().unwrap())
|
||||
.serve(app.into_make_service())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
2
quickpeep/src/web.rs
Normal file
2
quickpeep/src/web.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod searcher;
|
||||
pub mod seed_collector;
|
1
quickpeep/src/web/searcher.rs
Normal file
1
quickpeep/src/web/searcher.rs
Normal file
@ -0,0 +1 @@
|
||||
|
132
quickpeep/src/web/seed_collector.rs
Normal file
132
quickpeep/src/web/seed_collector.rs
Normal file
@ -0,0 +1,132 @@
|
||||
use crate::webutil::{internal_error, TemplatedHtml};
|
||||
use crate::WebConfig;
|
||||
use askama::Template;
|
||||
use axum::extract::{Extension, Form};
|
||||
use axum::response::IntoResponse;
|
||||
use itertools::Itertools;
|
||||
use serde::Deserialize;
|
||||
use sqlx::SqlitePool;
|
||||
use std::collections::{BTreeSet, HashMap};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
pub type SeedTagColumn = Vec<SeedTagGroup>;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct SeedTagGroup {
|
||||
name: String,
|
||||
tags: Vec<SeedTag>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct SeedTag {
|
||||
name: String,
|
||||
id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Template)]
|
||||
#[template(path = "seed_collection.html.askama")]
|
||||
pub struct SeedCollectionFormTemplate {
|
||||
column1: SeedTagColumn,
|
||||
column2: SeedTagColumn,
|
||||
column3: SeedTagColumn,
|
||||
thanks_for_submitting: bool,
|
||||
contact: Vec<(String, String)>,
|
||||
}
|
||||
|
||||
pub async fn seed_collection_root(
|
||||
Extension(web_config): Extension<WebConfig>,
|
||||
) -> impl IntoResponse {
|
||||
// TODO(perf): cloning is a bit ugly; can we do better?
|
||||
let seed_config = &web_config.seed_collection;
|
||||
TemplatedHtml(SeedCollectionFormTemplate {
|
||||
column1: seed_config.column1.clone(),
|
||||
column2: seed_config.column2.clone(),
|
||||
column3: seed_config.column3.clone(),
|
||||
thanks_for_submitting: false,
|
||||
contact: seed_config.contact.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct SeedCollectionPostForm {
|
||||
pub url: String,
|
||||
pub opendata_agree: bool,
|
||||
pub extratags: String,
|
||||
pub remarks_private: String,
|
||||
/// Mostly expecting `tag_xyz` bools to be here.
|
||||
#[serde(flatten)]
|
||||
pub leftovers: HashMap<String, String>,
|
||||
}
|
||||
|
||||
pub async fn seed_collection_root_post_inner(
|
||||
Form(form): Form<SeedCollectionPostForm>,
|
||||
Extension(web_config): Extension<WebConfig>,
|
||||
Extension(pool): Extension<SqlitePool>,
|
||||
) -> anyhow::Result<TemplatedHtml<SeedCollectionFormTemplate>> {
|
||||
// TODO(perf): cloning is a bit ugly; can we do better?
|
||||
eprintln!("{:?}", form);
|
||||
|
||||
let mut tags = BTreeSet::new();
|
||||
let seed_config = &web_config.seed_collection;
|
||||
|
||||
for tag_group in itertools::chain!(
|
||||
&seed_config.column1,
|
||||
&seed_config.column2,
|
||||
&seed_config.column3
|
||||
) {
|
||||
for tag in &tag_group.tags {
|
||||
if form.leftovers.contains_key(&format!("tag_{}", tag.id)) {
|
||||
tags.insert(tag.id.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let tags_str = tags.iter().join(",");
|
||||
|
||||
let extra_tags = form
|
||||
.extratags
|
||||
.trim()
|
||||
.replace(',', " ")
|
||||
.split_whitespace()
|
||||
.join(",");
|
||||
|
||||
let remarks_private: String = form.remarks_private.trim().chars().take(512).collect();
|
||||
|
||||
let ts = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.expect("Duration since Unix Epoch failed.")
|
||||
.as_secs() as i64;
|
||||
|
||||
let url = form.url.trim();
|
||||
|
||||
sqlx::query!(
|
||||
"INSERT INTO collected_seeds (
|
||||
collected_ts, url, tags, extra_tags, remarks_private
|
||||
) VALUES (?, ?, ?, ?, ?)",
|
||||
ts,
|
||||
url,
|
||||
tags_str,
|
||||
extra_tags,
|
||||
remarks_private
|
||||
)
|
||||
.execute(&pool)
|
||||
.await?;
|
||||
|
||||
Ok(TemplatedHtml(SeedCollectionFormTemplate {
|
||||
column1: seed_config.column1.clone(),
|
||||
column2: seed_config.column2.clone(),
|
||||
column3: seed_config.column3.clone(),
|
||||
thanks_for_submitting: true,
|
||||
contact: seed_config.contact.clone(),
|
||||
}))
|
||||
}
|
||||
|
||||
pub async fn seed_collection_root_post(
|
||||
form: Form<SeedCollectionPostForm>,
|
||||
web_config: Extension<WebConfig>,
|
||||
pool: Extension<SqlitePool>,
|
||||
) -> impl IntoResponse {
|
||||
seed_collection_root_post_inner(form, web_config, pool)
|
||||
.await
|
||||
.map_err(internal_error)
|
||||
}
|
25
quickpeep/src/webutil.rs
Normal file
25
quickpeep/src/webutil.rs
Normal file
@ -0,0 +1,25 @@
|
||||
use askama::Template;
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::{Html, IntoResponse, Response};
|
||||
|
||||
pub struct TemplatedHtml<T>(pub T);
|
||||
|
||||
impl<T> IntoResponse for TemplatedHtml<T>
|
||||
where
|
||||
T: Template,
|
||||
{
|
||||
fn into_response(self) -> Response {
|
||||
match self.0.render() {
|
||||
Ok(html) => Html(html).into_response(),
|
||||
Err(err) => (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Failed to render template. Error: {}", err),
|
||||
)
|
||||
.into_response(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn internal_error(err: anyhow::Error) -> (StatusCode, String) {
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, err.to_string())
|
||||
}
|
90
quickpeep/templates/seed_collection.html.askama
Normal file
90
quickpeep/templates/seed_collection.html.askama
Normal file
@ -0,0 +1,90 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>QuickPeep Seed Collection Service</title>
|
||||
<link rel="stylesheet" type="text/css" href="/static/main.css">
|
||||
</head>
|
||||
<body>
|
||||
<!-- Header -->
|
||||
<header class="container">
|
||||
<hgroup>
|
||||
<h1>QuickPeep Seed Collection Service</h1>
|
||||
<h2>Please submit high-quality websites; especially personal blogs and other 'artisanal' websites.</h2>
|
||||
</hgroup>
|
||||
</header><!-- ./ Header -->
|
||||
|
||||
<!-- Main -->
|
||||
<main class="container">
|
||||
{% if thanks_for_submitting %}
|
||||
<div class="bar_happy">Thanks for submitting a seed!</div>
|
||||
{% endif %}
|
||||
<!-- Preview -->
|
||||
<section id="seed">
|
||||
<form method="POST">
|
||||
<h2>Submit a seed</h2>
|
||||
<p>Feel free to submit your own sites! The more the merrier, as long as they are high quality.</p>
|
||||
|
||||
<label for="url">URL</label>
|
||||
<input type="text" id="url" name="url" placeholder="https://mycoolsite.com">
|
||||
<small>Only submit each website (determined by the domain) once; other articles on the same site can be discovered.
|
||||
<br>You can also submit an RSS/Atom feed or an XML sitemap.
|
||||
<br>To apply a tag to a tree of URLs rather than a whole site, please use an asterisk as in <code>https://www.postgresql.org/docs/current/*</code>.</small>
|
||||
|
||||
<h3>Tags (apply to entire site; select all that apply)</h3>
|
||||
<fieldset>
|
||||
<div class="grid">
|
||||
{% macro label_kind(name, labels) %}
|
||||
<p>
|
||||
<strong>{{ name }}</strong>
|
||||
{% for tag in labels %}
|
||||
<label for="tag_{{ tag.id }}">
|
||||
<input type="checkbox" id="tag_{{ tag.id }}" name="tag_{{ tag.id }}"> {{ tag.name }}<br>
|
||||
</label>
|
||||
{% endfor %}
|
||||
</p>
|
||||
{% endmacro %}
|
||||
{% macro label_column(column) %}
|
||||
<div>
|
||||
{% for group in column %}
|
||||
{% call label_kind(group.name, group.tags.iter()) %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
{% call label_column(column1) %}
|
||||
{% call label_column(column2) %}
|
||||
{% call label_column(column3) %}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<label for="extratags">Extra tags (if there are obvious missing tags above)</label>
|
||||
<input type="text" id="extratags" name="extratags" placeholder="..., ..., ...">
|
||||
<small>These won't be published directly, but can help guide the creation of more tags. Separate with spaces and/or commas, please.</small>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<label for="remarks_private">Extra (free-form) remarks</label>
|
||||
<textarea id="remarks_private" name="remarks_private" maxlength="256"></textarea>
|
||||
<small>If this might need some clean-up, you can add remarks here. These remarks <strong>will not</strong> be published.</small>
|
||||
</fieldset>
|
||||
|
||||
<h3>Open Data</h3>
|
||||
<label for="opendata_agree">
|
||||
<input type="checkbox" role="switch" id="opendata_agree" name="opendata_agree" value="true" required>
|
||||
I'm happy for this data to be Open Data under the <a href="https://bics.ga/reivilibre/quickpeep_seeds/src/branch/main/LICENCE" target="_blank">CC0 licence</a>.
|
||||
</label>
|
||||
|
||||
<input type="submit" value="Submit seed">
|
||||
</form>
|
||||
</section><!-- ./ Preview -->
|
||||
|
||||
<footer class="container">
|
||||
{% for (method, url) in contact %}
|
||||
<a href="{{ url }}">{{ method }}</a> •
|
||||
{% endfor %}
|
||||
<a href="/">Return to QuickPeep Root</a>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
@ -2,7 +2,7 @@ use clap::Parser;
|
||||
|
||||
use env_logger::Env;
|
||||
|
||||
use anyhow::Context;
|
||||
use anyhow::{bail, Context};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use quickpeep_raker::config;
|
||||
@ -12,6 +12,10 @@ use quickpeep_raker::config;
|
||||
pub struct Opts {
|
||||
#[clap(long = "config")]
|
||||
config: Option<PathBuf>,
|
||||
|
||||
#[clap(long = "import")]
|
||||
/// Import the seeds into the workbench
|
||||
import: bool,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
@ -25,6 +29,16 @@ pub async fn main() -> anyhow::Result<()> {
|
||||
.unwrap_or_else(|| PathBuf::from("qp_raker.toml"));
|
||||
let config = config::RakerConfig::load(&config_path).context("Failed to load config")?;
|
||||
|
||||
if !config.workbench_dir.exists() {
|
||||
bail!(
|
||||
"Workbench directory ({:?}) doesn't exist.",
|
||||
config.workbench_dir
|
||||
);
|
||||
}
|
||||
if !config.seed_dir.exists() {
|
||||
bail!("Seed directory ({:?}) doesn't exist.", config.seed_dir);
|
||||
}
|
||||
|
||||
eprintln!("{:#?}", config);
|
||||
|
||||
Ok(())
|
||||
|
@ -7,16 +7,16 @@ use std::path::{Path, PathBuf};
|
||||
/// when loading.
|
||||
pub struct RakerConfig {
|
||||
/// Path to data files
|
||||
data_dir: PathBuf,
|
||||
pub data_dir: PathBuf,
|
||||
|
||||
/// Path to seeds
|
||||
seed_dir: PathBuf,
|
||||
pub seed_dir: PathBuf,
|
||||
|
||||
/// Path to the raker's workbench (queue etc)
|
||||
workbench_dir: PathBuf,
|
||||
pub workbench_dir: PathBuf,
|
||||
|
||||
/// Directory where new rake packs will be emitted
|
||||
emit_dir: PathBuf,
|
||||
pub emit_dir: PathBuf,
|
||||
}
|
||||
|
||||
impl RakerConfig {
|
||||
|
16
quickpeep_static/package.json
Normal file
16
quickpeep_static/package.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "quickpeep_static",
|
||||
"version": "1.0.0",
|
||||
"license": "QuickPeep Licence",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "parcel build style/main.scss"
|
||||
},
|
||||
"dependencies": {
|
||||
"@picocss/pico": "^1.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@parcel/transformer-sass": "2.3.2",
|
||||
"parcel": "^2.3.2"
|
||||
}
|
||||
}
|
17
quickpeep_static/style/main.scss
Normal file
17
quickpeep_static/style/main.scss
Normal file
@ -0,0 +1,17 @@
|
||||
@import "npm:@picocss/pico/scss/pico.slim.scss";
|
||||
|
||||
:root {
|
||||
--typography-spacing-vertical: 1rem;
|
||||
}
|
||||
|
||||
.bar_happy {
|
||||
padding: 1em;
|
||||
margin: 3em;
|
||||
background-color: palegreen;
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
@media only screen and (prefers-color-scheme: dark) {
|
||||
.bar_happy {
|
||||
background-color: #11391F;
|
||||
}
|
||||
}
|
1660
quickpeep_static/yarn.lock
Normal file
1660
quickpeep_static/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user