Initial working version of the seed collection service

rei/minimum
Olivier 'reivilibre' 2022-03-16 19:53:08 +00:00
parent 2763ff2481
commit d09cfd4659
20 changed files with 2769 additions and 10 deletions

2
.env Normal file
View File

@ -0,0 +1,2 @@
DATABASE_URL=sqlite:quickpeep/testdb.sqlite

6
.gitignore vendored
View File

@ -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
View File

@ -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
View 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"
)

View File

@ -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
View File

@ -0,0 +1,3 @@
[[escaper]]
path = "askama::Html"
extensions = ["askama"]

7
quickpeep/dev_db.sh Executable file
View 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"

View 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
View 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,
}

View File

@ -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
View File

@ -0,0 +1,2 @@
pub mod searcher;
pub mod seed_collector;

View File

@ -0,0 +1 @@

View 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
View 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())
}

View 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>

View File

@ -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(())

View File

@ -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 {

View 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"
}
}

View 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

File diff suppressed because it is too large Load Diff